blob: 62d4c538682854da596ce15a68c1d1c146f9be4b [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
Sagar Vora4205f562023-07-24 18:37:36 +053021from erpnext.utilities.regional import temporary_flag
Chillar Anand915b3432021-09-02 16:44:59 +053022
Nabin Hait3237c752015-02-17 11:11:11 +053023
Nabin Haitfe81da22015-02-18 12:23:18 +053024class calculate_taxes_and_totals(object):
Raffael Meyer67cf7e12023-01-15 13:04:16 +010025 def __init__(self, doc: Document):
Nabin Hait3237c752015-02-17 11:11:11 +053026 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053027 frappe.flags.round_off_applicable_accounts = []
marination91982d12023-01-24 18:03:53 +053028
29 self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
30
Deepesh Garg6a5ef262021-02-19 14:30:23 +053031 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053032 self.calculate()
33
marination91982d12023-01-24 18:03:53 +053034 def filter_rows(self):
35 """Exclude rows, that do not fulfill the filter criteria, from totals computation."""
marinationcef7dfd2023-01-26 14:36:25 +053036 items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
marination91982d12023-01-24 18:03:53 +053037 return items
38
Nabin Hait3237c752015-02-17 11:11:11 +053039 def calculate(self):
marination91982d12023-01-24 18:03:53 +053040 if not len(self._items):
Nabin Haitb315acb2019-07-12 14:27:19 +053041 return
42
Nabin Hait3237c752015-02-17 11:11:11 +053043 self.discount_amount_applied = False
44 self._calculate()
45
46 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053047 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053048 self.apply_discount_amount()
49
Deepesh Garg3b159662022-08-21 17:51:05 +053050 # Update grand total as per cash and non trade discount
51 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
52 self.doc.grand_total -= self.doc.discount_amount
53 self.doc.base_grand_total -= self.doc.base_discount_amount
Deepesh Garg318da162022-08-29 14:18:39 +053054 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053055
Deepesh Gargd596e0e2022-03-10 20:56:36 +053056 self.calculate_shipping_charges()
57
Nabin Haitbd00e812015-02-17 12:50:51 +053058 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053059 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053060
Nabin Hait852cb642017-07-05 12:58:19 +053061 if self.doc.meta.get_field("other_charges_calculation"):
62 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053063
64 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053065 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053066 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053067 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053068 self.initialize_taxes()
69 self.determine_exclusive_rate()
70 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053071 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053072 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053073 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053074 self.calculate_totals()
75 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053076 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053077
niralisataparae758a752022-10-03 16:39:35 +053078 def calculate_tax_withholding_net_total(self):
79 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053080 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053081 sum_base_net_amount = 0
marination91982d12023-01-24 18:03:53 +053082 for item in self._items:
niralisataparae758a752022-10-03 16:39:35 +053083 if hasattr(item, "apply_tds") and item.apply_tds:
84 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053085 sum_base_net_amount += item.base_net_amount
86
niralisataparae758a752022-10-03 16:39:35 +053087 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053088 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053089
Deepesh Gargef0d26c2020-01-06 15:34:15 +053090 def validate_item_tax_template(self):
marination91982d12023-01-24 18:03:53 +053091 for item in self._items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053092 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053093 item_doc = frappe.get_cached_doc("Item", item.item_code)
94 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053095 "net_rate": item.net_rate or item.rate,
96 "tax_category": self.doc.get("tax_category"),
97 "posting_date": self.doc.get("posting_date"),
98 "bill_date": self.doc.get("bill_date"),
99 "transaction_date": self.doc.get("transaction_date"),
100 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530101 }
102
103 item_group = item_doc.item_group
104 item_group_taxes = []
105
106 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530107 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530108 item_group_taxes += item_group_doc.taxes or []
109 item_group = item_group_doc.parent_item_group
110
111 item_taxes = item_doc.taxes or []
112
113 if not item_group_taxes and (not item_taxes):
114 # No validation if no taxes in item or item group
115 continue
116
117 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
118
Deepesh Garg18be7672021-06-06 13:25:34 +0530119 if taxes:
120 if item.item_tax_template not in taxes:
121 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530122 frappe.msgprint(
123 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
124 item.idx, frappe.bold(item.item_code)
125 )
126 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530127
Nabin Haite7679702015-02-20 14:40:35 +0530128 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530129 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530130 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530131 if not self.doc.currency or self.doc.currency == company_currency:
132 self.doc.currency = company_currency
133 self.doc.conversion_rate = 1.0
134 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530135 validate_conversion_rate(
136 self.doc.currency,
137 self.doc.conversion_rate,
138 self.doc.meta.get_label("conversion_rate"),
139 self.doc.company,
140 )
Nabin Hait3237c752015-02-17 11:11:11 +0530141
142 self.doc.conversion_rate = flt(self.doc.conversion_rate)
143
Nabin Hait3237c752015-02-17 11:11:11 +0530144 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530145 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530146 return
147
Nabin Hait3237c752015-02-17 11:11:11 +0530148 if not self.discount_amount_applied:
marination91982d12023-01-24 18:03:53 +0530149 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530150 self.doc.round_floats_in(item)
151
152 if item.discount_percentage == 100:
153 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530154 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530155 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530156 item.rate = flt(
157 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
158 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530159
Deepesh Garga83a0a02022-03-25 12:28:55 +0530160 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530161
Deepesh Garg97e102c2022-03-25 12:39:59 +0530162 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530163 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530164
Ankush Menat494bd9e2022-03-28 18:52:46 +0530165 if item.doctype in [
166 "Quotation Item",
167 "Sales Order Item",
168 "Delivery Note Item",
169 "Sales Invoice Item",
170 "POS Invoice Item",
171 "Purchase Invoice Item",
172 "Purchase Order Item",
173 "Purchase Receipt Item",
174 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530175 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530176 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530177 item.rate = flt(
178 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
179 )
Nabin Hait10c61372021-04-13 15:46:01 +0530180
Walstan Baptista37b826b2021-04-03 19:48:46 +0530181 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530182 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530183 else:
184 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530185
Nabin Hait64bfdd92019-04-23 13:37:19 +0530186 elif flt(item.price_list_rate) > 0:
187 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530188 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
189 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530190
Nabin Haite7679702015-02-20 14:40:35 +0530191 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530192
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530193 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530194 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530195 elif not item.qty and self.doc.get("is_debit_note"):
196 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530197 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530198 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530199
Nabin Haite7679702015-02-20 14:40:35 +0530200 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530201
Ankush Menat494bd9e2022-03-28 18:52:46 +0530202 self._set_in_company_currency(
203 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
204 )
Nabin Hait3237c752015-02-17 11:11:11 +0530205
Nabin Haite7679702015-02-20 14:40:35 +0530206 item.item_tax_amount = 0.0
207
208 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530209 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530210 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530211 val = flt(
212 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
213 )
Nabin Haite7679702015-02-20 14:40:35 +0530214 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530215
216 def initialize_taxes(self):
217 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530218 if not self.discount_amount_applied:
219 validate_taxes_and_charges(tax)
220 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530221
Ankush Menat494bd9e2022-03-28 18:52:46 +0530222 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530223 tax.item_wise_tax_detail = {}
224
Ankush Menat494bd9e2022-03-28 18:52:46 +0530225 tax_fields = [
226 "total",
227 "tax_amount_after_discount_amount",
228 "tax_amount_for_current_item",
229 "grand_total_for_current_item",
230 "tax_fraction_for_current_item",
231 "grand_total_fraction_for_current_item",
232 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530233
Ankush Menat494bd9e2022-03-28 18:52:46 +0530234 if tax.charge_type != "Actual" and not (
235 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
236 ):
237 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530238
239 for fieldname in tax_fields:
240 tax.set(fieldname, 0.0)
241
Nabin Hait3237c752015-02-17 11:11:11 +0530242 self.doc.round_floats_in(tax)
243
Nabin Hait3237c752015-02-17 11:11:11 +0530244 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530245 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530246 return
Nabin Hait3237c752015-02-17 11:11:11 +0530247
marination91982d12023-01-24 18:03:53 +0530248 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530249 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
250 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530251 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530252 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530253 (
254 tax.tax_fraction_for_current_item,
255 inclusive_tax_amount_per_qty,
256 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530257
Ankush Menat494bd9e2022-03-28 18:52:46 +0530258 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530259 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
260 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530261 tax.grand_total_fraction_for_current_item = (
262 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530263 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530264 )
Nabin Hait3237c752015-02-17 11:11:11 +0530265
266 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530267 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530268
Ankush Menat494bd9e2022-03-28 18:52:46 +0530269 if (
270 not self.discount_amount_applied
271 and item.qty
272 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
273 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530274 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
275
276 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530277 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530278 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530279
Nabin Haite7679702015-02-20 14:40:35 +0530280 self._set_in_company_currency(item, ["net_rate", "net_amount"])
281
Nabin Hait3237c752015-02-17 11:11:11 +0530282 def _load_item_tax_rate(self, item_tax_rate):
283 return json.loads(item_tax_rate) if item_tax_rate else {}
284
285 def get_current_tax_fraction(self, tax, item_tax_map):
286 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530287 Get tax fraction for calculating tax exclusive amount
288 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530289 """
290 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530291 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530292
293 if cint(tax.included_in_print_rate):
294 tax_rate = self._get_tax_rate(tax, item_tax_map)
295
296 if tax.charge_type == "On Net Total":
297 current_tax_fraction = tax_rate / 100.0
298
299 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530300 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
301 cint(tax.row_id) - 1
302 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530303
304 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530305 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
306 cint(tax.row_id) - 1
307 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530308
Nabin Hait19ea7212020-08-11 20:34:57 +0530309 elif tax.charge_type == "On Item Quantity":
310 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530311
Nabin Hait19ea7212020-08-11 20:34:57 +0530312 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
313 current_tax_fraction *= -1.0
314 inclusive_tax_amount_per_qty *= -1.0
315
316 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530317
318 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530319 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530320 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
321 else:
322 return tax.rate
323
324 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530325 self.doc.total_qty = (
326 self.doc.total
327 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530328
marination91982d12023-01-24 18:03:53 +0530329 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530330 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530331 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530332 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530333 self.doc.net_total += item.net_amount
334 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530335
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530336 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530337
Subin Toma8e2c022021-11-16 19:06:49 +0530338 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530339
340 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530341 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530342 return
343
Subin Tomaf1fce02021-11-10 16:49:12 +0530344 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530345 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
346 shipping_rule.apply(self.doc)
347
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530348 self._calculate()
349
Nabin Hait3237c752015-02-17 11:11:11 +0530350 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530351 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
352 "rounding_adjustment"
353 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530354 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530355 self.doc.rounding_adjustment = 0
356
Nabin Hait3237c752015-02-17 11:11:11 +0530357 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530358 actual_tax_dict = dict(
359 [
360 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
361 for tax in self.doc.get("taxes")
362 if tax.charge_type == "Actual"
363 ]
364 )
Nabin Hait3237c752015-02-17 11:11:11 +0530365
marination91982d12023-01-24 18:03:53 +0530366 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530367 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530368 for i, tax in enumerate(self.doc.get("taxes")):
369 # tax_amount represents the amount of tax for the current step
370 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
371
372 # Adjust divisional loss to the last item
373 if tax.charge_type == "Actual":
374 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530375 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530376 current_tax_amount += actual_tax_dict[tax.idx]
377
Nabin Hait2b019ed2015-02-22 23:03:07 +0530378 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530379 if tax.charge_type != "Actual" and not (
380 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
381 ):
382 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530383
Nabin Hait3237c752015-02-17 11:11:11 +0530384 # store tax_amount for current item as it will be used for
385 # charge type = 'On Previous Row Amount'
386 tax.tax_amount_for_current_item = current_tax_amount
387
Nabin Hait2b019ed2015-02-22 23:03:07 +0530388 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530389 tax.tax_amount_after_discount_amount += current_tax_amount
390
Nabin Haitcd951342017-07-31 18:07:45 +0530391 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530392
Nabin Hait3237c752015-02-17 11:11:11 +0530393 # note: grand_total_for_current_item contains the contribution of
394 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530395 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530396 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530397 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530398 tax.grand_total_for_current_item = flt(
399 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
400 )
Nabin Hait3237c752015-02-17 11:11:11 +0530401
402 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530403 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530404 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530405 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530406
407 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530408 self.set_cumulative_total(i, tax)
409
Deepesh Gargb6705882021-04-14 11:20:27 +0530410 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530411
Nabin Hait3237c752015-02-17 11:11:11 +0530412 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530413 if (
414 i == (len(self.doc.get("taxes")) - 1)
415 and self.discount_amount_applied
416 and self.doc.discount_amount
417 and self.doc.apply_discount_on == "Grand Total"
418 and not rounding_adjustment_computed
419 ):
420 self.doc.rounding_adjustment = flt(
421 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
422 self.doc.precision("rounding_adjustment"),
423 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530424
Nabin Haitcd951342017-07-31 18:07:45 +0530425 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
426 # if just for valuation, do not add the tax amount in total
427 # if tax/charges is for deduction, multiply by -1
428 if getattr(tax, "category", None):
429 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530430 if self.doc.doctype in [
431 "Purchase Order",
432 "Purchase Invoice",
433 "Purchase Receipt",
434 "Supplier Quotation",
435 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530436 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530437 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530438
Nabin Haitcd951342017-07-31 18:07:45 +0530439 def set_cumulative_total(self, row_idx, tax):
440 tax_amount = tax.tax_amount_after_discount_amount
441 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
442
443 if row_idx == 0:
444 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
445 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530446 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530447
448 def get_current_tax_amount(self, item, tax, item_tax_map):
449 tax_rate = self._get_tax_rate(tax, item_tax_map)
450 current_tax_amount = 0.0
451
452 if tax.charge_type == "Actual":
453 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530454 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530455 current_tax_amount = (
456 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
457 )
Nabin Haite7679702015-02-20 14:40:35 +0530458
Nabin Hait3237c752015-02-17 11:11:11 +0530459 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530460 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530461 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530462 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
463 cint(tax.row_id) - 1
464 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530465 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530466 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
467 cint(tax.row_id) - 1
468 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530469 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530470 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530471
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530472 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530473 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530474
475 return current_tax_amount
476
Nabin Haite7679702015-02-20 14:40:35 +0530477 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
478 # store tax breakup for each item
479 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530480 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Nabin Haite7679702015-02-20 14:40:35 +0530481 if tax.item_wise_tax_detail.get(key):
482 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
483
Ankush Menat494bd9e2022-03-28 18:52:46 +0530484 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530485
Nabin Hait3237c752015-02-17 11:11:11 +0530486 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530487 if tax.account_head in frappe.flags.round_off_applicable_accounts:
488 tax.tax_amount = round(tax.tax_amount, 0)
489 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
490
Nabin Haite7679702015-02-20 14:40:35 +0530491 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530492 tax.tax_amount_after_discount_amount = flt(
493 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
494 )
Nabin Haitce245122015-02-22 20:14:49 +0530495
Deepesh Gargb6705882021-04-14 11:20:27 +0530496 def round_off_base_values(self, tax):
497 # Round off to nearest integer based on regional settings
498 if tax.account_head in frappe.flags.round_off_applicable_accounts:
499 tax.base_tax_amount = round(tax.base_tax_amount, 0)
500 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
501
Nabin Haita1bf43b2015-03-17 10:50:47 +0530502 def manipulate_grand_total_for_inclusive_tax(self):
503 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530504 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 +0530505 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530506 non_inclusive_tax_amount = sum(
507 flt(d.tax_amount_after_discount_amount)
508 for d in self.doc.get("taxes")
509 if not d.included_in_print_rate
510 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530511
Ankush Menat494bd9e2022-03-28 18:52:46 +0530512 diff = (
513 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
514 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530515
516 # If discount amount applied, deduct the discount amount
517 # because self.doc.total is always without discount, but last_tax.total is after discount
518 if self.discount_amount_applied and self.doc.discount_amount:
519 diff -= flt(self.doc.discount_amount)
520
521 diff = flt(diff, self.doc.precision("rounding_adjustment"))
522
Ankush Menat494bd9e2022-03-28 18:52:46 +0530523 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530524 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530525
526 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530527 if self.doc.get("taxes"):
528 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
529 else:
530 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530531
Subin Tom75a76e62021-10-29 16:45:04 +0530532 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530533 self.doc.total_taxes_and_charges = flt(
534 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
535 self.doc.precision("total_taxes_and_charges"),
536 )
Subin Tom75a76e62021-10-29 16:45:04 +0530537 else:
538 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530539
Nabin Hait2e4de832017-09-19 14:53:16 +0530540 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530541
Ankush Menat494bd9e2022-03-28 18:52:46 +0530542 if self.doc.doctype in [
543 "Quotation",
544 "Sales Order",
545 "Delivery Note",
546 "Sales Invoice",
547 "POS Invoice",
548 ]:
549 self.doc.base_grand_total = (
550 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
551 if self.doc.total_taxes_and_charges
552 else self.doc.base_net_total
553 )
Nabin Hait3237c752015-02-17 11:11:11 +0530554 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530555 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530556 for tax in self.doc.get("taxes"):
557 if tax.category in ["Valuation and Total", "Total"]:
558 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530559 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530560 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530561 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530562
Nabin Haite7679702015-02-20 14:40:35 +0530563 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530564
Ankush Menat494bd9e2022-03-28 18:52:46 +0530565 self.doc.base_grand_total = (
566 flt(self.doc.grand_total * self.doc.conversion_rate)
567 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530568 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530569 )
Nabin Hait3237c752015-02-17 11:11:11 +0530570
Ankush Menat494bd9e2022-03-28 18:52:46 +0530571 self._set_in_company_currency(
572 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
573 )
Nabin Hait3237c752015-02-17 11:11:11 +0530574
Nabin Haite7679702015-02-20 14:40:35 +0530575 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530576
Nabin Hait2e4de832017-09-19 14:53:16 +0530577 self.set_rounded_total()
578
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530579 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530580 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530581 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530582 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530583 if d.total_weight:
584 self.doc.total_net_weight += d.total_weight
585
Nabin Hait2e4de832017-09-19 14:53:16 +0530586 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530587 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530588 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530589
Saqib Ansari17445c72022-03-07 18:01:07 +0530590 if self.doc.meta.get_field("rounded_total"):
591 if self.doc.is_rounded_total_disabled():
592 self.doc.rounded_total = self.doc.base_rounded_total = 0
593 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530594
Ankush Menat494bd9e2022-03-28 18:52:46 +0530595 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
596 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
597 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530598
Ankush Menat494bd9e2022-03-28 18:52:46 +0530599 # if print_in_rate is set, we would have already calculated rounding adjustment
600 self.doc.rounding_adjustment += flt(
601 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
602 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530603
604 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530605
Nabin Hait3237c752015-02-17 11:11:11 +0530606 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530607 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530608 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530609 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530610 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530611
Nabin Hait3769d872015-12-18 13:12:02 +0530612 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530613 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530614 self.doc.discount_amount = flt(
615 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
616 * self.doc.additional_discount_percentage
617 / 100,
618 self.doc.precision("discount_amount"),
619 )
Nabin Hait3237c752015-02-17 11:11:11 +0530620
621 def apply_discount_amount(self):
622 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530623 if not self.doc.apply_discount_on:
624 frappe.throw(_("Please select Apply Discount On"))
625
Deepesh Garg3b159662022-08-21 17:51:05 +0530626 self.doc.base_discount_amount = flt(
627 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
628 )
629
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530630 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
631 "is_cash_or_non_trade_discount"
632 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530633 self.discount_amount_applied = True
634 return
635
Nabin Haite7679702015-02-20 14:40:35 +0530636 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530637 taxes = self.doc.get("taxes")
638 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530639
Nabin Haite7679702015-02-20 14:40:35 +0530640 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530641 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530642 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530643 distributed_amount = (
644 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
645 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530646
Nabin Haite7679702015-02-20 14:40:35 +0530647 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530648 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530649
Nabin Hait25bd84d2015-03-04 15:06:56 +0530650 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530651 if (
652 self.doc.apply_discount_on == "Net Total"
653 or not taxes
654 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530655 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530656 discount_amount_loss = flt(
657 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
658 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530659
Ankush Menat494bd9e2022-03-28 18:52:46 +0530660 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530661
Nabin Hait51e980d2015-10-10 18:10:05 +0530662 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 +0530663
Nabin Haite7679702015-02-20 14:40:35 +0530664 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530665
666 self.discount_amount_applied = True
667 self._calculate()
668 else:
669 self.doc.base_discount_amount = 0
670
Nabin Haite7679702015-02-20 14:40:35 +0530671 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530672 if self.doc.apply_discount_on == "Net Total":
673 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530674 else:
675 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530676
Nabin Haite7679702015-02-20 14:40:35 +0530677 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530678 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530679 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
680 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530681 elif tax.row_id in actual_taxes_dict:
682 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
683 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530684
Ankush Menat494bd9e2022-03-28 18:52:46 +0530685 return flt(
686 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
687 )
Nabin Hait3237c752015-02-17 11:11:11 +0530688
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530689 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100690 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530691 total_allocated_amount = sum(
692 flt(adv.allocated_amount, adv.precision("allocated_amount"))
693 for adv in self.doc.get("advances")
694 )
Nabin Hait3237c752015-02-17 11:11:11 +0530695
Nabin Haite7679702015-02-20 14:40:35 +0530696 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530697
Faris Ansari6041f5c2018-02-08 13:33:52 +0530698 grand_total = self.doc.rounded_total or self.doc.grand_total
699
Nabin Hait289ffb72016-02-08 11:06:55 +0530700 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530701 invoice_total = flt(
702 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
703 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530704 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530705 base_write_off_amount = flt(
706 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
707 self.doc.precision("base_write_off_amount"),
708 )
709 invoice_total = (
710 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
711 - base_write_off_amount
712 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530713
Nabin Haitadc09232016-02-09 10:31:11 +0530714 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530715 frappe.throw(
716 _("Advance amount cannot be greater than {0} {1}").format(
717 self.doc.party_account_currency, invoice_total
718 )
719 )
Nabin Hait3237c752015-02-17 11:11:11 +0530720
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100721 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530722 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530723 self.doc.write_off_amount = 0
724
Nabin Hait3237c752015-02-17 11:11:11 +0530725 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530726 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530727
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530728 def is_internal_invoice(self):
729 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530730 Checks if its an internal transfer invoice
731 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530732 """
733
Ankush Menat494bd9e2022-03-28 18:52:46 +0530734 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530735 return True
736
737 return False
738
Nabin Hait3237c752015-02-17 11:11:11 +0530739 def calculate_outstanding_amount(self):
740 # NOTE:
741 # write_off_amount is only for POS Invoice
742 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530743 if self.doc.doctype == "Sales Invoice":
744 self.calculate_paid_amount()
745
Ankush Menat494bd9e2022-03-28 18:52:46 +0530746 if (
747 self.doc.is_return
748 and self.doc.return_against
749 and not self.doc.get("is_pos")
750 or self.is_internal_invoice()
751 ):
752 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530753
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530754 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530755 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530756
Nabin Hait877e1bb2017-11-17 12:27:43 +0530757 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
758 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530759 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
760
Nabin Hait877e1bb2017-11-17 12:27:43 +0530761 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530762 total_amount_to_pay = flt(
763 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
764 self.doc.precision("grand_total"),
765 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530766 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530767 total_amount_to_pay = flt(
768 flt(base_grand_total, self.doc.precision("base_grand_total"))
769 - self.doc.total_advance
770 - flt(self.doc.base_write_off_amount),
771 self.doc.precision("base_grand_total"),
772 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530773
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530774 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530775 change_amount = 0
776
Ankush Menat494bd9e2022-03-28 18:52:46 +0530777 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530778 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530779 change_amount = (
780 self.doc.change_amount
781 if self.doc.party_account_currency == self.doc.currency
782 else self.doc.base_change_amount
783 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530784
Ankush Menat494bd9e2022-03-28 18:52:46 +0530785 paid_amount = (
786 self.doc.paid_amount
787 if self.doc.party_account_currency == self.doc.currency
788 else self.doc.base_paid_amount
789 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530790
Ankush Menat494bd9e2022-03-28 18:52:46 +0530791 self.doc.outstanding_amount = flt(
792 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
793 self.doc.precision("outstanding_amount"),
794 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530795
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530796 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530797 self.doc.doctype == "Sales Invoice"
798 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530799 and self.doc.get("pos_profile")
800 and self.doc.get("is_consolidated")
801 ):
802 write_off_limit = flt(
803 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
804 )
805 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
806 self.doc.write_off_outstanding_amount_automatically = 1
807
808 if (
809 self.doc.doctype == "Sales Invoice"
810 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530811 and self.doc.get("is_return")
812 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530813 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530814 self.set_total_amount_to_default_mop(total_amount_to_pay)
815 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530816
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530817 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530818
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530819 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530820
821 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530822 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530823 payment.amount = flt(payment.amount)
824 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530825 paid_amount += payment.amount
826 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530827 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530828 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530829
Manas Solankida486ee2018-07-06 12:36:57 +0530830 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
831 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530832 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530833
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530834 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
835 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
836
Nabin Hait3bb1a422016-08-02 16:41:10 +0530837 def calculate_change_amount(self):
838 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530839 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530840 grand_total = self.doc.rounded_total or self.doc.grand_total
841 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530842
Ankush Menat494bd9e2022-03-28 18:52:46 +0530843 if (
844 self.doc.doctype == "Sales Invoice"
845 and self.doc.paid_amount > grand_total
846 and not self.doc.is_return
847 and any(d.type == "Cash" for d in self.doc.payments)
848 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530849 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530850 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530851 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530852
Ankush Menat494bd9e2022-03-28 18:52:46 +0530853 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530854 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530855 )
mbauskar36b51892016-01-18 16:31:10 +0530856
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530857 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530858 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530859 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530860 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530861 )
862 self.doc.base_write_off_amount = flt(
863 self.doc.write_off_amount * self.doc.conversion_rate,
864 self.doc.precision("base_write_off_amount"),
865 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530866
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530867 self.calculate_outstanding_amount()
868
mbauskar36b51892016-01-18 16:31:10 +0530869 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530870 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530871 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530872 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530873 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530874 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530875 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530876 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530877
Ankush Menat494bd9e2022-03-28 18:52:46 +0530878 if pricing_rule.margin_rate_or_amount and (
879 (
880 pricing_rule.currency == self.doc.currency
881 and pricing_rule.margin_type in ["Amount", "Percentage"]
882 )
883 or pricing_rule.margin_type == "Percentage"
884 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530885 item.margin_type = pricing_rule.margin_type
886 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530887 has_margin = True
888
889 if not has_margin:
890 item.margin_type = None
891 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530892
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530893 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
894 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530895 item.margin_rate_or_amount = flt(
896 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
897 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530898 item.rate_with_margin = item.rate
899
900 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530901 margin_value = (
902 item.margin_rate_or_amount
903 if item.margin_type == "Amount"
904 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
905 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530906 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530907 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530908
Shreya Shahbe690ef2017-11-14 17:22:41 +0530909 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530910
911 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530912 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530913
Subin Tom7d627df2021-08-23 11:05:07 +0530914 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530915 total_paid_amount = 0
916 for payment in self.doc.get("payments"):
917 total_paid_amount += (
918 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530919 )
920
Deepesh Garg06e8e282022-10-31 19:58:46 +0530921 pending_amount = total_amount_to_pay - total_paid_amount
922
923 if pending_amount > 0:
924 default_mode_of_payment = frappe.db.get_value(
925 "POS Payment Method",
926 {"parent": self.doc.pos_profile, "default": 1},
927 ["mode_of_payment"],
928 as_dict=1,
929 )
930
931 if default_mode_of_payment:
932 self.doc.payments = []
933 self.doc.append(
934 "payments",
935 {
936 "mode_of_payment": default_mode_of_payment.mode_of_payment,
937 "amount": pending_amount,
938 "default": 1,
939 },
940 )
941
Deepesh Garg0ebace52020-02-25 13:21:16 +0530942
Nabin Hait9c421612017-07-20 13:32:01 +0530943def get_itemised_tax_breakup_html(doc):
944 if not doc.taxes:
945 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530946
Nabin Hait9c421612017-07-20 13:32:01 +0530947 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530948 tax_accounts = []
949 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530950 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530951 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530952 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530953 tax_accounts.append(tax.description)
954
Sagar Vora4205f562023-07-24 18:37:36 +0530955 with temporary_flag("company", doc.company):
956 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530957 itemised_tax_data = get_itemised_tax_breakup_data(doc)
958 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530959 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530960
Nabin Hait9c421612017-07-20 13:32:01 +0530961 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530962 "templates/includes/itemised_tax_breakup.html",
963 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530964 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530965 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +0530966 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530967 doc=doc,
968 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530969 )
Nabin Hait852cb642017-07-05 12:58:19 +0530970
Ankush Menat494bd9e2022-03-28 18:52:46 +0530971
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530972@frappe.whitelist()
973def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +0530974 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +0530975 with temporary_flag("company", company):
976 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530977
Ankush Menat494bd9e2022-03-28 18:52:46 +0530978
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530979@erpnext.allow_regional
980def get_regional_round_off_accounts(company, account_list):
981 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530982
Ankush Menat494bd9e2022-03-28 18:52:46 +0530983
rohitwaghchaured4526682017-12-28 14:20:13 +0530984@erpnext.allow_regional
985def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530986 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +0530987 pass
988
Ankush Menat494bd9e2022-03-28 18:52:46 +0530989
Nabin Haitb962fc12017-07-17 18:02:31 +0530990@erpnext.allow_regional
991def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
992 return [_("Item"), _("Taxable Amount")] + tax_accounts
993
Ankush Menat494bd9e2022-03-28 18:52:46 +0530994
Nabin Haitb962fc12017-07-17 18:02:31 +0530995@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +0530996def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +0530997 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +0530998
999 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1000
DaizyModib84deec2023-07-21 17:26:35 +05301001 itemised_tax_data = []
1002 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301003 itemised_tax_data.append(
1004 frappe._dict(
1005 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes}
1006 )
1007 )
DaizyModib84deec2023-07-21 17:26:35 +05301008
1009 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301010
Ankush Menat494bd9e2022-03-28 18:52:46 +05301011
Nabin Hait34c551d2019-07-03 10:34:31 +05301012def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301013 itemised_tax = {}
1014 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301015 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301016 continue
1017
Nabin Haitb962fc12017-07-17 18:02:31 +05301018 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301019 if item_tax_map:
1020 for item_code, tax_data in item_tax_map.items():
1021 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301022
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301023 tax_rate = 0.0
1024 tax_amount = 0.0
1025
Nabin Hait2e4de832017-09-19 14:53:16 +05301026 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301027 tax_rate = flt(tax_data[0])
1028 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301029 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301030 tax_rate = flt(tax_data)
1031
Ankush Menat494bd9e2022-03-28 18:52:46 +05301032 itemised_tax[item_code][tax.description] = frappe._dict(
1033 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1034 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301035
Nabin Hait34c551d2019-07-03 10:34:31 +05301036 if with_tax_account:
1037 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1038
Nabin Haitb962fc12017-07-17 18:02:31 +05301039 return itemised_tax
1040
Ankush Menat494bd9e2022-03-28 18:52:46 +05301041
Nabin Haitb962fc12017-07-17 18:02:31 +05301042def get_itemised_taxable_amount(items):
1043 itemised_taxable_amount = frappe._dict()
1044 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301045 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301046 itemised_taxable_amount.setdefault(item_code, 0)
1047 itemised_taxable_amount[item_code] += item.net_amount
1048
Nabin Haitcaab5822017-08-24 16:22:28 +05301049 return itemised_taxable_amount
1050
Ankush Menat494bd9e2022-03-28 18:52:46 +05301051
DaizyModi6f376cf2023-07-24 18:02:42 +05301052def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301053 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301054 for taxes in itemised_tax:
1055 for row in taxes.values():
1056 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1057 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301058
Ankush Menat494bd9e2022-03-28 18:52:46 +05301059
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301060class init_landed_taxes_and_totals(object):
1061 def __init__(self, doc):
1062 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301063 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301064 self.set_account_currency()
1065 self.set_exchange_rate()
1066 self.set_amounts_in_company_currency()
1067
1068 def set_account_currency(self):
1069 company_currency = erpnext.get_company_currency(self.doc.company)
1070 for d in self.doc.get(self.tax_field):
1071 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301072 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301073 d.account_currency = account_currency or company_currency
1074
1075 def set_exchange_rate(self):
1076 company_currency = erpnext.get_company_currency(self.doc.company)
1077 for d in self.doc.get(self.tax_field):
1078 if d.account_currency == company_currency:
1079 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301080 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301081 d.exchange_rate = get_exchange_rate(
1082 self.doc.posting_date,
1083 account=d.expense_account,
1084 account_currency=d.account_currency,
1085 company=self.doc.company,
1086 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301087
1088 if not d.exchange_rate:
1089 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1090
1091 def set_amounts_in_company_currency(self):
1092 for d in self.doc.get(self.tax_field):
1093 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301094 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))