blob: 69d8caaaf88d4eda46f8b77ff7206e808c791ce2 [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
Nabin Haitfe81da22015-02-18 12:23:18 +053025class calculate_taxes_and_totals(object):
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(
163 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
164 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530165
Deepesh Garga83a0a02022-03-25 12:28:55 +0530166 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530167
Deepesh Garg97e102c2022-03-25 12:39:59 +0530168 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530169 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530170
Ankush Menat494bd9e2022-03-28 18:52:46 +0530171 if item.doctype in [
172 "Quotation Item",
173 "Sales Order Item",
174 "Delivery Note Item",
175 "Sales Invoice Item",
176 "POS Invoice Item",
177 "Purchase Invoice Item",
178 "Purchase Order Item",
179 "Purchase Receipt Item",
180 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530181 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530182 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530183 item.rate = flt(
184 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
185 )
Nabin Hait10c61372021-04-13 15:46:01 +0530186
Walstan Baptista37b826b2021-04-03 19:48:46 +0530187 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530188 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530189 else:
190 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530191
Nabin Hait64bfdd92019-04-23 13:37:19 +0530192 elif flt(item.price_list_rate) > 0:
193 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530194 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
195 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530196
Nabin Haite7679702015-02-20 14:40:35 +0530197 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530198
mergify[bot]10c666b2023-10-06 14:43:13 +0530199 if (
200 not item.qty and self.doc.get("is_return") and self.doc.get("doctype") != "Purchase Receipt"
201 ):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530202 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530203 elif not item.qty and self.doc.get("is_debit_note"):
204 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530205 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530206 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530207
Nabin Haite7679702015-02-20 14:40:35 +0530208 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530209
Ankush Menat494bd9e2022-03-28 18:52:46 +0530210 self._set_in_company_currency(
211 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
212 )
Nabin Hait3237c752015-02-17 11:11:11 +0530213
Nabin Haite7679702015-02-20 14:40:35 +0530214 item.item_tax_amount = 0.0
215
216 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530217 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530218 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530219 val = flt(
220 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
221 )
Nabin Haite7679702015-02-20 14:40:35 +0530222 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530223
224 def initialize_taxes(self):
225 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530226 if not self.discount_amount_applied:
227 validate_taxes_and_charges(tax)
228 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530229
Ankush Menat494bd9e2022-03-28 18:52:46 +0530230 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530231 tax.item_wise_tax_detail = {}
232
Ankush Menat494bd9e2022-03-28 18:52:46 +0530233 tax_fields = [
234 "total",
235 "tax_amount_after_discount_amount",
236 "tax_amount_for_current_item",
237 "grand_total_for_current_item",
238 "tax_fraction_for_current_item",
239 "grand_total_fraction_for_current_item",
240 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530241
Ankush Menat494bd9e2022-03-28 18:52:46 +0530242 if tax.charge_type != "Actual" and not (
243 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
244 ):
245 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530246
247 for fieldname in tax_fields:
248 tax.set(fieldname, 0.0)
249
Nabin Hait3237c752015-02-17 11:11:11 +0530250 self.doc.round_floats_in(tax)
251
Nabin Hait3237c752015-02-17 11:11:11 +0530252 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530253 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530254 return
Nabin Hait3237c752015-02-17 11:11:11 +0530255
marination91982d12023-01-24 18:03:53 +0530256 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530257 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
258 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530259 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530260 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530261 (
262 tax.tax_fraction_for_current_item,
263 inclusive_tax_amount_per_qty,
264 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530265
Ankush Menat494bd9e2022-03-28 18:52:46 +0530266 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530267 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
268 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530269 tax.grand_total_fraction_for_current_item = (
270 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530271 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530272 )
Nabin Hait3237c752015-02-17 11:11:11 +0530273
274 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530275 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530276
Ankush Menat494bd9e2022-03-28 18:52:46 +0530277 if (
278 not self.discount_amount_applied
279 and item.qty
280 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
281 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530282 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
283
Dany Robert50d56db2024-01-29 09:32:44 +0530284 item.net_amount = flt(amount / (1 + cumulated_tax_fraction), item.precision("net_amount"))
Nabin Haite7679702015-02-20 14:40:35 +0530285 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530286 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530287
Nabin Haite7679702015-02-20 14:40:35 +0530288 self._set_in_company_currency(item, ["net_rate", "net_amount"])
289
Nabin Hait3237c752015-02-17 11:11:11 +0530290 def _load_item_tax_rate(self, item_tax_rate):
291 return json.loads(item_tax_rate) if item_tax_rate else {}
292
293 def get_current_tax_fraction(self, tax, item_tax_map):
294 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530295 Get tax fraction for calculating tax exclusive amount
296 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530297 """
298 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530299 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530300
301 if cint(tax.included_in_print_rate):
302 tax_rate = self._get_tax_rate(tax, item_tax_map)
303
304 if tax.charge_type == "On Net Total":
305 current_tax_fraction = tax_rate / 100.0
306
307 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530308 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
309 cint(tax.row_id) - 1
310 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530311
312 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530313 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
314 cint(tax.row_id) - 1
315 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530316
Nabin Hait19ea7212020-08-11 20:34:57 +0530317 elif tax.charge_type == "On Item Quantity":
318 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530319
Nabin Hait19ea7212020-08-11 20:34:57 +0530320 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
321 current_tax_fraction *= -1.0
322 inclusive_tax_amount_per_qty *= -1.0
323
324 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530325
326 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530327 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530328 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
329 else:
330 return tax.rate
331
332 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530333 self.doc.total_qty = (
334 self.doc.total
335 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530336
marination91982d12023-01-24 18:03:53 +0530337 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530338 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530339 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530340 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530341 self.doc.net_total += item.net_amount
342 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530343
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530344 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530345
Subin Toma8e2c022021-11-16 19:06:49 +0530346 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530347
348 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530349 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530350 return
351
Subin Tomaf1fce02021-11-10 16:49:12 +0530352 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530353 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
354 shipping_rule.apply(self.doc)
355
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530356 self._calculate()
357
Nabin Hait3237c752015-02-17 11:11:11 +0530358 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530359 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
360 "rounding_adjustment"
361 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530362 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530363 self.doc.rounding_adjustment = 0
364
Nabin Hait3237c752015-02-17 11:11:11 +0530365 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530366 actual_tax_dict = dict(
367 [
368 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
369 for tax in self.doc.get("taxes")
370 if tax.charge_type == "Actual"
371 ]
372 )
Nabin Hait3237c752015-02-17 11:11:11 +0530373
marination91982d12023-01-24 18:03:53 +0530374 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530375 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530376 for i, tax in enumerate(self.doc.get("taxes")):
377 # tax_amount represents the amount of tax for the current step
378 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
Dany Robert3ead2892023-08-22 14:41:07 +0000379 if frappe.flags.round_row_wise_tax:
380 current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530381
382 # Adjust divisional loss to the last item
383 if tax.charge_type == "Actual":
384 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530385 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530386 current_tax_amount += actual_tax_dict[tax.idx]
387
Nabin Hait2b019ed2015-02-22 23:03:07 +0530388 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530389 if tax.charge_type != "Actual" and not (
390 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
391 ):
392 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530393
Nabin Hait3237c752015-02-17 11:11:11 +0530394 # store tax_amount for current item as it will be used for
395 # charge type = 'On Previous Row Amount'
396 tax.tax_amount_for_current_item = current_tax_amount
397
Nabin Hait2b019ed2015-02-22 23:03:07 +0530398 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530399 tax.tax_amount_after_discount_amount += current_tax_amount
400
Nabin Haitcd951342017-07-31 18:07:45 +0530401 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530402
Nabin Hait3237c752015-02-17 11:11:11 +0530403 # note: grand_total_for_current_item contains the contribution of
404 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530405 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530406 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530407 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530408 tax.grand_total_for_current_item = flt(
409 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
410 )
Nabin Hait3237c752015-02-17 11:11:11 +0530411
412 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530413 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530414 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530415 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530416
417 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530418 self.set_cumulative_total(i, tax)
419
Deepesh Gargb6705882021-04-14 11:20:27 +0530420 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530421
Nabin Hait3237c752015-02-17 11:11:11 +0530422 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530423 if (
424 i == (len(self.doc.get("taxes")) - 1)
425 and self.discount_amount_applied
426 and self.doc.discount_amount
427 and self.doc.apply_discount_on == "Grand Total"
428 and not rounding_adjustment_computed
429 ):
430 self.doc.rounding_adjustment = flt(
431 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
432 self.doc.precision("rounding_adjustment"),
433 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530434
Nabin Haitcd951342017-07-31 18:07:45 +0530435 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
436 # if just for valuation, do not add the tax amount in total
437 # if tax/charges is for deduction, multiply by -1
438 if getattr(tax, "category", None):
439 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530440 if self.doc.doctype in [
441 "Purchase Order",
442 "Purchase Invoice",
443 "Purchase Receipt",
444 "Supplier Quotation",
445 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530446 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530447 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530448
Nabin Haitcd951342017-07-31 18:07:45 +0530449 def set_cumulative_total(self, row_idx, tax):
450 tax_amount = tax.tax_amount_after_discount_amount
451 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
452
453 if row_idx == 0:
454 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
455 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530456 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530457
458 def get_current_tax_amount(self, item, tax, item_tax_map):
459 tax_rate = self._get_tax_rate(tax, item_tax_map)
460 current_tax_amount = 0.0
461
462 if tax.charge_type == "Actual":
463 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530464 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530465 current_tax_amount = (
466 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
467 )
Nabin Haite7679702015-02-20 14:40:35 +0530468
Nabin Hait3237c752015-02-17 11:11:11 +0530469 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530470 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530471 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530472 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
473 cint(tax.row_id) - 1
474 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530475 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530476 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
477 cint(tax.row_id) - 1
478 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530479 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530480 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530481
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530482 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530483 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530484
485 return current_tax_amount
486
Nabin Haite7679702015-02-20 14:40:35 +0530487 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
488 # store tax breakup for each item
489 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530490 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000491 if frappe.flags.round_row_wise_tax:
492 item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
493 if tax.item_wise_tax_detail.get(key):
494 item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
Dany Robert9e1b2c92023-08-24 05:02:14 +0000495 tax.item_wise_tax_detail[key] = [
496 tax_rate,
497 flt(item_wise_tax_amount, tax.precision("tax_amount")),
498 ]
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000499 else:
500 if tax.item_wise_tax_detail.get(key):
501 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
Nabin Haite7679702015-02-20 14:40:35 +0530502
Dany Robert9e1b2c92023-08-24 05:02:14 +0000503 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530504
Nabin Hait3237c752015-02-17 11:11:11 +0530505 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530506 if tax.account_head in frappe.flags.round_off_applicable_accounts:
507 tax.tax_amount = round(tax.tax_amount, 0)
508 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
509
Nabin Haite7679702015-02-20 14:40:35 +0530510 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530511 tax.tax_amount_after_discount_amount = flt(
512 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
513 )
Nabin Haitce245122015-02-22 20:14:49 +0530514
Deepesh Gargb6705882021-04-14 11:20:27 +0530515 def round_off_base_values(self, tax):
516 # Round off to nearest integer based on regional settings
517 if tax.account_head in frappe.flags.round_off_applicable_accounts:
518 tax.base_tax_amount = round(tax.base_tax_amount, 0)
519 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
520
Dany Robert50d56db2024-01-29 09:32:44 +0530521 @deprecated
Nabin Haita1bf43b2015-03-17 10:50:47 +0530522 def manipulate_grand_total_for_inclusive_tax(self):
Dany Robert50d56db2024-01-29 09:32:44 +0530523 # for backward compatablility - if in case used by an external application
524 return self.adjust_grand_total_for_inclusive_tax()
525
526 def adjust_grand_total_for_inclusive_tax(self):
Nabin Haita1bf43b2015-03-17 10:50:47 +0530527 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530528 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 +0530529 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530530 non_inclusive_tax_amount = sum(
531 flt(d.tax_amount_after_discount_amount)
532 for d in self.doc.get("taxes")
533 if not d.included_in_print_rate
534 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530535
Ankush Menat494bd9e2022-03-28 18:52:46 +0530536 diff = (
537 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
538 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530539
540 # If discount amount applied, deduct the discount amount
541 # because self.doc.total is always without discount, but last_tax.total is after discount
542 if self.discount_amount_applied and self.doc.discount_amount:
543 diff -= flt(self.doc.discount_amount)
544
545 diff = flt(diff, self.doc.precision("rounding_adjustment"))
546
Ankush Menat494bd9e2022-03-28 18:52:46 +0530547 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Dany Robert50d56db2024-01-29 09:32:44 +0530548 self.doc.grand_total_diff = diff
549 else:
550 self.doc.grand_total_diff = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530551
552 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530553 if self.doc.get("taxes"):
Dany Robert50d56db2024-01-29 09:32:44 +0530554 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(
555 self.doc.get("grand_total_diff")
556 )
Subin Tom75a76e62021-10-29 16:45:04 +0530557 else:
558 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530559
Subin Tom75a76e62021-10-29 16:45:04 +0530560 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530561 self.doc.total_taxes_and_charges = flt(
Dany Robert50d56db2024-01-29 09:32:44 +0530562 self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")),
Ankush Menat494bd9e2022-03-28 18:52:46 +0530563 self.doc.precision("total_taxes_and_charges"),
564 )
Subin Tom75a76e62021-10-29 16:45:04 +0530565 else:
566 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530567
Nabin Hait2e4de832017-09-19 14:53:16 +0530568 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530569
Ankush Menat494bd9e2022-03-28 18:52:46 +0530570 if self.doc.doctype in [
571 "Quotation",
572 "Sales Order",
573 "Delivery Note",
574 "Sales Invoice",
575 "POS Invoice",
576 ]:
577 self.doc.base_grand_total = (
578 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
579 if self.doc.total_taxes_and_charges
580 else self.doc.base_net_total
581 )
Nabin Hait3237c752015-02-17 11:11:11 +0530582 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530583 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530584 for tax in self.doc.get("taxes"):
585 if tax.category in ["Valuation and Total", "Total"]:
586 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530587 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530588 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530589 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530590
Nabin Haite7679702015-02-20 14:40:35 +0530591 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530592
Ankush Menat494bd9e2022-03-28 18:52:46 +0530593 self.doc.base_grand_total = (
594 flt(self.doc.grand_total * self.doc.conversion_rate)
595 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530596 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530597 )
Nabin Hait3237c752015-02-17 11:11:11 +0530598
Ankush Menat494bd9e2022-03-28 18:52:46 +0530599 self._set_in_company_currency(
600 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
601 )
Nabin Hait3237c752015-02-17 11:11:11 +0530602
Nabin Haite7679702015-02-20 14:40:35 +0530603 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530604
Nabin Hait2e4de832017-09-19 14:53:16 +0530605 self.set_rounded_total()
606
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530607 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530608 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530609 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530610 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530611 if d.total_weight:
612 self.doc.total_net_weight += d.total_weight
613
Nabin Hait2e4de832017-09-19 14:53:16 +0530614 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530615 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530616 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530617
Saqib Ansari17445c72022-03-07 18:01:07 +0530618 if self.doc.meta.get_field("rounded_total"):
619 if self.doc.is_rounded_total_disabled():
620 self.doc.rounded_total = self.doc.base_rounded_total = 0
621 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530622
Ankush Menat494bd9e2022-03-28 18:52:46 +0530623 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
624 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
625 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530626
Dany Robert50d56db2024-01-29 09:32:44 +0530627 # rounding adjustment should always be the difference vetween grand and rounded total
628 self.doc.rounding_adjustment = flt(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530629 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
630 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530631
632 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530633
Nabin Hait3237c752015-02-17 11:11:11 +0530634 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530635 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530636 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530637 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530638 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530639
Nabin Hait3769d872015-12-18 13:12:02 +0530640 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530641 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530642 self.doc.discount_amount = flt(
643 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
644 * self.doc.additional_discount_percentage
645 / 100,
646 self.doc.precision("discount_amount"),
647 )
Nabin Hait3237c752015-02-17 11:11:11 +0530648
649 def apply_discount_amount(self):
650 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530651 if not self.doc.apply_discount_on:
652 frappe.throw(_("Please select Apply Discount On"))
653
Deepesh Garg3b159662022-08-21 17:51:05 +0530654 self.doc.base_discount_amount = flt(
655 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
656 )
657
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530658 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
659 "is_cash_or_non_trade_discount"
660 ):
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(
685 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
686 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530687
Ankush Menat494bd9e2022-03-28 18:52:46 +0530688 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530689
Nabin Hait51e980d2015-10-10 18:10:05 +0530690 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) if item.qty else 0
Anand Doshiec5ec602015-03-05 19:31:23 +0530691
Nabin Haite7679702015-02-20 14:40:35 +0530692 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530693
694 self.discount_amount_applied = True
695 self._calculate()
696 else:
697 self.doc.base_discount_amount = 0
698
Nabin Haite7679702015-02-20 14:40:35 +0530699 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530700 if self.doc.apply_discount_on == "Net Total":
701 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530702 else:
703 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530704
Nabin Haite7679702015-02-20 14:40:35 +0530705 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530706 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530707 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
708 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530709 elif tax.row_id in actual_taxes_dict:
710 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
711 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530712
Ankush Menat494bd9e2022-03-28 18:52:46 +0530713 return flt(
714 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
715 )
Nabin Hait3237c752015-02-17 11:11:11 +0530716
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530717 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100718 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530719 total_allocated_amount = sum(
720 flt(adv.allocated_amount, adv.precision("allocated_amount"))
721 for adv in self.doc.get("advances")
722 )
Nabin Hait3237c752015-02-17 11:11:11 +0530723
Nabin Haite7679702015-02-20 14:40:35 +0530724 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530725
Faris Ansari6041f5c2018-02-08 13:33:52 +0530726 grand_total = self.doc.rounded_total or self.doc.grand_total
727
Nabin Hait289ffb72016-02-08 11:06:55 +0530728 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530729 invoice_total = flt(
730 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
731 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530732 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530733 base_write_off_amount = flt(
734 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
735 self.doc.precision("base_write_off_amount"),
736 )
737 invoice_total = (
738 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
739 - base_write_off_amount
740 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530741
Nabin Haitadc09232016-02-09 10:31:11 +0530742 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530743 frappe.throw(
744 _("Advance amount cannot be greater than {0} {1}").format(
745 self.doc.party_account_currency, invoice_total
746 )
747 )
Nabin Hait3237c752015-02-17 11:11:11 +0530748
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100749 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530750 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530751 self.doc.write_off_amount = 0
752
Nabin Hait3237c752015-02-17 11:11:11 +0530753 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530754 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530755
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530756 def is_internal_invoice(self):
757 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530758 Checks if its an internal transfer invoice
759 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530760 """
761
Ankush Menat494bd9e2022-03-28 18:52:46 +0530762 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530763 return True
764
765 return False
766
Nabin Hait3237c752015-02-17 11:11:11 +0530767 def calculate_outstanding_amount(self):
768 # NOTE:
769 # write_off_amount is only for POS Invoice
770 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530771 if self.doc.doctype == "Sales Invoice":
772 self.calculate_paid_amount()
773
Ankush Menat494bd9e2022-03-28 18:52:46 +0530774 if (
775 self.doc.is_return
776 and self.doc.return_against
777 and not self.doc.get("is_pos")
778 or self.is_internal_invoice()
779 ):
780 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530781
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530782 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530783 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530784
Nabin Hait877e1bb2017-11-17 12:27:43 +0530785 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
786 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530787 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
788
Nabin Hait877e1bb2017-11-17 12:27:43 +0530789 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530790 total_amount_to_pay = flt(
791 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
792 self.doc.precision("grand_total"),
793 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530794 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530795 total_amount_to_pay = flt(
796 flt(base_grand_total, self.doc.precision("base_grand_total"))
797 - self.doc.total_advance
798 - flt(self.doc.base_write_off_amount),
799 self.doc.precision("base_grand_total"),
800 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530801
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530802 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530803 change_amount = 0
804
Ankush Menat494bd9e2022-03-28 18:52:46 +0530805 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530806 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530807 change_amount = (
808 self.doc.change_amount
809 if self.doc.party_account_currency == self.doc.currency
810 else self.doc.base_change_amount
811 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530812
Ankush Menat494bd9e2022-03-28 18:52:46 +0530813 paid_amount = (
814 self.doc.paid_amount
815 if self.doc.party_account_currency == self.doc.currency
816 else self.doc.base_paid_amount
817 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530818
Ankush Menat494bd9e2022-03-28 18:52:46 +0530819 self.doc.outstanding_amount = flt(
820 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
821 self.doc.precision("outstanding_amount"),
822 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530823
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530824 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530825 self.doc.doctype == "Sales Invoice"
826 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530827 and self.doc.get("pos_profile")
828 and self.doc.get("is_consolidated")
829 ):
830 write_off_limit = flt(
831 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
832 )
833 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
834 self.doc.write_off_outstanding_amount_automatically = 1
835
836 if (
837 self.doc.doctype == "Sales Invoice"
838 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530839 and self.doc.get("is_return")
840 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530841 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530842 self.set_total_amount_to_default_mop(total_amount_to_pay)
843 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530844
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530845 def calculate_paid_amount(self):
846 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530847
848 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530849 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530850 payment.amount = flt(payment.amount)
851 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530852 paid_amount += payment.amount
853 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530854 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530855 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530856
Manas Solankida486ee2018-07-06 12:36:57 +0530857 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
858 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530859 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530860
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530861 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
862 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
863
Nabin Hait3bb1a422016-08-02 16:41:10 +0530864 def calculate_change_amount(self):
865 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530866 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530867 grand_total = self.doc.rounded_total or self.doc.grand_total
868 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530869
Ankush Menat494bd9e2022-03-28 18:52:46 +0530870 if (
871 self.doc.doctype == "Sales Invoice"
872 and self.doc.paid_amount > grand_total
873 and not self.doc.is_return
874 and any(d.type == "Cash" for d in self.doc.payments)
875 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530876 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530877 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530878 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530879
Ankush Menat494bd9e2022-03-28 18:52:46 +0530880 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530881 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530882 )
mbauskar36b51892016-01-18 16:31:10 +0530883
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530884 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530885 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530886 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530887 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530888 )
889 self.doc.base_write_off_amount = flt(
890 self.doc.write_off_amount * self.doc.conversion_rate,
891 self.doc.precision("base_write_off_amount"),
892 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530893
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530894 self.calculate_outstanding_amount()
895
mbauskar36b51892016-01-18 16:31:10 +0530896 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530897 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530898 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530899 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530900 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530901 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530902 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530903 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530904
Ankush Menat494bd9e2022-03-28 18:52:46 +0530905 if pricing_rule.margin_rate_or_amount and (
906 (
907 pricing_rule.currency == self.doc.currency
908 and pricing_rule.margin_type in ["Amount", "Percentage"]
909 )
910 or pricing_rule.margin_type == "Percentage"
911 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530912 item.margin_type = pricing_rule.margin_type
913 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530914 has_margin = True
915
916 if not has_margin:
917 item.margin_type = None
918 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530919
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530920 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
921 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530922 item.margin_rate_or_amount = flt(
923 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
924 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530925 item.rate_with_margin = item.rate
926
927 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530928 margin_value = (
929 item.margin_rate_or_amount
930 if item.margin_type == "Amount"
931 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
932 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530933 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530934 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530935
Shreya Shahbe690ef2017-11-14 17:22:41 +0530936 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530937
938 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530939 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530940
Subin Tom7d627df2021-08-23 11:05:07 +0530941 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530942 total_paid_amount = 0
943 for payment in self.doc.get("payments"):
944 total_paid_amount += (
945 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530946 )
947
Deepesh Garg06e8e282022-10-31 19:58:46 +0530948 pending_amount = total_amount_to_pay - total_paid_amount
949
950 if pending_amount > 0:
951 default_mode_of_payment = frappe.db.get_value(
952 "POS Payment Method",
953 {"parent": self.doc.pos_profile, "default": 1},
954 ["mode_of_payment"],
955 as_dict=1,
956 )
957
958 if default_mode_of_payment:
959 self.doc.payments = []
960 self.doc.append(
961 "payments",
962 {
963 "mode_of_payment": default_mode_of_payment.mode_of_payment,
964 "amount": pending_amount,
965 "default": 1,
966 },
967 )
968
Deepesh Garg0ebace52020-02-25 13:21:16 +0530969
Nabin Hait9c421612017-07-20 13:32:01 +0530970def get_itemised_tax_breakup_html(doc):
971 if not doc.taxes:
972 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530973
Nabin Hait9c421612017-07-20 13:32:01 +0530974 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530975 tax_accounts = []
976 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530977 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530978 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530979 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530980 tax_accounts.append(tax.description)
981
Sagar Vora4205f562023-07-24 18:37:36 +0530982 with temporary_flag("company", doc.company):
983 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530984 itemised_tax_data = get_itemised_tax_breakup_data(doc)
985 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530986 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530987
Nabin Hait9c421612017-07-20 13:32:01 +0530988 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530989 "templates/includes/itemised_tax_breakup.html",
990 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530991 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530992 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +0530993 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530994 doc=doc,
995 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530996 )
Nabin Hait852cb642017-07-05 12:58:19 +0530997
Ankush Menat494bd9e2022-03-28 18:52:46 +0530998
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530999@frappe.whitelist()
1000def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +05301001 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +05301002 with temporary_flag("company", company):
1003 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301004
Ankush Menat494bd9e2022-03-28 18:52:46 +05301005
Deepesh Garg6a5ef262021-02-19 14:30:23 +05301006@erpnext.allow_regional
1007def get_regional_round_off_accounts(company, account_list):
1008 pass
rohitwaghchaured4526682017-12-28 14:20:13 +05301009
Ankush Menat494bd9e2022-03-28 18:52:46 +05301010
rohitwaghchaured4526682017-12-28 14:20:13 +05301011@erpnext.allow_regional
1012def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301013 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +05301014 pass
1015
Ankush Menat494bd9e2022-03-28 18:52:46 +05301016
Nabin Haitb962fc12017-07-17 18:02:31 +05301017@erpnext.allow_regional
1018def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
1019 return [_("Item"), _("Taxable Amount")] + tax_accounts
1020
Ankush Menat494bd9e2022-03-28 18:52:46 +05301021
Nabin Haitb962fc12017-07-17 18:02:31 +05301022@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +05301023def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +05301024 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301025
1026 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1027
DaizyModib84deec2023-07-21 17:26:35 +05301028 itemised_tax_data = []
1029 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301030 itemised_tax_data.append(
1031 frappe._dict(
ljain11258859782024-02-26 18:41:33 +05301032 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code, 0), **taxes}
DaizyModi6f376cf2023-07-24 18:02:42 +05301033 )
1034 )
DaizyModib84deec2023-07-21 17:26:35 +05301035
1036 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301037
Ankush Menat494bd9e2022-03-28 18:52:46 +05301038
Nabin Hait34c551d2019-07-03 10:34:31 +05301039def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301040 itemised_tax = {}
1041 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301042 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301043 continue
1044
Nabin Haitb962fc12017-07-17 18:02:31 +05301045 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301046 if item_tax_map:
1047 for item_code, tax_data in item_tax_map.items():
1048 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301049
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301050 tax_rate = 0.0
1051 tax_amount = 0.0
1052
Nabin Hait2e4de832017-09-19 14:53:16 +05301053 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301054 tax_rate = flt(tax_data[0])
1055 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301056 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301057 tax_rate = flt(tax_data)
1058
Ankush Menat494bd9e2022-03-28 18:52:46 +05301059 itemised_tax[item_code][tax.description] = frappe._dict(
1060 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1061 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301062
Nabin Hait34c551d2019-07-03 10:34:31 +05301063 if with_tax_account:
1064 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1065
Nabin Haitb962fc12017-07-17 18:02:31 +05301066 return itemised_tax
1067
Ankush Menat494bd9e2022-03-28 18:52:46 +05301068
Nabin Haitb962fc12017-07-17 18:02:31 +05301069def get_itemised_taxable_amount(items):
1070 itemised_taxable_amount = frappe._dict()
1071 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301072 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301073 itemised_taxable_amount.setdefault(item_code, 0)
1074 itemised_taxable_amount[item_code] += item.net_amount
1075
Nabin Haitcaab5822017-08-24 16:22:28 +05301076 return itemised_taxable_amount
1077
Ankush Menat494bd9e2022-03-28 18:52:46 +05301078
DaizyModi6f376cf2023-07-24 18:02:42 +05301079def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301080 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301081 for taxes in itemised_tax:
1082 for row in taxes.values():
1083 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1084 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301085
Ankush Menat494bd9e2022-03-28 18:52:46 +05301086
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301087class init_landed_taxes_and_totals(object):
1088 def __init__(self, doc):
1089 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301090 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301091 self.set_account_currency()
1092 self.set_exchange_rate()
1093 self.set_amounts_in_company_currency()
1094
1095 def set_account_currency(self):
1096 company_currency = erpnext.get_company_currency(self.doc.company)
1097 for d in self.doc.get(self.tax_field):
1098 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301099 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301100 d.account_currency = account_currency or company_currency
1101
1102 def set_exchange_rate(self):
1103 company_currency = erpnext.get_company_currency(self.doc.company)
1104 for d in self.doc.get(self.tax_field):
1105 if d.account_currency == company_currency:
1106 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301107 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301108 d.exchange_rate = get_exchange_rate(
1109 self.doc.posting_date,
1110 account=d.expense_account,
1111 account_currency=d.account_currency,
1112 company=self.doc.company,
1113 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301114
1115 if not d.exchange_rate:
1116 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1117
1118 def set_amounts_in_company_currency(self):
1119 for d in self.doc.get(self.tax_field):
1120 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301121 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))