blob: 2776628227708ae259ea0851fdd9fbc57a7de473 [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):
Subin Tom75a76e62021-10-29 16:45:04 +0530273 if not self.doc.get('is_consolidated'):
274 self.doc.rounding_adjustment = 0
275
Nabin Hait3237c752015-02-17 11:11:11 +0530276 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530277 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530278 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
279
280 for n, item in enumerate(self.doc.get("items")):
281 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530282 for i, tax in enumerate(self.doc.get("taxes")):
283 # tax_amount represents the amount of tax for the current step
284 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
285
286 # Adjust divisional loss to the last item
287 if tax.charge_type == "Actual":
288 actual_tax_dict[tax.idx] -= current_tax_amount
289 if n == len(self.doc.get("items")) - 1:
290 current_tax_amount += actual_tax_dict[tax.idx]
291
Nabin Hait2b019ed2015-02-22 23:03:07 +0530292 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530293 if tax.charge_type != "Actual" and \
294 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
295 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530296
Nabin Hait3237c752015-02-17 11:11:11 +0530297 # store tax_amount for current item as it will be used for
298 # charge type = 'On Previous Row Amount'
299 tax.tax_amount_for_current_item = current_tax_amount
300
Nabin Hait2b019ed2015-02-22 23:03:07 +0530301 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530302 tax.tax_amount_after_discount_amount += current_tax_amount
303
Nabin Haitcd951342017-07-31 18:07:45 +0530304 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530305
Nabin Hait3237c752015-02-17 11:11:11 +0530306 # note: grand_total_for_current_item contains the contribution of
307 # item's amount, previously applied tax and the current tax on that item
308 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530309 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530310 else:
311 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530312 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530313
314 # set precision in the last item iteration
315 if n == len(self.doc.get("items")) - 1:
316 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530317 self._set_in_company_currency(tax,
318 ["tax_amount", "tax_amount_after_discount_amount"])
319
320 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530321 self.set_cumulative_total(i, tax)
322
Deepesh Gargb6705882021-04-14 11:20:27 +0530323 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530324
Nabin Hait3237c752015-02-17 11:11:11 +0530325 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530326 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Subin Tom75a76e62021-10-29 16:45:04 +0530327 and self.doc.discount_amount \
328 and self.doc.apply_discount_on == "Grand Total" \
329 and not self.doc.get('is_consolidated'):
Nabin Hait2e4de832017-09-19 14:53:16 +0530330 self.doc.rounding_adjustment = flt(self.doc.grand_total
331 - flt(self.doc.discount_amount) - tax.total,
332 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530333
Nabin Haitcd951342017-07-31 18:07:45 +0530334 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
335 # if just for valuation, do not add the tax amount in total
336 # if tax/charges is for deduction, multiply by -1
337 if getattr(tax, "category", None):
338 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530339 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
340 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530341 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530342
Nabin Haitcd951342017-07-31 18:07:45 +0530343 def set_cumulative_total(self, row_idx, tax):
344 tax_amount = tax.tax_amount_after_discount_amount
345 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
346
347 if row_idx == 0:
348 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
349 else:
350 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530351
352 def get_current_tax_amount(self, item, tax, item_tax_map):
353 tax_rate = self._get_tax_rate(tax, item_tax_map)
354 current_tax_amount = 0.0
355
356 if tax.charge_type == "Actual":
357 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530358 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
359 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
360
Nabin Hait3237c752015-02-17 11:11:11 +0530361 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530362 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530363 elif tax.charge_type == "On Previous Row Amount":
364 current_tax_amount = (tax_rate / 100.0) * \
365 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
366 elif tax.charge_type == "On Previous Row Total":
367 current_tax_amount = (tax_rate / 100.0) * \
368 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530369 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530370 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530371
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530372 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530373 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530374
375 return current_tax_amount
376
Nabin Haite7679702015-02-20 14:40:35 +0530377 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
378 # store tax breakup for each item
379 key = item.item_code or item.item_name
380 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
381 if tax.item_wise_tax_detail.get(key):
382 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
383
Nabin Haitcaab5822017-08-24 16:22:28 +0530384 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530385
Nabin Hait3237c752015-02-17 11:11:11 +0530386 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530387 if tax.account_head in frappe.flags.round_off_applicable_accounts:
388 tax.tax_amount = round(tax.tax_amount, 0)
389 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
390
Nabin Haite7679702015-02-20 14:40:35 +0530391 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530392 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530393 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530394
Deepesh Gargb6705882021-04-14 11:20:27 +0530395 def round_off_base_values(self, tax):
396 # Round off to nearest integer based on regional settings
397 if tax.account_head in frappe.flags.round_off_applicable_accounts:
398 tax.base_tax_amount = round(tax.base_tax_amount, 0)
399 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
400
Nabin Haita1bf43b2015-03-17 10:50:47 +0530401 def manipulate_grand_total_for_inclusive_tax(self):
402 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530403 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 +0530404 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530405 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
406 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530407
Nabin Hait2e4de832017-09-19 14:53:16 +0530408 diff = self.doc.total + non_inclusive_tax_amount \
409 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530410
411 # If discount amount applied, deduct the discount amount
412 # because self.doc.total is always without discount, but last_tax.total is after discount
413 if self.discount_amount_applied and self.doc.discount_amount:
414 diff -= flt(self.doc.discount_amount)
415
416 diff = flt(diff, self.doc.precision("rounding_adjustment"))
417
Nabin Hait2e4de832017-09-19 14:53:16 +0530418 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530419 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530420
421 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530422 if self.doc.get("taxes"):
423 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
424 else:
425 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530426
Subin Tom75a76e62021-10-29 16:45:04 +0530427 if self.doc.get("taxes"):
428 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
Nabin Hait2e4de832017-09-19 14:53:16 +0530429 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Subin Tom75a76e62021-10-29 16:45:04 +0530430 else:
431 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530432
Nabin Hait2e4de832017-09-19 14:53:16 +0530433 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530434
Saqiba6f98d42020-07-23 18:51:26 +0530435 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530436 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 +0530437 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530438 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530439 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530440 for tax in self.doc.get("taxes"):
441 if tax.category in ["Valuation and Total", "Total"]:
442 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530443 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530444 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530445 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530446
Nabin Haite7679702015-02-20 14:40:35 +0530447 self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530448
Nabin Haite7679702015-02-20 14:40:35 +0530449 self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
450 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
451 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530452
Nabin Hait2e4de832017-09-19 14:53:16 +0530453 self._set_in_company_currency(self.doc,
454 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530455
Nabin Haite7679702015-02-20 14:40:35 +0530456 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530457
Nabin Hait2e4de832017-09-19 14:53:16 +0530458 self.set_rounded_total()
459
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530460 def calculate_total_net_weight(self):
461 if self.doc.meta.get_field('total_net_weight'):
462 self.doc.total_net_weight = 0.0
463 for d in self.doc.items:
464 if d.total_weight:
465 self.doc.total_net_weight += d.total_weight
466
Nabin Hait2e4de832017-09-19 14:53:16 +0530467 def set_rounded_total(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530468 if not self.doc.get('is_consolidated'):
469 if self.doc.meta.get_field("rounded_total"):
470 if self.doc.is_rounded_total_disabled():
471 self.doc.rounded_total = self.doc.base_rounded_total = 0
472 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530473
Subin Tom75a76e62021-10-29 16:45:04 +0530474 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
475 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530476
Subin Tom75a76e62021-10-29 16:45:04 +0530477 #if print_in_rate is set, we would have already calculated rounding adjustment
478 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
479 self.doc.precision("rounding_adjustment"))
Nabin Hait877e1bb2017-11-17 12:27:43 +0530480
Subin Tom75a76e62021-10-29 16:45:04 +0530481 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530482
Nabin Hait3237c752015-02-17 11:11:11 +0530483 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530484 if not self.doc.get('is_consolidated'):
485 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530486 if not tax.get("dont_recompute_tax"):
487 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530488
Nabin Hait3769d872015-12-18 13:12:02 +0530489 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530490 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530491 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530492 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530493
494 def apply_discount_amount(self):
495 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530496 if not self.doc.apply_discount_on:
497 frappe.throw(_("Please select Apply Discount On"))
498
Nabin Hait3237c752015-02-17 11:11:11 +0530499 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
500 self.doc.precision("base_discount_amount"))
501
Nabin Haite7679702015-02-20 14:40:35 +0530502 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530503 taxes = self.doc.get("taxes")
504 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530505
Nabin Haite7679702015-02-20 14:40:35 +0530506 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530507 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530508 for i, item in enumerate(self.doc.get("items")):
509 distributed_amount = flt(self.doc.discount_amount) * \
510 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530511
Nabin Haite7679702015-02-20 14:40:35 +0530512 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530513 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530514
Nabin Hait25bd84d2015-03-04 15:06:56 +0530515 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530516 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 +0530517 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530518 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530519 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530520
Anand Doshiec5ec602015-03-05 19:31:23 +0530521 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530522 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530523
Nabin Hait51e980d2015-10-10 18:10:05 +0530524 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 +0530525
Nabin Haite7679702015-02-20 14:40:35 +0530526 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530527
528 self.discount_amount_applied = True
529 self._calculate()
530 else:
531 self.doc.base_discount_amount = 0
532
Nabin Haite7679702015-02-20 14:40:35 +0530533 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530534 if self.doc.apply_discount_on == "Net Total":
535 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530536 else:
537 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530538
Nabin Haite7679702015-02-20 14:40:35 +0530539 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530540 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530541 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
542 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530543 elif tax.row_id in actual_taxes_dict:
544 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
545 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530546
Nabin Hait877e1bb2017-11-17 12:27:43 +0530547 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
548 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530549
550
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530551 def calculate_total_advance(self):
552 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530553 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
554 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530555
Nabin Haite7679702015-02-20 14:40:35 +0530556 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530557
Faris Ansari6041f5c2018-02-08 13:33:52 +0530558 grand_total = self.doc.rounded_total or self.doc.grand_total
559
Nabin Hait289ffb72016-02-08 11:06:55 +0530560 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530561 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530562 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530563 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530564 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530565 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530566 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530567 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530568
Nabin Haitadc09232016-02-09 10:31:11 +0530569 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530570 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
571 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530572
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530573 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530574 self.calculate_outstanding_amount()
575
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530576 def is_internal_invoice(self):
577 """
578 Checks if its an internal transfer invoice
579 and decides if to calculate any out standing amount or not
580 """
581
582 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
583 return True
584
585 return False
586
Nabin Hait3237c752015-02-17 11:11:11 +0530587 def calculate_outstanding_amount(self):
588 # NOTE:
589 # write_off_amount is only for POS Invoice
590 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530591 if self.doc.doctype == "Sales Invoice":
592 self.calculate_paid_amount()
593
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530594 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
595 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530596
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530597 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530598 self._set_in_company_currency(self.doc, ['write_off_amount'])
599
Nabin Hait877e1bb2017-11-17 12:27:43 +0530600 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
601 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530602 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
603
Nabin Hait877e1bb2017-11-17 12:27:43 +0530604 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530605 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530606 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
607 else:
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530608 total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
609 - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530610
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530611 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530612 change_amount = 0
613
Deepesh Garg0ebace52020-02-25 13:21:16 +0530614 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530615 self.calculate_write_off_amount()
616 self.calculate_change_amount()
617 change_amount = self.doc.change_amount \
618 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
619
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530620 paid_amount = self.doc.paid_amount \
621 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530622
Nabin Hait877e1bb2017-11-17 12:27:43 +0530623 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
624 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530625
Deepesh Garg0ebace52020-02-25 13:21:16 +0530626 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 +0530627 self.set_total_amount_to_default_mop(total_amount_to_pay)
628 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530629
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530630 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530631
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530632 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530633
634 if self.doc.is_pos:
635 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530636 payment.amount = flt(payment.amount)
637 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530638 paid_amount += payment.amount
639 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530640 elif not self.doc.is_return:
641 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530642
Manas Solankida486ee2018-07-06 12:36:57 +0530643 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
644 base_paid_amount += self.doc.loyalty_amount
645 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
646
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530647 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
648 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
649
Nabin Hait3bb1a422016-08-02 16:41:10 +0530650 def calculate_change_amount(self):
651 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530652 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530653 grand_total = self.doc.rounded_total or self.doc.grand_total
654 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530655
656 if self.doc.doctype == "Sales Invoice" \
Saqib Ansari75256862022-02-09 10:05:06 +0530657 and self.doc.paid_amount > grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530658 and any(d.type == "Cash" for d in self.doc.payments):
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530659
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530660 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530661 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530662
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530663 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530664 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530665
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530666 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530667 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530668 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
669 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530670 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
671 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530672
mbauskar36b51892016-01-18 16:31:10 +0530673 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530674 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530675 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530676 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530677 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530678 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530679 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530680 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530681
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530682 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
683 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530684 item.margin_type = pricing_rule.margin_type
685 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530686 has_margin = True
687
688 if not has_margin:
689 item.margin_type = None
690 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530691
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530692 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
693 item.margin_type = "Amount"
694 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
695 item.precision("margin_rate_or_amount"))
696 item.rate_with_margin = item.rate
697
698 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530699 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 +0530700 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530701 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530702
Shreya Shahbe690ef2017-11-14 17:22:41 +0530703 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530704
705 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530706 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530707
Subin Tom7d627df2021-08-23 11:05:07 +0530708 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530709 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
710 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530711
Deepesh Garg0ebace52020-02-25 13:21:16 +0530712 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530713 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530714 self.doc.append('payments', {
715 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530716 'amount': total_amount_to_pay,
717 'default': 1
Ankush Menatb147b852021-09-01 16:45:57 +0530718 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530719
Nabin Hait9c421612017-07-20 13:32:01 +0530720def get_itemised_tax_breakup_html(doc):
721 if not doc.taxes:
722 return
723 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530724
Nabin Hait9c421612017-07-20 13:32:01 +0530725 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530726 tax_accounts = []
727 for tax in doc.taxes:
728 if getattr(tax, "category", None) and tax.category=="Valuation":
729 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530730 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530731 tax_accounts.append(tax.description)
732
Nabin Hait9c421612017-07-20 13:32:01 +0530733 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530734
Nabin Hait9c421612017-07-20 13:32:01 +0530735 # get tax breakup data
736 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530737
738 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
739
rohitwaghchaured4526682017-12-28 14:20:13 +0530740 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530741 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530742
Nabin Hait9c421612017-07-20 13:32:01 +0530743 return frappe.render_template(
744 "templates/includes/itemised_tax_breakup.html", dict(
745 headers=headers,
746 itemised_tax=itemised_tax,
747 itemised_taxable_amount=itemised_taxable_amount,
748 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530749 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530750 )
Nabin Hait9c421612017-07-20 13:32:01 +0530751 )
Nabin Hait852cb642017-07-05 12:58:19 +0530752
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530753@frappe.whitelist()
754def get_round_off_applicable_accounts(company, account_list):
755 account_list = get_regional_round_off_accounts(company, account_list)
756
757 return account_list
758
759@erpnext.allow_regional
760def get_regional_round_off_accounts(company, account_list):
761 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530762
763@erpnext.allow_regional
764def update_itemised_tax_data(doc):
765 #Don't delete this method, used for localization
766 pass
767
Nabin Haitb962fc12017-07-17 18:02:31 +0530768@erpnext.allow_regional
769def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
770 return [_("Item"), _("Taxable Amount")] + tax_accounts
771
772@erpnext.allow_regional
773def get_itemised_tax_breakup_data(doc):
774 itemised_tax = get_itemised_tax(doc.taxes)
775
776 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
777
778 return itemised_tax, itemised_taxable_amount
779
Nabin Hait34c551d2019-07-03 10:34:31 +0530780def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530781 itemised_tax = {}
782 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530783 if getattr(tax, "category", None) and tax.category=="Valuation":
784 continue
785
Nabin Haitb962fc12017-07-17 18:02:31 +0530786 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530787 if item_tax_map:
788 for item_code, tax_data in item_tax_map.items():
789 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530790
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530791 tax_rate = 0.0
792 tax_amount = 0.0
793
Nabin Hait2e4de832017-09-19 14:53:16 +0530794 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530795 tax_rate = flt(tax_data[0])
796 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530797 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530798 tax_rate = flt(tax_data)
799
800 itemised_tax[item_code][tax.description] = frappe._dict(dict(
801 tax_rate = tax_rate,
802 tax_amount = tax_amount
803 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530804
Nabin Hait34c551d2019-07-03 10:34:31 +0530805 if with_tax_account:
806 itemised_tax[item_code][tax.description].tax_account = tax.account_head
807
Nabin Haitb962fc12017-07-17 18:02:31 +0530808 return itemised_tax
809
810def get_itemised_taxable_amount(items):
811 itemised_taxable_amount = frappe._dict()
812 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530813 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530814 itemised_taxable_amount.setdefault(item_code, 0)
815 itemised_taxable_amount[item_code] += item.net_amount
816
Nabin Haitcaab5822017-08-24 16:22:28 +0530817 return itemised_taxable_amount
818
819def get_rounded_tax_amount(itemised_tax, precision):
820 # Rounding based on tax_amount precision
821 for taxes in itemised_tax.values():
822 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530823 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530824
825class init_landed_taxes_and_totals(object):
826 def __init__(self, doc):
827 self.doc = doc
828 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
829 self.set_account_currency()
830 self.set_exchange_rate()
831 self.set_amounts_in_company_currency()
832
833 def set_account_currency(self):
834 company_currency = erpnext.get_company_currency(self.doc.company)
835 for d in self.doc.get(self.tax_field):
836 if not d.account_currency:
837 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
838 d.account_currency = account_currency or company_currency
839
840 def set_exchange_rate(self):
841 company_currency = erpnext.get_company_currency(self.doc.company)
842 for d in self.doc.get(self.tax_field):
843 if d.account_currency == company_currency:
844 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530845 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530846 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
847 account_currency=d.account_currency, company=self.doc.company)
848
849 if not d.exchange_rate:
850 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
851
852 def set_amounts_in_company_currency(self):
853 for d in self.doc.get(self.tax_field):
854 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530855 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))