blob: 42400bb807cc78216bd5a3deac4fd2c8f9a8efd9 [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
Chillar Anand915b3432021-09-02 16:44:59 +05304
Nabin Hait3237c752015-02-17 11:11:11 +05305import json
Chillar Anand915b3432021-09-02 16:44:59 +05306
7import frappe
Nabin Hait3769d872015-12-18 13:12:02 +05308from frappe import _, scrub
Nabin Haitb962fc12017-07-17 18:02:31 +05309from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
Chillar Anand915b3432021-09-02 16:44:59 +053010
11import erpnext
Deepesh Gargbfc17e42020-12-25 18:34:39 +053012from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
Chillar Anand915b3432021-09-02 16:44:59 +053013from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
14from erpnext.controllers.accounts_controller import (
15 validate_conversion_rate,
16 validate_inclusive_tax,
17 validate_taxes_and_charges,
18)
19from erpnext.stock.get_item_details import _get_item_tax_template
20
Nabin Hait3237c752015-02-17 11:11:11 +053021
Nabin Haitfe81da22015-02-18 12:23:18 +053022class calculate_taxes_and_totals(object):
Nabin Hait3237c752015-02-17 11:11:11 +053023 def __init__(self, doc):
24 self.doc = doc
Deepesh Garg6a5ef262021-02-19 14:30:23 +053025 frappe.flags.round_off_applicable_accounts = []
26 get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
Nabin Haitfe81da22015-02-18 12:23:18 +053027 self.calculate()
28
Nabin Hait3237c752015-02-17 11:11:11 +053029 def calculate(self):
Nabin Haitb315acb2019-07-12 14:27:19 +053030 if not len(self.doc.get("items")):
31 return
32
Nabin Hait3237c752015-02-17 11:11:11 +053033 self.discount_amount_applied = False
34 self._calculate()
35
36 if self.doc.meta.get_field("discount_amount"):
Nabin Hait3769d872015-12-18 13:12:02 +053037 self.set_discount_amount()
Nabin Hait3237c752015-02-17 11:11:11 +053038 self.apply_discount_amount()
39
Nabin Haitbd00e812015-02-17 12:50:51 +053040 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
Nabin Hait3237c752015-02-17 11:11:11 +053041 self.calculate_total_advance()
Vishal Dhayaguded42242d2017-11-29 16:09:59 +053042
Nabin Hait852cb642017-07-05 12:58:19 +053043 if self.doc.meta.get_field("other_charges_calculation"):
44 self.set_item_wise_tax_breakup()
Nabin Hait3237c752015-02-17 11:11:11 +053045
46 def _calculate(self):
Faris Ansarieae2dda2018-05-02 12:19:30 +053047 self.validate_conversion_rate()
Nabin Haite7679702015-02-20 14:40:35 +053048 self.calculate_item_values()
Deepesh Gargef0d26c2020-01-06 15:34:15 +053049 self.validate_item_tax_template()
Nabin Haite7679702015-02-20 14:40:35 +053050 self.initialize_taxes()
51 self.determine_exclusive_rate()
52 self.calculate_net_total()
Subin Toma8e2c022021-11-16 19:06:49 +053053 self.calculate_shipping_charges()
Nabin Haite7679702015-02-20 14:40:35 +053054 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):
Saqib Ansari0452d7d2022-02-08 11:26:23 +0530109 if self.doc.get('is_consolidated'):
110 return
111
Nabin Hait3237c752015-02-17 11:11:11 +0530112 if not self.discount_amount_applied:
113 for item in self.doc.get("items"):
114 self.doc.round_floats_in(item)
115
116 if item.discount_percentage == 100:
117 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530118 elif item.price_list_rate:
119 if not item.rate or (item.pricing_rules and item.discount_percentage > 0):
120 item.rate = flt(item.price_list_rate *
121 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
122 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
123 elif item.discount_amount and item.pricing_rules:
124 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530125
Anupam Kumared42afc2021-03-15 11:11:28 +0530126 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 +0530127 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530128 if flt(item.rate_with_margin) > 0:
129 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530130
Walstan Baptista37b826b2021-04-03 19:48:46 +0530131 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530132 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530133 else:
134 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530135
Nabin Hait64bfdd92019-04-23 13:37:19 +0530136 elif flt(item.price_list_rate) > 0:
137 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530138 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
139 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530140
Nabin Haite7679702015-02-20 14:40:35 +0530141 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530142
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530143 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530144 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530145 elif not item.qty and self.doc.get("is_debit_note"):
146 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530147 else:
148 item.amount = flt(item.rate * item.qty, item.precision("amount"))
149
Nabin Haite7679702015-02-20 14:40:35 +0530150 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530151
Nabin Haite7679702015-02-20 14:40:35 +0530152 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530153
Nabin Haite7679702015-02-20 14:40:35 +0530154 item.item_tax_amount = 0.0
155
156 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530157 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530158 for f in fields:
159 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
160 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530161
162 def initialize_taxes(self):
163 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530164 if not self.discount_amount_applied:
165 validate_taxes_and_charges(tax)
166 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530167
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530168 if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530169 tax.item_wise_tax_detail = {}
170
Nabin Hait3237c752015-02-17 11:11:11 +0530171 tax_fields = ["total", "tax_amount_after_discount_amount",
172 "tax_amount_for_current_item", "grand_total_for_current_item",
173 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
174
Nabin Haitde9c8a92015-02-23 01:06:00 +0530175 if tax.charge_type != "Actual" and \
176 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
177 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530178
179 for fieldname in tax_fields:
180 tax.set(fieldname, 0.0)
181
Nabin Hait3237c752015-02-17 11:11:11 +0530182 self.doc.round_floats_in(tax)
183
Nabin Hait3237c752015-02-17 11:11:11 +0530184 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530185 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530186 return
Nabin Hait3237c752015-02-17 11:11:11 +0530187
188 for item in self.doc.get("items"):
189 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
190 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530191 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530192 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530193 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 +0530194
195 if i==0:
196 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
197 else:
198 tax.grand_total_fraction_for_current_item = \
199 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
200 + tax.tax_fraction_for_current_item
201
202 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530203 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530204
Nabin Hait19ea7212020-08-11 20:34:57 +0530205 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
206 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
207
208 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530209 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530210 item.discount_percentage = flt(item.discount_percentage,
211 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530212
Nabin Haite7679702015-02-20 14:40:35 +0530213 self._set_in_company_currency(item, ["net_rate", "net_amount"])
214
Nabin Hait3237c752015-02-17 11:11:11 +0530215 def _load_item_tax_rate(self, item_tax_rate):
216 return json.loads(item_tax_rate) if item_tax_rate else {}
217
218 def get_current_tax_fraction(self, tax, item_tax_map):
219 """
220 Get tax fraction for calculating tax exclusive amount
221 from tax inclusive amount
222 """
223 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530224 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530225
226 if cint(tax.included_in_print_rate):
227 tax_rate = self._get_tax_rate(tax, item_tax_map)
228
229 if tax.charge_type == "On Net Total":
230 current_tax_fraction = tax_rate / 100.0
231
232 elif tax.charge_type == "On Previous Row Amount":
233 current_tax_fraction = (tax_rate / 100.0) * \
234 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
235
236 elif tax.charge_type == "On Previous Row Total":
237 current_tax_fraction = (tax_rate / 100.0) * \
238 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530239
Nabin Hait19ea7212020-08-11 20:34:57 +0530240 elif tax.charge_type == "On Item Quantity":
241 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530242
Nabin Hait19ea7212020-08-11 20:34:57 +0530243 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
244 current_tax_fraction *= -1.0
245 inclusive_tax_amount_per_qty *= -1.0
246
247 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530248
249 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530250 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530251 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
252 else:
253 return tax.rate
254
255 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530256 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 +0530257
Nabin Hait3237c752015-02-17 11:11:11 +0530258 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530259 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530260 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530261 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530262 self.doc.net_total += item.net_amount
263 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530264
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530265 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530266
Subin Toma8e2c022021-11-16 19:06:49 +0530267 def calculate_shipping_charges(self):
Subin Tomaf1fce02021-11-10 16:49:12 +0530268 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530269 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
270 shipping_rule.apply(self.doc)
271
Nabin Hait3237c752015-02-17 11:11:11 +0530272 def calculate_taxes(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530273 rounding_adjustment_computed = self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment')
274 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530275 self.doc.rounding_adjustment = 0
276
Nabin Hait3237c752015-02-17 11:11:11 +0530277 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530278 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530279 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
280
281 for n, item in enumerate(self.doc.get("items")):
282 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530283 for i, tax in enumerate(self.doc.get("taxes")):
284 # tax_amount represents the amount of tax for the current step
285 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
286
287 # Adjust divisional loss to the last item
288 if tax.charge_type == "Actual":
289 actual_tax_dict[tax.idx] -= current_tax_amount
290 if n == len(self.doc.get("items")) - 1:
291 current_tax_amount += actual_tax_dict[tax.idx]
292
Nabin Hait2b019ed2015-02-22 23:03:07 +0530293 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530294 if tax.charge_type != "Actual" and \
295 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
296 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530297
Nabin Hait3237c752015-02-17 11:11:11 +0530298 # store tax_amount for current item as it will be used for
299 # charge type = 'On Previous Row Amount'
300 tax.tax_amount_for_current_item = current_tax_amount
301
Nabin Hait2b019ed2015-02-22 23:03:07 +0530302 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530303 tax.tax_amount_after_discount_amount += current_tax_amount
304
Nabin Haitcd951342017-07-31 18:07:45 +0530305 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530306
Nabin Hait3237c752015-02-17 11:11:11 +0530307 # note: grand_total_for_current_item contains the contribution of
308 # item's amount, previously applied tax and the current tax on that item
309 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530310 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530311 else:
312 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530313 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530314
315 # set precision in the last item iteration
316 if n == len(self.doc.get("items")) - 1:
317 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530318 self._set_in_company_currency(tax,
319 ["tax_amount", "tax_amount_after_discount_amount"])
320
321 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530322 self.set_cumulative_total(i, tax)
323
Deepesh Gargb6705882021-04-14 11:20:27 +0530324 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530325
Nabin Hait3237c752015-02-17 11:11:11 +0530326 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530327 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Subin Tom75a76e62021-10-29 16:45:04 +0530328 and self.doc.discount_amount \
329 and self.doc.apply_discount_on == "Grand Total" \
Saqib Ansari17445c72022-03-07 18:01:07 +0530330 and not rounding_adjustment_computed:
Nabin Hait2e4de832017-09-19 14:53:16 +0530331 self.doc.rounding_adjustment = flt(self.doc.grand_total
332 - flt(self.doc.discount_amount) - tax.total,
333 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530334
Nabin Haitcd951342017-07-31 18:07:45 +0530335 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
336 # if just for valuation, do not add the tax amount in total
337 # if tax/charges is for deduction, multiply by -1
338 if getattr(tax, "category", None):
339 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530340 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
341 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530342 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530343
Nabin Haitcd951342017-07-31 18:07:45 +0530344 def set_cumulative_total(self, row_idx, tax):
345 tax_amount = tax.tax_amount_after_discount_amount
346 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
347
348 if row_idx == 0:
349 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
350 else:
351 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530352
353 def get_current_tax_amount(self, item, tax, item_tax_map):
354 tax_rate = self._get_tax_rate(tax, item_tax_map)
355 current_tax_amount = 0.0
356
357 if tax.charge_type == "Actual":
358 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530359 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
360 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
361
Nabin Hait3237c752015-02-17 11:11:11 +0530362 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530363 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530364 elif tax.charge_type == "On Previous Row Amount":
365 current_tax_amount = (tax_rate / 100.0) * \
366 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
367 elif tax.charge_type == "On Previous Row Total":
368 current_tax_amount = (tax_rate / 100.0) * \
369 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530370 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530371 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530372
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530373 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530374 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530375
376 return current_tax_amount
377
Nabin Haite7679702015-02-20 14:40:35 +0530378 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
379 # store tax breakup for each item
380 key = item.item_code or item.item_name
381 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
382 if tax.item_wise_tax_detail.get(key):
383 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
384
Nabin Haitcaab5822017-08-24 16:22:28 +0530385 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530386
Nabin Hait3237c752015-02-17 11:11:11 +0530387 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530388 if tax.account_head in frappe.flags.round_off_applicable_accounts:
389 tax.tax_amount = round(tax.tax_amount, 0)
390 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
391
Nabin Haite7679702015-02-20 14:40:35 +0530392 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530393 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530394 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530395
Deepesh Gargb6705882021-04-14 11:20:27 +0530396 def round_off_base_values(self, tax):
397 # Round off to nearest integer based on regional settings
398 if tax.account_head in frappe.flags.round_off_applicable_accounts:
399 tax.base_tax_amount = round(tax.base_tax_amount, 0)
400 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
401
Nabin Haita1bf43b2015-03-17 10:50:47 +0530402 def manipulate_grand_total_for_inclusive_tax(self):
403 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530404 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 +0530405 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530406 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
407 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530408
Nabin Hait2e4de832017-09-19 14:53:16 +0530409 diff = self.doc.total + non_inclusive_tax_amount \
410 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530411
412 # If discount amount applied, deduct the discount amount
413 # because self.doc.total is always without discount, but last_tax.total is after discount
414 if self.discount_amount_applied and self.doc.discount_amount:
415 diff -= flt(self.doc.discount_amount)
416
417 diff = flt(diff, self.doc.precision("rounding_adjustment"))
418
Nabin Hait2e4de832017-09-19 14:53:16 +0530419 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530420 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530421
422 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530423 if self.doc.get("taxes"):
424 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
425 else:
426 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530427
Subin Tom75a76e62021-10-29 16:45:04 +0530428 if self.doc.get("taxes"):
429 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
Nabin Hait2e4de832017-09-19 14:53:16 +0530430 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Subin Tom75a76e62021-10-29 16:45:04 +0530431 else:
432 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530433
Nabin Hait2e4de832017-09-19 14:53:16 +0530434 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530435
Saqiba6f98d42020-07-23 18:51:26 +0530436 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530437 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 +0530438 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530439 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530440 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530441 for tax in self.doc.get("taxes"):
442 if tax.category in ["Valuation and Total", "Total"]:
443 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530444 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530445 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530446 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530447
Nabin Haite7679702015-02-20 14:40:35 +0530448 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530449
Nabin Haite7679702015-02-20 14:40:35 +0530450 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
451 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
452 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530453
Nabin Hait2e4de832017-09-19 14:53:16 +0530454 self._set_in_company_currency(self.doc,
455 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530456
Nabin Haite7679702015-02-20 14:40:35 +0530457 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530458
Nabin Hait2e4de832017-09-19 14:53:16 +0530459 self.set_rounded_total()
460
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530461 def calculate_total_net_weight(self):
462 if self.doc.meta.get_field('total_net_weight'):
463 self.doc.total_net_weight = 0.0
464 for d in self.doc.items:
465 if d.total_weight:
466 self.doc.total_net_weight += d.total_weight
467
Nabin Hait2e4de832017-09-19 14:53:16 +0530468 def set_rounded_total(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530469 if self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment'):
470 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530471
Saqib Ansari17445c72022-03-07 18:01:07 +0530472 if self.doc.meta.get_field("rounded_total"):
473 if self.doc.is_rounded_total_disabled():
474 self.doc.rounded_total = self.doc.base_rounded_total = 0
475 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530476
Saqib Ansari17445c72022-03-07 18:01:07 +0530477 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
478 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait877e1bb2017-11-17 12:27:43 +0530479
Saqib Ansari17445c72022-03-07 18:01:07 +0530480 #if print_in_rate is set, we would have already calculated rounding adjustment
481 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
482 self.doc.precision("rounding_adjustment"))
483
484 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530485
Nabin Hait3237c752015-02-17 11:11:11 +0530486 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530487 if not self.doc.get('is_consolidated'):
488 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530489 if not tax.get("dont_recompute_tax"):
490 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530491
Nabin Hait3769d872015-12-18 13:12:02 +0530492 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530493 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530494 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530495 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530496
497 def apply_discount_amount(self):
498 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530499 if not self.doc.apply_discount_on:
500 frappe.throw(_("Please select Apply Discount On"))
501
Nabin Hait3237c752015-02-17 11:11:11 +0530502 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
503 self.doc.precision("base_discount_amount"))
504
Nabin Haite7679702015-02-20 14:40:35 +0530505 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530506 taxes = self.doc.get("taxes")
507 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530508
Nabin Haite7679702015-02-20 14:40:35 +0530509 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530510 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530511 for i, item in enumerate(self.doc.get("items")):
512 distributed_amount = flt(self.doc.discount_amount) * \
513 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530514
Nabin Haite7679702015-02-20 14:40:35 +0530515 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530516 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530517
Nabin Hait25bd84d2015-03-04 15:06:56 +0530518 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530519 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 +0530520 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530521 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530522 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530523
Anand Doshiec5ec602015-03-05 19:31:23 +0530524 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530525 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530526
Nabin Hait51e980d2015-10-10 18:10:05 +0530527 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 +0530528
Nabin Haite7679702015-02-20 14:40:35 +0530529 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530530
531 self.discount_amount_applied = True
532 self._calculate()
533 else:
534 self.doc.base_discount_amount = 0
535
Nabin Haite7679702015-02-20 14:40:35 +0530536 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530537 if self.doc.apply_discount_on == "Net Total":
538 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530539 else:
540 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530541
Nabin Haite7679702015-02-20 14:40:35 +0530542 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530543 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530544 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
545 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530546 elif tax.row_id in actual_taxes_dict:
547 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
548 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530549
Nabin Hait877e1bb2017-11-17 12:27:43 +0530550 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
551 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530552
553
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530554 def calculate_total_advance(self):
555 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530556 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
557 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530558
Nabin Haite7679702015-02-20 14:40:35 +0530559 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530560
Faris Ansari6041f5c2018-02-08 13:33:52 +0530561 grand_total = self.doc.rounded_total or self.doc.grand_total
562
Nabin Hait289ffb72016-02-08 11:06:55 +0530563 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530564 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530565 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530566 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530567 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530568 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530569 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530570 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530571
Nabin Haitadc09232016-02-09 10:31:11 +0530572 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530573 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
574 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530575
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530576 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530577 self.calculate_outstanding_amount()
578
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530579 def is_internal_invoice(self):
580 """
581 Checks if its an internal transfer invoice
582 and decides if to calculate any out standing amount or not
583 """
584
585 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
586 return True
587
588 return False
589
Nabin Hait3237c752015-02-17 11:11:11 +0530590 def calculate_outstanding_amount(self):
591 # NOTE:
592 # write_off_amount is only for POS Invoice
593 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530594 if self.doc.doctype == "Sales Invoice":
595 self.calculate_paid_amount()
596
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530597 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
598 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530599
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530600 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530601 self._set_in_company_currency(self.doc, ['write_off_amount'])
602
Nabin Hait877e1bb2017-11-17 12:27:43 +0530603 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
604 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530605 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
606
Nabin Hait877e1bb2017-11-17 12:27:43 +0530607 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530608 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530609 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
610 else:
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530611 total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
612 - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530613
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530614 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530615 change_amount = 0
616
Deepesh Garg0ebace52020-02-25 13:21:16 +0530617 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530618 self.calculate_write_off_amount()
619 self.calculate_change_amount()
620 change_amount = self.doc.change_amount \
621 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
622
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530623 paid_amount = self.doc.paid_amount \
624 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530625
Nabin Hait877e1bb2017-11-17 12:27:43 +0530626 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
627 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530628
Deepesh Garg0ebace52020-02-25 13:21:16 +0530629 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 +0530630 self.set_total_amount_to_default_mop(total_amount_to_pay)
631 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530632
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530633 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530634
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530635 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530636
637 if self.doc.is_pos:
638 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530639 payment.amount = flt(payment.amount)
640 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530641 paid_amount += payment.amount
642 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530643 elif not self.doc.is_return:
644 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530645
Manas Solankida486ee2018-07-06 12:36:57 +0530646 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
647 base_paid_amount += self.doc.loyalty_amount
648 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
649
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530650 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
651 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
652
Nabin Hait3bb1a422016-08-02 16:41:10 +0530653 def calculate_change_amount(self):
654 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530655 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530656 grand_total = self.doc.rounded_total or self.doc.grand_total
657 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530658
659 if self.doc.doctype == "Sales Invoice" \
Saqib Ansari75256862022-02-09 10:05:06 +0530660 and self.doc.paid_amount > grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530661 and any(d.type == "Cash" for d in self.doc.payments):
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530662
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530663 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530664 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530665
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530666 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530667 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530668
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530669 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530670 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530671 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
672 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530673 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
674 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530675
mbauskar36b51892016-01-18 16:31:10 +0530676 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530677 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530678 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530679 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530680 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530681 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530682 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530683 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530684
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530685 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
686 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530687 item.margin_type = pricing_rule.margin_type
688 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530689 has_margin = True
690
691 if not has_margin:
692 item.margin_type = None
693 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530694
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530695 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
696 item.margin_type = "Amount"
697 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
698 item.precision("margin_rate_or_amount"))
699 item.rate_with_margin = item.rate
700
701 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530702 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 +0530703 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530704 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530705
Shreya Shahbe690ef2017-11-14 17:22:41 +0530706 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530707
708 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530709 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530710
Subin Tom7d627df2021-08-23 11:05:07 +0530711 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530712 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
713 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530714
Deepesh Garg0ebace52020-02-25 13:21:16 +0530715 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530716 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530717 self.doc.append('payments', {
718 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530719 'amount': total_amount_to_pay,
720 'default': 1
Ankush Menatb147b852021-09-01 16:45:57 +0530721 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530722
Nabin Hait9c421612017-07-20 13:32:01 +0530723def get_itemised_tax_breakup_html(doc):
724 if not doc.taxes:
725 return
726 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530727
Nabin Hait9c421612017-07-20 13:32:01 +0530728 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530729 tax_accounts = []
730 for tax in doc.taxes:
731 if getattr(tax, "category", None) and tax.category=="Valuation":
732 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530733 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530734 tax_accounts.append(tax.description)
735
Nabin Hait9c421612017-07-20 13:32:01 +0530736 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530737
Nabin Hait9c421612017-07-20 13:32:01 +0530738 # get tax breakup data
739 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530740
741 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
742
rohitwaghchaured4526682017-12-28 14:20:13 +0530743 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530744 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530745
Nabin Hait9c421612017-07-20 13:32:01 +0530746 return frappe.render_template(
747 "templates/includes/itemised_tax_breakup.html", dict(
748 headers=headers,
749 itemised_tax=itemised_tax,
750 itemised_taxable_amount=itemised_taxable_amount,
751 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530752 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530753 )
Nabin Hait9c421612017-07-20 13:32:01 +0530754 )
Nabin Hait852cb642017-07-05 12:58:19 +0530755
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530756@frappe.whitelist()
757def get_round_off_applicable_accounts(company, account_list):
758 account_list = get_regional_round_off_accounts(company, account_list)
759
760 return account_list
761
762@erpnext.allow_regional
763def get_regional_round_off_accounts(company, account_list):
764 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530765
766@erpnext.allow_regional
767def update_itemised_tax_data(doc):
768 #Don't delete this method, used for localization
769 pass
770
Nabin Haitb962fc12017-07-17 18:02:31 +0530771@erpnext.allow_regional
772def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
773 return [_("Item"), _("Taxable Amount")] + tax_accounts
774
775@erpnext.allow_regional
776def get_itemised_tax_breakup_data(doc):
777 itemised_tax = get_itemised_tax(doc.taxes)
778
779 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
780
781 return itemised_tax, itemised_taxable_amount
782
Nabin Hait34c551d2019-07-03 10:34:31 +0530783def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530784 itemised_tax = {}
785 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530786 if getattr(tax, "category", None) and tax.category=="Valuation":
787 continue
788
Nabin Haitb962fc12017-07-17 18:02:31 +0530789 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530790 if item_tax_map:
791 for item_code, tax_data in item_tax_map.items():
792 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530793
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530794 tax_rate = 0.0
795 tax_amount = 0.0
796
Nabin Hait2e4de832017-09-19 14:53:16 +0530797 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530798 tax_rate = flt(tax_data[0])
799 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530800 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530801 tax_rate = flt(tax_data)
802
803 itemised_tax[item_code][tax.description] = frappe._dict(dict(
804 tax_rate = tax_rate,
805 tax_amount = tax_amount
806 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530807
Nabin Hait34c551d2019-07-03 10:34:31 +0530808 if with_tax_account:
809 itemised_tax[item_code][tax.description].tax_account = tax.account_head
810
Nabin Haitb962fc12017-07-17 18:02:31 +0530811 return itemised_tax
812
813def get_itemised_taxable_amount(items):
814 itemised_taxable_amount = frappe._dict()
815 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530816 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530817 itemised_taxable_amount.setdefault(item_code, 0)
818 itemised_taxable_amount[item_code] += item.net_amount
819
Nabin Haitcaab5822017-08-24 16:22:28 +0530820 return itemised_taxable_amount
821
822def get_rounded_tax_amount(itemised_tax, precision):
823 # Rounding based on tax_amount precision
824 for taxes in itemised_tax.values():
825 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530826 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530827
828class init_landed_taxes_and_totals(object):
829 def __init__(self, doc):
830 self.doc = doc
831 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
832 self.set_account_currency()
833 self.set_exchange_rate()
834 self.set_amounts_in_company_currency()
835
836 def set_account_currency(self):
837 company_currency = erpnext.get_company_currency(self.doc.company)
838 for d in self.doc.get(self.tax_field):
839 if not d.account_currency:
840 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
841 d.account_currency = account_currency or company_currency
842
843 def set_exchange_rate(self):
844 company_currency = erpnext.get_company_currency(self.doc.company)
845 for d in self.doc.get(self.tax_field):
846 if d.account_currency == company_currency:
847 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530848 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530849 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
850 account_currency=d.account_currency, company=self.doc.company)
851
852 if not d.exchange_rate:
853 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
854
855 def set_amounts_in_company_currency(self):
856 for d in self.doc.get(self.tax_field):
857 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530858 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))