blob: 8dd2e5bacbd274bf61e9db8ba12be856378f7a77 [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
Nabin Hait3237c752015-02-17 11:11:11 +053013
Nabin Haitfe81da22015-02-18 12:23:18 +053014class calculate_taxes_and_totals(object):
Nabin Hait3237c752015-02-17 11:11:11 +053015 def __init__(self, doc):
16 self.doc = doc
Nabin Haitfe81da22015-02-18 12:23:18 +053017 self.calculate()
18
Nabin Hait3237c752015-02-17 11:11:11 +053019 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053020 if not len(self.doc.get("items")):
21 return
22
Nabin Hait3237c752015-02-17 11:11:11 +053023 self.discount_amount_applied = False
24 self._calculate()
25
26 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053027 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053028 self.apply_discount_amount()
29
Nabin Haitbd00e812015-02-17 12:50:51 +053030 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053031 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053032
Nabin Hait852cb642017-07-05 12:58:19 +053033 if self.doc.meta.get_field("other_charges_calculation"):
34 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053035
36 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053037 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053038 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053039 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053040 self.initialize_taxes()
41 self.determine_exclusive_rate()
42 self.calculate_net_total()
43 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053044 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053045 self.calculate_totals()
46 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053047 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053048
Deepesh Gargef0d26c2020-01-06 15:34:15 +053049 def validate_item_tax_template(self):
50 for item in self.doc.get('items'):
51 if item.item_code and item.get('item_tax_template'):
52 item_doc = frappe.get_cached_doc("Item", item.item_code)
53 args = {
54 'tax_category': self.doc.get('tax_category'),
55 'posting_date': self.doc.get('posting_date'),
56 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050057 'transaction_date': self.doc.get('transaction_date'),
58 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053059 }
60
61 item_group = item_doc.item_group
62 item_group_taxes = []
63
64 while item_group:
65 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
66 item_group_taxes += item_group_doc.taxes or []
67 item_group = item_group_doc.parent_item_group
68
69 item_taxes = item_doc.taxes or []
70
71 if not item_group_taxes and (not item_taxes):
72 # No validation if no taxes in item or item group
73 continue
74
75 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
76
77 if item.item_tax_template not in taxes:
78 frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
79 item.idx, frappe.bold(item.item_code)
80 ))
81
Nabin Haite7679702015-02-20 14:40:35 +053082 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053083 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053084 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +053085 if not self.doc.currency or self.doc.currency == company_currency:
86 self.doc.currency = company_currency
87 self.doc.conversion_rate = 1.0
88 else:
89 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
90 self.doc.meta.get_label("conversion_rate"), self.doc.company)
91
92 self.doc.conversion_rate = flt(self.doc.conversion_rate)
93
Nabin Hait3237c752015-02-17 11:11:11 +053094 def calculate_item_values(self):
95 if not self.discount_amount_applied:
96 for item in self.doc.get("items"):
97 self.doc.round_floats_in(item)
98
99 if item.discount_percentage == 100:
100 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530101 elif item.price_list_rate:
102 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
103 item.rate = flt(item.price_list_rate *
104 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
105 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
106 elif item.discount_amount and item.pricing_rules:
107 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530108
mbauskara52472c2016-03-05 15:10:25 +0530109 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530110 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530111 if flt(item.rate_with_margin) > 0:
112 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
113 item.discount_amount = item.rate_with_margin - item.rate
114 elif flt(item.price_list_rate) > 0:
115 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530116 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
117 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530118
Nabin Haite7679702015-02-20 14:40:35 +0530119 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530120
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530121 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530122 item.amount = flt(-1 * item.rate, item.precision("amount"))
123 else:
124 item.amount = flt(item.rate * item.qty, item.precision("amount"))
125
Nabin Haite7679702015-02-20 14:40:35 +0530126 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530127
Nabin Haite7679702015-02-20 14:40:35 +0530128 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530129
Nabin Haite7679702015-02-20 14:40:35 +0530130 item.item_tax_amount = 0.0
131
132 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530133 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530134 for f in fields:
135 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
136 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530137
138 def initialize_taxes(self):
139 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530140 if not self.discount_amount_applied:
141 validate_taxes_and_charges(tax)
142 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530143
Nabin Hait3237c752015-02-17 11:11:11 +0530144 tax.item_wise_tax_detail = {}
145 tax_fields = ["total", "tax_amount_after_discount_amount",
146 "tax_amount_for_current_item", "grand_total_for_current_item",
147 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
148
Nabin Haitde9c8a92015-02-23 01:06:00 +0530149 if tax.charge_type != "Actual" and \
150 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
151 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530152
153 for fieldname in tax_fields:
154 tax.set(fieldname, 0.0)
155
Nabin Hait3237c752015-02-17 11:11:11 +0530156 self.doc.round_floats_in(tax)
157
Nabin Hait3237c752015-02-17 11:11:11 +0530158 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530159 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
160 return
Nabin Hait3237c752015-02-17 11:11:11 +0530161
162 for item in self.doc.get("items"):
163 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
164 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530165 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530166 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530167 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 +0530168
169 if i==0:
170 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
171 else:
172 tax.grand_total_fraction_for_current_item = \
173 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
174 + tax.tax_fraction_for_current_item
175
176 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530177 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530178
Nabin Hait19ea7212020-08-11 20:34:57 +0530179 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
180 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
181
182 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530183 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530184 item.discount_percentage = flt(item.discount_percentage,
185 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530186
Nabin Haite7679702015-02-20 14:40:35 +0530187 self._set_in_company_currency(item, ["net_rate", "net_amount"])
188
Nabin Hait3237c752015-02-17 11:11:11 +0530189 def _load_item_tax_rate(self, item_tax_rate):
190 return json.loads(item_tax_rate) if item_tax_rate else {}
191
192 def get_current_tax_fraction(self, tax, item_tax_map):
193 """
194 Get tax fraction for calculating tax exclusive amount
195 from tax inclusive amount
196 """
197 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530198 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530199
200 if cint(tax.included_in_print_rate):
201 tax_rate = self._get_tax_rate(tax, item_tax_map)
202
203 if tax.charge_type == "On Net Total":
204 current_tax_fraction = tax_rate / 100.0
205
206 elif tax.charge_type == "On Previous Row Amount":
207 current_tax_fraction = (tax_rate / 100.0) * \
208 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
209
210 elif tax.charge_type == "On Previous Row Total":
211 current_tax_fraction = (tax_rate / 100.0) * \
212 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530213
Nabin Hait19ea7212020-08-11 20:34:57 +0530214 elif tax.charge_type == "On Item Quantity":
215 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530216
Nabin Hait19ea7212020-08-11 20:34:57 +0530217 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
218 current_tax_fraction *= -1.0
219 inclusive_tax_amount_per_qty *= -1.0
220
221 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530222
223 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530224 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530225 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
226 else:
227 return tax.rate
228
229 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530230 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 +0530231
Nabin Hait3237c752015-02-17 11:11:11 +0530232 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530233 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530234 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530235 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530236 self.doc.net_total += item.net_amount
237 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530238
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530239 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530240
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530241 if self.doc.doctype == 'Sales Invoice' and self.doc.is_pos:
242 self.doc.pos_total_qty = self.doc.total_qty
243
Nabin Hait3237c752015-02-17 11:11:11 +0530244 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530245 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530246 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530247 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530248 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
249
250 for n, item in enumerate(self.doc.get("items")):
251 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530252 for i, tax in enumerate(self.doc.get("taxes")):
253 # tax_amount represents the amount of tax for the current step
254 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
255
256 # Adjust divisional loss to the last item
257 if tax.charge_type == "Actual":
258 actual_tax_dict[tax.idx] -= current_tax_amount
259 if n == len(self.doc.get("items")) - 1:
260 current_tax_amount += actual_tax_dict[tax.idx]
261
Nabin Hait2b019ed2015-02-22 23:03:07 +0530262 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530263 if tax.charge_type != "Actual" and \
264 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
265 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530266
Nabin Hait3237c752015-02-17 11:11:11 +0530267 # store tax_amount for current item as it will be used for
268 # charge type = 'On Previous Row Amount'
269 tax.tax_amount_for_current_item = current_tax_amount
270
Nabin Hait2b019ed2015-02-22 23:03:07 +0530271 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530272 tax.tax_amount_after_discount_amount += current_tax_amount
273
Nabin Haitcd951342017-07-31 18:07:45 +0530274 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530275
Nabin Hait3237c752015-02-17 11:11:11 +0530276 # note: grand_total_for_current_item contains the contribution of
277 # item's amount, previously applied tax and the current tax on that item
278 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530279 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530280 else:
281 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530282 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530283
284 # set precision in the last item iteration
285 if n == len(self.doc.get("items")) - 1:
286 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530287 self.set_cumulative_total(i, tax)
288
289 self._set_in_company_currency(tax,
290 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530291
Nabin Hait3237c752015-02-17 11:11:11 +0530292 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530293 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530294 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530295 self.doc.rounding_adjustment = flt(self.doc.grand_total
296 - flt(self.doc.discount_amount) - tax.total,
297 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530298
Nabin Haitcd951342017-07-31 18:07:45 +0530299 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
300 # if just for valuation, do not add the tax amount in total
301 # if tax/charges is for deduction, multiply by -1
302 if getattr(tax, "category", None):
303 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530304 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
305 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530306 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530307
Nabin Haitcd951342017-07-31 18:07:45 +0530308 def set_cumulative_total(self, row_idx, tax):
309 tax_amount = tax.tax_amount_after_discount_amount
310 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
311
312 if row_idx == 0:
313 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
314 else:
315 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530316
317 def get_current_tax_amount(self, item, tax, item_tax_map):
318 tax_rate = self._get_tax_rate(tax, item_tax_map)
319 current_tax_amount = 0.0
320
321 if tax.charge_type == "Actual":
322 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530323 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
324 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
325
Nabin Hait3237c752015-02-17 11:11:11 +0530326 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530327 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530328 elif tax.charge_type == "On Previous Row Amount":
329 current_tax_amount = (tax_rate / 100.0) * \
330 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
331 elif tax.charge_type == "On Previous Row Total":
332 current_tax_amount = (tax_rate / 100.0) * \
333 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530334 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530335 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530336
Nabin Haite7679702015-02-20 14:40:35 +0530337 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530338
339 return current_tax_amount
340
Nabin Haite7679702015-02-20 14:40:35 +0530341 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
342 # store tax breakup for each item
343 key = item.item_code or item.item_name
344 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
345 if tax.item_wise_tax_detail.get(key):
346 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
347
Nabin Haitcaab5822017-08-24 16:22:28 +0530348 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530349
Nabin Hait3237c752015-02-17 11:11:11 +0530350 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530351 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530352 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530353 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530354
Nabin Haita1bf43b2015-03-17 10:50:47 +0530355 def manipulate_grand_total_for_inclusive_tax(self):
356 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530357 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 +0530358 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530359 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
360 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530361
Nabin Hait2e4de832017-09-19 14:53:16 +0530362 diff = self.doc.total + non_inclusive_tax_amount \
363 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530364
365 # If discount amount applied, deduct the discount amount
366 # because self.doc.total is always without discount, but last_tax.total is after discount
367 if self.discount_amount_applied and self.doc.discount_amount:
368 diff -= flt(self.doc.discount_amount)
369
370 diff = flt(diff, self.doc.precision("rounding_adjustment"))
371
Nabin Hait2e4de832017-09-19 14:53:16 +0530372 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530373 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530374
375 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530376 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
377 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530378
Nabin Hait2e4de832017-09-19 14:53:16 +0530379 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
380 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530381
Nabin Hait2e4de832017-09-19 14:53:16 +0530382 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530383
Saqiba6f98d42020-07-23 18:51:26 +0530384 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530385 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 +0530386 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530387 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530388 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530389 for tax in self.doc.get("taxes"):
390 if tax.category in ["Valuation and Total", "Total"]:
391 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530392 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530393 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530394 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530395
Nabin Haite7679702015-02-20 14:40:35 +0530396 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530397
Nabin Haite7679702015-02-20 14:40:35 +0530398 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
399 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
400 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530401
Nabin Hait2e4de832017-09-19 14:53:16 +0530402 self._set_in_company_currency(self.doc,
403 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530404
Nabin Haite7679702015-02-20 14:40:35 +0530405 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530406
Nabin Hait2e4de832017-09-19 14:53:16 +0530407 self.set_rounded_total()
408
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530409 def calculate_total_net_weight(self):
410 if self.doc.meta.get_field('total_net_weight'):
411 self.doc.total_net_weight = 0.0
412 for d in self.doc.items:
413 if d.total_weight:
414 self.doc.total_net_weight += d.total_weight
415
Nabin Hait2e4de832017-09-19 14:53:16 +0530416 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530417 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530418 if self.doc.is_rounded_total_disabled():
419 self.doc.rounded_total = self.doc.base_rounded_total = 0
420 return
421
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530422 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530423 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530424
Nabin Hait877e1bb2017-11-17 12:27:43 +0530425 #if print_in_rate is set, we would have already calculated rounding adjustment
426 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
427 self.doc.precision("rounding_adjustment"))
428
Nabin Hait02ac9012017-11-22 16:12:20 +0530429 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530430
Nabin Hait3237c752015-02-17 11:11:11 +0530431 def _cleanup(self):
432 for tax in self.doc.get("taxes"):
433 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530434
Nabin Hait3769d872015-12-18 13:12:02 +0530435 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530436 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530437 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530438 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530439
440 def apply_discount_amount(self):
441 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530442 if not self.doc.apply_discount_on:
443 frappe.throw(_("Please select Apply Discount On"))
444
Nabin Hait3237c752015-02-17 11:11:11 +0530445 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
446 self.doc.precision("base_discount_amount"))
447
Nabin Haite7679702015-02-20 14:40:35 +0530448 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530449 taxes = self.doc.get("taxes")
450 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530451
Nabin Haite7679702015-02-20 14:40:35 +0530452 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530453 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530454 for i, item in enumerate(self.doc.get("items")):
455 distributed_amount = flt(self.doc.discount_amount) * \
456 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530457
Nabin Haite7679702015-02-20 14:40:35 +0530458 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530459 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530460
Nabin Hait25bd84d2015-03-04 15:06:56 +0530461 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530462 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 +0530463 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530464 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530465 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530466
Anand Doshiec5ec602015-03-05 19:31:23 +0530467 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530468 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530469
Nabin Hait51e980d2015-10-10 18:10:05 +0530470 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 +0530471
Nabin Haite7679702015-02-20 14:40:35 +0530472 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530473
474 self.discount_amount_applied = True
475 self._calculate()
476 else:
477 self.doc.base_discount_amount = 0
478
Nabin Haite7679702015-02-20 14:40:35 +0530479 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530480 if self.doc.apply_discount_on == "Net Total":
481 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530482 else:
483 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530484
Nabin Haite7679702015-02-20 14:40:35 +0530485 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530486 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530487 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
488 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530489 elif tax.row_id in actual_taxes_dict:
490 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
491 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530492
Nabin Hait877e1bb2017-11-17 12:27:43 +0530493 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
494 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530495
496
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530497 def calculate_total_advance(self):
498 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530499 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530500 for adv in self.doc.get("advances")])
501
Nabin Haite7679702015-02-20 14:40:35 +0530502 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530503
Faris Ansari6041f5c2018-02-08 13:33:52 +0530504 grand_total = self.doc.rounded_total or self.doc.grand_total
505
Nabin Hait289ffb72016-02-08 11:06:55 +0530506 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530507 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530508 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530509 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530510 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530511 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530512 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530513 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530514
Nabin Haitadc09232016-02-09 10:31:11 +0530515 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530516 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
517 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530518
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530519 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530520 self.calculate_outstanding_amount()
521
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530522 def is_internal_invoice(self):
523 """
524 Checks if its an internal transfer invoice
525 and decides if to calculate any out standing amount or not
526 """
527
528 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
529 return True
530
531 return False
532
Nabin Hait3237c752015-02-17 11:11:11 +0530533 def calculate_outstanding_amount(self):
534 # NOTE:
535 # write_off_amount is only for POS Invoice
536 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530537 if self.doc.doctype == "Sales Invoice":
538 self.calculate_paid_amount()
539
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530540 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
541 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530542
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530543 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530544 self._set_in_company_currency(self.doc, ['write_off_amount'])
545
Nabin Hait877e1bb2017-11-17 12:27:43 +0530546 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
547 grand_total = self.doc.rounded_total or self.doc.grand_total
548 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530549 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530550 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
551 else:
552 total_amount_to_pay = flt(flt(grand_total *
553 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
554 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530555
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530556 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530557 change_amount = 0
558
Deepesh Garg0ebace52020-02-25 13:21:16 +0530559 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530560 self.calculate_write_off_amount()
561 self.calculate_change_amount()
562 change_amount = self.doc.change_amount \
563 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
564
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530565 paid_amount = self.doc.paid_amount \
566 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530567
Nabin Hait877e1bb2017-11-17 12:27:43 +0530568 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
569 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530570
Deepesh Garg0ebace52020-02-25 13:21:16 +0530571 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
572 self.update_paid_amount_for_return(total_amount_to_pay)
573
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530574 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530575
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530576 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530577
578 if self.doc.is_pos:
579 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530580 payment.amount = flt(payment.amount)
581 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530582 paid_amount += payment.amount
583 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530584 elif not self.doc.is_return:
585 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530586
Manas Solankida486ee2018-07-06 12:36:57 +0530587 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
588 base_paid_amount += self.doc.loyalty_amount
589 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
590
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530591 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
592 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
593
Nabin Hait3bb1a422016-08-02 16:41:10 +0530594 def calculate_change_amount(self):
595 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530596 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530597
598 if self.doc.doctype == "Sales Invoice" \
599 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530600 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530601 grand_total = self.doc.rounded_total or self.doc.grand_total
602 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530603
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530604 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530605 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530606
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530607 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530608 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530609
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530610 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530611 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530612 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
613 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530614 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
615 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530616
mbauskar36b51892016-01-18 16:31:10 +0530617 def calculate_margin(self, item):
Shreya Shahf718b0c2018-02-20 11:26:46 +0530618
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530619 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530620 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530621 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530622 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530623 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530624 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530625 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530626
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530627 if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530628 or (pricing_rule.margin_type == 'Percentage'):
629 item.margin_type = pricing_rule.margin_type
630 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530631 has_margin = True
632
633 if not has_margin:
634 item.margin_type = None
635 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530636
mbauskara52472c2016-03-05 15:10:25 +0530637 if item.margin_type and item.margin_rate_or_amount:
638 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 +0530639 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530640 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530641
Shreya Shahbe690ef2017-11-14 17:22:41 +0530642 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530643
644 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530645 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530646
Deepesh Garg0ebace52020-02-25 13:21:16 +0530647 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530648 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
649 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530650
651 self.doc.payments = []
652
653 if default_mode_of_payment:
654 self.doc.append('payments', {
655 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530656 'amount': total_amount_to_pay,
657 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530658 })
659 else:
660 self.doc.is_pos = 0
661 self.doc.pos_profile = ''
662
663 self.calculate_paid_amount()
664
665
Nabin Hait9c421612017-07-20 13:32:01 +0530666def get_itemised_tax_breakup_html(doc):
667 if not doc.taxes:
668 return
669 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530670
Nabin Hait9c421612017-07-20 13:32:01 +0530671 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530672 tax_accounts = []
673 for tax in doc.taxes:
674 if getattr(tax, "category", None) and tax.category=="Valuation":
675 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530676 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530677 tax_accounts.append(tax.description)
678
Nabin Hait9c421612017-07-20 13:32:01 +0530679 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530680
Nabin Hait9c421612017-07-20 13:32:01 +0530681 # get tax breakup data
682 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530683
684 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
685
rohitwaghchaured4526682017-12-28 14:20:13 +0530686 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530687 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530688
Nabin Hait9c421612017-07-20 13:32:01 +0530689 return frappe.render_template(
690 "templates/includes/itemised_tax_breakup.html", dict(
691 headers=headers,
692 itemised_tax=itemised_tax,
693 itemised_taxable_amount=itemised_taxable_amount,
694 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530695 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530696 )
Nabin Hait9c421612017-07-20 13:32:01 +0530697 )
Nabin Hait852cb642017-07-05 12:58:19 +0530698
rohitwaghchaured4526682017-12-28 14:20:13 +0530699
700@erpnext.allow_regional
701def update_itemised_tax_data(doc):
702 #Don't delete this method, used for localization
703 pass
704
Nabin Haitb962fc12017-07-17 18:02:31 +0530705@erpnext.allow_regional
706def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
707 return [_("Item"), _("Taxable Amount")] + tax_accounts
708
709@erpnext.allow_regional
710def get_itemised_tax_breakup_data(doc):
711 itemised_tax = get_itemised_tax(doc.taxes)
712
713 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
714
715 return itemised_tax, itemised_taxable_amount
716
Nabin Hait34c551d2019-07-03 10:34:31 +0530717def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530718 itemised_tax = {}
719 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530720 if getattr(tax, "category", None) and tax.category=="Valuation":
721 continue
722
Nabin Haitb962fc12017-07-17 18:02:31 +0530723 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530724 if item_tax_map:
725 for item_code, tax_data in item_tax_map.items():
726 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530727
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530728 tax_rate = 0.0
729 tax_amount = 0.0
730
Nabin Hait2e4de832017-09-19 14:53:16 +0530731 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530732 tax_rate = flt(tax_data[0])
733 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530734 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530735 tax_rate = flt(tax_data)
736
737 itemised_tax[item_code][tax.description] = frappe._dict(dict(
738 tax_rate = tax_rate,
739 tax_amount = tax_amount
740 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530741
Nabin Hait34c551d2019-07-03 10:34:31 +0530742 if with_tax_account:
743 itemised_tax[item_code][tax.description].tax_account = tax.account_head
744
Nabin Haitb962fc12017-07-17 18:02:31 +0530745 return itemised_tax
746
747def get_itemised_taxable_amount(items):
748 itemised_taxable_amount = frappe._dict()
749 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530750 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530751 itemised_taxable_amount.setdefault(item_code, 0)
752 itemised_taxable_amount[item_code] += item.net_amount
753
Nabin Haitcaab5822017-08-24 16:22:28 +0530754 return itemised_taxable_amount
755
756def get_rounded_tax_amount(itemised_tax, precision):
757 # Rounding based on tax_amount precision
758 for taxes in itemised_tax.values():
759 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530760 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)