blob: c33e556c259dbb41b2c6eb57ff3c91ae42c4697c [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 = {
57 'tax_category': self.doc.get('tax_category'),
58 'posting_date': self.doc.get('posting_date'),
59 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050060 'transaction_date': self.doc.get('transaction_date'),
61 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053062 }
63
64 item_group = item_doc.item_group
65 item_group_taxes = []
66
67 while item_group:
68 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
69 item_group_taxes += item_group_doc.taxes or []
70 item_group = item_group_doc.parent_item_group
71
72 item_taxes = item_doc.taxes or []
73
74 if not item_group_taxes and (not item_taxes):
75 # No validation if no taxes in item or item group
76 continue
77
78 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
79
80 if item.item_tax_template not in taxes:
81 frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
82 item.idx, frappe.bold(item.item_code)
83 ))
84
Nabin Haite7679702015-02-20 14:40:35 +053085 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053086 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053087 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +053088 if not self.doc.currency or self.doc.currency == company_currency:
89 self.doc.currency = company_currency
90 self.doc.conversion_rate = 1.0
91 else:
92 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
93 self.doc.meta.get_label("conversion_rate"), self.doc.company)
94
95 self.doc.conversion_rate = flt(self.doc.conversion_rate)
96
Nabin Hait3237c752015-02-17 11:11:11 +053097 def calculate_item_values(self):
98 if not self.discount_amount_applied:
99 for item in self.doc.get("items"):
100 self.doc.round_floats_in(item)
101
102 if item.discount_percentage == 100:
103 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530104 elif item.price_list_rate:
105 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
106 item.rate = flt(item.price_list_rate *
107 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
108 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
109 elif item.discount_amount and item.pricing_rules:
110 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530111
Anupam Kumared42afc2021-03-15 11:11:28 +0530112 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 +0530113 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530114 if flt(item.rate_with_margin) > 0:
115 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
116 item.discount_amount = item.rate_with_margin - item.rate
117 elif flt(item.price_list_rate) > 0:
118 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530119 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
120 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530121
Nabin Haite7679702015-02-20 14:40:35 +0530122 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530123
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530124 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530125 item.amount = flt(-1 * item.rate, item.precision("amount"))
126 else:
127 item.amount = flt(item.rate * item.qty, item.precision("amount"))
128
Nabin Haite7679702015-02-20 14:40:35 +0530129 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530130
Nabin Haite7679702015-02-20 14:40:35 +0530131 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530132
Nabin Haite7679702015-02-20 14:40:35 +0530133 item.item_tax_amount = 0.0
134
135 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530136 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530137 for f in fields:
138 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
139 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530140
141 def initialize_taxes(self):
142 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530143 if not self.discount_amount_applied:
144 validate_taxes_and_charges(tax)
145 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530146
Nabin Hait3237c752015-02-17 11:11:11 +0530147 tax.item_wise_tax_detail = {}
148 tax_fields = ["total", "tax_amount_after_discount_amount",
149 "tax_amount_for_current_item", "grand_total_for_current_item",
150 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
151
Nabin Haitde9c8a92015-02-23 01:06:00 +0530152 if tax.charge_type != "Actual" and \
153 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
154 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530155
156 for fieldname in tax_fields:
157 tax.set(fieldname, 0.0)
158
Nabin Hait3237c752015-02-17 11:11:11 +0530159 self.doc.round_floats_in(tax)
160
Nabin Hait3237c752015-02-17 11:11:11 +0530161 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530162 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
163 return
Nabin Hait3237c752015-02-17 11:11:11 +0530164
165 for item in self.doc.get("items"):
166 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
167 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530168 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530169 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530170 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 +0530171
172 if i==0:
173 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
174 else:
175 tax.grand_total_fraction_for_current_item = \
176 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
177 + tax.tax_fraction_for_current_item
178
179 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530180 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530181
Nabin Hait19ea7212020-08-11 20:34:57 +0530182 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
183 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
184
185 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530186 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530187 item.discount_percentage = flt(item.discount_percentage,
188 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530189
Nabin Haite7679702015-02-20 14:40:35 +0530190 self._set_in_company_currency(item, ["net_rate", "net_amount"])
191
Nabin Hait3237c752015-02-17 11:11:11 +0530192 def _load_item_tax_rate(self, item_tax_rate):
193 return json.loads(item_tax_rate) if item_tax_rate else {}
194
195 def get_current_tax_fraction(self, tax, item_tax_map):
196 """
197 Get tax fraction for calculating tax exclusive amount
198 from tax inclusive amount
199 """
200 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530201 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530202
203 if cint(tax.included_in_print_rate):
204 tax_rate = self._get_tax_rate(tax, item_tax_map)
205
206 if tax.charge_type == "On Net Total":
207 current_tax_fraction = tax_rate / 100.0
208
209 elif tax.charge_type == "On Previous Row Amount":
210 current_tax_fraction = (tax_rate / 100.0) * \
211 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
212
213 elif tax.charge_type == "On Previous Row Total":
214 current_tax_fraction = (tax_rate / 100.0) * \
215 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530216
Nabin Hait19ea7212020-08-11 20:34:57 +0530217 elif tax.charge_type == "On Item Quantity":
218 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530219
Nabin Hait19ea7212020-08-11 20:34:57 +0530220 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
221 current_tax_fraction *= -1.0
222 inclusive_tax_amount_per_qty *= -1.0
223
224 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530225
226 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530227 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530228 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
229 else:
230 return tax.rate
231
232 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530233 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 +0530234
Nabin Hait3237c752015-02-17 11:11:11 +0530235 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530236 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530237 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530238 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530239 self.doc.net_total += item.net_amount
240 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530241
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530242 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530243
244 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530245 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530246 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530247 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530248 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
249
250 for n, item in enumerate(self.doc.get("items")):
251 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530252 for i, tax in enumerate(self.doc.get("taxes")):
253 # tax_amount represents the amount of tax for the current step
254 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
255
256 # Adjust divisional loss to the last item
257 if tax.charge_type == "Actual":
258 actual_tax_dict[tax.idx] -= current_tax_amount
259 if n == len(self.doc.get("items")) - 1:
260 current_tax_amount += actual_tax_dict[tax.idx]
261
Nabin Hait2b019ed2015-02-22 23:03:07 +0530262 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530263 if tax.charge_type != "Actual" and \
264 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
265 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530266
Nabin Hait3237c752015-02-17 11:11:11 +0530267 # store tax_amount for current item as it will be used for
268 # charge type = 'On Previous Row Amount'
269 tax.tax_amount_for_current_item = current_tax_amount
270
Nabin Hait2b019ed2015-02-22 23:03:07 +0530271 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530272 tax.tax_amount_after_discount_amount += current_tax_amount
273
Nabin Haitcd951342017-07-31 18:07:45 +0530274 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530275
Nabin Hait3237c752015-02-17 11:11:11 +0530276 # note: grand_total_for_current_item contains the contribution of
277 # item's amount, previously applied tax and the current tax on that item
278 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530279 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530280 else:
281 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530282 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530283
284 # set precision in the last item iteration
285 if n == len(self.doc.get("items")) - 1:
286 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530287 self.set_cumulative_total(i, tax)
288
289 self._set_in_company_currency(tax,
290 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530291
Nabin Hait3237c752015-02-17 11:11:11 +0530292 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530293 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530294 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530295 self.doc.rounding_adjustment = flt(self.doc.grand_total
296 - flt(self.doc.discount_amount) - tax.total,
297 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530298
Nabin Haitcd951342017-07-31 18:07:45 +0530299 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
300 # if just for valuation, do not add the tax amount in total
301 # if tax/charges is for deduction, multiply by -1
302 if getattr(tax, "category", None):
303 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530304 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
305 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530306 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530307
Nabin Haitcd951342017-07-31 18:07:45 +0530308 def set_cumulative_total(self, row_idx, tax):
309 tax_amount = tax.tax_amount_after_discount_amount
310 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
311
312 if row_idx == 0:
313 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
314 else:
315 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530316
317 def get_current_tax_amount(self, item, tax, item_tax_map):
318 tax_rate = self._get_tax_rate(tax, item_tax_map)
319 current_tax_amount = 0.0
320
321 if tax.charge_type == "Actual":
322 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530323 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
324 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
325
Nabin Hait3237c752015-02-17 11:11:11 +0530326 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530327 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530328 elif tax.charge_type == "On Previous Row Amount":
329 current_tax_amount = (tax_rate / 100.0) * \
330 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
331 elif tax.charge_type == "On Previous Row Total":
332 current_tax_amount = (tax_rate / 100.0) * \
333 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530334 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530335 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530336
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530337 current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530338 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530339
340 return current_tax_amount
341
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530342 def get_final_current_tax_amount(self, tax, current_tax_amount):
343 # Some countries need individual tax components to be rounded
344 # Handeled via regional doctypess
345 if tax.account_head in frappe.flags.round_off_applicable_accounts:
346 current_tax_amount = round(current_tax_amount, 0)
347 return current_tax_amount
348
Nabin Haite7679702015-02-20 14:40:35 +0530349 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
350 # store tax breakup for each item
351 key = item.item_code or item.item_name
352 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
353 if tax.item_wise_tax_detail.get(key):
354 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
355
Nabin Haitcaab5822017-08-24 16:22:28 +0530356 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530357
Nabin Hait3237c752015-02-17 11:11:11 +0530358 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530359 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530360 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530361 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530362
Nabin Haita1bf43b2015-03-17 10:50:47 +0530363 def manipulate_grand_total_for_inclusive_tax(self):
364 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530365 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 +0530366 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530367 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
368 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530369
Nabin Hait2e4de832017-09-19 14:53:16 +0530370 diff = self.doc.total + non_inclusive_tax_amount \
371 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530372
373 # If discount amount applied, deduct the discount amount
374 # because self.doc.total is always without discount, but last_tax.total is after discount
375 if self.discount_amount_applied and self.doc.discount_amount:
376 diff -= flt(self.doc.discount_amount)
377
378 diff = flt(diff, self.doc.precision("rounding_adjustment"))
379
Nabin Hait2e4de832017-09-19 14:53:16 +0530380 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530381 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530382
383 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530384 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
385 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530386
Nabin Hait2e4de832017-09-19 14:53:16 +0530387 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
388 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530389
Nabin Hait2e4de832017-09-19 14:53:16 +0530390 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530391
Saqiba6f98d42020-07-23 18:51:26 +0530392 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530393 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 +0530394 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530395 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530396 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530397 for tax in self.doc.get("taxes"):
398 if tax.category in ["Valuation and Total", "Total"]:
399 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530400 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530401 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530402 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530403
Nabin Haite7679702015-02-20 14:40:35 +0530404 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530405
Nabin Haite7679702015-02-20 14:40:35 +0530406 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
407 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
408 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530409
Nabin Hait2e4de832017-09-19 14:53:16 +0530410 self._set_in_company_currency(self.doc,
411 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530412
Nabin Haite7679702015-02-20 14:40:35 +0530413 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530414
Nabin Hait2e4de832017-09-19 14:53:16 +0530415 self.set_rounded_total()
416
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530417 def calculate_total_net_weight(self):
418 if self.doc.meta.get_field('total_net_weight'):
419 self.doc.total_net_weight = 0.0
420 for d in self.doc.items:
421 if d.total_weight:
422 self.doc.total_net_weight += d.total_weight
423
Nabin Hait2e4de832017-09-19 14:53:16 +0530424 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530425 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530426 if self.doc.is_rounded_total_disabled():
427 self.doc.rounded_total = self.doc.base_rounded_total = 0
428 return
429
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530430 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530431 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530432
Nabin Hait877e1bb2017-11-17 12:27:43 +0530433 #if print_in_rate is set, we would have already calculated rounding adjustment
434 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
435 self.doc.precision("rounding_adjustment"))
436
Nabin Hait02ac9012017-11-22 16:12:20 +0530437 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530438
Nabin Hait3237c752015-02-17 11:11:11 +0530439 def _cleanup(self):
440 for tax in self.doc.get("taxes"):
441 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530442
Nabin Hait3769d872015-12-18 13:12:02 +0530443 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530444 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530445 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530446 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530447
448 def apply_discount_amount(self):
449 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530450 if not self.doc.apply_discount_on:
451 frappe.throw(_("Please select Apply Discount On"))
452
Nabin Hait3237c752015-02-17 11:11:11 +0530453 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
454 self.doc.precision("base_discount_amount"))
455
Nabin Haite7679702015-02-20 14:40:35 +0530456 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530457 taxes = self.doc.get("taxes")
458 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530459
Nabin Haite7679702015-02-20 14:40:35 +0530460 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530461 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530462 for i, item in enumerate(self.doc.get("items")):
463 distributed_amount = flt(self.doc.discount_amount) * \
464 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530465
Nabin Haite7679702015-02-20 14:40:35 +0530466 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530467 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530468
Nabin Hait25bd84d2015-03-04 15:06:56 +0530469 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530470 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 +0530471 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530472 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530473 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530474
Anand Doshiec5ec602015-03-05 19:31:23 +0530475 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530476 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530477
Nabin Hait51e980d2015-10-10 18:10:05 +0530478 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 +0530479
Nabin Haite7679702015-02-20 14:40:35 +0530480 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530481
482 self.discount_amount_applied = True
483 self._calculate()
484 else:
485 self.doc.base_discount_amount = 0
486
Nabin Haite7679702015-02-20 14:40:35 +0530487 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530488 if self.doc.apply_discount_on == "Net Total":
489 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530490 else:
491 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530492
Nabin Haite7679702015-02-20 14:40:35 +0530493 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530494 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530495 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
496 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530497 elif tax.row_id in actual_taxes_dict:
498 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
499 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530500
Nabin Hait877e1bb2017-11-17 12:27:43 +0530501 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
502 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530503
504
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530505 def calculate_total_advance(self):
506 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530507 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530508 for adv in self.doc.get("advances")])
509
Nabin Haite7679702015-02-20 14:40:35 +0530510 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530511
Faris Ansari6041f5c2018-02-08 13:33:52 +0530512 grand_total = self.doc.rounded_total or self.doc.grand_total
513
Nabin Hait289ffb72016-02-08 11:06:55 +0530514 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530515 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530516 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530517 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530518 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530519 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530520 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530521 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530522
Nabin Haitadc09232016-02-09 10:31:11 +0530523 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530524 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
525 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530526
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530527 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530528 self.calculate_outstanding_amount()
529
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530530 def is_internal_invoice(self):
531 """
532 Checks if its an internal transfer invoice
533 and decides if to calculate any out standing amount or not
534 """
535
536 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
537 return True
538
539 return False
540
Nabin Hait3237c752015-02-17 11:11:11 +0530541 def calculate_outstanding_amount(self):
542 # NOTE:
543 # write_off_amount is only for POS Invoice
544 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530545 if self.doc.doctype == "Sales Invoice":
546 self.calculate_paid_amount()
547
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530548 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
549 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530550
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530551 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530552 self._set_in_company_currency(self.doc, ['write_off_amount'])
553
Nabin Hait877e1bb2017-11-17 12:27:43 +0530554 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
555 grand_total = self.doc.rounded_total or self.doc.grand_total
556 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530557 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530558 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
559 else:
560 total_amount_to_pay = flt(flt(grand_total *
561 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
562 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530563
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530564 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530565 change_amount = 0
566
Deepesh Garg0ebace52020-02-25 13:21:16 +0530567 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530568 self.calculate_write_off_amount()
569 self.calculate_change_amount()
570 change_amount = self.doc.change_amount \
571 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
572
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530573 paid_amount = self.doc.paid_amount \
574 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530575
Nabin Hait877e1bb2017-11-17 12:27:43 +0530576 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
577 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530578
Deepesh Garg0ebace52020-02-25 13:21:16 +0530579 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
580 self.update_paid_amount_for_return(total_amount_to_pay)
581
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530582 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530583
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530584 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530585
586 if self.doc.is_pos:
587 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530588 payment.amount = flt(payment.amount)
589 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530590 paid_amount += payment.amount
591 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530592 elif not self.doc.is_return:
593 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530594
Manas Solankida486ee2018-07-06 12:36:57 +0530595 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
596 base_paid_amount += self.doc.loyalty_amount
597 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
598
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530599 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
600 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
601
Nabin Hait3bb1a422016-08-02 16:41:10 +0530602 def calculate_change_amount(self):
603 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530604 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530605
606 if self.doc.doctype == "Sales Invoice" \
607 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530608 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530609 grand_total = self.doc.rounded_total or self.doc.grand_total
610 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530611
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530612 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530613 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530614
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530615 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530616 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530617
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530618 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530619 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530620 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
621 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530622 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
623 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530624
mbauskar36b51892016-01-18 16:31:10 +0530625 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530626 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530627 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530628 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530629 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530630 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530631 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530632 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530633
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530634 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
635 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530636 item.margin_type = pricing_rule.margin_type
637 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530638 has_margin = True
639
640 if not has_margin:
641 item.margin_type = None
642 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530643
mbauskara52472c2016-03-05 15:10:25 +0530644 if item.margin_type and item.margin_rate_or_amount:
645 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 +0530646 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530647 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530648
Shreya Shahbe690ef2017-11-14 17:22:41 +0530649 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530650
651 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530652 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530653
Deepesh Garg0ebace52020-02-25 13:21:16 +0530654 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530655 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
656 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530657
658 self.doc.payments = []
659
660 if default_mode_of_payment:
661 self.doc.append('payments', {
662 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530663 'amount': total_amount_to_pay,
664 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530665 })
666 else:
667 self.doc.is_pos = 0
668 self.doc.pos_profile = ''
669
670 self.calculate_paid_amount()
671
672
Nabin Hait9c421612017-07-20 13:32:01 +0530673def get_itemised_tax_breakup_html(doc):
674 if not doc.taxes:
675 return
676 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530677
Nabin Hait9c421612017-07-20 13:32:01 +0530678 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530679 tax_accounts = []
680 for tax in doc.taxes:
681 if getattr(tax, "category", None) and tax.category=="Valuation":
682 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530683 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530684 tax_accounts.append(tax.description)
685
Nabin Hait9c421612017-07-20 13:32:01 +0530686 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530687
Nabin Hait9c421612017-07-20 13:32:01 +0530688 # get tax breakup data
689 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530690
691 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
692
rohitwaghchaured4526682017-12-28 14:20:13 +0530693 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530694 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530695
Nabin Hait9c421612017-07-20 13:32:01 +0530696 return frappe.render_template(
697 "templates/includes/itemised_tax_breakup.html", dict(
698 headers=headers,
699 itemised_tax=itemised_tax,
700 itemised_taxable_amount=itemised_taxable_amount,
701 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530702 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530703 )
Nabin Hait9c421612017-07-20 13:32:01 +0530704 )
Nabin Hait852cb642017-07-05 12:58:19 +0530705
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530706@frappe.whitelist()
707def get_round_off_applicable_accounts(company, account_list):
708 account_list = get_regional_round_off_accounts(company, account_list)
709
710 return account_list
711
712@erpnext.allow_regional
713def get_regional_round_off_accounts(company, account_list):
714 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530715
716@erpnext.allow_regional
717def update_itemised_tax_data(doc):
718 #Don't delete this method, used for localization
719 pass
720
Nabin Haitb962fc12017-07-17 18:02:31 +0530721@erpnext.allow_regional
722def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
723 return [_("Item"), _("Taxable Amount")] + tax_accounts
724
725@erpnext.allow_regional
726def get_itemised_tax_breakup_data(doc):
727 itemised_tax = get_itemised_tax(doc.taxes)
728
729 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
730
731 return itemised_tax, itemised_taxable_amount
732
Nabin Hait34c551d2019-07-03 10:34:31 +0530733def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530734 itemised_tax = {}
735 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530736 if getattr(tax, "category", None) and tax.category=="Valuation":
737 continue
738
Nabin Haitb962fc12017-07-17 18:02:31 +0530739 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530740 if item_tax_map:
741 for item_code, tax_data in item_tax_map.items():
742 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530743
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530744 tax_rate = 0.0
745 tax_amount = 0.0
746
Nabin Hait2e4de832017-09-19 14:53:16 +0530747 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530748 tax_rate = flt(tax_data[0])
749 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530750 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530751 tax_rate = flt(tax_data)
752
753 itemised_tax[item_code][tax.description] = frappe._dict(dict(
754 tax_rate = tax_rate,
755 tax_amount = tax_amount
756 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530757
Nabin Hait34c551d2019-07-03 10:34:31 +0530758 if with_tax_account:
759 itemised_tax[item_code][tax.description].tax_account = tax.account_head
760
Nabin Haitb962fc12017-07-17 18:02:31 +0530761 return itemised_tax
762
763def get_itemised_taxable_amount(items):
764 itemised_taxable_amount = frappe._dict()
765 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530766 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530767 itemised_taxable_amount.setdefault(item_code, 0)
768 itemised_taxable_amount[item_code] += item.net_amount
769
Nabin Haitcaab5822017-08-24 16:22:28 +0530770 return itemised_taxable_amount
771
772def get_rounded_tax_amount(itemised_tax, precision):
773 # Rounding based on tax_amount precision
774 for taxes in itemised_tax.values():
775 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530776 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530777
778class init_landed_taxes_and_totals(object):
779 def __init__(self, doc):
780 self.doc = doc
781 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
782 self.set_account_currency()
783 self.set_exchange_rate()
784 self.set_amounts_in_company_currency()
785
786 def set_account_currency(self):
787 company_currency = erpnext.get_company_currency(self.doc.company)
788 for d in self.doc.get(self.tax_field):
789 if not d.account_currency:
790 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
791 d.account_currency = account_currency or company_currency
792
793 def set_exchange_rate(self):
794 company_currency = erpnext.get_company_currency(self.doc.company)
795 for d in self.doc.get(self.tax_field):
796 if d.account_currency == company_currency:
797 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530798 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530799 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
800 account_currency=d.account_currency, company=self.doc.company)
801
802 if not d.exchange_rate:
803 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
804
805 def set_amounts_in_company_currency(self):
806 for d in self.doc.get(self.tax_field):
807 d.amount = flt(d.amount, d.precision("amount"))
808 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))