blob: 05edb2530c227de528e47f2c65efbe9557f92b04 [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
4from __future__ import unicode_literals
5import json
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +05306import frappe, erpnext
Nabin Hait3769d872015-12-18 13:12:02 +05307from frappe import _, scrub
Nabin Haitb962fc12017-07-17 18:02:31 +05308from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
Nabin Hait613d0812015-02-23 11:58:15 +05309from erpnext.controllers.accounts_controller import validate_conversion_rate, \
10 validate_taxes_and_charges, validate_inclusive_tax
Deepesh Gargef0d26c2020-01-06 15:34:15 +053011from erpnext.stock.get_item_details import _get_item_tax_template
marination733fd5f2020-08-26 18:23:12 +053012from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
Deepesh Gargbfc17e42020-12-25 18:34:39 +053013from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Nabin Hait3237c752015-02-17 11:11:11 +053014
Nabin Haitfe81da22015-02-18 12:23:18 +053015class calculate_taxes_and_totals(object):
Nabin Hait3237c752015-02-17 11:11:11 +053016 def __init__(self, doc):
17 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053018 frappe.flags.round_off_applicable_accounts = []
19 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053020 self.calculate()
21
Nabin Hait3237c752015-02-17 11:11:11 +053022 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053023 if not len(self.doc.get("items")):
24 return
25
Nabin Hait3237c752015-02-17 11:11:11 +053026 self.discount_amount_applied = False
27 self._calculate()
28
29 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053030 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053031 self.apply_discount_amount()
32
Nabin Haitbd00e812015-02-17 12:50:51 +053033 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053034 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053035
Nabin Hait852cb642017-07-05 12:58:19 +053036 if self.doc.meta.get_field("other_charges_calculation"):
37 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053038
39 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053040 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053041 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053042 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053043 self.initialize_taxes()
44 self.determine_exclusive_rate()
45 self.calculate_net_total()
46 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053047 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053048 self.calculate_totals()
49 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053050 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053051
Deepesh Gargef0d26c2020-01-06 15:34:15 +053052 def validate_item_tax_template(self):
53 for item in self.doc.get('items'):
54 if item.item_code and item.get('item_tax_template'):
55 item_doc = frappe.get_cached_doc("Item", item.item_code)
56 args = {
Deepesh Garg8a7e2832021-06-04 22:53:26 +053057 'net_rate': item.net_rate or item.rate,
Deepesh Gargef0d26c2020-01-06 15:34:15 +053058 'tax_category': self.doc.get('tax_category'),
59 'posting_date': self.doc.get('posting_date'),
60 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050061 'transaction_date': self.doc.get('transaction_date'),
62 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053063 }
64
65 item_group = item_doc.item_group
66 item_group_taxes = []
67
68 while item_group:
69 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
70 item_group_taxes += item_group_doc.taxes or []
71 item_group = item_group_doc.parent_item_group
72
73 item_taxes = item_doc.taxes or []
74
75 if not item_group_taxes and (not item_taxes):
76 # No validation if no taxes in item or item group
77 continue
78
79 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
80
Deepesh Garg18be7672021-06-06 13:25:34 +053081 if taxes:
82 if item.item_tax_template not in taxes:
83 item.item_tax_template = taxes[0]
84 frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
85 item.idx, frappe.bold(item.item_code)
86 ))
Deepesh Gargef0d26c2020-01-06 15:34:15 +053087
Nabin Haite7679702015-02-20 14:40:35 +053088 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053089 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053090 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +053091 if not self.doc.currency or self.doc.currency == company_currency:
92 self.doc.currency = company_currency
93 self.doc.conversion_rate = 1.0
94 else:
95 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
96 self.doc.meta.get_label("conversion_rate"), self.doc.company)
97
98 self.doc.conversion_rate = flt(self.doc.conversion_rate)
99
Nabin Hait3237c752015-02-17 11:11:11 +0530100 def calculate_item_values(self):
101 if not self.discount_amount_applied:
102 for item in self.doc.get("items"):
103 self.doc.round_floats_in(item)
104
105 if item.discount_percentage == 100:
106 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530107 elif item.price_list_rate:
108 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
109 item.rate = flt(item.price_list_rate *
110 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
111 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
112 elif item.discount_amount and item.pricing_rules:
113 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530114
Anupam Kumared42afc2021-03-15 11:11:28 +0530115 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530116 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530117 if flt(item.rate_with_margin) > 0:
118 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530119
Walstan Baptista37b826b2021-04-03 19:48:46 +0530120 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530121 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530122 else:
123 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530124
Nabin Hait64bfdd92019-04-23 13:37:19 +0530125 elif flt(item.price_list_rate) > 0:
126 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530127 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
128 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530129
Nabin Haite7679702015-02-20 14:40:35 +0530130 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530131
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530132 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530133 item.amount = flt(-1 * item.rate, item.precision("amount"))
134 else:
135 item.amount = flt(item.rate * item.qty, item.precision("amount"))
136
Nabin Haite7679702015-02-20 14:40:35 +0530137 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530138
Nabin Haite7679702015-02-20 14:40:35 +0530139 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530140
Nabin Haite7679702015-02-20 14:40:35 +0530141 item.item_tax_amount = 0.0
142
143 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530144 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530145 for f in fields:
146 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
147 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530148
149 def initialize_taxes(self):
150 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530151 if not self.discount_amount_applied:
152 validate_taxes_and_charges(tax)
153 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530154
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530155 if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530156 tax.item_wise_tax_detail = {}
157
Nabin Hait3237c752015-02-17 11:11:11 +0530158 tax_fields = ["total", "tax_amount_after_discount_amount",
159 "tax_amount_for_current_item", "grand_total_for_current_item",
160 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
161
Nabin Haitde9c8a92015-02-23 01:06:00 +0530162 if tax.charge_type != "Actual" and \
163 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
164 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530165
166 for fieldname in tax_fields:
167 tax.set(fieldname, 0.0)
168
Nabin Hait3237c752015-02-17 11:11:11 +0530169 self.doc.round_floats_in(tax)
170
Nabin Hait3237c752015-02-17 11:11:11 +0530171 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530172 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
173 return
Nabin Hait3237c752015-02-17 11:11:11 +0530174
175 for item in self.doc.get("items"):
176 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
177 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530178 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530179 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530180 tax.tax_fraction_for_current_item, inclusive_tax_amount_per_qty = self.get_current_tax_fraction(tax, item_tax_map)
Nabin Hait3237c752015-02-17 11:11:11 +0530181
182 if i==0:
183 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
184 else:
185 tax.grand_total_fraction_for_current_item = \
186 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
187 + tax.tax_fraction_for_current_item
188
189 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530190 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530191
Nabin Hait19ea7212020-08-11 20:34:57 +0530192 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
193 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
194
195 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530196 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530197 item.discount_percentage = flt(item.discount_percentage,
198 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530199
Nabin Haite7679702015-02-20 14:40:35 +0530200 self._set_in_company_currency(item, ["net_rate", "net_amount"])
201
Nabin Hait3237c752015-02-17 11:11:11 +0530202 def _load_item_tax_rate(self, item_tax_rate):
203 return json.loads(item_tax_rate) if item_tax_rate else {}
204
205 def get_current_tax_fraction(self, tax, item_tax_map):
206 """
207 Get tax fraction for calculating tax exclusive amount
208 from tax inclusive amount
209 """
210 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530211 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530212
213 if cint(tax.included_in_print_rate):
214 tax_rate = self._get_tax_rate(tax, item_tax_map)
215
216 if tax.charge_type == "On Net Total":
217 current_tax_fraction = tax_rate / 100.0
218
219 elif tax.charge_type == "On Previous Row Amount":
220 current_tax_fraction = (tax_rate / 100.0) * \
221 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
222
223 elif tax.charge_type == "On Previous Row Total":
224 current_tax_fraction = (tax_rate / 100.0) * \
225 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530226
Nabin Hait19ea7212020-08-11 20:34:57 +0530227 elif tax.charge_type == "On Item Quantity":
228 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530229
Nabin Hait19ea7212020-08-11 20:34:57 +0530230 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
231 current_tax_fraction *= -1.0
232 inclusive_tax_amount_per_qty *= -1.0
233
234 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530235
236 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530237 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530238 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
239 else:
240 return tax.rate
241
242 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530243 self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530244
Nabin Hait3237c752015-02-17 11:11:11 +0530245 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530246 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530247 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530248 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530249 self.doc.net_total += item.net_amount
250 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530251
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530252 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530253
254 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530255 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530256 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530257 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530258 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
259
260 for n, item in enumerate(self.doc.get("items")):
261 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530262 for i, tax in enumerate(self.doc.get("taxes")):
263 # tax_amount represents the amount of tax for the current step
264 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
265
266 # Adjust divisional loss to the last item
267 if tax.charge_type == "Actual":
268 actual_tax_dict[tax.idx] -= current_tax_amount
269 if n == len(self.doc.get("items")) - 1:
270 current_tax_amount += actual_tax_dict[tax.idx]
271
Nabin Hait2b019ed2015-02-22 23:03:07 +0530272 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530273 if tax.charge_type != "Actual" and \
274 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
275 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530276
Nabin Hait3237c752015-02-17 11:11:11 +0530277 # store tax_amount for current item as it will be used for
278 # charge type = 'On Previous Row Amount'
279 tax.tax_amount_for_current_item = current_tax_amount
280
Nabin Hait2b019ed2015-02-22 23:03:07 +0530281 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530282 tax.tax_amount_after_discount_amount += current_tax_amount
283
Nabin Haitcd951342017-07-31 18:07:45 +0530284 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530285
Nabin Hait3237c752015-02-17 11:11:11 +0530286 # note: grand_total_for_current_item contains the contribution of
287 # item's amount, previously applied tax and the current tax on that item
288 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530289 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530290 else:
291 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530292 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530293
294 # set precision in the last item iteration
295 if n == len(self.doc.get("items")) - 1:
296 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530297 self._set_in_company_currency(tax,
298 ["tax_amount", "tax_amount_after_discount_amount"])
299
300 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530301 self.set_cumulative_total(i, tax)
302
Deepesh Gargb6705882021-04-14 11:20:27 +0530303 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530304
Nabin Hait3237c752015-02-17 11:11:11 +0530305 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530306 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530307 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530308 self.doc.rounding_adjustment = flt(self.doc.grand_total
309 - flt(self.doc.discount_amount) - tax.total,
310 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530311
Nabin Haitcd951342017-07-31 18:07:45 +0530312 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
313 # if just for valuation, do not add the tax amount in total
314 # if tax/charges is for deduction, multiply by -1
315 if getattr(tax, "category", None):
316 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530317 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
318 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530319 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530320
Nabin Haitcd951342017-07-31 18:07:45 +0530321 def set_cumulative_total(self, row_idx, tax):
322 tax_amount = tax.tax_amount_after_discount_amount
323 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
324
325 if row_idx == 0:
326 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
327 else:
328 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530329
330 def get_current_tax_amount(self, item, tax, item_tax_map):
331 tax_rate = self._get_tax_rate(tax, item_tax_map)
332 current_tax_amount = 0.0
333
334 if tax.charge_type == "Actual":
335 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530336 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
337 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
338
Nabin Hait3237c752015-02-17 11:11:11 +0530339 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530340 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530341 elif tax.charge_type == "On Previous Row Amount":
342 current_tax_amount = (tax_rate / 100.0) * \
343 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
344 elif tax.charge_type == "On Previous Row Total":
345 current_tax_amount = (tax_rate / 100.0) * \
346 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530347 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530348 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530349
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530350 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530351 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530352
353 return current_tax_amount
354
Nabin Haite7679702015-02-20 14:40:35 +0530355 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
356 # store tax breakup for each item
357 key = item.item_code or item.item_name
358 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
359 if tax.item_wise_tax_detail.get(key):
360 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
361
Nabin Haitcaab5822017-08-24 16:22:28 +0530362 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530363
Nabin Hait3237c752015-02-17 11:11:11 +0530364 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530365 if tax.account_head in frappe.flags.round_off_applicable_accounts:
366 tax.tax_amount = round(tax.tax_amount, 0)
367 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
368
Nabin Haite7679702015-02-20 14:40:35 +0530369 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530370 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530371 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530372
Deepesh Gargb6705882021-04-14 11:20:27 +0530373 def round_off_base_values(self, tax):
374 # Round off to nearest integer based on regional settings
375 if tax.account_head in frappe.flags.round_off_applicable_accounts:
376 tax.base_tax_amount = round(tax.base_tax_amount, 0)
377 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
378
Nabin Haita1bf43b2015-03-17 10:50:47 +0530379 def manipulate_grand_total_for_inclusive_tax(self):
380 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530381 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 +0530382 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530383 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
384 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530385
Nabin Hait2e4de832017-09-19 14:53:16 +0530386 diff = self.doc.total + non_inclusive_tax_amount \
387 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530388
389 # If discount amount applied, deduct the discount amount
390 # because self.doc.total is always without discount, but last_tax.total is after discount
391 if self.discount_amount_applied and self.doc.discount_amount:
392 diff -= flt(self.doc.discount_amount)
393
394 diff = flt(diff, self.doc.precision("rounding_adjustment"))
395
Nabin Hait2e4de832017-09-19 14:53:16 +0530396 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530397 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530398
399 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530400 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
401 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530402
Nabin Hait2e4de832017-09-19 14:53:16 +0530403 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
404 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530405
Nabin Hait2e4de832017-09-19 14:53:16 +0530406 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530407
Saqiba6f98d42020-07-23 18:51:26 +0530408 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530409 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total")) \
Nabin Haite7679702015-02-20 14:40:35 +0530410 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530411 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530412 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530413 for tax in self.doc.get("taxes"):
414 if tax.category in ["Valuation and Total", "Total"]:
415 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530416 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530417 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530418 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530419
Nabin Haite7679702015-02-20 14:40:35 +0530420 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530421
Nabin Haite7679702015-02-20 14:40:35 +0530422 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
423 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
424 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530425
Nabin Hait2e4de832017-09-19 14:53:16 +0530426 self._set_in_company_currency(self.doc,
427 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530428
Nabin Haite7679702015-02-20 14:40:35 +0530429 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530430
Nabin Hait2e4de832017-09-19 14:53:16 +0530431 self.set_rounded_total()
432
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530433 def calculate_total_net_weight(self):
434 if self.doc.meta.get_field('total_net_weight'):
435 self.doc.total_net_weight = 0.0
436 for d in self.doc.items:
437 if d.total_weight:
438 self.doc.total_net_weight += d.total_weight
439
Nabin Hait2e4de832017-09-19 14:53:16 +0530440 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530441 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530442 if self.doc.is_rounded_total_disabled():
443 self.doc.rounded_total = self.doc.base_rounded_total = 0
444 return
445
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530446 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530447 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530448
Nabin Hait877e1bb2017-11-17 12:27:43 +0530449 #if print_in_rate is set, we would have already calculated rounding adjustment
450 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
451 self.doc.precision("rounding_adjustment"))
452
Nabin Hait02ac9012017-11-22 16:12:20 +0530453 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530454
Nabin Hait3237c752015-02-17 11:11:11 +0530455 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530456 if not self.doc.get('is_consolidated'):
457 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530458 if not tax.get("dont_recompute_tax"):
459 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530460
Nabin Hait3769d872015-12-18 13:12:02 +0530461 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530462 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530463 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530464 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530465
466 def apply_discount_amount(self):
467 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530468 if not self.doc.apply_discount_on:
469 frappe.throw(_("Please select Apply Discount On"))
470
Nabin Hait3237c752015-02-17 11:11:11 +0530471 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
472 self.doc.precision("base_discount_amount"))
473
Nabin Haite7679702015-02-20 14:40:35 +0530474 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530475 taxes = self.doc.get("taxes")
476 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530477
Nabin Haite7679702015-02-20 14:40:35 +0530478 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530479 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530480 for i, item in enumerate(self.doc.get("items")):
481 distributed_amount = flt(self.doc.discount_amount) * \
482 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530483
Nabin Haite7679702015-02-20 14:40:35 +0530484 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530485 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530486
Nabin Hait25bd84d2015-03-04 15:06:56 +0530487 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530488 if (self.doc.apply_discount_on == "Net Total" or not taxes or total_for_discount_amount==self.doc.net_total) \
Nabin Hait25bd84d2015-03-04 15:06:56 +0530489 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530490 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530491 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530492
Anand Doshiec5ec602015-03-05 19:31:23 +0530493 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530494 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530495
Nabin Hait51e980d2015-10-10 18:10:05 +0530496 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 +0530497
Nabin Haite7679702015-02-20 14:40:35 +0530498 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530499
500 self.discount_amount_applied = True
501 self._calculate()
502 else:
503 self.doc.base_discount_amount = 0
504
Nabin Haite7679702015-02-20 14:40:35 +0530505 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530506 if self.doc.apply_discount_on == "Net Total":
507 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530508 else:
509 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530510
Nabin Haite7679702015-02-20 14:40:35 +0530511 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530512 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530513 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
514 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530515 elif tax.row_id in actual_taxes_dict:
516 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
517 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530518
Nabin Hait877e1bb2017-11-17 12:27:43 +0530519 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
520 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530521
522
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530523 def calculate_total_advance(self):
524 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530525 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
526 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530527
Nabin Haite7679702015-02-20 14:40:35 +0530528 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530529
Faris Ansari6041f5c2018-02-08 13:33:52 +0530530 grand_total = self.doc.rounded_total or self.doc.grand_total
531
Nabin Hait289ffb72016-02-08 11:06:55 +0530532 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530533 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530534 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530535 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530536 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530537 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530538 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530539 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530540
Nabin Haitadc09232016-02-09 10:31:11 +0530541 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530542 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
543 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530544
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530545 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530546 self.calculate_outstanding_amount()
547
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530548 def is_internal_invoice(self):
549 """
550 Checks if its an internal transfer invoice
551 and decides if to calculate any out standing amount or not
552 """
553
554 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
555 return True
556
557 return False
558
Nabin Hait3237c752015-02-17 11:11:11 +0530559 def calculate_outstanding_amount(self):
560 # NOTE:
561 # write_off_amount is only for POS Invoice
562 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530563 if self.doc.doctype == "Sales Invoice":
564 self.calculate_paid_amount()
565
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530566 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
567 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530568
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530569 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530570 self._set_in_company_currency(self.doc, ['write_off_amount'])
571
Nabin Hait877e1bb2017-11-17 12:27:43 +0530572 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
573 grand_total = self.doc.rounded_total or self.doc.grand_total
574 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530575 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530576 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
577 else:
578 total_amount_to_pay = flt(flt(grand_total *
579 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
580 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530581
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530582 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530583 change_amount = 0
584
Deepesh Garg0ebace52020-02-25 13:21:16 +0530585 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530586 self.calculate_write_off_amount()
587 self.calculate_change_amount()
588 change_amount = self.doc.change_amount \
589 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
590
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530591 paid_amount = self.doc.paid_amount \
592 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530593
Nabin Hait877e1bb2017-11-17 12:27:43 +0530594 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
595 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530596
Deepesh Garg0ebace52020-02-25 13:21:16 +0530597 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
598 self.update_paid_amount_for_return(total_amount_to_pay)
599
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530600 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530601
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530602 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530603
604 if self.doc.is_pos:
605 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530606 payment.amount = flt(payment.amount)
607 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530608 paid_amount += payment.amount
609 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530610 elif not self.doc.is_return:
611 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530612
Manas Solankida486ee2018-07-06 12:36:57 +0530613 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
614 base_paid_amount += self.doc.loyalty_amount
615 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
616
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530617 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
618 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
619
Nabin Hait3bb1a422016-08-02 16:41:10 +0530620 def calculate_change_amount(self):
621 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530622 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530623
624 if self.doc.doctype == "Sales Invoice" \
625 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530626 and any(d.type == "Cash" for d in self.doc.payments):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530627 grand_total = self.doc.rounded_total or self.doc.grand_total
628 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530629
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530630 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530631 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530632
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530633 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530634 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530635
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530636 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530637 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530638 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
639 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530640 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
641 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530642
mbauskar36b51892016-01-18 16:31:10 +0530643 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530644 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530645 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530646 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530647 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530648 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530649 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530650 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530651
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530652 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
653 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530654 item.margin_type = pricing_rule.margin_type
655 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530656 has_margin = True
657
658 if not has_margin:
659 item.margin_type = None
660 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530661
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530662 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
663 item.margin_type = "Amount"
664 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
665 item.precision("margin_rate_or_amount"))
666 item.rate_with_margin = item.rate
667
668 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530669 margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530670 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530671 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530672
Shreya Shahbe690ef2017-11-14 17:22:41 +0530673 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530674
675 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530676 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530677
Deepesh Garg0ebace52020-02-25 13:21:16 +0530678 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530679 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
680 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530681
Deepesh Garg0ebace52020-02-25 13:21:16 +0530682 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530683 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530684 self.doc.append('payments', {
685 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530686 'amount': total_amount_to_pay,
687 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530688 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530689
690 self.calculate_paid_amount()
691
Nabin Hait9c421612017-07-20 13:32:01 +0530692def get_itemised_tax_breakup_html(doc):
693 if not doc.taxes:
694 return
695 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530696
Nabin Hait9c421612017-07-20 13:32:01 +0530697 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530698 tax_accounts = []
699 for tax in doc.taxes:
700 if getattr(tax, "category", None) and tax.category=="Valuation":
701 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530702 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530703 tax_accounts.append(tax.description)
704
Nabin Hait9c421612017-07-20 13:32:01 +0530705 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530706
Nabin Hait9c421612017-07-20 13:32:01 +0530707 # get tax breakup data
708 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530709
710 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
711
rohitwaghchaured4526682017-12-28 14:20:13 +0530712 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530713 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530714
Nabin Hait9c421612017-07-20 13:32:01 +0530715 return frappe.render_template(
716 "templates/includes/itemised_tax_breakup.html", dict(
717 headers=headers,
718 itemised_tax=itemised_tax,
719 itemised_taxable_amount=itemised_taxable_amount,
720 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530721 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530722 )
Nabin Hait9c421612017-07-20 13:32:01 +0530723 )
Nabin Hait852cb642017-07-05 12:58:19 +0530724
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530725@frappe.whitelist()
726def get_round_off_applicable_accounts(company, account_list):
727 account_list = get_regional_round_off_accounts(company, account_list)
728
729 return account_list
730
731@erpnext.allow_regional
732def get_regional_round_off_accounts(company, account_list):
733 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530734
735@erpnext.allow_regional
736def update_itemised_tax_data(doc):
737 #Don't delete this method, used for localization
738 pass
739
Nabin Haitb962fc12017-07-17 18:02:31 +0530740@erpnext.allow_regional
741def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
742 return [_("Item"), _("Taxable Amount")] + tax_accounts
743
744@erpnext.allow_regional
745def get_itemised_tax_breakup_data(doc):
746 itemised_tax = get_itemised_tax(doc.taxes)
747
748 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
749
750 return itemised_tax, itemised_taxable_amount
751
Nabin Hait34c551d2019-07-03 10:34:31 +0530752def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530753 itemised_tax = {}
754 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530755 if getattr(tax, "category", None) and tax.category=="Valuation":
756 continue
757
Nabin Haitb962fc12017-07-17 18:02:31 +0530758 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530759 if item_tax_map:
760 for item_code, tax_data in item_tax_map.items():
761 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530762
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530763 tax_rate = 0.0
764 tax_amount = 0.0
765
Nabin Hait2e4de832017-09-19 14:53:16 +0530766 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530767 tax_rate = flt(tax_data[0])
768 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530769 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530770 tax_rate = flt(tax_data)
771
772 itemised_tax[item_code][tax.description] = frappe._dict(dict(
773 tax_rate = tax_rate,
774 tax_amount = tax_amount
775 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530776
Nabin Hait34c551d2019-07-03 10:34:31 +0530777 if with_tax_account:
778 itemised_tax[item_code][tax.description].tax_account = tax.account_head
779
Nabin Haitb962fc12017-07-17 18:02:31 +0530780 return itemised_tax
781
782def get_itemised_taxable_amount(items):
783 itemised_taxable_amount = frappe._dict()
784 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530785 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530786 itemised_taxable_amount.setdefault(item_code, 0)
787 itemised_taxable_amount[item_code] += item.net_amount
788
Nabin Haitcaab5822017-08-24 16:22:28 +0530789 return itemised_taxable_amount
790
791def get_rounded_tax_amount(itemised_tax, precision):
792 # Rounding based on tax_amount precision
793 for taxes in itemised_tax.values():
794 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530795 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530796
797class init_landed_taxes_and_totals(object):
798 def __init__(self, doc):
799 self.doc = doc
800 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
801 self.set_account_currency()
802 self.set_exchange_rate()
803 self.set_amounts_in_company_currency()
804
805 def set_account_currency(self):
806 company_currency = erpnext.get_company_currency(self.doc.company)
807 for d in self.doc.get(self.tax_field):
808 if not d.account_currency:
809 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
810 d.account_currency = account_currency or company_currency
811
812 def set_exchange_rate(self):
813 company_currency = erpnext.get_company_currency(self.doc.company)
814 for d in self.doc.get(self.tax_field):
815 if d.account_currency == company_currency:
816 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530817 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530818 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
819 account_currency=d.account_currency, company=self.doc.company)
820
821 if not d.exchange_rate:
822 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
823
824 def set_amounts_in_company_currency(self):
825 for d in self.doc.get(self.tax_field):
826 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530827 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))