blob: f9f68a119b37508eafab2d066b87340fb29836ae [file] [log] [blame]
Anand Doshi885e0742015-03-03 14:55:30 +05301# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
Nabin Hait3237c752015-02-17 11:11:11 +05302# License: GNU General Public License v3. See license.txt
3
Chillar Anand915b3432021-09-02 16:44:59 +05304
Nabin Hait3237c752015-02-17 11:11:11 +05305import json
Chillar Anand915b3432021-09-02 16:44:59 +05306
7import frappe
Nabin Hait3769d872015-12-18 13:12:02 +05308from frappe import _, scrub
Raffael Meyer67cf7e12023-01-15 13:04:16 +01009from frappe.model.document import Document
Nabin Haitb962fc12017-07-17 18:02:31 +053010from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
Chillar Anand915b3432021-09-02 16:44:59 +053011
12import erpnext
Deepesh Gargbfc17e42020-12-25 18:34:39 +053013from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Chillar Anand915b3432021-09-02 16:44:59 +053014from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
15from erpnext.controllers.accounts_controller import (
16 validate_conversion_rate,
17 validate_inclusive_tax,
18 validate_taxes_and_charges,
19)
20from erpnext.stock.get_item_details import _get_item_tax_template
Sagar Vora4205f562023-07-24 18:37:36 +053021from erpnext.utilities.regional import temporary_flag
Chillar Anand915b3432021-09-02 16:44:59 +053022
Nabin Hait3237c752015-02-17 11:11:11 +053023
Nabin Haitfe81da22015-02-18 12:23:18 +053024class calculate_taxes_and_totals(object):
Raffael Meyer67cf7e12023-01-15 13:04:16 +010025 def __init__(self, doc: Document):
Nabin Hait3237c752015-02-17 11:11:11 +053026 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053027 frappe.flags.round_off_applicable_accounts = []
Dany Robertdfb5b882023-08-23 04:01:00 +000028 frappe.flags.round_row_wise_tax = frappe.db.get_single_value(
29 "Accounts Settings", "round_row_wise_tax"
Dany Robert3ead2892023-08-22 14:41:07 +000030 )
marination91982d12023-01-24 18:03:53 +053031
32 self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
33
Deepesh Garg6a5ef262021-02-19 14:30:23 +053034 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053035 self.calculate()
36
marination91982d12023-01-24 18:03:53 +053037 def filter_rows(self):
38 """Exclude rows, that do not fulfill the filter criteria, from totals computation."""
marinationcef7dfd2023-01-26 14:36:25 +053039 items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
marination91982d12023-01-24 18:03:53 +053040 return items
41
Nabin Hait3237c752015-02-17 11:11:11 +053042 def calculate(self):
marination91982d12023-01-24 18:03:53 +053043 if not len(self._items):
Nabin Haitb315acb2019-07-12 14:27:19 +053044 return
45
Nabin Hait3237c752015-02-17 11:11:11 +053046 self.discount_amount_applied = False
47 self._calculate()
48
49 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053050 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053051 self.apply_discount_amount()
52
Deepesh Garg3b159662022-08-21 17:51:05 +053053 # Update grand total as per cash and non trade discount
54 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
55 self.doc.grand_total -= self.doc.discount_amount
56 self.doc.base_grand_total -= self.doc.base_discount_amount
Dany Robert3a487bd2023-11-18 13:32:04 +000057 self.doc.rounding_adjustment = self.doc.base_rounding_adjustment = 0.0
Deepesh Garg318da162022-08-29 14:18:39 +053058 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053059
Deepesh Gargd596e0e2022-03-10 20:56:36 +053060 self.calculate_shipping_charges()
61
Nabin Haitbd00e812015-02-17 12:50:51 +053062 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053063 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053064
Nabin Hait852cb642017-07-05 12:58:19 +053065 if self.doc.meta.get_field("other_charges_calculation"):
66 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053067
68 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053069 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053070 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053071 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053072 self.initialize_taxes()
73 self.determine_exclusive_rate()
74 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053075 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053076 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053077 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053078 self.calculate_totals()
79 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053080 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053081
niralisataparae758a752022-10-03 16:39:35 +053082 def calculate_tax_withholding_net_total(self):
83 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053084 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053085 sum_base_net_amount = 0
marination91982d12023-01-24 18:03:53 +053086 for item in self._items:
niralisataparae758a752022-10-03 16:39:35 +053087 if hasattr(item, "apply_tds") and item.apply_tds:
88 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053089 sum_base_net_amount += item.base_net_amount
90
niralisataparae758a752022-10-03 16:39:35 +053091 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053092 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053093
Deepesh Gargef0d26c2020-01-06 15:34:15 +053094 def validate_item_tax_template(self):
marination91982d12023-01-24 18:03:53 +053095 for item in self._items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053096 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053097 item_doc = frappe.get_cached_doc("Item", item.item_code)
98 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053099 "net_rate": item.net_rate or item.rate,
100 "tax_category": self.doc.get("tax_category"),
101 "posting_date": self.doc.get("posting_date"),
102 "bill_date": self.doc.get("bill_date"),
103 "transaction_date": self.doc.get("transaction_date"),
104 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530105 }
106
107 item_group = item_doc.item_group
108 item_group_taxes = []
109
110 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530111 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530112 item_group_taxes += item_group_doc.taxes or []
113 item_group = item_group_doc.parent_item_group
114
115 item_taxes = item_doc.taxes or []
116
117 if not item_group_taxes and (not item_taxes):
118 # No validation if no taxes in item or item group
119 continue
120
121 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
122
Deepesh Garg18be7672021-06-06 13:25:34 +0530123 if taxes:
124 if item.item_tax_template not in taxes:
125 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530126 frappe.msgprint(
127 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
128 item.idx, frappe.bold(item.item_code)
129 )
130 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530131
Nabin Haite7679702015-02-20 14:40:35 +0530132 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530133 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530134 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530135 if not self.doc.currency or self.doc.currency == company_currency:
136 self.doc.currency = company_currency
137 self.doc.conversion_rate = 1.0
138 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530139 validate_conversion_rate(
140 self.doc.currency,
141 self.doc.conversion_rate,
142 self.doc.meta.get_label("conversion_rate"),
143 self.doc.company,
144 )
Nabin Hait3237c752015-02-17 11:11:11 +0530145
146 self.doc.conversion_rate = flt(self.doc.conversion_rate)
147
Nabin Hait3237c752015-02-17 11:11:11 +0530148 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530149 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530150 return
151
Nabin Hait3237c752015-02-17 11:11:11 +0530152 if not self.discount_amount_applied:
marination91982d12023-01-24 18:03:53 +0530153 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530154 self.doc.round_floats_in(item)
155
156 if item.discount_percentage == 100:
157 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530158 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530159 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530160 item.rate = flt(
161 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
162 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530163
Deepesh Garga83a0a02022-03-25 12:28:55 +0530164 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530165
Deepesh Garg97e102c2022-03-25 12:39:59 +0530166 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530167 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530168
Ankush Menat494bd9e2022-03-28 18:52:46 +0530169 if item.doctype in [
170 "Quotation Item",
171 "Sales Order Item",
172 "Delivery Note Item",
173 "Sales Invoice Item",
174 "POS Invoice Item",
175 "Purchase Invoice Item",
176 "Purchase Order Item",
177 "Purchase Receipt Item",
178 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530179 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530180 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530181 item.rate = flt(
182 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
183 )
Nabin Hait10c61372021-04-13 15:46:01 +0530184
Walstan Baptista37b826b2021-04-03 19:48:46 +0530185 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530186 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530187 else:
188 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530189
Nabin Hait64bfdd92019-04-23 13:37:19 +0530190 elif flt(item.price_list_rate) > 0:
191 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530192 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
193 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530194
Nabin Haite7679702015-02-20 14:40:35 +0530195 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530196
mergify[bot]10c666b2023-10-06 14:43:13 +0530197 if (
198 not item.qty and self.doc.get("is_return") and self.doc.get("doctype") != "Purchase Receipt"
199 ):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530200 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530201 elif not item.qty and self.doc.get("is_debit_note"):
202 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530203 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530204 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530205
Nabin Haite7679702015-02-20 14:40:35 +0530206 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530207
Ankush Menat494bd9e2022-03-28 18:52:46 +0530208 self._set_in_company_currency(
209 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
210 )
Nabin Hait3237c752015-02-17 11:11:11 +0530211
Nabin Haite7679702015-02-20 14:40:35 +0530212 item.item_tax_amount = 0.0
213
214 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530215 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530216 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530217 val = flt(
218 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
219 )
Nabin Haite7679702015-02-20 14:40:35 +0530220 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530221
222 def initialize_taxes(self):
223 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530224 if not self.discount_amount_applied:
225 validate_taxes_and_charges(tax)
226 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530227
Ankush Menat494bd9e2022-03-28 18:52:46 +0530228 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530229 tax.item_wise_tax_detail = {}
230
Ankush Menat494bd9e2022-03-28 18:52:46 +0530231 tax_fields = [
232 "total",
233 "tax_amount_after_discount_amount",
234 "tax_amount_for_current_item",
235 "grand_total_for_current_item",
236 "tax_fraction_for_current_item",
237 "grand_total_fraction_for_current_item",
238 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530239
Ankush Menat494bd9e2022-03-28 18:52:46 +0530240 if tax.charge_type != "Actual" and not (
241 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
242 ):
243 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530244
245 for fieldname in tax_fields:
246 tax.set(fieldname, 0.0)
247
Nabin Hait3237c752015-02-17 11:11:11 +0530248 self.doc.round_floats_in(tax)
249
Nabin Hait3237c752015-02-17 11:11:11 +0530250 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530251 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530252 return
Nabin Hait3237c752015-02-17 11:11:11 +0530253
marination91982d12023-01-24 18:03:53 +0530254 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530255 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
256 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530257 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530258 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530259 (
260 tax.tax_fraction_for_current_item,
261 inclusive_tax_amount_per_qty,
262 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530263
Ankush Menat494bd9e2022-03-28 18:52:46 +0530264 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530265 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
266 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530267 tax.grand_total_fraction_for_current_item = (
268 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530269 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530270 )
Nabin Hait3237c752015-02-17 11:11:11 +0530271
272 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530273 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530274
Ankush Menat494bd9e2022-03-28 18:52:46 +0530275 if (
276 not self.discount_amount_applied
277 and item.qty
278 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
279 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530280 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
281
282 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530283 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530284 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530285
Nabin Haite7679702015-02-20 14:40:35 +0530286 self._set_in_company_currency(item, ["net_rate", "net_amount"])
287
Nabin Hait3237c752015-02-17 11:11:11 +0530288 def _load_item_tax_rate(self, item_tax_rate):
289 return json.loads(item_tax_rate) if item_tax_rate else {}
290
291 def get_current_tax_fraction(self, tax, item_tax_map):
292 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530293 Get tax fraction for calculating tax exclusive amount
294 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530295 """
296 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530297 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530298
299 if cint(tax.included_in_print_rate):
300 tax_rate = self._get_tax_rate(tax, item_tax_map)
301
302 if tax.charge_type == "On Net Total":
303 current_tax_fraction = tax_rate / 100.0
304
305 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530306 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
307 cint(tax.row_id) - 1
308 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530309
310 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530311 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
312 cint(tax.row_id) - 1
313 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530314
Nabin Hait19ea7212020-08-11 20:34:57 +0530315 elif tax.charge_type == "On Item Quantity":
316 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530317
Nabin Hait19ea7212020-08-11 20:34:57 +0530318 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
319 current_tax_fraction *= -1.0
320 inclusive_tax_amount_per_qty *= -1.0
321
322 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530323
324 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530325 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530326 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
327 else:
328 return tax.rate
329
330 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530331 self.doc.total_qty = (
332 self.doc.total
333 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530334
marination91982d12023-01-24 18:03:53 +0530335 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530336 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530337 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530338 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530339 self.doc.net_total += item.net_amount
340 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530341
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530342 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530343
Subin Toma8e2c022021-11-16 19:06:49 +0530344 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530345
346 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530347 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530348 return
349
Subin Tomaf1fce02021-11-10 16:49:12 +0530350 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530351 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
352 shipping_rule.apply(self.doc)
353
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530354 self._calculate()
355
Nabin Hait3237c752015-02-17 11:11:11 +0530356 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530357 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
358 "rounding_adjustment"
359 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530360 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530361 self.doc.rounding_adjustment = 0
362
Nabin Hait3237c752015-02-17 11:11:11 +0530363 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530364 actual_tax_dict = dict(
365 [
366 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
367 for tax in self.doc.get("taxes")
368 if tax.charge_type == "Actual"
369 ]
370 )
Nabin Hait3237c752015-02-17 11:11:11 +0530371
marination91982d12023-01-24 18:03:53 +0530372 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530373 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530374 for i, tax in enumerate(self.doc.get("taxes")):
375 # tax_amount represents the amount of tax for the current step
376 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
Dany Robert3ead2892023-08-22 14:41:07 +0000377 if frappe.flags.round_row_wise_tax:
378 current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530379
380 # Adjust divisional loss to the last item
381 if tax.charge_type == "Actual":
382 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530383 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530384 current_tax_amount += actual_tax_dict[tax.idx]
385
Nabin Hait2b019ed2015-02-22 23:03:07 +0530386 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530387 if tax.charge_type != "Actual" and not (
388 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
389 ):
390 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530391
Nabin Hait3237c752015-02-17 11:11:11 +0530392 # store tax_amount for current item as it will be used for
393 # charge type = 'On Previous Row Amount'
394 tax.tax_amount_for_current_item = current_tax_amount
395
Nabin Hait2b019ed2015-02-22 23:03:07 +0530396 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530397 tax.tax_amount_after_discount_amount += current_tax_amount
398
Nabin Haitcd951342017-07-31 18:07:45 +0530399 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530400
Nabin Hait3237c752015-02-17 11:11:11 +0530401 # note: grand_total_for_current_item contains the contribution of
402 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530403 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530404 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530405 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530406 tax.grand_total_for_current_item = flt(
407 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
408 )
Nabin Hait3237c752015-02-17 11:11:11 +0530409
410 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530411 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530412 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530413 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530414
415 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530416 self.set_cumulative_total(i, tax)
417
Deepesh Gargb6705882021-04-14 11:20:27 +0530418 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530419
Nabin Hait3237c752015-02-17 11:11:11 +0530420 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530421 if (
422 i == (len(self.doc.get("taxes")) - 1)
423 and self.discount_amount_applied
424 and self.doc.discount_amount
425 and self.doc.apply_discount_on == "Grand Total"
426 and not rounding_adjustment_computed
427 ):
428 self.doc.rounding_adjustment = flt(
429 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
430 self.doc.precision("rounding_adjustment"),
431 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530432
Nabin Haitcd951342017-07-31 18:07:45 +0530433 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
434 # if just for valuation, do not add the tax amount in total
435 # if tax/charges is for deduction, multiply by -1
436 if getattr(tax, "category", None):
437 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530438 if self.doc.doctype in [
439 "Purchase Order",
440 "Purchase Invoice",
441 "Purchase Receipt",
442 "Supplier Quotation",
443 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530444 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530445 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530446
Nabin Haitcd951342017-07-31 18:07:45 +0530447 def set_cumulative_total(self, row_idx, tax):
448 tax_amount = tax.tax_amount_after_discount_amount
449 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
450
451 if row_idx == 0:
452 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
453 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530454 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530455
456 def get_current_tax_amount(self, item, tax, item_tax_map):
457 tax_rate = self._get_tax_rate(tax, item_tax_map)
458 current_tax_amount = 0.0
459
460 if tax.charge_type == "Actual":
461 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530462 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530463 current_tax_amount = (
464 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
465 )
Nabin Haite7679702015-02-20 14:40:35 +0530466
Nabin Hait3237c752015-02-17 11:11:11 +0530467 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530468 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530469 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530470 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
471 cint(tax.row_id) - 1
472 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530473 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530474 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
475 cint(tax.row_id) - 1
476 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530477 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530478 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530479
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530480 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530481 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530482
483 return current_tax_amount
484
Nabin Haite7679702015-02-20 14:40:35 +0530485 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
486 # store tax breakup for each item
487 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530488 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000489 if frappe.flags.round_row_wise_tax:
490 item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
491 if tax.item_wise_tax_detail.get(key):
492 item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
Dany Robert9e1b2c92023-08-24 05:02:14 +0000493 tax.item_wise_tax_detail[key] = [
494 tax_rate,
495 flt(item_wise_tax_amount, tax.precision("tax_amount")),
496 ]
Dany Robert0ebcc2c2023-08-23 04:51:09 +0000497 else:
498 if tax.item_wise_tax_detail.get(key):
499 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
Nabin Haite7679702015-02-20 14:40:35 +0530500
Dany Robert9e1b2c92023-08-24 05:02:14 +0000501 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530502
Nabin Hait3237c752015-02-17 11:11:11 +0530503 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530504 if tax.account_head in frappe.flags.round_off_applicable_accounts:
505 tax.tax_amount = round(tax.tax_amount, 0)
506 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
507
Nabin Haite7679702015-02-20 14:40:35 +0530508 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530509 tax.tax_amount_after_discount_amount = flt(
510 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
511 )
Nabin Haitce245122015-02-22 20:14:49 +0530512
Deepesh Gargb6705882021-04-14 11:20:27 +0530513 def round_off_base_values(self, tax):
514 # Round off to nearest integer based on regional settings
515 if tax.account_head in frappe.flags.round_off_applicable_accounts:
516 tax.base_tax_amount = round(tax.base_tax_amount, 0)
517 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
518
Nabin Haita1bf43b2015-03-17 10:50:47 +0530519 def manipulate_grand_total_for_inclusive_tax(self):
520 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530521 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 +0530522 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530523 non_inclusive_tax_amount = sum(
524 flt(d.tax_amount_after_discount_amount)
525 for d in self.doc.get("taxes")
526 if not d.included_in_print_rate
527 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530528
Ankush Menat494bd9e2022-03-28 18:52:46 +0530529 diff = (
530 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
531 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530532
533 # If discount amount applied, deduct the discount amount
534 # because self.doc.total is always without discount, but last_tax.total is after discount
535 if self.discount_amount_applied and self.doc.discount_amount:
536 diff -= flt(self.doc.discount_amount)
537
538 diff = flt(diff, self.doc.precision("rounding_adjustment"))
539
Ankush Menat494bd9e2022-03-28 18:52:46 +0530540 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530541 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530542
543 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530544 if self.doc.get("taxes"):
545 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
546 else:
547 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530548
Subin Tom75a76e62021-10-29 16:45:04 +0530549 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530550 self.doc.total_taxes_and_charges = flt(
551 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
552 self.doc.precision("total_taxes_and_charges"),
553 )
Subin Tom75a76e62021-10-29 16:45:04 +0530554 else:
555 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530556
Nabin Hait2e4de832017-09-19 14:53:16 +0530557 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530558
Ankush Menat494bd9e2022-03-28 18:52:46 +0530559 if self.doc.doctype in [
560 "Quotation",
561 "Sales Order",
562 "Delivery Note",
563 "Sales Invoice",
564 "POS Invoice",
565 ]:
566 self.doc.base_grand_total = (
567 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
568 if self.doc.total_taxes_and_charges
569 else self.doc.base_net_total
570 )
Nabin Hait3237c752015-02-17 11:11:11 +0530571 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530572 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530573 for tax in self.doc.get("taxes"):
574 if tax.category in ["Valuation and Total", "Total"]:
575 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530576 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530577 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530578 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530579
Nabin Haite7679702015-02-20 14:40:35 +0530580 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530581
Ankush Menat494bd9e2022-03-28 18:52:46 +0530582 self.doc.base_grand_total = (
583 flt(self.doc.grand_total * self.doc.conversion_rate)
584 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530585 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530586 )
Nabin Hait3237c752015-02-17 11:11:11 +0530587
Ankush Menat494bd9e2022-03-28 18:52:46 +0530588 self._set_in_company_currency(
589 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
590 )
Nabin Hait3237c752015-02-17 11:11:11 +0530591
Nabin Haite7679702015-02-20 14:40:35 +0530592 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530593
Nabin Hait2e4de832017-09-19 14:53:16 +0530594 self.set_rounded_total()
595
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530596 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530597 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530598 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530599 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530600 if d.total_weight:
601 self.doc.total_net_weight += d.total_weight
602
Nabin Hait2e4de832017-09-19 14:53:16 +0530603 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530604 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530605 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530606
Saqib Ansari17445c72022-03-07 18:01:07 +0530607 if self.doc.meta.get_field("rounded_total"):
608 if self.doc.is_rounded_total_disabled():
609 self.doc.rounded_total = self.doc.base_rounded_total = 0
610 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530611
Ankush Menat494bd9e2022-03-28 18:52:46 +0530612 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
613 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
614 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530615
Ankush Menat494bd9e2022-03-28 18:52:46 +0530616 # if print_in_rate is set, we would have already calculated rounding adjustment
617 self.doc.rounding_adjustment += flt(
618 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
619 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530620
621 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530622
Nabin Hait3237c752015-02-17 11:11:11 +0530623 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530624 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530625 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530626 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530627 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530628
Nabin Hait3769d872015-12-18 13:12:02 +0530629 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530630 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530631 self.doc.discount_amount = flt(
632 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
633 * self.doc.additional_discount_percentage
634 / 100,
635 self.doc.precision("discount_amount"),
636 )
Nabin Hait3237c752015-02-17 11:11:11 +0530637
638 def apply_discount_amount(self):
639 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530640 if not self.doc.apply_discount_on:
641 frappe.throw(_("Please select Apply Discount On"))
642
Deepesh Garg3b159662022-08-21 17:51:05 +0530643 self.doc.base_discount_amount = flt(
644 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
645 )
646
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530647 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
648 "is_cash_or_non_trade_discount"
649 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530650 self.discount_amount_applied = True
651 return
652
Nabin Haite7679702015-02-20 14:40:35 +0530653 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530654 taxes = self.doc.get("taxes")
655 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530656
Nabin Haite7679702015-02-20 14:40:35 +0530657 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530658 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530659 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530660 distributed_amount = (
661 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
662 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530663
Nabin Haite7679702015-02-20 14:40:35 +0530664 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530665 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530666
Nabin Hait25bd84d2015-03-04 15:06:56 +0530667 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530668 if (
669 self.doc.apply_discount_on == "Net Total"
670 or not taxes
671 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530672 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530673 discount_amount_loss = flt(
674 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
675 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530676
Ankush Menat494bd9e2022-03-28 18:52:46 +0530677 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530678
Nabin Hait51e980d2015-10-10 18:10:05 +0530679 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 +0530680
Nabin Haite7679702015-02-20 14:40:35 +0530681 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530682
683 self.discount_amount_applied = True
684 self._calculate()
685 else:
686 self.doc.base_discount_amount = 0
687
Nabin Haite7679702015-02-20 14:40:35 +0530688 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530689 if self.doc.apply_discount_on == "Net Total":
690 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530691 else:
692 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530693
Nabin Haite7679702015-02-20 14:40:35 +0530694 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530695 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530696 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
697 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530698 elif tax.row_id in actual_taxes_dict:
699 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
700 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530701
Ankush Menat494bd9e2022-03-28 18:52:46 +0530702 return flt(
703 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
704 )
Nabin Hait3237c752015-02-17 11:11:11 +0530705
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530706 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100707 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530708 total_allocated_amount = sum(
709 flt(adv.allocated_amount, adv.precision("allocated_amount"))
710 for adv in self.doc.get("advances")
711 )
Nabin Hait3237c752015-02-17 11:11:11 +0530712
Nabin Haite7679702015-02-20 14:40:35 +0530713 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530714
Faris Ansari6041f5c2018-02-08 13:33:52 +0530715 grand_total = self.doc.rounded_total or self.doc.grand_total
716
Nabin Hait289ffb72016-02-08 11:06:55 +0530717 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530718 invoice_total = flt(
719 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
720 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530721 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530722 base_write_off_amount = flt(
723 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
724 self.doc.precision("base_write_off_amount"),
725 )
726 invoice_total = (
727 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
728 - base_write_off_amount
729 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530730
Nabin Haitadc09232016-02-09 10:31:11 +0530731 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530732 frappe.throw(
733 _("Advance amount cannot be greater than {0} {1}").format(
734 self.doc.party_account_currency, invoice_total
735 )
736 )
Nabin Hait3237c752015-02-17 11:11:11 +0530737
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100738 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530739 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530740 self.doc.write_off_amount = 0
741
Nabin Hait3237c752015-02-17 11:11:11 +0530742 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530743 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530744
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530745 def is_internal_invoice(self):
746 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530747 Checks if its an internal transfer invoice
748 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530749 """
750
Ankush Menat494bd9e2022-03-28 18:52:46 +0530751 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530752 return True
753
754 return False
755
Nabin Hait3237c752015-02-17 11:11:11 +0530756 def calculate_outstanding_amount(self):
757 # NOTE:
758 # write_off_amount is only for POS Invoice
759 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530760 if self.doc.doctype == "Sales Invoice":
761 self.calculate_paid_amount()
762
Ankush Menat494bd9e2022-03-28 18:52:46 +0530763 if (
764 self.doc.is_return
765 and self.doc.return_against
766 and not self.doc.get("is_pos")
767 or self.is_internal_invoice()
768 ):
769 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530770
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530771 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530772 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530773
Nabin Hait877e1bb2017-11-17 12:27:43 +0530774 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
775 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530776 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
777
Nabin Hait877e1bb2017-11-17 12:27:43 +0530778 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530779 total_amount_to_pay = flt(
780 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
781 self.doc.precision("grand_total"),
782 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530783 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530784 total_amount_to_pay = flt(
785 flt(base_grand_total, self.doc.precision("base_grand_total"))
786 - self.doc.total_advance
787 - flt(self.doc.base_write_off_amount),
788 self.doc.precision("base_grand_total"),
789 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530790
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530791 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530792 change_amount = 0
793
Ankush Menat494bd9e2022-03-28 18:52:46 +0530794 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530795 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530796 change_amount = (
797 self.doc.change_amount
798 if self.doc.party_account_currency == self.doc.currency
799 else self.doc.base_change_amount
800 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530801
Ankush Menat494bd9e2022-03-28 18:52:46 +0530802 paid_amount = (
803 self.doc.paid_amount
804 if self.doc.party_account_currency == self.doc.currency
805 else self.doc.base_paid_amount
806 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530807
Ankush Menat494bd9e2022-03-28 18:52:46 +0530808 self.doc.outstanding_amount = flt(
809 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
810 self.doc.precision("outstanding_amount"),
811 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530812
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530813 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530814 self.doc.doctype == "Sales Invoice"
815 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530816 and self.doc.get("pos_profile")
817 and self.doc.get("is_consolidated")
818 ):
819 write_off_limit = flt(
820 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
821 )
822 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
823 self.doc.write_off_outstanding_amount_automatically = 1
824
825 if (
826 self.doc.doctype == "Sales Invoice"
827 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530828 and self.doc.get("is_return")
829 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530830 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530831 self.set_total_amount_to_default_mop(total_amount_to_pay)
832 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530833
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530834 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530835
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530836 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530837
838 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530839 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530840 payment.amount = flt(payment.amount)
841 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530842 paid_amount += payment.amount
843 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530844 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530845 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530846
Manas Solankida486ee2018-07-06 12:36:57 +0530847 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
848 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530849 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530850
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530851 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
852 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
853
Nabin Hait3bb1a422016-08-02 16:41:10 +0530854 def calculate_change_amount(self):
855 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530856 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530857 grand_total = self.doc.rounded_total or self.doc.grand_total
858 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530859
Ankush Menat494bd9e2022-03-28 18:52:46 +0530860 if (
861 self.doc.doctype == "Sales Invoice"
862 and self.doc.paid_amount > grand_total
863 and not self.doc.is_return
864 and any(d.type == "Cash" for d in self.doc.payments)
865 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530866 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530867 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530868 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530869
Ankush Menat494bd9e2022-03-28 18:52:46 +0530870 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530871 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530872 )
mbauskar36b51892016-01-18 16:31:10 +0530873
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530874 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530875 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530876 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530877 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530878 )
879 self.doc.base_write_off_amount = flt(
880 self.doc.write_off_amount * self.doc.conversion_rate,
881 self.doc.precision("base_write_off_amount"),
882 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530883
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530884 self.calculate_outstanding_amount()
885
mbauskar36b51892016-01-18 16:31:10 +0530886 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530887 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530888 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530889 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530890 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530891 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530892 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530893 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530894
Ankush Menat494bd9e2022-03-28 18:52:46 +0530895 if pricing_rule.margin_rate_or_amount and (
896 (
897 pricing_rule.currency == self.doc.currency
898 and pricing_rule.margin_type in ["Amount", "Percentage"]
899 )
900 or pricing_rule.margin_type == "Percentage"
901 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530902 item.margin_type = pricing_rule.margin_type
903 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530904 has_margin = True
905
906 if not has_margin:
907 item.margin_type = None
908 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530909
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530910 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
911 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530912 item.margin_rate_or_amount = flt(
913 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
914 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530915 item.rate_with_margin = item.rate
916
917 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530918 margin_value = (
919 item.margin_rate_or_amount
920 if item.margin_type == "Amount"
921 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
922 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530923 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530924 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530925
Shreya Shahbe690ef2017-11-14 17:22:41 +0530926 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530927
928 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530929 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530930
Subin Tom7d627df2021-08-23 11:05:07 +0530931 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530932 total_paid_amount = 0
933 for payment in self.doc.get("payments"):
934 total_paid_amount += (
935 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530936 )
937
Deepesh Garg06e8e282022-10-31 19:58:46 +0530938 pending_amount = total_amount_to_pay - total_paid_amount
939
940 if pending_amount > 0:
941 default_mode_of_payment = frappe.db.get_value(
942 "POS Payment Method",
943 {"parent": self.doc.pos_profile, "default": 1},
944 ["mode_of_payment"],
945 as_dict=1,
946 )
947
948 if default_mode_of_payment:
949 self.doc.payments = []
950 self.doc.append(
951 "payments",
952 {
953 "mode_of_payment": default_mode_of_payment.mode_of_payment,
954 "amount": pending_amount,
955 "default": 1,
956 },
957 )
958
Deepesh Garg0ebace52020-02-25 13:21:16 +0530959
Nabin Hait9c421612017-07-20 13:32:01 +0530960def get_itemised_tax_breakup_html(doc):
961 if not doc.taxes:
962 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530963
Nabin Hait9c421612017-07-20 13:32:01 +0530964 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530965 tax_accounts = []
966 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530967 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530968 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530969 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530970 tax_accounts.append(tax.description)
971
Sagar Vora4205f562023-07-24 18:37:36 +0530972 with temporary_flag("company", doc.company):
973 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530974 itemised_tax_data = get_itemised_tax_breakup_data(doc)
975 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530976 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530977
Nabin Hait9c421612017-07-20 13:32:01 +0530978 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530979 "templates/includes/itemised_tax_breakup.html",
980 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530981 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530982 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +0530983 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530984 doc=doc,
985 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530986 )
Nabin Hait852cb642017-07-05 12:58:19 +0530987
Ankush Menat494bd9e2022-03-28 18:52:46 +0530988
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530989@frappe.whitelist()
990def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +0530991 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +0530992 with temporary_flag("company", company):
993 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530994
Ankush Menat494bd9e2022-03-28 18:52:46 +0530995
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530996@erpnext.allow_regional
997def get_regional_round_off_accounts(company, account_list):
998 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530999
Ankush Menat494bd9e2022-03-28 18:52:46 +05301000
rohitwaghchaured4526682017-12-28 14:20:13 +05301001@erpnext.allow_regional
1002def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +05301003 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +05301004 pass
1005
Ankush Menat494bd9e2022-03-28 18:52:46 +05301006
Nabin Haitb962fc12017-07-17 18:02:31 +05301007@erpnext.allow_regional
1008def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
1009 return [_("Item"), _("Taxable Amount")] + tax_accounts
1010
Ankush Menat494bd9e2022-03-28 18:52:46 +05301011
Nabin Haitb962fc12017-07-17 18:02:31 +05301012@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +05301013def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +05301014 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301015
1016 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1017
DaizyModib84deec2023-07-21 17:26:35 +05301018 itemised_tax_data = []
1019 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301020 itemised_tax_data.append(
1021 frappe._dict(
1022 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes}
1023 )
1024 )
DaizyModib84deec2023-07-21 17:26:35 +05301025
1026 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301027
Ankush Menat494bd9e2022-03-28 18:52:46 +05301028
Nabin Hait34c551d2019-07-03 10:34:31 +05301029def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301030 itemised_tax = {}
1031 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301032 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301033 continue
1034
Nabin Haitb962fc12017-07-17 18:02:31 +05301035 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301036 if item_tax_map:
1037 for item_code, tax_data in item_tax_map.items():
1038 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301039
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301040 tax_rate = 0.0
1041 tax_amount = 0.0
1042
Nabin Hait2e4de832017-09-19 14:53:16 +05301043 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301044 tax_rate = flt(tax_data[0])
1045 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301046 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301047 tax_rate = flt(tax_data)
1048
Ankush Menat494bd9e2022-03-28 18:52:46 +05301049 itemised_tax[item_code][tax.description] = frappe._dict(
1050 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1051 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301052
Nabin Hait34c551d2019-07-03 10:34:31 +05301053 if with_tax_account:
1054 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1055
Nabin Haitb962fc12017-07-17 18:02:31 +05301056 return itemised_tax
1057
Ankush Menat494bd9e2022-03-28 18:52:46 +05301058
Nabin Haitb962fc12017-07-17 18:02:31 +05301059def get_itemised_taxable_amount(items):
1060 itemised_taxable_amount = frappe._dict()
1061 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301062 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301063 itemised_taxable_amount.setdefault(item_code, 0)
1064 itemised_taxable_amount[item_code] += item.net_amount
1065
Nabin Haitcaab5822017-08-24 16:22:28 +05301066 return itemised_taxable_amount
1067
Ankush Menat494bd9e2022-03-28 18:52:46 +05301068
DaizyModi6f376cf2023-07-24 18:02:42 +05301069def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301070 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301071 for taxes in itemised_tax:
1072 for row in taxes.values():
1073 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1074 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301075
Ankush Menat494bd9e2022-03-28 18:52:46 +05301076
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301077class init_landed_taxes_and_totals(object):
1078 def __init__(self, doc):
1079 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301080 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301081 self.set_account_currency()
1082 self.set_exchange_rate()
1083 self.set_amounts_in_company_currency()
1084
1085 def set_account_currency(self):
1086 company_currency = erpnext.get_company_currency(self.doc.company)
1087 for d in self.doc.get(self.tax_field):
1088 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301089 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301090 d.account_currency = account_currency or company_currency
1091
1092 def set_exchange_rate(self):
1093 company_currency = erpnext.get_company_currency(self.doc.company)
1094 for d in self.doc.get(self.tax_field):
1095 if d.account_currency == company_currency:
1096 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301097 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301098 d.exchange_rate = get_exchange_rate(
1099 self.doc.posting_date,
1100 account=d.expense_account,
1101 account_currency=d.account_currency,
1102 company=self.doc.company,
1103 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301104
1105 if not d.exchange_rate:
1106 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1107
1108 def set_amounts_in_company_currency(self):
1109 for d in self.doc.get(self.tax_field):
1110 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301111 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))