blob: fb22a1d608b29088f31619858e68b0bfe4e00033 [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
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530155 if not self.doc.get('is_consolidated'):
156 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
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530350 if not self.doc.get("is_consolidated"):
351 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
Nabin Hait2e4de832017-09-19 14:53:16 +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]
Nabin Hait2e4de832017-09-19 14:53:16 +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"):
458 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530459
Nabin Hait3769d872015-12-18 13:12:02 +0530460 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530461 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530462 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530463 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530464
465 def apply_discount_amount(self):
466 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530467 if not self.doc.apply_discount_on:
468 frappe.throw(_("Please select Apply Discount On"))
469
Nabin Hait3237c752015-02-17 11:11:11 +0530470 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
471 self.doc.precision("base_discount_amount"))
472
Nabin Haite7679702015-02-20 14:40:35 +0530473 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530474 taxes = self.doc.get("taxes")
475 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530476
Nabin Haite7679702015-02-20 14:40:35 +0530477 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530478 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530479 for i, item in enumerate(self.doc.get("items")):
480 distributed_amount = flt(self.doc.discount_amount) * \
481 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530482
Nabin Haite7679702015-02-20 14:40:35 +0530483 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530484 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530485
Nabin Hait25bd84d2015-03-04 15:06:56 +0530486 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530487 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 +0530488 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530489 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530490 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530491
Anand Doshiec5ec602015-03-05 19:31:23 +0530492 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530493 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530494
Nabin Hait51e980d2015-10-10 18:10:05 +0530495 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 +0530496
Nabin Haite7679702015-02-20 14:40:35 +0530497 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530498
499 self.discount_amount_applied = True
500 self._calculate()
501 else:
502 self.doc.base_discount_amount = 0
503
Nabin Haite7679702015-02-20 14:40:35 +0530504 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530505 if self.doc.apply_discount_on == "Net Total":
506 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530507 else:
508 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530509
Nabin Haite7679702015-02-20 14:40:35 +0530510 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530511 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530512 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
513 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530514 elif tax.row_id in actual_taxes_dict:
515 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
516 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530517
Nabin Hait877e1bb2017-11-17 12:27:43 +0530518 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
519 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530520
521
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530522 def calculate_total_advance(self):
523 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530524 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530525 for adv in self.doc.get("advances")])
526
Nabin Haite7679702015-02-20 14:40:35 +0530527 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530528
Faris Ansari6041f5c2018-02-08 13:33:52 +0530529 grand_total = self.doc.rounded_total or self.doc.grand_total
530
Nabin Hait289ffb72016-02-08 11:06:55 +0530531 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530532 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530533 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530534 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530535 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530536 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530537 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530538 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530539
Nabin Haitadc09232016-02-09 10:31:11 +0530540 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530541 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
542 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530543
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530544 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530545 self.calculate_outstanding_amount()
546
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530547 def is_internal_invoice(self):
548 """
549 Checks if its an internal transfer invoice
550 and decides if to calculate any out standing amount or not
551 """
552
553 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
554 return True
555
556 return False
557
Nabin Hait3237c752015-02-17 11:11:11 +0530558 def calculate_outstanding_amount(self):
559 # NOTE:
560 # write_off_amount is only for POS Invoice
561 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530562 if self.doc.doctype == "Sales Invoice":
563 self.calculate_paid_amount()
564
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530565 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
566 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530567
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530568 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530569 self._set_in_company_currency(self.doc, ['write_off_amount'])
570
Nabin Hait877e1bb2017-11-17 12:27:43 +0530571 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
572 grand_total = self.doc.rounded_total or self.doc.grand_total
573 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530574 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530575 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
576 else:
577 total_amount_to_pay = flt(flt(grand_total *
578 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
579 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530580
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530581 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530582 change_amount = 0
583
Deepesh Garg0ebace52020-02-25 13:21:16 +0530584 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530585 self.calculate_write_off_amount()
586 self.calculate_change_amount()
587 change_amount = self.doc.change_amount \
588 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
589
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530590 paid_amount = self.doc.paid_amount \
591 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530592
Nabin Hait877e1bb2017-11-17 12:27:43 +0530593 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
594 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530595
Deepesh Garg0ebace52020-02-25 13:21:16 +0530596 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
597 self.update_paid_amount_for_return(total_amount_to_pay)
598
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530599 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530600
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530601 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530602
603 if self.doc.is_pos:
604 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530605 payment.amount = flt(payment.amount)
606 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530607 paid_amount += payment.amount
608 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530609 elif not self.doc.is_return:
610 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530611
Manas Solankida486ee2018-07-06 12:36:57 +0530612 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
613 base_paid_amount += self.doc.loyalty_amount
614 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
615
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530616 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
617 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
618
Nabin Hait3bb1a422016-08-02 16:41:10 +0530619 def calculate_change_amount(self):
620 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530621 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530622
623 if self.doc.doctype == "Sales Invoice" \
624 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530625 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530626 grand_total = self.doc.rounded_total or self.doc.grand_total
627 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530628
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530629 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530630 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530631
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530632 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530633 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530634
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530635 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530636 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530637 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
638 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530639 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
640 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530641
mbauskar36b51892016-01-18 16:31:10 +0530642 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530643 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530644 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530645 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530646 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530647 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530648 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530649 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530650
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530651 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
652 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530653 item.margin_type = pricing_rule.margin_type
654 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530655 has_margin = True
656
657 if not has_margin:
658 item.margin_type = None
659 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530660
mbauskara52472c2016-03-05 15:10:25 +0530661 if item.margin_type and item.margin_rate_or_amount:
662 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 +0530663 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530664 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530665
Shreya Shahbe690ef2017-11-14 17:22:41 +0530666 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530667
668 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530669 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530670
Deepesh Garg0ebace52020-02-25 13:21:16 +0530671 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530672 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
673 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530674
675 self.doc.payments = []
676
677 if default_mode_of_payment:
678 self.doc.append('payments', {
679 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530680 'amount': total_amount_to_pay,
681 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530682 })
683 else:
684 self.doc.is_pos = 0
685 self.doc.pos_profile = ''
686
687 self.calculate_paid_amount()
688
689
Nabin Hait9c421612017-07-20 13:32:01 +0530690def get_itemised_tax_breakup_html(doc):
691 if not doc.taxes:
692 return
693 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530694
Nabin Hait9c421612017-07-20 13:32:01 +0530695 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530696 tax_accounts = []
697 for tax in doc.taxes:
698 if getattr(tax, "category", None) and tax.category=="Valuation":
699 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530700 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530701 tax_accounts.append(tax.description)
702
Nabin Hait9c421612017-07-20 13:32:01 +0530703 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530704
Nabin Hait9c421612017-07-20 13:32:01 +0530705 # get tax breakup data
706 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530707
708 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
709
rohitwaghchaured4526682017-12-28 14:20:13 +0530710 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530711 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530712
Nabin Hait9c421612017-07-20 13:32:01 +0530713 return frappe.render_template(
714 "templates/includes/itemised_tax_breakup.html", dict(
715 headers=headers,
716 itemised_tax=itemised_tax,
717 itemised_taxable_amount=itemised_taxable_amount,
718 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530719 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530720 )
Nabin Hait9c421612017-07-20 13:32:01 +0530721 )
Nabin Hait852cb642017-07-05 12:58:19 +0530722
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530723@frappe.whitelist()
724def get_round_off_applicable_accounts(company, account_list):
725 account_list = get_regional_round_off_accounts(company, account_list)
726
727 return account_list
728
729@erpnext.allow_regional
730def get_regional_round_off_accounts(company, account_list):
731 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530732
733@erpnext.allow_regional
734def update_itemised_tax_data(doc):
735 #Don't delete this method, used for localization
736 pass
737
Nabin Haitb962fc12017-07-17 18:02:31 +0530738@erpnext.allow_regional
739def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
740 return [_("Item"), _("Taxable Amount")] + tax_accounts
741
742@erpnext.allow_regional
743def get_itemised_tax_breakup_data(doc):
744 itemised_tax = get_itemised_tax(doc.taxes)
745
746 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
747
748 return itemised_tax, itemised_taxable_amount
749
Nabin Hait34c551d2019-07-03 10:34:31 +0530750def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530751 itemised_tax = {}
752 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530753 if getattr(tax, "category", None) and tax.category=="Valuation":
754 continue
755
Nabin Haitb962fc12017-07-17 18:02:31 +0530756 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530757 if item_tax_map:
758 for item_code, tax_data in item_tax_map.items():
759 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530760
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530761 tax_rate = 0.0
762 tax_amount = 0.0
763
Nabin Hait2e4de832017-09-19 14:53:16 +0530764 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530765 tax_rate = flt(tax_data[0])
766 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530767 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530768 tax_rate = flt(tax_data)
769
770 itemised_tax[item_code][tax.description] = frappe._dict(dict(
771 tax_rate = tax_rate,
772 tax_amount = tax_amount
773 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530774
Nabin Hait34c551d2019-07-03 10:34:31 +0530775 if with_tax_account:
776 itemised_tax[item_code][tax.description].tax_account = tax.account_head
777
Nabin Haitb962fc12017-07-17 18:02:31 +0530778 return itemised_tax
779
780def get_itemised_taxable_amount(items):
781 itemised_taxable_amount = frappe._dict()
782 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530783 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530784 itemised_taxable_amount.setdefault(item_code, 0)
785 itemised_taxable_amount[item_code] += item.net_amount
786
Nabin Haitcaab5822017-08-24 16:22:28 +0530787 return itemised_taxable_amount
788
789def get_rounded_tax_amount(itemised_tax, precision):
790 # Rounding based on tax_amount precision
791 for taxes in itemised_tax.values():
792 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530793 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530794
795class init_landed_taxes_and_totals(object):
796 def __init__(self, doc):
797 self.doc = doc
798 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
799 self.set_account_currency()
800 self.set_exchange_rate()
801 self.set_amounts_in_company_currency()
802
803 def set_account_currency(self):
804 company_currency = erpnext.get_company_currency(self.doc.company)
805 for d in self.doc.get(self.tax_field):
806 if not d.account_currency:
807 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
808 d.account_currency = account_currency or company_currency
809
810 def set_exchange_rate(self):
811 company_currency = erpnext.get_company_currency(self.doc.company)
812 for d in self.doc.get(self.tax_field):
813 if d.account_currency == company_currency:
814 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530815 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530816 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
817 account_currency=d.account_currency, company=self.doc.company)
818
819 if not d.exchange_rate:
820 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
821
822 def set_amounts_in_company_currency(self):
823 for d in self.doc.get(self.tax_field):
824 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530825 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))