blob: d362cdde110f60b3a2c48ee7554cfb03e71a4944 [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
Deepesh Gargd95f8932022-03-01 23:09:59 +0530117 if not item.rate:
118 item.rate = item.price_list_rate
119
Nabin Hait3237c752015-02-17 11:11:11 +0530120 if item.discount_percentage == 100:
121 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530122 elif item.price_list_rate:
Ankush Menatbbc47102022-03-01 11:48:28 +0530123 if item.pricing_rules or abs(item.discount_percentage) > 0:
Nabin Hait593242f2019-04-05 19:35:02 +0530124 item.rate = flt(item.price_list_rate *
125 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Deepesh Gargd95f8932022-03-01 23:09:59 +0530126
127 if abs(item.discount_percentage) > 0:
128 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
129
Ankush Menatbbc47102022-03-01 11:48:28 +0530130 elif item.discount_amount or item.pricing_rules:
Nabin Hait593242f2019-04-05 19:35:02 +0530131 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530132
Deepesh Gargd95f8932022-03-01 23:09:59 +0530133 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item',
134 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530135 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530136 if flt(item.rate_with_margin) > 0:
137 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530138
Walstan Baptista37b826b2021-04-03 19:48:46 +0530139 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530140 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530141 else:
142 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530143
Nabin Hait64bfdd92019-04-23 13:37:19 +0530144 elif flt(item.price_list_rate) > 0:
145 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530146 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
147 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530148
Nabin Haite7679702015-02-20 14:40:35 +0530149 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530150
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530151 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530152 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530153 elif not item.qty and self.doc.get("is_debit_note"):
154 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530155 else:
156 item.amount = flt(item.rate * item.qty, item.precision("amount"))
157
Nabin Haite7679702015-02-20 14:40:35 +0530158 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530159
Nabin Haite7679702015-02-20 14:40:35 +0530160 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530161
Nabin Haite7679702015-02-20 14:40:35 +0530162 item.item_tax_amount = 0.0
163
164 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530165 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530166 for f in fields:
167 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
168 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530169
170 def initialize_taxes(self):
171 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530172 if not self.discount_amount_applied:
173 validate_taxes_and_charges(tax)
174 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530175
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530176 if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530177 tax.item_wise_tax_detail = {}
178
Nabin Hait3237c752015-02-17 11:11:11 +0530179 tax_fields = ["total", "tax_amount_after_discount_amount",
180 "tax_amount_for_current_item", "grand_total_for_current_item",
181 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
182
Nabin Haitde9c8a92015-02-23 01:06:00 +0530183 if tax.charge_type != "Actual" and \
184 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
185 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530186
187 for fieldname in tax_fields:
188 tax.set(fieldname, 0.0)
189
Nabin Hait3237c752015-02-17 11:11:11 +0530190 self.doc.round_floats_in(tax)
191
Nabin Hait3237c752015-02-17 11:11:11 +0530192 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530193 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530194 return
Nabin Hait3237c752015-02-17 11:11:11 +0530195
196 for item in self.doc.get("items"):
197 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
198 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530199 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530200 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530201 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 +0530202
203 if i==0:
204 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
205 else:
206 tax.grand_total_fraction_for_current_item = \
207 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
208 + tax.tax_fraction_for_current_item
209
210 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530211 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530212
Nabin Hait19ea7212020-08-11 20:34:57 +0530213 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
214 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
215
216 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530217 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530218 item.discount_percentage = flt(item.discount_percentage,
219 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530220
Nabin Haite7679702015-02-20 14:40:35 +0530221 self._set_in_company_currency(item, ["net_rate", "net_amount"])
222
Nabin Hait3237c752015-02-17 11:11:11 +0530223 def _load_item_tax_rate(self, item_tax_rate):
224 return json.loads(item_tax_rate) if item_tax_rate else {}
225
226 def get_current_tax_fraction(self, tax, item_tax_map):
227 """
228 Get tax fraction for calculating tax exclusive amount
229 from tax inclusive amount
230 """
231 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530232 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530233
234 if cint(tax.included_in_print_rate):
235 tax_rate = self._get_tax_rate(tax, item_tax_map)
236
237 if tax.charge_type == "On Net Total":
238 current_tax_fraction = tax_rate / 100.0
239
240 elif tax.charge_type == "On Previous Row Amount":
241 current_tax_fraction = (tax_rate / 100.0) * \
242 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
243
244 elif tax.charge_type == "On Previous Row Total":
245 current_tax_fraction = (tax_rate / 100.0) * \
246 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530247
Nabin Hait19ea7212020-08-11 20:34:57 +0530248 elif tax.charge_type == "On Item Quantity":
249 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530250
Nabin Hait19ea7212020-08-11 20:34:57 +0530251 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
252 current_tax_fraction *= -1.0
253 inclusive_tax_amount_per_qty *= -1.0
254
255 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530256
257 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530258 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530259 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
260 else:
261 return tax.rate
262
263 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530264 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 +0530265
Nabin Hait3237c752015-02-17 11:11:11 +0530266 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530267 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530268 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530269 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530270 self.doc.net_total += item.net_amount
271 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530272
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530273 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530274
Subin Toma8e2c022021-11-16 19:06:49 +0530275 def calculate_shipping_charges(self):
Subin Tomaf1fce02021-11-10 16:49:12 +0530276 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530277 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
278 shipping_rule.apply(self.doc)
279
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530280 self._calculate()
281
Nabin Hait3237c752015-02-17 11:11:11 +0530282 def calculate_taxes(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530283 rounding_adjustment_computed = self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment')
284 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530285 self.doc.rounding_adjustment = 0
286
Nabin Hait3237c752015-02-17 11:11:11 +0530287 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530288 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530289 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
290
291 for n, item in enumerate(self.doc.get("items")):
292 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530293 for i, tax in enumerate(self.doc.get("taxes")):
294 # tax_amount represents the amount of tax for the current step
295 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
296
297 # Adjust divisional loss to the last item
298 if tax.charge_type == "Actual":
299 actual_tax_dict[tax.idx] -= current_tax_amount
300 if n == len(self.doc.get("items")) - 1:
301 current_tax_amount += actual_tax_dict[tax.idx]
302
Nabin Hait2b019ed2015-02-22 23:03:07 +0530303 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530304 if tax.charge_type != "Actual" and \
305 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
306 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530307
Nabin Hait3237c752015-02-17 11:11:11 +0530308 # store tax_amount for current item as it will be used for
309 # charge type = 'On Previous Row Amount'
310 tax.tax_amount_for_current_item = current_tax_amount
311
Nabin Hait2b019ed2015-02-22 23:03:07 +0530312 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530313 tax.tax_amount_after_discount_amount += current_tax_amount
314
Nabin Haitcd951342017-07-31 18:07:45 +0530315 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530316
Nabin Hait3237c752015-02-17 11:11:11 +0530317 # note: grand_total_for_current_item contains the contribution of
318 # item's amount, previously applied tax and the current tax on that item
319 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530320 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530321 else:
322 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530323 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530324
325 # set precision in the last item iteration
326 if n == len(self.doc.get("items")) - 1:
327 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530328 self._set_in_company_currency(tax,
329 ["tax_amount", "tax_amount_after_discount_amount"])
330
331 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530332 self.set_cumulative_total(i, tax)
333
Deepesh Gargb6705882021-04-14 11:20:27 +0530334 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530335
Nabin Hait3237c752015-02-17 11:11:11 +0530336 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530337 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Subin Tom75a76e62021-10-29 16:45:04 +0530338 and self.doc.discount_amount \
339 and self.doc.apply_discount_on == "Grand Total" \
Saqib Ansari17445c72022-03-07 18:01:07 +0530340 and not rounding_adjustment_computed:
Nabin Hait2e4de832017-09-19 14:53:16 +0530341 self.doc.rounding_adjustment = flt(self.doc.grand_total
342 - flt(self.doc.discount_amount) - tax.total,
343 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530344
Nabin Haitcd951342017-07-31 18:07:45 +0530345 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
346 # if just for valuation, do not add the tax amount in total
347 # if tax/charges is for deduction, multiply by -1
348 if getattr(tax, "category", None):
349 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530350 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
351 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530352 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530353
Nabin Haitcd951342017-07-31 18:07:45 +0530354 def set_cumulative_total(self, row_idx, tax):
355 tax_amount = tax.tax_amount_after_discount_amount
356 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
357
358 if row_idx == 0:
359 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
360 else:
361 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530362
363 def get_current_tax_amount(self, item, tax, item_tax_map):
364 tax_rate = self._get_tax_rate(tax, item_tax_map)
365 current_tax_amount = 0.0
366
367 if tax.charge_type == "Actual":
368 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530369 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
370 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
371
Nabin Hait3237c752015-02-17 11:11:11 +0530372 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530373 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530374 elif tax.charge_type == "On Previous Row Amount":
375 current_tax_amount = (tax_rate / 100.0) * \
376 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
377 elif tax.charge_type == "On Previous Row Total":
378 current_tax_amount = (tax_rate / 100.0) * \
379 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530380 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530381 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530382
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530383 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530384 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530385
386 return current_tax_amount
387
Nabin Haite7679702015-02-20 14:40:35 +0530388 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
389 # store tax breakup for each item
390 key = item.item_code or item.item_name
391 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
392 if tax.item_wise_tax_detail.get(key):
393 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
394
Nabin Haitcaab5822017-08-24 16:22:28 +0530395 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530396
Nabin Hait3237c752015-02-17 11:11:11 +0530397 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530398 if tax.account_head in frappe.flags.round_off_applicable_accounts:
399 tax.tax_amount = round(tax.tax_amount, 0)
400 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
401
Nabin Haite7679702015-02-20 14:40:35 +0530402 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530403 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530404 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530405
Deepesh Gargb6705882021-04-14 11:20:27 +0530406 def round_off_base_values(self, tax):
407 # Round off to nearest integer based on regional settings
408 if tax.account_head in frappe.flags.round_off_applicable_accounts:
409 tax.base_tax_amount = round(tax.base_tax_amount, 0)
410 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
411
Nabin Haita1bf43b2015-03-17 10:50:47 +0530412 def manipulate_grand_total_for_inclusive_tax(self):
413 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530414 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 +0530415 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530416 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
417 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530418
Nabin Hait2e4de832017-09-19 14:53:16 +0530419 diff = self.doc.total + non_inclusive_tax_amount \
420 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530421
422 # If discount amount applied, deduct the discount amount
423 # because self.doc.total is always without discount, but last_tax.total is after discount
424 if self.discount_amount_applied and self.doc.discount_amount:
425 diff -= flt(self.doc.discount_amount)
426
427 diff = flt(diff, self.doc.precision("rounding_adjustment"))
428
Nabin Hait2e4de832017-09-19 14:53:16 +0530429 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530430 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530431
432 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530433 if self.doc.get("taxes"):
434 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
435 else:
436 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530437
Subin Tom75a76e62021-10-29 16:45:04 +0530438 if self.doc.get("taxes"):
439 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
Nabin Hait2e4de832017-09-19 14:53:16 +0530440 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Subin Tom75a76e62021-10-29 16:45:04 +0530441 else:
442 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530443
Nabin Hait2e4de832017-09-19 14:53:16 +0530444 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530445
Saqiba6f98d42020-07-23 18:51:26 +0530446 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530447 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 +0530448 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530449 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530450 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530451 for tax in self.doc.get("taxes"):
452 if tax.category in ["Valuation and Total", "Total"]:
453 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530454 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530455 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530456 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530457
Nabin Haite7679702015-02-20 14:40:35 +0530458 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530459
Nabin Haite7679702015-02-20 14:40:35 +0530460 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
461 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
462 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530463
Nabin Hait2e4de832017-09-19 14:53:16 +0530464 self._set_in_company_currency(self.doc,
465 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530466
Nabin Haite7679702015-02-20 14:40:35 +0530467 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530468
Nabin Hait2e4de832017-09-19 14:53:16 +0530469 self.set_rounded_total()
470
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530471 def calculate_total_net_weight(self):
472 if self.doc.meta.get_field('total_net_weight'):
473 self.doc.total_net_weight = 0.0
474 for d in self.doc.items:
475 if d.total_weight:
476 self.doc.total_net_weight += d.total_weight
477
Nabin Hait2e4de832017-09-19 14:53:16 +0530478 def set_rounded_total(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530479 if self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment'):
480 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530481
Saqib Ansari17445c72022-03-07 18:01:07 +0530482 if self.doc.meta.get_field("rounded_total"):
483 if self.doc.is_rounded_total_disabled():
484 self.doc.rounded_total = self.doc.base_rounded_total = 0
485 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530486
Saqib Ansari17445c72022-03-07 18:01:07 +0530487 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
488 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait877e1bb2017-11-17 12:27:43 +0530489
Saqib Ansari17445c72022-03-07 18:01:07 +0530490 #if print_in_rate is set, we would have already calculated rounding adjustment
491 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
492 self.doc.precision("rounding_adjustment"))
493
494 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530495
Nabin Hait3237c752015-02-17 11:11:11 +0530496 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530497 if not self.doc.get('is_consolidated'):
498 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530499 if not tax.get("dont_recompute_tax"):
500 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530501
Nabin Hait3769d872015-12-18 13:12:02 +0530502 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530503 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530504 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530505 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530506
507 def apply_discount_amount(self):
508 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530509 if not self.doc.apply_discount_on:
510 frappe.throw(_("Please select Apply Discount On"))
511
Nabin Hait3237c752015-02-17 11:11:11 +0530512 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
513 self.doc.precision("base_discount_amount"))
514
Nabin Haite7679702015-02-20 14:40:35 +0530515 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530516 taxes = self.doc.get("taxes")
517 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530518
Nabin Haite7679702015-02-20 14:40:35 +0530519 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530520 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530521 for i, item in enumerate(self.doc.get("items")):
522 distributed_amount = flt(self.doc.discount_amount) * \
523 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530524
Nabin Haite7679702015-02-20 14:40:35 +0530525 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530526 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530527
Nabin Hait25bd84d2015-03-04 15:06:56 +0530528 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530529 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 +0530530 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530531 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530532 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530533
Anand Doshiec5ec602015-03-05 19:31:23 +0530534 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530535 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530536
Nabin Hait51e980d2015-10-10 18:10:05 +0530537 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 +0530538
Nabin Haite7679702015-02-20 14:40:35 +0530539 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530540
541 self.discount_amount_applied = True
542 self._calculate()
543 else:
544 self.doc.base_discount_amount = 0
545
Nabin Haite7679702015-02-20 14:40:35 +0530546 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530547 if self.doc.apply_discount_on == "Net Total":
548 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530549 else:
550 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530551
Nabin Haite7679702015-02-20 14:40:35 +0530552 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530553 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530554 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
555 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530556 elif tax.row_id in actual_taxes_dict:
557 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
558 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530559
Nabin Hait877e1bb2017-11-17 12:27:43 +0530560 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
561 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530562
563
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530564 def calculate_total_advance(self):
565 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530566 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
567 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530568
Nabin Haite7679702015-02-20 14:40:35 +0530569 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530570
Faris Ansari6041f5c2018-02-08 13:33:52 +0530571 grand_total = self.doc.rounded_total or self.doc.grand_total
572
Nabin Hait289ffb72016-02-08 11:06:55 +0530573 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530574 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530575 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530576 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530577 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530578 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530579 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530580 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530581
Nabin Haitadc09232016-02-09 10:31:11 +0530582 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530583 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
584 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530585
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530586 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530587 self.calculate_outstanding_amount()
588
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530589 def is_internal_invoice(self):
590 """
591 Checks if its an internal transfer invoice
592 and decides if to calculate any out standing amount or not
593 """
594
595 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
596 return True
597
598 return False
599
Nabin Hait3237c752015-02-17 11:11:11 +0530600 def calculate_outstanding_amount(self):
601 # NOTE:
602 # write_off_amount is only for POS Invoice
603 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530604 if self.doc.doctype == "Sales Invoice":
605 self.calculate_paid_amount()
606
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530607 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
608 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530609
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530610 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530611 self._set_in_company_currency(self.doc, ['write_off_amount'])
612
Nabin Hait877e1bb2017-11-17 12:27:43 +0530613 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
614 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530615 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
616
Nabin Hait877e1bb2017-11-17 12:27:43 +0530617 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530618 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530619 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
620 else:
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530621 total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
622 - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530623
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530624 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530625 change_amount = 0
626
Deepesh Garg0ebace52020-02-25 13:21:16 +0530627 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530628 self.calculate_write_off_amount()
629 self.calculate_change_amount()
630 change_amount = self.doc.change_amount \
631 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
632
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530633 paid_amount = self.doc.paid_amount \
634 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530635
Nabin Hait877e1bb2017-11-17 12:27:43 +0530636 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
637 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530638
Deepesh Garg0ebace52020-02-25 13:21:16 +0530639 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
Subin Tom7d627df2021-08-23 11:05:07 +0530640 self.set_total_amount_to_default_mop(total_amount_to_pay)
641 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530642
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530643 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530644
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530645 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530646
647 if self.doc.is_pos:
648 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530649 payment.amount = flt(payment.amount)
650 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530651 paid_amount += payment.amount
652 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530653 elif not self.doc.is_return:
654 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530655
Manas Solankida486ee2018-07-06 12:36:57 +0530656 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
657 base_paid_amount += self.doc.loyalty_amount
658 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
659
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530660 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
661 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
662
Nabin Hait3bb1a422016-08-02 16:41:10 +0530663 def calculate_change_amount(self):
664 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530665 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530666 grand_total = self.doc.rounded_total or self.doc.grand_total
667 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530668
669 if self.doc.doctype == "Sales Invoice" \
Saqib Ansari75256862022-02-09 10:05:06 +0530670 and self.doc.paid_amount > grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530671 and any(d.type == "Cash" for d in self.doc.payments):
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530672
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530673 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530674 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530675
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530676 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530677 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530678
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530679 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530680 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530681 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
682 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530683 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
684 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530685
mbauskar36b51892016-01-18 16:31:10 +0530686 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530687 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530688 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530689 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530690 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530691 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530692 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530693 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530694
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530695 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
696 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530697 item.margin_type = pricing_rule.margin_type
698 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530699 has_margin = True
700
701 if not has_margin:
702 item.margin_type = None
703 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530704
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530705 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
706 item.margin_type = "Amount"
707 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
708 item.precision("margin_rate_or_amount"))
709 item.rate_with_margin = item.rate
710
711 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530712 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 +0530713 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530714 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530715
Shreya Shahbe690ef2017-11-14 17:22:41 +0530716 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530717
718 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530719 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530720
Subin Tom7d627df2021-08-23 11:05:07 +0530721 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530722 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
723 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530724
Deepesh Garg0ebace52020-02-25 13:21:16 +0530725 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530726 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530727 self.doc.append('payments', {
728 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530729 'amount': total_amount_to_pay,
730 'default': 1
Ankush Menatb147b852021-09-01 16:45:57 +0530731 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530732
Nabin Hait9c421612017-07-20 13:32:01 +0530733def get_itemised_tax_breakup_html(doc):
734 if not doc.taxes:
735 return
736 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530737
Nabin Hait9c421612017-07-20 13:32:01 +0530738 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530739 tax_accounts = []
740 for tax in doc.taxes:
741 if getattr(tax, "category", None) and tax.category=="Valuation":
742 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530743 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530744 tax_accounts.append(tax.description)
745
Nabin Hait9c421612017-07-20 13:32:01 +0530746 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530747
Nabin Hait9c421612017-07-20 13:32:01 +0530748 # get tax breakup data
749 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530750
751 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
752
rohitwaghchaured4526682017-12-28 14:20:13 +0530753 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530754 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530755
Nabin Hait9c421612017-07-20 13:32:01 +0530756 return frappe.render_template(
757 "templates/includes/itemised_tax_breakup.html", dict(
758 headers=headers,
759 itemised_tax=itemised_tax,
760 itemised_taxable_amount=itemised_taxable_amount,
761 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530762 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530763 )
Nabin Hait9c421612017-07-20 13:32:01 +0530764 )
Nabin Hait852cb642017-07-05 12:58:19 +0530765
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530766@frappe.whitelist()
767def get_round_off_applicable_accounts(company, account_list):
768 account_list = get_regional_round_off_accounts(company, account_list)
769
770 return account_list
771
772@erpnext.allow_regional
773def get_regional_round_off_accounts(company, account_list):
774 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530775
776@erpnext.allow_regional
777def update_itemised_tax_data(doc):
778 #Don't delete this method, used for localization
779 pass
780
Nabin Haitb962fc12017-07-17 18:02:31 +0530781@erpnext.allow_regional
782def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
783 return [_("Item"), _("Taxable Amount")] + tax_accounts
784
785@erpnext.allow_regional
786def get_itemised_tax_breakup_data(doc):
787 itemised_tax = get_itemised_tax(doc.taxes)
788
789 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
790
791 return itemised_tax, itemised_taxable_amount
792
Nabin Hait34c551d2019-07-03 10:34:31 +0530793def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530794 itemised_tax = {}
795 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530796 if getattr(tax, "category", None) and tax.category=="Valuation":
797 continue
798
Nabin Haitb962fc12017-07-17 18:02:31 +0530799 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530800 if item_tax_map:
801 for item_code, tax_data in item_tax_map.items():
802 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530803
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530804 tax_rate = 0.0
805 tax_amount = 0.0
806
Nabin Hait2e4de832017-09-19 14:53:16 +0530807 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530808 tax_rate = flt(tax_data[0])
809 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530810 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530811 tax_rate = flt(tax_data)
812
813 itemised_tax[item_code][tax.description] = frappe._dict(dict(
814 tax_rate = tax_rate,
815 tax_amount = tax_amount
816 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530817
Nabin Hait34c551d2019-07-03 10:34:31 +0530818 if with_tax_account:
819 itemised_tax[item_code][tax.description].tax_account = tax.account_head
820
Nabin Haitb962fc12017-07-17 18:02:31 +0530821 return itemised_tax
822
823def get_itemised_taxable_amount(items):
824 itemised_taxable_amount = frappe._dict()
825 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530826 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530827 itemised_taxable_amount.setdefault(item_code, 0)
828 itemised_taxable_amount[item_code] += item.net_amount
829
Nabin Haitcaab5822017-08-24 16:22:28 +0530830 return itemised_taxable_amount
831
832def get_rounded_tax_amount(itemised_tax, precision):
833 # Rounding based on tax_amount precision
834 for taxes in itemised_tax.values():
835 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530836 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530837
838class init_landed_taxes_and_totals(object):
839 def __init__(self, doc):
840 self.doc = doc
841 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
842 self.set_account_currency()
843 self.set_exchange_rate()
844 self.set_amounts_in_company_currency()
845
846 def set_account_currency(self):
847 company_currency = erpnext.get_company_currency(self.doc.company)
848 for d in self.doc.get(self.tax_field):
849 if not d.account_currency:
850 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
851 d.account_currency = account_currency or company_currency
852
853 def set_exchange_rate(self):
854 company_currency = erpnext.get_company_currency(self.doc.company)
855 for d in self.doc.get(self.tax_field):
856 if d.account_currency == company_currency:
857 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530858 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530859 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
860 account_currency=d.account_currency, company=self.doc.company)
861
862 if not d.exchange_rate:
863 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
864
865 def set_amounts_in_company_currency(self):
866 for d in self.doc.get(self.tax_field):
867 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530868 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))