blob: 4661c5ca7e8fc40c4e371b0f953328bb12158156 [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
21
Nabin Hait3237c752015-02-17 11:11:11 +053022
Nabin Haitfe81da22015-02-18 12:23:18 +053023class calculate_taxes_and_totals(object):
Raffael Meyer67cf7e12023-01-15 13:04:16 +010024 def __init__(self, doc: Document):
Nabin Hait3237c752015-02-17 11:11:11 +053025 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053026 frappe.flags.round_off_applicable_accounts = []
marination91982d12023-01-24 18:03:53 +053027
28 self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
29
Deepesh Garg6a5ef262021-02-19 14:30:23 +053030 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053031 self.calculate()
32
marination91982d12023-01-24 18:03:53 +053033 def filter_rows(self):
34 """Exclude rows, that do not fulfill the filter criteria, from totals computation."""
marinationcef7dfd2023-01-26 14:36:25 +053035 items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
marination91982d12023-01-24 18:03:53 +053036 return items
37
Nabin Hait3237c752015-02-17 11:11:11 +053038 def calculate(self):
marination91982d12023-01-24 18:03:53 +053039 if not len(self._items):
Nabin Haitb315acb2019-07-12 14:27:19 +053040 return
41
Nabin Hait3237c752015-02-17 11:11:11 +053042 self.discount_amount_applied = False
43 self._calculate()
44
45 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053046 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053047 self.apply_discount_amount()
48
Deepesh Garg3b159662022-08-21 17:51:05 +053049 # Update grand total as per cash and non trade discount
50 if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
51 self.doc.grand_total -= self.doc.discount_amount
52 self.doc.base_grand_total -= self.doc.base_discount_amount
Deepesh Garg318da162022-08-29 14:18:39 +053053 self.set_rounded_total()
Deepesh Garg3b159662022-08-21 17:51:05 +053054
Deepesh Gargd596e0e2022-03-10 20:56:36 +053055 self.calculate_shipping_charges()
56
Nabin Haitbd00e812015-02-17 12:50:51 +053057 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053058 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053059
Nabin Hait852cb642017-07-05 12:58:19 +053060 if self.doc.meta.get_field("other_charges_calculation"):
61 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053062
63 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053064 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053065 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053066 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053067 self.initialize_taxes()
68 self.determine_exclusive_rate()
69 self.calculate_net_total()
niralisataparae758a752022-10-03 16:39:35 +053070 self.calculate_tax_withholding_net_total()
Nabin Haite7679702015-02-20 14:40:35 +053071 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053072 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053073 self.calculate_totals()
74 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053075 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053076
niralisataparae758a752022-10-03 16:39:35 +053077 def calculate_tax_withholding_net_total(self):
78 if hasattr(self.doc, "tax_withholding_net_total"):
niralisataparae758a752022-10-03 16:39:35 +053079 sum_net_amount = 0
niralisatapara2ca0cf62022-11-02 12:19:51 +053080 sum_base_net_amount = 0
marination91982d12023-01-24 18:03:53 +053081 for item in self._items:
niralisataparae758a752022-10-03 16:39:35 +053082 if hasattr(item, "apply_tds") and item.apply_tds:
83 sum_net_amount += item.net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053084 sum_base_net_amount += item.base_net_amount
85
niralisataparae758a752022-10-03 16:39:35 +053086 self.doc.tax_withholding_net_total = sum_net_amount
niralisatapara2ca0cf62022-11-02 12:19:51 +053087 self.doc.base_tax_withholding_net_total = sum_base_net_amount
niralisataparae758a752022-10-03 16:39:35 +053088
Deepesh Gargef0d26c2020-01-06 15:34:15 +053089 def validate_item_tax_template(self):
marination91982d12023-01-24 18:03:53 +053090 for item in self._items:
Ankush Menat494bd9e2022-03-28 18:52:46 +053091 if item.item_code and item.get("item_tax_template"):
Deepesh Gargef0d26c2020-01-06 15:34:15 +053092 item_doc = frappe.get_cached_doc("Item", item.item_code)
93 args = {
Ankush Menat494bd9e2022-03-28 18:52:46 +053094 "net_rate": item.net_rate or item.rate,
95 "tax_category": self.doc.get("tax_category"),
96 "posting_date": self.doc.get("posting_date"),
97 "bill_date": self.doc.get("bill_date"),
98 "transaction_date": self.doc.get("transaction_date"),
99 "company": self.doc.get("company"),
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530100 }
101
102 item_group = item_doc.item_group
103 item_group_taxes = []
104
105 while item_group:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530106 item_group_doc = frappe.get_cached_doc("Item Group", item_group)
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530107 item_group_taxes += item_group_doc.taxes or []
108 item_group = item_group_doc.parent_item_group
109
110 item_taxes = item_doc.taxes or []
111
112 if not item_group_taxes and (not item_taxes):
113 # No validation if no taxes in item or item group
114 continue
115
116 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
117
Deepesh Garg18be7672021-06-06 13:25:34 +0530118 if taxes:
119 if item.item_tax_template not in taxes:
120 item.item_tax_template = taxes[0]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530121 frappe.msgprint(
122 _("Row {0}: Item Tax template updated as per validity and rate applied").format(
123 item.idx, frappe.bold(item.item_code)
124 )
125 )
Deepesh Gargef0d26c2020-01-06 15:34:15 +0530126
Nabin Haite7679702015-02-20 14:40:35 +0530127 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530128 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +0530129 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +0530130 if not self.doc.currency or self.doc.currency == company_currency:
131 self.doc.currency = company_currency
132 self.doc.conversion_rate = 1.0
133 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530134 validate_conversion_rate(
135 self.doc.currency,
136 self.doc.conversion_rate,
137 self.doc.meta.get_label("conversion_rate"),
138 self.doc.company,
139 )
Nabin Hait3237c752015-02-17 11:11:11 +0530140
141 self.doc.conversion_rate = flt(self.doc.conversion_rate)
142
Nabin Hait3237c752015-02-17 11:11:11 +0530143 def calculate_item_values(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530144 if self.doc.get("is_consolidated"):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530145 return
146
Nabin Hait3237c752015-02-17 11:11:11 +0530147 if not self.discount_amount_applied:
marination91982d12023-01-24 18:03:53 +0530148 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530149 self.doc.round_floats_in(item)
150
151 if item.discount_percentage == 100:
152 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530153 elif item.price_list_rate:
Deepesh Garga83a0a02022-03-25 12:28:55 +0530154 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530155 item.rate = flt(
156 item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
157 )
Deepesh Gargd95f8932022-03-01 23:09:59 +0530158
Deepesh Garga83a0a02022-03-25 12:28:55 +0530159 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
Deepesh Gargd95f8932022-03-01 23:09:59 +0530160
Deepesh Garg97e102c2022-03-25 12:39:59 +0530161 elif item.discount_amount and item.pricing_rules:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530162 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530163
Ankush Menat494bd9e2022-03-28 18:52:46 +0530164 if item.doctype in [
165 "Quotation Item",
166 "Sales Order Item",
167 "Delivery Note Item",
168 "Sales Invoice Item",
169 "POS Invoice Item",
170 "Purchase Invoice Item",
171 "Purchase Order Item",
172 "Purchase Receipt Item",
173 ]:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530174 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530175 if flt(item.rate_with_margin) > 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530176 item.rate = flt(
177 item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")
178 )
Nabin Hait10c61372021-04-13 15:46:01 +0530179
Walstan Baptista37b826b2021-04-03 19:48:46 +0530180 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530181 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530182 else:
183 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530184
Nabin Hait64bfdd92019-04-23 13:37:19 +0530185 elif flt(item.price_list_rate) > 0:
186 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530187 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
188 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530189
Nabin Haite7679702015-02-20 14:40:35 +0530190 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530191
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530192 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530193 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530194 elif not item.qty and self.doc.get("is_debit_note"):
195 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530196 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530197 item.amount = flt(item.rate * item.qty, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530198
Nabin Haite7679702015-02-20 14:40:35 +0530199 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530200
Ankush Menat494bd9e2022-03-28 18:52:46 +0530201 self._set_in_company_currency(
202 item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]
203 )
Nabin Hait3237c752015-02-17 11:11:11 +0530204
Nabin Haite7679702015-02-20 14:40:35 +0530205 item.item_tax_amount = 0.0
206
207 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530208 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530209 for f in fields:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530210 val = flt(
211 flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f)
212 )
Nabin Haite7679702015-02-20 14:40:35 +0530213 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530214
215 def initialize_taxes(self):
216 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530217 if not self.discount_amount_applied:
218 validate_taxes_and_charges(tax)
219 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530220
Ankush Menat494bd9e2022-03-28 18:52:46 +0530221 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530222 tax.item_wise_tax_detail = {}
223
Ankush Menat494bd9e2022-03-28 18:52:46 +0530224 tax_fields = [
225 "total",
226 "tax_amount_after_discount_amount",
227 "tax_amount_for_current_item",
228 "grand_total_for_current_item",
229 "tax_fraction_for_current_item",
230 "grand_total_fraction_for_current_item",
231 ]
Nabin Hait3237c752015-02-17 11:11:11 +0530232
Ankush Menat494bd9e2022-03-28 18:52:46 +0530233 if tax.charge_type != "Actual" and not (
234 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
235 ):
236 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530237
238 for fieldname in tax_fields:
239 tax.set(fieldname, 0.0)
240
Nabin Hait3237c752015-02-17 11:11:11 +0530241 self.doc.round_floats_in(tax)
242
Nabin Hait3237c752015-02-17 11:11:11 +0530243 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530244 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530245 return
Nabin Hait3237c752015-02-17 11:11:11 +0530246
marination91982d12023-01-24 18:03:53 +0530247 for item in self._items:
Nabin Hait3237c752015-02-17 11:11:11 +0530248 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
249 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530250 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530251 for i, tax in enumerate(self.doc.get("taxes")):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530252 (
253 tax.tax_fraction_for_current_item,
254 inclusive_tax_amount_per_qty,
255 ) = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530256
Ankush Menat494bd9e2022-03-28 18:52:46 +0530257 if i == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530258 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
259 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530260 tax.grand_total_fraction_for_current_item = (
261 self.doc.get("taxes")[i - 1].grand_total_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530262 + tax.tax_fraction_for_current_item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530263 )
Nabin Hait3237c752015-02-17 11:11:11 +0530264
265 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530266 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530267
Ankush Menat494bd9e2022-03-28 18:52:46 +0530268 if (
269 not self.discount_amount_applied
270 and item.qty
271 and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty)
272 ):
Nabin Hait19ea7212020-08-11 20:34:57 +0530273 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
274
275 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530276 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530277 item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530278
Nabin Haite7679702015-02-20 14:40:35 +0530279 self._set_in_company_currency(item, ["net_rate", "net_amount"])
280
Nabin Hait3237c752015-02-17 11:11:11 +0530281 def _load_item_tax_rate(self, item_tax_rate):
282 return json.loads(item_tax_rate) if item_tax_rate else {}
283
284 def get_current_tax_fraction(self, tax, item_tax_map):
285 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530286 Get tax fraction for calculating tax exclusive amount
287 from tax inclusive amount
Nabin Hait3237c752015-02-17 11:11:11 +0530288 """
289 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530290 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530291
292 if cint(tax.included_in_print_rate):
293 tax_rate = self._get_tax_rate(tax, item_tax_map)
294
295 if tax.charge_type == "On Net Total":
296 current_tax_fraction = tax_rate / 100.0
297
298 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530299 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
300 cint(tax.row_id) - 1
301 ].tax_fraction_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530302
303 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530304 current_tax_fraction = (tax_rate / 100.0) * self.doc.get("taxes")[
305 cint(tax.row_id) - 1
306 ].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530307
Nabin Hait19ea7212020-08-11 20:34:57 +0530308 elif tax.charge_type == "On Item Quantity":
309 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530310
Nabin Hait19ea7212020-08-11 20:34:57 +0530311 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
312 current_tax_fraction *= -1.0
313 inclusive_tax_amount_per_qty *= -1.0
314
315 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530316
317 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530318 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530319 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
320 else:
321 return tax.rate
322
323 def calculate_net_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530324 self.doc.total_qty = (
325 self.doc.total
326 ) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530327
marination91982d12023-01-24 18:03:53 +0530328 for item in self._items:
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530329 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530330 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530331 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530332 self.doc.net_total += item.net_amount
333 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530334
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530335 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530336
Subin Toma8e2c022021-11-16 19:06:49 +0530337 def calculate_shipping_charges(self):
Deepesh Garg714fc082022-04-04 20:05:10 +0530338
339 # Do not apply shipping rule for POS
Deepesh Garg631545a2022-04-06 09:27:40 +0530340 if self.doc.get("is_pos"):
Deepesh Garg714fc082022-04-04 20:05:10 +0530341 return
342
Subin Tomaf1fce02021-11-10 16:49:12 +0530343 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530344 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
345 shipping_rule.apply(self.doc)
346
Deepesh Gargd596e0e2022-03-10 20:56:36 +0530347 self._calculate()
348
Nabin Hait3237c752015-02-17 11:11:11 +0530349 def calculate_taxes(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530350 rounding_adjustment_computed = self.doc.get("is_consolidated") and self.doc.get(
351 "rounding_adjustment"
352 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530353 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530354 self.doc.rounding_adjustment = 0
355
Nabin Hait3237c752015-02-17 11:11:11 +0530356 # maintain actual tax rate based on idx
Ankush Menat494bd9e2022-03-28 18:52:46 +0530357 actual_tax_dict = dict(
358 [
359 [tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
360 for tax in self.doc.get("taxes")
361 if tax.charge_type == "Actual"
362 ]
363 )
Nabin Hait3237c752015-02-17 11:11:11 +0530364
marination91982d12023-01-24 18:03:53 +0530365 for n, item in enumerate(self._items):
Nabin Hait3237c752015-02-17 11:11:11 +0530366 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530367 for i, tax in enumerate(self.doc.get("taxes")):
368 # tax_amount represents the amount of tax for the current step
369 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
370
371 # Adjust divisional loss to the last item
372 if tax.charge_type == "Actual":
373 actual_tax_dict[tax.idx] -= current_tax_amount
marination91982d12023-01-24 18:03:53 +0530374 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530375 current_tax_amount += actual_tax_dict[tax.idx]
376
Nabin Hait2b019ed2015-02-22 23:03:07 +0530377 # accumulate tax amount into tax.tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530378 if tax.charge_type != "Actual" and not (
379 self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
380 ):
381 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530382
Nabin Hait3237c752015-02-17 11:11:11 +0530383 # store tax_amount for current item as it will be used for
384 # charge type = 'On Previous Row Amount'
385 tax.tax_amount_for_current_item = current_tax_amount
386
Nabin Hait2b019ed2015-02-22 23:03:07 +0530387 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530388 tax.tax_amount_after_discount_amount += current_tax_amount
389
Nabin Haitcd951342017-07-31 18:07:45 +0530390 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530391
Nabin Hait3237c752015-02-17 11:11:11 +0530392 # note: grand_total_for_current_item contains the contribution of
393 # item's amount, previously applied tax and the current tax on that item
Ankush Menat494bd9e2022-03-28 18:52:46 +0530394 if i == 0:
Nabin Haitcd951342017-07-31 18:07:45 +0530395 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530396 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530397 tax.grand_total_for_current_item = flt(
398 self.doc.get("taxes")[i - 1].grand_total_for_current_item + current_tax_amount
399 )
Nabin Hait3237c752015-02-17 11:11:11 +0530400
401 # set precision in the last item iteration
marination91982d12023-01-24 18:03:53 +0530402 if n == len(self._items) - 1:
Nabin Hait3237c752015-02-17 11:11:11 +0530403 self.round_off_totals(tax)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530404 self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
Deepesh Gargb6705882021-04-14 11:20:27 +0530405
406 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530407 self.set_cumulative_total(i, tax)
408
Deepesh Gargb6705882021-04-14 11:20:27 +0530409 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530410
Nabin Hait3237c752015-02-17 11:11:11 +0530411 # adjust Discount Amount loss in last tax iteration
Ankush Menat494bd9e2022-03-28 18:52:46 +0530412 if (
413 i == (len(self.doc.get("taxes")) - 1)
414 and self.discount_amount_applied
415 and self.doc.discount_amount
416 and self.doc.apply_discount_on == "Grand Total"
417 and not rounding_adjustment_computed
418 ):
419 self.doc.rounding_adjustment = flt(
420 self.doc.grand_total - flt(self.doc.discount_amount) - tax.total,
421 self.doc.precision("rounding_adjustment"),
422 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530423
Nabin Haitcd951342017-07-31 18:07:45 +0530424 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
425 # if just for valuation, do not add the tax amount in total
426 # if tax/charges is for deduction, multiply by -1
427 if getattr(tax, "category", None):
428 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530429 if self.doc.doctype in [
430 "Purchase Order",
431 "Purchase Invoice",
432 "Purchase Receipt",
433 "Supplier Quotation",
434 ]:
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530435 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530436 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530437
Nabin Haitcd951342017-07-31 18:07:45 +0530438 def set_cumulative_total(self, row_idx, tax):
439 tax_amount = tax.tax_amount_after_discount_amount
440 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
441
442 if row_idx == 0:
443 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
444 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530445 tax.total = flt(self.doc.get("taxes")[row_idx - 1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530446
447 def get_current_tax_amount(self, item, tax, item_tax_map):
448 tax_rate = self._get_tax_rate(tax, item_tax_map)
449 current_tax_amount = 0.0
450
451 if tax.charge_type == "Actual":
452 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530453 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530454 current_tax_amount = (
455 item.net_amount * actual / self.doc.net_total if self.doc.net_total else 0.0
456 )
Nabin Haite7679702015-02-20 14:40:35 +0530457
Nabin Hait3237c752015-02-17 11:11:11 +0530458 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530459 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530460 elif tax.charge_type == "On Previous Row Amount":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530461 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
462 cint(tax.row_id) - 1
463 ].tax_amount_for_current_item
Nabin Hait3237c752015-02-17 11:11:11 +0530464 elif tax.charge_type == "On Previous Row Total":
Ankush Menat494bd9e2022-03-28 18:52:46 +0530465 current_tax_amount = (tax_rate / 100.0) * self.doc.get("taxes")[
466 cint(tax.row_id) - 1
467 ].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530468 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530469 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530470
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530471 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530472 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530473
474 return current_tax_amount
475
Nabin Haite7679702015-02-20 14:40:35 +0530476 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
477 # store tax breakup for each item
478 key = item.item_code or item.item_name
Ankush Menat494bd9e2022-03-28 18:52:46 +0530479 item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
Nabin Haite7679702015-02-20 14:40:35 +0530480 if tax.item_wise_tax_detail.get(key):
481 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
482
Ankush Menat494bd9e2022-03-28 18:52:46 +0530483 tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530484
Nabin Hait3237c752015-02-17 11:11:11 +0530485 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530486 if tax.account_head in frappe.flags.round_off_applicable_accounts:
487 tax.tax_amount = round(tax.tax_amount, 0)
488 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
489
Nabin Haite7679702015-02-20 14:40:35 +0530490 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Ankush Menat494bd9e2022-03-28 18:52:46 +0530491 tax.tax_amount_after_discount_amount = flt(
492 tax.tax_amount_after_discount_amount, tax.precision("tax_amount")
493 )
Nabin Haitce245122015-02-22 20:14:49 +0530494
Deepesh Gargb6705882021-04-14 11:20:27 +0530495 def round_off_base_values(self, tax):
496 # Round off to nearest integer based on regional settings
497 if tax.account_head in frappe.flags.round_off_applicable_accounts:
498 tax.base_tax_amount = round(tax.base_tax_amount, 0)
499 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
500
Nabin Haita1bf43b2015-03-17 10:50:47 +0530501 def manipulate_grand_total_for_inclusive_tax(self):
502 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530503 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 +0530504 last_tax = self.doc.get("taxes")[-1]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530505 non_inclusive_tax_amount = sum(
506 flt(d.tax_amount_after_discount_amount)
507 for d in self.doc.get("taxes")
508 if not d.included_in_print_rate
509 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530510
Ankush Menat494bd9e2022-03-28 18:52:46 +0530511 diff = (
512 self.doc.total + non_inclusive_tax_amount - flt(last_tax.total, last_tax.precision("total"))
513 )
Nabin Haitf32fc232019-12-25 13:59:24 +0530514
515 # If discount amount applied, deduct the discount amount
516 # because self.doc.total is always without discount, but last_tax.total is after discount
517 if self.discount_amount_applied and self.doc.discount_amount:
518 diff -= flt(self.doc.discount_amount)
519
520 diff = flt(diff, self.doc.precision("rounding_adjustment"))
521
Ankush Menat494bd9e2022-03-28 18:52:46 +0530522 if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530523 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530524
525 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530526 if self.doc.get("taxes"):
527 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
528 else:
529 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530530
Subin Tom75a76e62021-10-29 16:45:04 +0530531 if self.doc.get("taxes"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530532 self.doc.total_taxes_and_charges = flt(
533 self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
534 self.doc.precision("total_taxes_and_charges"),
535 )
Subin Tom75a76e62021-10-29 16:45:04 +0530536 else:
537 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530538
Nabin Hait2e4de832017-09-19 14:53:16 +0530539 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530540
Ankush Menat494bd9e2022-03-28 18:52:46 +0530541 if self.doc.doctype in [
542 "Quotation",
543 "Sales Order",
544 "Delivery Note",
545 "Sales Invoice",
546 "POS Invoice",
547 ]:
548 self.doc.base_grand_total = (
549 flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total"))
550 if self.doc.total_taxes_and_charges
551 else self.doc.base_net_total
552 )
Nabin Hait3237c752015-02-17 11:11:11 +0530553 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530554 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530555 for tax in self.doc.get("taxes"):
556 if tax.category in ["Valuation and Total", "Total"]:
557 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530558 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530559 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530560 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530561
Nabin Haite7679702015-02-20 14:40:35 +0530562 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530563
Ankush Menat494bd9e2022-03-28 18:52:46 +0530564 self.doc.base_grand_total = (
565 flt(self.doc.grand_total * self.doc.conversion_rate)
566 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted)
Nabin Haite7679702015-02-20 14:40:35 +0530567 else self.doc.base_net_total
Ankush Menat494bd9e2022-03-28 18:52:46 +0530568 )
Nabin Hait3237c752015-02-17 11:11:11 +0530569
Ankush Menat494bd9e2022-03-28 18:52:46 +0530570 self._set_in_company_currency(
571 self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]
572 )
Nabin Hait3237c752015-02-17 11:11:11 +0530573
Nabin Haite7679702015-02-20 14:40:35 +0530574 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530575
Nabin Hait2e4de832017-09-19 14:53:16 +0530576 self.set_rounded_total()
577
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530578 def calculate_total_net_weight(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530579 if self.doc.meta.get_field("total_net_weight"):
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530580 self.doc.total_net_weight = 0.0
marination91982d12023-01-24 18:03:53 +0530581 for d in self._items:
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530582 if d.total_weight:
583 self.doc.total_net_weight += d.total_weight
584
Nabin Hait2e4de832017-09-19 14:53:16 +0530585 def set_rounded_total(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530586 if self.doc.get("is_consolidated") and self.doc.get("rounding_adjustment"):
Saqib Ansari17445c72022-03-07 18:01:07 +0530587 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530588
Saqib Ansari17445c72022-03-07 18:01:07 +0530589 if self.doc.meta.get_field("rounded_total"):
590 if self.doc.is_rounded_total_disabled():
591 self.doc.rounded_total = self.doc.base_rounded_total = 0
592 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530593
Ankush Menat494bd9e2022-03-28 18:52:46 +0530594 self.doc.rounded_total = round_based_on_smallest_currency_fraction(
595 self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total")
596 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530597
Ankush Menat494bd9e2022-03-28 18:52:46 +0530598 # if print_in_rate is set, we would have already calculated rounding adjustment
599 self.doc.rounding_adjustment += flt(
600 self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment")
601 )
Saqib Ansari17445c72022-03-07 18:01:07 +0530602
603 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530604
Nabin Hait3237c752015-02-17 11:11:11 +0530605 def _cleanup(self):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530606 if not self.doc.get("is_consolidated"):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530607 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530608 if not tax.get("dont_recompute_tax"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530609 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530610
Nabin Hait3769d872015-12-18 13:12:02 +0530611 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530612 if self.doc.additional_discount_percentage:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530613 self.doc.discount_amount = flt(
614 flt(self.doc.get(scrub(self.doc.apply_discount_on)))
615 * self.doc.additional_discount_percentage
616 / 100,
617 self.doc.precision("discount_amount"),
618 )
Nabin Hait3237c752015-02-17 11:11:11 +0530619
620 def apply_discount_amount(self):
621 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530622 if not self.doc.apply_discount_on:
623 frappe.throw(_("Please select Apply Discount On"))
624
Deepesh Garg3b159662022-08-21 17:51:05 +0530625 self.doc.base_discount_amount = flt(
626 self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
627 )
628
Deepesh Garge54ec4b2022-07-03 11:02:21 +0530629 if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
630 "is_cash_or_non_trade_discount"
631 ):
Deepesh Garg169ff5a2022-06-19 21:18:12 +0530632 self.discount_amount_applied = True
633 return
634
Nabin Haite7679702015-02-20 14:40:35 +0530635 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530636 taxes = self.doc.get("taxes")
637 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530638
Nabin Haite7679702015-02-20 14:40:35 +0530639 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530640 # calculate item amount after Discount Amount
marination91982d12023-01-24 18:03:53 +0530641 for i, item in enumerate(self._items):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530642 distributed_amount = (
643 flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
644 )
Anand Doshiec5ec602015-03-05 19:31:23 +0530645
Nabin Haite7679702015-02-20 14:40:35 +0530646 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530647 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530648
Nabin Hait25bd84d2015-03-04 15:06:56 +0530649 # discount amount rounding loss adjustment if no taxes
Ankush Menat494bd9e2022-03-28 18:52:46 +0530650 if (
651 self.doc.apply_discount_on == "Net Total"
652 or not taxes
653 or total_for_discount_amount == self.doc.net_total
marination91982d12023-01-24 18:03:53 +0530654 ) and i == len(self._items) - 1:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530655 discount_amount_loss = flt(
656 self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
657 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530658
Ankush Menat494bd9e2022-03-28 18:52:46 +0530659 item.net_amount = flt(item.net_amount + discount_amount_loss, item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530660
Nabin Hait51e980d2015-10-10 18:10:05 +0530661 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 +0530662
Nabin Haite7679702015-02-20 14:40:35 +0530663 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530664
665 self.discount_amount_applied = True
666 self._calculate()
667 else:
668 self.doc.base_discount_amount = 0
669
Nabin Haite7679702015-02-20 14:40:35 +0530670 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530671 if self.doc.apply_discount_on == "Net Total":
672 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530673 else:
674 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530675
Nabin Haite7679702015-02-20 14:40:35 +0530676 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530677 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530678 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
679 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530680 elif tax.row_id in actual_taxes_dict:
681 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
682 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530683
Ankush Menat494bd9e2022-03-28 18:52:46 +0530684 return flt(
685 self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total")
686 )
Nabin Hait3237c752015-02-17 11:11:11 +0530687
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530688 def calculate_total_advance(self):
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100689 if not self.doc.docstatus.is_cancelled():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530690 total_allocated_amount = sum(
691 flt(adv.allocated_amount, adv.precision("allocated_amount"))
692 for adv in self.doc.get("advances")
693 )
Nabin Hait3237c752015-02-17 11:11:11 +0530694
Nabin Haite7679702015-02-20 14:40:35 +0530695 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530696
Faris Ansari6041f5c2018-02-08 13:33:52 +0530697 grand_total = self.doc.rounded_total or self.doc.grand_total
698
Nabin Hait289ffb72016-02-08 11:06:55 +0530699 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530700 invoice_total = flt(
701 grand_total - flt(self.doc.write_off_amount), self.doc.precision("grand_total")
702 )
Nabin Hait8d8cba72017-04-03 17:26:22 +0530703 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530704 base_write_off_amount = flt(
705 flt(self.doc.write_off_amount) * self.doc.conversion_rate,
706 self.doc.precision("base_write_off_amount"),
707 )
708 invoice_total = (
709 flt(grand_total * self.doc.conversion_rate, self.doc.precision("grand_total"))
710 - base_write_off_amount
711 )
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530712
Nabin Haitadc09232016-02-09 10:31:11 +0530713 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530714 frappe.throw(
715 _("Advance amount cannot be greater than {0} {1}").format(
716 self.doc.party_account_currency, invoice_total
717 )
718 )
Nabin Hait3237c752015-02-17 11:11:11 +0530719
Raffael Meyer67cf7e12023-01-15 13:04:16 +0100720 if self.doc.docstatus.is_draft():
Ankush Menat3821a972022-03-28 20:14:19 +0530721 if self.doc.get("write_off_outstanding_amount_automatically"):
Deepesh Garg19b1b1f2022-03-25 18:02:14 +0530722 self.doc.write_off_amount = 0
723
Nabin Hait3237c752015-02-17 11:11:11 +0530724 self.calculate_outstanding_amount()
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530725 self.calculate_write_off_amount()
Nabin Hait3237c752015-02-17 11:11:11 +0530726
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530727 def is_internal_invoice(self):
728 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530729 Checks if its an internal transfer invoice
730 and decides if to calculate any out standing amount or not
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530731 """
732
Ankush Menat494bd9e2022-03-28 18:52:46 +0530733 if self.doc.doctype in ("Sales Invoice", "Purchase Invoice") and self.doc.is_internal_transfer():
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530734 return True
735
736 return False
737
Nabin Hait3237c752015-02-17 11:11:11 +0530738 def calculate_outstanding_amount(self):
739 # NOTE:
740 # write_off_amount is only for POS Invoice
741 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530742 if self.doc.doctype == "Sales Invoice":
743 self.calculate_paid_amount()
744
Ankush Menat494bd9e2022-03-28 18:52:46 +0530745 if (
746 self.doc.is_return
747 and self.doc.return_against
748 and not self.doc.get("is_pos")
749 or self.is_internal_invoice()
750 ):
751 return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530752
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530753 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530754 self._set_in_company_currency(self.doc, ["write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530755
Nabin Hait877e1bb2017-11-17 12:27:43 +0530756 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
757 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530758 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
759
Nabin Hait877e1bb2017-11-17 12:27:43 +0530760 if self.doc.party_account_currency == self.doc.currency:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530761 total_amount_to_pay = flt(
762 grand_total - self.doc.total_advance - flt(self.doc.write_off_amount),
763 self.doc.precision("grand_total"),
764 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530765 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530766 total_amount_to_pay = flt(
767 flt(base_grand_total, self.doc.precision("base_grand_total"))
768 - self.doc.total_advance
769 - flt(self.doc.base_write_off_amount),
770 self.doc.precision("base_grand_total"),
771 )
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530772
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530773 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530774 change_amount = 0
775
Ankush Menat494bd9e2022-03-28 18:52:46 +0530776 if self.doc.doctype == "Sales Invoice" and not self.doc.get("is_return"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530777 self.calculate_change_amount()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530778 change_amount = (
779 self.doc.change_amount
780 if self.doc.party_account_currency == self.doc.currency
781 else self.doc.base_change_amount
782 )
Nabin Hait877e1bb2017-11-17 12:27:43 +0530783
Ankush Menat494bd9e2022-03-28 18:52:46 +0530784 paid_amount = (
785 self.doc.paid_amount
786 if self.doc.party_account_currency == self.doc.currency
787 else self.doc.base_paid_amount
788 )
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530789
Ankush Menat494bd9e2022-03-28 18:52:46 +0530790 self.doc.outstanding_amount = flt(
791 total_amount_to_pay - flt(paid_amount) + flt(change_amount),
792 self.doc.precision("outstanding_amount"),
793 )
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530794
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530795 if (
Ankush Menat494bd9e2022-03-28 18:52:46 +0530796 self.doc.doctype == "Sales Invoice"
797 and self.doc.get("is_pos")
Saqib Ansari33762db2022-08-10 14:17:28 +0530798 and self.doc.get("pos_profile")
799 and self.doc.get("is_consolidated")
800 ):
801 write_off_limit = flt(
802 frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit")
803 )
804 if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit:
805 self.doc.write_off_outstanding_amount_automatically = 1
806
807 if (
808 self.doc.doctype == "Sales Invoice"
809 and self.doc.get("is_pos")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530810 and self.doc.get("is_return")
811 and not self.doc.get("is_consolidated")
Saqib Ansarie8a7a542022-03-12 19:20:48 +0530812 ):
Subin Tom7d627df2021-08-23 11:05:07 +0530813 self.set_total_amount_to_default_mop(total_amount_to_pay)
814 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530815
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530816 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530817
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530818 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530819
820 if self.doc.is_pos:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530821 for payment in self.doc.get("payments"):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530822 payment.amount = flt(payment.amount)
823 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530824 paid_amount += payment.amount
825 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530826 elif not self.doc.is_return:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530827 self.doc.set("payments", [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530828
Manas Solankida486ee2018-07-06 12:36:57 +0530829 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
830 base_paid_amount += self.doc.loyalty_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530831 paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
Manas Solankida486ee2018-07-06 12:36:57 +0530832
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530833 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
834 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
835
Nabin Hait3bb1a422016-08-02 16:41:10 +0530836 def calculate_change_amount(self):
837 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530838 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530839 grand_total = self.doc.rounded_total or self.doc.grand_total
840 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530841
Ankush Menat494bd9e2022-03-28 18:52:46 +0530842 if (
843 self.doc.doctype == "Sales Invoice"
844 and self.doc.paid_amount > grand_total
845 and not self.doc.is_return
846 and any(d.type == "Cash" for d in self.doc.payments)
847 ):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530848 self.doc.change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530849 self.doc.paid_amount - grand_total, self.doc.precision("change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530850 )
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530851
Ankush Menat494bd9e2022-03-28 18:52:46 +0530852 self.doc.base_change_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530853 self.doc.base_paid_amount - base_grand_total, self.doc.precision("base_change_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530854 )
mbauskar36b51892016-01-18 16:31:10 +0530855
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530856 def calculate_write_off_amount(self):
Ankush Menat3821a972022-03-28 20:14:19 +0530857 if self.doc.get("write_off_outstanding_amount_automatically"):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530858 self.doc.write_off_amount = flt(
Ankush Menat3821a972022-03-28 20:14:19 +0530859 self.doc.outstanding_amount, self.doc.precision("write_off_amount")
Ankush Menat494bd9e2022-03-28 18:52:46 +0530860 )
861 self.doc.base_write_off_amount = flt(
862 self.doc.write_off_amount * self.doc.conversion_rate,
863 self.doc.precision("base_write_off_amount"),
864 )
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530865
Deepesh Gargf57f4af2022-03-24 12:31:37 +0530866 self.calculate_outstanding_amount()
867
mbauskar36b51892016-01-18 16:31:10 +0530868 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530869 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530870 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530871 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530872 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530873 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530874 for d in get_applied_pricing_rules(item.pricing_rules):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530875 pricing_rule = frappe.get_cached_doc("Pricing Rule", d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530876
Ankush Menat494bd9e2022-03-28 18:52:46 +0530877 if pricing_rule.margin_rate_or_amount and (
878 (
879 pricing_rule.currency == self.doc.currency
880 and pricing_rule.margin_type in ["Amount", "Percentage"]
881 )
882 or pricing_rule.margin_type == "Percentage"
883 ):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530884 item.margin_type = pricing_rule.margin_type
885 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530886 has_margin = True
887
888 if not has_margin:
889 item.margin_type = None
890 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530891
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530892 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
893 item.margin_type = "Amount"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530894 item.margin_rate_or_amount = flt(
895 item.rate - item.price_list_rate, item.precision("margin_rate_or_amount")
896 )
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530897 item.rate_with_margin = item.rate
898
899 elif item.margin_type and item.margin_rate_or_amount:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530900 margin_value = (
901 item.margin_rate_or_amount
902 if item.margin_type == "Amount"
903 else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
904 )
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530905 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530906 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530907
Shreya Shahbe690ef2017-11-14 17:22:41 +0530908 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530909
910 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530911 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530912
Subin Tom7d627df2021-08-23 11:05:07 +0530913 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Deepesh Garg06e8e282022-10-31 19:58:46 +0530914 total_paid_amount = 0
915 for payment in self.doc.get("payments"):
916 total_paid_amount += (
917 payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
Ankush Menat494bd9e2022-03-28 18:52:46 +0530918 )
919
Deepesh Garg06e8e282022-10-31 19:58:46 +0530920 pending_amount = total_amount_to_pay - total_paid_amount
921
922 if pending_amount > 0:
923 default_mode_of_payment = frappe.db.get_value(
924 "POS Payment Method",
925 {"parent": self.doc.pos_profile, "default": 1},
926 ["mode_of_payment"],
927 as_dict=1,
928 )
929
930 if default_mode_of_payment:
931 self.doc.payments = []
932 self.doc.append(
933 "payments",
934 {
935 "mode_of_payment": default_mode_of_payment.mode_of_payment,
936 "amount": pending_amount,
937 "default": 1,
938 },
939 )
940
Deepesh Garg0ebace52020-02-25 13:21:16 +0530941
Nabin Hait9c421612017-07-20 13:32:01 +0530942def get_itemised_tax_breakup_html(doc):
943 if not doc.taxes:
944 return
945 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530946
Nabin Hait9c421612017-07-20 13:32:01 +0530947 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530948 tax_accounts = []
949 for tax in doc.taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530950 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +0530951 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530952 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530953 tax_accounts.append(tax.description)
954
Nabin Hait9c421612017-07-20 13:32:01 +0530955 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530956
Nabin Hait9c421612017-07-20 13:32:01 +0530957 # get tax breakup data
958 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530959
960 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
961
rohitwaghchaured4526682017-12-28 14:20:13 +0530962 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530963 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530964
Nabin Hait9c421612017-07-20 13:32:01 +0530965 return frappe.render_template(
Ankush Menat494bd9e2022-03-28 18:52:46 +0530966 "templates/includes/itemised_tax_breakup.html",
967 dict(
Nabin Hait9c421612017-07-20 13:32:01 +0530968 headers=headers,
969 itemised_tax=itemised_tax,
970 itemised_taxable_amount=itemised_taxable_amount,
971 tax_accounts=tax_accounts,
Ankush Menat494bd9e2022-03-28 18:52:46 +0530972 doc=doc,
973 ),
Nabin Hait9c421612017-07-20 13:32:01 +0530974 )
Nabin Hait852cb642017-07-05 12:58:19 +0530975
Ankush Menat494bd9e2022-03-28 18:52:46 +0530976
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530977@frappe.whitelist()
978def get_round_off_applicable_accounts(company, account_list):
Sagar Vora17ef3c92023-04-04 17:49:50 +0530979 # required to set correct region
980 frappe.flags.company = company
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530981 account_list = get_regional_round_off_accounts(company, account_list)
982
983 return account_list
984
Ankush Menat494bd9e2022-03-28 18:52:46 +0530985
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530986@erpnext.allow_regional
987def get_regional_round_off_accounts(company, account_list):
988 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530989
Ankush Menat494bd9e2022-03-28 18:52:46 +0530990
rohitwaghchaured4526682017-12-28 14:20:13 +0530991@erpnext.allow_regional
992def update_itemised_tax_data(doc):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530993 # Don't delete this method, used for localization
rohitwaghchaured4526682017-12-28 14:20:13 +0530994 pass
995
Ankush Menat494bd9e2022-03-28 18:52:46 +0530996
Nabin Haitb962fc12017-07-17 18:02:31 +0530997@erpnext.allow_regional
998def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
999 return [_("Item"), _("Taxable Amount")] + tax_accounts
1000
Ankush Menat494bd9e2022-03-28 18:52:46 +05301001
Nabin Haitb962fc12017-07-17 18:02:31 +05301002@erpnext.allow_regional
1003def get_itemised_tax_breakup_data(doc):
1004 itemised_tax = get_itemised_tax(doc.taxes)
1005
1006 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
1007
1008 return itemised_tax, itemised_taxable_amount
1009
Ankush Menat494bd9e2022-03-28 18:52:46 +05301010
Nabin Hait34c551d2019-07-03 10:34:31 +05301011def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +05301012 itemised_tax = {}
1013 for tax in taxes:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301014 if getattr(tax, "category", None) and tax.category == "Valuation":
Nabin Haitcaab5822017-08-24 16:22:28 +05301015 continue
1016
Nabin Haitb962fc12017-07-17 18:02:31 +05301017 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +05301018 if item_tax_map:
1019 for item_code, tax_data in item_tax_map.items():
1020 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +05301021
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301022 tax_rate = 0.0
1023 tax_amount = 0.0
1024
Nabin Hait2e4de832017-09-19 14:53:16 +05301025 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301026 tax_rate = flt(tax_data[0])
1027 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +05301028 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +05301029 tax_rate = flt(tax_data)
1030
Ankush Menat494bd9e2022-03-28 18:52:46 +05301031 itemised_tax[item_code][tax.description] = frappe._dict(
1032 dict(tax_rate=tax_rate, tax_amount=tax_amount)
1033 )
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301034
Nabin Hait34c551d2019-07-03 10:34:31 +05301035 if with_tax_account:
1036 itemised_tax[item_code][tax.description].tax_account = tax.account_head
1037
Nabin Haitb962fc12017-07-17 18:02:31 +05301038 return itemised_tax
1039
Ankush Menat494bd9e2022-03-28 18:52:46 +05301040
Nabin Haitb962fc12017-07-17 18:02:31 +05301041def get_itemised_taxable_amount(items):
1042 itemised_taxable_amount = frappe._dict()
1043 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +05301044 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +05301045 itemised_taxable_amount.setdefault(item_code, 0)
1046 itemised_taxable_amount[item_code] += item.net_amount
1047
Nabin Haitcaab5822017-08-24 16:22:28 +05301048 return itemised_taxable_amount
1049
Ankush Menat494bd9e2022-03-28 18:52:46 +05301050
Nabin Haitcaab5822017-08-24 16:22:28 +05301051def get_rounded_tax_amount(itemised_tax, precision):
1052 # Rounding based on tax_amount precision
1053 for taxes in itemised_tax.values():
1054 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +05301055 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301056
Ankush Menat494bd9e2022-03-28 18:52:46 +05301057
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301058class init_landed_taxes_and_totals(object):
1059 def __init__(self, doc):
1060 self.doc = doc
Ankush Menat494bd9e2022-03-28 18:52:46 +05301061 self.tax_field = "taxes" if self.doc.doctype == "Landed Cost Voucher" else "additional_costs"
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301062 self.set_account_currency()
1063 self.set_exchange_rate()
1064 self.set_amounts_in_company_currency()
1065
1066 def set_account_currency(self):
1067 company_currency = erpnext.get_company_currency(self.doc.company)
1068 for d in self.doc.get(self.tax_field):
1069 if not d.account_currency:
Daizy Modi4efc9472022-11-07 09:21:03 +05301070 account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301071 d.account_currency = account_currency or company_currency
1072
1073 def set_exchange_rate(self):
1074 company_currency = erpnext.get_company_currency(self.doc.company)
1075 for d in self.doc.get(self.tax_field):
1076 if d.account_currency == company_currency:
1077 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +05301078 elif not d.exchange_rate:
Ankush Menat494bd9e2022-03-28 18:52:46 +05301079 d.exchange_rate = get_exchange_rate(
1080 self.doc.posting_date,
1081 account=d.expense_account,
1082 account_currency=d.account_currency,
1083 company=self.doc.company,
1084 )
Deepesh Gargbfc17e42020-12-25 18:34:39 +05301085
1086 if not d.exchange_rate:
1087 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
1088
1089 def set_amounts_in_company_currency(self):
1090 for d in self.doc.get(self.tax_field):
1091 d.amount = flt(d.amount, d.precision("amount"))
niralisatapara12456f92022-11-03 10:46:30 +05301092 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))