blob: 96284d612fa1787dd4de76bd887253bdf84c86b3 [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
mergify[bot]10c666b2023-10-06 14:43:13 +0530196 if (
197 not item.qty and self.doc.get("is_return") and self.doc.get("doctype") != "Purchase Receipt"
198 ):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530199 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530200 elif not item.qty and self.doc.get("is_debit_note"):
201 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530202 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530203 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530204
Nabin Haite7679702015-02-20 14:40:35 +0530205 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530206
Ankush Menat494bd9e2022-03-28 18:52:46 +0530207 self._set_in_company_currency(
208 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
209 )
Nabin Hait3237c752015-02-17 11:11:11 +0530210
Nabin Haite7679702015-02-20 14:40:35 +0530211 item.item_tax_amount = 0.0
212
213 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530214 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530215 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530216 val = flt(
217 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
218 )
Nabin Haite7679702015-02-20 14:40:35 +0530219 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530220
221 def initialize_taxes(self):
222 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530223 if not self.discount_amount_applied:
224 validate_taxes_and_charges(tax)
225 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530226
Ankush Menat494bd9e2022-03-28 18:52:46 +0530227 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530228 tax.item_wise_tax_detail = {}
229
Ankush Menat494bd9e2022-03-28 18:52:46 +0530230 tax_fields = [
231 "total",
232 "tax_amount_after_discount_amount",
233 "tax_amount_for_current_item",
234 "grand_total_for_current_item",
235 "tax_fraction_for_current_item",
236 "grand_total_fraction_for_current_item",
237 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530238
Ankush Menat494bd9e2022-03-28 18:52:46 +0530239 if tax.charge_type != "Actual" and not (
240 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
241 ):
242 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530243
244 for fieldname in tax_fields:
245 tax.set(fieldname, 0.0)
246
Nabin Hait3237c752015-02-17 11:11:11 +0530247 self.doc.round_floats_in(tax)
248
Nabin Hait3237c752015-02-17 11:11:11 +0530249 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530250 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530251 return
Nabin Hait3237c752015-02-17 11:11:11 +0530252
marination91982d12023-01-24 18:03:53 +0530253 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530254 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
255 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530256 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530257 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530258 (
259 tax.tax_fraction_for_current_item,
260 inclusive_tax_amount_per_qty,
261 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530262
Ankush Menat494bd9e2022-03-28 18:52:46 +0530263 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530264 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
265 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530266 tax.grand_total_fraction_for_current_item = (
267 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530268 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530269 )
Nabin Hait3237c752015-02-17 11:11:11 +0530270
271 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530272 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530273
Ankush Menat494bd9e2022-03-28 18:52:46 +0530274 if (
275 not self.discount_amount_applied
276 and item.qty
277 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
278 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530279 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
280
281 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530282 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530283 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530284
Nabin Haite7679702015-02-20 14:40:35 +0530285 self._set_in_company_currency(item, ["net_rate", "net_amount"])
286
Nabin Hait3237c752015-02-17 11:11:11 +0530287 def _load_item_tax_rate(self, item_tax_rate):
288 return json.loads(item_tax_rate) if item_tax_rate else {}
289
290 def get_current_tax_fraction(self, tax, item_tax_map):
291 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530292 Get tax fraction for calculating tax exclusive amount
293 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530294 """
295 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530296 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530297
298 if cint(tax.included_in_print_rate):
299 tax_rate = self._get_tax_rate(tax, item_tax_map)
300
301 if tax.charge_type == "On Net Total":
302 current_tax_fraction = tax_rate / 100.0
303
304 elif tax.charge_type == "On Previous Row Amount":
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 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530308
309 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530310 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
311 cint(tax.row_id) - 1
312 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530313
Nabin Hait19ea7212020-08-11 20:34:57 +0530314 elif tax.charge_type == "On Item Quantity":
315 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530316
Nabin Hait19ea7212020-08-11 20:34:57 +0530317 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
318 current_tax_fraction *= -1.0
319 inclusive_tax_amount_per_qty *= -1.0
320
321 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530322
323 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530324 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530325 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
326 else:
327 return tax.rate
328
329 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530330 self.doc.total_qty = (
331 self.doc.total
332 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530333
marination91982d12023-01-24 18:03:53 +0530334 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530335 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530336 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530337 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530338 self.doc.net_total += item.net_amount
339 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530340
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530341 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530342
Subin Toma8e2c022021-11-16 19:06:49 +0530343 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530344
345 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530346 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530347 return
348
Subin Tomaf1fce02021-11-10 16:49:12 +0530349 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530350 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
351 shipping_rule.apply(self.doc)
352
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530353 self._calculate()
354
Nabin Hait3237c752015-02-17 11:11:11 +0530355 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530356 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
357 "rounding_adjustment"
358 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530359 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530360 self.doc.rounding_adjustment = 0
361
Nabin Hait3237c752015-02-17 11:11:11 +0530362 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530363 actual_tax_dict = dict(
364 [
365 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
366 for tax in self.doc.get("taxes")
367 if tax.charge_type == "Actual"
368 ]
369 )
Nabin Hait3237c752015-02-17 11:11:11 +0530370
marination91982d12023-01-24 18:03:53 +0530371 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530372 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530373 for i, tax in enumerate(self.doc.get("taxes")):
374 # tax_amount represents the amount of tax for the current step
375 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
Dany Robert3ead2892023-08-22 14:41:07 +0000376 if frappe.flags.round_row_wise_tax:
377 current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530378
379 # Adjust divisional loss to the last item
380 if tax.charge_type == "Actual":
381 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530382 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530383 current_tax_amount += actual_tax_dict[tax.idx]
384
Nabin Hait2b019ed2015-02-22 23:03:07 +0530385 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530386 if tax.charge_type != "Actual" and not (
387 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
388 ):
389 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530390
Nabin Hait3237c752015-02-17 11:11:11 +0530391 # store tax_amount for current item as it will be used for
392 # charge type = 'On Previous Row Amount'
393 tax.tax_amount_for_current_item = current_tax_amount
394
Nabin Hait2b019ed2015-02-22 23:03:07 +0530395 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530396 tax.tax_amount_after_discount_amount += current_tax_amount
397
Nabin Haitcd951342017-07-31 18:07:45 +0530398 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530399
Nabin Hait3237c752015-02-17 11:11:11 +0530400 # note: grand_total_for_current_item contains the contribution of
401 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530402 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530403 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530404 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530405 tax.grand_total_for_current_item = flt(
406 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
407 )
Nabin Hait3237c752015-02-17 11:11:11 +0530408
409 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530410 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530411 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530412 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530413
414 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530415 self.set_cumulative_total(i, tax)
416
Deepesh Gargb6705882021-04-14 11:20:27 +0530417 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530418
Nabin Hait3237c752015-02-17 11:11:11 +0530419 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530420 if (
421 i == (len(self.doc.get("taxes")) - 1)
422 and self.discount_amount_applied
423 and self.doc.discount_amount
424 and self.doc.apply_discount_on == "Grand Total"
425 and not rounding_adjustment_computed
426 ):
427 self.doc.rounding_adjustment = flt(
428 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
429 self.doc.precision("rounding_adjustment"),
430 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530431
Nabin Haitcd951342017-07-31 18:07:45 +0530432 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
433 # if just for valuation, do not add the tax amount in total
434 # if tax/charges is for deduction, multiply by -1
435 if getattr(tax, "category", None):
436 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530437 if self.doc.doctype in [
438 "Purchase Order",
439 "Purchase Invoice",
440 "Purchase Receipt",
441 "Supplier Quotation",
442 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530443 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530444 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530445
Nabin Haitcd951342017-07-31 18:07:45 +0530446 def set_cumulative_total(self, row_idx, tax):
447 tax_amount = tax.tax_amount_after_discount_amount
448 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
449
450 if row_idx == 0:
451 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
452 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530453 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530454
455 def get_current_tax_amount(self, item, tax, item_tax_map):
456 tax_rate = self._get_tax_rate(tax, item_tax_map)
457 current_tax_amount = 0.0
458
459 if tax.charge_type == "Actual":
460 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530461 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530462 current_tax_amount = (
463 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
464 )
Nabin Haite7679702015-02-20 14:40:35 +0530465
Nabin Hait3237c752015-02-17 11:11:11 +0530466 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530467 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530468 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530469 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
470 cint(tax.row_id) - 1
471 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530472 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530473 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
474 cint(tax.row_id) - 1
475 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530476 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530477 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530478
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530479 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530480 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530481
482 return current_tax_amount
483
Nabin Haite7679702015-02-20 14:40:35 +0530484 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
485 # store tax breakup for each item
486 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530487 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000488 if frappe.flags.round_row_wise_tax:
489 item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
490 if tax.item_wise_tax_detail.get(key):
491 item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
Dany Robert9e1b2c92023-08-24 05:02:14 +0000492 tax.item_wise_tax_detail[key] = [
493 tax_rate,
494 flt(item_wise_tax_amount, tax.precision("tax_amount")),
495 ]
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000496 else:
497 if tax.item_wise_tax_detail.get(key):
498 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
Nabin Haite7679702015-02-20 14:40:35 +0530499
Dany Robert9e1b2c92023-08-24 05:02:14 +0000500 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530501
Nabin Hait3237c752015-02-17 11:11:11 +0530502 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530503 if tax.account_head in frappe.flags.round_off_applicable_accounts:
504 tax.tax_amount = round(tax.tax_amount, 0)
505 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
506
Nabin Haite7679702015-02-20 14:40:35 +0530507 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530508 tax.tax_amount_after_discount_amount = flt(
509 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
510 )
Nabin Haitce245122015-02-22 20:14:49 +0530511
Deepesh Gargb6705882021-04-14 11:20:27 +0530512 def round_off_base_values(self, tax):
513 # Round off to nearest integer based on regional settings
514 if tax.account_head in frappe.flags.round_off_applicable_accounts:
515 tax.base_tax_amount = round(tax.base_tax_amount, 0)
516 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
517
Nabin Haita1bf43b2015-03-17 10:50:47 +0530518 def manipulate_grand_total_for_inclusive_tax(self):
519 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530520 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 +0530521 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530522 non_inclusive_tax_amount = sum(
523 flt(d.tax_amount_after_discount_amount)
524 for d in self.doc.get("taxes")
525 if not d.included_in_print_rate
526 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530527
Ankush Menat494bd9e2022-03-28 18:52:46 +0530528 diff = (
529 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
530 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530531
532 # If discount amount applied, deduct the discount amount
533 # because self.doc.total is always without discount, but last_tax.total is after discount
534 if self.discount_amount_applied and self.doc.discount_amount:
535 diff -= flt(self.doc.discount_amount)
536
537 diff = flt(diff, self.doc.precision("rounding_adjustment"))
538
Ankush Menat494bd9e2022-03-28 18:52:46 +0530539 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530540 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530541
542 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530543 if self.doc.get("taxes"):
544 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
545 else:
546 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530547
Subin Tom75a76e62021-10-29 16:45:04 +0530548 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530549 self.doc.total_taxes_and_charges = flt(
550 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
551 self.doc.precision("total_taxes_and_charges"),
552 )
Subin Tom75a76e62021-10-29 16:45:04 +0530553 else:
554 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530555
Nabin Hait2e4de832017-09-19 14:53:16 +0530556 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530557
Ankush Menat494bd9e2022-03-28 18:52:46 +0530558 if self.doc.doctype in [
559 "Quotation",
560 "Sales Order",
561 "Delivery Note",
562 "Sales Invoice",
563 "POS Invoice",
564 ]:
565 self.doc.base_grand_total = (
566 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
567 if self.doc.total_taxes_and_charges
568 else self.doc.base_net_total
569 )
Nabin Hait3237c752015-02-17 11:11:11 +0530570 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530571 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530572 for tax in self.doc.get("taxes"):
573 if tax.category in ["Valuation and Total", "Total"]:
574 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530575 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530576 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530577 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530578
Nabin Haite7679702015-02-20 14:40:35 +0530579 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530580
Ankush Menat494bd9e2022-03-28 18:52:46 +0530581 self.doc.base_grand_total = (
582 flt(self.doc.grand_total * self.doc.conversion_rate)
583 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530584 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530585 )
Nabin Hait3237c752015-02-17 11:11:11 +0530586
Ankush Menat494bd9e2022-03-28 18:52:46 +0530587 self._set_in_company_currency(
588 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
589 )
Nabin Hait3237c752015-02-17 11:11:11 +0530590
Nabin Haite7679702015-02-20 14:40:35 +0530591 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530592
Nabin Hait2e4de832017-09-19 14:53:16 +0530593 self.set_rounded_total()
594
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530595 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530596 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530597 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530598 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530599 if d.total_weight:
600 self.doc.total_net_weight += d.total_weight
601
Nabin Hait2e4de832017-09-19 14:53:16 +0530602 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530603 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530604 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530605
Saqib Ansari17445c72022-03-07 18:01:07 +0530606 if self.doc.meta.get_field("rounded_total"):
607 if self.doc.is_rounded_total_disabled():
608 self.doc.rounded_total = self.doc.base_rounded_total = 0
609 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530610
Ankush Menat494bd9e2022-03-28 18:52:46 +0530611 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
612 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
613 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530614
Ankush Menat494bd9e2022-03-28 18:52:46 +0530615 # if print_in_rate is set, we would have already calculated rounding adjustment
616 self.doc.rounding_adjustment += flt(
617 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
618 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530619
620 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530621
Nabin Hait3237c752015-02-17 11:11:11 +0530622 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530623 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530624 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530625 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530626 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530627
Nabin Hait3769d872015-12-18 13:12:02 +0530628 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530629 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530630 self.doc.discount_amount = flt(
631 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
632 * self.doc.additional_discount_percentage
633 / 100,
634 self.doc.precision("discount_amount"),
635 )
Nabin Hait3237c752015-02-17 11:11:11 +0530636
637 def apply_discount_amount(self):
638 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530639 if not self.doc.apply_discount_on:
640 frappe.throw(_("Please select Apply Discount On"))
641
Deepesh Garg3b159662022-08-21 17:51:05 +0530642 self.doc.base_discount_amount = flt(
643 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
644 )
645
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530646 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
647 "is_cash_or_non_trade_discount"
648 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530649 self.discount_amount_applied = True
650 return
651
Nabin Haite7679702015-02-20 14:40:35 +0530652 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530653 taxes = self.doc.get("taxes")
654 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530655
Nabin Haite7679702015-02-20 14:40:35 +0530656 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530657 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530658 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530659 distributed_amount = (
660 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
661 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530662
Nabin Haite7679702015-02-20 14:40:35 +0530663 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530664 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530665
Nabin Hait25bd84d2015-03-04 15:06:56 +0530666 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530667 if (
668 self.doc.apply_discount_on == "Net Total"
669 or not taxes
670 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530671 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530672 discount_amount_loss = flt(
673 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
674 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530675
Ankush Menat494bd9e2022-03-28 18:52:46 +0530676 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530677
Nabin Hait51e980d2015-10-10 18:10:05 +0530678 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 +0530679
Nabin Haite7679702015-02-20 14:40:35 +0530680 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530681
682 self.discount_amount_applied = True
683 self._calculate()
684 else:
685 self.doc.base_discount_amount = 0
686
Nabin Haite7679702015-02-20 14:40:35 +0530687 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530688 if self.doc.apply_discount_on == "Net Total":
689 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530690 else:
691 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530692
Nabin Haite7679702015-02-20 14:40:35 +0530693 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530694 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530695 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
696 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530697 elif tax.row_id in actual_taxes_dict:
698 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
699 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530700
Ankush Menat494bd9e2022-03-28 18:52:46 +0530701 return flt(
702 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
703 )
Nabin Hait3237c752015-02-17 11:11:11 +0530704
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530705 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100706 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530707 total_allocated_amount = sum(
708 flt(adv.allocated_amount, adv.precision("allocated_amount"))
709 for adv in self.doc.get("advances")
710 )
Nabin Hait3237c752015-02-17 11:11:11 +0530711
Nabin Haite7679702015-02-20 14:40:35 +0530712 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530713
Faris Ansari6041f5c2018-02-08 13:33:52 +0530714 grand_total = self.doc.rounded_total or self.doc.grand_total
715
Nabin Hait289ffb72016-02-08 11:06:55 +0530716 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530717 invoice_total = flt(
718 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
719 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530720 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530721 base_write_off_amount = flt(
722 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
723 self.doc.precision("base_write_off_amount"),
724 )
725 invoice_total = (
726 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
727 - base_write_off_amount
728 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530729
Nabin Haitadc09232016-02-09 10:31:11 +0530730 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530731 frappe.throw(
732 _("Advance amount cannot be greater than {0} {1}").format(
733 self.doc.party_account_currency, invoice_total
734 )
735 )
Nabin Hait3237c752015-02-17 11:11:11 +0530736
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100737 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530738 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530739 self.doc.write_off_amount = 0
740
Nabin Hait3237c752015-02-17 11:11:11 +0530741 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530742 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530743
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530744 def is_internal_invoice(self):
745 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530746 Checks if its an internal transfer invoice
747 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530748 """
749
Ankush Menat494bd9e2022-03-28 18:52:46 +0530750 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530751 return True
752
753 return False
754
Nabin Hait3237c752015-02-17 11:11:11 +0530755 def calculate_outstanding_amount(self):
756 # NOTE:
757 # write_off_amount is only for POS Invoice
758 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530759 if self.doc.doctype == "Sales Invoice":
760 self.calculate_paid_amount()
761
Ankush Menat494bd9e2022-03-28 18:52:46 +0530762 if (
763 self.doc.is_return
764 and self.doc.return_against
765 and not self.doc.get("is_pos")
766 or self.is_internal_invoice()
767 ):
768 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530769
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530770 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530771 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530772
Nabin Hait877e1bb2017-11-17 12:27:43 +0530773 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
774 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530775 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
776
Nabin Hait877e1bb2017-11-17 12:27:43 +0530777 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530778 total_amount_to_pay = flt(
779 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
780 self.doc.precision("grand_total"),
781 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530782 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530783 total_amount_to_pay = flt(
784 flt(base_grand_total, self.doc.precision("base_grand_total"))
785 - self.doc.total_advance
786 - flt(self.doc.base_write_off_amount),
787 self.doc.precision("base_grand_total"),
788 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530789
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530790 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530791 change_amount = 0
792
Ankush Menat494bd9e2022-03-28 18:52:46 +0530793 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530794 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530795 change_amount = (
796 self.doc.change_amount
797 if self.doc.party_account_currency == self.doc.currency
798 else self.doc.base_change_amount
799 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530800
Ankush Menat494bd9e2022-03-28 18:52:46 +0530801 paid_amount = (
802 self.doc.paid_amount
803 if self.doc.party_account_currency == self.doc.currency
804 else self.doc.base_paid_amount
805 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530806
Ankush Menat494bd9e2022-03-28 18:52:46 +0530807 self.doc.outstanding_amount = flt(
808 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
809 self.doc.precision("outstanding_amount"),
810 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530811
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530812 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530813 self.doc.doctype == "Sales Invoice"
814 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530815 and self.doc.get("pos_profile")
816 and self.doc.get("is_consolidated")
817 ):
818 write_off_limit = flt(
819 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
820 )
821 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
822 self.doc.write_off_outstanding_amount_automatically = 1
823
824 if (
825 self.doc.doctype == "Sales Invoice"
826 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530827 and self.doc.get("is_return")
828 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530829 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530830 self.set_total_amount_to_default_mop(total_amount_to_pay)
831 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530832
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530833 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530834
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530835 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530836
837 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530838 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530839 payment.amount = flt(payment.amount)
840 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530841 paid_amount += payment.amount
842 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530843 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530844 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530845
Manas Solankida486ee2018-07-06 12:36:57 +0530846 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
847 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530848 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530849
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530850 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
851 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
852
Nabin Hait3bb1a422016-08-02 16:41:10 +0530853 def calculate_change_amount(self):
854 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530855 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530856 grand_total = self.doc.rounded_total or self.doc.grand_total
857 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530858
Ankush Menat494bd9e2022-03-28 18:52:46 +0530859 if (
860 self.doc.doctype == "Sales Invoice"
861 and self.doc.paid_amount > grand_total
862 and not self.doc.is_return
863 and any(d.type == "Cash" for d in self.doc.payments)
864 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530865 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530866 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530867 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530868
Ankush Menat494bd9e2022-03-28 18:52:46 +0530869 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530870 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530871 )
mbauskar36b51892016-01-18 16:31:10 +0530872
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530873 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530874 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530875 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530876 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530877 )
878 self.doc.base_write_off_amount = flt(
879 self.doc.write_off_amount * self.doc.conversion_rate,
880 self.doc.precision("base_write_off_amount"),
881 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530882
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530883 self.calculate_outstanding_amount()
884
mbauskar36b51892016-01-18 16:31:10 +0530885 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530886 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530887 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530888 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530889 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530890 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530891 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530892 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530893
Ankush Menat494bd9e2022-03-28 18:52:46 +0530894 if pricing_rule.margin_rate_or_amount and (
895 (
896 pricing_rule.currency == self.doc.currency
897 and pricing_rule.margin_type in ["Amount", "Percentage"]
898 )
899 or pricing_rule.margin_type == "Percentage"
900 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530901 item.margin_type = pricing_rule.margin_type
902 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530903 has_margin = True
904
905 if not has_margin:
906 item.margin_type = None
907 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530908
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530909 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
910 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530911 item.margin_rate_or_amount = flt(
912 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
913 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530914 item.rate_with_margin = item.rate
915
916 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530917 margin_value = (
918 item.margin_rate_or_amount
919 if item.margin_type == "Amount"
920 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
921 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530922 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530923 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530924
Shreya Shahbe690ef2017-11-14 17:22:41 +0530925 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530926
927 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530928 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530929
Subin Tom7d627df2021-08-23 11:05:07 +0530930 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530931 total_paid_amount = 0
932 for payment in self.doc.get("payments"):
933 total_paid_amount += (
934 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530935 )
936
Deepesh Garg06e8e282022-10-31 19:58:46 +0530937 pending_amount = total_amount_to_pay - total_paid_amount
938
939 if pending_amount > 0:
940 default_mode_of_payment = frappe.db.get_value(
941 "POS Payment Method",
942 {"parent": self.doc.pos_profile, "default": 1},
943 ["mode_of_payment"],
944 as_dict=1,
945 )
946
947 if default_mode_of_payment:
948 self.doc.payments = []
949 self.doc.append(
950 "payments",
951 {
952 "mode_of_payment": default_mode_of_payment.mode_of_payment,
953 "amount": pending_amount,
954 "default": 1,
955 },
956 )
957
Deepesh Garg0ebace52020-02-25 13:21:16 +0530958
Nabin Hait9c421612017-07-20 13:32:01 +0530959def get_itemised_tax_breakup_html(doc):
960 if not doc.taxes:
961 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530962
Nabin Hait9c421612017-07-20 13:32:01 +0530963 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530964 tax_accounts = []
965 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530966 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530967 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530968 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530969 tax_accounts.append(tax.description)
970
Sagar Vora4205f562023-07-24 18:37:36 +0530971 with temporary_flag("company", doc.company):
972 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530973 itemised_tax_data = get_itemised_tax_breakup_data(doc)
974 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530975 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530976
Nabin Hait9c421612017-07-20 13:32:01 +0530977 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530978 "templates/includes/itemised_tax_breakup.html",
979 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530980 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530981 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +0530982 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530983 doc=doc,
984 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530985 )
Nabin Hait852cb642017-07-05 12:58:19 +0530986
Ankush Menat494bd9e2022-03-28 18:52:46 +0530987
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530988@frappe.whitelist()
989def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +0530990 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +0530991 with temporary_flag("company", company):
992 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530993
Ankush Menat494bd9e2022-03-28 18:52:46 +0530994
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530995@erpnext.allow_regional
996def get_regional_round_off_accounts(company, account_list):
997 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530998
Ankush Menat494bd9e2022-03-28 18:52:46 +0530999
rohitwaghchaured4526682017-12-28 14:20:13 +05301000@erpnext.allow_regional
1001def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301002 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +05301003 pass
1004
Ankush Menat494bd9e2022-03-28 18:52:46 +05301005
Nabin Haitb962fc12017-07-17 18:02:31 +05301006@erpnext.allow_regional
1007def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
1008 return [_("Item"), _("Taxable Amount")] + tax_accounts
1009
Ankush Menat494bd9e2022-03-28 18:52:46 +05301010
Nabin Haitb962fc12017-07-17 18:02:31 +05301011@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +05301012def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +05301013 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301014
1015 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1016
DaizyModib84deec2023-07-21 17:26:35 +05301017 itemised_tax_data = []
1018 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301019 itemised_tax_data.append(
1020 frappe._dict(
1021 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes}
1022 )
1023 )
DaizyModib84deec2023-07-21 17:26:35 +05301024
1025 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301026
Ankush Menat494bd9e2022-03-28 18:52:46 +05301027
Nabin Hait34c551d2019-07-03 10:34:31 +05301028def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301029 itemised_tax = {}
1030 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301031 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301032 continue
1033
Nabin Haitb962fc12017-07-17 18:02:31 +05301034 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301035 if item_tax_map:
1036 for item_code, tax_data in item_tax_map.items():
1037 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301038
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301039 tax_rate = 0.0
1040 tax_amount = 0.0
1041
Nabin Hait2e4de832017-09-19 14:53:16 +05301042 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301043 tax_rate = flt(tax_data[0])
1044 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301045 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301046 tax_rate = flt(tax_data)
1047
Ankush Menat494bd9e2022-03-28 18:52:46 +05301048 itemised_tax[item_code][tax.description] = frappe._dict(
1049 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1050 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301051
Nabin Hait34c551d2019-07-03 10:34:31 +05301052 if with_tax_account:
1053 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1054
Nabin Haitb962fc12017-07-17 18:02:31 +05301055 return itemised_tax
1056
Ankush Menat494bd9e2022-03-28 18:52:46 +05301057
Nabin Haitb962fc12017-07-17 18:02:31 +05301058def get_itemised_taxable_amount(items):
1059 itemised_taxable_amount = frappe._dict()
1060 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301061 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301062 itemised_taxable_amount.setdefault(item_code, 0)
1063 itemised_taxable_amount[item_code] += item.net_amount
1064
Nabin Haitcaab5822017-08-24 16:22:28 +05301065 return itemised_taxable_amount
1066
Ankush Menat494bd9e2022-03-28 18:52:46 +05301067
DaizyModi6f376cf2023-07-24 18:02:42 +05301068def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301069 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301070 for taxes in itemised_tax:
1071 for row in taxes.values():
1072 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1073 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301074
Ankush Menat494bd9e2022-03-28 18:52:46 +05301075
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301076class init_landed_taxes_and_totals(object):
1077 def __init__(self, doc):
1078 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301079 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301080 self.set_account_currency()
1081 self.set_exchange_rate()
1082 self.set_amounts_in_company_currency()
1083
1084 def set_account_currency(self):
1085 company_currency = erpnext.get_company_currency(self.doc.company)
1086 for d in self.doc.get(self.tax_field):
1087 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301088 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301089 d.account_currency = account_currency or company_currency
1090
1091 def set_exchange_rate(self):
1092 company_currency = erpnext.get_company_currency(self.doc.company)
1093 for d in self.doc.get(self.tax_field):
1094 if d.account_currency == company_currency:
1095 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301096 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301097 d.exchange_rate = get_exchange_rate(
1098 self.doc.posting_date,
1099 account=d.expense_account,
1100 account_currency=d.account_currency,
1101 company=self.doc.company,
1102 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301103
1104 if not d.exchange_rate:
1105 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1106
1107 def set_amounts_in_company_currency(self):
1108 for d in self.doc.get(self.tax_field):
1109 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301110 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))