blob: d90c14a0705222397d632ed6e9e02b57d0bff3de [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
Dany Robert50d56db2024-01-29 09:32:44 +053011from frappe.utils.deprecations import deprecated
Chillar Anand915b3432021-09-02 16:44:59 +053012
13import erpnext
Deepesh Gargbfc17e42020-12-25 18:34:39 +053014from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Chillar Anand915b3432021-09-02 16:44:59 +053015from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
16from erpnext.controllers.accounts_controller import (
17 validate_conversion_rate,
18 validate_inclusive_tax,
19 validate_taxes_and_charges,
20)
21from erpnext.stock.get_item_details import _get_item_tax_template
Sagar Vora4205f562023-07-24 18:37:36 +053022from erpnext.utilities.regional import temporary_flag
Chillar Anand915b3432021-09-02 16:44:59 +053023
Nabin Hait3237c752015-02-17 11:11:11 +053024
Akhil Narang3effaf22024-03-27 11:37:26 +053025class calculate_taxes_and_totals:
Raffael Meyer67cf7e12023-01-15 13:04:16 +010026 def __init__(self, doc: Document):
Nabin Hait3237c752015-02-17 11:11:11 +053027 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053028 frappe.flags.round_off_applicable_accounts = []
Dany Robertdfb5b882023-08-23 04:01:00 +000029 frappe.flags.round_row_wise_tax = frappe.db.get_single_value(
30 "Accounts Settings", "round_row_wise_tax"
Dany Robert3ead2892023-08-22 14:41:07 +000031 )
marination91982d12023-01-24 18:03:53 +053032
33 self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
34
Deepesh Garg6a5ef262021-02-19 14:30:23 +053035 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053036 self.calculate()
37
marination91982d12023-01-24 18:03:53 +053038 def filter_rows(self):
39 """Exclude rows, that do not fulfill the filter criteria, from totals computation."""
marinationcef7dfd2023-01-26 14:36:25 +053040 items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
marination91982d12023-01-24 18:03:53 +053041 return items
42
Nabin Hait3237c752015-02-17 11:11:11 +053043 def calculate(self):
marination91982d12023-01-24 18:03:53 +053044 if not len(self._items):
Nabin Haitb315acb2019-07-12 14:27:19 +053045 return
46
Nabin Hait3237c752015-02-17 11:11:11 +053047 self.discount_amount_applied = False
48 self._calculate()
49
50 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053051 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053052 self.apply_discount_amount()
53
Deepesh Garg3b159662022-08-21 17:51:05 +053054 # Update grand total as per cash and non trade discount
55 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
56 self.doc.grand_total -= self.doc.discount_amount
57 self.doc.base_grand_total -= self.doc.base_discount_amount
Dany Robert3a487bd2023-11-18 13:32:04 +000058 self.doc.rounding_adjustment = self.doc.base_rounding_adjustment = 0.0
Deepesh Garg318da162022-08-29 14:18:39 +053059 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053060
Deepesh Gargd596e0e2022-03-10 20:56:36 +053061 self.calculate_shipping_charges()
62
Nabin Haitbd00e812015-02-17 12:50:51 +053063 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053064 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053065
Nabin Hait852cb642017-07-05 12:58:19 +053066 if self.doc.meta.get_field("other_charges_calculation"):
67 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053068
69 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053070 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053071 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053072 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053073 self.initialize_taxes()
74 self.determine_exclusive_rate()
75 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053076 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053077 self.calculate_taxes()
Dany Robert50d56db2024-01-29 09:32:44 +053078 self.adjust_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053079 self.calculate_totals()
80 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053081 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053082
niralisataparae758a752022-10-03 16:39:35 +053083 def calculate_tax_withholding_net_total(self):
84 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053085 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053086 sum_base_net_amount = 0
marination91982d12023-01-24 18:03:53 +053087 for item in self._items:
niralisataparae758a752022-10-03 16:39:35 +053088 if hasattr(item, "apply_tds") and item.apply_tds:
89 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053090 sum_base_net_amount += item.base_net_amount
91
niralisataparae758a752022-10-03 16:39:35 +053092 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053093 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053094
Deepesh Gargef0d26c2020-01-06 15:34:15 +053095 def validate_item_tax_template(self):
marination91982d12023-01-24 18:03:53 +053096 for item in self._items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053097 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053098 item_doc = frappe.get_cached_doc("Item", item.item_code)
99 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +0530100 "net_rate": item.net_rate or item.rate,
Divyam Mistrye9fe10c2024-02-01 16:15:43 +0530101 "base_net_rate": item.base_net_rate or item.base_rate,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530102 "tax_category": self.doc.get("tax_category"),
103 "posting_date": self.doc.get("posting_date"),
104 "bill_date": self.doc.get("bill_date"),
105 "transaction_date": self.doc.get("transaction_date"),
106 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530107 }
108
109 item_group = item_doc.item_group
110 item_group_taxes = []
111
112 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530113 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530114 item_group_taxes += item_group_doc.taxes or []
115 item_group = item_group_doc.parent_item_group
116
117 item_taxes = item_doc.taxes or []
118
119 if not item_group_taxes and (not item_taxes):
120 # No validation if no taxes in item or item group
121 continue
122
123 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
124
Deepesh Garg18be7672021-06-06 13:25:34 +0530125 if taxes:
126 if item.item_tax_template not in taxes:
127 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530128 frappe.msgprint(
129 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
130 item.idx, frappe.bold(item.item_code)
131 )
132 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530133
Nabin Haite7679702015-02-20 14:40:35 +0530134 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530135 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530136 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530137 if not self.doc.currency or self.doc.currency == company_currency:
138 self.doc.currency = company_currency
139 self.doc.conversion_rate = 1.0
140 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530141 validate_conversion_rate(
142 self.doc.currency,
143 self.doc.conversion_rate,
144 self.doc.meta.get_label("conversion_rate"),
145 self.doc.company,
146 )
Nabin Hait3237c752015-02-17 11:11:11 +0530147
148 self.doc.conversion_rate = flt(self.doc.conversion_rate)
149
Nabin Hait3237c752015-02-17 11:11:11 +0530150 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530151 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530152 return
153
Nabin Hait3237c752015-02-17 11:11:11 +0530154 if not self.discount_amount_applied:
marination91982d12023-01-24 18:03:53 +0530155 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530156 self.doc.round_floats_in(item)
157
158 if item.discount_percentage == 100:
159 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530160 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530161 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530162 item.rate = flt(
Akhil Narang3effaf22024-03-27 11:37:26 +0530163 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)),
164 item.precision("rate"),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530165 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530166
Deepesh Garga83a0a02022-03-25 12:28:55 +0530167 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530168
Deepesh Garg97e102c2022-03-25 12:39:59 +0530169 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530170 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530171
Ankush Menat494bd9e2022-03-28 18:52:46 +0530172 if item.doctype in [
173 "Quotation Item",
174 "Sales Order Item",
175 "Delivery Note Item",
176 "Sales Invoice Item",
177 "POS Invoice Item",
178 "Purchase Invoice Item",
179 "Purchase Order Item",
180 "Purchase Receipt Item",
181 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530182 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530183 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530184 item.rate = flt(
Akhil Narang3effaf22024-03-27 11:37:26 +0530185 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)),
186 item.precision("rate"),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530187 )
Nabin Hait10c61372021-04-13 15:46:01 +0530188
Walstan Baptista37b826b2021-04-03 19:48:46 +0530189 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530190 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530191 else:
192 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530193
Nabin Hait64bfdd92019-04-23 13:37:19 +0530194 elif flt(item.price_list_rate) > 0:
195 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530196 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
197 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530198
Nabin Haite7679702015-02-20 14:40:35 +0530199 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530200
mergify[bot]10c666b2023-10-06 14:43:13 +0530201 if (
Akhil Narang3effaf22024-03-27 11:37:26 +0530202 not item.qty
203 and self.doc.get("is_return")
204 and self.doc.get("doctype") != "Purchase Receipt"
mergify[bot]10c666b2023-10-06 14:43:13 +0530205 ):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530206 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530207 elif not item.qty and self.doc.get("is_debit_note"):
208 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530209 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530210 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530211
Nabin Haite7679702015-02-20 14:40:35 +0530212 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530213
Ankush Menat494bd9e2022-03-28 18:52:46 +0530214 self._set_in_company_currency(
215 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
216 )
Nabin Hait3237c752015-02-17 11:11:11 +0530217
Nabin Haite7679702015-02-20 14:40:35 +0530218 item.item_tax_amount = 0.0
219
220 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530221 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530222 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530223 val = flt(
224 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
225 )
Nabin Haite7679702015-02-20 14:40:35 +0530226 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530227
228 def initialize_taxes(self):
229 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530230 if not self.discount_amount_applied:
231 validate_taxes_and_charges(tax)
232 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530233
Ankush Menat494bd9e2022-03-28 18:52:46 +0530234 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530235 tax.item_wise_tax_detail = {}
236
Ankush Menat494bd9e2022-03-28 18:52:46 +0530237 tax_fields = [
238 "total",
239 "tax_amount_after_discount_amount",
240 "tax_amount_for_current_item",
241 "grand_total_for_current_item",
242 "tax_fraction_for_current_item",
243 "grand_total_fraction_for_current_item",
244 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530245
Ankush Menat494bd9e2022-03-28 18:52:46 +0530246 if tax.charge_type != "Actual" and not (
247 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
248 ):
249 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530250
251 for fieldname in tax_fields:
252 tax.set(fieldname, 0.0)
253
Nabin Hait3237c752015-02-17 11:11:11 +0530254 self.doc.round_floats_in(tax)
255
Nabin Hait3237c752015-02-17 11:11:11 +0530256 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530257 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530258 return
Nabin Hait3237c752015-02-17 11:11:11 +0530259
marination91982d12023-01-24 18:03:53 +0530260 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530261 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
262 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530263 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530264 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530265 (
266 tax.tax_fraction_for_current_item,
267 inclusive_tax_amount_per_qty,
268 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530269
Ankush Menat494bd9e2022-03-28 18:52:46 +0530270 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530271 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
272 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530273 tax.grand_total_fraction_for_current_item = (
274 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530275 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530276 )
Nabin Hait3237c752015-02-17 11:11:11 +0530277
278 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530279 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530280
Ankush Menat494bd9e2022-03-28 18:52:46 +0530281 if (
282 not self.discount_amount_applied
283 and item.qty
284 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
285 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530286 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
287
Dany Robert50d56db2024-01-29 09:32:44 +0530288 item.net_amount = flt(amount / (1 + cumulated_tax_fraction), item.precision("net_amount"))
Nabin Haite7679702015-02-20 14:40:35 +0530289 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Akhil Narang3effaf22024-03-27 11:37:26 +0530290 item.discount_percentage = flt(
291 item.discount_percentage, item.precision("discount_percentage")
292 )
Nabin Hait3237c752015-02-17 11:11:11 +0530293
Nabin Haite7679702015-02-20 14:40:35 +0530294 self._set_in_company_currency(item, ["net_rate", "net_amount"])
295
Nabin Hait3237c752015-02-17 11:11:11 +0530296 def _load_item_tax_rate(self, item_tax_rate):
297 return json.loads(item_tax_rate) if item_tax_rate else {}
298
299 def get_current_tax_fraction(self, tax, item_tax_map):
300 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530301 Get tax fraction for calculating tax exclusive amount
302 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530303 """
304 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530305 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530306
307 if cint(tax.included_in_print_rate):
308 tax_rate = self._get_tax_rate(tax, item_tax_map)
309
310 if tax.charge_type == "On Net Total":
311 current_tax_fraction = tax_rate / 100.0
312
313 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530314 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
315 cint(tax.row_id) - 1
316 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530317
318 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530319 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
320 cint(tax.row_id) - 1
321 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530322
Nabin Hait19ea7212020-08-11 20:34:57 +0530323 elif tax.charge_type == "On Item Quantity":
324 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530325
Nabin Hait19ea7212020-08-11 20:34:57 +0530326 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
327 current_tax_fraction *= -1.0
328 inclusive_tax_amount_per_qty *= -1.0
329
330 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530331
332 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530333 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530334 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
335 else:
336 return tax.rate
337
338 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530339 self.doc.total_qty = (
340 self.doc.total
341 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530342
marination91982d12023-01-24 18:03:53 +0530343 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530344 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530345 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530346 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530347 self.doc.net_total += item.net_amount
348 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530349
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530350 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530351
Subin Toma8e2c022021-11-16 19:06:49 +0530352 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530353 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530354 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530355 return
356
Subin Tomaf1fce02021-11-10 16:49:12 +0530357 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530358 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
359 shipping_rule.apply(self.doc)
360
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530361 self._calculate()
362
Nabin Hait3237c752015-02-17 11:11:11 +0530363 def calculate_taxes(self):
Akhil Narang3effaf22024-03-27 11:37:26 +0530364 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment")
Saqib Ansari17445c72022-03-07 18:01:07 +0530365 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530366 self.doc.rounding_adjustment = 0
367
Nabin Hait3237c752015-02-17 11:11:11 +0530368 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530369 actual_tax_dict = dict(
370 [
371 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
372 for tax in self.doc.get("taxes")
373 if tax.charge_type == "Actual"
374 ]
375 )
Nabin Hait3237c752015-02-17 11:11:11 +0530376
marination91982d12023-01-24 18:03:53 +0530377 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530378 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530379 for i, tax in enumerate(self.doc.get("taxes")):
380 # tax_amount represents the amount of tax for the current step
381 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
Dany Robert3ead2892023-08-22 14:41:07 +0000382 if frappe.flags.round_row_wise_tax:
383 current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530384
385 # Adjust divisional loss to the last item
386 if tax.charge_type == "Actual":
387 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530388 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530389 current_tax_amount += actual_tax_dict[tax.idx]
390
Nabin Hait2b019ed2015-02-22 23:03:07 +0530391 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530392 if tax.charge_type != "Actual" and not (
393 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
394 ):
395 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530396
Nabin Hait3237c752015-02-17 11:11:11 +0530397 # store tax_amount for current item as it will be used for
398 # charge type = 'On Previous Row Amount'
399 tax.tax_amount_for_current_item = current_tax_amount
400
Nabin Hait2b019ed2015-02-22 23:03:07 +0530401 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530402 tax.tax_amount_after_discount_amount += current_tax_amount
403
Akhil Narang3effaf22024-03-27 11:37:26 +0530404 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(
405 current_tax_amount, tax
406 )
Nabin Hait3237c752015-02-17 11:11:11 +0530407
Nabin Hait3237c752015-02-17 11:11:11 +0530408 # note: grand_total_for_current_item contains the contribution of
409 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530410 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530411 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530412 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530413 tax.grand_total_for_current_item = flt(
414 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
415 )
Nabin Hait3237c752015-02-17 11:11:11 +0530416
417 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530418 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530419 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530420 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530421
422 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530423 self.set_cumulative_total(i, tax)
424
Deepesh Gargb6705882021-04-14 11:20:27 +0530425 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530426
Nabin Hait3237c752015-02-17 11:11:11 +0530427 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530428 if (
429 i == (len(self.doc.get("taxes")) - 1)
430 and self.discount_amount_applied
431 and self.doc.discount_amount
432 and self.doc.apply_discount_on == "Grand Total"
433 and not rounding_adjustment_computed
434 ):
435 self.doc.rounding_adjustment = flt(
436 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
437 self.doc.precision("rounding_adjustment"),
438 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530439
Nabin Haitcd951342017-07-31 18:07:45 +0530440 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
441 # if just for valuation, do not add the tax amount in total
442 # if tax/charges is for deduction, multiply by -1
443 if getattr(tax, "category", None):
444 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530445 if self.doc.doctype in [
446 "Purchase Order",
447 "Purchase Invoice",
448 "Purchase Receipt",
449 "Supplier Quotation",
450 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530451 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530452 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530453
Nabin Haitcd951342017-07-31 18:07:45 +0530454 def set_cumulative_total(self, row_idx, tax):
455 tax_amount = tax.tax_amount_after_discount_amount
456 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
457
458 if row_idx == 0:
459 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
460 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530461 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530462
463 def get_current_tax_amount(self, item, tax, item_tax_map):
464 tax_rate = self._get_tax_rate(tax, item_tax_map)
465 current_tax_amount = 0.0
466
467 if tax.charge_type == "Actual":
468 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530469 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Akhil Narang3effaf22024-03-27 11:37:26 +0530470 current_tax_amount = item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
Nabin Haite7679702015-02-20 14:40:35 +0530471
Nabin Hait3237c752015-02-17 11:11:11 +0530472 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530473 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530474 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530475 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
476 cint(tax.row_id) - 1
477 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530478 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530479 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
480 cint(tax.row_id) - 1
481 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530482 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530483 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530484
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530485 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530486 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530487
488 return current_tax_amount
489
Nabin Haite7679702015-02-20 14:40:35 +0530490 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
491 # store tax breakup for each item
492 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530493 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000494 if frappe.flags.round_row_wise_tax:
495 item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
496 if tax.item_wise_tax_detail.get(key):
497 item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
Dany Robert9e1b2c92023-08-24 05:02:14 +0000498 tax.item_wise_tax_detail[key] = [
499 tax_rate,
500 flt(item_wise_tax_amount, tax.precision("tax_amount")),
501 ]
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000502 else:
503 if tax.item_wise_tax_detail.get(key):
504 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
Nabin Haite7679702015-02-20 14:40:35 +0530505
Dany Robert9e1b2c92023-08-24 05:02:14 +0000506 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530507
Nabin Hait3237c752015-02-17 11:11:11 +0530508 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530509 if tax.account_head in frappe.flags.round_off_applicable_accounts:
510 tax.tax_amount = round(tax.tax_amount, 0)
511 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
512
Nabin Haite7679702015-02-20 14:40:35 +0530513 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530514 tax.tax_amount_after_discount_amount = flt(
515 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
516 )
Nabin Haitce245122015-02-22 20:14:49 +0530517
Deepesh Gargb6705882021-04-14 11:20:27 +0530518 def round_off_base_values(self, tax):
519 # Round off to nearest integer based on regional settings
520 if tax.account_head in frappe.flags.round_off_applicable_accounts:
521 tax.base_tax_amount = round(tax.base_tax_amount, 0)
522 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
523
Dany Robert50d56db2024-01-29 09:32:44 +0530524 @deprecated
Nabin Haita1bf43b2015-03-17 10:50:47 +0530525 def manipulate_grand_total_for_inclusive_tax(self):
Dany Robert50d56db2024-01-29 09:32:44 +0530526 # for backward compatablility - if in case used by an external application
527 return self.adjust_grand_total_for_inclusive_tax()
528
529 def adjust_grand_total_for_inclusive_tax(self):
Nabin Haita1bf43b2015-03-17 10:50:47 +0530530 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530531 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 +0530532 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530533 non_inclusive_tax_amount = sum(
534 flt(d.tax_amount_after_discount_amount)
535 for d in self.doc.get("taxes")
536 if not d.included_in_print_rate
537 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530538
Ankush Menat494bd9e2022-03-28 18:52:46 +0530539 diff = (
540 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
541 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530542
543 # If discount amount applied, deduct the discount amount
544 # because self.doc.total is always without discount, but last_tax.total is after discount
545 if self.discount_amount_applied and self.doc.discount_amount:
546 diff -= flt(self.doc.discount_amount)
547
548 diff = flt(diff, self.doc.precision("rounding_adjustment"))
549
Ankush Menat494bd9e2022-03-28 18:52:46 +0530550 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Dany Robert50d56db2024-01-29 09:32:44 +0530551 self.doc.grand_total_diff = diff
552 else:
553 self.doc.grand_total_diff = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530554
555 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530556 if self.doc.get("taxes"):
Dany Robert50d56db2024-01-29 09:32:44 +0530557 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(
558 self.doc.get("grand_total_diff")
559 )
Subin Tom75a76e62021-10-29 16:45:04 +0530560 else:
561 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530562
Subin Tom75a76e62021-10-29 16:45:04 +0530563 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530564 self.doc.total_taxes_and_charges = flt(
Dany Robert50d56db2024-01-29 09:32:44 +0530565 self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530566 self.doc.precision("total_taxes_and_charges"),
567 )
Subin Tom75a76e62021-10-29 16:45:04 +0530568 else:
569 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530570
Nabin Hait2e4de832017-09-19 14:53:16 +0530571 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530572
Ankush Menat494bd9e2022-03-28 18:52:46 +0530573 if self.doc.doctype in [
574 "Quotation",
575 "Sales Order",
576 "Delivery Note",
577 "Sales Invoice",
578 "POS Invoice",
579 ]:
580 self.doc.base_grand_total = (
581 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
582 if self.doc.total_taxes_and_charges
583 else self.doc.base_net_total
584 )
Nabin Hait3237c752015-02-17 11:11:11 +0530585 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530586 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530587 for tax in self.doc.get("taxes"):
588 if tax.category in ["Valuation and Total", "Total"]:
589 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530590 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530591 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530592 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530593
Nabin Haite7679702015-02-20 14:40:35 +0530594 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530595
Ankush Menat494bd9e2022-03-28 18:52:46 +0530596 self.doc.base_grand_total = (
597 flt(self.doc.grand_total * self.doc.conversion_rate)
598 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530599 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530600 )
Nabin Hait3237c752015-02-17 11:11:11 +0530601
Akhil Narang3effaf22024-03-27 11:37:26 +0530602 self._set_in_company_currency(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530603
Nabin Haite7679702015-02-20 14:40:35 +0530604 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530605
Nabin Hait2e4de832017-09-19 14:53:16 +0530606 self.set_rounded_total()
607
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530608 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530609 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530610 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530611 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530612 if d.total_weight:
613 self.doc.total_net_weight += d.total_weight
614
Nabin Hait2e4de832017-09-19 14:53:16 +0530615 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530616 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530617 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530618
Saqib Ansari17445c72022-03-07 18:01:07 +0530619 if self.doc.meta.get_field("rounded_total"):
620 if self.doc.is_rounded_total_disabled():
621 self.doc.rounded_total = self.doc.base_rounded_total = 0
622 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530623
Ankush Menat494bd9e2022-03-28 18:52:46 +0530624 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
625 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
626 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530627
Dany Robert50d56db2024-01-29 09:32:44 +0530628 # rounding adjustment should always be the difference vetween grand and rounded total
629 self.doc.rounding_adjustment = flt(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530630 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
631 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530632
633 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530634
Nabin Hait3237c752015-02-17 11:11:11 +0530635 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530636 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530637 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530638 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530639 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530640
Nabin Hait3769d872015-12-18 13:12:02 +0530641 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530642 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530643 self.doc.discount_amount = flt(
644 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
645 * self.doc.additional_discount_percentage
646 / 100,
647 self.doc.precision("discount_amount"),
648 )
Nabin Hait3237c752015-02-17 11:11:11 +0530649
650 def apply_discount_amount(self):
651 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530652 if not self.doc.apply_discount_on:
653 frappe.throw(_("Please select Apply Discount On"))
654
Deepesh Garg3b159662022-08-21 17:51:05 +0530655 self.doc.base_discount_amount = flt(
Akhil Narang3effaf22024-03-27 11:37:26 +0530656 self.doc.discount_amount * self.doc.conversion_rate,
657 self.doc.precision("base_discount_amount"),
Deepesh Garg3b159662022-08-21 17:51:05 +0530658 )
659
Akhil Narang3effaf22024-03-27 11:37:26 +0530660 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530661 self.discount_amount_applied = True
662 return
663
Nabin Haite7679702015-02-20 14:40:35 +0530664 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530665 taxes = self.doc.get("taxes")
666 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530667
Nabin Haite7679702015-02-20 14:40:35 +0530668 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530669 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530670 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530671 distributed_amount = (
672 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
673 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530674
Nabin Haite7679702015-02-20 14:40:35 +0530675 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530676 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530677
Nabin Hait25bd84d2015-03-04 15:06:56 +0530678 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530679 if (
680 self.doc.apply_discount_on == "Net Total"
681 or not taxes
682 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530683 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530684 discount_amount_loss = flt(
Akhil Narang3effaf22024-03-27 11:37:26 +0530685 self.doc.net_total - net_total - self.doc.discount_amount,
686 self.doc.precision("net_total"),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530687 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530688
Akhil Narang3effaf22024-03-27 11:37:26 +0530689 item.net_amount = flt(
690 item.net_amount + discount_amount_loss, item.precision("net_amount")
691 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530692
Akhil Narang3effaf22024-03-27 11:37:26 +0530693 item.net_rate = (
694 flt(item.net_amount / item.qty, item.precision("net_rate")) if item.qty else 0
695 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530696
Nabin Haite7679702015-02-20 14:40:35 +0530697 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530698
699 self.discount_amount_applied = True
700 self._calculate()
701 else:
702 self.doc.base_discount_amount = 0
703
Nabin Haite7679702015-02-20 14:40:35 +0530704 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530705 if self.doc.apply_discount_on == "Net Total":
706 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530707 else:
708 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530709
Nabin Haite7679702015-02-20 14:40:35 +0530710 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530711 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530712 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
713 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530714 elif tax.row_id in actual_taxes_dict:
715 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
716 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530717
Ankush Menat494bd9e2022-03-28 18:52:46 +0530718 return flt(
719 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
720 )
Nabin Hait3237c752015-02-17 11:11:11 +0530721
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530722 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100723 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530724 total_allocated_amount = sum(
725 flt(adv.allocated_amount, adv.precision("allocated_amount"))
726 for adv in self.doc.get("advances")
727 )
Nabin Hait3237c752015-02-17 11:11:11 +0530728
Nabin Haite7679702015-02-20 14:40:35 +0530729 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530730
Faris Ansari6041f5c2018-02-08 13:33:52 +0530731 grand_total = self.doc.rounded_total or self.doc.grand_total
732
Nabin Hait289ffb72016-02-08 11:06:55 +0530733 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530734 invoice_total = flt(
735 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
736 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530737 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530738 base_write_off_amount = flt(
739 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
740 self.doc.precision("base_write_off_amount"),
741 )
742 invoice_total = (
743 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
744 - base_write_off_amount
745 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530746
Nabin Haitadc09232016-02-09 10:31:11 +0530747 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530748 frappe.throw(
749 _("Advance amount cannot be greater than {0} {1}").format(
750 self.doc.party_account_currency, invoice_total
751 )
752 )
Nabin Hait3237c752015-02-17 11:11:11 +0530753
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100754 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530755 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530756 self.doc.write_off_amount = 0
757
Nabin Hait3237c752015-02-17 11:11:11 +0530758 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530759 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530760
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530761 def is_internal_invoice(self):
762 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530763 Checks if its an internal transfer invoice
764 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530765 """
766
Ankush Menat494bd9e2022-03-28 18:52:46 +0530767 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530768 return True
769
770 return False
771
Nabin Hait3237c752015-02-17 11:11:11 +0530772 def calculate_outstanding_amount(self):
773 # NOTE:
774 # write_off_amount is only for POS Invoice
775 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530776 if self.doc.doctype == "Sales Invoice":
777 self.calculate_paid_amount()
778
Ankush Menat494bd9e2022-03-28 18:52:46 +0530779 if (
780 self.doc.is_return
781 and self.doc.return_against
782 and not self.doc.get("is_pos")
783 or self.is_internal_invoice()
784 ):
785 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530786
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530787 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530788 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530789
Nabin Hait877e1bb2017-11-17 12:27:43 +0530790 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
791 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530792 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
793
Nabin Hait877e1bb2017-11-17 12:27:43 +0530794 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530795 total_amount_to_pay = flt(
796 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
797 self.doc.precision("grand_total"),
798 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530799 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530800 total_amount_to_pay = flt(
801 flt(base_grand_total, self.doc.precision("base_grand_total"))
802 - self.doc.total_advance
803 - flt(self.doc.base_write_off_amount),
804 self.doc.precision("base_grand_total"),
805 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530806
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530807 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530808 change_amount = 0
809
Ankush Menat494bd9e2022-03-28 18:52:46 +0530810 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530811 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530812 change_amount = (
813 self.doc.change_amount
814 if self.doc.party_account_currency == self.doc.currency
815 else self.doc.base_change_amount
816 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530817
Ankush Menat494bd9e2022-03-28 18:52:46 +0530818 paid_amount = (
819 self.doc.paid_amount
820 if self.doc.party_account_currency == self.doc.currency
821 else self.doc.base_paid_amount
822 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530823
Ankush Menat494bd9e2022-03-28 18:52:46 +0530824 self.doc.outstanding_amount = flt(
825 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
826 self.doc.precision("outstanding_amount"),
827 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530828
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530829 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530830 self.doc.doctype == "Sales Invoice"
831 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530832 and self.doc.get("pos_profile")
833 and self.doc.get("is_consolidated")
834 ):
835 write_off_limit = flt(
836 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
837 )
838 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
839 self.doc.write_off_outstanding_amount_automatically = 1
840
841 if (
842 self.doc.doctype == "Sales Invoice"
843 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530844 and self.doc.get("is_return")
845 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530846 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530847 self.set_total_amount_to_default_mop(total_amount_to_pay)
848 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530849
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530850 def calculate_paid_amount(self):
851 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530852
853 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530854 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530855 payment.amount = flt(payment.amount)
856 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530857 paid_amount += payment.amount
858 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530859 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530860 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530861
Manas Solankida486ee2018-07-06 12:36:57 +0530862 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
863 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530864 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530865
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530866 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
867 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
868
Nabin Hait3bb1a422016-08-02 16:41:10 +0530869 def calculate_change_amount(self):
870 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530871 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530872 grand_total = self.doc.rounded_total or self.doc.grand_total
873 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530874
Ankush Menat494bd9e2022-03-28 18:52:46 +0530875 if (
876 self.doc.doctype == "Sales Invoice"
877 and self.doc.paid_amount > grand_total
878 and not self.doc.is_return
879 and any(d.type == "Cash" for d in self.doc.payments)
880 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530881 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530882 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530883 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530884
Ankush Menat494bd9e2022-03-28 18:52:46 +0530885 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530886 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530887 )
mbauskar36b51892016-01-18 16:31:10 +0530888
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530889 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530890 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530891 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530892 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530893 )
894 self.doc.base_write_off_amount = flt(
895 self.doc.write_off_amount * self.doc.conversion_rate,
896 self.doc.precision("base_write_off_amount"),
897 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530898
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530899 self.calculate_outstanding_amount()
900
mbauskar36b51892016-01-18 16:31:10 +0530901 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530902 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530903 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530904 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530905 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530906 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530907 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530908 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530909
Ankush Menat494bd9e2022-03-28 18:52:46 +0530910 if pricing_rule.margin_rate_or_amount and (
911 (
912 pricing_rule.currency == self.doc.currency
913 and pricing_rule.margin_type in ["Amount", "Percentage"]
914 )
915 or pricing_rule.margin_type == "Percentage"
916 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530917 item.margin_type = pricing_rule.margin_type
918 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530919 has_margin = True
920
921 if not has_margin:
922 item.margin_type = None
923 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530924
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530925 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
926 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530927 item.margin_rate_or_amount = flt(
928 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
929 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530930 item.rate_with_margin = item.rate
931
932 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530933 margin_value = (
934 item.margin_rate_or_amount
935 if item.margin_type == "Amount"
936 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
937 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530938 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530939 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530940
Shreya Shahbe690ef2017-11-14 17:22:41 +0530941 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530942
943 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530944 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530945
Subin Tom7d627df2021-08-23 11:05:07 +0530946 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530947 total_paid_amount = 0
948 for payment in self.doc.get("payments"):
949 total_paid_amount += (
Akhil Narang3effaf22024-03-27 11:37:26 +0530950 payment.amount
951 if self.doc.party_account_currency == self.doc.currency
952 else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530953 )
954
Deepesh Garg06e8e282022-10-31 19:58:46 +0530955 pending_amount = total_amount_to_pay - total_paid_amount
956
957 if pending_amount > 0:
958 default_mode_of_payment = frappe.db.get_value(
959 "POS Payment Method",
960 {"parent": self.doc.pos_profile, "default": 1},
961 ["mode_of_payment"],
962 as_dict=1,
963 )
964
965 if default_mode_of_payment:
966 self.doc.payments = []
967 self.doc.append(
968 "payments",
969 {
970 "mode_of_payment": default_mode_of_payment.mode_of_payment,
971 "amount": pending_amount,
972 "default": 1,
973 },
974 )
975
Deepesh Garg0ebace52020-02-25 13:21:16 +0530976
Nabin Hait9c421612017-07-20 13:32:01 +0530977def get_itemised_tax_breakup_html(doc):
978 if not doc.taxes:
979 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530980
Nabin Hait9c421612017-07-20 13:32:01 +0530981 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530982 tax_accounts = []
983 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530984 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530985 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530986 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530987 tax_accounts.append(tax.description)
988
Sagar Vora4205f562023-07-24 18:37:36 +0530989 with temporary_flag("company", doc.company):
990 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530991 itemised_tax_data = get_itemised_tax_breakup_data(doc)
992 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530993 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530994
Nabin Hait9c421612017-07-20 13:32:01 +0530995 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530996 "templates/includes/itemised_tax_breakup.html",
997 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530998 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530999 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +05301000 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +05301001 doc=doc,
1002 ),
Nabin Hait9c421612017-07-20 13:32:01 +05301003 )
Nabin Hait852cb642017-07-05 12:58:19 +05301004
Ankush Menat494bd9e2022-03-28 18:52:46 +05301005
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301006@frappe.whitelist()
1007def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +05301008 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +05301009 with temporary_flag("company", company):
1010 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301011
Ankush Menat494bd9e2022-03-28 18:52:46 +05301012
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301013@erpnext.allow_regional
1014def get_regional_round_off_accounts(company, account_list):
1015 pass
rohitwaghchaured4526682017-12-28 14:20:13 +05301016
Ankush Menat494bd9e2022-03-28 18:52:46 +05301017
rohitwaghchaured4526682017-12-28 14:20:13 +05301018@erpnext.allow_regional
1019def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301020 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +05301021 pass
1022
Ankush Menat494bd9e2022-03-28 18:52:46 +05301023
Nabin Haitb962fc12017-07-17 18:02:31 +05301024@erpnext.allow_regional
1025def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
Akhil Narang3effaf22024-03-27 11:37:26 +05301026 return [_("Item"), _("Taxable Amount"), *tax_accounts]
Nabin Haitb962fc12017-07-17 18:02:31 +05301027
Ankush Menat494bd9e2022-03-28 18:52:46 +05301028
Nabin Haitb962fc12017-07-17 18:02:31 +05301029@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +05301030def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +05301031 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301032
1033 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1034
DaizyModib84deec2023-07-21 17:26:35 +05301035 itemised_tax_data = []
1036 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301037 itemised_tax_data.append(
1038 frappe._dict(
ljain11258859782024-02-26 18:41:33 +05301039 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code, 0), **taxes}
DaizyModi6f376cf2023-07-24 18:02:42 +05301040 )
1041 )
DaizyModib84deec2023-07-21 17:26:35 +05301042
1043 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301044
Ankush Menat494bd9e2022-03-28 18:52:46 +05301045
Nabin Hait34c551d2019-07-03 10:34:31 +05301046def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301047 itemised_tax = {}
1048 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301049 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301050 continue
1051
Nabin Haitb962fc12017-07-17 18:02:31 +05301052 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301053 if item_tax_map:
1054 for item_code, tax_data in item_tax_map.items():
1055 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301056
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301057 tax_rate = 0.0
1058 tax_amount = 0.0
1059
Nabin Hait2e4de832017-09-19 14:53:16 +05301060 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301061 tax_rate = flt(tax_data[0])
1062 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301063 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301064 tax_rate = flt(tax_data)
1065
Ankush Menat494bd9e2022-03-28 18:52:46 +05301066 itemised_tax[item_code][tax.description] = frappe._dict(
1067 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1068 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301069
Nabin Hait34c551d2019-07-03 10:34:31 +05301070 if with_tax_account:
1071 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1072
Nabin Haitb962fc12017-07-17 18:02:31 +05301073 return itemised_tax
1074
Ankush Menat494bd9e2022-03-28 18:52:46 +05301075
Nabin Haitb962fc12017-07-17 18:02:31 +05301076def get_itemised_taxable_amount(items):
1077 itemised_taxable_amount = frappe._dict()
1078 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301079 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301080 itemised_taxable_amount.setdefault(item_code, 0)
1081 itemised_taxable_amount[item_code] += item.net_amount
1082
Nabin Haitcaab5822017-08-24 16:22:28 +05301083 return itemised_taxable_amount
1084
Ankush Menat494bd9e2022-03-28 18:52:46 +05301085
DaizyModi6f376cf2023-07-24 18:02:42 +05301086def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301087 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301088 for taxes in itemised_tax:
1089 for row in taxes.values():
1090 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1091 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301092
Ankush Menat494bd9e2022-03-28 18:52:46 +05301093
Akhil Narang3effaf22024-03-27 11:37:26 +05301094class init_landed_taxes_and_totals:
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301095 def __init__(self, doc):
1096 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301097 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301098 self.set_account_currency()
1099 self.set_exchange_rate()
1100 self.set_amounts_in_company_currency()
1101
1102 def set_account_currency(self):
1103 company_currency = erpnext.get_company_currency(self.doc.company)
1104 for d in self.doc.get(self.tax_field):
1105 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301106 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301107 d.account_currency = account_currency or company_currency
1108
1109 def set_exchange_rate(self):
1110 company_currency = erpnext.get_company_currency(self.doc.company)
1111 for d in self.doc.get(self.tax_field):
1112 if d.account_currency == company_currency:
1113 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301114 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301115 d.exchange_rate = get_exchange_rate(
1116 self.doc.posting_date,
1117 account=d.expense_account,
1118 account_currency=d.account_currency,
1119 company=self.doc.company,
1120 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301121
1122 if not d.exchange_rate:
1123 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1124
1125 def set_amounts_in_company_currency(self):
1126 for d in self.doc.get(self.tax_field):
1127 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301128 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))