blob: 95bf0e4688ef630c4603c32eab325699172fd958 [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 = []
marination91982d12023-01-24 18:03:53 +053028
29 self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
30
Deepesh Garg6a5ef262021-02-19 14:30:23 +053031 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053032 self.calculate()
33
marination91982d12023-01-24 18:03:53 +053034 def filter_rows(self):
35 """Exclude rows, that do not fulfill the filter criteria, from totals computation."""
marinationcef7dfd2023-01-26 14:36:25 +053036 items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
marination91982d12023-01-24 18:03:53 +053037 return items
38
Nabin Hait3237c752015-02-17 11:11:11 +053039 def calculate(self):
marination91982d12023-01-24 18:03:53 +053040 if not len(self._items):
Nabin Haitb315acb2019-07-12 14:27:19 +053041 return
42
Nabin Hait3237c752015-02-17 11:11:11 +053043 self.discount_amount_applied = False
44 self._calculate()
45
46 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053047 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053048 self.apply_discount_amount()
49
Deepesh Garg3b159662022-08-21 17:51:05 +053050 # Update grand total as per cash and non trade discount
51 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
52 self.doc.grand_total -= self.doc.discount_amount
53 self.doc.base_grand_total -= self.doc.base_discount_amount
Deepesh Garg318da162022-08-29 14:18:39 +053054 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053055
Deepesh Gargd596e0e2022-03-10 20:56:36 +053056 self.calculate_shipping_charges()
57
Nabin Haitbd00e812015-02-17 12:50:51 +053058 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053059 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053060
Nabin Hait852cb642017-07-05 12:58:19 +053061 if self.doc.meta.get_field("other_charges_calculation"):
62 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053063
64 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053065 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053066 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053067 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053068 self.initialize_taxes()
69 self.determine_exclusive_rate()
70 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053071 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053072 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053073 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053074 self.calculate_totals()
75 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053076 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053077
niralisataparae758a752022-10-03 16:39:35 +053078 def calculate_tax_withholding_net_total(self):
79 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053080 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053081 sum_base_net_amount = 0
marination91982d12023-01-24 18:03:53 +053082 for item in self._items:
niralisataparae758a752022-10-03 16:39:35 +053083 if hasattr(item, "apply_tds") and item.apply_tds:
84 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053085 sum_base_net_amount += item.base_net_amount
86
niralisataparae758a752022-10-03 16:39:35 +053087 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053088 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053089
Deepesh Gargef0d26c2020-01-06 15:34:15 +053090 def validate_item_tax_template(self):
marination91982d12023-01-24 18:03:53 +053091 for item in self._items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053092 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053093 item_doc = frappe.get_cached_doc("Item", item.item_code)
94 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053095 "net_rate": item.net_rate or item.rate,
96 "tax_category": self.doc.get("tax_category"),
97 "posting_date": self.doc.get("posting_date"),
98 "bill_date": self.doc.get("bill_date"),
99 "transaction_date": self.doc.get("transaction_date"),
100 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530101 }
102
103 item_group = item_doc.item_group
104 item_group_taxes = []
105
106 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530107 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530108 item_group_taxes += item_group_doc.taxes or []
109 item_group = item_group_doc.parent_item_group
110
111 item_taxes = item_doc.taxes or []
112
113 if not item_group_taxes and (not item_taxes):
114 # No validation if no taxes in item or item group
115 continue
116
117 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
118
Deepesh Garg18be7672021-06-06 13:25:34 +0530119 if taxes:
120 if item.item_tax_template not in taxes:
121 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530122 frappe.msgprint(
123 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
124 item.idx, frappe.bold(item.item_code)
125 )
126 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530127
Nabin Haite7679702015-02-20 14:40:35 +0530128 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530129 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530130 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530131 if not self.doc.currency or self.doc.currency == company_currency:
132 self.doc.currency = company_currency
133 self.doc.conversion_rate = 1.0
134 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530135 validate_conversion_rate(
136 self.doc.currency,
137 self.doc.conversion_rate,
138 self.doc.meta.get_label("conversion_rate"),
139 self.doc.company,
140 )
Nabin Hait3237c752015-02-17 11:11:11 +0530141
142 self.doc.conversion_rate = flt(self.doc.conversion_rate)
143
Nabin Hait3237c752015-02-17 11:11:11 +0530144 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530145 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530146 return
147
Nabin Hait3237c752015-02-17 11:11:11 +0530148 if not self.discount_amount_applied:
marination91982d12023-01-24 18:03:53 +0530149 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530150 self.doc.round_floats_in(item)
151
152 if item.discount_percentage == 100:
153 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530154 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530155 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530156 item.rate = flt(
157 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
158 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530159
Deepesh Garga83a0a02022-03-25 12:28:55 +0530160 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530161
Deepesh Garg97e102c2022-03-25 12:39:59 +0530162 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530163 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530164
Ankush Menat494bd9e2022-03-28 18:52:46 +0530165 if item.doctype in [
166 "Quotation Item",
167 "Sales Order Item",
168 "Delivery Note Item",
169 "Sales Invoice Item",
170 "POS Invoice Item",
171 "Purchase Invoice Item",
172 "Purchase Order Item",
173 "Purchase Receipt Item",
174 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530175 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530176 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530177 item.rate = flt(
178 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
179 )
Nabin Hait10c61372021-04-13 15:46:01 +0530180
Walstan Baptista37b826b2021-04-03 19:48:46 +0530181 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530182 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530183 else:
184 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530185
Nabin Hait64bfdd92019-04-23 13:37:19 +0530186 elif flt(item.price_list_rate) > 0:
187 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530188 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
189 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530190
Nabin Haite7679702015-02-20 14:40:35 +0530191 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530192
mergify[bot]10c666b2023-10-06 14:43:13 +0530193 if (
194 not item.qty and self.doc.get("is_return") and self.doc.get("doctype") != "Purchase Receipt"
195 ):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530196 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530197 elif not item.qty and self.doc.get("is_debit_note"):
198 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530199 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530200 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530201
Nabin Haite7679702015-02-20 14:40:35 +0530202 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530203
Ankush Menat494bd9e2022-03-28 18:52:46 +0530204 self._set_in_company_currency(
205 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
206 )
Nabin Hait3237c752015-02-17 11:11:11 +0530207
Nabin Haite7679702015-02-20 14:40:35 +0530208 item.item_tax_amount = 0.0
209
210 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530211 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530212 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530213 val = flt(
214 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
215 )
Nabin Haite7679702015-02-20 14:40:35 +0530216 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530217
218 def initialize_taxes(self):
219 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530220 if not self.discount_amount_applied:
221 validate_taxes_and_charges(tax)
222 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530223
Ankush Menat494bd9e2022-03-28 18:52:46 +0530224 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530225 tax.item_wise_tax_detail = {}
226
Ankush Menat494bd9e2022-03-28 18:52:46 +0530227 tax_fields = [
228 "total",
229 "tax_amount_after_discount_amount",
230 "tax_amount_for_current_item",
231 "grand_total_for_current_item",
232 "tax_fraction_for_current_item",
233 "grand_total_fraction_for_current_item",
234 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530235
Ankush Menat494bd9e2022-03-28 18:52:46 +0530236 if tax.charge_type != "Actual" and not (
237 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
238 ):
239 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530240
241 for fieldname in tax_fields:
242 tax.set(fieldname, 0.0)
243
Nabin Hait3237c752015-02-17 11:11:11 +0530244 self.doc.round_floats_in(tax)
245
Nabin Hait3237c752015-02-17 11:11:11 +0530246 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530247 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530248 return
Nabin Hait3237c752015-02-17 11:11:11 +0530249
marination91982d12023-01-24 18:03:53 +0530250 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530251 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
252 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530253 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530254 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530255 (
256 tax.tax_fraction_for_current_item,
257 inclusive_tax_amount_per_qty,
258 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530259
Ankush Menat494bd9e2022-03-28 18:52:46 +0530260 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530261 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
262 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530263 tax.grand_total_fraction_for_current_item = (
264 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530265 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530266 )
Nabin Hait3237c752015-02-17 11:11:11 +0530267
268 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530269 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530270
Ankush Menat494bd9e2022-03-28 18:52:46 +0530271 if (
272 not self.discount_amount_applied
273 and item.qty
274 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
275 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530276 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
277
278 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530279 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530280 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530281
Nabin Haite7679702015-02-20 14:40:35 +0530282 self._set_in_company_currency(item, ["net_rate", "net_amount"])
283
Nabin Hait3237c752015-02-17 11:11:11 +0530284 def _load_item_tax_rate(self, item_tax_rate):
285 return json.loads(item_tax_rate) if item_tax_rate else {}
286
287 def get_current_tax_fraction(self, tax, item_tax_map):
288 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530289 Get tax fraction for calculating tax exclusive amount
290 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530291 """
292 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530293 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530294
295 if cint(tax.included_in_print_rate):
296 tax_rate = self._get_tax_rate(tax, item_tax_map)
297
298 if tax.charge_type == "On Net Total":
299 current_tax_fraction = tax_rate / 100.0
300
301 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530302 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
303 cint(tax.row_id) - 1
304 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530305
306 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530307 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
308 cint(tax.row_id) - 1
309 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530310
Nabin Hait19ea7212020-08-11 20:34:57 +0530311 elif tax.charge_type == "On Item Quantity":
312 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530313
Nabin Hait19ea7212020-08-11 20:34:57 +0530314 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
315 current_tax_fraction *= -1.0
316 inclusive_tax_amount_per_qty *= -1.0
317
318 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530319
320 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530321 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530322 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
323 else:
324 return tax.rate
325
326 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530327 self.doc.total_qty = (
328 self.doc.total
329 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530330
marination91982d12023-01-24 18:03:53 +0530331 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530332 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530333 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530334 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530335 self.doc.net_total += item.net_amount
336 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530337
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530338 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530339
Subin Toma8e2c022021-11-16 19:06:49 +0530340 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530341
342 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530343 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530344 return
345
Subin Tomaf1fce02021-11-10 16:49:12 +0530346 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530347 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
348 shipping_rule.apply(self.doc)
349
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530350 self._calculate()
351
Nabin Hait3237c752015-02-17 11:11:11 +0530352 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530353 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
354 "rounding_adjustment"
355 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530356 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530357 self.doc.rounding_adjustment = 0
358
Nabin Hait3237c752015-02-17 11:11:11 +0530359 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530360 actual_tax_dict = dict(
361 [
362 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
363 for tax in self.doc.get("taxes")
364 if tax.charge_type == "Actual"
365 ]
366 )
Nabin Hait3237c752015-02-17 11:11:11 +0530367
marination91982d12023-01-24 18:03:53 +0530368 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530369 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530370 for i, tax in enumerate(self.doc.get("taxes")):
371 # tax_amount represents the amount of tax for the current step
372 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
373
374 # Adjust divisional loss to the last item
375 if tax.charge_type == "Actual":
376 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530377 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530378 current_tax_amount += actual_tax_dict[tax.idx]
379
Nabin Hait2b019ed2015-02-22 23:03:07 +0530380 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530381 if tax.charge_type != "Actual" and not (
382 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
383 ):
384 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530385
Nabin Hait3237c752015-02-17 11:11:11 +0530386 # store tax_amount for current item as it will be used for
387 # charge type = 'On Previous Row Amount'
388 tax.tax_amount_for_current_item = current_tax_amount
389
Nabin Hait2b019ed2015-02-22 23:03:07 +0530390 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530391 tax.tax_amount_after_discount_amount += current_tax_amount
392
Nabin Haitcd951342017-07-31 18:07:45 +0530393 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530394
Nabin Hait3237c752015-02-17 11:11:11 +0530395 # note: grand_total_for_current_item contains the contribution of
396 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530397 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530398 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530399 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530400 tax.grand_total_for_current_item = flt(
401 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
402 )
Nabin Hait3237c752015-02-17 11:11:11 +0530403
404 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530405 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530406 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530407 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530408
409 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530410 self.set_cumulative_total(i, tax)
411
Deepesh Gargb6705882021-04-14 11:20:27 +0530412 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530413
Nabin Hait3237c752015-02-17 11:11:11 +0530414 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530415 if (
416 i == (len(self.doc.get("taxes")) - 1)
417 and self.discount_amount_applied
418 and self.doc.discount_amount
419 and self.doc.apply_discount_on == "Grand Total"
420 and not rounding_adjustment_computed
421 ):
422 self.doc.rounding_adjustment = flt(
423 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
424 self.doc.precision("rounding_adjustment"),
425 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530426
Nabin Haitcd951342017-07-31 18:07:45 +0530427 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
428 # if just for valuation, do not add the tax amount in total
429 # if tax/charges is for deduction, multiply by -1
430 if getattr(tax, "category", None):
431 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530432 if self.doc.doctype in [
433 "Purchase Order",
434 "Purchase Invoice",
435 "Purchase Receipt",
436 "Supplier Quotation",
437 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530438 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530439 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530440
Nabin Haitcd951342017-07-31 18:07:45 +0530441 def set_cumulative_total(self, row_idx, tax):
442 tax_amount = tax.tax_amount_after_discount_amount
443 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
444
445 if row_idx == 0:
446 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
447 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530448 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530449
450 def get_current_tax_amount(self, item, tax, item_tax_map):
451 tax_rate = self._get_tax_rate(tax, item_tax_map)
452 current_tax_amount = 0.0
453
454 if tax.charge_type == "Actual":
455 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530456 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530457 current_tax_amount = (
458 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
459 )
Nabin Haite7679702015-02-20 14:40:35 +0530460
Nabin Hait3237c752015-02-17 11:11:11 +0530461 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530462 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530463 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530464 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
465 cint(tax.row_id) - 1
466 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530467 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530468 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
469 cint(tax.row_id) - 1
470 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530471 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530472 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530473
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530474 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530475 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530476
477 return current_tax_amount
478
Nabin Haite7679702015-02-20 14:40:35 +0530479 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
480 # store tax breakup for each item
481 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530482 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Nabin Haite7679702015-02-20 14:40:35 +0530483 if tax.item_wise_tax_detail.get(key):
484 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
485
Ankush Menat494bd9e2022-03-28 18:52:46 +0530486 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530487
Nabin Hait3237c752015-02-17 11:11:11 +0530488 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530489 if tax.account_head in frappe.flags.round_off_applicable_accounts:
490 tax.tax_amount = round(tax.tax_amount, 0)
491 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
492
Nabin Haite7679702015-02-20 14:40:35 +0530493 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530494 tax.tax_amount_after_discount_amount = flt(
495 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
496 )
Nabin Haitce245122015-02-22 20:14:49 +0530497
Deepesh Gargb6705882021-04-14 11:20:27 +0530498 def round_off_base_values(self, tax):
499 # Round off to nearest integer based on regional settings
500 if tax.account_head in frappe.flags.round_off_applicable_accounts:
501 tax.base_tax_amount = round(tax.base_tax_amount, 0)
502 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
503
Nabin Haita1bf43b2015-03-17 10:50:47 +0530504 def manipulate_grand_total_for_inclusive_tax(self):
505 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530506 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 +0530507 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530508 non_inclusive_tax_amount = sum(
509 flt(d.tax_amount_after_discount_amount)
510 for d in self.doc.get("taxes")
511 if not d.included_in_print_rate
512 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530513
Ankush Menat494bd9e2022-03-28 18:52:46 +0530514 diff = (
515 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
516 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530517
518 # If discount amount applied, deduct the discount amount
519 # because self.doc.total is always without discount, but last_tax.total is after discount
520 if self.discount_amount_applied and self.doc.discount_amount:
521 diff -= flt(self.doc.discount_amount)
522
523 diff = flt(diff, self.doc.precision("rounding_adjustment"))
524
Ankush Menat494bd9e2022-03-28 18:52:46 +0530525 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530526 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530527
528 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530529 if self.doc.get("taxes"):
530 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
531 else:
532 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530533
Subin Tom75a76e62021-10-29 16:45:04 +0530534 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530535 self.doc.total_taxes_and_charges = flt(
536 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
537 self.doc.precision("total_taxes_and_charges"),
538 )
Subin Tom75a76e62021-10-29 16:45:04 +0530539 else:
540 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530541
Nabin Hait2e4de832017-09-19 14:53:16 +0530542 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530543
Ankush Menat494bd9e2022-03-28 18:52:46 +0530544 if self.doc.doctype in [
545 "Quotation",
546 "Sales Order",
547 "Delivery Note",
548 "Sales Invoice",
549 "POS Invoice",
550 ]:
551 self.doc.base_grand_total = (
552 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
553 if self.doc.total_taxes_and_charges
554 else self.doc.base_net_total
555 )
Nabin Hait3237c752015-02-17 11:11:11 +0530556 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530557 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530558 for tax in self.doc.get("taxes"):
559 if tax.category in ["Valuation and Total", "Total"]:
560 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530561 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530562 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530563 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530564
Nabin Haite7679702015-02-20 14:40:35 +0530565 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530566
Ankush Menat494bd9e2022-03-28 18:52:46 +0530567 self.doc.base_grand_total = (
568 flt(self.doc.grand_total * self.doc.conversion_rate)
569 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530570 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530571 )
Nabin Hait3237c752015-02-17 11:11:11 +0530572
Ankush Menat494bd9e2022-03-28 18:52:46 +0530573 self._set_in_company_currency(
574 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
575 )
Nabin Hait3237c752015-02-17 11:11:11 +0530576
Nabin Haite7679702015-02-20 14:40:35 +0530577 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530578
Nabin Hait2e4de832017-09-19 14:53:16 +0530579 self.set_rounded_total()
580
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530581 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530582 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530583 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530584 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530585 if d.total_weight:
586 self.doc.total_net_weight += d.total_weight
587
Nabin Hait2e4de832017-09-19 14:53:16 +0530588 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530589 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530590 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530591
Saqib Ansari17445c72022-03-07 18:01:07 +0530592 if self.doc.meta.get_field("rounded_total"):
593 if self.doc.is_rounded_total_disabled():
594 self.doc.rounded_total = self.doc.base_rounded_total = 0
595 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530596
Ankush Menat494bd9e2022-03-28 18:52:46 +0530597 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
598 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
599 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530600
Ankush Menat494bd9e2022-03-28 18:52:46 +0530601 # if print_in_rate is set, we would have already calculated rounding adjustment
602 self.doc.rounding_adjustment += flt(
603 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
604 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530605
606 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530607
Nabin Hait3237c752015-02-17 11:11:11 +0530608 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530609 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530610 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530611 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530612 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530613
Nabin Hait3769d872015-12-18 13:12:02 +0530614 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530615 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530616 self.doc.discount_amount = flt(
617 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
618 * self.doc.additional_discount_percentage
619 / 100,
620 self.doc.precision("discount_amount"),
621 )
Nabin Hait3237c752015-02-17 11:11:11 +0530622
623 def apply_discount_amount(self):
624 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530625 if not self.doc.apply_discount_on:
626 frappe.throw(_("Please select Apply Discount On"))
627
Deepesh Garg3b159662022-08-21 17:51:05 +0530628 self.doc.base_discount_amount = flt(
629 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
630 )
631
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530632 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
633 "is_cash_or_non_trade_discount"
634 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530635 self.discount_amount_applied = True
636 return
637
Nabin Haite7679702015-02-20 14:40:35 +0530638 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530639 taxes = self.doc.get("taxes")
640 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530641
Nabin Haite7679702015-02-20 14:40:35 +0530642 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530643 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530644 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530645 distributed_amount = (
646 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
647 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530648
Nabin Haite7679702015-02-20 14:40:35 +0530649 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530650 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530651
Nabin Hait25bd84d2015-03-04 15:06:56 +0530652 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530653 if (
654 self.doc.apply_discount_on == "Net Total"
655 or not taxes
656 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530657 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530658 discount_amount_loss = flt(
659 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
660 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530661
Ankush Menat494bd9e2022-03-28 18:52:46 +0530662 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530663
Nabin Hait51e980d2015-10-10 18:10:05 +0530664 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 +0530665
Nabin Haite7679702015-02-20 14:40:35 +0530666 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530667
668 self.discount_amount_applied = True
669 self._calculate()
670 else:
671 self.doc.base_discount_amount = 0
672
Nabin Haite7679702015-02-20 14:40:35 +0530673 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530674 if self.doc.apply_discount_on == "Net Total":
675 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530676 else:
677 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530678
Nabin Haite7679702015-02-20 14:40:35 +0530679 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530680 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530681 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
682 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530683 elif tax.row_id in actual_taxes_dict:
684 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
685 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530686
Ankush Menat494bd9e2022-03-28 18:52:46 +0530687 return flt(
688 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
689 )
Nabin Hait3237c752015-02-17 11:11:11 +0530690
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530691 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100692 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530693 total_allocated_amount = sum(
694 flt(adv.allocated_amount, adv.precision("allocated_amount"))
695 for adv in self.doc.get("advances")
696 )
Nabin Hait3237c752015-02-17 11:11:11 +0530697
Nabin Haite7679702015-02-20 14:40:35 +0530698 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530699
Faris Ansari6041f5c2018-02-08 13:33:52 +0530700 grand_total = self.doc.rounded_total or self.doc.grand_total
701
Nabin Hait289ffb72016-02-08 11:06:55 +0530702 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530703 invoice_total = flt(
704 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
705 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530706 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530707 base_write_off_amount = flt(
708 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
709 self.doc.precision("base_write_off_amount"),
710 )
711 invoice_total = (
712 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
713 - base_write_off_amount
714 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530715
Nabin Haitadc09232016-02-09 10:31:11 +0530716 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530717 frappe.throw(
718 _("Advance amount cannot be greater than {0} {1}").format(
719 self.doc.party_account_currency, invoice_total
720 )
721 )
Nabin Hait3237c752015-02-17 11:11:11 +0530722
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100723 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530724 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530725 self.doc.write_off_amount = 0
726
Nabin Hait3237c752015-02-17 11:11:11 +0530727 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530728 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530729
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530730 def is_internal_invoice(self):
731 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530732 Checks if its an internal transfer invoice
733 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530734 """
735
Ankush Menat494bd9e2022-03-28 18:52:46 +0530736 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530737 return True
738
739 return False
740
Nabin Hait3237c752015-02-17 11:11:11 +0530741 def calculate_outstanding_amount(self):
742 # NOTE:
743 # write_off_amount is only for POS Invoice
744 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530745 if self.doc.doctype == "Sales Invoice":
746 self.calculate_paid_amount()
747
Ankush Menat494bd9e2022-03-28 18:52:46 +0530748 if (
749 self.doc.is_return
750 and self.doc.return_against
751 and not self.doc.get("is_pos")
752 or self.is_internal_invoice()
753 ):
754 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530755
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530756 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530757 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530758
Nabin Hait877e1bb2017-11-17 12:27:43 +0530759 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
760 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530761 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
762
Nabin Hait877e1bb2017-11-17 12:27:43 +0530763 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530764 total_amount_to_pay = flt(
765 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
766 self.doc.precision("grand_total"),
767 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530768 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530769 total_amount_to_pay = flt(
770 flt(base_grand_total, self.doc.precision("base_grand_total"))
771 - self.doc.total_advance
772 - flt(self.doc.base_write_off_amount),
773 self.doc.precision("base_grand_total"),
774 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530775
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530776 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530777 change_amount = 0
778
Ankush Menat494bd9e2022-03-28 18:52:46 +0530779 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530780 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530781 change_amount = (
782 self.doc.change_amount
783 if self.doc.party_account_currency == self.doc.currency
784 else self.doc.base_change_amount
785 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530786
Ankush Menat494bd9e2022-03-28 18:52:46 +0530787 paid_amount = (
788 self.doc.paid_amount
789 if self.doc.party_account_currency == self.doc.currency
790 else self.doc.base_paid_amount
791 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530792
Ankush Menat494bd9e2022-03-28 18:52:46 +0530793 self.doc.outstanding_amount = flt(
794 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
795 self.doc.precision("outstanding_amount"),
796 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530797
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530798 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530799 self.doc.doctype == "Sales Invoice"
800 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530801 and self.doc.get("pos_profile")
802 and self.doc.get("is_consolidated")
803 ):
804 write_off_limit = flt(
805 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
806 )
807 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
808 self.doc.write_off_outstanding_amount_automatically = 1
809
810 if (
811 self.doc.doctype == "Sales Invoice"
812 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530813 and self.doc.get("is_return")
814 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530815 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530816 self.set_total_amount_to_default_mop(total_amount_to_pay)
817 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530818
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530819 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530820
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530821 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530822
823 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530824 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530825 payment.amount = flt(payment.amount)
826 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530827 paid_amount += payment.amount
828 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530829 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530830 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530831
Manas Solankida486ee2018-07-06 12:36:57 +0530832 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
833 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530834 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530835
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530836 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
837 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
838
Nabin Hait3bb1a422016-08-02 16:41:10 +0530839 def calculate_change_amount(self):
840 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530841 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530842 grand_total = self.doc.rounded_total or self.doc.grand_total
843 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530844
Ankush Menat494bd9e2022-03-28 18:52:46 +0530845 if (
846 self.doc.doctype == "Sales Invoice"
847 and self.doc.paid_amount > grand_total
848 and not self.doc.is_return
849 and any(d.type == "Cash" for d in self.doc.payments)
850 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530851 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530852 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530853 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530854
Ankush Menat494bd9e2022-03-28 18:52:46 +0530855 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530856 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530857 )
mbauskar36b51892016-01-18 16:31:10 +0530858
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530859 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530860 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530861 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530862 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530863 )
864 self.doc.base_write_off_amount = flt(
865 self.doc.write_off_amount * self.doc.conversion_rate,
866 self.doc.precision("base_write_off_amount"),
867 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530868
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530869 self.calculate_outstanding_amount()
870
mbauskar36b51892016-01-18 16:31:10 +0530871 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530872 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530873 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530874 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530875 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530876 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530877 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530878 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530879
Ankush Menat494bd9e2022-03-28 18:52:46 +0530880 if pricing_rule.margin_rate_or_amount and (
881 (
882 pricing_rule.currency == self.doc.currency
883 and pricing_rule.margin_type in ["Amount", "Percentage"]
884 )
885 or pricing_rule.margin_type == "Percentage"
886 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530887 item.margin_type = pricing_rule.margin_type
888 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530889 has_margin = True
890
891 if not has_margin:
892 item.margin_type = None
893 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530894
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530895 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
896 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530897 item.margin_rate_or_amount = flt(
898 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
899 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530900 item.rate_with_margin = item.rate
901
902 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530903 margin_value = (
904 item.margin_rate_or_amount
905 if item.margin_type == "Amount"
906 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
907 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530908 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530909 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530910
Shreya Shahbe690ef2017-11-14 17:22:41 +0530911 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530912
913 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530914 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530915
Subin Tom7d627df2021-08-23 11:05:07 +0530916 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530917 total_paid_amount = 0
918 for payment in self.doc.get("payments"):
919 total_paid_amount += (
920 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530921 )
922
Deepesh Garg06e8e282022-10-31 19:58:46 +0530923 pending_amount = total_amount_to_pay - total_paid_amount
924
925 if pending_amount > 0:
926 default_mode_of_payment = frappe.db.get_value(
927 "POS Payment Method",
928 {"parent": self.doc.pos_profile, "default": 1},
929 ["mode_of_payment"],
930 as_dict=1,
931 )
932
933 if default_mode_of_payment:
934 self.doc.payments = []
935 self.doc.append(
936 "payments",
937 {
938 "mode_of_payment": default_mode_of_payment.mode_of_payment,
939 "amount": pending_amount,
940 "default": 1,
941 },
942 )
943
Deepesh Garg0ebace52020-02-25 13:21:16 +0530944
Nabin Hait9c421612017-07-20 13:32:01 +0530945def get_itemised_tax_breakup_html(doc):
946 if not doc.taxes:
947 return
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530948
Nabin Hait9c421612017-07-20 13:32:01 +0530949 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530950 tax_accounts = []
951 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530952 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530953 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530954 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530955 tax_accounts.append(tax.description)
956
Sagar Vora4205f562023-07-24 18:37:36 +0530957 with temporary_flag("company", doc.company):
958 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Smit Vora1b8490d2023-07-24 20:20:19 +0530959 itemised_tax_data = get_itemised_tax_breakup_data(doc)
960 get_rounded_tax_amount(itemised_tax_data, doc.precision("tax_amount", "taxes"))
Sagar Vora4205f562023-07-24 18:37:36 +0530961 update_itemised_tax_data(doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530962
Nabin Hait9c421612017-07-20 13:32:01 +0530963 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530964 "templates/includes/itemised_tax_breakup.html",
965 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530966 headers=headers,
DaizyModib84deec2023-07-21 17:26:35 +0530967 itemised_tax_data=itemised_tax_data,
Nabin Hait9c421612017-07-20 13:32:01 +0530968 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530969 doc=doc,
970 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530971 )
Nabin Hait852cb642017-07-05 12:58:19 +0530972
Ankush Menat494bd9e2022-03-28 18:52:46 +0530973
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530974@frappe.whitelist()
975def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +0530976 # required to set correct region
Sagar Vora4205f562023-07-24 18:37:36 +0530977 with temporary_flag("company", company):
978 return get_regional_round_off_accounts(company, account_list)
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530979
Ankush Menat494bd9e2022-03-28 18:52:46 +0530980
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530981@erpnext.allow_regional
982def get_regional_round_off_accounts(company, account_list):
983 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530984
Ankush Menat494bd9e2022-03-28 18:52:46 +0530985
rohitwaghchaured4526682017-12-28 14:20:13 +0530986@erpnext.allow_regional
987def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530988 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +0530989 pass
990
Ankush Menat494bd9e2022-03-28 18:52:46 +0530991
Nabin Haitb962fc12017-07-17 18:02:31 +0530992@erpnext.allow_regional
993def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
994 return [_("Item"), _("Taxable Amount")] + tax_accounts
995
Ankush Menat494bd9e2022-03-28 18:52:46 +0530996
Nabin Haitb962fc12017-07-17 18:02:31 +0530997@erpnext.allow_regional
DaizyModi653117c2023-07-21 17:52:54 +0530998def get_itemised_tax_breakup_data(doc):
DaizyModi6f376cf2023-07-24 18:02:42 +0530999 itemised_tax = get_itemised_tax(doc.taxes)
Nabin Haitb962fc12017-07-17 18:02:31 +05301000
1001 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1002
DaizyModib84deec2023-07-21 17:26:35 +05301003 itemised_tax_data = []
1004 for item_code, taxes in itemised_tax.items():
DaizyModi6f376cf2023-07-24 18:02:42 +05301005 itemised_tax_data.append(
1006 frappe._dict(
1007 {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes}
1008 )
1009 )
DaizyModib84deec2023-07-21 17:26:35 +05301010
1011 return itemised_tax_data
Nabin Haitb962fc12017-07-17 18:02:31 +05301012
Ankush Menat494bd9e2022-03-28 18:52:46 +05301013
Nabin Hait34c551d2019-07-03 10:34:31 +05301014def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301015 itemised_tax = {}
1016 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301017 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301018 continue
1019
Nabin Haitb962fc12017-07-17 18:02:31 +05301020 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301021 if item_tax_map:
1022 for item_code, tax_data in item_tax_map.items():
1023 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301024
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301025 tax_rate = 0.0
1026 tax_amount = 0.0
1027
Nabin Hait2e4de832017-09-19 14:53:16 +05301028 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301029 tax_rate = flt(tax_data[0])
1030 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301031 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301032 tax_rate = flt(tax_data)
1033
Ankush Menat494bd9e2022-03-28 18:52:46 +05301034 itemised_tax[item_code][tax.description] = frappe._dict(
1035 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1036 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301037
Nabin Hait34c551d2019-07-03 10:34:31 +05301038 if with_tax_account:
1039 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1040
Nabin Haitb962fc12017-07-17 18:02:31 +05301041 return itemised_tax
1042
Ankush Menat494bd9e2022-03-28 18:52:46 +05301043
Nabin Haitb962fc12017-07-17 18:02:31 +05301044def get_itemised_taxable_amount(items):
1045 itemised_taxable_amount = frappe._dict()
1046 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301047 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301048 itemised_taxable_amount.setdefault(item_code, 0)
1049 itemised_taxable_amount[item_code] += item.net_amount
1050
Nabin Haitcaab5822017-08-24 16:22:28 +05301051 return itemised_taxable_amount
1052
Ankush Menat494bd9e2022-03-28 18:52:46 +05301053
DaizyModi6f376cf2023-07-24 18:02:42 +05301054def get_rounded_tax_amount(itemised_tax, precision):
Nabin Haitcaab5822017-08-24 16:22:28 +05301055 # Rounding based on tax_amount precision
DaizyModi6f376cf2023-07-24 18:02:42 +05301056 for taxes in itemised_tax:
1057 for row in taxes.values():
1058 if isinstance(row, dict) and isinstance(row["tax_amount"], float):
1059 row["tax_amount"] = flt(row["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301060
Ankush Menat494bd9e2022-03-28 18:52:46 +05301061
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301062class init_landed_taxes_and_totals(object):
1063 def __init__(self, doc):
1064 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301065 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301066 self.set_account_currency()
1067 self.set_exchange_rate()
1068 self.set_amounts_in_company_currency()
1069
1070 def set_account_currency(self):
1071 company_currency = erpnext.get_company_currency(self.doc.company)
1072 for d in self.doc.get(self.tax_field):
1073 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301074 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301075 d.account_currency = account_currency or company_currency
1076
1077 def set_exchange_rate(self):
1078 company_currency = erpnext.get_company_currency(self.doc.company)
1079 for d in self.doc.get(self.tax_field):
1080 if d.account_currency == company_currency:
1081 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301082 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301083 d.exchange_rate = get_exchange_rate(
1084 self.doc.posting_date,
1085 account=d.expense_account,
1086 account_currency=d.account_currency,
1087 company=self.doc.company,
1088 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301089
1090 if not d.exchange_rate:
1091 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1092
1093 def set_amounts_in_company_currency(self):
1094 for d in self.doc.get(self.tax_field):
1095 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301096 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))