blob: fd744a779d07768f266316af1572c752aaf6a0cb [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
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530242 if self.doc.doctype == 'Sales Invoice' and self.doc.is_pos:
243 self.doc.pos_total_qty = self.doc.total_qty
244
Nabin Hait3237c752015-02-17 11:11:11 +0530245 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530246 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530247 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530248 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530249 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
250
251 for n, item in enumerate(self.doc.get("items")):
252 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530253 for i, tax in enumerate(self.doc.get("taxes")):
254 # tax_amount represents the amount of tax for the current step
255 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
256
257 # Adjust divisional loss to the last item
258 if tax.charge_type == "Actual":
259 actual_tax_dict[tax.idx] -= current_tax_amount
260 if n == len(self.doc.get("items")) - 1:
261 current_tax_amount += actual_tax_dict[tax.idx]
262
Nabin Hait2b019ed2015-02-22 23:03:07 +0530263 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530264 if tax.charge_type != "Actual" and \
265 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
266 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530267
Nabin Hait3237c752015-02-17 11:11:11 +0530268 # store tax_amount for current item as it will be used for
269 # charge type = 'On Previous Row Amount'
270 tax.tax_amount_for_current_item = current_tax_amount
271
Nabin Hait2b019ed2015-02-22 23:03:07 +0530272 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530273 tax.tax_amount_after_discount_amount += current_tax_amount
274
Nabin Haitcd951342017-07-31 18:07:45 +0530275 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530276
Nabin Hait3237c752015-02-17 11:11:11 +0530277 # note: grand_total_for_current_item contains the contribution of
278 # item's amount, previously applied tax and the current tax on that item
279 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530280 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530281 else:
282 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530283 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530284
285 # set precision in the last item iteration
286 if n == len(self.doc.get("items")) - 1:
287 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530288 self.set_cumulative_total(i, tax)
289
290 self._set_in_company_currency(tax,
291 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530292
Nabin Hait3237c752015-02-17 11:11:11 +0530293 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530294 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530295 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530296 self.doc.rounding_adjustment = flt(self.doc.grand_total
297 - flt(self.doc.discount_amount) - tax.total,
298 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530299
Nabin Haitcd951342017-07-31 18:07:45 +0530300 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
301 # if just for valuation, do not add the tax amount in total
302 # if tax/charges is for deduction, multiply by -1
303 if getattr(tax, "category", None):
304 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530305 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
306 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530307 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530308
Nabin Haitcd951342017-07-31 18:07:45 +0530309 def set_cumulative_total(self, row_idx, tax):
310 tax_amount = tax.tax_amount_after_discount_amount
311 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
312
313 if row_idx == 0:
314 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
315 else:
316 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530317
318 def get_current_tax_amount(self, item, tax, item_tax_map):
319 tax_rate = self._get_tax_rate(tax, item_tax_map)
320 current_tax_amount = 0.0
321
322 if tax.charge_type == "Actual":
323 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530324 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
325 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
326
Nabin Hait3237c752015-02-17 11:11:11 +0530327 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530328 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530329 elif tax.charge_type == "On Previous Row Amount":
330 current_tax_amount = (tax_rate / 100.0) * \
331 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
332 elif tax.charge_type == "On Previous Row Total":
333 current_tax_amount = (tax_rate / 100.0) * \
334 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530335 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530336 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530337
Nabin Haite7679702015-02-20 14:40:35 +0530338 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530339
340 return current_tax_amount
341
Nabin Haite7679702015-02-20 14:40:35 +0530342 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
343 # store tax breakup for each item
344 key = item.item_code or item.item_name
345 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
346 if tax.item_wise_tax_detail.get(key):
347 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
348
Nabin Haitcaab5822017-08-24 16:22:28 +0530349 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530350
Nabin Hait3237c752015-02-17 11:11:11 +0530351 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530352 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530353 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530354 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530355
Nabin Haita1bf43b2015-03-17 10:50:47 +0530356 def manipulate_grand_total_for_inclusive_tax(self):
357 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530358 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 +0530359 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530360 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
361 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530362
Nabin Hait2e4de832017-09-19 14:53:16 +0530363 diff = self.doc.total + non_inclusive_tax_amount \
364 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530365
366 # If discount amount applied, deduct the discount amount
367 # because self.doc.total is always without discount, but last_tax.total is after discount
368 if self.discount_amount_applied and self.doc.discount_amount:
369 diff -= flt(self.doc.discount_amount)
370
371 diff = flt(diff, self.doc.precision("rounding_adjustment"))
372
Nabin Hait2e4de832017-09-19 14:53:16 +0530373 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530374 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530375
376 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530377 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
378 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530379
Nabin Hait2e4de832017-09-19 14:53:16 +0530380 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
381 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530382
Nabin Hait2e4de832017-09-19 14:53:16 +0530383 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530384
Saqiba6f98d42020-07-23 18:51:26 +0530385 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530386 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 +0530387 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530388 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530389 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530390 for tax in self.doc.get("taxes"):
391 if tax.category in ["Valuation and Total", "Total"]:
392 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530393 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530394 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530395 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530396
Nabin Haite7679702015-02-20 14:40:35 +0530397 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530398
Nabin Haite7679702015-02-20 14:40:35 +0530399 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
400 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
401 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530402
Nabin Hait2e4de832017-09-19 14:53:16 +0530403 self._set_in_company_currency(self.doc,
404 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530405
Nabin Haite7679702015-02-20 14:40:35 +0530406 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530407
Nabin Hait2e4de832017-09-19 14:53:16 +0530408 self.set_rounded_total()
409
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530410 def calculate_total_net_weight(self):
411 if self.doc.meta.get_field('total_net_weight'):
412 self.doc.total_net_weight = 0.0
413 for d in self.doc.items:
414 if d.total_weight:
415 self.doc.total_net_weight += d.total_weight
416
Nabin Hait2e4de832017-09-19 14:53:16 +0530417 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530418 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530419 if self.doc.is_rounded_total_disabled():
420 self.doc.rounded_total = self.doc.base_rounded_total = 0
421 return
422
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530423 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530424 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530425
Nabin Hait877e1bb2017-11-17 12:27:43 +0530426 #if print_in_rate is set, we would have already calculated rounding adjustment
427 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
428 self.doc.precision("rounding_adjustment"))
429
Nabin Hait02ac9012017-11-22 16:12:20 +0530430 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530431
Nabin Hait3237c752015-02-17 11:11:11 +0530432 def _cleanup(self):
433 for tax in self.doc.get("taxes"):
434 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530435
Nabin Hait3769d872015-12-18 13:12:02 +0530436 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530437 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530438 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530439 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530440
441 def apply_discount_amount(self):
442 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530443 if not self.doc.apply_discount_on:
444 frappe.throw(_("Please select Apply Discount On"))
445
Nabin Hait3237c752015-02-17 11:11:11 +0530446 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
447 self.doc.precision("base_discount_amount"))
448
Nabin Haite7679702015-02-20 14:40:35 +0530449 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530450 taxes = self.doc.get("taxes")
451 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530452
Nabin Haite7679702015-02-20 14:40:35 +0530453 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530454 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530455 for i, item in enumerate(self.doc.get("items")):
456 distributed_amount = flt(self.doc.discount_amount) * \
457 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530458
Nabin Haite7679702015-02-20 14:40:35 +0530459 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530460 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530461
Nabin Hait25bd84d2015-03-04 15:06:56 +0530462 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530463 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 +0530464 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530465 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530466 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530467
Anand Doshiec5ec602015-03-05 19:31:23 +0530468 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530469 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530470
Nabin Hait51e980d2015-10-10 18:10:05 +0530471 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 +0530472
Nabin Haite7679702015-02-20 14:40:35 +0530473 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530474
475 self.discount_amount_applied = True
476 self._calculate()
477 else:
478 self.doc.base_discount_amount = 0
479
Nabin Haite7679702015-02-20 14:40:35 +0530480 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530481 if self.doc.apply_discount_on == "Net Total":
482 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530483 else:
484 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530485
Nabin Haite7679702015-02-20 14:40:35 +0530486 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530487 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530488 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
489 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530490 elif tax.row_id in actual_taxes_dict:
491 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
492 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530493
Nabin Hait877e1bb2017-11-17 12:27:43 +0530494 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
495 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530496
497
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530498 def calculate_total_advance(self):
499 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530500 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530501 for adv in self.doc.get("advances")])
502
Nabin Haite7679702015-02-20 14:40:35 +0530503 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530504
Faris Ansari6041f5c2018-02-08 13:33:52 +0530505 grand_total = self.doc.rounded_total or self.doc.grand_total
506
Nabin Hait289ffb72016-02-08 11:06:55 +0530507 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530508 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530509 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530510 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530511 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530512 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530513 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530514 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530515
Nabin Haitadc09232016-02-09 10:31:11 +0530516 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530517 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
518 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530519
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530520 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530521 self.calculate_outstanding_amount()
522
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530523 def is_internal_invoice(self):
524 """
525 Checks if its an internal transfer invoice
526 and decides if to calculate any out standing amount or not
527 """
528
529 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
530 return True
531
532 return False
533
Nabin Hait3237c752015-02-17 11:11:11 +0530534 def calculate_outstanding_amount(self):
535 # NOTE:
536 # write_off_amount is only for POS Invoice
537 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530538 if self.doc.doctype == "Sales Invoice":
539 self.calculate_paid_amount()
540
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530541 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
542 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530543
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530544 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530545 self._set_in_company_currency(self.doc, ['write_off_amount'])
546
Nabin Hait877e1bb2017-11-17 12:27:43 +0530547 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
548 grand_total = self.doc.rounded_total or self.doc.grand_total
549 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530550 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530551 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
552 else:
553 total_amount_to_pay = flt(flt(grand_total *
554 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
555 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530556
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530557 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530558 change_amount = 0
559
Deepesh Garg0ebace52020-02-25 13:21:16 +0530560 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530561 self.calculate_write_off_amount()
562 self.calculate_change_amount()
563 change_amount = self.doc.change_amount \
564 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
565
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530566 paid_amount = self.doc.paid_amount \
567 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530568
Nabin Hait877e1bb2017-11-17 12:27:43 +0530569 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
570 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530571
Deepesh Garg0ebace52020-02-25 13:21:16 +0530572 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
573 self.update_paid_amount_for_return(total_amount_to_pay)
574
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530575 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530576
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530577 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530578
579 if self.doc.is_pos:
580 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530581 payment.amount = flt(payment.amount)
582 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530583 paid_amount += payment.amount
584 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530585 elif not self.doc.is_return:
586 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530587
Manas Solankida486ee2018-07-06 12:36:57 +0530588 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
589 base_paid_amount += self.doc.loyalty_amount
590 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
591
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530592 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
593 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
594
Nabin Hait3bb1a422016-08-02 16:41:10 +0530595 def calculate_change_amount(self):
596 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530597 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530598
599 if self.doc.doctype == "Sales Invoice" \
600 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530601 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530602 grand_total = self.doc.rounded_total or self.doc.grand_total
603 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530604
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530605 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530606 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530607
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530608 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530609 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530610
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530611 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530612 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530613 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
614 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530615 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
616 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530617
mbauskar36b51892016-01-18 16:31:10 +0530618 def calculate_margin(self, item):
Shreya Shahf718b0c2018-02-20 11:26:46 +0530619
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530620 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530621 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530622 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530623 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530624 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530625 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530626 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530627
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530628 if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530629 or (pricing_rule.margin_type == 'Percentage'):
630 item.margin_type = pricing_rule.margin_type
631 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530632 has_margin = True
633
634 if not has_margin:
635 item.margin_type = None
636 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530637
mbauskara52472c2016-03-05 15:10:25 +0530638 if item.margin_type and item.margin_rate_or_amount:
639 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 +0530640 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530641 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530642
Shreya Shahbe690ef2017-11-14 17:22:41 +0530643 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530644
645 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530646 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530647
Deepesh Garg0ebace52020-02-25 13:21:16 +0530648 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530649 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
650 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530651
652 self.doc.payments = []
653
654 if default_mode_of_payment:
655 self.doc.append('payments', {
656 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530657 'amount': total_amount_to_pay,
658 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530659 })
660 else:
661 self.doc.is_pos = 0
662 self.doc.pos_profile = ''
663
664 self.calculate_paid_amount()
665
666
Nabin Hait9c421612017-07-20 13:32:01 +0530667def get_itemised_tax_breakup_html(doc):
668 if not doc.taxes:
669 return
670 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530671
Nabin Hait9c421612017-07-20 13:32:01 +0530672 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530673 tax_accounts = []
674 for tax in doc.taxes:
675 if getattr(tax, "category", None) and tax.category=="Valuation":
676 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530677 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530678 tax_accounts.append(tax.description)
679
Nabin Hait9c421612017-07-20 13:32:01 +0530680 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530681
Nabin Hait9c421612017-07-20 13:32:01 +0530682 # get tax breakup data
683 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530684
685 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
686
rohitwaghchaured4526682017-12-28 14:20:13 +0530687 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530688 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530689
Nabin Hait9c421612017-07-20 13:32:01 +0530690 return frappe.render_template(
691 "templates/includes/itemised_tax_breakup.html", dict(
692 headers=headers,
693 itemised_tax=itemised_tax,
694 itemised_taxable_amount=itemised_taxable_amount,
695 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530696 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530697 )
Nabin Hait9c421612017-07-20 13:32:01 +0530698 )
Nabin Hait852cb642017-07-05 12:58:19 +0530699
rohitwaghchaured4526682017-12-28 14:20:13 +0530700
701@erpnext.allow_regional
702def update_itemised_tax_data(doc):
703 #Don't delete this method, used for localization
704 pass
705
Nabin Haitb962fc12017-07-17 18:02:31 +0530706@erpnext.allow_regional
707def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
708 return [_("Item"), _("Taxable Amount")] + tax_accounts
709
710@erpnext.allow_regional
711def get_itemised_tax_breakup_data(doc):
712 itemised_tax = get_itemised_tax(doc.taxes)
713
714 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
715
716 return itemised_tax, itemised_taxable_amount
717
Nabin Hait34c551d2019-07-03 10:34:31 +0530718def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530719 itemised_tax = {}
720 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530721 if getattr(tax, "category", None) and tax.category=="Valuation":
722 continue
723
Nabin Haitb962fc12017-07-17 18:02:31 +0530724 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530725 if item_tax_map:
726 for item_code, tax_data in item_tax_map.items():
727 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530728
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530729 tax_rate = 0.0
730 tax_amount = 0.0
731
Nabin Hait2e4de832017-09-19 14:53:16 +0530732 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530733 tax_rate = flt(tax_data[0])
734 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530735 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530736 tax_rate = flt(tax_data)
737
738 itemised_tax[item_code][tax.description] = frappe._dict(dict(
739 tax_rate = tax_rate,
740 tax_amount = tax_amount
741 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530742
Nabin Hait34c551d2019-07-03 10:34:31 +0530743 if with_tax_account:
744 itemised_tax[item_code][tax.description].tax_account = tax.account_head
745
Nabin Haitb962fc12017-07-17 18:02:31 +0530746 return itemised_tax
747
748def get_itemised_taxable_amount(items):
749 itemised_taxable_amount = frappe._dict()
750 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530751 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530752 itemised_taxable_amount.setdefault(item_code, 0)
753 itemised_taxable_amount[item_code] += item.net_amount
754
Nabin Haitcaab5822017-08-24 16:22:28 +0530755 return itemised_taxable_amount
756
757def get_rounded_tax_amount(itemised_tax, precision):
758 # Rounding based on tax_amount precision
759 for taxes in itemised_tax.values():
760 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530761 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530762
763class init_landed_taxes_and_totals(object):
764 def __init__(self, doc):
765 self.doc = doc
766 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
767 self.set_account_currency()
768 self.set_exchange_rate()
769 self.set_amounts_in_company_currency()
770
771 def set_account_currency(self):
772 company_currency = erpnext.get_company_currency(self.doc.company)
773 for d in self.doc.get(self.tax_field):
774 if not d.account_currency:
775 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
776 d.account_currency = account_currency or company_currency
777
778 def set_exchange_rate(self):
779 company_currency = erpnext.get_company_currency(self.doc.company)
780 for d in self.doc.get(self.tax_field):
781 if d.account_currency == company_currency:
782 d.exchange_rate = 1
783 elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
784 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
785 account_currency=d.account_currency, company=self.doc.company)
786
787 if not d.exchange_rate:
788 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
789
790 def set_amounts_in_company_currency(self):
791 for d in self.doc.get(self.tax_field):
792 d.amount = flt(d.amount, d.precision("amount"))
793 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))