blob: 26b8db4a81f19e7a4949acad1e304a7e096b8fcd [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):
Ankush Menat494bd9e2022-03-28 18:52:46 +053062 for item in self.doc.get("items"):
63 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053064 item_doc = frappe.get_cached_doc("Item", item.item_code)
65 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053066 "net_rate": item.net_rate or item.rate,
67 "tax_category": self.doc.get("tax_category"),
68 "posting_date": self.doc.get("posting_date"),
69 "bill_date": self.doc.get("bill_date"),
70 "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:
Ankush Menat494bd9e2022-03-28 18:52:46 +053078 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +053079 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]
Ankush Menat494bd9e2022-03-28 18:52:46 +053093 frappe.msgprint(
94 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
95 item.idx, frappe.bold(item.item_code)
96 )
97 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +053098
Nabin Haite7679702015-02-20 14:40:35 +053099 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530100 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530101 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530102 if not self.doc.currency or self.doc.currency == company_currency:
103 self.doc.currency = company_currency
104 self.doc.conversion_rate = 1.0
105 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530106 validate_conversion_rate(
107 self.doc.currency,
108 self.doc.conversion_rate,
109 self.doc.meta.get_label("conversion_rate"),
110 self.doc.company,
111 )
Nabin Hait3237c752015-02-17 11:11:11 +0530112
113 self.doc.conversion_rate = flt(self.doc.conversion_rate)
114
Nabin Hait3237c752015-02-17 11:11:11 +0530115 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530116 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530117 return
118
Nabin Hait3237c752015-02-17 11:11:11 +0530119 if not self.discount_amount_applied:
120 for item in self.doc.get("items"):
121 self.doc.round_floats_in(item)
122
123 if item.discount_percentage == 100:
124 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530125 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530126 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530127 item.rate = flt(
128 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
129 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530130
Deepesh Garga83a0a02022-03-25 12:28:55 +0530131 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530132
Deepesh Garg97e102c2022-03-25 12:39:59 +0530133 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530134 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530135
Ankush Menat494bd9e2022-03-28 18:52:46 +0530136 if item.doctype in [
137 "Quotation Item",
138 "Sales Order Item",
139 "Delivery Note Item",
140 "Sales Invoice Item",
141 "POS Invoice Item",
142 "Purchase Invoice Item",
143 "Purchase Order Item",
144 "Purchase Receipt Item",
145 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530146 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530147 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530148 item.rate = flt(
149 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
150 )
Nabin Hait10c61372021-04-13 15:46:01 +0530151
Walstan Baptista37b826b2021-04-03 19:48:46 +0530152 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530153 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530154 else:
155 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530156
Nabin Hait64bfdd92019-04-23 13:37:19 +0530157 elif flt(item.price_list_rate) > 0:
158 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530159 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
160 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530161
Nabin Haite7679702015-02-20 14:40:35 +0530162 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530163
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530164 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530165 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530166 elif not item.qty and self.doc.get("is_debit_note"):
167 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530168 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530169 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530170
Nabin Haite7679702015-02-20 14:40:35 +0530171 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530172
Ankush Menat494bd9e2022-03-28 18:52:46 +0530173 self._set_in_company_currency(
174 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
175 )
Nabin Hait3237c752015-02-17 11:11:11 +0530176
Nabin Haite7679702015-02-20 14:40:35 +0530177 item.item_tax_amount = 0.0
178
179 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530180 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530181 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530182 val = flt(
183 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
184 )
Nabin Haite7679702015-02-20 14:40:35 +0530185 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530186
187 def initialize_taxes(self):
188 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530189 if not self.discount_amount_applied:
190 validate_taxes_and_charges(tax)
191 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530192
Ankush Menat494bd9e2022-03-28 18:52:46 +0530193 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530194 tax.item_wise_tax_detail = {}
195
Ankush Menat494bd9e2022-03-28 18:52:46 +0530196 tax_fields = [
197 "total",
198 "tax_amount_after_discount_amount",
199 "tax_amount_for_current_item",
200 "grand_total_for_current_item",
201 "tax_fraction_for_current_item",
202 "grand_total_fraction_for_current_item",
203 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530204
Ankush Menat494bd9e2022-03-28 18:52:46 +0530205 if tax.charge_type != "Actual" and not (
206 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
207 ):
208 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530209
210 for fieldname in tax_fields:
211 tax.set(fieldname, 0.0)
212
Nabin Hait3237c752015-02-17 11:11:11 +0530213 self.doc.round_floats_in(tax)
214
Nabin Hait3237c752015-02-17 11:11:11 +0530215 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530216 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530217 return
Nabin Hait3237c752015-02-17 11:11:11 +0530218
219 for item in self.doc.get("items"):
220 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
221 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530222 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530223 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530224 (
225 tax.tax_fraction_for_current_item,
226 inclusive_tax_amount_per_qty,
227 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530228
Ankush Menat494bd9e2022-03-28 18:52:46 +0530229 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530230 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
231 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530232 tax.grand_total_fraction_for_current_item = (
233 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530234 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530235 )
Nabin Hait3237c752015-02-17 11:11:11 +0530236
237 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530238 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530239
Ankush Menat494bd9e2022-03-28 18:52:46 +0530240 if (
241 not self.discount_amount_applied
242 and item.qty
243 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
244 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530245 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
246
247 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530248 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530249 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530250
Nabin Haite7679702015-02-20 14:40:35 +0530251 self._set_in_company_currency(item, ["net_rate", "net_amount"])
252
Nabin Hait3237c752015-02-17 11:11:11 +0530253 def _load_item_tax_rate(self, item_tax_rate):
254 return json.loads(item_tax_rate) if item_tax_rate else {}
255
256 def get_current_tax_fraction(self, tax, item_tax_map):
257 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530258 Get tax fraction for calculating tax exclusive amount
259 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530260 """
261 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530262 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530263
264 if cint(tax.included_in_print_rate):
265 tax_rate = self._get_tax_rate(tax, item_tax_map)
266
267 if tax.charge_type == "On Net Total":
268 current_tax_fraction = tax_rate / 100.0
269
270 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530271 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
272 cint(tax.row_id) - 1
273 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530274
275 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530276 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
277 cint(tax.row_id) - 1
278 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530279
Nabin Hait19ea7212020-08-11 20:34:57 +0530280 elif tax.charge_type == "On Item Quantity":
281 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530282
Nabin Hait19ea7212020-08-11 20:34:57 +0530283 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
284 current_tax_fraction *= -1.0
285 inclusive_tax_amount_per_qty *= -1.0
286
287 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530288
289 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530290 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530291 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
292 else:
293 return tax.rate
294
295 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530296 self.doc.total_qty = (
297 self.doc.total
298 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530299
Nabin Hait3237c752015-02-17 11:11:11 +0530300 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530301 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530302 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530303 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530304 self.doc.net_total += item.net_amount
305 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530306
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530307 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530308
Subin Toma8e2c022021-11-16 19:06:49 +0530309 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530310
311 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530312 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530313 return
314
Subin Tomaf1fce02021-11-10 16:49:12 +0530315 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530316 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
317 shipping_rule.apply(self.doc)
318
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530319 self._calculate()
320
Nabin Hait3237c752015-02-17 11:11:11 +0530321 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530322 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
323 "rounding_adjustment"
324 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530325 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530326 self.doc.rounding_adjustment = 0
327
Nabin Hait3237c752015-02-17 11:11:11 +0530328 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530329 actual_tax_dict = dict(
330 [
331 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
332 for tax in self.doc.get("taxes")
333 if tax.charge_type == "Actual"
334 ]
335 )
Nabin Hait3237c752015-02-17 11:11:11 +0530336
337 for n, item in enumerate(self.doc.get("items")):
338 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530339 for i, tax in enumerate(self.doc.get("taxes")):
340 # tax_amount represents the amount of tax for the current step
341 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
342
343 # Adjust divisional loss to the last item
344 if tax.charge_type == "Actual":
345 actual_tax_dict[tax.idx] -= current_tax_amount
346 if n == len(self.doc.get("items")) - 1:
347 current_tax_amount += actual_tax_dict[tax.idx]
348
Nabin Hait2b019ed2015-02-22 23:03:07 +0530349 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530350 if tax.charge_type != "Actual" and not (
351 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
352 ):
353 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530354
Nabin Hait3237c752015-02-17 11:11:11 +0530355 # store tax_amount for current item as it will be used for
356 # charge type = 'On Previous Row Amount'
357 tax.tax_amount_for_current_item = current_tax_amount
358
Nabin Hait2b019ed2015-02-22 23:03:07 +0530359 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530360 tax.tax_amount_after_discount_amount += current_tax_amount
361
Nabin Haitcd951342017-07-31 18:07:45 +0530362 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530363
Nabin Hait3237c752015-02-17 11:11:11 +0530364 # note: grand_total_for_current_item contains the contribution of
365 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530366 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530367 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530368 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530369 tax.grand_total_for_current_item = flt(
370 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
371 )
Nabin Hait3237c752015-02-17 11:11:11 +0530372
373 # set precision in the last item iteration
374 if n == len(self.doc.get("items")) - 1:
375 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530376 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530377
378 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530379 self.set_cumulative_total(i, tax)
380
Deepesh Gargb6705882021-04-14 11:20:27 +0530381 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530382
Nabin Hait3237c752015-02-17 11:11:11 +0530383 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530384 if (
385 i == (len(self.doc.get("taxes")) - 1)
386 and self.discount_amount_applied
387 and self.doc.discount_amount
388 and self.doc.apply_discount_on == "Grand Total"
389 and not rounding_adjustment_computed
390 ):
391 self.doc.rounding_adjustment = flt(
392 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
393 self.doc.precision("rounding_adjustment"),
394 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530395
Nabin Haitcd951342017-07-31 18:07:45 +0530396 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
397 # if just for valuation, do not add the tax amount in total
398 # if tax/charges is for deduction, multiply by -1
399 if getattr(tax, "category", None):
400 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530401 if self.doc.doctype in [
402 "Purchase Order",
403 "Purchase Invoice",
404 "Purchase Receipt",
405 "Supplier Quotation",
406 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530407 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530408 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530409
Nabin Haitcd951342017-07-31 18:07:45 +0530410 def set_cumulative_total(self, row_idx, tax):
411 tax_amount = tax.tax_amount_after_discount_amount
412 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
413
414 if row_idx == 0:
415 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
416 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530417 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530418
419 def get_current_tax_amount(self, item, tax, item_tax_map):
420 tax_rate = self._get_tax_rate(tax, item_tax_map)
421 current_tax_amount = 0.0
422
423 if tax.charge_type == "Actual":
424 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530425 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530426 current_tax_amount = (
427 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
428 )
Nabin Haite7679702015-02-20 14:40:35 +0530429
Nabin Hait3237c752015-02-17 11:11:11 +0530430 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530431 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530432 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530433 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
434 cint(tax.row_id) - 1
435 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530436 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530437 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
438 cint(tax.row_id) - 1
439 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530440 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530441 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530442
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530443 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530444 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530445
446 return current_tax_amount
447
Nabin Haite7679702015-02-20 14:40:35 +0530448 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
449 # store tax breakup for each item
450 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530451 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Nabin Haite7679702015-02-20 14:40:35 +0530452 if tax.item_wise_tax_detail.get(key):
453 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
454
Ankush Menat494bd9e2022-03-28 18:52:46 +0530455 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530456
Nabin Hait3237c752015-02-17 11:11:11 +0530457 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530458 if tax.account_head in frappe.flags.round_off_applicable_accounts:
459 tax.tax_amount = round(tax.tax_amount, 0)
460 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
461
Nabin Haite7679702015-02-20 14:40:35 +0530462 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530463 tax.tax_amount_after_discount_amount = flt(
464 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
465 )
Nabin Haitce245122015-02-22 20:14:49 +0530466
Deepesh Gargb6705882021-04-14 11:20:27 +0530467 def round_off_base_values(self, tax):
468 # Round off to nearest integer based on regional settings
469 if tax.account_head in frappe.flags.round_off_applicable_accounts:
470 tax.base_tax_amount = round(tax.base_tax_amount, 0)
471 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
472
Nabin Haita1bf43b2015-03-17 10:50:47 +0530473 def manipulate_grand_total_for_inclusive_tax(self):
474 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530475 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 +0530476 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530477 non_inclusive_tax_amount = sum(
478 flt(d.tax_amount_after_discount_amount)
479 for d in self.doc.get("taxes")
480 if not d.included_in_print_rate
481 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530482
Ankush Menat494bd9e2022-03-28 18:52:46 +0530483 diff = (
484 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
485 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530486
487 # If discount amount applied, deduct the discount amount
488 # because self.doc.total is always without discount, but last_tax.total is after discount
489 if self.discount_amount_applied and self.doc.discount_amount:
490 diff -= flt(self.doc.discount_amount)
491
492 diff = flt(diff, self.doc.precision("rounding_adjustment"))
493
Ankush Menat494bd9e2022-03-28 18:52:46 +0530494 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530495 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530496
497 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530498 if self.doc.get("taxes"):
499 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
500 else:
501 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530502
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530503 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
504 self.doc.grand_total -= self.doc.discount_amount
505
Subin Tom75a76e62021-10-29 16:45:04 +0530506 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530507 self.doc.total_taxes_and_charges = flt(
508 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
509 self.doc.precision("total_taxes_and_charges"),
510 )
Subin Tom75a76e62021-10-29 16:45:04 +0530511 else:
512 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530513
Nabin Hait2e4de832017-09-19 14:53:16 +0530514 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530515
Ankush Menat494bd9e2022-03-28 18:52:46 +0530516 if self.doc.doctype in [
517 "Quotation",
518 "Sales Order",
519 "Delivery Note",
520 "Sales Invoice",
521 "POS Invoice",
522 ]:
523 self.doc.base_grand_total = (
524 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
525 if self.doc.total_taxes_and_charges
526 else self.doc.base_net_total
527 )
Nabin Hait3237c752015-02-17 11:11:11 +0530528 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530529 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530530 for tax in self.doc.get("taxes"):
531 if tax.category in ["Valuation and Total", "Total"]:
532 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530533 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530534 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530535 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530536
Nabin Haite7679702015-02-20 14:40:35 +0530537 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530538
Ankush Menat494bd9e2022-03-28 18:52:46 +0530539 self.doc.base_grand_total = (
540 flt(self.doc.grand_total * self.doc.conversion_rate)
541 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530542 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530543 )
Nabin Hait3237c752015-02-17 11:11:11 +0530544
Ankush Menat494bd9e2022-03-28 18:52:46 +0530545 self._set_in_company_currency(
546 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
547 )
Nabin Hait3237c752015-02-17 11:11:11 +0530548
Nabin Haite7679702015-02-20 14:40:35 +0530549 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530550
Nabin Hait2e4de832017-09-19 14:53:16 +0530551 self.set_rounded_total()
552
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530553 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530554 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530555 self.doc.total_net_weight = 0.0
556 for d in self.doc.items:
557 if d.total_weight:
558 self.doc.total_net_weight += d.total_weight
559
Nabin Hait2e4de832017-09-19 14:53:16 +0530560 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530561 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530562 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530563
Saqib Ansari17445c72022-03-07 18:01:07 +0530564 if self.doc.meta.get_field("rounded_total"):
565 if self.doc.is_rounded_total_disabled():
566 self.doc.rounded_total = self.doc.base_rounded_total = 0
567 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530568
Ankush Menat494bd9e2022-03-28 18:52:46 +0530569 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
570 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
571 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530572
Ankush Menat494bd9e2022-03-28 18:52:46 +0530573 # if print_in_rate is set, we would have already calculated rounding adjustment
574 self.doc.rounding_adjustment += flt(
575 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
576 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530577
578 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530579
Nabin Hait3237c752015-02-17 11:11:11 +0530580 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530581 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530582 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530583 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530584 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530585
Nabin Hait3769d872015-12-18 13:12:02 +0530586 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530587 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530588 self.doc.discount_amount = flt(
589 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
590 * self.doc.additional_discount_percentage
591 / 100,
592 self.doc.precision("discount_amount"),
593 )
Nabin Hait3237c752015-02-17 11:11:11 +0530594
595 def apply_discount_amount(self):
596 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530597 if not self.doc.apply_discount_on:
598 frappe.throw(_("Please select Apply Discount On"))
599
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530600 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
601 "is_cash_or_non_trade_discount"
602 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530603 self.discount_amount_applied = True
604 return
605
Ankush Menat494bd9e2022-03-28 18:52:46 +0530606 self.doc.base_discount_amount = flt(
607 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
608 )
Nabin Hait3237c752015-02-17 11:11:11 +0530609
Nabin Haite7679702015-02-20 14:40:35 +0530610 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530611 taxes = self.doc.get("taxes")
612 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530613
Nabin Haite7679702015-02-20 14:40:35 +0530614 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530615 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530616 for i, item in enumerate(self.doc.get("items")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530617 distributed_amount = (
618 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
619 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530620
Nabin Haite7679702015-02-20 14:40:35 +0530621 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530622 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530623
Nabin Hait25bd84d2015-03-04 15:06:56 +0530624 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530625 if (
626 self.doc.apply_discount_on == "Net Total"
627 or not taxes
628 or total_for_discount_amount == self.doc.net_total
629 ) and i == len(self.doc.get("items")) - 1:
630 discount_amount_loss = flt(
631 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
632 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530633
Ankush Menat494bd9e2022-03-28 18:52:46 +0530634 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530635
Nabin Hait51e980d2015-10-10 18:10:05 +0530636 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 +0530637
Nabin Haite7679702015-02-20 14:40:35 +0530638 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530639
640 self.discount_amount_applied = True
641 self._calculate()
642 else:
643 self.doc.base_discount_amount = 0
644
Nabin Haite7679702015-02-20 14:40:35 +0530645 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530646 if self.doc.apply_discount_on == "Net Total":
647 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530648 else:
649 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530650
Nabin Haite7679702015-02-20 14:40:35 +0530651 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530652 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530653 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
654 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530655 elif tax.row_id in actual_taxes_dict:
656 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
657 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530658
Ankush Menat494bd9e2022-03-28 18:52:46 +0530659 return flt(
660 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
661 )
Nabin Hait3237c752015-02-17 11:11:11 +0530662
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530663 def calculate_total_advance(self):
664 if self.doc.docstatus < 2:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530665 total_allocated_amount = sum(
666 flt(adv.allocated_amount, adv.precision("allocated_amount"))
667 for adv in self.doc.get("advances")
668 )
Nabin Hait3237c752015-02-17 11:11:11 +0530669
Nabin Haite7679702015-02-20 14:40:35 +0530670 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530671
Faris Ansari6041f5c2018-02-08 13:33:52 +0530672 grand_total = self.doc.rounded_total or self.doc.grand_total
673
Nabin Hait289ffb72016-02-08 11:06:55 +0530674 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530675 invoice_total = flt(
676 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
677 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530678 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530679 base_write_off_amount = flt(
680 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
681 self.doc.precision("base_write_off_amount"),
682 )
683 invoice_total = (
684 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
685 - base_write_off_amount
686 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530687
Nabin Haitadc09232016-02-09 10:31:11 +0530688 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530689 frappe.throw(
690 _("Advance amount cannot be greater than {0} {1}").format(
691 self.doc.party_account_currency, invoice_total
692 )
693 )
Nabin Hait3237c752015-02-17 11:11:11 +0530694
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530695 if self.doc.docstatus == 0:
Ankush Menat3821a972022-03-28 20:14:19 +0530696 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530697 self.doc.write_off_amount = 0
698
Nabin Hait3237c752015-02-17 11:11:11 +0530699 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530700 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530701
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530702 def is_internal_invoice(self):
703 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530704 Checks if its an internal transfer invoice
705 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530706 """
707
Ankush Menat494bd9e2022-03-28 18:52:46 +0530708 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530709 return True
710
711 return False
712
Nabin Hait3237c752015-02-17 11:11:11 +0530713 def calculate_outstanding_amount(self):
714 # NOTE:
715 # write_off_amount is only for POS Invoice
716 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530717 if self.doc.doctype == "Sales Invoice":
718 self.calculate_paid_amount()
719
Ankush Menat494bd9e2022-03-28 18:52:46 +0530720 if (
721 self.doc.is_return
722 and self.doc.return_against
723 and not self.doc.get("is_pos")
724 or self.is_internal_invoice()
725 ):
726 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530727
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530728 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530729 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530730
Nabin Hait877e1bb2017-11-17 12:27:43 +0530731 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
732 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530733 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
734
Nabin Hait877e1bb2017-11-17 12:27:43 +0530735 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530736 total_amount_to_pay = flt(
737 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
738 self.doc.precision("grand_total"),
739 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530740 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530741 total_amount_to_pay = flt(
742 flt(base_grand_total, self.doc.precision("base_grand_total"))
743 - self.doc.total_advance
744 - flt(self.doc.base_write_off_amount),
745 self.doc.precision("base_grand_total"),
746 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530747
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530748 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530749 change_amount = 0
750
Ankush Menat494bd9e2022-03-28 18:52:46 +0530751 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530752 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530753 change_amount = (
754 self.doc.change_amount
755 if self.doc.party_account_currency == self.doc.currency
756 else self.doc.base_change_amount
757 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530758
Ankush Menat494bd9e2022-03-28 18:52:46 +0530759 paid_amount = (
760 self.doc.paid_amount
761 if self.doc.party_account_currency == self.doc.currency
762 else self.doc.base_paid_amount
763 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530764
Ankush Menat494bd9e2022-03-28 18:52:46 +0530765 self.doc.outstanding_amount = flt(
766 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
767 self.doc.precision("outstanding_amount"),
768 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530769
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530770 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530771 self.doc.doctype == "Sales Invoice"
772 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530773 and self.doc.get("pos_profile")
774 and self.doc.get("is_consolidated")
775 ):
776 write_off_limit = flt(
777 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
778 )
779 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
780 self.doc.write_off_outstanding_amount_automatically = 1
781
782 if (
783 self.doc.doctype == "Sales Invoice"
784 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530785 and self.doc.get("is_return")
786 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530787 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530788 self.set_total_amount_to_default_mop(total_amount_to_pay)
789 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530790
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530791 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530792
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530793 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530794
795 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530796 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530797 payment.amount = flt(payment.amount)
798 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530799 paid_amount += payment.amount
800 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530801 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530802 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530803
Manas Solankida486ee2018-07-06 12:36:57 +0530804 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
805 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530806 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530807
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530808 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
809 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
810
Nabin Hait3bb1a422016-08-02 16:41:10 +0530811 def calculate_change_amount(self):
812 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530813 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530814 grand_total = self.doc.rounded_total or self.doc.grand_total
815 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530816
Ankush Menat494bd9e2022-03-28 18:52:46 +0530817 if (
818 self.doc.doctype == "Sales Invoice"
819 and self.doc.paid_amount > grand_total
820 and not self.doc.is_return
821 and any(d.type == "Cash" for d in self.doc.payments)
822 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530823 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530824 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530825 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530826
Ankush Menat494bd9e2022-03-28 18:52:46 +0530827 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530828 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530829 )
mbauskar36b51892016-01-18 16:31:10 +0530830
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530831 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530832 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530833 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530834 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530835 )
836 self.doc.base_write_off_amount = flt(
837 self.doc.write_off_amount * self.doc.conversion_rate,
838 self.doc.precision("base_write_off_amount"),
839 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530840
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530841 self.calculate_outstanding_amount()
842
mbauskar36b51892016-01-18 16:31:10 +0530843 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530844 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530845 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530846 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530847 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530848 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530849 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530850 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530851
Ankush Menat494bd9e2022-03-28 18:52:46 +0530852 if pricing_rule.margin_rate_or_amount and (
853 (
854 pricing_rule.currency == self.doc.currency
855 and pricing_rule.margin_type in ["Amount", "Percentage"]
856 )
857 or pricing_rule.margin_type == "Percentage"
858 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530859 item.margin_type = pricing_rule.margin_type
860 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530861 has_margin = True
862
863 if not has_margin:
864 item.margin_type = None
865 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530866
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530867 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
868 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530869 item.margin_rate_or_amount = flt(
870 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
871 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530872 item.rate_with_margin = item.rate
873
874 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530875 margin_value = (
876 item.margin_rate_or_amount
877 if item.margin_type == "Amount"
878 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
879 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530880 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530881 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530882
Shreya Shahbe690ef2017-11-14 17:22:41 +0530883 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530884
885 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530886 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530887
Subin Tom7d627df2021-08-23 11:05:07 +0530888 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530889 default_mode_of_payment = frappe.db.get_value(
890 "POS Payment Method",
891 {"parent": self.doc.pos_profile, "default": 1},
892 ["mode_of_payment"],
893 as_dict=1,
894 )
Deepesh Garg0ebace52020-02-25 13:21:16 +0530895
Deepesh Garg0ebace52020-02-25 13:21:16 +0530896 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530897 self.doc.payments = []
Ankush Menat494bd9e2022-03-28 18:52:46 +0530898 self.doc.append(
899 "payments",
900 {
901 "mode_of_payment": default_mode_of_payment.mode_of_payment,
902 "amount": total_amount_to_pay,
903 "default": 1,
904 },
905 )
906
Deepesh Garg0ebace52020-02-25 13:21:16 +0530907
Nabin Hait9c421612017-07-20 13:32:01 +0530908def get_itemised_tax_breakup_html(doc):
909 if not doc.taxes:
910 return
911 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530912
Nabin Hait9c421612017-07-20 13:32:01 +0530913 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530914 tax_accounts = []
915 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530916 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530917 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530918 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530919 tax_accounts.append(tax.description)
920
Nabin Hait9c421612017-07-20 13:32:01 +0530921 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530922
Nabin Hait9c421612017-07-20 13:32:01 +0530923 # get tax breakup data
924 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530925
926 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
927
rohitwaghchaured4526682017-12-28 14:20:13 +0530928 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530929 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530930
Nabin Hait9c421612017-07-20 13:32:01 +0530931 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530932 "templates/includes/itemised_tax_breakup.html",
933 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530934 headers=headers,
935 itemised_tax=itemised_tax,
936 itemised_taxable_amount=itemised_taxable_amount,
937 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530938 doc=doc,
939 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530940 )
Nabin Hait852cb642017-07-05 12:58:19 +0530941
Ankush Menat494bd9e2022-03-28 18:52:46 +0530942
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530943@frappe.whitelist()
944def get_round_off_applicable_accounts(company, account_list):
945 account_list = get_regional_round_off_accounts(company, account_list)
946
947 return account_list
948
Ankush Menat494bd9e2022-03-28 18:52:46 +0530949
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530950@erpnext.allow_regional
951def get_regional_round_off_accounts(company, account_list):
952 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530953
Ankush Menat494bd9e2022-03-28 18:52:46 +0530954
rohitwaghchaured4526682017-12-28 14:20:13 +0530955@erpnext.allow_regional
956def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530957 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +0530958 pass
959
Ankush Menat494bd9e2022-03-28 18:52:46 +0530960
Nabin Haitb962fc12017-07-17 18:02:31 +0530961@erpnext.allow_regional
962def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
963 return [_("Item"), _("Taxable Amount")] + tax_accounts
964
Ankush Menat494bd9e2022-03-28 18:52:46 +0530965
Nabin Haitb962fc12017-07-17 18:02:31 +0530966@erpnext.allow_regional
967def get_itemised_tax_breakup_data(doc):
968 itemised_tax = get_itemised_tax(doc.taxes)
969
970 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
971
972 return itemised_tax, itemised_taxable_amount
973
Ankush Menat494bd9e2022-03-28 18:52:46 +0530974
Nabin Hait34c551d2019-07-03 10:34:31 +0530975def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530976 itemised_tax = {}
977 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530978 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530979 continue
980
Nabin Haitb962fc12017-07-17 18:02:31 +0530981 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530982 if item_tax_map:
983 for item_code, tax_data in item_tax_map.items():
984 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530985
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530986 tax_rate = 0.0
987 tax_amount = 0.0
988
Nabin Hait2e4de832017-09-19 14:53:16 +0530989 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530990 tax_rate = flt(tax_data[0])
991 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530992 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530993 tax_rate = flt(tax_data)
994
Ankush Menat494bd9e2022-03-28 18:52:46 +0530995 itemised_tax[item_code][tax.description] = frappe._dict(
996 dict(tax_rate=tax_rate, tax_amount=tax_amount)
997 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530998
Nabin Hait34c551d2019-07-03 10:34:31 +0530999 if with_tax_account:
1000 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1001
Nabin Haitb962fc12017-07-17 18:02:31 +05301002 return itemised_tax
1003
Ankush Menat494bd9e2022-03-28 18:52:46 +05301004
Nabin Haitb962fc12017-07-17 18:02:31 +05301005def get_itemised_taxable_amount(items):
1006 itemised_taxable_amount = frappe._dict()
1007 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301008 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301009 itemised_taxable_amount.setdefault(item_code, 0)
1010 itemised_taxable_amount[item_code] += item.net_amount
1011
Nabin Haitcaab5822017-08-24 16:22:28 +05301012 return itemised_taxable_amount
1013
Ankush Menat494bd9e2022-03-28 18:52:46 +05301014
Nabin Haitcaab5822017-08-24 16:22:28 +05301015def get_rounded_tax_amount(itemised_tax, precision):
1016 # Rounding based on tax_amount precision
1017 for taxes in itemised_tax.values():
1018 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +05301019 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301020
Ankush Menat494bd9e2022-03-28 18:52:46 +05301021
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301022class init_landed_taxes_and_totals(object):
1023 def __init__(self, doc):
1024 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301025 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301026 self.set_account_currency()
1027 self.set_exchange_rate()
1028 self.set_amounts_in_company_currency()
1029
1030 def set_account_currency(self):
1031 company_currency = erpnext.get_company_currency(self.doc.company)
1032 for d in self.doc.get(self.tax_field):
1033 if not d.account_currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301034 account_currency = frappe.db.get_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301035 d.account_currency = account_currency or company_currency
1036
1037 def set_exchange_rate(self):
1038 company_currency = erpnext.get_company_currency(self.doc.company)
1039 for d in self.doc.get(self.tax_field):
1040 if d.account_currency == company_currency:
1041 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301042 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301043 d.exchange_rate = get_exchange_rate(
1044 self.doc.posting_date,
1045 account=d.expense_account,
1046 account_currency=d.account_currency,
1047 company=self.doc.company,
1048 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301049
1050 if not d.exchange_rate:
1051 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1052
1053 def set_amounts_in_company_currency(self):
1054 for d in self.doc.get(self.tax_field):
1055 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +05301056 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))