blob: cfa499191caec5e2fcbcbeb0998c07b8f41bbfb9 [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
Saqibac9e6ff2021-01-28 17:58:55 +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"))
114 item.discount_amount = item.rate_with_margin - item.rate
115 elif flt(item.price_list_rate) > 0:
116 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530117 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
118 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530119
Nabin Haite7679702015-02-20 14:40:35 +0530120 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530121
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530122 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530123 item.amount = flt(-1 * item.rate, item.precision("amount"))
124 else:
125 item.amount = flt(item.rate * item.qty, item.precision("amount"))
126
Nabin Haite7679702015-02-20 14:40:35 +0530127 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530128
Nabin Haite7679702015-02-20 14:40:35 +0530129 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530130
Nabin Haite7679702015-02-20 14:40:35 +0530131 item.item_tax_amount = 0.0
132
133 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530134 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530135 for f in fields:
136 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
137 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530138
139 def initialize_taxes(self):
140 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530141 if not self.discount_amount_applied:
142 validate_taxes_and_charges(tax)
143 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530144
Nabin Hait3237c752015-02-17 11:11:11 +0530145 tax.item_wise_tax_detail = {}
146 tax_fields = ["total", "tax_amount_after_discount_amount",
147 "tax_amount_for_current_item", "grand_total_for_current_item",
148 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
149
Nabin Haitde9c8a92015-02-23 01:06:00 +0530150 if tax.charge_type != "Actual" and \
151 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
152 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530153
154 for fieldname in tax_fields:
155 tax.set(fieldname, 0.0)
156
Nabin Hait3237c752015-02-17 11:11:11 +0530157 self.doc.round_floats_in(tax)
158
Nabin Hait3237c752015-02-17 11:11:11 +0530159 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530160 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
161 return
Nabin Hait3237c752015-02-17 11:11:11 +0530162
163 for item in self.doc.get("items"):
164 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
165 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530166 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530167 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530168 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 +0530169
170 if i==0:
171 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
172 else:
173 tax.grand_total_fraction_for_current_item = \
174 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
175 + tax.tax_fraction_for_current_item
176
177 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530178 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530179
Nabin Hait19ea7212020-08-11 20:34:57 +0530180 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
181 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
182
183 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530184 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530185 item.discount_percentage = flt(item.discount_percentage,
186 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530187
Nabin Haite7679702015-02-20 14:40:35 +0530188 self._set_in_company_currency(item, ["net_rate", "net_amount"])
189
Nabin Hait3237c752015-02-17 11:11:11 +0530190 def _load_item_tax_rate(self, item_tax_rate):
191 return json.loads(item_tax_rate) if item_tax_rate else {}
192
193 def get_current_tax_fraction(self, tax, item_tax_map):
194 """
195 Get tax fraction for calculating tax exclusive amount
196 from tax inclusive amount
197 """
198 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530199 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530200
201 if cint(tax.included_in_print_rate):
202 tax_rate = self._get_tax_rate(tax, item_tax_map)
203
204 if tax.charge_type == "On Net Total":
205 current_tax_fraction = tax_rate / 100.0
206
207 elif tax.charge_type == "On Previous Row Amount":
208 current_tax_fraction = (tax_rate / 100.0) * \
209 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
210
211 elif tax.charge_type == "On Previous Row Total":
212 current_tax_fraction = (tax_rate / 100.0) * \
213 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530214
Nabin Hait19ea7212020-08-11 20:34:57 +0530215 elif tax.charge_type == "On Item Quantity":
216 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530217
Nabin Hait19ea7212020-08-11 20:34:57 +0530218 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
219 current_tax_fraction *= -1.0
220 inclusive_tax_amount_per_qty *= -1.0
221
222 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530223
224 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530225 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530226 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
227 else:
228 return tax.rate
229
230 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530231 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 +0530232
Nabin Hait3237c752015-02-17 11:11:11 +0530233 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530234 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530235 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530236 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530237 self.doc.net_total += item.net_amount
238 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530239
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530240 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530241
242 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530243 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530244 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530245 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530246 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
247
248 for n, item in enumerate(self.doc.get("items")):
249 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530250 for i, tax in enumerate(self.doc.get("taxes")):
251 # tax_amount represents the amount of tax for the current step
252 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
253
254 # Adjust divisional loss to the last item
255 if tax.charge_type == "Actual":
256 actual_tax_dict[tax.idx] -= current_tax_amount
257 if n == len(self.doc.get("items")) - 1:
258 current_tax_amount += actual_tax_dict[tax.idx]
259
Nabin Hait2b019ed2015-02-22 23:03:07 +0530260 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530261 if tax.charge_type != "Actual" and \
262 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
263 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530264
Nabin Hait3237c752015-02-17 11:11:11 +0530265 # store tax_amount for current item as it will be used for
266 # charge type = 'On Previous Row Amount'
267 tax.tax_amount_for_current_item = current_tax_amount
268
Nabin Hait2b019ed2015-02-22 23:03:07 +0530269 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530270 tax.tax_amount_after_discount_amount += current_tax_amount
271
Nabin Haitcd951342017-07-31 18:07:45 +0530272 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530273
Nabin Hait3237c752015-02-17 11:11:11 +0530274 # note: grand_total_for_current_item contains the contribution of
275 # item's amount, previously applied tax and the current tax on that item
276 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530277 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530278 else:
279 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530280 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530281
282 # set precision in the last item iteration
283 if n == len(self.doc.get("items")) - 1:
284 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530285 self.set_cumulative_total(i, tax)
286
287 self._set_in_company_currency(tax,
288 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530289
Nabin Hait3237c752015-02-17 11:11:11 +0530290 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530291 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530292 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530293 self.doc.rounding_adjustment = flt(self.doc.grand_total
294 - flt(self.doc.discount_amount) - tax.total,
295 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530296
Nabin Haitcd951342017-07-31 18:07:45 +0530297 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
298 # if just for valuation, do not add the tax amount in total
299 # if tax/charges is for deduction, multiply by -1
300 if getattr(tax, "category", None):
301 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530302 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
303 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530304 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530305
Nabin Haitcd951342017-07-31 18:07:45 +0530306 def set_cumulative_total(self, row_idx, tax):
307 tax_amount = tax.tax_amount_after_discount_amount
308 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
309
310 if row_idx == 0:
311 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
312 else:
313 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530314
315 def get_current_tax_amount(self, item, tax, item_tax_map):
316 tax_rate = self._get_tax_rate(tax, item_tax_map)
317 current_tax_amount = 0.0
318
319 if tax.charge_type == "Actual":
320 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530321 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
322 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
323
Nabin Hait3237c752015-02-17 11:11:11 +0530324 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530325 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530326 elif tax.charge_type == "On Previous Row Amount":
327 current_tax_amount = (tax_rate / 100.0) * \
328 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
329 elif tax.charge_type == "On Previous Row Total":
330 current_tax_amount = (tax_rate / 100.0) * \
331 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530332 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530333 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530334
Nabin Haite7679702015-02-20 14:40:35 +0530335 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530336
337 return current_tax_amount
338
Nabin Haite7679702015-02-20 14:40:35 +0530339 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
340 # store tax breakup for each item
341 key = item.item_code or item.item_name
342 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
343 if tax.item_wise_tax_detail.get(key):
344 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
345
Nabin Haitcaab5822017-08-24 16:22:28 +0530346 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530347
Nabin Hait3237c752015-02-17 11:11:11 +0530348 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530349 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530350 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530351 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530352
Nabin Haita1bf43b2015-03-17 10:50:47 +0530353 def manipulate_grand_total_for_inclusive_tax(self):
354 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530355 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 +0530356 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530357 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
358 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530359
Nabin Hait2e4de832017-09-19 14:53:16 +0530360 diff = self.doc.total + non_inclusive_tax_amount \
361 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530362
363 # If discount amount applied, deduct the discount amount
364 # because self.doc.total is always without discount, but last_tax.total is after discount
365 if self.discount_amount_applied and self.doc.discount_amount:
366 diff -= flt(self.doc.discount_amount)
367
368 diff = flt(diff, self.doc.precision("rounding_adjustment"))
369
Nabin Hait2e4de832017-09-19 14:53:16 +0530370 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530371 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530372
373 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530374 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
375 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530376
Nabin Hait2e4de832017-09-19 14:53:16 +0530377 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
378 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530379
Nabin Hait2e4de832017-09-19 14:53:16 +0530380 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530381
Saqiba6f98d42020-07-23 18:51:26 +0530382 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530383 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 +0530384 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530385 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530386 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530387 for tax in self.doc.get("taxes"):
388 if tax.category in ["Valuation and Total", "Total"]:
389 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530390 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530391 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530392 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530393
Nabin Haite7679702015-02-20 14:40:35 +0530394 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530395
Nabin Haite7679702015-02-20 14:40:35 +0530396 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
397 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
398 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530399
Nabin Hait2e4de832017-09-19 14:53:16 +0530400 self._set_in_company_currency(self.doc,
401 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530402
Nabin Haite7679702015-02-20 14:40:35 +0530403 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530404
Nabin Hait2e4de832017-09-19 14:53:16 +0530405 self.set_rounded_total()
406
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530407 def calculate_total_net_weight(self):
408 if self.doc.meta.get_field('total_net_weight'):
409 self.doc.total_net_weight = 0.0
410 for d in self.doc.items:
411 if d.total_weight:
412 self.doc.total_net_weight += d.total_weight
413
Nabin Hait2e4de832017-09-19 14:53:16 +0530414 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530415 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530416 if self.doc.is_rounded_total_disabled():
417 self.doc.rounded_total = self.doc.base_rounded_total = 0
418 return
419
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530420 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530421 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530422
Nabin Hait877e1bb2017-11-17 12:27:43 +0530423 #if print_in_rate is set, we would have already calculated rounding adjustment
424 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
425 self.doc.precision("rounding_adjustment"))
426
Nabin Hait02ac9012017-11-22 16:12:20 +0530427 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530428
Nabin Hait3237c752015-02-17 11:11:11 +0530429 def _cleanup(self):
430 for tax in self.doc.get("taxes"):
431 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530432
Nabin Hait3769d872015-12-18 13:12:02 +0530433 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530434 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530435 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530436 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530437
438 def apply_discount_amount(self):
439 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530440 if not self.doc.apply_discount_on:
441 frappe.throw(_("Please select Apply Discount On"))
442
Nabin Hait3237c752015-02-17 11:11:11 +0530443 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
444 self.doc.precision("base_discount_amount"))
445
Nabin Haite7679702015-02-20 14:40:35 +0530446 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530447 taxes = self.doc.get("taxes")
448 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530449
Nabin Haite7679702015-02-20 14:40:35 +0530450 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530451 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530452 for i, item in enumerate(self.doc.get("items")):
453 distributed_amount = flt(self.doc.discount_amount) * \
454 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530455
Nabin Haite7679702015-02-20 14:40:35 +0530456 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530457 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530458
Nabin Hait25bd84d2015-03-04 15:06:56 +0530459 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530460 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 +0530461 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530462 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530463 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530464
Anand Doshiec5ec602015-03-05 19:31:23 +0530465 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530466 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530467
Nabin Hait51e980d2015-10-10 18:10:05 +0530468 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 +0530469
Nabin Haite7679702015-02-20 14:40:35 +0530470 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530471
472 self.discount_amount_applied = True
473 self._calculate()
474 else:
475 self.doc.base_discount_amount = 0
476
Nabin Haite7679702015-02-20 14:40:35 +0530477 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530478 if self.doc.apply_discount_on == "Net Total":
479 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530480 else:
481 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530482
Nabin Haite7679702015-02-20 14:40:35 +0530483 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530484 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530485 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
486 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530487 elif tax.row_id in actual_taxes_dict:
488 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
489 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530490
Nabin Hait877e1bb2017-11-17 12:27:43 +0530491 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
492 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530493
494
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530495 def calculate_total_advance(self):
496 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530497 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530498 for adv in self.doc.get("advances")])
499
Nabin Haite7679702015-02-20 14:40:35 +0530500 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530501
Faris Ansari6041f5c2018-02-08 13:33:52 +0530502 grand_total = self.doc.rounded_total or self.doc.grand_total
503
Nabin Hait289ffb72016-02-08 11:06:55 +0530504 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530505 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530506 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530507 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530508 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530509 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530510 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530511 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530512
Nabin Haitadc09232016-02-09 10:31:11 +0530513 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530514 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
515 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530516
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530517 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530518 self.calculate_outstanding_amount()
519
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530520 def is_internal_invoice(self):
521 """
522 Checks if its an internal transfer invoice
523 and decides if to calculate any out standing amount or not
524 """
525
526 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
527 return True
528
529 return False
530
Nabin Hait3237c752015-02-17 11:11:11 +0530531 def calculate_outstanding_amount(self):
532 # NOTE:
533 # write_off_amount is only for POS Invoice
534 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530535 if self.doc.doctype == "Sales Invoice":
536 self.calculate_paid_amount()
537
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530538 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
539 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530540
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530541 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530542 self._set_in_company_currency(self.doc, ['write_off_amount'])
543
Nabin Hait877e1bb2017-11-17 12:27:43 +0530544 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
545 grand_total = self.doc.rounded_total or self.doc.grand_total
546 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530547 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530548 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
549 else:
550 total_amount_to_pay = flt(flt(grand_total *
551 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
552 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530553
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530554 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530555 change_amount = 0
556
Deepesh Garg0ebace52020-02-25 13:21:16 +0530557 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530558 self.calculate_write_off_amount()
559 self.calculate_change_amount()
560 change_amount = self.doc.change_amount \
561 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
562
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530563 paid_amount = self.doc.paid_amount \
564 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530565
Nabin Hait877e1bb2017-11-17 12:27:43 +0530566 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
567 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530568
Deepesh Garg0ebace52020-02-25 13:21:16 +0530569 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
570 self.update_paid_amount_for_return(total_amount_to_pay)
571
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530572 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530573
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530574 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530575
576 if self.doc.is_pos:
577 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530578 payment.amount = flt(payment.amount)
579 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530580 paid_amount += payment.amount
581 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530582 elif not self.doc.is_return:
583 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530584
Manas Solankida486ee2018-07-06 12:36:57 +0530585 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
586 base_paid_amount += self.doc.loyalty_amount
587 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
588
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530589 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
590 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
591
Nabin Hait3bb1a422016-08-02 16:41:10 +0530592 def calculate_change_amount(self):
593 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530594 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530595
596 if self.doc.doctype == "Sales Invoice" \
597 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530598 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530599 grand_total = self.doc.rounded_total or self.doc.grand_total
600 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530601
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530602 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530603 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530604
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530605 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530606 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530607
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530608 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530609 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530610 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
611 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530612 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
613 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530614
mbauskar36b51892016-01-18 16:31:10 +0530615 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530616 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530617 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530618 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530619 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530620 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530621 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530622 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530623
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530624 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
625 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530626 item.margin_type = pricing_rule.margin_type
627 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530628 has_margin = True
629
630 if not has_margin:
631 item.margin_type = None
632 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530633
mbauskara52472c2016-03-05 15:10:25 +0530634 if item.margin_type and item.margin_rate_or_amount:
635 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 +0530636 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530637 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530638
Shreya Shahbe690ef2017-11-14 17:22:41 +0530639 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530640
641 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530642 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530643
Deepesh Garg0ebace52020-02-25 13:21:16 +0530644 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530645 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
646 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530647
648 self.doc.payments = []
649
650 if default_mode_of_payment:
651 self.doc.append('payments', {
652 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530653 'amount': total_amount_to_pay,
654 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530655 })
656 else:
657 self.doc.is_pos = 0
658 self.doc.pos_profile = ''
659
660 self.calculate_paid_amount()
661
662
Nabin Hait9c421612017-07-20 13:32:01 +0530663def get_itemised_tax_breakup_html(doc):
664 if not doc.taxes:
665 return
666 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530667
Nabin Hait9c421612017-07-20 13:32:01 +0530668 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530669 tax_accounts = []
670 for tax in doc.taxes:
671 if getattr(tax, "category", None) and tax.category=="Valuation":
672 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530673 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530674 tax_accounts.append(tax.description)
675
Nabin Hait9c421612017-07-20 13:32:01 +0530676 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530677
Nabin Hait9c421612017-07-20 13:32:01 +0530678 # get tax breakup data
679 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530680
681 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
682
rohitwaghchaured4526682017-12-28 14:20:13 +0530683 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530684 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530685
Nabin Hait9c421612017-07-20 13:32:01 +0530686 return frappe.render_template(
687 "templates/includes/itemised_tax_breakup.html", dict(
688 headers=headers,
689 itemised_tax=itemised_tax,
690 itemised_taxable_amount=itemised_taxable_amount,
691 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530692 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530693 )
Nabin Hait9c421612017-07-20 13:32:01 +0530694 )
Nabin Hait852cb642017-07-05 12:58:19 +0530695
rohitwaghchaured4526682017-12-28 14:20:13 +0530696
697@erpnext.allow_regional
698def update_itemised_tax_data(doc):
699 #Don't delete this method, used for localization
700 pass
701
Nabin Haitb962fc12017-07-17 18:02:31 +0530702@erpnext.allow_regional
703def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
704 return [_("Item"), _("Taxable Amount")] + tax_accounts
705
706@erpnext.allow_regional
707def get_itemised_tax_breakup_data(doc):
708 itemised_tax = get_itemised_tax(doc.taxes)
709
710 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
711
712 return itemised_tax, itemised_taxable_amount
713
Nabin Hait34c551d2019-07-03 10:34:31 +0530714def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530715 itemised_tax = {}
716 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530717 if getattr(tax, "category", None) and tax.category=="Valuation":
718 continue
719
Nabin Haitb962fc12017-07-17 18:02:31 +0530720 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530721 if item_tax_map:
722 for item_code, tax_data in item_tax_map.items():
723 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530724
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530725 tax_rate = 0.0
726 tax_amount = 0.0
727
Nabin Hait2e4de832017-09-19 14:53:16 +0530728 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530729 tax_rate = flt(tax_data[0])
730 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530731 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530732 tax_rate = flt(tax_data)
733
734 itemised_tax[item_code][tax.description] = frappe._dict(dict(
735 tax_rate = tax_rate,
736 tax_amount = tax_amount
737 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530738
Nabin Hait34c551d2019-07-03 10:34:31 +0530739 if with_tax_account:
740 itemised_tax[item_code][tax.description].tax_account = tax.account_head
741
Nabin Haitb962fc12017-07-17 18:02:31 +0530742 return itemised_tax
743
744def get_itemised_taxable_amount(items):
745 itemised_taxable_amount = frappe._dict()
746 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530747 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530748 itemised_taxable_amount.setdefault(item_code, 0)
749 itemised_taxable_amount[item_code] += item.net_amount
750
Nabin Haitcaab5822017-08-24 16:22:28 +0530751 return itemised_taxable_amount
752
753def get_rounded_tax_amount(itemised_tax, precision):
754 # Rounding based on tax_amount precision
755 for taxes in itemised_tax.values():
756 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530757 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530758
759class init_landed_taxes_and_totals(object):
760 def __init__(self, doc):
761 self.doc = doc
762 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
763 self.set_account_currency()
764 self.set_exchange_rate()
765 self.set_amounts_in_company_currency()
766
767 def set_account_currency(self):
768 company_currency = erpnext.get_company_currency(self.doc.company)
769 for d in self.doc.get(self.tax_field):
770 if not d.account_currency:
771 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
772 d.account_currency = account_currency or company_currency
773
774 def set_exchange_rate(self):
775 company_currency = erpnext.get_company_currency(self.doc.company)
776 for d in self.doc.get(self.tax_field):
777 if d.account_currency == company_currency:
778 d.exchange_rate = 1
779 elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
780 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
781 account_currency=d.account_currency, company=self.doc.company)
782
783 if not d.exchange_rate:
784 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
785
786 def set_amounts_in_company_currency(self):
787 for d in self.doc.get(self.tax_field):
788 d.amount = flt(d.amount, d.precision("amount"))
789 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))