blob: 8c403aa9bfe252386be2e5166f5e638ba13a0d8d [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
Raffael Meyer67cf7e12023-01-15 13:04:16 +01009from frappe.model.document import Document
Nabin Haitb962fc12017-07-17 18:02:31 +053010from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
Chillar Anand915b3432021-09-02 16:44:59 +053011
12import erpnext
Deepesh Gargbfc17e42020-12-25 18:34:39 +053013from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Chillar Anand915b3432021-09-02 16:44:59 +053014from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
15from erpnext.controllers.accounts_controller import (
16 validate_conversion_rate,
17 validate_inclusive_tax,
18 validate_taxes_and_charges,
19)
20from erpnext.stock.get_item_details import _get_item_tax_template
21
Nabin Hait3237c752015-02-17 11:11:11 +053022
Nabin Haitfe81da22015-02-18 12:23:18 +053023class calculate_taxes_and_totals(object):
Raffael Meyer67cf7e12023-01-15 13:04:16 +010024 def __init__(self, doc: Document):
Nabin Hait3237c752015-02-17 11:11:11 +053025 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053026 frappe.flags.round_off_applicable_accounts = []
27 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053028 self.calculate()
29
Nabin Hait3237c752015-02-17 11:11:11 +053030 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053031 if not len(self.doc.get("items")):
32 return
33
Nabin Hait3237c752015-02-17 11:11:11 +053034 self.discount_amount_applied = False
35 self._calculate()
36
37 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053038 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053039 self.apply_discount_amount()
40
Deepesh Garg3b159662022-08-21 17:51:05 +053041 # Update grand total as per cash and non trade discount
42 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
43 self.doc.grand_total -= self.doc.discount_amount
44 self.doc.base_grand_total -= self.doc.base_discount_amount
Deepesh Garg318da162022-08-29 14:18:39 +053045 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053046
Deepesh Gargd596e0e2022-03-10 20:56:36 +053047 self.calculate_shipping_charges()
48
Nabin Haitbd00e812015-02-17 12:50:51 +053049 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053050 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053051
Nabin Hait852cb642017-07-05 12:58:19 +053052 if self.doc.meta.get_field("other_charges_calculation"):
53 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053054
55 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053056 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053057 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053058 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053059 self.initialize_taxes()
60 self.determine_exclusive_rate()
61 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053062 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053063 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053064 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053065 self.calculate_totals()
66 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053067 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053068
niralisataparae758a752022-10-03 16:39:35 +053069 def calculate_tax_withholding_net_total(self):
70 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053071 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053072 sum_base_net_amount = 0
niralisataparae758a752022-10-03 16:39:35 +053073 for item in self.doc.get("items"):
74 if hasattr(item, "apply_tds") and item.apply_tds:
75 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053076 sum_base_net_amount += item.base_net_amount
77
niralisataparae758a752022-10-03 16:39:35 +053078 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053079 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053080
Deepesh Gargef0d26c2020-01-06 15:34:15 +053081 def validate_item_tax_template(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +053082 for item in self.doc.get("items"):
83 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053084 item_doc = frappe.get_cached_doc("Item", item.item_code)
85 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053086 "net_rate": item.net_rate or item.rate,
87 "tax_category": self.doc.get("tax_category"),
88 "posting_date": self.doc.get("posting_date"),
89 "bill_date": self.doc.get("bill_date"),
90 "transaction_date": self.doc.get("transaction_date"),
91 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +053092 }
93
94 item_group = item_doc.item_group
95 item_group_taxes = []
96
97 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +053098 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +053099 item_group_taxes += item_group_doc.taxes or []
100 item_group = item_group_doc.parent_item_group
101
102 item_taxes = item_doc.taxes or []
103
104 if not item_group_taxes and (not item_taxes):
105 # No validation if no taxes in item or item group
106 continue
107
108 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
109
Deepesh Garg18be7672021-06-06 13:25:34 +0530110 if taxes:
111 if item.item_tax_template not in taxes:
112 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530113 frappe.msgprint(
114 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
115 item.idx, frappe.bold(item.item_code)
116 )
117 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530118
Nabin Haite7679702015-02-20 14:40:35 +0530119 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530120 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530121 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530122 if not self.doc.currency or self.doc.currency == company_currency:
123 self.doc.currency = company_currency
124 self.doc.conversion_rate = 1.0
125 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530126 validate_conversion_rate(
127 self.doc.currency,
128 self.doc.conversion_rate,
129 self.doc.meta.get_label("conversion_rate"),
130 self.doc.company,
131 )
Nabin Hait3237c752015-02-17 11:11:11 +0530132
133 self.doc.conversion_rate = flt(self.doc.conversion_rate)
134
Nabin Hait3237c752015-02-17 11:11:11 +0530135 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530136 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530137 return
138
Nabin Hait3237c752015-02-17 11:11:11 +0530139 if not self.discount_amount_applied:
140 for item in self.doc.get("items"):
141 self.doc.round_floats_in(item)
142
143 if item.discount_percentage == 100:
144 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530145 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530146 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530147 item.rate = flt(
148 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
149 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530150
Deepesh Garga83a0a02022-03-25 12:28:55 +0530151 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530152
Deepesh Garg97e102c2022-03-25 12:39:59 +0530153 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530154 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530155
Ankush Menat494bd9e2022-03-28 18:52:46 +0530156 if item.doctype in [
157 "Quotation Item",
158 "Sales Order Item",
159 "Delivery Note Item",
160 "Sales Invoice Item",
161 "POS Invoice Item",
162 "Purchase Invoice Item",
163 "Purchase Order Item",
164 "Purchase Receipt Item",
165 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530166 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530167 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530168 item.rate = flt(
169 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
170 )
Nabin Hait10c61372021-04-13 15:46:01 +0530171
Walstan Baptista37b826b2021-04-03 19:48:46 +0530172 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530173 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530174 else:
175 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530176
Nabin Hait64bfdd92019-04-23 13:37:19 +0530177 elif flt(item.price_list_rate) > 0:
178 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530179 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
180 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530181
Nabin Haite7679702015-02-20 14:40:35 +0530182 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530183
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530184 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530185 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530186 elif not item.qty and self.doc.get("is_debit_note"):
187 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530188 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530189 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530190
Nabin Haite7679702015-02-20 14:40:35 +0530191 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530192
Ankush Menat494bd9e2022-03-28 18:52:46 +0530193 self._set_in_company_currency(
194 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
195 )
Nabin Hait3237c752015-02-17 11:11:11 +0530196
Nabin Haite7679702015-02-20 14:40:35 +0530197 item.item_tax_amount = 0.0
198
199 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530200 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530201 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530202 val = flt(
203 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
204 )
Nabin Haite7679702015-02-20 14:40:35 +0530205 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530206
207 def initialize_taxes(self):
208 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530209 if not self.discount_amount_applied:
210 validate_taxes_and_charges(tax)
211 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530212
Ankush Menat494bd9e2022-03-28 18:52:46 +0530213 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530214 tax.item_wise_tax_detail = {}
215
Ankush Menat494bd9e2022-03-28 18:52:46 +0530216 tax_fields = [
217 "total",
218 "tax_amount_after_discount_amount",
219 "tax_amount_for_current_item",
220 "grand_total_for_current_item",
221 "tax_fraction_for_current_item",
222 "grand_total_fraction_for_current_item",
223 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530224
Ankush Menat494bd9e2022-03-28 18:52:46 +0530225 if tax.charge_type != "Actual" and not (
226 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
227 ):
228 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530229
230 for fieldname in tax_fields:
231 tax.set(fieldname, 0.0)
232
Nabin Hait3237c752015-02-17 11:11:11 +0530233 self.doc.round_floats_in(tax)
234
Nabin Hait3237c752015-02-17 11:11:11 +0530235 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530236 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530237 return
Nabin Hait3237c752015-02-17 11:11:11 +0530238
239 for item in self.doc.get("items"):
240 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
241 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530242 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530243 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530244 (
245 tax.tax_fraction_for_current_item,
246 inclusive_tax_amount_per_qty,
247 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530248
Ankush Menat494bd9e2022-03-28 18:52:46 +0530249 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530250 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
251 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530252 tax.grand_total_fraction_for_current_item = (
253 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530254 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530255 )
Nabin Hait3237c752015-02-17 11:11:11 +0530256
257 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530258 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530259
Ankush Menat494bd9e2022-03-28 18:52:46 +0530260 if (
261 not self.discount_amount_applied
262 and item.qty
263 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
264 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530265 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
266
267 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530268 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530269 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530270
Nabin Haite7679702015-02-20 14:40:35 +0530271 self._set_in_company_currency(item, ["net_rate", "net_amount"])
272
Nabin Hait3237c752015-02-17 11:11:11 +0530273 def _load_item_tax_rate(self, item_tax_rate):
274 return json.loads(item_tax_rate) if item_tax_rate else {}
275
276 def get_current_tax_fraction(self, tax, item_tax_map):
277 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530278 Get tax fraction for calculating tax exclusive amount
279 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530280 """
281 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530282 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530283
284 if cint(tax.included_in_print_rate):
285 tax_rate = self._get_tax_rate(tax, item_tax_map)
286
287 if tax.charge_type == "On Net Total":
288 current_tax_fraction = tax_rate / 100.0
289
290 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530291 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
292 cint(tax.row_id) - 1
293 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530294
295 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530296 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
297 cint(tax.row_id) - 1
298 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530299
Nabin Hait19ea7212020-08-11 20:34:57 +0530300 elif tax.charge_type == "On Item Quantity":
301 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530302
Nabin Hait19ea7212020-08-11 20:34:57 +0530303 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
304 current_tax_fraction *= -1.0
305 inclusive_tax_amount_per_qty *= -1.0
306
307 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530308
309 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530310 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530311 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
312 else:
313 return tax.rate
314
315 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530316 self.doc.total_qty = (
317 self.doc.total
318 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530319
Nabin Hait3237c752015-02-17 11:11:11 +0530320 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530321 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530322 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530323 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530324 self.doc.net_total += item.net_amount
325 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530326
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530327 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530328
Subin Toma8e2c022021-11-16 19:06:49 +0530329 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530330
331 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530332 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530333 return
334
Subin Tomaf1fce02021-11-10 16:49:12 +0530335 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530336 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
337 shipping_rule.apply(self.doc)
338
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530339 self._calculate()
340
Nabin Hait3237c752015-02-17 11:11:11 +0530341 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530342 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
343 "rounding_adjustment"
344 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530345 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530346 self.doc.rounding_adjustment = 0
347
Nabin Hait3237c752015-02-17 11:11:11 +0530348 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530349 actual_tax_dict = dict(
350 [
351 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
352 for tax in self.doc.get("taxes")
353 if tax.charge_type == "Actual"
354 ]
355 )
Nabin Hait3237c752015-02-17 11:11:11 +0530356
357 for n, item in enumerate(self.doc.get("items")):
358 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530359 for i, tax in enumerate(self.doc.get("taxes")):
360 # tax_amount represents the amount of tax for the current step
361 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
362
363 # Adjust divisional loss to the last item
364 if tax.charge_type == "Actual":
365 actual_tax_dict[tax.idx] -= current_tax_amount
366 if n == len(self.doc.get("items")) - 1:
367 current_tax_amount += actual_tax_dict[tax.idx]
368
Nabin Hait2b019ed2015-02-22 23:03:07 +0530369 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530370 if tax.charge_type != "Actual" and not (
371 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
372 ):
373 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530374
Nabin Hait3237c752015-02-17 11:11:11 +0530375 # store tax_amount for current item as it will be used for
376 # charge type = 'On Previous Row Amount'
377 tax.tax_amount_for_current_item = current_tax_amount
378
Nabin Hait2b019ed2015-02-22 23:03:07 +0530379 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530380 tax.tax_amount_after_discount_amount += current_tax_amount
381
Nabin Haitcd951342017-07-31 18:07:45 +0530382 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530383
Nabin Hait3237c752015-02-17 11:11:11 +0530384 # note: grand_total_for_current_item contains the contribution of
385 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530386 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530387 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530388 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530389 tax.grand_total_for_current_item = flt(
390 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
391 )
Nabin Hait3237c752015-02-17 11:11:11 +0530392
393 # set precision in the last item iteration
394 if n == len(self.doc.get("items")) - 1:
395 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530396 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530397
398 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530399 self.set_cumulative_total(i, tax)
400
Deepesh Gargb6705882021-04-14 11:20:27 +0530401 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530402
Nabin Hait3237c752015-02-17 11:11:11 +0530403 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530404 if (
405 i == (len(self.doc.get("taxes")) - 1)
406 and self.discount_amount_applied
407 and self.doc.discount_amount
408 and self.doc.apply_discount_on == "Grand Total"
409 and not rounding_adjustment_computed
410 ):
411 self.doc.rounding_adjustment = flt(
412 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
413 self.doc.precision("rounding_adjustment"),
414 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530415
Nabin Haitcd951342017-07-31 18:07:45 +0530416 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
417 # if just for valuation, do not add the tax amount in total
418 # if tax/charges is for deduction, multiply by -1
419 if getattr(tax, "category", None):
420 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530421 if self.doc.doctype in [
422 "Purchase Order",
423 "Purchase Invoice",
424 "Purchase Receipt",
425 "Supplier Quotation",
426 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530427 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530428 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530429
Nabin Haitcd951342017-07-31 18:07:45 +0530430 def set_cumulative_total(self, row_idx, tax):
431 tax_amount = tax.tax_amount_after_discount_amount
432 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
433
434 if row_idx == 0:
435 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
436 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530437 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530438
439 def get_current_tax_amount(self, item, tax, item_tax_map):
440 tax_rate = self._get_tax_rate(tax, item_tax_map)
441 current_tax_amount = 0.0
442
443 if tax.charge_type == "Actual":
444 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530445 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530446 current_tax_amount = (
447 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
448 )
Nabin Haite7679702015-02-20 14:40:35 +0530449
Nabin Hait3237c752015-02-17 11:11:11 +0530450 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530451 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530452 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530453 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
454 cint(tax.row_id) - 1
455 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530456 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530457 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
458 cint(tax.row_id) - 1
459 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530460 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530461 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530462
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530463 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530464 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530465
466 return current_tax_amount
467
Nabin Haite7679702015-02-20 14:40:35 +0530468 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
469 # store tax breakup for each item
470 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530471 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Nabin Haite7679702015-02-20 14:40:35 +0530472 if tax.item_wise_tax_detail.get(key):
473 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
474
Ankush Menat494bd9e2022-03-28 18:52:46 +0530475 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530476
Nabin Hait3237c752015-02-17 11:11:11 +0530477 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530478 if tax.account_head in frappe.flags.round_off_applicable_accounts:
479 tax.tax_amount = round(tax.tax_amount, 0)
480 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
481
Nabin Haite7679702015-02-20 14:40:35 +0530482 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530483 tax.tax_amount_after_discount_amount = flt(
484 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
485 )
Nabin Haitce245122015-02-22 20:14:49 +0530486
Deepesh Gargb6705882021-04-14 11:20:27 +0530487 def round_off_base_values(self, tax):
488 # Round off to nearest integer based on regional settings
489 if tax.account_head in frappe.flags.round_off_applicable_accounts:
490 tax.base_tax_amount = round(tax.base_tax_amount, 0)
491 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
492
Nabin Haita1bf43b2015-03-17 10:50:47 +0530493 def manipulate_grand_total_for_inclusive_tax(self):
494 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530495 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 +0530496 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530497 non_inclusive_tax_amount = sum(
498 flt(d.tax_amount_after_discount_amount)
499 for d in self.doc.get("taxes")
500 if not d.included_in_print_rate
501 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530502
Ankush Menat494bd9e2022-03-28 18:52:46 +0530503 diff = (
504 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
505 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530506
507 # If discount amount applied, deduct the discount amount
508 # because self.doc.total is always without discount, but last_tax.total is after discount
509 if self.discount_amount_applied and self.doc.discount_amount:
510 diff -= flt(self.doc.discount_amount)
511
512 diff = flt(diff, self.doc.precision("rounding_adjustment"))
513
Ankush Menat494bd9e2022-03-28 18:52:46 +0530514 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530515 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530516
517 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530518 if self.doc.get("taxes"):
519 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
520 else:
521 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530522
Subin Tom75a76e62021-10-29 16:45:04 +0530523 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530524 self.doc.total_taxes_and_charges = flt(
525 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
526 self.doc.precision("total_taxes_and_charges"),
527 )
Subin Tom75a76e62021-10-29 16:45:04 +0530528 else:
529 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530530
Nabin Hait2e4de832017-09-19 14:53:16 +0530531 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530532
Ankush Menat494bd9e2022-03-28 18:52:46 +0530533 if self.doc.doctype in [
534 "Quotation",
535 "Sales Order",
536 "Delivery Note",
537 "Sales Invoice",
538 "POS Invoice",
539 ]:
540 self.doc.base_grand_total = (
541 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
542 if self.doc.total_taxes_and_charges
543 else self.doc.base_net_total
544 )
Nabin Hait3237c752015-02-17 11:11:11 +0530545 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530546 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530547 for tax in self.doc.get("taxes"):
548 if tax.category in ["Valuation and Total", "Total"]:
549 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530550 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530551 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530552 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530553
Nabin Haite7679702015-02-20 14:40:35 +0530554 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530555
Ankush Menat494bd9e2022-03-28 18:52:46 +0530556 self.doc.base_grand_total = (
557 flt(self.doc.grand_total * self.doc.conversion_rate)
558 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530559 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530560 )
Nabin Hait3237c752015-02-17 11:11:11 +0530561
Ankush Menat494bd9e2022-03-28 18:52:46 +0530562 self._set_in_company_currency(
563 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
564 )
Nabin Hait3237c752015-02-17 11:11:11 +0530565
Nabin Haite7679702015-02-20 14:40:35 +0530566 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530567
Nabin Hait2e4de832017-09-19 14:53:16 +0530568 self.set_rounded_total()
569
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530570 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530571 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530572 self.doc.total_net_weight = 0.0
573 for d in self.doc.items:
574 if d.total_weight:
575 self.doc.total_net_weight += d.total_weight
576
Nabin Hait2e4de832017-09-19 14:53:16 +0530577 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530578 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530579 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530580
Saqib Ansari17445c72022-03-07 18:01:07 +0530581 if self.doc.meta.get_field("rounded_total"):
582 if self.doc.is_rounded_total_disabled():
583 self.doc.rounded_total = self.doc.base_rounded_total = 0
584 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530585
Ankush Menat494bd9e2022-03-28 18:52:46 +0530586 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
587 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
588 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530589
Ankush Menat494bd9e2022-03-28 18:52:46 +0530590 # if print_in_rate is set, we would have already calculated rounding adjustment
591 self.doc.rounding_adjustment += flt(
592 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
593 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530594
595 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530596
Nabin Hait3237c752015-02-17 11:11:11 +0530597 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530598 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530599 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530600 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530601 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530602
Nabin Hait3769d872015-12-18 13:12:02 +0530603 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530604 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530605 self.doc.discount_amount = flt(
606 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
607 * self.doc.additional_discount_percentage
608 / 100,
609 self.doc.precision("discount_amount"),
610 )
Nabin Hait3237c752015-02-17 11:11:11 +0530611
612 def apply_discount_amount(self):
613 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530614 if not self.doc.apply_discount_on:
615 frappe.throw(_("Please select Apply Discount On"))
616
Deepesh Garg3b159662022-08-21 17:51:05 +0530617 self.doc.base_discount_amount = flt(
618 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
619 )
620
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530621 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
622 "is_cash_or_non_trade_discount"
623 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530624 self.discount_amount_applied = True
625 return
626
Nabin Haite7679702015-02-20 14:40:35 +0530627 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530628 taxes = self.doc.get("taxes")
629 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530630
Nabin Haite7679702015-02-20 14:40:35 +0530631 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530632 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530633 for i, item in enumerate(self.doc.get("items")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530634 distributed_amount = (
635 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
636 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530637
Nabin Haite7679702015-02-20 14:40:35 +0530638 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530639 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530640
Nabin Hait25bd84d2015-03-04 15:06:56 +0530641 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530642 if (
643 self.doc.apply_discount_on == "Net Total"
644 or not taxes
645 or total_for_discount_amount == self.doc.net_total
646 ) and i == len(self.doc.get("items")) - 1:
647 discount_amount_loss = flt(
648 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
649 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530650
Ankush Menat494bd9e2022-03-28 18:52:46 +0530651 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530652
Nabin Hait51e980d2015-10-10 18:10:05 +0530653 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 +0530654
Nabin Haite7679702015-02-20 14:40:35 +0530655 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530656
657 self.discount_amount_applied = True
658 self._calculate()
659 else:
660 self.doc.base_discount_amount = 0
661
Nabin Haite7679702015-02-20 14:40:35 +0530662 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530663 if self.doc.apply_discount_on == "Net Total":
664 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530665 else:
666 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530667
Nabin Haite7679702015-02-20 14:40:35 +0530668 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530669 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530670 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
671 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530672 elif tax.row_id in actual_taxes_dict:
673 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
674 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530675
Ankush Menat494bd9e2022-03-28 18:52:46 +0530676 return flt(
677 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
678 )
Nabin Hait3237c752015-02-17 11:11:11 +0530679
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530680 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100681 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530682 total_allocated_amount = sum(
683 flt(adv.allocated_amount, adv.precision("allocated_amount"))
684 for adv in self.doc.get("advances")
685 )
Nabin Hait3237c752015-02-17 11:11:11 +0530686
Nabin Haite7679702015-02-20 14:40:35 +0530687 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530688
Faris Ansari6041f5c2018-02-08 13:33:52 +0530689 grand_total = self.doc.rounded_total or self.doc.grand_total
690
Nabin Hait289ffb72016-02-08 11:06:55 +0530691 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530692 invoice_total = flt(
693 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
694 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530695 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530696 base_write_off_amount = flt(
697 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
698 self.doc.precision("base_write_off_amount"),
699 )
700 invoice_total = (
701 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
702 - base_write_off_amount
703 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530704
Nabin Haitadc09232016-02-09 10:31:11 +0530705 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530706 frappe.throw(
707 _("Advance amount cannot be greater than {0} {1}").format(
708 self.doc.party_account_currency, invoice_total
709 )
710 )
Nabin Hait3237c752015-02-17 11:11:11 +0530711
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100712 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530713 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530714 self.doc.write_off_amount = 0
715
Nabin Hait3237c752015-02-17 11:11:11 +0530716 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530717 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530718
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530719 def is_internal_invoice(self):
720 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530721 Checks if its an internal transfer invoice
722 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530723 """
724
Ankush Menat494bd9e2022-03-28 18:52:46 +0530725 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530726 return True
727
728 return False
729
Nabin Hait3237c752015-02-17 11:11:11 +0530730 def calculate_outstanding_amount(self):
731 # NOTE:
732 # write_off_amount is only for POS Invoice
733 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530734 if self.doc.doctype == "Sales Invoice":
735 self.calculate_paid_amount()
736
Ankush Menat494bd9e2022-03-28 18:52:46 +0530737 if (
738 self.doc.is_return
739 and self.doc.return_against
740 and not self.doc.get("is_pos")
741 or self.is_internal_invoice()
742 ):
743 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530744
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530745 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530746 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530747
Nabin Hait877e1bb2017-11-17 12:27:43 +0530748 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
749 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530750 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
751
Nabin Hait877e1bb2017-11-17 12:27:43 +0530752 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530753 total_amount_to_pay = flt(
754 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
755 self.doc.precision("grand_total"),
756 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530757 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530758 total_amount_to_pay = flt(
759 flt(base_grand_total, self.doc.precision("base_grand_total"))
760 - self.doc.total_advance
761 - flt(self.doc.base_write_off_amount),
762 self.doc.precision("base_grand_total"),
763 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530764
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530765 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530766 change_amount = 0
767
Ankush Menat494bd9e2022-03-28 18:52:46 +0530768 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530769 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530770 change_amount = (
771 self.doc.change_amount
772 if self.doc.party_account_currency == self.doc.currency
773 else self.doc.base_change_amount
774 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530775
Ankush Menat494bd9e2022-03-28 18:52:46 +0530776 paid_amount = (
777 self.doc.paid_amount
778 if self.doc.party_account_currency == self.doc.currency
779 else self.doc.base_paid_amount
780 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530781
Ankush Menat494bd9e2022-03-28 18:52:46 +0530782 self.doc.outstanding_amount = flt(
783 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
784 self.doc.precision("outstanding_amount"),
785 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530786
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530787 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530788 self.doc.doctype == "Sales Invoice"
789 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530790 and self.doc.get("pos_profile")
791 and self.doc.get("is_consolidated")
792 ):
793 write_off_limit = flt(
794 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
795 )
796 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
797 self.doc.write_off_outstanding_amount_automatically = 1
798
799 if (
800 self.doc.doctype == "Sales Invoice"
801 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530802 and self.doc.get("is_return")
803 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530804 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530805 self.set_total_amount_to_default_mop(total_amount_to_pay)
806 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530807
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530808 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530809
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530810 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530811
812 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530813 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530814 payment.amount = flt(payment.amount)
815 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530816 paid_amount += payment.amount
817 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530818 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530819 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530820
Manas Solankida486ee2018-07-06 12:36:57 +0530821 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
822 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530823 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530824
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530825 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
826 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
827
Nabin Hait3bb1a422016-08-02 16:41:10 +0530828 def calculate_change_amount(self):
829 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530830 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530831 grand_total = self.doc.rounded_total or self.doc.grand_total
832 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530833
Ankush Menat494bd9e2022-03-28 18:52:46 +0530834 if (
835 self.doc.doctype == "Sales Invoice"
836 and self.doc.paid_amount > grand_total
837 and not self.doc.is_return
838 and any(d.type == "Cash" for d in self.doc.payments)
839 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530840 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530841 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530842 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530843
Ankush Menat494bd9e2022-03-28 18:52:46 +0530844 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530845 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530846 )
mbauskar36b51892016-01-18 16:31:10 +0530847
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530848 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530849 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530850 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530851 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530852 )
853 self.doc.base_write_off_amount = flt(
854 self.doc.write_off_amount * self.doc.conversion_rate,
855 self.doc.precision("base_write_off_amount"),
856 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530857
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530858 self.calculate_outstanding_amount()
859
mbauskar36b51892016-01-18 16:31:10 +0530860 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530861 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530862 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530863 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530864 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530865 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530866 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530867 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530868
Ankush Menat494bd9e2022-03-28 18:52:46 +0530869 if pricing_rule.margin_rate_or_amount and (
870 (
871 pricing_rule.currency == self.doc.currency
872 and pricing_rule.margin_type in ["Amount", "Percentage"]
873 )
874 or pricing_rule.margin_type == "Percentage"
875 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530876 item.margin_type = pricing_rule.margin_type
877 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530878 has_margin = True
879
880 if not has_margin:
881 item.margin_type = None
882 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530883
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530884 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
885 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530886 item.margin_rate_or_amount = flt(
887 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
888 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530889 item.rate_with_margin = item.rate
890
891 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530892 margin_value = (
893 item.margin_rate_or_amount
894 if item.margin_type == "Amount"
895 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
896 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530897 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530898 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530899
Shreya Shahbe690ef2017-11-14 17:22:41 +0530900 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530901
902 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530903 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530904
Subin Tom7d627df2021-08-23 11:05:07 +0530905 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530906 total_paid_amount = 0
907 for payment in self.doc.get("payments"):
908 total_paid_amount += (
909 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530910 )
911
Deepesh Garg06e8e282022-10-31 19:58:46 +0530912 pending_amount = total_amount_to_pay - total_paid_amount
913
914 if pending_amount > 0:
915 default_mode_of_payment = frappe.db.get_value(
916 "POS Payment Method",
917 {"parent": self.doc.pos_profile, "default": 1},
918 ["mode_of_payment"],
919 as_dict=1,
920 )
921
922 if default_mode_of_payment:
923 self.doc.payments = []
924 self.doc.append(
925 "payments",
926 {
927 "mode_of_payment": default_mode_of_payment.mode_of_payment,
928 "amount": pending_amount,
929 "default": 1,
930 },
931 )
932
Deepesh Garg0ebace52020-02-25 13:21:16 +0530933
Nabin Hait9c421612017-07-20 13:32:01 +0530934def get_itemised_tax_breakup_html(doc):
935 if not doc.taxes:
936 return
937 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530938
Nabin Hait9c421612017-07-20 13:32:01 +0530939 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530940 tax_accounts = []
941 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530942 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530943 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530944 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530945 tax_accounts.append(tax.description)
946
Nabin Hait9c421612017-07-20 13:32:01 +0530947 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530948
Nabin Hait9c421612017-07-20 13:32:01 +0530949 # get tax breakup data
950 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530951
952 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
953
rohitwaghchaured4526682017-12-28 14:20:13 +0530954 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530955 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530956
Nabin Hait9c421612017-07-20 13:32:01 +0530957 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530958 "templates/includes/itemised_tax_breakup.html",
959 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530960 headers=headers,
961 itemised_tax=itemised_tax,
962 itemised_taxable_amount=itemised_taxable_amount,
963 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530964 doc=doc,
965 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530966 )
Nabin Hait852cb642017-07-05 12:58:19 +0530967
Ankush Menat494bd9e2022-03-28 18:52:46 +0530968
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530969@frappe.whitelist()
970def get_round_off_applicable_accounts(company, account_list):
971 account_list = get_regional_round_off_accounts(company, account_list)
972
973 return account_list
974
Ankush Menat494bd9e2022-03-28 18:52:46 +0530975
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530976@erpnext.allow_regional
977def get_regional_round_off_accounts(company, account_list):
978 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530979
Ankush Menat494bd9e2022-03-28 18:52:46 +0530980
rohitwaghchaured4526682017-12-28 14:20:13 +0530981@erpnext.allow_regional
982def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530983 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +0530984 pass
985
Ankush Menat494bd9e2022-03-28 18:52:46 +0530986
Nabin Haitb962fc12017-07-17 18:02:31 +0530987@erpnext.allow_regional
988def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
989 return [_("Item"), _("Taxable Amount")] + tax_accounts
990
Ankush Menat494bd9e2022-03-28 18:52:46 +0530991
Nabin Haitb962fc12017-07-17 18:02:31 +0530992@erpnext.allow_regional
993def get_itemised_tax_breakup_data(doc):
994 itemised_tax = get_itemised_tax(doc.taxes)
995
996 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
997
998 return itemised_tax, itemised_taxable_amount
999
Ankush Menat494bd9e2022-03-28 18:52:46 +05301000
Nabin Hait34c551d2019-07-03 10:34:31 +05301001def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301002 itemised_tax = {}
1003 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301004 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301005 continue
1006
Nabin Haitb962fc12017-07-17 18:02:31 +05301007 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301008 if item_tax_map:
1009 for item_code, tax_data in item_tax_map.items():
1010 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301011
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301012 tax_rate = 0.0
1013 tax_amount = 0.0
1014
Nabin Hait2e4de832017-09-19 14:53:16 +05301015 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301016 tax_rate = flt(tax_data[0])
1017 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301018 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301019 tax_rate = flt(tax_data)
1020
Ankush Menat494bd9e2022-03-28 18:52:46 +05301021 itemised_tax[item_code][tax.description] = frappe._dict(
1022 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1023 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301024
Nabin Hait34c551d2019-07-03 10:34:31 +05301025 if with_tax_account:
1026 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1027
Nabin Haitb962fc12017-07-17 18:02:31 +05301028 return itemised_tax
1029
Ankush Menat494bd9e2022-03-28 18:52:46 +05301030
Nabin Haitb962fc12017-07-17 18:02:31 +05301031def get_itemised_taxable_amount(items):
1032 itemised_taxable_amount = frappe._dict()
1033 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301034 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301035 itemised_taxable_amount.setdefault(item_code, 0)
1036 itemised_taxable_amount[item_code] += item.net_amount
1037
Nabin Haitcaab5822017-08-24 16:22:28 +05301038 return itemised_taxable_amount
1039
Ankush Menat494bd9e2022-03-28 18:52:46 +05301040
Nabin Haitcaab5822017-08-24 16:22:28 +05301041def get_rounded_tax_amount(itemised_tax, precision):
1042 # Rounding based on tax_amount precision
1043 for taxes in itemised_tax.values():
1044 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +05301045 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301046
Ankush Menat494bd9e2022-03-28 18:52:46 +05301047
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301048class init_landed_taxes_and_totals(object):
1049 def __init__(self, doc):
1050 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301051 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301052 self.set_account_currency()
1053 self.set_exchange_rate()
1054 self.set_amounts_in_company_currency()
1055
1056 def set_account_currency(self):
1057 company_currency = erpnext.get_company_currency(self.doc.company)
1058 for d in self.doc.get(self.tax_field):
1059 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301060 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301061 d.account_currency = account_currency or company_currency
1062
1063 def set_exchange_rate(self):
1064 company_currency = erpnext.get_company_currency(self.doc.company)
1065 for d in self.doc.get(self.tax_field):
1066 if d.account_currency == company_currency:
1067 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301068 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301069 d.exchange_rate = get_exchange_rate(
1070 self.doc.posting_date,
1071 account=d.expense_account,
1072 account_currency=d.account_currency,
1073 company=self.doc.company,
1074 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301075
1076 if not d.exchange_rate:
1077 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1078
1079 def set_amounts_in_company_currency(self):
1080 for d in self.doc.get(self.tax_field):
1081 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301082 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))