blob: c1dc316c541ee821db016cda9d2684d4c67eb68b [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 = []
Dany Robertdfb5b882023-08-23 04:01:00 +000028 frappe.flags.round_row_wise_tax = frappe.db.get_single_value(
29 "Accounts Settings", "round_row_wise_tax"
Dany Robert3ead2892023-08-22 14:41:07 +000030 )
marination91982d12023-01-24 18:03:53 +053031
32 self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
33
Deepesh Garg6a5ef262021-02-19 14:30:23 +053034 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053035 self.calculate()
36
marination91982d12023-01-24 18:03:53 +053037 def filter_rows(self):
38 """Exclude rows, that do not fulfill the filter criteria, from totals computation."""
marinationcef7dfd2023-01-26 14:36:25 +053039 items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
marination91982d12023-01-24 18:03:53 +053040 return items
41
Nabin Hait3237c752015-02-17 11:11:11 +053042 def calculate(self):
marination91982d12023-01-24 18:03:53 +053043 if not len(self._items):
Nabin Haitb315acb2019-07-12 14:27:19 +053044 return
45
Nabin Hait3237c752015-02-17 11:11:11 +053046 self.discount_amount_applied = False
47 self._calculate()
48
49 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053050 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053051 self.apply_discount_amount()
52
Deepesh Garg3b159662022-08-21 17:51:05 +053053 # Update grand total as per cash and non trade discount
54 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
55 self.doc.grand_total -= self.doc.discount_amount
56 self.doc.base_grand_total -= self.doc.base_discount_amount
Deepesh Garg318da162022-08-29 14:18:39 +053057 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053058
Deepesh Gargd596e0e2022-03-10 20:56:36 +053059 self.calculate_shipping_charges()
60
Nabin Haitbd00e812015-02-17 12:50:51 +053061 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053062 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053063
Nabin Hait852cb642017-07-05 12:58:19 +053064 if self.doc.meta.get_field("other_charges_calculation"):
65 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053066
67 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053068 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053069 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053070 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053071 self.initialize_taxes()
72 self.determine_exclusive_rate()
73 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053074 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053075 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053076 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053077 self.calculate_totals()
78 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053079 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053080
niralisataparae758a752022-10-03 16:39:35 +053081 def calculate_tax_withholding_net_total(self):
82 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053083 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053084 sum_base_net_amount = 0
marination91982d12023-01-24 18:03:53 +053085 for item in self._items:
niralisataparae758a752022-10-03 16:39:35 +053086 if hasattr(item, "apply_tds") and item.apply_tds:
87 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053088 sum_base_net_amount += item.base_net_amount
89
niralisataparae758a752022-10-03 16:39:35 +053090 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053091 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053092
Deepesh Gargef0d26c2020-01-06 15:34:15 +053093 def validate_item_tax_template(self):
marination91982d12023-01-24 18:03:53 +053094 for item in self._items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053095 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053096 item_doc = frappe.get_cached_doc("Item", item.item_code)
97 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053098 "net_rate": item.net_rate or item.rate,
99 "tax_category": self.doc.get("tax_category"),
100 "posting_date": self.doc.get("posting_date"),
101 "bill_date": self.doc.get("bill_date"),
102 "transaction_date": self.doc.get("transaction_date"),
103 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530104 }
105
106 item_group = item_doc.item_group
107 item_group_taxes = []
108
109 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530110 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530111 item_group_taxes += item_group_doc.taxes or []
112 item_group = item_group_doc.parent_item_group
113
114 item_taxes = item_doc.taxes or []
115
116 if not item_group_taxes and (not item_taxes):
117 # No validation if no taxes in item or item group
118 continue
119
120 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
121
Deepesh Garg18be7672021-06-06 13:25:34 +0530122 if taxes:
123 if item.item_tax_template not in taxes:
124 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530125 frappe.msgprint(
126 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
127 item.idx, frappe.bold(item.item_code)
128 )
129 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530130
Nabin Haite7679702015-02-20 14:40:35 +0530131 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530132 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530133 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530134 if not self.doc.currency or self.doc.currency == company_currency:
135 self.doc.currency = company_currency
136 self.doc.conversion_rate = 1.0
137 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530138 validate_conversion_rate(
139 self.doc.currency,
140 self.doc.conversion_rate,
141 self.doc.meta.get_label("conversion_rate"),
142 self.doc.company,
143 )
Nabin Hait3237c752015-02-17 11:11:11 +0530144
145 self.doc.conversion_rate = flt(self.doc.conversion_rate)
146
Nabin Hait3237c752015-02-17 11:11:11 +0530147 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530148 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530149 return
150
Nabin Hait3237c752015-02-17 11:11:11 +0530151 if not self.discount_amount_applied:
marination91982d12023-01-24 18:03:53 +0530152 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530153 self.doc.round_floats_in(item)
154
155 if item.discount_percentage == 100:
156 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530157 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530158 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530159 item.rate = flt(
160 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
161 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530162
Deepesh Garga83a0a02022-03-25 12:28:55 +0530163 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530164
Deepesh Garg97e102c2022-03-25 12:39:59 +0530165 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530166 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530167
Ankush Menat494bd9e2022-03-28 18:52:46 +0530168 if item.doctype in [
169 "Quotation Item",
170 "Sales Order Item",
171 "Delivery Note Item",
172 "Sales Invoice Item",
173 "POS Invoice Item",
174 "Purchase Invoice Item",
175 "Purchase Order Item",
176 "Purchase Receipt Item",
177 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530178 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530179 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530180 item.rate = flt(
181 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
182 )
Nabin Hait10c61372021-04-13 15:46:01 +0530183
Walstan Baptista37b826b2021-04-03 19:48:46 +0530184 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530185 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530186 else:
187 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530188
Nabin Hait64bfdd92019-04-23 13:37:19 +0530189 elif flt(item.price_list_rate) > 0:
190 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530191 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
192 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530193
Nabin Haite7679702015-02-20 14:40:35 +0530194 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530195
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530196 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530197 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530198 elif not item.qty and self.doc.get("is_debit_note"):
199 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530200 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530201 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530202
Nabin Haite7679702015-02-20 14:40:35 +0530203 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530204
Ankush Menat494bd9e2022-03-28 18:52:46 +0530205 self._set_in_company_currency(
206 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
207 )
Nabin Hait3237c752015-02-17 11:11:11 +0530208
Nabin Haite7679702015-02-20 14:40:35 +0530209 item.item_tax_amount = 0.0
210
211 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530212 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530213 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530214 val = flt(
215 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
216 )
Nabin Haite7679702015-02-20 14:40:35 +0530217 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530218
219 def initialize_taxes(self):
220 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530221 if not self.discount_amount_applied:
222 validate_taxes_and_charges(tax)
223 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530224
Ankush Menat494bd9e2022-03-28 18:52:46 +0530225 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530226 tax.item_wise_tax_detail = {}
227
Ankush Menat494bd9e2022-03-28 18:52:46 +0530228 tax_fields = [
229 "total",
230 "tax_amount_after_discount_amount",
231 "tax_amount_for_current_item",
232 "grand_total_for_current_item",
233 "tax_fraction_for_current_item",
234 "grand_total_fraction_for_current_item",
235 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530236
Ankush Menat494bd9e2022-03-28 18:52:46 +0530237 if tax.charge_type != "Actual" and not (
238 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
239 ):
240 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530241
242 for fieldname in tax_fields:
243 tax.set(fieldname, 0.0)
244
Nabin Hait3237c752015-02-17 11:11:11 +0530245 self.doc.round_floats_in(tax)
246
Nabin Hait3237c752015-02-17 11:11:11 +0530247 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530248 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530249 return
Nabin Hait3237c752015-02-17 11:11:11 +0530250
marination91982d12023-01-24 18:03:53 +0530251 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530252 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
253 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530254 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530255 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530256 (
257 tax.tax_fraction_for_current_item,
258 inclusive_tax_amount_per_qty,
259 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530260
Ankush Menat494bd9e2022-03-28 18:52:46 +0530261 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530262 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
263 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530264 tax.grand_total_fraction_for_current_item = (
265 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530266 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530267 )
Nabin Hait3237c752015-02-17 11:11:11 +0530268
269 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530270 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530271
Ankush Menat494bd9e2022-03-28 18:52:46 +0530272 if (
273 not self.discount_amount_applied
274 and item.qty
275 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
276 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530277 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
278
279 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530280 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530281 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530282
Nabin Haite7679702015-02-20 14:40:35 +0530283 self._set_in_company_currency(item, ["net_rate", "net_amount"])
284
Nabin Hait3237c752015-02-17 11:11:11 +0530285 def _load_item_tax_rate(self, item_tax_rate):
286 return json.loads(item_tax_rate) if item_tax_rate else {}
287
288 def get_current_tax_fraction(self, tax, item_tax_map):
289 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530290 Get tax fraction for calculating tax exclusive amount
291 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530292 """
293 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530294 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530295
296 if cint(tax.included_in_print_rate):
297 tax_rate = self._get_tax_rate(tax, item_tax_map)
298
299 if tax.charge_type == "On Net Total":
300 current_tax_fraction = tax_rate / 100.0
301
302 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530303 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
304 cint(tax.row_id) - 1
305 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530306
307 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530308 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
309 cint(tax.row_id) - 1
310 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530311
Nabin Hait19ea7212020-08-11 20:34:57 +0530312 elif tax.charge_type == "On Item Quantity":
313 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530314
Nabin Hait19ea7212020-08-11 20:34:57 +0530315 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
316 current_tax_fraction *= -1.0
317 inclusive_tax_amount_per_qty *= -1.0
318
319 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530320
321 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530322 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530323 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
324 else:
325 return tax.rate
326
327 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530328 self.doc.total_qty = (
329 self.doc.total
330 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530331
marination91982d12023-01-24 18:03:53 +0530332 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530333 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530334 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530335 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530336 self.doc.net_total += item.net_amount
337 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530338
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530339 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530340
Subin Toma8e2c022021-11-16 19:06:49 +0530341 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530342
343 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530344 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530345 return
346
Subin Tomaf1fce02021-11-10 16:49:12 +0530347 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530348 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
349 shipping_rule.apply(self.doc)
350
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530351 self._calculate()
352
Nabin Hait3237c752015-02-17 11:11:11 +0530353 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530354 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
355 "rounding_adjustment"
356 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530357 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530358 self.doc.rounding_adjustment = 0
359
Nabin Hait3237c752015-02-17 11:11:11 +0530360 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530361 actual_tax_dict = dict(
362 [
363 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
364 for tax in self.doc.get("taxes")
365 if tax.charge_type == "Actual"
366 ]
367 )
Nabin Hait3237c752015-02-17 11:11:11 +0530368
marination91982d12023-01-24 18:03:53 +0530369 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530370 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530371 for i, tax in enumerate(self.doc.get("taxes")):
372 # tax_amount represents the amount of tax for the current step
373 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
Dany Robert3ead2892023-08-22 14:41:07 +0000374 if frappe.flags.round_row_wise_tax:
375 current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530376
377 # Adjust divisional loss to the last item
378 if tax.charge_type == "Actual":
379 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530380 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530381 current_tax_amount += actual_tax_dict[tax.idx]
382
Nabin Hait2b019ed2015-02-22 23:03:07 +0530383 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530384 if tax.charge_type != "Actual" and not (
385 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
386 ):
387 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530388
Nabin Hait3237c752015-02-17 11:11:11 +0530389 # store tax_amount for current item as it will be used for
390 # charge type = 'On Previous Row Amount'
391 tax.tax_amount_for_current_item = current_tax_amount
392
Nabin Hait2b019ed2015-02-22 23:03:07 +0530393 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530394 tax.tax_amount_after_discount_amount += current_tax_amount
395
Nabin Haitcd951342017-07-31 18:07:45 +0530396 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530397
Nabin Hait3237c752015-02-17 11:11:11 +0530398 # note: grand_total_for_current_item contains the contribution of
399 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530400 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530401 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530402 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530403 tax.grand_total_for_current_item = flt(
404 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
405 )
Nabin Hait3237c752015-02-17 11:11:11 +0530406
407 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530408 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530409 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530410 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530411
412 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530413 self.set_cumulative_total(i, tax)
414
Deepesh Gargb6705882021-04-14 11:20:27 +0530415 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530416
Nabin Hait3237c752015-02-17 11:11:11 +0530417 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530418 if (
419 i == (len(self.doc.get("taxes")) - 1)
420 and self.discount_amount_applied
421 and self.doc.discount_amount
422 and self.doc.apply_discount_on == "Grand Total"
423 and not rounding_adjustment_computed
424 ):
425 self.doc.rounding_adjustment = flt(
426 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
427 self.doc.precision("rounding_adjustment"),
428 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530429
Nabin Haitcd951342017-07-31 18:07:45 +0530430 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
431 # if just for valuation, do not add the tax amount in total
432 # if tax/charges is for deduction, multiply by -1
433 if getattr(tax, "category", None):
434 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530435 if self.doc.doctype in [
436 "Purchase Order",
437 "Purchase Invoice",
438 "Purchase Receipt",
439 "Supplier Quotation",
440 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530441 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530442 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530443
Nabin Haitcd951342017-07-31 18:07:45 +0530444 def set_cumulative_total(self, row_idx, tax):
445 tax_amount = tax.tax_amount_after_discount_amount
446 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
447
448 if row_idx == 0:
449 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
450 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530451 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530452
453 def get_current_tax_amount(self, item, tax, item_tax_map):
454 tax_rate = self._get_tax_rate(tax, item_tax_map)
455 current_tax_amount = 0.0
456
457 if tax.charge_type == "Actual":
458 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530459 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530460 current_tax_amount = (
461 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
462 )
Nabin Haite7679702015-02-20 14:40:35 +0530463
Nabin Hait3237c752015-02-17 11:11:11 +0530464 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530465 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530466 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530467 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
468 cint(tax.row_id) - 1
469 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530470 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530471 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
472 cint(tax.row_id) - 1
473 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530474 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530475 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530476
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530477 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530478 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530479
480 return current_tax_amount
481
Nabin Haite7679702015-02-20 14:40:35 +0530482 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
483 # store tax breakup for each item
484 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530485 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000486 if frappe.flags.round_row_wise_tax:
487 item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
488 if tax.item_wise_tax_detail.get(key):
489 item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
Dany Robert9e1b2c92023-08-24 05:02:14 +0000490 tax.item_wise_tax_detail[key] = [
491 tax_rate,
492 flt(item_wise_tax_amount, tax.precision("tax_amount")),
493 ]
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000494 else:
495 if tax.item_wise_tax_detail.get(key):
496 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
Nabin Haite7679702015-02-20 14:40:35 +0530497
Dany Robert9e1b2c92023-08-24 05:02:14 +0000498 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530499
Nabin Hait3237c752015-02-17 11:11:11 +0530500 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530501 if tax.account_head in frappe.flags.round_off_applicable_accounts:
502 tax.tax_amount = round(tax.tax_amount, 0)
503 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
504
Nabin Haite7679702015-02-20 14:40:35 +0530505 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530506 tax.tax_amount_after_discount_amount = flt(
507 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
508 )
Nabin Haitce245122015-02-22 20:14:49 +0530509
Deepesh Gargb6705882021-04-14 11:20:27 +0530510 def round_off_base_values(self, tax):
511 # Round off to nearest integer based on regional settings
512 if tax.account_head in frappe.flags.round_off_applicable_accounts:
513 tax.base_tax_amount = round(tax.base_tax_amount, 0)
514 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
515
Nabin Haita1bf43b2015-03-17 10:50:47 +0530516 def manipulate_grand_total_for_inclusive_tax(self):
517 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530518 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 +0530519 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530520 non_inclusive_tax_amount = sum(
521 flt(d.tax_amount_after_discount_amount)
522 for d in self.doc.get("taxes")
523 if not d.included_in_print_rate
524 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530525
Ankush Menat494bd9e2022-03-28 18:52:46 +0530526 diff = (
527 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
528 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530529
530 # If discount amount applied, deduct the discount amount
531 # because self.doc.total is always without discount, but last_tax.total is after discount
532 if self.discount_amount_applied and self.doc.discount_amount:
533 diff -= flt(self.doc.discount_amount)
534
535 diff = flt(diff, self.doc.precision("rounding_adjustment"))
536
Ankush Menat494bd9e2022-03-28 18:52:46 +0530537 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530538 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530539
540 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530541 if self.doc.get("taxes"):
542 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
543 else:
544 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530545
Subin Tom75a76e62021-10-29 16:45:04 +0530546 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530547 self.doc.total_taxes_and_charges = flt(
548 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
549 self.doc.precision("total_taxes_and_charges"),
550 )
Subin Tom75a76e62021-10-29 16:45:04 +0530551 else:
552 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530553
Nabin Hait2e4de832017-09-19 14:53:16 +0530554 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530555
Ankush Menat494bd9e2022-03-28 18:52:46 +0530556 if self.doc.doctype in [
557 "Quotation",
558 "Sales Order",
559 "Delivery Note",
560 "Sales Invoice",
561 "POS Invoice",
562 ]:
563 self.doc.base_grand_total = (
564 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
565 if self.doc.total_taxes_and_charges
566 else self.doc.base_net_total
567 )
Nabin Hait3237c752015-02-17 11:11:11 +0530568 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530569 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530570 for tax in self.doc.get("taxes"):
571 if tax.category in ["Valuation and Total", "Total"]:
572 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530573 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530574 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530575 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530576
Nabin Haite7679702015-02-20 14:40:35 +0530577 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530578
Ankush Menat494bd9e2022-03-28 18:52:46 +0530579 self.doc.base_grand_total = (
580 flt(self.doc.grand_total * self.doc.conversion_rate)
581 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530582 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530583 )
Nabin Hait3237c752015-02-17 11:11:11 +0530584
Ankush Menat494bd9e2022-03-28 18:52:46 +0530585 self._set_in_company_currency(
586 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
587 )
Nabin Hait3237c752015-02-17 11:11:11 +0530588
Nabin Haite7679702015-02-20 14:40:35 +0530589 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530590
Nabin Hait2e4de832017-09-19 14:53:16 +0530591 self.set_rounded_total()
592
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530593 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530594 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530595 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530596 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530597 if d.total_weight:
598 self.doc.total_net_weight += d.total_weight
599
Nabin Hait2e4de832017-09-19 14:53:16 +0530600 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530601 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530602 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530603
Saqib Ansari17445c72022-03-07 18:01:07 +0530604 if self.doc.meta.get_field("rounded_total"):
605 if self.doc.is_rounded_total_disabled():
606 self.doc.rounded_total = self.doc.base_rounded_total = 0
607 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530608
Ankush Menat494bd9e2022-03-28 18:52:46 +0530609 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
610 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
611 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530612
Ankush Menat494bd9e2022-03-28 18:52:46 +0530613 # if print_in_rate is set, we would have already calculated rounding adjustment
614 self.doc.rounding_adjustment += flt(
615 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
616 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530617
618 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530619
Nabin Hait3237c752015-02-17 11:11:11 +0530620 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530621 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530622 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530623 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530624 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530625
Nabin Hait3769d872015-12-18 13:12:02 +0530626 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530627 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530628 self.doc.discount_amount = flt(
629 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
630 * self.doc.additional_discount_percentage
631 / 100,
632 self.doc.precision("discount_amount"),
633 )
Nabin Hait3237c752015-02-17 11:11:11 +0530634
635 def apply_discount_amount(self):
636 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530637 if not self.doc.apply_discount_on:
638 frappe.throw(_("Please select Apply Discount On"))
639
Deepesh Garg3b159662022-08-21 17:51:05 +0530640 self.doc.base_discount_amount = flt(
641 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
642 )
643
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530644 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
645 "is_cash_or_non_trade_discount"
646 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530647 self.discount_amount_applied = True
648 return
649
Nabin Haite7679702015-02-20 14:40:35 +0530650 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530651 taxes = self.doc.get("taxes")
652 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530653
Nabin Haite7679702015-02-20 14:40:35 +0530654 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530655 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530656 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530657 distributed_amount = (
658 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
659 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530660
Nabin Haite7679702015-02-20 14:40:35 +0530661 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530662 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530663
Nabin Hait25bd84d2015-03-04 15:06:56 +0530664 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530665 if (
666 self.doc.apply_discount_on == "Net Total"
667 or not taxes
668 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530669 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530670 discount_amount_loss = flt(
671 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
672 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530673
Ankush Menat494bd9e2022-03-28 18:52:46 +0530674 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530675
Nabin Hait51e980d2015-10-10 18:10:05 +0530676 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 +0530677
Nabin Haite7679702015-02-20 14:40:35 +0530678 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530679
680 self.discount_amount_applied = True
681 self._calculate()
682 else:
683 self.doc.base_discount_amount = 0
684
Nabin Haite7679702015-02-20 14:40:35 +0530685 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530686 if self.doc.apply_discount_on == "Net Total":
687 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530688 else:
689 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530690
Nabin Haite7679702015-02-20 14:40:35 +0530691 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530692 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530693 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
694 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530695 elif tax.row_id in actual_taxes_dict:
696 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
697 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530698
Ankush Menat494bd9e2022-03-28 18:52:46 +0530699 return flt(
700 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
701 )
Nabin Hait3237c752015-02-17 11:11:11 +0530702
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530703 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100704 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530705 total_allocated_amount = sum(
706 flt(adv.allocated_amount, adv.precision("allocated_amount"))
707 for adv in self.doc.get("advances")
708 )
Nabin Hait3237c752015-02-17 11:11:11 +0530709
Nabin Haite7679702015-02-20 14:40:35 +0530710 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530711
Faris Ansari6041f5c2018-02-08 13:33:52 +0530712 grand_total = self.doc.rounded_total or self.doc.grand_total
713
Nabin Hait289ffb72016-02-08 11:06:55 +0530714 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530715 invoice_total = flt(
716 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
717 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530718 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530719 base_write_off_amount = flt(
720 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
721 self.doc.precision("base_write_off_amount"),
722 )
723 invoice_total = (
724 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
725 - base_write_off_amount
726 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530727
Nabin Haitadc09232016-02-09 10:31:11 +0530728 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530729 frappe.throw(
730 _("Advance amount cannot be greater than {0} {1}").format(
731 self.doc.party_account_currency, invoice_total
732 )
733 )
Nabin Hait3237c752015-02-17 11:11:11 +0530734
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100735 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530736 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530737 self.doc.write_off_amount = 0
738
Nabin Hait3237c752015-02-17 11:11:11 +0530739 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530740 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530741
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530742 def is_internal_invoice(self):
743 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530744 Checks if its an internal transfer invoice
745 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530746 """
747
Ankush Menat494bd9e2022-03-28 18:52:46 +0530748 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530749 return True
750
751 return False
752
Nabin Hait3237c752015-02-17 11:11:11 +0530753 def calculate_outstanding_amount(self):
754 # NOTE:
755 # write_off_amount is only for POS Invoice
756 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530757 if self.doc.doctype == "Sales Invoice":
758 self.calculate_paid_amount()
759
Ankush Menat494bd9e2022-03-28 18:52:46 +0530760 if (
761 self.doc.is_return
762 and self.doc.return_against
763 and not self.doc.get("is_pos")
764 or self.is_internal_invoice()
765 ):
766 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530767
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530768 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530769 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530770
Nabin Hait877e1bb2017-11-17 12:27:43 +0530771 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
772 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530773 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
774
Nabin Hait877e1bb2017-11-17 12:27:43 +0530775 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530776 total_amount_to_pay = flt(
777 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
778 self.doc.precision("grand_total"),
779 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530780 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530781 total_amount_to_pay = flt(
782 flt(base_grand_total, self.doc.precision("base_grand_total"))
783 - self.doc.total_advance
784 - flt(self.doc.base_write_off_amount),
785 self.doc.precision("base_grand_total"),
786 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530787
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530788 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530789 change_amount = 0
790
Ankush Menat494bd9e2022-03-28 18:52:46 +0530791 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530792 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530793 change_amount = (
794 self.doc.change_amount
795 if self.doc.party_account_currency == self.doc.currency
796 else self.doc.base_change_amount
797 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530798
Ankush Menat494bd9e2022-03-28 18:52:46 +0530799 paid_amount = (
800 self.doc.paid_amount
801 if self.doc.party_account_currency == self.doc.currency
802 else self.doc.base_paid_amount
803 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530804
Ankush Menat494bd9e2022-03-28 18:52:46 +0530805 self.doc.outstanding_amount = flt(
806 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
807 self.doc.precision("outstanding_amount"),
808 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530809
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530810 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530811 self.doc.doctype == "Sales Invoice"
812 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530813 and self.doc.get("pos_profile")
814 and self.doc.get("is_consolidated")
815 ):
816 write_off_limit = flt(
817 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
818 )
819 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
820 self.doc.write_off_outstanding_amount_automatically = 1
821
822 if (
823 self.doc.doctype == "Sales Invoice"
824 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530825 and self.doc.get("is_return")
826 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530827 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530828 self.set_total_amount_to_default_mop(total_amount_to_pay)
829 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530830
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530831 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530832
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530833 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530834
835 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530836 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530837 payment.amount = flt(payment.amount)
838 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530839 paid_amount += payment.amount
840 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530841 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530842 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530843
Manas Solankida486ee2018-07-06 12:36:57 +0530844 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
845 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530846 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530847
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530848 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
849 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
850
Nabin Hait3bb1a422016-08-02 16:41:10 +0530851 def calculate_change_amount(self):
852 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530853 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530854 grand_total = self.doc.rounded_total or self.doc.grand_total
855 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530856
Ankush Menat494bd9e2022-03-28 18:52:46 +0530857 if (
858 self.doc.doctype == "Sales Invoice"
859 and self.doc.paid_amount > grand_total
860 and not self.doc.is_return
861 and any(d.type == "Cash" for d in self.doc.payments)
862 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530863 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530864 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530865 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530866
Ankush Menat494bd9e2022-03-28 18:52:46 +0530867 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530868 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530869 )
mbauskar36b51892016-01-18 16:31:10 +0530870
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530871 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530872 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530873 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530874 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530875 )
876 self.doc.base_write_off_amount = flt(
877 self.doc.write_off_amount * self.doc.conversion_rate,
878 self.doc.precision("base_write_off_amount"),
879 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530880
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530881 self.calculate_outstanding_amount()
882
mbauskar36b51892016-01-18 16:31:10 +0530883 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530884 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530885 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530886 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530887 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530888 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530889 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530890 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530891
Ankush Menat494bd9e2022-03-28 18:52:46 +0530892 if pricing_rule.margin_rate_or_amount and (
893 (
894 pricing_rule.currency == self.doc.currency
895 and pricing_rule.margin_type in ["Amount", "Percentage"]
896 )
897 or pricing_rule.margin_type == "Percentage"
898 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530899 item.margin_type = pricing_rule.margin_type
900 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530901 has_margin = True
902
903 if not has_margin:
904 item.margin_type = None
905 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530906
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530907 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
908 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530909 item.margin_rate_or_amount = flt(
910 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
911 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530912 item.rate_with_margin = item.rate
913
914 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530915 margin_value = (
916 item.margin_rate_or_amount
917 if item.margin_type == "Amount"
918 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
919 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530920 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530921 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530922
Shreya Shahbe690ef2017-11-14 17:22:41 +0530923 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530924
925 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530926 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530927
Subin Tom7d627df2021-08-23 11:05:07 +0530928 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530929 total_paid_amount = 0
930 for payment in self.doc.get("payments"):
931 total_paid_amount += (
932 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530933 )
934
Deepesh Garg06e8e282022-10-31 19:58:46 +0530935 pending_amount = total_amount_to_pay - total_paid_amount
936
937 if pending_amount > 0:
938 default_mode_of_payment = frappe.db.get_value(
939 "POS Payment Method",
940 {"parent": self.doc.pos_profile, "default": 1},
941 ["mode_of_payment"],
942 as_dict=1,
943 )
944
945 if default_mode_of_payment:
946 self.doc.payments = []
947 self.doc.append(
948 "payments",
949 {
950 "mode_of_payment": default_mode_of_payment.mode_of_payment,
951 "amount": pending_amount,
952 "default": 1,
953 },
954 )
955
Deepesh Garg0ebace52020-02-25 13:21:16 +0530956
Nabin Hait9c421612017-07-20 13:32:01 +0530957def get_itemised_tax_breakup_html(doc):
958 if not doc.taxes:
959 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530960
Nabin Hait9c421612017-07-20 13:32:01 +0530961 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530962 tax_accounts = []
963 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530964 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530965 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530966 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530967 tax_accounts.append(tax.description)
968
Sagar Vora4205f562023-07-24 18:37:36 +0530969 with temporary_flag("company", doc.company):
970 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530971 itemised_tax_data = get_itemised_tax_breakup_data(doc)
972 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530973 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530974
Nabin Hait9c421612017-07-20 13:32:01 +0530975 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530976 "templates/includes/itemised_tax_breakup.html",
977 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530978 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530979 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +0530980 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530981 doc=doc,
982 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530983 )
Nabin Hait852cb642017-07-05 12:58:19 +0530984
Ankush Menat494bd9e2022-03-28 18:52:46 +0530985
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530986@frappe.whitelist()
987def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +0530988 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +0530989 with temporary_flag("company", company):
990 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530991
Ankush Menat494bd9e2022-03-28 18:52:46 +0530992
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530993@erpnext.allow_regional
994def get_regional_round_off_accounts(company, account_list):
995 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530996
Ankush Menat494bd9e2022-03-28 18:52:46 +0530997
rohitwaghchaured4526682017-12-28 14:20:13 +0530998@erpnext.allow_regional
999def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301000 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +05301001 pass
1002
Ankush Menat494bd9e2022-03-28 18:52:46 +05301003
Nabin Haitb962fc12017-07-17 18:02:31 +05301004@erpnext.allow_regional
1005def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
1006 return [_("Item"), _("Taxable Amount")] + tax_accounts
1007
Ankush Menat494bd9e2022-03-28 18:52:46 +05301008
Nabin Haitb962fc12017-07-17 18:02:31 +05301009@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +05301010def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +05301011 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301012
1013 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1014
DaizyModib84deec2023-07-21 17:26:35 +05301015 itemised_tax_data = []
1016 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301017 itemised_tax_data.append(
1018 frappe._dict(
1019 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes}
1020 )
1021 )
DaizyModib84deec2023-07-21 17:26:35 +05301022
1023 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301024
Ankush Menat494bd9e2022-03-28 18:52:46 +05301025
Nabin Hait34c551d2019-07-03 10:34:31 +05301026def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301027 itemised_tax = {}
1028 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301029 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301030 continue
1031
Nabin Haitb962fc12017-07-17 18:02:31 +05301032 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301033 if item_tax_map:
1034 for item_code, tax_data in item_tax_map.items():
1035 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301036
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301037 tax_rate = 0.0
1038 tax_amount = 0.0
1039
Nabin Hait2e4de832017-09-19 14:53:16 +05301040 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301041 tax_rate = flt(tax_data[0])
1042 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301043 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301044 tax_rate = flt(tax_data)
1045
Ankush Menat494bd9e2022-03-28 18:52:46 +05301046 itemised_tax[item_code][tax.description] = frappe._dict(
1047 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1048 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301049
Nabin Hait34c551d2019-07-03 10:34:31 +05301050 if with_tax_account:
1051 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1052
Nabin Haitb962fc12017-07-17 18:02:31 +05301053 return itemised_tax
1054
Ankush Menat494bd9e2022-03-28 18:52:46 +05301055
Nabin Haitb962fc12017-07-17 18:02:31 +05301056def get_itemised_taxable_amount(items):
1057 itemised_taxable_amount = frappe._dict()
1058 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301059 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301060 itemised_taxable_amount.setdefault(item_code, 0)
1061 itemised_taxable_amount[item_code] += item.net_amount
1062
Nabin Haitcaab5822017-08-24 16:22:28 +05301063 return itemised_taxable_amount
1064
Ankush Menat494bd9e2022-03-28 18:52:46 +05301065
DaizyModi6f376cf2023-07-24 18:02:42 +05301066def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301067 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301068 for taxes in itemised_tax:
1069 for row in taxes.values():
1070 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1071 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301072
Ankush Menat494bd9e2022-03-28 18:52:46 +05301073
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301074class init_landed_taxes_and_totals(object):
1075 def __init__(self, doc):
1076 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301077 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301078 self.set_account_currency()
1079 self.set_exchange_rate()
1080 self.set_amounts_in_company_currency()
1081
1082 def set_account_currency(self):
1083 company_currency = erpnext.get_company_currency(self.doc.company)
1084 for d in self.doc.get(self.tax_field):
1085 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301086 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301087 d.account_currency = account_currency or company_currency
1088
1089 def set_exchange_rate(self):
1090 company_currency = erpnext.get_company_currency(self.doc.company)
1091 for d in self.doc.get(self.tax_field):
1092 if d.account_currency == company_currency:
1093 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301094 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301095 d.exchange_rate = get_exchange_rate(
1096 self.doc.posting_date,
1097 account=d.expense_account,
1098 account_currency=d.account_currency,
1099 company=self.doc.company,
1100 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301101
1102 if not d.exchange_rate:
1103 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1104
1105 def set_amounts_in_company_currency(self):
1106 for d in self.doc.get(self.tax_field):
1107 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301108 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))