blob: eb33a47873297a681af04d194718575d264cd377 [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
Anupam Kumar9be11af2021-03-30 12:09:46 +0530110 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +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"))
rohitwaghchaureb38ea0c2021-03-28 15:51:32 +0530114
115 if item.discount_amount and not item.discount_percentage:
116 item.rate = item.rate_with_margin - item.discount_amount
117 else:
Rohit Waghchaurecd8422a2021-03-06 22:08:08 +0530118 item.discount_amount = item.rate_with_margin - item.rate
rohitwaghchaureb38ea0c2021-03-28 15:51:32 +0530119
Nabin Hait64bfdd92019-04-23 13:37:19 +0530120 elif flt(item.price_list_rate) > 0:
121 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530122 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
123 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530124
Nabin Haite7679702015-02-20 14:40:35 +0530125 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530126
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530127 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530128 item.amount = flt(-1 * item.rate, item.precision("amount"))
129 else:
130 item.amount = flt(item.rate * item.qty, item.precision("amount"))
131
Nabin Haite7679702015-02-20 14:40:35 +0530132 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530133
Nabin Haite7679702015-02-20 14:40:35 +0530134 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530135
Nabin Haite7679702015-02-20 14:40:35 +0530136 item.item_tax_amount = 0.0
137
138 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530139 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530140 for f in fields:
141 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
142 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530143
144 def initialize_taxes(self):
145 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530146 if not self.discount_amount_applied:
147 validate_taxes_and_charges(tax)
148 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530149
Nabin Hait3237c752015-02-17 11:11:11 +0530150 tax.item_wise_tax_detail = {}
151 tax_fields = ["total", "tax_amount_after_discount_amount",
152 "tax_amount_for_current_item", "grand_total_for_current_item",
153 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
154
Nabin Haitde9c8a92015-02-23 01:06:00 +0530155 if tax.charge_type != "Actual" and \
156 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
157 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530158
159 for fieldname in tax_fields:
160 tax.set(fieldname, 0.0)
161
Nabin Hait3237c752015-02-17 11:11:11 +0530162 self.doc.round_floats_in(tax)
163
Nabin Hait3237c752015-02-17 11:11:11 +0530164 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530165 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
166 return
Nabin Hait3237c752015-02-17 11:11:11 +0530167
168 for item in self.doc.get("items"):
169 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
170 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530171 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530172 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530173 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 +0530174
175 if i==0:
176 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
177 else:
178 tax.grand_total_fraction_for_current_item = \
179 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
180 + tax.tax_fraction_for_current_item
181
182 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530183 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530184
Nabin Hait19ea7212020-08-11 20:34:57 +0530185 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
186 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
187
188 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530189 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530190 item.discount_percentage = flt(item.discount_percentage,
191 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530192
Nabin Haite7679702015-02-20 14:40:35 +0530193 self._set_in_company_currency(item, ["net_rate", "net_amount"])
194
Nabin Hait3237c752015-02-17 11:11:11 +0530195 def _load_item_tax_rate(self, item_tax_rate):
196 return json.loads(item_tax_rate) if item_tax_rate else {}
197
198 def get_current_tax_fraction(self, tax, item_tax_map):
199 """
200 Get tax fraction for calculating tax exclusive amount
201 from tax inclusive amount
202 """
203 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530204 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530205
206 if cint(tax.included_in_print_rate):
207 tax_rate = self._get_tax_rate(tax, item_tax_map)
208
209 if tax.charge_type == "On Net Total":
210 current_tax_fraction = tax_rate / 100.0
211
212 elif tax.charge_type == "On Previous Row Amount":
213 current_tax_fraction = (tax_rate / 100.0) * \
214 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
215
216 elif tax.charge_type == "On Previous Row Total":
217 current_tax_fraction = (tax_rate / 100.0) * \
218 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530219
Nabin Hait19ea7212020-08-11 20:34:57 +0530220 elif tax.charge_type == "On Item Quantity":
221 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530222
Nabin Hait19ea7212020-08-11 20:34:57 +0530223 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
224 current_tax_fraction *= -1.0
225 inclusive_tax_amount_per_qty *= -1.0
226
227 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530228
229 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530230 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530231 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
232 else:
233 return tax.rate
234
235 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530236 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 +0530237
Nabin Hait3237c752015-02-17 11:11:11 +0530238 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530239 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530240 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530241 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530242 self.doc.net_total += item.net_amount
243 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530244
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530245 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530246
rohitwaghchaure3a595d02018-06-25 10:10:29 +0530247 if self.doc.doctype == 'Sales Invoice' and self.doc.is_pos:
248 self.doc.pos_total_qty = self.doc.total_qty
249
Nabin Hait3237c752015-02-17 11:11:11 +0530250 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530251 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530252 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530253 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530254 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
255
256 for n, item in enumerate(self.doc.get("items")):
257 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530258 for i, tax in enumerate(self.doc.get("taxes")):
259 # tax_amount represents the amount of tax for the current step
260 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
261
262 # Adjust divisional loss to the last item
263 if tax.charge_type == "Actual":
264 actual_tax_dict[tax.idx] -= current_tax_amount
265 if n == len(self.doc.get("items")) - 1:
266 current_tax_amount += actual_tax_dict[tax.idx]
267
Nabin Hait2b019ed2015-02-22 23:03:07 +0530268 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530269 if tax.charge_type != "Actual" and \
270 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
271 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530272
Nabin Hait3237c752015-02-17 11:11:11 +0530273 # store tax_amount for current item as it will be used for
274 # charge type = 'On Previous Row Amount'
275 tax.tax_amount_for_current_item = current_tax_amount
276
Nabin Hait2b019ed2015-02-22 23:03:07 +0530277 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530278 tax.tax_amount_after_discount_amount += current_tax_amount
279
Nabin Haitcd951342017-07-31 18:07:45 +0530280 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530281
Nabin Hait3237c752015-02-17 11:11:11 +0530282 # note: grand_total_for_current_item contains the contribution of
283 # item's amount, previously applied tax and the current tax on that item
284 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530285 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530286 else:
287 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530288 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530289
290 # set precision in the last item iteration
291 if n == len(self.doc.get("items")) - 1:
292 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530293 self.set_cumulative_total(i, tax)
294
295 self._set_in_company_currency(tax,
296 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530297
Nabin Hait3237c752015-02-17 11:11:11 +0530298 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530299 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530300 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530301 self.doc.rounding_adjustment = flt(self.doc.grand_total
302 - flt(self.doc.discount_amount) - tax.total,
303 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530304
Nabin Haitcd951342017-07-31 18:07:45 +0530305 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
306 # if just for valuation, do not add the tax amount in total
307 # if tax/charges is for deduction, multiply by -1
308 if getattr(tax, "category", None):
309 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530310 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
311 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530312 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530313
Nabin Haitcd951342017-07-31 18:07:45 +0530314 def set_cumulative_total(self, row_idx, tax):
315 tax_amount = tax.tax_amount_after_discount_amount
316 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
317
318 if row_idx == 0:
319 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
320 else:
321 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530322
323 def get_current_tax_amount(self, item, tax, item_tax_map):
324 tax_rate = self._get_tax_rate(tax, item_tax_map)
325 current_tax_amount = 0.0
326
327 if tax.charge_type == "Actual":
328 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530329 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
330 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
331
Nabin Hait3237c752015-02-17 11:11:11 +0530332 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530333 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530334 elif tax.charge_type == "On Previous Row Amount":
335 current_tax_amount = (tax_rate / 100.0) * \
336 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
337 elif tax.charge_type == "On Previous Row Total":
338 current_tax_amount = (tax_rate / 100.0) * \
339 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530340 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530341 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530342
Nabin Haite7679702015-02-20 14:40:35 +0530343 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530344
345 return current_tax_amount
346
Nabin Haite7679702015-02-20 14:40:35 +0530347 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
348 # store tax breakup for each item
349 key = item.item_code or item.item_name
350 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
351 if tax.item_wise_tax_detail.get(key):
352 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
353
Nabin Haitcaab5822017-08-24 16:22:28 +0530354 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530355
Nabin Hait3237c752015-02-17 11:11:11 +0530356 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530357 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530358 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530359 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530360
Nabin Haita1bf43b2015-03-17 10:50:47 +0530361 def manipulate_grand_total_for_inclusive_tax(self):
362 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530363 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 +0530364 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530365 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
366 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530367
Nabin Hait2e4de832017-09-19 14:53:16 +0530368 diff = self.doc.total + non_inclusive_tax_amount \
369 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530370
371 # If discount amount applied, deduct the discount amount
372 # because self.doc.total is always without discount, but last_tax.total is after discount
373 if self.discount_amount_applied and self.doc.discount_amount:
374 diff -= flt(self.doc.discount_amount)
375
376 diff = flt(diff, self.doc.precision("rounding_adjustment"))
377
Nabin Hait2e4de832017-09-19 14:53:16 +0530378 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530379 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530380
381 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530382 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
383 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530384
Nabin Hait2e4de832017-09-19 14:53:16 +0530385 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
386 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530387
Nabin Hait2e4de832017-09-19 14:53:16 +0530388 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530389
Saqiba6f98d42020-07-23 18:51:26 +0530390 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530391 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 +0530392 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530393 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530394 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530395 for tax in self.doc.get("taxes"):
396 if tax.category in ["Valuation and Total", "Total"]:
397 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530398 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530399 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530400 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530401
Nabin Haite7679702015-02-20 14:40:35 +0530402 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530403
Nabin Haite7679702015-02-20 14:40:35 +0530404 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
405 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
406 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530407
Nabin Hait2e4de832017-09-19 14:53:16 +0530408 self._set_in_company_currency(self.doc,
409 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530410
Nabin Haite7679702015-02-20 14:40:35 +0530411 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530412
Nabin Hait2e4de832017-09-19 14:53:16 +0530413 self.set_rounded_total()
414
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530415 def calculate_total_net_weight(self):
416 if self.doc.meta.get_field('total_net_weight'):
417 self.doc.total_net_weight = 0.0
418 for d in self.doc.items:
419 if d.total_weight:
420 self.doc.total_net_weight += d.total_weight
421
Nabin Hait2e4de832017-09-19 14:53:16 +0530422 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530423 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530424 if self.doc.is_rounded_total_disabled():
425 self.doc.rounded_total = self.doc.base_rounded_total = 0
426 return
427
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530428 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530429 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530430
Nabin Hait877e1bb2017-11-17 12:27:43 +0530431 #if print_in_rate is set, we would have already calculated rounding adjustment
432 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
433 self.doc.precision("rounding_adjustment"))
434
Nabin Hait02ac9012017-11-22 16:12:20 +0530435 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530436
Nabin Hait3237c752015-02-17 11:11:11 +0530437 def _cleanup(self):
438 for tax in self.doc.get("taxes"):
439 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530440
Nabin Hait3769d872015-12-18 13:12:02 +0530441 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530442 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530443 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530444 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530445
446 def apply_discount_amount(self):
447 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530448 if not self.doc.apply_discount_on:
449 frappe.throw(_("Please select Apply Discount On"))
450
Nabin Hait3237c752015-02-17 11:11:11 +0530451 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
452 self.doc.precision("base_discount_amount"))
453
Nabin Haite7679702015-02-20 14:40:35 +0530454 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530455 taxes = self.doc.get("taxes")
456 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530457
Nabin Haite7679702015-02-20 14:40:35 +0530458 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530459 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530460 for i, item in enumerate(self.doc.get("items")):
461 distributed_amount = flt(self.doc.discount_amount) * \
462 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530463
Nabin Haite7679702015-02-20 14:40:35 +0530464 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530465 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530466
Nabin Hait25bd84d2015-03-04 15:06:56 +0530467 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530468 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 +0530469 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530470 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530471 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530472
Anand Doshiec5ec602015-03-05 19:31:23 +0530473 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530474 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530475
Nabin Hait51e980d2015-10-10 18:10:05 +0530476 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 +0530477
Nabin Haite7679702015-02-20 14:40:35 +0530478 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530479
480 self.discount_amount_applied = True
481 self._calculate()
482 else:
483 self.doc.base_discount_amount = 0
484
Nabin Haite7679702015-02-20 14:40:35 +0530485 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530486 if self.doc.apply_discount_on == "Net Total":
487 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530488 else:
489 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530490
Nabin Haite7679702015-02-20 14:40:35 +0530491 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530492 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530493 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
494 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530495 elif tax.row_id in actual_taxes_dict:
496 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
497 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530498
Nabin Hait877e1bb2017-11-17 12:27:43 +0530499 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
500 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530501
502
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530503 def calculate_total_advance(self):
504 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530505 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530506 for adv in self.doc.get("advances")])
507
Nabin Haite7679702015-02-20 14:40:35 +0530508 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530509
Faris Ansari6041f5c2018-02-08 13:33:52 +0530510 grand_total = self.doc.rounded_total or self.doc.grand_total
511
Nabin Hait289ffb72016-02-08 11:06:55 +0530512 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530513 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530514 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530515 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530516 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530517 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530518 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530519 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530520
Nabin Haitadc09232016-02-09 10:31:11 +0530521 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530522 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
523 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530524
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530525 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530526 self.calculate_outstanding_amount()
527
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530528 def is_internal_invoice(self):
529 """
530 Checks if its an internal transfer invoice
531 and decides if to calculate any out standing amount or not
532 """
533
534 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
535 return True
536
537 return False
538
Nabin Hait3237c752015-02-17 11:11:11 +0530539 def calculate_outstanding_amount(self):
540 # NOTE:
541 # write_off_amount is only for POS Invoice
542 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530543 if self.doc.doctype == "Sales Invoice":
544 self.calculate_paid_amount()
545
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530546 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
547 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530548
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530549 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530550 self._set_in_company_currency(self.doc, ['write_off_amount'])
551
Nabin Hait877e1bb2017-11-17 12:27:43 +0530552 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
553 grand_total = self.doc.rounded_total or self.doc.grand_total
554 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530555 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530556 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
557 else:
558 total_amount_to_pay = flt(flt(grand_total *
559 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
560 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530561
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530562 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530563 change_amount = 0
564
Deepesh Garg0ebace52020-02-25 13:21:16 +0530565 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530566 self.calculate_write_off_amount()
567 self.calculate_change_amount()
568 change_amount = self.doc.change_amount \
569 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
570
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530571 paid_amount = self.doc.paid_amount \
572 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530573
Nabin Hait877e1bb2017-11-17 12:27:43 +0530574 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
575 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530576
Deepesh Garg0ebace52020-02-25 13:21:16 +0530577 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
578 self.update_paid_amount_for_return(total_amount_to_pay)
579
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530580 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530581
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530582 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530583
584 if self.doc.is_pos:
585 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530586 payment.amount = flt(payment.amount)
587 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530588 paid_amount += payment.amount
589 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530590 elif not self.doc.is_return:
591 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530592
Manas Solankida486ee2018-07-06 12:36:57 +0530593 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
594 base_paid_amount += self.doc.loyalty_amount
595 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
596
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530597 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
598 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
599
Nabin Hait3bb1a422016-08-02 16:41:10 +0530600 def calculate_change_amount(self):
601 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530602 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530603
604 if self.doc.doctype == "Sales Invoice" \
605 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530606 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530607 grand_total = self.doc.rounded_total or self.doc.grand_total
608 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530609
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530610 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530611 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530612
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530613 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530614 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530615
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530616 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530617 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530618 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
619 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530620 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
621 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530622
mbauskar36b51892016-01-18 16:31:10 +0530623 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530624 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530625 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530626 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530627 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530628 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530629 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530630 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530631
Rohit Waghchaure5c284162021-02-01 22:58:22 +0530632 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
633 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530634 item.margin_type = pricing_rule.margin_type
635 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530636 has_margin = True
637
638 if not has_margin:
639 item.margin_type = None
640 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530641
mbauskara52472c2016-03-05 15:10:25 +0530642 if item.margin_type and item.margin_rate_or_amount:
643 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 +0530644 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530645 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530646
Shreya Shahbe690ef2017-11-14 17:22:41 +0530647 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530648
649 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530650 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530651
Deepesh Garg0ebace52020-02-25 13:21:16 +0530652 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530653 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
654 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530655
656 self.doc.payments = []
657
658 if default_mode_of_payment:
659 self.doc.append('payments', {
660 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530661 'amount': total_amount_to_pay,
662 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530663 })
664 else:
665 self.doc.is_pos = 0
666 self.doc.pos_profile = ''
667
668 self.calculate_paid_amount()
669
670
Nabin Hait9c421612017-07-20 13:32:01 +0530671def get_itemised_tax_breakup_html(doc):
672 if not doc.taxes:
673 return
674 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530675
Nabin Hait9c421612017-07-20 13:32:01 +0530676 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530677 tax_accounts = []
678 for tax in doc.taxes:
679 if getattr(tax, "category", None) and tax.category=="Valuation":
680 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530681 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530682 tax_accounts.append(tax.description)
683
Nabin Hait9c421612017-07-20 13:32:01 +0530684 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530685
Nabin Hait9c421612017-07-20 13:32:01 +0530686 # get tax breakup data
687 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530688
689 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
690
rohitwaghchaured4526682017-12-28 14:20:13 +0530691 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530692 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530693
Nabin Hait9c421612017-07-20 13:32:01 +0530694 return frappe.render_template(
695 "templates/includes/itemised_tax_breakup.html", dict(
696 headers=headers,
697 itemised_tax=itemised_tax,
698 itemised_taxable_amount=itemised_taxable_amount,
699 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530700 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530701 )
Nabin Hait9c421612017-07-20 13:32:01 +0530702 )
Nabin Hait852cb642017-07-05 12:58:19 +0530703
rohitwaghchaured4526682017-12-28 14:20:13 +0530704
705@erpnext.allow_regional
706def update_itemised_tax_data(doc):
707 #Don't delete this method, used for localization
708 pass
709
Nabin Haitb962fc12017-07-17 18:02:31 +0530710@erpnext.allow_regional
711def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
712 return [_("Item"), _("Taxable Amount")] + tax_accounts
713
714@erpnext.allow_regional
715def get_itemised_tax_breakup_data(doc):
716 itemised_tax = get_itemised_tax(doc.taxes)
717
718 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
719
720 return itemised_tax, itemised_taxable_amount
721
Nabin Hait34c551d2019-07-03 10:34:31 +0530722def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530723 itemised_tax = {}
724 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530725 if getattr(tax, "category", None) and tax.category=="Valuation":
726 continue
727
Nabin Haitb962fc12017-07-17 18:02:31 +0530728 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530729 if item_tax_map:
730 for item_code, tax_data in item_tax_map.items():
731 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530732
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530733 tax_rate = 0.0
734 tax_amount = 0.0
735
Nabin Hait2e4de832017-09-19 14:53:16 +0530736 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530737 tax_rate = flt(tax_data[0])
738 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530739 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530740 tax_rate = flt(tax_data)
741
742 itemised_tax[item_code][tax.description] = frappe._dict(dict(
743 tax_rate = tax_rate,
744 tax_amount = tax_amount
745 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530746
Nabin Hait34c551d2019-07-03 10:34:31 +0530747 if with_tax_account:
748 itemised_tax[item_code][tax.description].tax_account = tax.account_head
749
Nabin Haitb962fc12017-07-17 18:02:31 +0530750 return itemised_tax
751
752def get_itemised_taxable_amount(items):
753 itemised_taxable_amount = frappe._dict()
754 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530755 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530756 itemised_taxable_amount.setdefault(item_code, 0)
757 itemised_taxable_amount[item_code] += item.net_amount
758
Nabin Haitcaab5822017-08-24 16:22:28 +0530759 return itemised_taxable_amount
760
761def get_rounded_tax_amount(itemised_tax, precision):
762 # Rounding based on tax_amount precision
763 for taxes in itemised_tax.values():
764 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530765 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530766
767class init_landed_taxes_and_totals(object):
768 def __init__(self, doc):
769 self.doc = doc
770 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
771 self.set_account_currency()
772 self.set_exchange_rate()
773 self.set_amounts_in_company_currency()
774
775 def set_account_currency(self):
776 company_currency = erpnext.get_company_currency(self.doc.company)
777 for d in self.doc.get(self.tax_field):
778 if not d.account_currency:
779 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
780 d.account_currency = account_currency or company_currency
781
782 def set_exchange_rate(self):
783 company_currency = erpnext.get_company_currency(self.doc.company)
784 for d in self.doc.get(self.tax_field):
785 if d.account_currency == company_currency:
786 d.exchange_rate = 1
Deepesh Gargb75cbee2021-03-17 10:56:52 +0530787 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530788 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
789 account_currency=d.account_currency, company=self.doc.company)
790
791 if not d.exchange_rate:
792 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
793
794 def set_amounts_in_company_currency(self):
795 for d in self.doc.get(self.tax_field):
796 d.amount = flt(d.amount, d.precision("amount"))
797 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))