blob: 29da5f11ab0a7f67b8c3a9b9e414a3851b69fab6 [file] [log] [blame]
Anand Doshi885e0742015-03-03 14:55:30 +05301# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
Nabin Hait3237c752015-02-17 11:11:11 +05302# License: GNU General Public License v3. See license.txt
3
Chillar Anand915b3432021-09-02 16:44:59 +05304
Nabin Hait3237c752015-02-17 11:11:11 +05305import json
Chillar Anand915b3432021-09-02 16:44:59 +05306
7import frappe
Nabin Hait3769d872015-12-18 13:12:02 +05308from frappe import _, scrub
Nabin Haitb962fc12017-07-17 18:02:31 +05309from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
Chillar Anand915b3432021-09-02 16:44:59 +053010
11import erpnext
Deepesh Gargbfc17e42020-12-25 18:34:39 +053012from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Chillar Anand915b3432021-09-02 16:44:59 +053013from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
14from erpnext.controllers.accounts_controller import (
15 validate_conversion_rate,
16 validate_inclusive_tax,
17 validate_taxes_and_charges,
18)
19from erpnext.stock.get_item_details import _get_item_tax_template
20
Nabin Hait3237c752015-02-17 11:11:11 +053021
Nabin Haitfe81da22015-02-18 12:23:18 +053022class calculate_taxes_and_totals(object):
Nabin Hait3237c752015-02-17 11:11:11 +053023 def __init__(self, doc):
24 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053025 frappe.flags.round_off_applicable_accounts = []
26 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053027 self.calculate()
28
Nabin Hait3237c752015-02-17 11:11:11 +053029 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053030 if not len(self.doc.get("items")):
31 return
32
Nabin Hait3237c752015-02-17 11:11:11 +053033 self.discount_amount_applied = False
34 self._calculate()
35
36 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053037 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053038 self.apply_discount_amount()
39
Deepesh Gargd596e0e2022-03-10 20:56:36 +053040 self.calculate_shipping_charges()
41
Nabin Haitbd00e812015-02-17 12:50:51 +053042 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053043 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053044
Nabin Hait852cb642017-07-05 12:58:19 +053045 if self.doc.meta.get_field("other_charges_calculation"):
46 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053047
48 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053049 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053050 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053051 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053052 self.initialize_taxes()
53 self.determine_exclusive_rate()
54 self.calculate_net_total()
55 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053056 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053057 self.calculate_totals()
58 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053059 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053060
Deepesh Gargef0d26c2020-01-06 15:34:15 +053061 def validate_item_tax_template(self):
62 for item in self.doc.get('items'):
63 if item.item_code and item.get('item_tax_template'):
64 item_doc = frappe.get_cached_doc("Item", item.item_code)
65 args = {
Deepesh Garg8a7e2832021-06-04 22:53:26 +053066 'net_rate': item.net_rate or item.rate,
Deepesh Gargef0d26c2020-01-06 15:34:15 +053067 'tax_category': self.doc.get('tax_category'),
68 'posting_date': self.doc.get('posting_date'),
69 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050070 'transaction_date': self.doc.get('transaction_date'),
71 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053072 }
73
74 item_group = item_doc.item_group
75 item_group_taxes = []
76
77 while item_group:
78 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
79 item_group_taxes += item_group_doc.taxes or []
80 item_group = item_group_doc.parent_item_group
81
82 item_taxes = item_doc.taxes or []
83
84 if not item_group_taxes and (not item_taxes):
85 # No validation if no taxes in item or item group
86 continue
87
88 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
89
Deepesh Garg18be7672021-06-06 13:25:34 +053090 if taxes:
91 if item.item_tax_template not in taxes:
92 item.item_tax_template = taxes[0]
93 frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
94 item.idx, frappe.bold(item.item_code)
95 ))
Deepesh Gargef0d26c2020-01-06 15:34:15 +053096
Nabin Haite7679702015-02-20 14:40:35 +053097 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053098 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053099 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530100 if not self.doc.currency or self.doc.currency == company_currency:
101 self.doc.currency = company_currency
102 self.doc.conversion_rate = 1.0
103 else:
104 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
105 self.doc.meta.get_label("conversion_rate"), self.doc.company)
106
107 self.doc.conversion_rate = flt(self.doc.conversion_rate)
108
Nabin Hait3237c752015-02-17 11:11:11 +0530109 def calculate_item_values(self):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530110 if self.doc.get('is_consolidated'):
111 return
112
Nabin Hait3237c752015-02-17 11:11:11 +0530113 if not self.discount_amount_applied:
114 for item in self.doc.get("items"):
115 self.doc.round_floats_in(item)
116
117 if item.discount_percentage == 100:
118 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530119 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530120 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Nabin Hait593242f2019-04-05 19:35:02 +0530121 item.rate = flt(item.price_list_rate *
122 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Deepesh Gargd95f8932022-03-01 23:09:59 +0530123
Deepesh Garga83a0a02022-03-25 12:28:55 +0530124 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530125
Deepesh Garg97e102c2022-03-25 12:39:59 +0530126 elif item.discount_amount and item.pricing_rules:
Nabin Hait593242f2019-04-05 19:35:02 +0530127 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530128
Deepesh Gargd95f8932022-03-01 23:09:59 +0530129 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item',
130 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530131 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530132 if flt(item.rate_with_margin) > 0:
133 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530134
Walstan Baptista37b826b2021-04-03 19:48:46 +0530135 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530136 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530137 else:
138 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530139
Nabin Hait64bfdd92019-04-23 13:37:19 +0530140 elif flt(item.price_list_rate) > 0:
141 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530142 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
143 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530144
Nabin Haite7679702015-02-20 14:40:35 +0530145 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530146
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530147 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530148 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530149 elif not item.qty and self.doc.get("is_debit_note"):
150 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530151 else:
152 item.amount = flt(item.rate * item.qty, item.precision("amount"))
153
Nabin Haite7679702015-02-20 14:40:35 +0530154 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530155
Nabin Haite7679702015-02-20 14:40:35 +0530156 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530157
Nabin Haite7679702015-02-20 14:40:35 +0530158 item.item_tax_amount = 0.0
159
160 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530161 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530162 for f in fields:
163 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
164 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530165
166 def initialize_taxes(self):
167 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530168 if not self.discount_amount_applied:
169 validate_taxes_and_charges(tax)
170 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530171
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530172 if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530173 tax.item_wise_tax_detail = {}
174
Nabin Hait3237c752015-02-17 11:11:11 +0530175 tax_fields = ["total", "tax_amount_after_discount_amount",
176 "tax_amount_for_current_item", "grand_total_for_current_item",
177 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
178
Nabin Haitde9c8a92015-02-23 01:06:00 +0530179 if tax.charge_type != "Actual" and \
180 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
181 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530182
183 for fieldname in tax_fields:
184 tax.set(fieldname, 0.0)
185
Nabin Hait3237c752015-02-17 11:11:11 +0530186 self.doc.round_floats_in(tax)
187
Nabin Hait3237c752015-02-17 11:11:11 +0530188 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530189 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530190 return
Nabin Hait3237c752015-02-17 11:11:11 +0530191
192 for item in self.doc.get("items"):
193 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
194 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530195 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530196 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530197 tax.tax_fraction_for_current_item, inclusive_tax_amount_per_qty = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530198
199 if i==0:
200 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
201 else:
202 tax.grand_total_fraction_for_current_item = \
203 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
204 + tax.tax_fraction_for_current_item
205
206 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530207 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530208
Nabin Hait19ea7212020-08-11 20:34:57 +0530209 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
210 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
211
212 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530213 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530214 item.discount_percentage = flt(item.discount_percentage,
215 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530216
Nabin Haite7679702015-02-20 14:40:35 +0530217 self._set_in_company_currency(item, ["net_rate", "net_amount"])
218
Nabin Hait3237c752015-02-17 11:11:11 +0530219 def _load_item_tax_rate(self, item_tax_rate):
220 return json.loads(item_tax_rate) if item_tax_rate else {}
221
222 def get_current_tax_fraction(self, tax, item_tax_map):
223 """
224 Get tax fraction for calculating tax exclusive amount
225 from tax inclusive amount
226 """
227 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530228 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530229
230 if cint(tax.included_in_print_rate):
231 tax_rate = self._get_tax_rate(tax, item_tax_map)
232
233 if tax.charge_type == "On Net Total":
234 current_tax_fraction = tax_rate / 100.0
235
236 elif tax.charge_type == "On Previous Row Amount":
237 current_tax_fraction = (tax_rate / 100.0) * \
238 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
239
240 elif tax.charge_type == "On Previous Row Total":
241 current_tax_fraction = (tax_rate / 100.0) * \
242 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530243
Nabin Hait19ea7212020-08-11 20:34:57 +0530244 elif tax.charge_type == "On Item Quantity":
245 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530246
Nabin Hait19ea7212020-08-11 20:34:57 +0530247 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
248 current_tax_fraction *= -1.0
249 inclusive_tax_amount_per_qty *= -1.0
250
251 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530252
253 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530254 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530255 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
256 else:
257 return tax.rate
258
259 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530260 self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530261
Nabin Hait3237c752015-02-17 11:11:11 +0530262 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530263 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530264 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530265 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530266 self.doc.net_total += item.net_amount
267 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530268
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530269 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530270
Subin Toma8e2c022021-11-16 19:06:49 +0530271 def calculate_shipping_charges(self):
Subin Tomaf1fce02021-11-10 16:49:12 +0530272 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530273 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
274 shipping_rule.apply(self.doc)
275
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530276 self._calculate()
277
Nabin Hait3237c752015-02-17 11:11:11 +0530278 def calculate_taxes(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530279 rounding_adjustment_computed = self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment')
280 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530281 self.doc.rounding_adjustment = 0
282
Nabin Hait3237c752015-02-17 11:11:11 +0530283 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530284 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530285 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
286
287 for n, item in enumerate(self.doc.get("items")):
288 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530289 for i, tax in enumerate(self.doc.get("taxes")):
290 # tax_amount represents the amount of tax for the current step
291 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
292
293 # Adjust divisional loss to the last item
294 if tax.charge_type == "Actual":
295 actual_tax_dict[tax.idx] -= current_tax_amount
296 if n == len(self.doc.get("items")) - 1:
297 current_tax_amount += actual_tax_dict[tax.idx]
298
Nabin Hait2b019ed2015-02-22 23:03:07 +0530299 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530300 if tax.charge_type != "Actual" and \
301 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
302 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530303
Nabin Hait3237c752015-02-17 11:11:11 +0530304 # store tax_amount for current item as it will be used for
305 # charge type = 'On Previous Row Amount'
306 tax.tax_amount_for_current_item = current_tax_amount
307
Nabin Hait2b019ed2015-02-22 23:03:07 +0530308 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530309 tax.tax_amount_after_discount_amount += current_tax_amount
310
Nabin Haitcd951342017-07-31 18:07:45 +0530311 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530312
Nabin Hait3237c752015-02-17 11:11:11 +0530313 # note: grand_total_for_current_item contains the contribution of
314 # item's amount, previously applied tax and the current tax on that item
315 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530316 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530317 else:
318 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530319 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530320
321 # set precision in the last item iteration
322 if n == len(self.doc.get("items")) - 1:
323 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530324 self._set_in_company_currency(tax,
325 ["tax_amount", "tax_amount_after_discount_amount"])
326
327 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530328 self.set_cumulative_total(i, tax)
329
Deepesh Gargb6705882021-04-14 11:20:27 +0530330 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530331
Nabin Hait3237c752015-02-17 11:11:11 +0530332 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530333 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Subin Tom75a76e62021-10-29 16:45:04 +0530334 and self.doc.discount_amount \
335 and self.doc.apply_discount_on == "Grand Total" \
Saqib Ansari17445c72022-03-07 18:01:07 +0530336 and not rounding_adjustment_computed:
Nabin Hait2e4de832017-09-19 14:53:16 +0530337 self.doc.rounding_adjustment = flt(self.doc.grand_total
338 - flt(self.doc.discount_amount) - tax.total,
339 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530340
Nabin Haitcd951342017-07-31 18:07:45 +0530341 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
342 # if just for valuation, do not add the tax amount in total
343 # if tax/charges is for deduction, multiply by -1
344 if getattr(tax, "category", None):
345 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530346 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
347 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530348 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530349
Nabin Haitcd951342017-07-31 18:07:45 +0530350 def set_cumulative_total(self, row_idx, tax):
351 tax_amount = tax.tax_amount_after_discount_amount
352 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
353
354 if row_idx == 0:
355 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
356 else:
357 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530358
359 def get_current_tax_amount(self, item, tax, item_tax_map):
360 tax_rate = self._get_tax_rate(tax, item_tax_map)
361 current_tax_amount = 0.0
362
363 if tax.charge_type == "Actual":
364 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530365 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
366 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
367
Nabin Hait3237c752015-02-17 11:11:11 +0530368 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530369 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530370 elif tax.charge_type == "On Previous Row Amount":
371 current_tax_amount = (tax_rate / 100.0) * \
372 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
373 elif tax.charge_type == "On Previous Row Total":
374 current_tax_amount = (tax_rate / 100.0) * \
375 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530376 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530377 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530378
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530379 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530380 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530381
382 return current_tax_amount
383
Nabin Haite7679702015-02-20 14:40:35 +0530384 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
385 # store tax breakup for each item
386 key = item.item_code or item.item_name
387 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
388 if tax.item_wise_tax_detail.get(key):
389 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
390
Nabin Haitcaab5822017-08-24 16:22:28 +0530391 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530392
Nabin Hait3237c752015-02-17 11:11:11 +0530393 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530394 if tax.account_head in frappe.flags.round_off_applicable_accounts:
395 tax.tax_amount = round(tax.tax_amount, 0)
396 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
397
Nabin Haite7679702015-02-20 14:40:35 +0530398 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530399 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530400 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530401
Deepesh Gargb6705882021-04-14 11:20:27 +0530402 def round_off_base_values(self, tax):
403 # Round off to nearest integer based on regional settings
404 if tax.account_head in frappe.flags.round_off_applicable_accounts:
405 tax.base_tax_amount = round(tax.base_tax_amount, 0)
406 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
407
Nabin Haita1bf43b2015-03-17 10:50:47 +0530408 def manipulate_grand_total_for_inclusive_tax(self):
409 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530410 if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")):
Nabin Haita1bf43b2015-03-17 10:50:47 +0530411 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530412 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
413 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530414
Nabin Hait2e4de832017-09-19 14:53:16 +0530415 diff = self.doc.total + non_inclusive_tax_amount \
416 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530417
418 # If discount amount applied, deduct the discount amount
419 # because self.doc.total is always without discount, but last_tax.total is after discount
420 if self.discount_amount_applied and self.doc.discount_amount:
421 diff -= flt(self.doc.discount_amount)
422
423 diff = flt(diff, self.doc.precision("rounding_adjustment"))
424
Nabin Hait2e4de832017-09-19 14:53:16 +0530425 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530426 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530427
428 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530429 if self.doc.get("taxes"):
430 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
431 else:
432 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530433
Subin Tom75a76e62021-10-29 16:45:04 +0530434 if self.doc.get("taxes"):
435 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
Nabin Hait2e4de832017-09-19 14:53:16 +0530436 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Subin Tom75a76e62021-10-29 16:45:04 +0530437 else:
438 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530439
Nabin Hait2e4de832017-09-19 14:53:16 +0530440 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530441
Saqiba6f98d42020-07-23 18:51:26 +0530442 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530443 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total")) \
Nabin Haite7679702015-02-20 14:40:35 +0530444 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530445 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530446 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530447 for tax in self.doc.get("taxes"):
448 if tax.category in ["Valuation and Total", "Total"]:
449 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530450 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530451 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530452 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530453
Nabin Haite7679702015-02-20 14:40:35 +0530454 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530455
Nabin Haite7679702015-02-20 14:40:35 +0530456 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
457 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
458 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530459
Nabin Hait2e4de832017-09-19 14:53:16 +0530460 self._set_in_company_currency(self.doc,
461 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530462
Nabin Haite7679702015-02-20 14:40:35 +0530463 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530464
Nabin Hait2e4de832017-09-19 14:53:16 +0530465 self.set_rounded_total()
466
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530467 def calculate_total_net_weight(self):
468 if self.doc.meta.get_field('total_net_weight'):
469 self.doc.total_net_weight = 0.0
470 for d in self.doc.items:
471 if d.total_weight:
472 self.doc.total_net_weight += d.total_weight
473
Nabin Hait2e4de832017-09-19 14:53:16 +0530474 def set_rounded_total(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530475 if self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment'):
476 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530477
Saqib Ansari17445c72022-03-07 18:01:07 +0530478 if self.doc.meta.get_field("rounded_total"):
479 if self.doc.is_rounded_total_disabled():
480 self.doc.rounded_total = self.doc.base_rounded_total = 0
481 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530482
Saqib Ansari17445c72022-03-07 18:01:07 +0530483 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
484 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait877e1bb2017-11-17 12:27:43 +0530485
Saqib Ansari17445c72022-03-07 18:01:07 +0530486 #if print_in_rate is set, we would have already calculated rounding adjustment
487 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
488 self.doc.precision("rounding_adjustment"))
489
490 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530491
Nabin Hait3237c752015-02-17 11:11:11 +0530492 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530493 if not self.doc.get('is_consolidated'):
494 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530495 if not tax.get("dont_recompute_tax"):
496 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530497
Nabin Hait3769d872015-12-18 13:12:02 +0530498 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530499 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530500 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530501 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530502
503 def apply_discount_amount(self):
504 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530505 if not self.doc.apply_discount_on:
506 frappe.throw(_("Please select Apply Discount On"))
507
Nabin Hait3237c752015-02-17 11:11:11 +0530508 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
509 self.doc.precision("base_discount_amount"))
510
Nabin Haite7679702015-02-20 14:40:35 +0530511 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530512 taxes = self.doc.get("taxes")
513 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530514
Nabin Haite7679702015-02-20 14:40:35 +0530515 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530516 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530517 for i, item in enumerate(self.doc.get("items")):
518 distributed_amount = flt(self.doc.discount_amount) * \
519 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530520
Nabin Haite7679702015-02-20 14:40:35 +0530521 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530522 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530523
Nabin Hait25bd84d2015-03-04 15:06:56 +0530524 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530525 if (self.doc.apply_discount_on == "Net Total" or not taxes or total_for_discount_amount==self.doc.net_total) \
Nabin Hait25bd84d2015-03-04 15:06:56 +0530526 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530527 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530528 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530529
Anand Doshiec5ec602015-03-05 19:31:23 +0530530 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530531 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530532
Nabin Hait51e980d2015-10-10 18:10:05 +0530533 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) if item.qty else 0
Anand Doshiec5ec602015-03-05 19:31:23 +0530534
Nabin Haite7679702015-02-20 14:40:35 +0530535 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530536
537 self.discount_amount_applied = True
538 self._calculate()
539 else:
540 self.doc.base_discount_amount = 0
541
Nabin Haite7679702015-02-20 14:40:35 +0530542 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530543 if self.doc.apply_discount_on == "Net Total":
544 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530545 else:
546 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530547
Nabin Haite7679702015-02-20 14:40:35 +0530548 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530549 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530550 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
551 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530552 elif tax.row_id in actual_taxes_dict:
553 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
554 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530555
Nabin Hait877e1bb2017-11-17 12:27:43 +0530556 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
557 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530558
559
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530560 def calculate_total_advance(self):
561 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530562 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
563 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530564
Nabin Haite7679702015-02-20 14:40:35 +0530565 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530566
Faris Ansari6041f5c2018-02-08 13:33:52 +0530567 grand_total = self.doc.rounded_total or self.doc.grand_total
568
Nabin Hait289ffb72016-02-08 11:06:55 +0530569 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530570 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530571 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530572 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530573 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530574 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530575 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530576 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530577
Nabin Haitadc09232016-02-09 10:31:11 +0530578 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530579 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
580 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530581
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530582 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530583 self.calculate_outstanding_amount()
584
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530585 def is_internal_invoice(self):
586 """
587 Checks if its an internal transfer invoice
588 and decides if to calculate any out standing amount or not
589 """
590
591 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
592 return True
593
594 return False
595
Nabin Hait3237c752015-02-17 11:11:11 +0530596 def calculate_outstanding_amount(self):
597 # NOTE:
598 # write_off_amount is only for POS Invoice
599 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530600 if self.doc.doctype == "Sales Invoice":
601 self.calculate_paid_amount()
602
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530603 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
604 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530605
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530606 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530607 self._set_in_company_currency(self.doc, ['write_off_amount'])
608
Nabin Hait877e1bb2017-11-17 12:27:43 +0530609 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
610 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530611 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
612
Nabin Hait877e1bb2017-11-17 12:27:43 +0530613 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530614 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530615 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
616 else:
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530617 total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
618 - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530619
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530620 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530621 change_amount = 0
622
Deepesh Garg0ebace52020-02-25 13:21:16 +0530623 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530624 self.calculate_write_off_amount()
625 self.calculate_change_amount()
626 change_amount = self.doc.change_amount \
627 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
628
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530629 paid_amount = self.doc.paid_amount \
630 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530631
Nabin Hait877e1bb2017-11-17 12:27:43 +0530632 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
633 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530634
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530635 if (
636 self.doc.doctype == 'Sales Invoice'
637 and self.doc.get('is_pos')
638 and self.doc.get('is_return')
639 and not self.doc.get('is_consolidated')
640 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530641 self.set_total_amount_to_default_mop(total_amount_to_pay)
642 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530643
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530644 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530645
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530646 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530647
648 if self.doc.is_pos:
649 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530650 payment.amount = flt(payment.amount)
651 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530652 paid_amount += payment.amount
653 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530654 elif not self.doc.is_return:
655 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530656
Manas Solankida486ee2018-07-06 12:36:57 +0530657 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
658 base_paid_amount += self.doc.loyalty_amount
659 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
660
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530661 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
662 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
663
Nabin Hait3bb1a422016-08-02 16:41:10 +0530664 def calculate_change_amount(self):
665 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530666 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530667 grand_total = self.doc.rounded_total or self.doc.grand_total
668 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530669
670 if self.doc.doctype == "Sales Invoice" \
Saqib Ansari75256862022-02-09 10:05:06 +0530671 and self.doc.paid_amount > grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530672 and any(d.type == "Cash" for d in self.doc.payments):
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530673
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530674 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530675 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530676
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530677 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530678 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530679
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530680 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530681 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530682 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
683 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530684 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
685 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530686
mbauskar36b51892016-01-18 16:31:10 +0530687 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530688 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530689 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530690 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530691 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530692 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530693 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530694 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530695
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530696 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
697 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530698 item.margin_type = pricing_rule.margin_type
699 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530700 has_margin = True
701
702 if not has_margin:
703 item.margin_type = None
704 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530705
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530706 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
707 item.margin_type = "Amount"
708 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
709 item.precision("margin_rate_or_amount"))
710 item.rate_with_margin = item.rate
711
712 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530713 margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530714 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530715 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530716
Shreya Shahbe690ef2017-11-14 17:22:41 +0530717 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530718
719 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530720 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530721
Subin Tom7d627df2021-08-23 11:05:07 +0530722 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530723 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
724 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530725
Deepesh Garg0ebace52020-02-25 13:21:16 +0530726 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530727 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530728 self.doc.append('payments', {
729 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530730 'amount': total_amount_to_pay,
731 'default': 1
Ankush Menatb147b852021-09-01 16:45:57 +0530732 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530733
Nabin Hait9c421612017-07-20 13:32:01 +0530734def get_itemised_tax_breakup_html(doc):
735 if not doc.taxes:
736 return
737 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530738
Nabin Hait9c421612017-07-20 13:32:01 +0530739 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530740 tax_accounts = []
741 for tax in doc.taxes:
742 if getattr(tax, "category", None) and tax.category=="Valuation":
743 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530744 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530745 tax_accounts.append(tax.description)
746
Nabin Hait9c421612017-07-20 13:32:01 +0530747 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530748
Nabin Hait9c421612017-07-20 13:32:01 +0530749 # get tax breakup data
750 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530751
752 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
753
rohitwaghchaured4526682017-12-28 14:20:13 +0530754 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530755 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530756
Nabin Hait9c421612017-07-20 13:32:01 +0530757 return frappe.render_template(
758 "templates/includes/itemised_tax_breakup.html", dict(
759 headers=headers,
760 itemised_tax=itemised_tax,
761 itemised_taxable_amount=itemised_taxable_amount,
762 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530763 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530764 )
Nabin Hait9c421612017-07-20 13:32:01 +0530765 )
Nabin Hait852cb642017-07-05 12:58:19 +0530766
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530767@frappe.whitelist()
768def get_round_off_applicable_accounts(company, account_list):
769 account_list = get_regional_round_off_accounts(company, account_list)
770
771 return account_list
772
773@erpnext.allow_regional
774def get_regional_round_off_accounts(company, account_list):
775 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530776
777@erpnext.allow_regional
778def update_itemised_tax_data(doc):
779 #Don't delete this method, used for localization
780 pass
781
Nabin Haitb962fc12017-07-17 18:02:31 +0530782@erpnext.allow_regional
783def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
784 return [_("Item"), _("Taxable Amount")] + tax_accounts
785
786@erpnext.allow_regional
787def get_itemised_tax_breakup_data(doc):
788 itemised_tax = get_itemised_tax(doc.taxes)
789
790 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
791
792 return itemised_tax, itemised_taxable_amount
793
Nabin Hait34c551d2019-07-03 10:34:31 +0530794def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530795 itemised_tax = {}
796 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530797 if getattr(tax, "category", None) and tax.category=="Valuation":
798 continue
799
Nabin Haitb962fc12017-07-17 18:02:31 +0530800 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530801 if item_tax_map:
802 for item_code, tax_data in item_tax_map.items():
803 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530804
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530805 tax_rate = 0.0
806 tax_amount = 0.0
807
Nabin Hait2e4de832017-09-19 14:53:16 +0530808 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530809 tax_rate = flt(tax_data[0])
810 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530811 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530812 tax_rate = flt(tax_data)
813
814 itemised_tax[item_code][tax.description] = frappe._dict(dict(
815 tax_rate = tax_rate,
816 tax_amount = tax_amount
817 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530818
Nabin Hait34c551d2019-07-03 10:34:31 +0530819 if with_tax_account:
820 itemised_tax[item_code][tax.description].tax_account = tax.account_head
821
Nabin Haitb962fc12017-07-17 18:02:31 +0530822 return itemised_tax
823
824def get_itemised_taxable_amount(items):
825 itemised_taxable_amount = frappe._dict()
826 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530827 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530828 itemised_taxable_amount.setdefault(item_code, 0)
829 itemised_taxable_amount[item_code] += item.net_amount
830
Nabin Haitcaab5822017-08-24 16:22:28 +0530831 return itemised_taxable_amount
832
833def get_rounded_tax_amount(itemised_tax, precision):
834 # Rounding based on tax_amount precision
835 for taxes in itemised_tax.values():
836 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530837 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530838
839class init_landed_taxes_and_totals(object):
840 def __init__(self, doc):
841 self.doc = doc
842 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
843 self.set_account_currency()
844 self.set_exchange_rate()
845 self.set_amounts_in_company_currency()
846
847 def set_account_currency(self):
848 company_currency = erpnext.get_company_currency(self.doc.company)
849 for d in self.doc.get(self.tax_field):
850 if not d.account_currency:
851 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
852 d.account_currency = account_currency or company_currency
853
854 def set_exchange_rate(self):
855 company_currency = erpnext.get_company_currency(self.doc.company)
856 for d in self.doc.get(self.tax_field):
857 if d.account_currency == company_currency:
858 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530859 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530860 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
861 account_currency=d.account_currency, company=self.doc.company)
862
863 if not d.exchange_rate:
864 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
865
866 def set_amounts_in_company_currency(self):
867 for d in self.doc.get(self.tax_field):
868 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530869 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))