blob: 1ae7310747b1ac5fca49f220c8f8cc5c38e6259c [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
Deepesh Garg6a5ef262021-02-19 14:30:23 +053018 frappe.flags.round_off_applicable_accounts = []
19 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053020 self.calculate()
21
Nabin Hait3237c752015-02-17 11:11:11 +053022 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053023 if not len(self.doc.get("items")):
24 return
25
Nabin Hait3237c752015-02-17 11:11:11 +053026 self.discount_amount_applied = False
27 self._calculate()
28
29 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053030 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053031 self.apply_discount_amount()
32
Nabin Haitbd00e812015-02-17 12:50:51 +053033 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053034 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053035
Nabin Hait852cb642017-07-05 12:58:19 +053036 if self.doc.meta.get_field("other_charges_calculation"):
37 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053038
39 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053040 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053041 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053042 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053043 self.initialize_taxes()
44 self.determine_exclusive_rate()
45 self.calculate_net_total()
46 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053047 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053048 self.calculate_totals()
49 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053050 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053051
Deepesh Gargef0d26c2020-01-06 15:34:15 +053052 def validate_item_tax_template(self):
53 for item in self.doc.get('items'):
54 if item.item_code and item.get('item_tax_template'):
55 item_doc = frappe.get_cached_doc("Item", item.item_code)
56 args = {
57 'tax_category': self.doc.get('tax_category'),
58 'posting_date': self.doc.get('posting_date'),
59 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050060 'transaction_date': self.doc.get('transaction_date'),
61 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053062 }
63
64 item_group = item_doc.item_group
65 item_group_taxes = []
66
67 while item_group:
68 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
69 item_group_taxes += item_group_doc.taxes or []
70 item_group = item_group_doc.parent_item_group
71
72 item_taxes = item_doc.taxes or []
73
74 if not item_group_taxes and (not item_taxes):
75 # No validation if no taxes in item or item group
76 continue
77
78 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
79
80 if item.item_tax_template not in taxes:
81 frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
82 item.idx, frappe.bold(item.item_code)
83 ))
84
Nabin Haite7679702015-02-20 14:40:35 +053085 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053086 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053087 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +053088 if not self.doc.currency or self.doc.currency == company_currency:
89 self.doc.currency = company_currency
90 self.doc.conversion_rate = 1.0
91 else:
92 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
93 self.doc.meta.get_label("conversion_rate"), self.doc.company)
94
95 self.doc.conversion_rate = flt(self.doc.conversion_rate)
96
Nabin Hait3237c752015-02-17 11:11:11 +053097 def calculate_item_values(self):
98 if not self.discount_amount_applied:
99 for item in self.doc.get("items"):
100 self.doc.round_floats_in(item)
101
102 if item.discount_percentage == 100:
103 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530104 elif item.price_list_rate:
105 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
106 item.rate = flt(item.price_list_rate *
107 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
108 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
109 elif item.discount_amount and item.pricing_rules:
110 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530111
Anupam Kumared42afc2021-03-15 11:11:28 +0530112 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 +0530113 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530114 if flt(item.rate_with_margin) > 0:
115 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530116
Walstan Baptista37b826b2021-04-03 19:48:46 +0530117 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530118 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530119 else:
120 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530121
Nabin Hait64bfdd92019-04-23 13:37:19 +0530122 elif flt(item.price_list_rate) > 0:
123 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530124 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
125 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530126
Nabin Haite7679702015-02-20 14:40:35 +0530127 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530128
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530129 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530130 item.amount = flt(-1 * item.rate, item.precision("amount"))
131 else:
132 item.amount = flt(item.rate * item.qty, item.precision("amount"))
133
Nabin Haite7679702015-02-20 14:40:35 +0530134 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530135
Nabin Haite7679702015-02-20 14:40:35 +0530136 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530137
Nabin Haite7679702015-02-20 14:40:35 +0530138 item.item_tax_amount = 0.0
139
140 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530141 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530142 for f in fields:
143 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
144 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530145
146 def initialize_taxes(self):
147 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530148 if not self.discount_amount_applied:
149 validate_taxes_and_charges(tax)
150 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530151
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530152 if not self.doc.get('is_consolidated'):
153 tax.item_wise_tax_detail = {}
154
Nabin Hait3237c752015-02-17 11:11:11 +0530155 tax_fields = ["total", "tax_amount_after_discount_amount",
156 "tax_amount_for_current_item", "grand_total_for_current_item",
157 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
158
Nabin Haitde9c8a92015-02-23 01:06:00 +0530159 if tax.charge_type != "Actual" and \
160 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
161 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530162
163 for fieldname in tax_fields:
164 tax.set(fieldname, 0.0)
165
Nabin Hait3237c752015-02-17 11:11:11 +0530166 self.doc.round_floats_in(tax)
167
Nabin Hait3237c752015-02-17 11:11:11 +0530168 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530169 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
170 return
Nabin Hait3237c752015-02-17 11:11:11 +0530171
172 for item in self.doc.get("items"):
173 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
174 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530175 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530176 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530177 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 +0530178
179 if i==0:
180 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
181 else:
182 tax.grand_total_fraction_for_current_item = \
183 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
184 + tax.tax_fraction_for_current_item
185
186 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530187 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530188
Nabin Hait19ea7212020-08-11 20:34:57 +0530189 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
190 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
191
192 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530193 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530194 item.discount_percentage = flt(item.discount_percentage,
195 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530196
Nabin Haite7679702015-02-20 14:40:35 +0530197 self._set_in_company_currency(item, ["net_rate", "net_amount"])
198
Nabin Hait3237c752015-02-17 11:11:11 +0530199 def _load_item_tax_rate(self, item_tax_rate):
200 return json.loads(item_tax_rate) if item_tax_rate else {}
201
202 def get_current_tax_fraction(self, tax, item_tax_map):
203 """
204 Get tax fraction for calculating tax exclusive amount
205 from tax inclusive amount
206 """
207 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530208 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530209
210 if cint(tax.included_in_print_rate):
211 tax_rate = self._get_tax_rate(tax, item_tax_map)
212
213 if tax.charge_type == "On Net Total":
214 current_tax_fraction = tax_rate / 100.0
215
216 elif tax.charge_type == "On Previous Row Amount":
217 current_tax_fraction = (tax_rate / 100.0) * \
218 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
219
220 elif tax.charge_type == "On Previous Row Total":
221 current_tax_fraction = (tax_rate / 100.0) * \
222 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530223
Nabin Hait19ea7212020-08-11 20:34:57 +0530224 elif tax.charge_type == "On Item Quantity":
225 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530226
Nabin Hait19ea7212020-08-11 20:34:57 +0530227 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
228 current_tax_fraction *= -1.0
229 inclusive_tax_amount_per_qty *= -1.0
230
231 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530232
233 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530234 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530235 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
236 else:
237 return tax.rate
238
239 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530240 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 +0530241
Nabin Hait3237c752015-02-17 11:11:11 +0530242 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530243 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530244 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530245 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530246 self.doc.net_total += item.net_amount
247 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530248
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530249 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530250
251 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530252 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530253 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530254 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530255 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
256
257 for n, item in enumerate(self.doc.get("items")):
258 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530259 for i, tax in enumerate(self.doc.get("taxes")):
260 # tax_amount represents the amount of tax for the current step
261 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
262
263 # Adjust divisional loss to the last item
264 if tax.charge_type == "Actual":
265 actual_tax_dict[tax.idx] -= current_tax_amount
266 if n == len(self.doc.get("items")) - 1:
267 current_tax_amount += actual_tax_dict[tax.idx]
268
Nabin Hait2b019ed2015-02-22 23:03:07 +0530269 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530270 if tax.charge_type != "Actual" and \
271 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
272 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530273
Nabin Hait3237c752015-02-17 11:11:11 +0530274 # store tax_amount for current item as it will be used for
275 # charge type = 'On Previous Row Amount'
276 tax.tax_amount_for_current_item = current_tax_amount
277
Nabin Hait2b019ed2015-02-22 23:03:07 +0530278 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530279 tax.tax_amount_after_discount_amount += current_tax_amount
280
Nabin Haitcd951342017-07-31 18:07:45 +0530281 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530282
Nabin Hait3237c752015-02-17 11:11:11 +0530283 # note: grand_total_for_current_item contains the contribution of
284 # item's amount, previously applied tax and the current tax on that item
285 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530286 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530287 else:
288 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530289 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530290
291 # set precision in the last item iteration
292 if n == len(self.doc.get("items")) - 1:
293 self.round_off_totals(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530294 self.set_cumulative_total(i, tax)
295
296 self._set_in_company_currency(tax,
297 ["total", "tax_amount", "tax_amount_after_discount_amount"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530298
Nabin Hait3237c752015-02-17 11:11:11 +0530299 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530300 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530301 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530302 self.doc.rounding_adjustment = flt(self.doc.grand_total
303 - flt(self.doc.discount_amount) - tax.total,
304 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530305
Nabin Haitcd951342017-07-31 18:07:45 +0530306 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
307 # if just for valuation, do not add the tax amount in total
308 # if tax/charges is for deduction, multiply by -1
309 if getattr(tax, "category", None):
310 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530311 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
312 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530313 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530314
Nabin Haitcd951342017-07-31 18:07:45 +0530315 def set_cumulative_total(self, row_idx, tax):
316 tax_amount = tax.tax_amount_after_discount_amount
317 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
318
319 if row_idx == 0:
320 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
321 else:
322 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530323
324 def get_current_tax_amount(self, item, tax, item_tax_map):
325 tax_rate = self._get_tax_rate(tax, item_tax_map)
326 current_tax_amount = 0.0
327
328 if tax.charge_type == "Actual":
329 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530330 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
331 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
332
Nabin Hait3237c752015-02-17 11:11:11 +0530333 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530334 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530335 elif tax.charge_type == "On Previous Row Amount":
336 current_tax_amount = (tax_rate / 100.0) * \
337 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
338 elif tax.charge_type == "On Previous Row Total":
339 current_tax_amount = (tax_rate / 100.0) * \
340 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530341 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530342 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530343
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530344 current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount)
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530345
346 if not self.doc.get("is_consolidated"):
347 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530348
349 return current_tax_amount
350
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530351 def get_final_current_tax_amount(self, tax, current_tax_amount):
352 # Some countries need individual tax components to be rounded
353 # Handeled via regional doctypess
354 if tax.account_head in frappe.flags.round_off_applicable_accounts:
355 current_tax_amount = round(current_tax_amount, 0)
356 return current_tax_amount
357
Nabin Haite7679702015-02-20 14:40:35 +0530358 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
359 # store tax breakup for each item
360 key = item.item_code or item.item_name
361 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
362 if tax.item_wise_tax_detail.get(key):
363 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
364
Nabin Haitcaab5822017-08-24 16:22:28 +0530365 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530366
Nabin Hait3237c752015-02-17 11:11:11 +0530367 def round_off_totals(self, tax):
Nabin Haite7679702015-02-20 14:40:35 +0530368 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530369 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530370 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530371
Nabin Haita1bf43b2015-03-17 10:50:47 +0530372 def manipulate_grand_total_for_inclusive_tax(self):
373 # if fully inclusive taxes and diff
Nabin Hait2e4de832017-09-19 14:53:16 +0530374 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 +0530375 last_tax = self.doc.get("taxes")[-1]
Nabin Hait2e4de832017-09-19 14:53:16 +0530376 non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
377 for d in self.doc.get("taxes") if not d.included_in_print_rate])
Nabin Haitf32fc232019-12-25 13:59:24 +0530378
Nabin Hait2e4de832017-09-19 14:53:16 +0530379 diff = self.doc.total + non_inclusive_tax_amount \
380 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530381
382 # If discount amount applied, deduct the discount amount
383 # because self.doc.total is always without discount, but last_tax.total is after discount
384 if self.discount_amount_applied and self.doc.discount_amount:
385 diff -= flt(self.doc.discount_amount)
386
387 diff = flt(diff, self.doc.precision("rounding_adjustment"))
388
Nabin Hait2e4de832017-09-19 14:53:16 +0530389 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530390 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530391
392 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530393 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
394 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530395
Nabin Hait2e4de832017-09-19 14:53:16 +0530396 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
397 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530398
Nabin Hait2e4de832017-09-19 14:53:16 +0530399 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530400
Saqiba6f98d42020-07-23 18:51:26 +0530401 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530402 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 +0530403 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530404 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530405 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530406 for tax in self.doc.get("taxes"):
407 if tax.category in ["Valuation and Total", "Total"]:
408 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530409 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530410 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530411 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530412
Nabin Haite7679702015-02-20 14:40:35 +0530413 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530414
Nabin Haite7679702015-02-20 14:40:35 +0530415 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
416 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
417 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530418
Nabin Hait2e4de832017-09-19 14:53:16 +0530419 self._set_in_company_currency(self.doc,
420 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530421
Nabin Haite7679702015-02-20 14:40:35 +0530422 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530423
Nabin Hait2e4de832017-09-19 14:53:16 +0530424 self.set_rounded_total()
425
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530426 def calculate_total_net_weight(self):
427 if self.doc.meta.get_field('total_net_weight'):
428 self.doc.total_net_weight = 0.0
429 for d in self.doc.items:
430 if d.total_weight:
431 self.doc.total_net_weight += d.total_weight
432
Nabin Hait2e4de832017-09-19 14:53:16 +0530433 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530434 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530435 if self.doc.is_rounded_total_disabled():
436 self.doc.rounded_total = self.doc.base_rounded_total = 0
437 return
438
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530439 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530440 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530441
Nabin Hait877e1bb2017-11-17 12:27:43 +0530442 #if print_in_rate is set, we would have already calculated rounding adjustment
443 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
444 self.doc.precision("rounding_adjustment"))
445
Nabin Hait02ac9012017-11-22 16:12:20 +0530446 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530447
Nabin Hait3237c752015-02-17 11:11:11 +0530448 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530449 if not self.doc.get('is_consolidated'):
450 for tax in self.doc.get("taxes"):
451 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530452
Nabin Hait3769d872015-12-18 13:12:02 +0530453 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530454 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530455 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530456 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530457
458 def apply_discount_amount(self):
459 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530460 if not self.doc.apply_discount_on:
461 frappe.throw(_("Please select Apply Discount On"))
462
Nabin Hait3237c752015-02-17 11:11:11 +0530463 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
464 self.doc.precision("base_discount_amount"))
465
Nabin Haite7679702015-02-20 14:40:35 +0530466 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530467 taxes = self.doc.get("taxes")
468 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530469
Nabin Haite7679702015-02-20 14:40:35 +0530470 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530471 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530472 for i, item in enumerate(self.doc.get("items")):
473 distributed_amount = flt(self.doc.discount_amount) * \
474 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530475
Nabin Haite7679702015-02-20 14:40:35 +0530476 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530477 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530478
Nabin Hait25bd84d2015-03-04 15:06:56 +0530479 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530480 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 +0530481 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530482 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530483 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530484
Anand Doshiec5ec602015-03-05 19:31:23 +0530485 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530486 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530487
Nabin Hait51e980d2015-10-10 18:10:05 +0530488 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 +0530489
Nabin Haite7679702015-02-20 14:40:35 +0530490 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530491
492 self.discount_amount_applied = True
493 self._calculate()
494 else:
495 self.doc.base_discount_amount = 0
496
Nabin Haite7679702015-02-20 14:40:35 +0530497 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530498 if self.doc.apply_discount_on == "Net Total":
499 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530500 else:
501 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530502
Nabin Haite7679702015-02-20 14:40:35 +0530503 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530504 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530505 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
506 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530507 elif tax.row_id in actual_taxes_dict:
508 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
509 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530510
Nabin Hait877e1bb2017-11-17 12:27:43 +0530511 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
512 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530513
514
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530515 def calculate_total_advance(self):
516 if self.doc.docstatus < 2:
Nabin Haite7679702015-02-20 14:40:35 +0530517 total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530518 for adv in self.doc.get("advances")])
519
Nabin Haite7679702015-02-20 14:40:35 +0530520 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530521
Faris Ansari6041f5c2018-02-08 13:33:52 +0530522 grand_total = self.doc.rounded_total or self.doc.grand_total
523
Nabin Hait289ffb72016-02-08 11:06:55 +0530524 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530525 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530526 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530527 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530528 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530529 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530530 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530531 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530532
Nabin Haitadc09232016-02-09 10:31:11 +0530533 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530534 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
535 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530536
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530537 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530538 self.calculate_outstanding_amount()
539
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530540 def is_internal_invoice(self):
541 """
542 Checks if its an internal transfer invoice
543 and decides if to calculate any out standing amount or not
544 """
545
546 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
547 return True
548
549 return False
550
Nabin Hait3237c752015-02-17 11:11:11 +0530551 def calculate_outstanding_amount(self):
552 # NOTE:
553 # write_off_amount is only for POS Invoice
554 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530555 if self.doc.doctype == "Sales Invoice":
556 self.calculate_paid_amount()
557
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530558 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
559 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530560
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530561 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530562 self._set_in_company_currency(self.doc, ['write_off_amount'])
563
Nabin Hait877e1bb2017-11-17 12:27:43 +0530564 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
565 grand_total = self.doc.rounded_total or self.doc.grand_total
566 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530567 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530568 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
569 else:
570 total_amount_to_pay = flt(flt(grand_total *
571 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
572 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530573
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530574 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530575 change_amount = 0
576
Deepesh Garg0ebace52020-02-25 13:21:16 +0530577 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530578 self.calculate_write_off_amount()
579 self.calculate_change_amount()
580 change_amount = self.doc.change_amount \
581 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
582
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530583 paid_amount = self.doc.paid_amount \
584 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530585
Nabin Hait877e1bb2017-11-17 12:27:43 +0530586 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
587 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530588
Deepesh Garg0ebace52020-02-25 13:21:16 +0530589 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
590 self.update_paid_amount_for_return(total_amount_to_pay)
591
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530592 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530593
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530594 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530595
596 if self.doc.is_pos:
597 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530598 payment.amount = flt(payment.amount)
599 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530600 paid_amount += payment.amount
601 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530602 elif not self.doc.is_return:
603 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530604
Manas Solankida486ee2018-07-06 12:36:57 +0530605 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
606 base_paid_amount += self.doc.loyalty_amount
607 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
608
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530609 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
610 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
611
Nabin Hait3bb1a422016-08-02 16:41:10 +0530612 def calculate_change_amount(self):
613 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530614 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530615
616 if self.doc.doctype == "Sales Invoice" \
617 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530618 and any([d.type == "Cash" for d in self.doc.payments]):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530619 grand_total = self.doc.rounded_total or self.doc.grand_total
620 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530621
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530622 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530623 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530624
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530625 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530626 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530627
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530628 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530629 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530630 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
631 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530632 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
633 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530634
mbauskar36b51892016-01-18 16:31:10 +0530635 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530636 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530637 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530638 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530639 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530640 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530641 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530642 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530643
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530644 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
645 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530646 item.margin_type = pricing_rule.margin_type
647 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530648 has_margin = True
649
650 if not has_margin:
651 item.margin_type = None
652 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530653
mbauskara52472c2016-03-05 15:10:25 +0530654 if item.margin_type and item.margin_rate_or_amount:
655 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 +0530656 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530657 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530658
Shreya Shahbe690ef2017-11-14 17:22:41 +0530659 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530660
661 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530662 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530663
Deepesh Garg0ebace52020-02-25 13:21:16 +0530664 def update_paid_amount_for_return(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530665 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
666 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530667
668 self.doc.payments = []
669
670 if default_mode_of_payment:
671 self.doc.append('payments', {
672 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530673 'amount': total_amount_to_pay,
674 'default': 1
Deepesh Garg0ebace52020-02-25 13:21:16 +0530675 })
676 else:
677 self.doc.is_pos = 0
678 self.doc.pos_profile = ''
679
680 self.calculate_paid_amount()
681
682
Nabin Hait9c421612017-07-20 13:32:01 +0530683def get_itemised_tax_breakup_html(doc):
684 if not doc.taxes:
685 return
686 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530687
Nabin Hait9c421612017-07-20 13:32:01 +0530688 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530689 tax_accounts = []
690 for tax in doc.taxes:
691 if getattr(tax, "category", None) and tax.category=="Valuation":
692 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530693 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530694 tax_accounts.append(tax.description)
695
Nabin Hait9c421612017-07-20 13:32:01 +0530696 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530697
Nabin Hait9c421612017-07-20 13:32:01 +0530698 # get tax breakup data
699 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530700
701 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
702
rohitwaghchaured4526682017-12-28 14:20:13 +0530703 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530704 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530705
Nabin Hait9c421612017-07-20 13:32:01 +0530706 return frappe.render_template(
707 "templates/includes/itemised_tax_breakup.html", dict(
708 headers=headers,
709 itemised_tax=itemised_tax,
710 itemised_taxable_amount=itemised_taxable_amount,
711 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530712 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530713 )
Nabin Hait9c421612017-07-20 13:32:01 +0530714 )
Nabin Hait852cb642017-07-05 12:58:19 +0530715
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530716@frappe.whitelist()
717def get_round_off_applicable_accounts(company, account_list):
718 account_list = get_regional_round_off_accounts(company, account_list)
719
720 return account_list
721
722@erpnext.allow_regional
723def get_regional_round_off_accounts(company, account_list):
724 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530725
726@erpnext.allow_regional
727def update_itemised_tax_data(doc):
728 #Don't delete this method, used for localization
729 pass
730
Nabin Haitb962fc12017-07-17 18:02:31 +0530731@erpnext.allow_regional
732def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
733 return [_("Item"), _("Taxable Amount")] + tax_accounts
734
735@erpnext.allow_regional
736def get_itemised_tax_breakup_data(doc):
737 itemised_tax = get_itemised_tax(doc.taxes)
738
739 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
740
741 return itemised_tax, itemised_taxable_amount
742
Nabin Hait34c551d2019-07-03 10:34:31 +0530743def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530744 itemised_tax = {}
745 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530746 if getattr(tax, "category", None) and tax.category=="Valuation":
747 continue
748
Nabin Haitb962fc12017-07-17 18:02:31 +0530749 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530750 if item_tax_map:
751 for item_code, tax_data in item_tax_map.items():
752 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530753
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530754 tax_rate = 0.0
755 tax_amount = 0.0
756
Nabin Hait2e4de832017-09-19 14:53:16 +0530757 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530758 tax_rate = flt(tax_data[0])
759 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530760 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530761 tax_rate = flt(tax_data)
762
763 itemised_tax[item_code][tax.description] = frappe._dict(dict(
764 tax_rate = tax_rate,
765 tax_amount = tax_amount
766 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530767
Nabin Hait34c551d2019-07-03 10:34:31 +0530768 if with_tax_account:
769 itemised_tax[item_code][tax.description].tax_account = tax.account_head
770
Nabin Haitb962fc12017-07-17 18:02:31 +0530771 return itemised_tax
772
773def get_itemised_taxable_amount(items):
774 itemised_taxable_amount = frappe._dict()
775 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530776 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530777 itemised_taxable_amount.setdefault(item_code, 0)
778 itemised_taxable_amount[item_code] += item.net_amount
779
Nabin Haitcaab5822017-08-24 16:22:28 +0530780 return itemised_taxable_amount
781
782def get_rounded_tax_amount(itemised_tax, precision):
783 # Rounding based on tax_amount precision
784 for taxes in itemised_tax.values():
785 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530786 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530787
788class init_landed_taxes_and_totals(object):
789 def __init__(self, doc):
790 self.doc = doc
791 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
792 self.set_account_currency()
793 self.set_exchange_rate()
794 self.set_amounts_in_company_currency()
795
796 def set_account_currency(self):
797 company_currency = erpnext.get_company_currency(self.doc.company)
798 for d in self.doc.get(self.tax_field):
799 if not d.account_currency:
800 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
801 d.account_currency = account_currency or company_currency
802
803 def set_exchange_rate(self):
804 company_currency = erpnext.get_company_currency(self.doc.company)
805 for d in self.doc.get(self.tax_field):
806 if d.account_currency == company_currency:
807 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530808 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530809 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
810 account_currency=d.account_currency, company=self.doc.company)
811
812 if not d.exchange_rate:
813 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
814
815 def set_amounts_in_company_currency(self):
816 for d in self.doc.get(self.tax_field):
817 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530818 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))