blob: 22957b614ff6eb7b4043bf3f3f2d5c5cb485578a [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"))
Nabin Haitc66dde62024-04-08 20:26:44 +0530470
471 if tax.get("is_tax_withholding_account") and item.meta.get_field("apply_tds"):
472 if not item.get("apply_tds") or not self.doc.tax_withholding_net_total:
473 current_tax_amount = 0.0
474 else:
475 current_tax_amount = item.net_amount * actual / self.doc.tax_withholding_net_total
476 else:
477 current_tax_amount = (
478 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
479 )
Nabin Haite7679702015-02-20 14:40:35 +0530480
Nabin Hait3237c752015-02-17 11:11:11 +0530481 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530482 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530483 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530484 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
485 cint(tax.row_id) - 1
486 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530487 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530488 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
489 cint(tax.row_id) - 1
490 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530491 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530492 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530493
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530494 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530495 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530496
497 return current_tax_amount
498
Nabin Haite7679702015-02-20 14:40:35 +0530499 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
500 # store tax breakup for each item
501 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530502 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000503 if frappe.flags.round_row_wise_tax:
504 item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
505 if tax.item_wise_tax_detail.get(key):
506 item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
Dany Robert9e1b2c92023-08-24 05:02:14 +0000507 tax.item_wise_tax_detail[key] = [
508 tax_rate,
509 flt(item_wise_tax_amount, tax.precision("tax_amount")),
510 ]
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000511 else:
512 if tax.item_wise_tax_detail.get(key):
513 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
Nabin Haite7679702015-02-20 14:40:35 +0530514
Dany Robert9e1b2c92023-08-24 05:02:14 +0000515 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530516
Nabin Hait3237c752015-02-17 11:11:11 +0530517 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530518 if tax.account_head in frappe.flags.round_off_applicable_accounts:
519 tax.tax_amount = round(tax.tax_amount, 0)
520 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
521
Nabin Haite7679702015-02-20 14:40:35 +0530522 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530523 tax.tax_amount_after_discount_amount = flt(
524 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
525 )
Nabin Haitce245122015-02-22 20:14:49 +0530526
Deepesh Gargb6705882021-04-14 11:20:27 +0530527 def round_off_base_values(self, tax):
528 # Round off to nearest integer based on regional settings
529 if tax.account_head in frappe.flags.round_off_applicable_accounts:
530 tax.base_tax_amount = round(tax.base_tax_amount, 0)
531 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
532
Dany Robert50d56db2024-01-29 09:32:44 +0530533 @deprecated
Nabin Haita1bf43b2015-03-17 10:50:47 +0530534 def manipulate_grand_total_for_inclusive_tax(self):
Dany Robert50d56db2024-01-29 09:32:44 +0530535 # for backward compatablility - if in case used by an external application
536 return self.adjust_grand_total_for_inclusive_tax()
537
538 def adjust_grand_total_for_inclusive_tax(self):
Nabin Haita1bf43b2015-03-17 10:50:47 +0530539 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530540 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 +0530541 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530542 non_inclusive_tax_amount = sum(
543 flt(d.tax_amount_after_discount_amount)
544 for d in self.doc.get("taxes")
545 if not d.included_in_print_rate
546 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530547
Ankush Menat494bd9e2022-03-28 18:52:46 +0530548 diff = (
549 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
550 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530551
552 # If discount amount applied, deduct the discount amount
553 # because self.doc.total is always without discount, but last_tax.total is after discount
554 if self.discount_amount_applied and self.doc.discount_amount:
555 diff -= flt(self.doc.discount_amount)
556
557 diff = flt(diff, self.doc.precision("rounding_adjustment"))
558
Ankush Menat494bd9e2022-03-28 18:52:46 +0530559 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Dany Robert50d56db2024-01-29 09:32:44 +0530560 self.doc.grand_total_diff = diff
561 else:
562 self.doc.grand_total_diff = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530563
564 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530565 if self.doc.get("taxes"):
Dany Robert50d56db2024-01-29 09:32:44 +0530566 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(
567 self.doc.get("grand_total_diff")
568 )
Subin Tom75a76e62021-10-29 16:45:04 +0530569 else:
570 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530571
Subin Tom75a76e62021-10-29 16:45:04 +0530572 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530573 self.doc.total_taxes_and_charges = flt(
Dany Robert50d56db2024-01-29 09:32:44 +0530574 self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530575 self.doc.precision("total_taxes_and_charges"),
576 )
Subin Tom75a76e62021-10-29 16:45:04 +0530577 else:
578 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530579
Nabin Hait2e4de832017-09-19 14:53:16 +0530580 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530581
Ankush Menat494bd9e2022-03-28 18:52:46 +0530582 if self.doc.doctype in [
583 "Quotation",
584 "Sales Order",
585 "Delivery Note",
586 "Sales Invoice",
587 "POS Invoice",
588 ]:
589 self.doc.base_grand_total = (
590 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
591 if self.doc.total_taxes_and_charges
592 else self.doc.base_net_total
593 )
Nabin Hait3237c752015-02-17 11:11:11 +0530594 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530595 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530596 for tax in self.doc.get("taxes"):
597 if tax.category in ["Valuation and Total", "Total"]:
598 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530599 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530600 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530601 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530602
Nabin Haite7679702015-02-20 14:40:35 +0530603 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530604
Ankush Menat494bd9e2022-03-28 18:52:46 +0530605 self.doc.base_grand_total = (
606 flt(self.doc.grand_total * self.doc.conversion_rate)
607 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530608 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530609 )
Nabin Hait3237c752015-02-17 11:11:11 +0530610
Akhil Narang3effaf22024-03-27 11:37:26 +0530611 self._set_in_company_currency(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530612
Nabin Haite7679702015-02-20 14:40:35 +0530613 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530614
Nabin Hait2e4de832017-09-19 14:53:16 +0530615 self.set_rounded_total()
616
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530617 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530618 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530619 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530620 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530621 if d.total_weight:
622 self.doc.total_net_weight += d.total_weight
623
Nabin Hait2e4de832017-09-19 14:53:16 +0530624 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530625 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530626 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530627
Saqib Ansari17445c72022-03-07 18:01:07 +0530628 if self.doc.meta.get_field("rounded_total"):
629 if self.doc.is_rounded_total_disabled():
630 self.doc.rounded_total = self.doc.base_rounded_total = 0
631 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530632
Ankush Menat494bd9e2022-03-28 18:52:46 +0530633 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
634 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
635 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530636
Dany Robert50d56db2024-01-29 09:32:44 +0530637 # rounding adjustment should always be the difference vetween grand and rounded total
638 self.doc.rounding_adjustment = flt(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530639 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
640 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530641
642 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530643
Nabin Hait3237c752015-02-17 11:11:11 +0530644 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530645 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530646 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530647 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530648 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530649
Nabin Hait3769d872015-12-18 13:12:02 +0530650 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530651 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530652 self.doc.discount_amount = flt(
653 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
654 * self.doc.additional_discount_percentage
655 / 100,
656 self.doc.precision("discount_amount"),
657 )
Nabin Hait3237c752015-02-17 11:11:11 +0530658
659 def apply_discount_amount(self):
660 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530661 if not self.doc.apply_discount_on:
662 frappe.throw(_("Please select Apply Discount On"))
663
Deepesh Garg3b159662022-08-21 17:51:05 +0530664 self.doc.base_discount_amount = flt(
Akhil Narang3effaf22024-03-27 11:37:26 +0530665 self.doc.discount_amount * self.doc.conversion_rate,
666 self.doc.precision("base_discount_amount"),
Deepesh Garg3b159662022-08-21 17:51:05 +0530667 )
668
Akhil Narang3effaf22024-03-27 11:37:26 +0530669 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 +0530670 self.discount_amount_applied = True
671 return
672
Nabin Haite7679702015-02-20 14:40:35 +0530673 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530674 taxes = self.doc.get("taxes")
675 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530676
Nabin Haite7679702015-02-20 14:40:35 +0530677 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530678 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530679 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530680 distributed_amount = (
681 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
682 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530683
Nabin Haite7679702015-02-20 14:40:35 +0530684 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530685 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530686
Nabin Hait25bd84d2015-03-04 15:06:56 +0530687 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530688 if (
689 self.doc.apply_discount_on == "Net Total"
690 or not taxes
691 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530692 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530693 discount_amount_loss = flt(
Akhil Narang3effaf22024-03-27 11:37:26 +0530694 self.doc.net_total - net_total - self.doc.discount_amount,
695 self.doc.precision("net_total"),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530696 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530697
Akhil Narang3effaf22024-03-27 11:37:26 +0530698 item.net_amount = flt(
699 item.net_amount + discount_amount_loss, item.precision("net_amount")
700 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530701
Akhil Narang3effaf22024-03-27 11:37:26 +0530702 item.net_rate = (
703 flt(item.net_amount / item.qty, item.precision("net_rate")) if item.qty else 0
704 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530705
Nabin Haite7679702015-02-20 14:40:35 +0530706 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530707
708 self.discount_amount_applied = True
709 self._calculate()
710 else:
711 self.doc.base_discount_amount = 0
712
Nabin Haite7679702015-02-20 14:40:35 +0530713 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530714 if self.doc.apply_discount_on == "Net Total":
715 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530716 else:
717 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530718
Nabin Haite7679702015-02-20 14:40:35 +0530719 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530720 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530721 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
722 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530723 elif tax.row_id in actual_taxes_dict:
724 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
725 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530726
Ankush Menat494bd9e2022-03-28 18:52:46 +0530727 return flt(
728 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
729 )
Nabin Hait3237c752015-02-17 11:11:11 +0530730
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530731 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100732 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530733 total_allocated_amount = sum(
734 flt(adv.allocated_amount, adv.precision("allocated_amount"))
735 for adv in self.doc.get("advances")
736 )
Nabin Hait3237c752015-02-17 11:11:11 +0530737
Nabin Haite7679702015-02-20 14:40:35 +0530738 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530739
Faris Ansari6041f5c2018-02-08 13:33:52 +0530740 grand_total = self.doc.rounded_total or self.doc.grand_total
741
Nabin Hait289ffb72016-02-08 11:06:55 +0530742 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530743 invoice_total = flt(
744 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
745 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530746 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530747 base_write_off_amount = flt(
748 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
749 self.doc.precision("base_write_off_amount"),
750 )
751 invoice_total = (
752 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
753 - base_write_off_amount
754 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530755
Nabin Haitadc09232016-02-09 10:31:11 +0530756 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530757 frappe.throw(
758 _("Advance amount cannot be greater than {0} {1}").format(
759 self.doc.party_account_currency, invoice_total
760 )
761 )
Nabin Hait3237c752015-02-17 11:11:11 +0530762
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100763 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530764 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530765 self.doc.write_off_amount = 0
766
Nabin Hait3237c752015-02-17 11:11:11 +0530767 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530768 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530769
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530770 def is_internal_invoice(self):
771 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530772 Checks if its an internal transfer invoice
773 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530774 """
775
Ankush Menat494bd9e2022-03-28 18:52:46 +0530776 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530777 return True
778
779 return False
780
Nabin Hait3237c752015-02-17 11:11:11 +0530781 def calculate_outstanding_amount(self):
782 # NOTE:
783 # write_off_amount is only for POS Invoice
784 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530785 if self.doc.doctype == "Sales Invoice":
786 self.calculate_paid_amount()
787
Ankush Menat494bd9e2022-03-28 18:52:46 +0530788 if (
789 self.doc.is_return
790 and self.doc.return_against
791 and not self.doc.get("is_pos")
792 or self.is_internal_invoice()
793 ):
794 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530795
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530796 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530797 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530798
Nabin Hait877e1bb2017-11-17 12:27:43 +0530799 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
800 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530801 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
802
Nabin Hait877e1bb2017-11-17 12:27:43 +0530803 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530804 total_amount_to_pay = flt(
805 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
806 self.doc.precision("grand_total"),
807 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530808 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530809 total_amount_to_pay = flt(
810 flt(base_grand_total, self.doc.precision("base_grand_total"))
811 - self.doc.total_advance
812 - flt(self.doc.base_write_off_amount),
813 self.doc.precision("base_grand_total"),
814 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530815
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530816 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530817 change_amount = 0
818
Ankush Menat494bd9e2022-03-28 18:52:46 +0530819 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530820 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530821 change_amount = (
822 self.doc.change_amount
823 if self.doc.party_account_currency == self.doc.currency
824 else self.doc.base_change_amount
825 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530826
Ankush Menat494bd9e2022-03-28 18:52:46 +0530827 paid_amount = (
828 self.doc.paid_amount
829 if self.doc.party_account_currency == self.doc.currency
830 else self.doc.base_paid_amount
831 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530832
Ankush Menat494bd9e2022-03-28 18:52:46 +0530833 self.doc.outstanding_amount = flt(
834 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
835 self.doc.precision("outstanding_amount"),
836 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530837
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530838 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530839 self.doc.doctype == "Sales Invoice"
840 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530841 and self.doc.get("pos_profile")
842 and self.doc.get("is_consolidated")
843 ):
844 write_off_limit = flt(
845 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
846 )
847 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
848 self.doc.write_off_outstanding_amount_automatically = 1
849
850 if (
851 self.doc.doctype == "Sales Invoice"
852 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530853 and self.doc.get("is_return")
854 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530855 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530856 self.set_total_amount_to_default_mop(total_amount_to_pay)
857 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530858
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530859 def calculate_paid_amount(self):
860 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530861
862 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530863 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530864 payment.amount = flt(payment.amount)
865 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530866 paid_amount += payment.amount
867 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530868 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530869 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530870
Manas Solankida486ee2018-07-06 12:36:57 +0530871 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
872 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530873 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530874
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530875 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
876 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
877
Nabin Hait3bb1a422016-08-02 16:41:10 +0530878 def calculate_change_amount(self):
879 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530880 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530881 grand_total = self.doc.rounded_total or self.doc.grand_total
882 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530883
Ankush Menat494bd9e2022-03-28 18:52:46 +0530884 if (
885 self.doc.doctype == "Sales Invoice"
886 and self.doc.paid_amount > grand_total
887 and not self.doc.is_return
888 and any(d.type == "Cash" for d in self.doc.payments)
889 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530890 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530891 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530892 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530893
Ankush Menat494bd9e2022-03-28 18:52:46 +0530894 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530895 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530896 )
mbauskar36b51892016-01-18 16:31:10 +0530897
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530898 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530899 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530900 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530901 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530902 )
903 self.doc.base_write_off_amount = flt(
904 self.doc.write_off_amount * self.doc.conversion_rate,
905 self.doc.precision("base_write_off_amount"),
906 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530907
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530908 self.calculate_outstanding_amount()
909
mbauskar36b51892016-01-18 16:31:10 +0530910 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530911 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530912 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530913 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530914 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530915 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530916 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530917 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530918
Ankush Menat494bd9e2022-03-28 18:52:46 +0530919 if pricing_rule.margin_rate_or_amount and (
920 (
921 pricing_rule.currency == self.doc.currency
922 and pricing_rule.margin_type in ["Amount", "Percentage"]
923 )
924 or pricing_rule.margin_type == "Percentage"
925 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530926 item.margin_type = pricing_rule.margin_type
927 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530928 has_margin = True
929
930 if not has_margin:
931 item.margin_type = None
932 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530933
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530934 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
935 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530936 item.margin_rate_or_amount = flt(
937 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
938 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530939 item.rate_with_margin = item.rate
940
941 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530942 margin_value = (
943 item.margin_rate_or_amount
944 if item.margin_type == "Amount"
945 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
946 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530947 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530948 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530949
Shreya Shahbe690ef2017-11-14 17:22:41 +0530950 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530951
952 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530953 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530954
Subin Tom7d627df2021-08-23 11:05:07 +0530955 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530956 total_paid_amount = 0
957 for payment in self.doc.get("payments"):
958 total_paid_amount += (
Akhil Narang3effaf22024-03-27 11:37:26 +0530959 payment.amount
960 if self.doc.party_account_currency == self.doc.currency
961 else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530962 )
963
Deepesh Garg06e8e282022-10-31 19:58:46 +0530964 pending_amount = total_amount_to_pay - total_paid_amount
965
966 if pending_amount > 0:
967 default_mode_of_payment = frappe.db.get_value(
968 "POS Payment Method",
969 {"parent": self.doc.pos_profile, "default": 1},
970 ["mode_of_payment"],
971 as_dict=1,
972 )
973
974 if default_mode_of_payment:
975 self.doc.payments = []
976 self.doc.append(
977 "payments",
978 {
979 "mode_of_payment": default_mode_of_payment.mode_of_payment,
980 "amount": pending_amount,
981 "default": 1,
982 },
983 )
984
Deepesh Garg0ebace52020-02-25 13:21:16 +0530985
Nabin Hait9c421612017-07-20 13:32:01 +0530986def get_itemised_tax_breakup_html(doc):
987 if not doc.taxes:
988 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530989
Nabin Hait9c421612017-07-20 13:32:01 +0530990 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530991 tax_accounts = []
992 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530993 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530994 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530995 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530996 tax_accounts.append(tax.description)
997
Sagar Vora4205f562023-07-24 18:37:36 +0530998 with temporary_flag("company", doc.company):
999 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +05301000 itemised_tax_data = get_itemised_tax_breakup_data(doc)
1001 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +05301002 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301003
Nabin Hait9c421612017-07-20 13:32:01 +05301004 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +05301005 "templates/includes/itemised_tax_breakup.html",
1006 dict(
Nabin Hait9c421612017-07-20 13:32:01 +05301007 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +05301008 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +05301009 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +05301010 doc=doc,
1011 ),
Nabin Hait9c421612017-07-20 13:32:01 +05301012 )
Nabin Hait852cb642017-07-05 12:58:19 +05301013
Ankush Menat494bd9e2022-03-28 18:52:46 +05301014
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301015@frappe.whitelist()
1016def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +05301017 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +05301018 with temporary_flag("company", company):
1019 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301020
Ankush Menat494bd9e2022-03-28 18:52:46 +05301021
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301022@erpnext.allow_regional
1023def get_regional_round_off_accounts(company, account_list):
1024 pass
rohitwaghchaured4526682017-12-28 14:20:13 +05301025
Ankush Menat494bd9e2022-03-28 18:52:46 +05301026
rohitwaghchaured4526682017-12-28 14:20:13 +05301027@erpnext.allow_regional
1028def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301029 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +05301030 pass
1031
Ankush Menat494bd9e2022-03-28 18:52:46 +05301032
Nabin Haitb962fc12017-07-17 18:02:31 +05301033@erpnext.allow_regional
1034def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
Akhil Narang3effaf22024-03-27 11:37:26 +05301035 return [_("Item"), _("Taxable Amount"), *tax_accounts]
Nabin Haitb962fc12017-07-17 18:02:31 +05301036
Ankush Menat494bd9e2022-03-28 18:52:46 +05301037
Nabin Haitb962fc12017-07-17 18:02:31 +05301038@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +05301039def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +05301040 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301041
1042 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1043
DaizyModib84deec2023-07-21 17:26:35 +05301044 itemised_tax_data = []
1045 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301046 itemised_tax_data.append(
1047 frappe._dict(
ljain11258859782024-02-26 18:41:33 +05301048 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code, 0), **taxes}
DaizyModi6f376cf2023-07-24 18:02:42 +05301049 )
1050 )
DaizyModib84deec2023-07-21 17:26:35 +05301051
1052 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301053
Ankush Menat494bd9e2022-03-28 18:52:46 +05301054
Nabin Hait34c551d2019-07-03 10:34:31 +05301055def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301056 itemised_tax = {}
1057 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301058 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301059 continue
1060
Nabin Haitb962fc12017-07-17 18:02:31 +05301061 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301062 if item_tax_map:
1063 for item_code, tax_data in item_tax_map.items():
1064 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301065
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301066 tax_rate = 0.0
1067 tax_amount = 0.0
1068
Nabin Hait2e4de832017-09-19 14:53:16 +05301069 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301070 tax_rate = flt(tax_data[0])
1071 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301072 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301073 tax_rate = flt(tax_data)
1074
Ankush Menat494bd9e2022-03-28 18:52:46 +05301075 itemised_tax[item_code][tax.description] = frappe._dict(
1076 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1077 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301078
Nabin Hait34c551d2019-07-03 10:34:31 +05301079 if with_tax_account:
1080 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1081
Nabin Haitb962fc12017-07-17 18:02:31 +05301082 return itemised_tax
1083
Ankush Menat494bd9e2022-03-28 18:52:46 +05301084
Nabin Haitb962fc12017-07-17 18:02:31 +05301085def get_itemised_taxable_amount(items):
1086 itemised_taxable_amount = frappe._dict()
1087 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301088 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301089 itemised_taxable_amount.setdefault(item_code, 0)
1090 itemised_taxable_amount[item_code] += item.net_amount
1091
Nabin Haitcaab5822017-08-24 16:22:28 +05301092 return itemised_taxable_amount
1093
Ankush Menat494bd9e2022-03-28 18:52:46 +05301094
DaizyModi6f376cf2023-07-24 18:02:42 +05301095def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301096 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301097 for taxes in itemised_tax:
1098 for row in taxes.values():
1099 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1100 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301101
Ankush Menat494bd9e2022-03-28 18:52:46 +05301102
Nabin Haitf61faa82024-04-07 11:33:40 +05301103@frappe.whitelist()
1104def get_rounding_tax_settings():
1105 return frappe.db.get_single_value("Accounts Settings", "round_row_wise_tax")
1106
1107
Akhil Narang3effaf22024-03-27 11:37:26 +05301108class init_landed_taxes_and_totals:
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301109 def __init__(self, doc):
1110 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301111 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301112 self.set_account_currency()
1113 self.set_exchange_rate()
1114 self.set_amounts_in_company_currency()
1115
1116 def set_account_currency(self):
1117 company_currency = erpnext.get_company_currency(self.doc.company)
1118 for d in self.doc.get(self.tax_field):
1119 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301120 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301121 d.account_currency = account_currency or company_currency
1122
1123 def set_exchange_rate(self):
1124 company_currency = erpnext.get_company_currency(self.doc.company)
1125 for d in self.doc.get(self.tax_field):
1126 if d.account_currency == company_currency:
1127 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301128 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301129 d.exchange_rate = get_exchange_rate(
1130 self.doc.posting_date,
1131 account=d.expense_account,
1132 account_currency=d.account_currency,
1133 company=self.doc.company,
1134 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301135
1136 if not d.exchange_rate:
1137 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1138
1139 def set_amounts_in_company_currency(self):
1140 for d in self.doc.get(self.tax_field):
1141 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301142 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))