blob: 70cc8a58bfeb5cd243fa20b30cfb7463f6b650ef [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
Chillar Anand915b3432021-09-02 16:44:59 +05305
Nabin Hait3237c752015-02-17 11:11:11 +05306import json
Chillar Anand915b3432021-09-02 16:44:59 +05307
8import frappe
Nabin Hait3769d872015-12-18 13:12:02 +05309from frappe import _, scrub
Nabin Haitb962fc12017-07-17 18:02:31 +053010from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
Chillar Anand915b3432021-09-02 16:44:59 +053011
12import erpnext
Deepesh Gargbfc17e42020-12-25 18:34:39 +053013from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Chillar Anand915b3432021-09-02 16:44:59 +053014from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
15from erpnext.controllers.accounts_controller import (
16 validate_conversion_rate,
17 validate_inclusive_tax,
18 validate_taxes_and_charges,
19)
20from erpnext.stock.get_item_details import _get_item_tax_template
21
Nabin Hait3237c752015-02-17 11:11:11 +053022
Nabin Haitfe81da22015-02-18 12:23:18 +053023class calculate_taxes_and_totals(object):
Nabin Hait3237c752015-02-17 11:11:11 +053024 def __init__(self, doc):
25 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053026 frappe.flags.round_off_applicable_accounts = []
27 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053028 self.calculate()
29
Nabin Hait3237c752015-02-17 11:11:11 +053030 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053031 if not len(self.doc.get("items")):
32 return
33
Nabin Hait3237c752015-02-17 11:11:11 +053034 self.discount_amount_applied = False
35 self._calculate()
36
37 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053038 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053039 self.apply_discount_amount()
40
Nabin Haitbd00e812015-02-17 12:50:51 +053041 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053042 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053043
Nabin Hait852cb642017-07-05 12:58:19 +053044 if self.doc.meta.get_field("other_charges_calculation"):
45 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053046
47 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053048 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053049 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053050 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053051 self.initialize_taxes()
52 self.determine_exclusive_rate()
53 self.calculate_net_total()
54 self.calculate_taxes()
Nabin Haita1bf43b2015-03-17 10:50:47 +053055 self.manipulate_grand_total_for_inclusive_tax()
Nabin Haite7679702015-02-20 14:40:35 +053056 self.calculate_totals()
57 self._cleanup()
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +053058 self.calculate_total_net_weight()
Nabin Haite7679702015-02-20 14:40:35 +053059
Deepesh Gargef0d26c2020-01-06 15:34:15 +053060 def validate_item_tax_template(self):
61 for item in self.doc.get('items'):
62 if item.item_code and item.get('item_tax_template'):
63 item_doc = frappe.get_cached_doc("Item", item.item_code)
64 args = {
Deepesh Garg8a7e2832021-06-04 22:53:26 +053065 'net_rate': item.net_rate or item.rate,
Deepesh Gargef0d26c2020-01-06 15:34:15 +053066 'tax_category': self.doc.get('tax_category'),
67 'posting_date': self.doc.get('posting_date'),
68 'bill_date': self.doc.get('bill_date'),
mohammadahmad1990728bf0e2020-06-18 12:21:42 +050069 'transaction_date': self.doc.get('transaction_date'),
70 'company': self.doc.get('company')
Deepesh Gargef0d26c2020-01-06 15:34:15 +053071 }
72
73 item_group = item_doc.item_group
74 item_group_taxes = []
75
76 while item_group:
77 item_group_doc = frappe.get_cached_doc('Item Group', item_group)
78 item_group_taxes += item_group_doc.taxes or []
79 item_group = item_group_doc.parent_item_group
80
81 item_taxes = item_doc.taxes or []
82
83 if not item_group_taxes and (not item_taxes):
84 # No validation if no taxes in item or item group
85 continue
86
87 taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
88
Deepesh Garg18be7672021-06-06 13:25:34 +053089 if taxes:
90 if item.item_tax_template not in taxes:
91 item.item_tax_template = taxes[0]
92 frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
93 item.idx, frappe.bold(item.item_code)
94 ))
Deepesh Gargef0d26c2020-01-06 15:34:15 +053095
Nabin Haite7679702015-02-20 14:40:35 +053096 def validate_conversion_rate(self):
Nabin Hait3237c752015-02-17 11:11:11 +053097 # validate conversion rate
Rushabh Mehtacc8b2b22017-03-31 12:44:29 +053098 company_currency = erpnext.get_company_currency(self.doc.company)
Nabin Hait3237c752015-02-17 11:11:11 +053099 if not self.doc.currency or self.doc.currency == company_currency:
100 self.doc.currency = company_currency
101 self.doc.conversion_rate = 1.0
102 else:
103 validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
104 self.doc.meta.get_label("conversion_rate"), self.doc.company)
105
106 self.doc.conversion_rate = flt(self.doc.conversion_rate)
107
Nabin Hait3237c752015-02-17 11:11:11 +0530108 def calculate_item_values(self):
109 if not self.discount_amount_applied:
110 for item in self.doc.get("items"):
111 self.doc.round_floats_in(item)
112
113 if item.discount_percentage == 100:
114 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530115 elif item.price_list_rate:
116 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
117 item.rate = flt(item.price_list_rate *
118 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
119 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
120 elif item.discount_amount and item.pricing_rules:
121 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530122
Anupam Kumared42afc2021-03-15 11:11:28 +0530123 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 +0530124 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530125 if flt(item.rate_with_margin) > 0:
126 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530127
Walstan Baptista37b826b2021-04-03 19:48:46 +0530128 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530129 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530130 else:
131 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530132
Nabin Hait64bfdd92019-04-23 13:37:19 +0530133 elif flt(item.price_list_rate) > 0:
134 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530135 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
136 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530137
Nabin Haite7679702015-02-20 14:40:35 +0530138 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530139
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530140 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530141 item.amount = flt(-1 * item.rate, item.precision("amount"))
142 else:
143 item.amount = flt(item.rate * item.qty, item.precision("amount"))
144
Nabin Haite7679702015-02-20 14:40:35 +0530145 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530146
Nabin Haite7679702015-02-20 14:40:35 +0530147 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530148
Nabin Haite7679702015-02-20 14:40:35 +0530149 item.item_tax_amount = 0.0
150
151 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530152 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530153 for f in fields:
154 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
155 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530156
157 def initialize_taxes(self):
158 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530159 if not self.discount_amount_applied:
160 validate_taxes_and_charges(tax)
161 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530162
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530163 if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530164 tax.item_wise_tax_detail = {}
165
Nabin Hait3237c752015-02-17 11:11:11 +0530166 tax_fields = ["total", "tax_amount_after_discount_amount",
167 "tax_amount_for_current_item", "grand_total_for_current_item",
168 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
169
Nabin Haitde9c8a92015-02-23 01:06:00 +0530170 if tax.charge_type != "Actual" and \
171 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
172 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530173
174 for fieldname in tax_fields:
175 tax.set(fieldname, 0.0)
176
Nabin Hait3237c752015-02-17 11:11:11 +0530177 self.doc.round_floats_in(tax)
178
Nabin Hait3237c752015-02-17 11:11:11 +0530179 def determine_exclusive_rate(self):
Nabin Hait37b047d2015-02-23 16:01:33 +0530180 if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
181 return
Nabin Hait3237c752015-02-17 11:11:11 +0530182
183 for item in self.doc.get("items"):
184 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
185 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530186 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530187 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530188 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 +0530189
190 if i==0:
191 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
192 else:
193 tax.grand_total_fraction_for_current_item = \
194 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
195 + tax.tax_fraction_for_current_item
196
197 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530198 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530199
Nabin Hait19ea7212020-08-11 20:34:57 +0530200 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
201 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
202
203 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530204 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530205 item.discount_percentage = flt(item.discount_percentage,
206 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530207
Nabin Haite7679702015-02-20 14:40:35 +0530208 self._set_in_company_currency(item, ["net_rate", "net_amount"])
209
Nabin Hait3237c752015-02-17 11:11:11 +0530210 def _load_item_tax_rate(self, item_tax_rate):
211 return json.loads(item_tax_rate) if item_tax_rate else {}
212
213 def get_current_tax_fraction(self, tax, item_tax_map):
214 """
215 Get tax fraction for calculating tax exclusive amount
216 from tax inclusive amount
217 """
218 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530219 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530220
221 if cint(tax.included_in_print_rate):
222 tax_rate = self._get_tax_rate(tax, item_tax_map)
223
224 if tax.charge_type == "On Net Total":
225 current_tax_fraction = tax_rate / 100.0
226
227 elif tax.charge_type == "On Previous Row Amount":
228 current_tax_fraction = (tax_rate / 100.0) * \
229 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
230
231 elif tax.charge_type == "On Previous Row Total":
232 current_tax_fraction = (tax_rate / 100.0) * \
233 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530234
Nabin Hait19ea7212020-08-11 20:34:57 +0530235 elif tax.charge_type == "On Item Quantity":
236 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530237
Nabin Hait19ea7212020-08-11 20:34:57 +0530238 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
239 current_tax_fraction *= -1.0
240 inclusive_tax_amount_per_qty *= -1.0
241
242 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530243
244 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530245 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530246 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
247 else:
248 return tax.rate
249
250 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530251 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 +0530252
Nabin Hait3237c752015-02-17 11:11:11 +0530253 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530254 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530255 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530256 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530257 self.doc.net_total += item.net_amount
258 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530259
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530260 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530261
262 def calculate_taxes(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530263 self.doc.rounding_adjustment = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530264 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530265 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530266 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
267
268 for n, item in enumerate(self.doc.get("items")):
269 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530270 for i, tax in enumerate(self.doc.get("taxes")):
271 # tax_amount represents the amount of tax for the current step
272 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
273
274 # Adjust divisional loss to the last item
275 if tax.charge_type == "Actual":
276 actual_tax_dict[tax.idx] -= current_tax_amount
277 if n == len(self.doc.get("items")) - 1:
278 current_tax_amount += actual_tax_dict[tax.idx]
279
Nabin Hait2b019ed2015-02-22 23:03:07 +0530280 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530281 if tax.charge_type != "Actual" and \
282 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
283 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530284
Nabin Hait3237c752015-02-17 11:11:11 +0530285 # store tax_amount for current item as it will be used for
286 # charge type = 'On Previous Row Amount'
287 tax.tax_amount_for_current_item = current_tax_amount
288
Nabin Hait2b019ed2015-02-22 23:03:07 +0530289 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530290 tax.tax_amount_after_discount_amount += current_tax_amount
291
Nabin Haitcd951342017-07-31 18:07:45 +0530292 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530293
Nabin Hait3237c752015-02-17 11:11:11 +0530294 # note: grand_total_for_current_item contains the contribution of
295 # item's amount, previously applied tax and the current tax on that item
296 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530297 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530298 else:
299 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530300 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530301
302 # set precision in the last item iteration
303 if n == len(self.doc.get("items")) - 1:
304 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530305 self._set_in_company_currency(tax,
306 ["tax_amount", "tax_amount_after_discount_amount"])
307
308 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530309 self.set_cumulative_total(i, tax)
310
Deepesh Gargb6705882021-04-14 11:20:27 +0530311 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530312
Nabin Hait3237c752015-02-17 11:11:11 +0530313 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530314 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Nabin Haitdb53a782015-07-31 16:53:13 +0530315 and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
Nabin Hait2e4de832017-09-19 14:53:16 +0530316 self.doc.rounding_adjustment = flt(self.doc.grand_total
317 - flt(self.doc.discount_amount) - tax.total,
318 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530319
Nabin Haitcd951342017-07-31 18:07:45 +0530320 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
321 # if just for valuation, do not add the tax amount in total
322 # if tax/charges is for deduction, multiply by -1
323 if getattr(tax, "category", None):
324 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530325 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
326 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530327 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530328
Nabin Haitcd951342017-07-31 18:07:45 +0530329 def set_cumulative_total(self, row_idx, tax):
330 tax_amount = tax.tax_amount_after_discount_amount
331 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
332
333 if row_idx == 0:
334 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
335 else:
336 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530337
338 def get_current_tax_amount(self, item, tax, item_tax_map):
339 tax_rate = self._get_tax_rate(tax, item_tax_map)
340 current_tax_amount = 0.0
341
342 if tax.charge_type == "Actual":
343 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530344 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
345 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
346
Nabin Hait3237c752015-02-17 11:11:11 +0530347 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530348 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530349 elif tax.charge_type == "On Previous Row Amount":
350 current_tax_amount = (tax_rate / 100.0) * \
351 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
352 elif tax.charge_type == "On Previous Row Total":
353 current_tax_amount = (tax_rate / 100.0) * \
354 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530355 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530356 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530357
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530358 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530359 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530360
361 return current_tax_amount
362
Nabin Haite7679702015-02-20 14:40:35 +0530363 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
364 # store tax breakup for each item
365 key = item.item_code or item.item_name
366 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
367 if tax.item_wise_tax_detail.get(key):
368 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
369
Nabin Haitcaab5822017-08-24 16:22:28 +0530370 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530371
Nabin Hait3237c752015-02-17 11:11:11 +0530372 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530373 if tax.account_head in frappe.flags.round_off_applicable_accounts:
374 tax.tax_amount = round(tax.tax_amount, 0)
375 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
376
Nabin Haite7679702015-02-20 14:40:35 +0530377 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530378 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530379 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530380
Deepesh Gargb6705882021-04-14 11:20:27 +0530381 def round_off_base_values(self, tax):
382 # Round off to nearest integer based on regional settings
383 if tax.account_head in frappe.flags.round_off_applicable_accounts:
384 tax.base_tax_amount = round(tax.base_tax_amount, 0)
385 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
386
Nabin Haita1bf43b2015-03-17 10:50:47 +0530387 def manipulate_grand_total_for_inclusive_tax(self):
388 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530389 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 +0530390 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530391 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
392 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530393
Nabin Hait2e4de832017-09-19 14:53:16 +0530394 diff = self.doc.total + non_inclusive_tax_amount \
395 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530396
397 # If discount amount applied, deduct the discount amount
398 # because self.doc.total is always without discount, but last_tax.total is after discount
399 if self.discount_amount_applied and self.doc.discount_amount:
400 diff -= flt(self.doc.discount_amount)
401
402 diff = flt(diff, self.doc.precision("rounding_adjustment"))
403
Nabin Hait2e4de832017-09-19 14:53:16 +0530404 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530405 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530406
407 def calculate_totals(self):
Nabin Hait2e4de832017-09-19 14:53:16 +0530408 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
409 if self.doc.get("taxes") else flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530410
Nabin Hait2e4de832017-09-19 14:53:16 +0530411 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
412 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530413
Nabin Hait2e4de832017-09-19 14:53:16 +0530414 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530415
Saqiba6f98d42020-07-23 18:51:26 +0530416 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530417 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 +0530418 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530419 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530420 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530421 for tax in self.doc.get("taxes"):
422 if tax.category in ["Valuation and Total", "Total"]:
423 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530424 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530425 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530426 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530427
Nabin Haite7679702015-02-20 14:40:35 +0530428 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530429
Nabin Haite7679702015-02-20 14:40:35 +0530430 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
431 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
432 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530433
Nabin Hait2e4de832017-09-19 14:53:16 +0530434 self._set_in_company_currency(self.doc,
435 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530436
Nabin Haite7679702015-02-20 14:40:35 +0530437 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530438
Nabin Hait2e4de832017-09-19 14:53:16 +0530439 self.set_rounded_total()
440
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530441 def calculate_total_net_weight(self):
442 if self.doc.meta.get_field('total_net_weight'):
443 self.doc.total_net_weight = 0.0
444 for d in self.doc.items:
445 if d.total_weight:
446 self.doc.total_net_weight += d.total_weight
447
Nabin Hait2e4de832017-09-19 14:53:16 +0530448 def set_rounded_total(self):
Nabin Hait3237c752015-02-17 11:11:11 +0530449 if self.doc.meta.get_field("rounded_total"):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530450 if self.doc.is_rounded_total_disabled():
451 self.doc.rounded_total = self.doc.base_rounded_total = 0
452 return
453
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530454 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
Nabin Haitfb0b24a2016-01-20 14:46:26 +0530455 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530456
Nabin Hait877e1bb2017-11-17 12:27:43 +0530457 #if print_in_rate is set, we would have already calculated rounding adjustment
458 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
459 self.doc.precision("rounding_adjustment"))
460
Nabin Hait02ac9012017-11-22 16:12:20 +0530461 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530462
Nabin Hait3237c752015-02-17 11:11:11 +0530463 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530464 if not self.doc.get('is_consolidated'):
465 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530466 if not tax.get("dont_recompute_tax"):
467 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530468
Nabin Hait3769d872015-12-18 13:12:02 +0530469 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530470 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530471 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530472 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530473
474 def apply_discount_amount(self):
475 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530476 if not self.doc.apply_discount_on:
477 frappe.throw(_("Please select Apply Discount On"))
478
Nabin Hait3237c752015-02-17 11:11:11 +0530479 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
480 self.doc.precision("base_discount_amount"))
481
Nabin Haite7679702015-02-20 14:40:35 +0530482 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530483 taxes = self.doc.get("taxes")
484 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530485
Nabin Haite7679702015-02-20 14:40:35 +0530486 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530487 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530488 for i, item in enumerate(self.doc.get("items")):
489 distributed_amount = flt(self.doc.discount_amount) * \
490 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530491
Nabin Haite7679702015-02-20 14:40:35 +0530492 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530493 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530494
Nabin Hait25bd84d2015-03-04 15:06:56 +0530495 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530496 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 +0530497 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530498 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530499 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530500
Anand Doshiec5ec602015-03-05 19:31:23 +0530501 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530502 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530503
Nabin Hait51e980d2015-10-10 18:10:05 +0530504 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 +0530505
Nabin Haite7679702015-02-20 14:40:35 +0530506 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530507
508 self.discount_amount_applied = True
509 self._calculate()
510 else:
511 self.doc.base_discount_amount = 0
512
Nabin Haite7679702015-02-20 14:40:35 +0530513 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530514 if self.doc.apply_discount_on == "Net Total":
515 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530516 else:
517 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530518
Nabin Haite7679702015-02-20 14:40:35 +0530519 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530520 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530521 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
522 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530523 elif tax.row_id in actual_taxes_dict:
524 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
525 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530526
Nabin Hait877e1bb2017-11-17 12:27:43 +0530527 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
528 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530529
530
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530531 def calculate_total_advance(self):
532 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530533 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
534 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530535
Nabin Haite7679702015-02-20 14:40:35 +0530536 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530537
Faris Ansari6041f5c2018-02-08 13:33:52 +0530538 grand_total = self.doc.rounded_total or self.doc.grand_total
539
Nabin Hait289ffb72016-02-08 11:06:55 +0530540 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530541 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530542 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530543 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530544 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530545 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530546 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530547 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530548
Nabin Haitadc09232016-02-09 10:31:11 +0530549 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530550 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
551 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530552
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530553 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530554 self.calculate_outstanding_amount()
555
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530556 def is_internal_invoice(self):
557 """
558 Checks if its an internal transfer invoice
559 and decides if to calculate any out standing amount or not
560 """
561
562 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
563 return True
564
565 return False
566
Nabin Hait3237c752015-02-17 11:11:11 +0530567 def calculate_outstanding_amount(self):
568 # NOTE:
569 # write_off_amount is only for POS Invoice
570 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530571 if self.doc.doctype == "Sales Invoice":
572 self.calculate_paid_amount()
573
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530574 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
575 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530576
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530577 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530578 self._set_in_company_currency(self.doc, ['write_off_amount'])
579
Nabin Hait877e1bb2017-11-17 12:27:43 +0530580 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
581 grand_total = self.doc.rounded_total or self.doc.grand_total
582 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530583 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530584 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
585 else:
586 total_amount_to_pay = flt(flt(grand_total *
587 self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
588 - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530589
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530590 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530591 change_amount = 0
592
Deepesh Garg0ebace52020-02-25 13:21:16 +0530593 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530594 self.calculate_write_off_amount()
595 self.calculate_change_amount()
596 change_amount = self.doc.change_amount \
597 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
598
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530599 paid_amount = self.doc.paid_amount \
600 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530601
Nabin Hait877e1bb2017-11-17 12:27:43 +0530602 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
603 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530604
Deepesh Garg0ebace52020-02-25 13:21:16 +0530605 if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
Subin Tom7d627df2021-08-23 11:05:07 +0530606 self.set_total_amount_to_default_mop(total_amount_to_pay)
607 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530608
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530609 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530610
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530611 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530612
613 if self.doc.is_pos:
614 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530615 payment.amount = flt(payment.amount)
616 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530617 paid_amount += payment.amount
618 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530619 elif not self.doc.is_return:
620 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530621
Manas Solankida486ee2018-07-06 12:36:57 +0530622 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
623 base_paid_amount += self.doc.loyalty_amount
624 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
625
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530626 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
627 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
628
Nabin Hait3bb1a422016-08-02 16:41:10 +0530629 def calculate_change_amount(self):
630 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530631 self.doc.base_change_amount = 0.0
Nabin Hait877e1bb2017-11-17 12:27:43 +0530632
633 if self.doc.doctype == "Sales Invoice" \
634 and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530635 and any(d.type == "Cash" for d in self.doc.payments):
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530636 grand_total = self.doc.rounded_total or self.doc.grand_total
637 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530638
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530639 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530640 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530641
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530642 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530643 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530644
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530645 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530646 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530647 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
648 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530649 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
650 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530651
mbauskar36b51892016-01-18 16:31:10 +0530652 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530653 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530654 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530655 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530656 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530657 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530658 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530659 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530660
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530661 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
662 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530663 item.margin_type = pricing_rule.margin_type
664 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530665 has_margin = True
666
667 if not has_margin:
668 item.margin_type = None
669 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530670
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530671 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
672 item.margin_type = "Amount"
673 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
674 item.precision("margin_rate_or_amount"))
675 item.rate_with_margin = item.rate
676
677 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530678 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 +0530679 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530680 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530681
Shreya Shahbe690ef2017-11-14 17:22:41 +0530682 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530683
684 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530685 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530686
Subin Tom7d627df2021-08-23 11:05:07 +0530687 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530688 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
689 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530690
Deepesh Garg0ebace52020-02-25 13:21:16 +0530691 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530692 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530693 self.doc.append('payments', {
694 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530695 'amount': total_amount_to_pay,
696 'default': 1
Ankush Menatb147b852021-09-01 16:45:57 +0530697 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530698
Nabin Hait9c421612017-07-20 13:32:01 +0530699def get_itemised_tax_breakup_html(doc):
700 if not doc.taxes:
701 return
702 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530703
Nabin Hait9c421612017-07-20 13:32:01 +0530704 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530705 tax_accounts = []
706 for tax in doc.taxes:
707 if getattr(tax, "category", None) and tax.category=="Valuation":
708 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530709 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530710 tax_accounts.append(tax.description)
711
Nabin Hait9c421612017-07-20 13:32:01 +0530712 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530713
Nabin Hait9c421612017-07-20 13:32:01 +0530714 # get tax breakup data
715 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530716
717 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
718
rohitwaghchaured4526682017-12-28 14:20:13 +0530719 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530720 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530721
Nabin Hait9c421612017-07-20 13:32:01 +0530722 return frappe.render_template(
723 "templates/includes/itemised_tax_breakup.html", dict(
724 headers=headers,
725 itemised_tax=itemised_tax,
726 itemised_taxable_amount=itemised_taxable_amount,
727 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530728 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530729 )
Nabin Hait9c421612017-07-20 13:32:01 +0530730 )
Nabin Hait852cb642017-07-05 12:58:19 +0530731
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530732@frappe.whitelist()
733def get_round_off_applicable_accounts(company, account_list):
734 account_list = get_regional_round_off_accounts(company, account_list)
735
736 return account_list
737
738@erpnext.allow_regional
739def get_regional_round_off_accounts(company, account_list):
740 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530741
742@erpnext.allow_regional
743def update_itemised_tax_data(doc):
744 #Don't delete this method, used for localization
745 pass
746
Nabin Haitb962fc12017-07-17 18:02:31 +0530747@erpnext.allow_regional
748def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
749 return [_("Item"), _("Taxable Amount")] + tax_accounts
750
751@erpnext.allow_regional
752def get_itemised_tax_breakup_data(doc):
753 itemised_tax = get_itemised_tax(doc.taxes)
754
755 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
756
757 return itemised_tax, itemised_taxable_amount
758
Nabin Hait34c551d2019-07-03 10:34:31 +0530759def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530760 itemised_tax = {}
761 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530762 if getattr(tax, "category", None) and tax.category=="Valuation":
763 continue
764
Nabin Haitb962fc12017-07-17 18:02:31 +0530765 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530766 if item_tax_map:
767 for item_code, tax_data in item_tax_map.items():
768 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530769
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530770 tax_rate = 0.0
771 tax_amount = 0.0
772
Nabin Hait2e4de832017-09-19 14:53:16 +0530773 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530774 tax_rate = flt(tax_data[0])
775 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530776 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530777 tax_rate = flt(tax_data)
778
779 itemised_tax[item_code][tax.description] = frappe._dict(dict(
780 tax_rate = tax_rate,
781 tax_amount = tax_amount
782 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530783
Nabin Hait34c551d2019-07-03 10:34:31 +0530784 if with_tax_account:
785 itemised_tax[item_code][tax.description].tax_account = tax.account_head
786
Nabin Haitb962fc12017-07-17 18:02:31 +0530787 return itemised_tax
788
789def get_itemised_taxable_amount(items):
790 itemised_taxable_amount = frappe._dict()
791 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530792 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530793 itemised_taxable_amount.setdefault(item_code, 0)
794 itemised_taxable_amount[item_code] += item.net_amount
795
Nabin Haitcaab5822017-08-24 16:22:28 +0530796 return itemised_taxable_amount
797
798def get_rounded_tax_amount(itemised_tax, precision):
799 # Rounding based on tax_amount precision
800 for taxes in itemised_tax.values():
801 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530802 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530803
804class init_landed_taxes_and_totals(object):
805 def __init__(self, doc):
806 self.doc = doc
807 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
808 self.set_account_currency()
809 self.set_exchange_rate()
810 self.set_amounts_in_company_currency()
811
812 def set_account_currency(self):
813 company_currency = erpnext.get_company_currency(self.doc.company)
814 for d in self.doc.get(self.tax_field):
815 if not d.account_currency:
816 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
817 d.account_currency = account_currency or company_currency
818
819 def set_exchange_rate(self):
820 company_currency = erpnext.get_company_currency(self.doc.company)
821 for d in self.doc.get(self.tax_field):
822 if d.account_currency == company_currency:
823 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530824 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530825 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
826 account_currency=d.account_currency, company=self.doc.company)
827
828 if not d.exchange_rate:
829 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
830
831 def set_amounts_in_company_currency(self):
832 for d in self.doc.get(self.tax_field):
833 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530834 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))