blob: 220c876cc2177b929edcb286e869ae18f02ef648 [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
Nabin Haitfe81da22015-02-18 12:23:18 +053018 self.calculate()
19
Nabin Hait3237c752015-02-17 11:11:11 +053020 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053021 if not len(self.doc.get("items")):
22 return
23
Nabin Hait3237c752015-02-17 11:11:11 +053024 self.discount_amount_applied = False
25 self._calculate()
26
27 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053028 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053029 self.apply_discount_amount()
30
Nabin Haitbd00e812015-02-17 12:50:51 +053031 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053032 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053033
Nabin Hait852cb642017-07-05 12:58:19 +053034 if self.doc.meta.get_field("other_charges_calculation"):
35 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053036
37 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053038 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053039 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053040 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053041 self.initialize_taxes()
42 self.determine_exclusive_rate()
43 self.calculate_net_total()
44 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053045 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053046 self.calculate_totals()
47 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053048 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053049
Deepesh Gargef0d26c2020-01-06 15:34:15 +053050 def validate_item_tax_template(self):
51 for item in self.doc.get('items'):
52 if item.item_code and item.get('item_tax_template'):
53 item_doc = frappe.get_cached_doc("Item", item.item_code)
54 args = {
55 'tax_category': self.doc.get('tax_category'),
56 'posting_date': self.doc.get('posting_date'),
57 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050058 'transaction_date': self.doc.get('transaction_date'),
59 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053060 }
61
62 item_group = item_doc.item_group
63 item_group_taxes = []
64
65 while item_group:
66 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
67 item_group_taxes += item_group_doc.taxes or []
68 item_group = item_group_doc.parent_item_group
69
70 item_taxes = item_doc.taxes or []
71
72 if not item_group_taxes and (not item_taxes):
73 # No validation if no taxes in item or item group
74 continue
75
76 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
77
78 if item.item_tax_template not in taxes:
79 frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
80 item.idx, frappe.bold(item.item_code)
81 ))
82
Nabin Haite7679702015-02-20 14:40:35 +053083 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053084 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053085 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +053086 if not self.doc.currency or self.doc.currency == company_currency:
87 self.doc.currency = company_currency
88 self.doc.conversion_rate = 1.0
89 else:
90 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
91 self.doc.meta.get_label("conversion_rate"), self.doc.company)
92
93 self.doc.conversion_rate = flt(self.doc.conversion_rate)
94
Nabin Hait3237c752015-02-17 11:11:11 +053095 def calculate_item_values(self):
96 if not self.discount_amount_applied:
97 for item in self.doc.get("items"):
98 self.doc.round_floats_in(item)
99
100 if item.discount_percentage == 100:
101 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530102 elif item.price_list_rate:
103 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
104 item.rate = flt(item.price_list_rate *
105 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
106 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
107 elif item.discount_amount and item.pricing_rules:
108 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530109
Nabin Haitd46b2362021-02-23 16:38:52 +0530110 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530111 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530112 if flt(item.rate_with_margin) > 0:
113 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Rohit Waghchaurecd8422a2021-03-06 22:08:08 +0530114 if not item.discount_amount:
115 item.discount_amount = item.rate_with_margin - item.rate
116 elif not item.discount_percentage:
117 item.rate -= item.discount_amount
Nabin Hait64bfdd92019-04-23 13:37:19 +0530118 elif flt(item.price_list_rate) > 0:
119 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530120 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
121 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530122
Nabin Haite7679702015-02-20 14:40:35 +0530123 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530124
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530125 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530126 item.amount = flt(-1 * item.rate, item.precision("amount"))
127 else:
128 item.amount = flt(item.rate * item.qty, item.precision("amount"))
129
Nabin Haite7679702015-02-20 14:40:35 +0530130 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530131
Nabin Haite7679702015-02-20 14:40:35 +0530132 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530133
Nabin Haite7679702015-02-20 14:40:35 +0530134 item.item_tax_amount = 0.0
135
136 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530137 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530138 for f in fields:
139 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
140 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530141
142 def initialize_taxes(self):
143 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530144 if not self.discount_amount_applied:
145 validate_taxes_and_charges(tax)
146 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530147
Nabin Hait3237c752015-02-17 11:11:11 +0530148 tax.item_wise_tax_detail = {}
149 tax_fields = ["total", "tax_amount_after_discount_amount",
150 "tax_amount_for_current_item", "grand_total_for_current_item",
151 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
152
Nabin Haitde9c8a92015-02-23 01:06:00 +0530153 if tax.charge_type != "Actual" and \
154 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
155 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530156
157 for fieldname in tax_fields:
158 tax.set(fieldname, 0.0)
159
Nabin Hait3237c752015-02-17 11:11:11 +0530160 self.doc.round_floats_in(tax)
161
Nabin Hait3237c752015-02-17 11:11:11 +0530162 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530163 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
164 return
Nabin Hait3237c752015-02-17 11:11:11 +0530165
166 for item in self.doc.get("items"):
167 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
168 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530169 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530170 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530171 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 +0530172
173 if i==0:
174 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
175 else:
176 tax.grand_total_fraction_for_current_item = \
177 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
178 + tax.tax_fraction_for_current_item
179
180 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530181 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530182
Nabin Hait19ea7212020-08-11 20:34:57 +0530183 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
184 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
185
186 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530187 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530188 item.discount_percentage = flt(item.discount_percentage,
189 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530190
Nabin Haite7679702015-02-20 14:40:35 +0530191 self._set_in_company_currency(item, ["net_rate", "net_amount"])
192
Nabin Hait3237c752015-02-17 11:11:11 +0530193 def _load_item_tax_rate(self, item_tax_rate):
194 return json.loads(item_tax_rate) if item_tax_rate else {}
195
196 def get_current_tax_fraction(self, tax, item_tax_map):
197 """
198 Get tax fraction for calculating tax exclusive amount
199 from tax inclusive amount
200 """
201 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530202 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530203
204 if cint(tax.included_in_print_rate):
205 tax_rate = self._get_tax_rate(tax, item_tax_map)
206
207 if tax.charge_type == "On Net Total":
208 current_tax_fraction = tax_rate / 100.0
209
210 elif tax.charge_type == "On Previous Row Amount":
211 current_tax_fraction = (tax_rate / 100.0) * \
212 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
213
214 elif tax.charge_type == "On Previous Row Total":
215 current_tax_fraction = (tax_rate / 100.0) * \
216 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530217
Nabin Hait19ea7212020-08-11 20:34:57 +0530218 elif tax.charge_type == "On Item Quantity":
219 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530220
Nabin Hait19ea7212020-08-11 20:34:57 +0530221 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
222 current_tax_fraction *= -1.0
223 inclusive_tax_amount_per_qty *= -1.0
224
225 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530226
227 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530228 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530229 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
230 else:
231 return tax.rate
232
233 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530234 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 +0530235
Nabin Hait3237c752015-02-17 11:11:11 +0530236 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530237 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530238 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530239 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530240 self.doc.net_total += item.net_amount
241 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530242
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530243 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530244
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530245 if self.doc.doctype == 'Sales Invoice' and self.doc.is_pos:
246 self.doc.pos_total_qty = self.doc.total_qty
247
Nabin Hait3237c752015-02-17 11:11:11 +0530248 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530249 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530250 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530251 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530252 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
253
254 for n, item in enumerate(self.doc.get("items")):
255 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530256 for i, tax in enumerate(self.doc.get("taxes")):
257 # tax_amount represents the amount of tax for the current step
258 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
259
260 # Adjust divisional loss to the last item
261 if tax.charge_type == "Actual":
262 actual_tax_dict[tax.idx] -= current_tax_amount
263 if n == len(self.doc.get("items")) - 1:
264 current_tax_amount += actual_tax_dict[tax.idx]
265
Nabin Hait2b019ed2015-02-22 23:03:07 +0530266 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530267 if tax.charge_type != "Actual" and \
268 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
269 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530270
Nabin Hait3237c752015-02-17 11:11:11 +0530271 # store tax_amount for current item as it will be used for
272 # charge type = 'On Previous Row Amount'
273 tax.tax_amount_for_current_item = current_tax_amount
274
Nabin Hait2b019ed2015-02-22 23:03:07 +0530275 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530276 tax.tax_amount_after_discount_amount += current_tax_amount
277
Nabin Haitcd951342017-07-31 18:07:45 +0530278 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530279
Nabin Hait3237c752015-02-17 11:11:11 +0530280 # note: grand_total_for_current_item contains the contribution of
281 # item's amount, previously applied tax and the current tax on that item
282 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530283 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530284 else:
285 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530286 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530287
288 # set precision in the last item iteration
289 if n == len(self.doc.get("items")) - 1:
290 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530291 self.set_cumulative_total(i, tax)
292
293 self._set_in_company_currency(tax,
294 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530295
Nabin Hait3237c752015-02-17 11:11:11 +0530296 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530297 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530298 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530299 self.doc.rounding_adjustment = flt(self.doc.grand_total
300 - flt(self.doc.discount_amount) - tax.total,
301 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530302
Nabin Haitcd951342017-07-31 18:07:45 +0530303 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
304 # if just for valuation, do not add the tax amount in total
305 # if tax/charges is for deduction, multiply by -1
306 if getattr(tax, "category", None):
307 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530308 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
309 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530310 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530311
Nabin Haitcd951342017-07-31 18:07:45 +0530312 def set_cumulative_total(self, row_idx, tax):
313 tax_amount = tax.tax_amount_after_discount_amount
314 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
315
316 if row_idx == 0:
317 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
318 else:
319 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530320
321 def get_current_tax_amount(self, item, tax, item_tax_map):
322 tax_rate = self._get_tax_rate(tax, item_tax_map)
323 current_tax_amount = 0.0
324
325 if tax.charge_type == "Actual":
326 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530327 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
328 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
329
Nabin Hait3237c752015-02-17 11:11:11 +0530330 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530331 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530332 elif tax.charge_type == "On Previous Row Amount":
333 current_tax_amount = (tax_rate / 100.0) * \
334 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
335 elif tax.charge_type == "On Previous Row Total":
336 current_tax_amount = (tax_rate / 100.0) * \
337 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530338 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530339 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530340
Nabin Haite7679702015-02-20 14:40:35 +0530341 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530342
343 return current_tax_amount
344
Nabin Haite7679702015-02-20 14:40:35 +0530345 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
346 # store tax breakup for each item
347 key = item.item_code or item.item_name
348 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
349 if tax.item_wise_tax_detail.get(key):
350 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
351
Nabin Haitcaab5822017-08-24 16:22:28 +0530352 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530353
Nabin Hait3237c752015-02-17 11:11:11 +0530354 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530355 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530356 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530357 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530358
Nabin Haita1bf43b2015-03-17 10:50:47 +0530359 def manipulate_grand_total_for_inclusive_tax(self):
360 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530361 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 +0530362 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530363 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
364 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530365
Nabin Hait2e4de832017-09-19 14:53:16 +0530366 diff = self.doc.total + non_inclusive_tax_amount \
367 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530368
369 # If discount amount applied, deduct the discount amount
370 # because self.doc.total is always without discount, but last_tax.total is after discount
371 if self.discount_amount_applied and self.doc.discount_amount:
372 diff -= flt(self.doc.discount_amount)
373
374 diff = flt(diff, self.doc.precision("rounding_adjustment"))
375
Nabin Hait2e4de832017-09-19 14:53:16 +0530376 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530377 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530378
379 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530380 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
381 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530382
Nabin Hait2e4de832017-09-19 14:53:16 +0530383 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
384 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530385
Nabin Hait2e4de832017-09-19 14:53:16 +0530386 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530387
Saqiba6f98d42020-07-23 18:51:26 +0530388 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530389 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 +0530390 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530391 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530392 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530393 for tax in self.doc.get("taxes"):
394 if tax.category in ["Valuation and Total", "Total"]:
395 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530396 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530397 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530398 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530399
Nabin Haite7679702015-02-20 14:40:35 +0530400 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530401
Nabin Haite7679702015-02-20 14:40:35 +0530402 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
403 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
404 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530405
Nabin Hait2e4de832017-09-19 14:53:16 +0530406 self._set_in_company_currency(self.doc,
407 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530408
Nabin Haite7679702015-02-20 14:40:35 +0530409 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530410
Nabin Hait2e4de832017-09-19 14:53:16 +0530411 self.set_rounded_total()
412
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530413 def calculate_total_net_weight(self):
414 if self.doc.meta.get_field('total_net_weight'):
415 self.doc.total_net_weight = 0.0
416 for d in self.doc.items:
417 if d.total_weight:
418 self.doc.total_net_weight += d.total_weight
419
Nabin Hait2e4de832017-09-19 14:53:16 +0530420 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530421 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530422 if self.doc.is_rounded_total_disabled():
423 self.doc.rounded_total = self.doc.base_rounded_total = 0
424 return
425
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530426 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530427 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530428
Nabin Hait877e1bb2017-11-17 12:27:43 +0530429 #if print_in_rate is set, we would have already calculated rounding adjustment
430 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
431 self.doc.precision("rounding_adjustment"))
432
Nabin Hait02ac9012017-11-22 16:12:20 +0530433 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530434
Nabin Hait3237c752015-02-17 11:11:11 +0530435 def _cleanup(self):
436 for tax in self.doc.get("taxes"):
437 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530438
Nabin Hait3769d872015-12-18 13:12:02 +0530439 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530440 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530441 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530442 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530443
444 def apply_discount_amount(self):
445 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530446 if not self.doc.apply_discount_on:
447 frappe.throw(_("Please select Apply Discount On"))
448
Nabin Hait3237c752015-02-17 11:11:11 +0530449 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
450 self.doc.precision("base_discount_amount"))
451
Nabin Haite7679702015-02-20 14:40:35 +0530452 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530453 taxes = self.doc.get("taxes")
454 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530455
Nabin Haite7679702015-02-20 14:40:35 +0530456 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530457 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530458 for i, item in enumerate(self.doc.get("items")):
459 distributed_amount = flt(self.doc.discount_amount) * \
460 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530461
Nabin Haite7679702015-02-20 14:40:35 +0530462 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530463 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530464
Nabin Hait25bd84d2015-03-04 15:06:56 +0530465 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530466 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 +0530467 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530468 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530469 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530470
Anand Doshiec5ec602015-03-05 19:31:23 +0530471 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530472 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530473
Nabin Hait51e980d2015-10-10 18:10:05 +0530474 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 +0530475
Nabin Haite7679702015-02-20 14:40:35 +0530476 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530477
478 self.discount_amount_applied = True
479 self._calculate()
480 else:
481 self.doc.base_discount_amount = 0
482
Nabin Haite7679702015-02-20 14:40:35 +0530483 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530484 if self.doc.apply_discount_on == "Net Total":
485 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530486 else:
487 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530488
Nabin Haite7679702015-02-20 14:40:35 +0530489 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530490 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530491 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
492 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530493 elif tax.row_id in actual_taxes_dict:
494 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
495 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530496
Nabin Hait877e1bb2017-11-17 12:27:43 +0530497 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
498 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530499
500
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530501 def calculate_total_advance(self):
502 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530503 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530504 for adv in self.doc.get("advances")])
505
Nabin Haite7679702015-02-20 14:40:35 +0530506 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530507
Faris Ansari6041f5c2018-02-08 13:33:52 +0530508 grand_total = self.doc.rounded_total or self.doc.grand_total
509
Nabin Hait289ffb72016-02-08 11:06:55 +0530510 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530511 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530512 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530513 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530514 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530515 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530516 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530517 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530518
Nabin Haitadc09232016-02-09 10:31:11 +0530519 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530520 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
521 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530522
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530523 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530524 self.calculate_outstanding_amount()
525
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530526 def is_internal_invoice(self):
527 """
528 Checks if its an internal transfer invoice
529 and decides if to calculate any out standing amount or not
530 """
531
532 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
533 return True
534
535 return False
536
Nabin Hait3237c752015-02-17 11:11:11 +0530537 def calculate_outstanding_amount(self):
538 # NOTE:
539 # write_off_amount is only for POS Invoice
540 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530541 if self.doc.doctype == "Sales Invoice":
542 self.calculate_paid_amount()
543
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530544 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
545 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530546
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530547 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530548 self._set_in_company_currency(self.doc, ['write_off_amount'])
549
Nabin Hait877e1bb2017-11-17 12:27:43 +0530550 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
551 grand_total = self.doc.rounded_total or self.doc.grand_total
552 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530553 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530554 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
555 else:
556 total_amount_to_pay = flt(flt(grand_total *
557 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
558 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530559
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530560 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530561 change_amount = 0
562
Deepesh Garg0ebace52020-02-25 13:21:16 +0530563 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530564 self.calculate_write_off_amount()
565 self.calculate_change_amount()
566 change_amount = self.doc.change_amount \
567 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
568
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530569 paid_amount = self.doc.paid_amount \
570 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530571
Nabin Hait877e1bb2017-11-17 12:27:43 +0530572 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
573 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530574
Deepesh Garg0ebace52020-02-25 13:21:16 +0530575 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
576 self.update_paid_amount_for_return(total_amount_to_pay)
577
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530578 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530579
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530580 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530581
582 if self.doc.is_pos:
583 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530584 payment.amount = flt(payment.amount)
585 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530586 paid_amount += payment.amount
587 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530588 elif not self.doc.is_return:
589 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530590
Manas Solankida486ee2018-07-06 12:36:57 +0530591 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
592 base_paid_amount += self.doc.loyalty_amount
593 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
594
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530595 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
596 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
597
Nabin Hait3bb1a422016-08-02 16:41:10 +0530598 def calculate_change_amount(self):
599 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530600 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530601
602 if self.doc.doctype == "Sales Invoice" \
603 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530604 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530605 grand_total = self.doc.rounded_total or self.doc.grand_total
606 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530607
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530608 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530609 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530610
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530611 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530612 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530613
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530614 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530615 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530616 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
617 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530618 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
619 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530620
mbauskar36b51892016-01-18 16:31:10 +0530621 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530622 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530623 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530624 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530625 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530626 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530627 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530628 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530629
Rohit Waghchaure5c284162021-02-01 22:58:22 +0530630 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
631 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530632 item.margin_type = pricing_rule.margin_type
633 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530634 has_margin = True
635
636 if not has_margin:
637 item.margin_type = None
638 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530639
mbauskara52472c2016-03-05 15:10:25 +0530640 if item.margin_type and item.margin_rate_or_amount:
641 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 +0530642 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530643 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530644
Shreya Shahbe690ef2017-11-14 17:22:41 +0530645 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530646
647 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530648 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530649
Deepesh Garg0ebace52020-02-25 13:21:16 +0530650 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530651 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
652 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530653
654 self.doc.payments = []
655
656 if default_mode_of_payment:
657 self.doc.append('payments', {
658 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530659 'amount': total_amount_to_pay,
660 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530661 })
662 else:
663 self.doc.is_pos = 0
664 self.doc.pos_profile = ''
665
666 self.calculate_paid_amount()
667
668
Nabin Hait9c421612017-07-20 13:32:01 +0530669def get_itemised_tax_breakup_html(doc):
670 if not doc.taxes:
671 return
672 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530673
Nabin Hait9c421612017-07-20 13:32:01 +0530674 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530675 tax_accounts = []
676 for tax in doc.taxes:
677 if getattr(tax, "category", None) and tax.category=="Valuation":
678 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530679 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530680 tax_accounts.append(tax.description)
681
Nabin Hait9c421612017-07-20 13:32:01 +0530682 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530683
Nabin Hait9c421612017-07-20 13:32:01 +0530684 # get tax breakup data
685 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530686
687 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
688
rohitwaghchaured4526682017-12-28 14:20:13 +0530689 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530690 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530691
Nabin Hait9c421612017-07-20 13:32:01 +0530692 return frappe.render_template(
693 "templates/includes/itemised_tax_breakup.html", dict(
694 headers=headers,
695 itemised_tax=itemised_tax,
696 itemised_taxable_amount=itemised_taxable_amount,
697 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530698 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530699 )
Nabin Hait9c421612017-07-20 13:32:01 +0530700 )
Nabin Hait852cb642017-07-05 12:58:19 +0530701
rohitwaghchaured4526682017-12-28 14:20:13 +0530702
703@erpnext.allow_regional
704def update_itemised_tax_data(doc):
705 #Don't delete this method, used for localization
706 pass
707
Nabin Haitb962fc12017-07-17 18:02:31 +0530708@erpnext.allow_regional
709def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
710 return [_("Item"), _("Taxable Amount")] + tax_accounts
711
712@erpnext.allow_regional
713def get_itemised_tax_breakup_data(doc):
714 itemised_tax = get_itemised_tax(doc.taxes)
715
716 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
717
718 return itemised_tax, itemised_taxable_amount
719
Nabin Hait34c551d2019-07-03 10:34:31 +0530720def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530721 itemised_tax = {}
722 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530723 if getattr(tax, "category", None) and tax.category=="Valuation":
724 continue
725
Nabin Haitb962fc12017-07-17 18:02:31 +0530726 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530727 if item_tax_map:
728 for item_code, tax_data in item_tax_map.items():
729 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530730
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530731 tax_rate = 0.0
732 tax_amount = 0.0
733
Nabin Hait2e4de832017-09-19 14:53:16 +0530734 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530735 tax_rate = flt(tax_data[0])
736 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530737 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530738 tax_rate = flt(tax_data)
739
740 itemised_tax[item_code][tax.description] = frappe._dict(dict(
741 tax_rate = tax_rate,
742 tax_amount = tax_amount
743 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530744
Nabin Hait34c551d2019-07-03 10:34:31 +0530745 if with_tax_account:
746 itemised_tax[item_code][tax.description].tax_account = tax.account_head
747
Nabin Haitb962fc12017-07-17 18:02:31 +0530748 return itemised_tax
749
750def get_itemised_taxable_amount(items):
751 itemised_taxable_amount = frappe._dict()
752 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530753 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530754 itemised_taxable_amount.setdefault(item_code, 0)
755 itemised_taxable_amount[item_code] += item.net_amount
756
Nabin Haitcaab5822017-08-24 16:22:28 +0530757 return itemised_taxable_amount
758
759def get_rounded_tax_amount(itemised_tax, precision):
760 # Rounding based on tax_amount precision
761 for taxes in itemised_tax.values():
762 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530763 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530764
765class init_landed_taxes_and_totals(object):
766 def __init__(self, doc):
767 self.doc = doc
768 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
769 self.set_account_currency()
770 self.set_exchange_rate()
771 self.set_amounts_in_company_currency()
772
773 def set_account_currency(self):
774 company_currency = erpnext.get_company_currency(self.doc.company)
775 for d in self.doc.get(self.tax_field):
776 if not d.account_currency:
777 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
778 d.account_currency = account_currency or company_currency
779
780 def set_exchange_rate(self):
781 company_currency = erpnext.get_company_currency(self.doc.company)
782 for d in self.doc.get(self.tax_field):
783 if d.account_currency == company_currency:
784 d.exchange_rate = 1
Deepesh Gargb75cbee2021-03-17 10:56:52 +0530785 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530786 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
787 account_currency=d.account_currency, company=self.doc.company)
788
789 if not d.exchange_rate:
790 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
791
792 def set_amounts_in_company_currency(self):
793 for d in self.doc.get(self.tax_field):
794 d.amount = flt(d.amount, d.precision("amount"))
795 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))