blob: a1bb6670c422d0d6a69e1479edffdf9814962763 [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
Deepesh Gargd95f8932022-03-01 23:09:59 +0530116 if not item.rate:
117 item.rate = item.price_list_rate
118
Nabin Hait3237c752015-02-17 11:11:11 +0530119 if item.discount_percentage == 100:
120 item.rate = 0.0
Nabin Hait593242f2019-04-05 19:35:02 +0530121 elif item.price_list_rate:
Ankush Menatbbc47102022-03-01 11:48:28 +0530122 if item.pricing_rules or abs(item.discount_percentage) > 0:
Nabin Hait593242f2019-04-05 19:35:02 +0530123 item.rate = flt(item.price_list_rate *
124 (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Deepesh Gargd95f8932022-03-01 23:09:59 +0530125
126 if abs(item.discount_percentage) > 0:
127 item.discount_amount = item.price_list_rate * (item.discount_percentage / 100.0)
128
Ankush Menatbbc47102022-03-01 11:48:28 +0530129 elif item.discount_amount or item.pricing_rules:
Nabin Hait593242f2019-04-05 19:35:02 +0530130 item.rate = item.price_list_rate - item.discount_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530131
Deepesh Gargd95f8932022-03-01 23:09:59 +0530132 if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item',
133 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
Shreya Shahbe690ef2017-11-14 17:22:41 +0530134 item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
Nabin Hait64bfdd92019-04-23 13:37:19 +0530135 if flt(item.rate_with_margin) > 0:
136 item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
Nabin Hait10c61372021-04-13 15:46:01 +0530137
Walstan Baptista37b826b2021-04-03 19:48:46 +0530138 if item.discount_amount and not item.discount_percentage:
Nabin Hait10c61372021-04-13 15:46:01 +0530139 item.rate = item.rate_with_margin - item.discount_amount
Walstan Baptista37b826b2021-04-03 19:48:46 +0530140 else:
141 item.discount_amount = item.rate_with_margin - item.rate
Nabin Hait10c61372021-04-13 15:46:01 +0530142
Nabin Hait64bfdd92019-04-23 13:37:19 +0530143 elif flt(item.price_list_rate) > 0:
144 item.discount_amount = item.price_list_rate - item.rate
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530145 elif flt(item.price_list_rate) > 0 and not item.discount_amount:
146 item.discount_amount = item.price_list_rate - item.rate
mbauskara52472c2016-03-05 15:10:25 +0530147
Nabin Haite7679702015-02-20 14:40:35 +0530148 item.net_rate = item.rate
Deepesh Gargb65c7612019-07-31 15:58:01 +0530149
deepeshgarg0078bf19ce2019-08-03 13:40:37 +0530150 if not item.qty and self.doc.get("is_return"):
Deepesh Gargb65c7612019-07-31 15:58:01 +0530151 item.amount = flt(-1 * item.rate, item.precision("amount"))
Saqib Ansari04ea42c2021-12-22 13:23:42 +0530152 elif not item.qty and self.doc.get("is_debit_note"):
153 item.amount = flt(item.rate, item.precision("amount"))
Deepesh Gargb65c7612019-07-31 15:58:01 +0530154 else:
155 item.amount = flt(item.rate * item.qty, item.precision("amount"))
156
Nabin Haite7679702015-02-20 14:40:35 +0530157 item.net_amount = item.amount
Nabin Hait3237c752015-02-17 11:11:11 +0530158
Nabin Haite7679702015-02-20 14:40:35 +0530159 self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530160
Nabin Haite7679702015-02-20 14:40:35 +0530161 item.item_tax_amount = 0.0
162
163 def _set_in_company_currency(self, doc, fields):
Nabin Hait3237c752015-02-17 11:11:11 +0530164 """set values in base currency"""
Nabin Haite7679702015-02-20 14:40:35 +0530165 for f in fields:
166 val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
167 doc.set("base_" + f, val)
Nabin Hait3237c752015-02-17 11:11:11 +0530168
169 def initialize_taxes(self):
170 for tax in self.doc.get("taxes"):
Nabin Hait86cd4cc2015-02-28 19:11:51 +0530171 if not self.discount_amount_applied:
172 validate_taxes_and_charges(tax)
173 validate_inclusive_tax(tax, self.doc)
Nabin Hait613d0812015-02-23 11:58:15 +0530174
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530175 if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530176 tax.item_wise_tax_detail = {}
177
Nabin Hait3237c752015-02-17 11:11:11 +0530178 tax_fields = ["total", "tax_amount_after_discount_amount",
179 "tax_amount_for_current_item", "grand_total_for_current_item",
180 "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
181
Nabin Haitde9c8a92015-02-23 01:06:00 +0530182 if tax.charge_type != "Actual" and \
183 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
184 tax_fields.append("tax_amount")
Nabin Hait3237c752015-02-17 11:11:11 +0530185
186 for fieldname in tax_fields:
187 tax.set(fieldname, 0.0)
188
Nabin Hait3237c752015-02-17 11:11:11 +0530189 self.doc.round_floats_in(tax)
190
Nabin Hait3237c752015-02-17 11:11:11 +0530191 def determine_exclusive_rate(self):
Ankush Menat8fe5feb2021-11-04 19:48:32 +0530192 if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
Nabin Hait37b047d2015-02-23 16:01:33 +0530193 return
Nabin Hait3237c752015-02-17 11:11:11 +0530194
195 for item in self.doc.get("items"):
196 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
197 cumulated_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530198 total_inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530199 for i, tax in enumerate(self.doc.get("taxes")):
Nabin Hait19ea7212020-08-11 20:34:57 +0530200 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 +0530201
202 if i==0:
203 tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
204 else:
205 tax.grand_total_fraction_for_current_item = \
206 self.doc.get("taxes")[i-1].grand_total_fraction_for_current_item \
207 + tax.tax_fraction_for_current_item
208
209 cumulated_tax_fraction += tax.tax_fraction_for_current_item
Nabin Haite2ee4552020-08-12 20:58:03 +0530210 total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty)
Nabin Hait3237c752015-02-17 11:11:11 +0530211
Nabin Hait19ea7212020-08-11 20:34:57 +0530212 if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
213 amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
214
215 item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
Nabin Haite7679702015-02-20 14:40:35 +0530216 item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
Nabin Hait2e4de832017-09-19 14:53:16 +0530217 item.discount_percentage = flt(item.discount_percentage,
218 item.precision("discount_percentage"))
Nabin Hait3237c752015-02-17 11:11:11 +0530219
Nabin Haite7679702015-02-20 14:40:35 +0530220 self._set_in_company_currency(item, ["net_rate", "net_amount"])
221
Nabin Hait3237c752015-02-17 11:11:11 +0530222 def _load_item_tax_rate(self, item_tax_rate):
223 return json.loads(item_tax_rate) if item_tax_rate else {}
224
225 def get_current_tax_fraction(self, tax, item_tax_map):
226 """
227 Get tax fraction for calculating tax exclusive amount
228 from tax inclusive amount
229 """
230 current_tax_fraction = 0
Nabin Hait19ea7212020-08-11 20:34:57 +0530231 inclusive_tax_amount_per_qty = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530232
233 if cint(tax.included_in_print_rate):
234 tax_rate = self._get_tax_rate(tax, item_tax_map)
235
236 if tax.charge_type == "On Net Total":
237 current_tax_fraction = tax_rate / 100.0
238
239 elif tax.charge_type == "On Previous Row Amount":
240 current_tax_fraction = (tax_rate / 100.0) * \
241 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
242
243 elif tax.charge_type == "On Previous Row Total":
244 current_tax_fraction = (tax_rate / 100.0) * \
245 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
marination733fd5f2020-08-26 18:23:12 +0530246
Nabin Hait19ea7212020-08-11 20:34:57 +0530247 elif tax.charge_type == "On Item Quantity":
248 inclusive_tax_amount_per_qty = flt(tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530249
Nabin Hait19ea7212020-08-11 20:34:57 +0530250 if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
251 current_tax_fraction *= -1.0
252 inclusive_tax_amount_per_qty *= -1.0
253
254 return current_tax_fraction, inclusive_tax_amount_per_qty
Nabin Hait3237c752015-02-17 11:11:11 +0530255
256 def _get_tax_rate(self, tax, item_tax_map):
Achilles Rasquinha87dab142018-03-08 14:21:48 +0530257 if tax.account_head in item_tax_map:
Nabin Hait3237c752015-02-17 11:11:11 +0530258 return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax))
259 else:
260 return tax.rate
261
262 def calculate_net_total(self):
Shreya Shahe3290382018-05-28 11:49:08 +0530263 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 +0530264
Nabin Hait3237c752015-02-17 11:11:11 +0530265 for item in self.doc.get("items"):
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530266 self.doc.total += item.amount
Shreya Shahe3290382018-05-28 11:49:08 +0530267 self.doc.total_qty += item.qty
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530268 self.doc.base_total += item.base_amount
Nabin Haite7679702015-02-20 14:40:35 +0530269 self.doc.net_total += item.net_amount
270 self.doc.base_net_total += item.base_net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530271
Nabin Haitf0bc9b62015-02-23 01:40:01 +0530272 self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530273
Subin Toma8e2c022021-11-16 19:06:49 +0530274 def calculate_shipping_charges(self):
Subin Tomaf1fce02021-11-10 16:49:12 +0530275 if hasattr(self.doc, "shipping_rule") and self.doc.shipping_rule:
Subin Tom18ae03d2021-11-10 15:57:41 +0530276 shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
277 shipping_rule.apply(self.doc)
278
Nabin Hait3237c752015-02-17 11:11:11 +0530279 def calculate_taxes(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530280 rounding_adjustment_computed = self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment')
281 if not rounding_adjustment_computed:
Subin Tom75a76e62021-10-29 16:45:04 +0530282 self.doc.rounding_adjustment = 0
283
Nabin Hait3237c752015-02-17 11:11:11 +0530284 # maintain actual tax rate based on idx
Nabin Haite7679702015-02-20 14:40:35 +0530285 actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
Nabin Hait3237c752015-02-17 11:11:11 +0530286 for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
287
288 for n, item in enumerate(self.doc.get("items")):
289 item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
Nabin Hait3237c752015-02-17 11:11:11 +0530290 for i, tax in enumerate(self.doc.get("taxes")):
291 # tax_amount represents the amount of tax for the current step
292 current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
293
294 # Adjust divisional loss to the last item
295 if tax.charge_type == "Actual":
296 actual_tax_dict[tax.idx] -= current_tax_amount
297 if n == len(self.doc.get("items")) - 1:
298 current_tax_amount += actual_tax_dict[tax.idx]
299
Nabin Hait2b019ed2015-02-22 23:03:07 +0530300 # accumulate tax amount into tax.tax_amount
Nabin Haitde9c8a92015-02-23 01:06:00 +0530301 if tax.charge_type != "Actual" and \
302 not (self.discount_amount_applied and self.doc.apply_discount_on=="Grand Total"):
303 tax.tax_amount += current_tax_amount
Nabin Hait2b019ed2015-02-22 23:03:07 +0530304
Nabin Hait3237c752015-02-17 11:11:11 +0530305 # store tax_amount for current item as it will be used for
306 # charge type = 'On Previous Row Amount'
307 tax.tax_amount_for_current_item = current_tax_amount
308
Nabin Hait2b019ed2015-02-22 23:03:07 +0530309 # set tax after discount
Nabin Hait3237c752015-02-17 11:11:11 +0530310 tax.tax_amount_after_discount_amount += current_tax_amount
311
Nabin Haitcd951342017-07-31 18:07:45 +0530312 current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax)
Nabin Hait3237c752015-02-17 11:11:11 +0530313
Nabin Hait3237c752015-02-17 11:11:11 +0530314 # note: grand_total_for_current_item contains the contribution of
315 # item's amount, previously applied tax and the current tax on that item
316 if i==0:
Nabin Haitcd951342017-07-31 18:07:45 +0530317 tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530318 else:
319 tax.grand_total_for_current_item = \
Nabin Haitcd951342017-07-31 18:07:45 +0530320 flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530321
322 # set precision in the last item iteration
323 if n == len(self.doc.get("items")) - 1:
324 self.round_off_totals(tax)
Deepesh Gargb6705882021-04-14 11:20:27 +0530325 self._set_in_company_currency(tax,
326 ["tax_amount", "tax_amount_after_discount_amount"])
327
328 self.round_off_base_values(tax)
Nabin Haitcd951342017-07-31 18:07:45 +0530329 self.set_cumulative_total(i, tax)
330
Deepesh Gargb6705882021-04-14 11:20:27 +0530331 self._set_in_company_currency(tax, ["total"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530332
Nabin Hait3237c752015-02-17 11:11:11 +0530333 # adjust Discount Amount loss in last tax iteration
Nabin Haitde9c8a92015-02-23 01:06:00 +0530334 if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
Subin Tom75a76e62021-10-29 16:45:04 +0530335 and self.doc.discount_amount \
336 and self.doc.apply_discount_on == "Grand Total" \
Saqib Ansari17445c72022-03-07 18:01:07 +0530337 and not rounding_adjustment_computed:
Nabin Hait2e4de832017-09-19 14:53:16 +0530338 self.doc.rounding_adjustment = flt(self.doc.grand_total
339 - flt(self.doc.discount_amount) - tax.total,
340 self.doc.precision("rounding_adjustment"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530341
Nabin Haitcd951342017-07-31 18:07:45 +0530342 def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
343 # if just for valuation, do not add the tax amount in total
344 # if tax/charges is for deduction, multiply by -1
345 if getattr(tax, "category", None):
346 tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount
Rushabh Mehta30dc9a12017-11-17 14:31:09 +0530347 if self.doc.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", "Supplier Quotation"]:
348 tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
Nabin Haitcd951342017-07-31 18:07:45 +0530349 return tax_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530350
Nabin Haitcd951342017-07-31 18:07:45 +0530351 def set_cumulative_total(self, row_idx, tax):
352 tax_amount = tax.tax_amount_after_discount_amount
353 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax)
354
355 if row_idx == 0:
356 tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total"))
357 else:
358 tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530359
360 def get_current_tax_amount(self, item, tax, item_tax_map):
361 tax_rate = self._get_tax_rate(tax, item_tax_map)
362 current_tax_amount = 0.0
363
364 if tax.charge_type == "Actual":
365 # distribute the tax amount proportionally to each item row
Nabin Haite7679702015-02-20 14:40:35 +0530366 actual = flt(tax.tax_amount, tax.precision("tax_amount"))
367 current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
368
Nabin Hait3237c752015-02-17 11:11:11 +0530369 elif tax.charge_type == "On Net Total":
Nabin Haite7679702015-02-20 14:40:35 +0530370 current_tax_amount = (tax_rate / 100.0) * item.net_amount
Nabin Hait3237c752015-02-17 11:11:11 +0530371 elif tax.charge_type == "On Previous Row Amount":
372 current_tax_amount = (tax_rate / 100.0) * \
373 self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
374 elif tax.charge_type == "On Previous Row Total":
375 current_tax_amount = (tax_rate / 100.0) * \
376 self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
Himanshu Mishra35b26272018-11-13 11:13:04 +0530377 elif tax.charge_type == "On Item Quantity":
Nabin Hait19ea7212020-08-11 20:34:57 +0530378 current_tax_amount = tax_rate * item.qty
Nabin Hait3237c752015-02-17 11:11:11 +0530379
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530380 if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530381 self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530382
383 return current_tax_amount
384
Nabin Haite7679702015-02-20 14:40:35 +0530385 def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
386 # store tax breakup for each item
387 key = item.item_code or item.item_name
388 item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
389 if tax.item_wise_tax_detail.get(key):
390 item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
391
Nabin Haitcaab5822017-08-24 16:22:28 +0530392 tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
Nabin Haite7679702015-02-20 14:40:35 +0530393
Nabin Hait3237c752015-02-17 11:11:11 +0530394 def round_off_totals(self, tax):
Deepesh Gargb6705882021-04-14 11:20:27 +0530395 if tax.account_head in frappe.flags.round_off_applicable_accounts:
396 tax.tax_amount = round(tax.tax_amount, 0)
397 tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
398
Nabin Haite7679702015-02-20 14:40:35 +0530399 tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530400 tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
Nabin Haitcd951342017-07-31 18:07:45 +0530401 tax.precision("tax_amount"))
Nabin Haitce245122015-02-22 20:14:49 +0530402
Deepesh Gargb6705882021-04-14 11:20:27 +0530403 def round_off_base_values(self, tax):
404 # Round off to nearest integer based on regional settings
405 if tax.account_head in frappe.flags.round_off_applicable_accounts:
406 tax.base_tax_amount = round(tax.base_tax_amount, 0)
407 tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
408
Nabin Haita1bf43b2015-03-17 10:50:47 +0530409 def manipulate_grand_total_for_inclusive_tax(self):
410 # if fully inclusive taxes and diff
Ankush Menat98917802021-06-11 18:40:22 +0530411 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 +0530412 last_tax = self.doc.get("taxes")[-1]
Ankush Menat98917802021-06-11 18:40:22 +0530413 non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
414 for d in self.doc.get("taxes") if not d.included_in_print_rate)
Nabin Haitf32fc232019-12-25 13:59:24 +0530415
Nabin Hait2e4de832017-09-19 14:53:16 +0530416 diff = self.doc.total + non_inclusive_tax_amount \
417 - flt(last_tax.total, last_tax.precision("total"))
Nabin Haitf32fc232019-12-25 13:59:24 +0530418
419 # If discount amount applied, deduct the discount amount
420 # because self.doc.total is always without discount, but last_tax.total is after discount
421 if self.discount_amount_applied and self.doc.discount_amount:
422 diff -= flt(self.doc.discount_amount)
423
424 diff = flt(diff, self.doc.precision("rounding_adjustment"))
425
Nabin Hait2e4de832017-09-19 14:53:16 +0530426 if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
Nabin Haitf32fc232019-12-25 13:59:24 +0530427 self.doc.rounding_adjustment = diff
Nabin Hait3237c752015-02-17 11:11:11 +0530428
429 def calculate_totals(self):
Subin Tom75a76e62021-10-29 16:45:04 +0530430 if self.doc.get("taxes"):
431 self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment)
432 else:
433 self.doc.grand_total = flt(self.doc.net_total)
Nabin Hait3237c752015-02-17 11:11:11 +0530434
Subin Tom75a76e62021-10-29 16:45:04 +0530435 if self.doc.get("taxes"):
436 self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
Nabin Hait2e4de832017-09-19 14:53:16 +0530437 - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
Subin Tom75a76e62021-10-29 16:45:04 +0530438 else:
439 self.doc.total_taxes_and_charges = 0.0
Anand Doshiec5ec602015-03-05 19:31:23 +0530440
Nabin Hait2e4de832017-09-19 14:53:16 +0530441 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
Anand Doshiec5ec602015-03-05 19:31:23 +0530442
Saqiba6f98d42020-07-23 18:51:26 +0530443 if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"]:
Anurag Mishra8a06b8f2019-07-17 14:55:16 +0530444 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 +0530445 if self.doc.total_taxes_and_charges else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530446 else:
Anand Doshiec5ec602015-03-05 19:31:23 +0530447 self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
Nabin Hait3237c752015-02-17 11:11:11 +0530448 for tax in self.doc.get("taxes"):
449 if tax.category in ["Valuation and Total", "Total"]:
450 if tax.add_deduct_tax == "Add":
Nabin Haitdb53a782015-07-31 16:53:13 +0530451 self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530452 else:
Nabin Haitdb53a782015-07-31 16:53:13 +0530453 self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530454
Nabin Haite7679702015-02-20 14:40:35 +0530455 self.doc.round_floats_in(self.doc, ["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.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
458 if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
459 else self.doc.base_net_total
Nabin Hait3237c752015-02-17 11:11:11 +0530460
Nabin Hait2e4de832017-09-19 14:53:16 +0530461 self._set_in_company_currency(self.doc,
462 ["taxes_and_charges_added", "taxes_and_charges_deducted"])
Nabin Hait3237c752015-02-17 11:11:11 +0530463
Nabin Haite7679702015-02-20 14:40:35 +0530464 self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
Nabin Hait3237c752015-02-17 11:11:11 +0530465
Nabin Hait2e4de832017-09-19 14:53:16 +0530466 self.set_rounded_total()
467
rohitwaghchaurea8fb2db2018-05-26 09:23:02 +0530468 def calculate_total_net_weight(self):
469 if self.doc.meta.get_field('total_net_weight'):
470 self.doc.total_net_weight = 0.0
471 for d in self.doc.items:
472 if d.total_weight:
473 self.doc.total_net_weight += d.total_weight
474
Nabin Hait2e4de832017-09-19 14:53:16 +0530475 def set_rounded_total(self):
Saqib Ansari17445c72022-03-07 18:01:07 +0530476 if self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment'):
477 return
Nabin Hait877e1bb2017-11-17 12:27:43 +0530478
Saqib Ansari17445c72022-03-07 18:01:07 +0530479 if self.doc.meta.get_field("rounded_total"):
480 if self.doc.is_rounded_total_disabled():
481 self.doc.rounded_total = self.doc.base_rounded_total = 0
482 return
Nabin Hait2e4de832017-09-19 14:53:16 +0530483
Saqib Ansari17445c72022-03-07 18:01:07 +0530484 self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
485 self.doc.currency, self.doc.precision("rounded_total"))
Nabin Hait877e1bb2017-11-17 12:27:43 +0530486
Saqib Ansari17445c72022-03-07 18:01:07 +0530487 #if print_in_rate is set, we would have already calculated rounding adjustment
488 self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
489 self.doc.precision("rounding_adjustment"))
490
491 self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530492
Nabin Hait3237c752015-02-17 11:11:11 +0530493 def _cleanup(self):
Deepesh Garg5f3d7f52021-04-12 10:56:47 +0530494 if not self.doc.get('is_consolidated'):
495 for tax in self.doc.get("taxes"):
Ankush Menatdfdd1c62021-07-23 15:50:37 +0530496 if not tax.get("dont_recompute_tax"):
497 tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530498
Nabin Hait3769d872015-12-18 13:12:02 +0530499 def set_discount_amount(self):
Nabin Haite0405102016-10-13 12:14:32 +0530500 if self.doc.additional_discount_percentage:
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530501 self.doc.discount_amount = flt(flt(self.doc.get(scrub(self.doc.apply_discount_on)))
Nabin Hait3769d872015-12-18 13:12:02 +0530502 * self.doc.additional_discount_percentage / 100, self.doc.precision("discount_amount"))
Nabin Hait3237c752015-02-17 11:11:11 +0530503
504 def apply_discount_amount(self):
505 if self.doc.discount_amount:
Nabin Hait37b047d2015-02-23 16:01:33 +0530506 if not self.doc.apply_discount_on:
507 frappe.throw(_("Please select Apply Discount On"))
508
Nabin Hait3237c752015-02-17 11:11:11 +0530509 self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
510 self.doc.precision("base_discount_amount"))
511
Nabin Haite7679702015-02-20 14:40:35 +0530512 total_for_discount_amount = self.get_total_for_discount_amount()
Nabin Hait25bd84d2015-03-04 15:06:56 +0530513 taxes = self.doc.get("taxes")
514 net_total = 0
Nabin Hait3237c752015-02-17 11:11:11 +0530515
Nabin Haite7679702015-02-20 14:40:35 +0530516 if total_for_discount_amount:
Nabin Hait3237c752015-02-17 11:11:11 +0530517 # calculate item amount after Discount Amount
Nabin Hait25bd84d2015-03-04 15:06:56 +0530518 for i, item in enumerate(self.doc.get("items")):
519 distributed_amount = flt(self.doc.discount_amount) * \
520 item.net_amount / total_for_discount_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530521
Nabin Haite7679702015-02-20 14:40:35 +0530522 item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
Nabin Hait25bd84d2015-03-04 15:06:56 +0530523 net_total += item.net_amount
Anand Doshiec5ec602015-03-05 19:31:23 +0530524
Nabin Hait25bd84d2015-03-04 15:06:56 +0530525 # discount amount rounding loss adjustment if no taxes
Nabin Hait4d587342019-05-30 15:50:46 +0530526 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 +0530527 and i == len(self.doc.get("items")) - 1:
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530528 discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530529 self.doc.precision("net_total"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530530
Anand Doshiec5ec602015-03-05 19:31:23 +0530531 item.net_amount = flt(item.net_amount + discount_amount_loss,
Nabin Hait25bd84d2015-03-04 15:06:56 +0530532 item.precision("net_amount"))
Anand Doshiec5ec602015-03-05 19:31:23 +0530533
Nabin Hait51e980d2015-10-10 18:10:05 +0530534 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 +0530535
Nabin Haite7679702015-02-20 14:40:35 +0530536 self._set_in_company_currency(item, ["net_rate", "net_amount"])
Nabin Hait3237c752015-02-17 11:11:11 +0530537
538 self.discount_amount_applied = True
539 self._calculate()
540 else:
541 self.doc.base_discount_amount = 0
542
Nabin Haite7679702015-02-20 14:40:35 +0530543 def get_total_for_discount_amount(self):
Nabin Haitde9c8a92015-02-23 01:06:00 +0530544 if self.doc.apply_discount_on == "Net Total":
545 return self.doc.net_total
Nabin Haite7679702015-02-20 14:40:35 +0530546 else:
547 actual_taxes_dict = {}
Nabin Hait3237c752015-02-17 11:11:11 +0530548
Nabin Haite7679702015-02-20 14:40:35 +0530549 for tax in self.doc.get("taxes"):
Nabin Hait19ea7212020-08-11 20:34:57 +0530550 if tax.charge_type in ["Actual", "On Item Quantity"]:
Nabin Haitaf9bdfe2017-12-12 18:50:05 +0530551 tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
552 actual_taxes_dict.setdefault(tax.idx, tax_amount)
Nabin Haite7679702015-02-20 14:40:35 +0530553 elif tax.row_id in actual_taxes_dict:
554 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
555 actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
Nabin Hait3237c752015-02-17 11:11:11 +0530556
Nabin Hait877e1bb2017-11-17 12:27:43 +0530557 return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
558 self.doc.precision("grand_total"))
Nabin Hait3237c752015-02-17 11:11:11 +0530559
560
Nabin Hait7b19b9e2015-02-24 09:42:24 +0530561 def calculate_total_advance(self):
562 if self.doc.docstatus < 2:
Ankush Menat98917802021-06-11 18:40:22 +0530563 total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
564 for adv in self.doc.get("advances"))
Nabin Hait3237c752015-02-17 11:11:11 +0530565
Nabin Haite7679702015-02-20 14:40:35 +0530566 self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530567
Faris Ansari6041f5c2018-02-08 13:33:52 +0530568 grand_total = self.doc.rounded_total or self.doc.grand_total
569
Nabin Hait289ffb72016-02-08 11:06:55 +0530570 if self.doc.party_account_currency == self.doc.currency:
Faris Ansari6041f5c2018-02-08 13:33:52 +0530571 invoice_total = flt(grand_total - flt(self.doc.write_off_amount),
Nabin Hait289ffb72016-02-08 11:06:55 +0530572 self.doc.precision("grand_total"))
Nabin Hait8d8cba72017-04-03 17:26:22 +0530573 else:
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530574 base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530575 self.doc.precision("base_write_off_amount"))
Faris Ansari6041f5c2018-02-08 13:33:52 +0530576 invoice_total = flt(grand_total * self.doc.conversion_rate,
Nabin Hait8d8cba72017-04-03 17:26:22 +0530577 self.doc.precision("grand_total")) - base_write_off_amount
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530578
Nabin Haitadc09232016-02-09 10:31:11 +0530579 if invoice_total > 0 and self.doc.total_advance > invoice_total:
Nabin Hait289ffb72016-02-08 11:06:55 +0530580 frappe.throw(_("Advance amount cannot be greater than {0} {1}")
581 .format(self.doc.party_account_currency, invoice_total))
Nabin Hait3237c752015-02-17 11:11:11 +0530582
Rushabh Mehta8bb6e532015-02-18 20:22:59 +0530583 if self.doc.docstatus == 0:
Nabin Hait3237c752015-02-17 11:11:11 +0530584 self.calculate_outstanding_amount()
585
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530586 def is_internal_invoice(self):
587 """
588 Checks if its an internal transfer invoice
589 and decides if to calculate any out standing amount or not
590 """
591
592 if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
593 return True
594
595 return False
596
Nabin Hait3237c752015-02-17 11:11:11 +0530597 def calculate_outstanding_amount(self):
598 # NOTE:
599 # write_off_amount is only for POS Invoice
600 # total_advance is only for non POS Invoice
Rohit Waghchaureefb5bf22016-08-25 02:09:53 +0530601 if self.doc.doctype == "Sales Invoice":
602 self.calculate_paid_amount()
603
Deepesh Gargf17ea2c2020-12-11 21:30:39 +0530604 if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
605 self.is_internal_invoice(): return
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530606
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530607 self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530608 self._set_in_company_currency(self.doc, ['write_off_amount'])
609
Nabin Hait877e1bb2017-11-17 12:27:43 +0530610 if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
611 grand_total = self.doc.rounded_total or self.doc.grand_total
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530612 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
613
Nabin Hait877e1bb2017-11-17 12:27:43 +0530614 if self.doc.party_account_currency == self.doc.currency:
Manas Solankida486ee2018-07-06 12:36:57 +0530615 total_amount_to_pay = flt(grand_total - self.doc.total_advance
Nabin Hait877e1bb2017-11-17 12:27:43 +0530616 - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
617 else:
Deepesh Garg9d3a5c32022-01-06 18:58:49 +0530618 total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
619 - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
Anand Doshi15f7b1e2016-04-04 15:03:28 +0530620
Nabin Hait4ffd7f32015-08-27 12:28:36 +0530621 self.doc.round_floats_in(self.doc, ["paid_amount"])
Nabin Hait877e1bb2017-11-17 12:27:43 +0530622 change_amount = 0
623
Deepesh Garg0ebace52020-02-25 13:21:16 +0530624 if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
Nabin Hait877e1bb2017-11-17 12:27:43 +0530625 self.calculate_write_off_amount()
626 self.calculate_change_amount()
627 change_amount = self.doc.change_amount \
628 if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
629
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530630 paid_amount = self.doc.paid_amount \
631 if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530632
Nabin Hait877e1bb2017-11-17 12:27:43 +0530633 self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
634 self.doc.precision("outstanding_amount"))
Rushabh Mehtac6bd7ad2016-12-21 17:30:29 +0530635
Deepesh Garg0ebace52020-02-25 13:21:16 +0530636 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 +0530637 self.set_total_amount_to_default_mop(total_amount_to_pay)
638 self.calculate_paid_amount()
Deepesh Garg0ebace52020-02-25 13:21:16 +0530639
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530640 def calculate_paid_amount(self):
Manas Solankida486ee2018-07-06 12:36:57 +0530641
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530642 paid_amount = base_paid_amount = 0.0
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530643
644 if self.doc.is_pos:
645 for payment in self.doc.get('payments'):
Ayush Shuklae9cf1ab2017-05-25 14:14:55 +0530646 payment.amount = flt(payment.amount)
647 payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
Rohit Waghchauref58cad62017-01-17 12:11:57 +0530648 paid_amount += payment.amount
649 base_paid_amount += payment.base_amount
rohitwaghchaure73456ac2017-05-16 11:29:57 +0530650 elif not self.doc.is_return:
651 self.doc.set('payments', [])
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530652
Manas Solankida486ee2018-07-06 12:36:57 +0530653 if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
654 base_paid_amount += self.doc.loyalty_amount
655 paid_amount += (self.doc.loyalty_amount / flt(self.doc.conversion_rate))
656
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530657 self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
658 self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
659
Nabin Hait3bb1a422016-08-02 16:41:10 +0530660 def calculate_change_amount(self):
661 self.doc.change_amount = 0.0
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530662 self.doc.base_change_amount = 0.0
Saqib Ansaric2b83a02022-02-08 17:07:51 +0530663 grand_total = self.doc.rounded_total or self.doc.grand_total
664 base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
Nabin Hait877e1bb2017-11-17 12:27:43 +0530665
666 if self.doc.doctype == "Sales Invoice" \
Saqib Ansari75256862022-02-09 10:05:06 +0530667 and self.doc.paid_amount > grand_total and not self.doc.is_return \
Ankush Menat98917802021-06-11 18:40:22 +0530668 and any(d.type == "Cash" for d in self.doc.payments):
Nabin Haitac3b2aa2017-05-30 15:35:01 +0530669
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530670 self.doc.change_amount = flt(self.doc.paid_amount - grand_total +
Nabin Hait3bb1a422016-08-02 16:41:10 +0530671 self.doc.write_off_amount, self.doc.precision("change_amount"))
Rohit Waghchaure6087fe12016-04-09 14:31:09 +0530672
Rohit Waghchauree8d22bb2018-02-05 18:13:29 +0530673 self.doc.base_change_amount = flt(self.doc.base_paid_amount - base_grand_total +
Rohit Waghchaure609e2b42016-08-31 02:04:37 +0530674 self.doc.base_write_off_amount, self.doc.precision("base_change_amount"))
mbauskar36b51892016-01-18 16:31:10 +0530675
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530676 def calculate_write_off_amount(self):
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530677 if flt(self.doc.change_amount) > 0:
Nabin Hait877e1bb2017-11-17 12:27:43 +0530678 self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
679 + self.doc.change_amount, self.doc.precision("write_off_amount"))
Rohit Waghchaurebaef2622016-08-05 15:41:36 +0530680 self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
681 self.doc.precision("base_write_off_amount"))
Rohit Waghchaure7127a8f2016-08-04 14:56:15 +0530682
mbauskar36b51892016-01-18 16:31:10 +0530683 def calculate_margin(self, item):
Makarand Bauskar0e4c5c92017-05-11 11:40:02 +0530684 rate_with_margin = 0.0
Shreya Shahbe690ef2017-11-14 17:22:41 +0530685 base_rate_with_margin = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530686 if item.price_list_rate:
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530687 if item.pricing_rules and not self.doc.ignore_pricing_rule:
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530688 has_margin = False
marination733fd5f2020-08-26 18:23:12 +0530689 for d in get_applied_pricing_rules(item.pricing_rules):
rohitwaghchaurea85ddf22019-11-19 18:47:48 +0530690 pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
Shreya Shahf718b0c2018-02-20 11:26:46 +0530691
Rohit Waghchaured4d639c2021-02-01 22:58:22 +0530692 if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
693 pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
Rohit Waghchaure8bfe3302019-03-18 14:34:19 +0530694 item.margin_type = pricing_rule.margin_type
695 item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
Rohit Waghchaurea248dfb2020-07-16 17:27:26 +0530696 has_margin = True
697
698 if not has_margin:
699 item.margin_type = None
700 item.margin_rate_or_amount = 0.0
mbauskar36b51892016-01-18 16:31:10 +0530701
Rohit Waghchaure01faa9c2021-06-21 00:59:02 +0530702 if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
703 item.margin_type = "Amount"
704 item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
705 item.precision("margin_rate_or_amount"))
706 item.rate_with_margin = item.rate
707
708 elif item.margin_type and item.margin_rate_or_amount:
mbauskara52472c2016-03-05 15:10:25 +0530709 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 +0530710 rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
Shreya Shahbe690ef2017-11-14 17:22:41 +0530711 base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
mbauskar36b51892016-01-18 16:31:10 +0530712
Shreya Shahbe690ef2017-11-14 17:22:41 +0530713 return rate_with_margin, base_rate_with_margin
Nabin Hait852cb642017-07-05 12:58:19 +0530714
715 def set_item_wise_tax_breakup(self):
Nabin Hait9c421612017-07-20 13:32:01 +0530716 self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530717
Subin Tom7d627df2021-08-23 11:05:07 +0530718 def set_total_amount_to_default_mop(self, total_amount_to_pay):
Saqiba6f98d42020-07-23 18:51:26 +0530719 default_mode_of_payment = frappe.db.get_value('POS Payment Method',
720 {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1)
Deepesh Garg0ebace52020-02-25 13:21:16 +0530721
Deepesh Garg0ebace52020-02-25 13:21:16 +0530722 if default_mode_of_payment:
Afshanb6148342021-08-10 21:33:58 +0530723 self.doc.payments = []
Deepesh Garg0ebace52020-02-25 13:21:16 +0530724 self.doc.append('payments', {
725 'mode_of_payment': default_mode_of_payment.mode_of_payment,
Rucha Mahabaledee5302020-12-04 12:13:26 +0530726 'amount': total_amount_to_pay,
727 'default': 1
Ankush Menatb147b852021-09-01 16:45:57 +0530728 })
Deepesh Garg0ebace52020-02-25 13:21:16 +0530729
Nabin Hait9c421612017-07-20 13:32:01 +0530730def get_itemised_tax_breakup_html(doc):
731 if not doc.taxes:
732 return
733 frappe.flags.company = doc.company
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530734
Nabin Hait9c421612017-07-20 13:32:01 +0530735 # get headers
Nabin Haitcaab5822017-08-24 16:22:28 +0530736 tax_accounts = []
737 for tax in doc.taxes:
738 if getattr(tax, "category", None) and tax.category=="Valuation":
739 continue
rohitwaghchaure4e17fae2017-12-12 14:40:52 +0530740 if tax.description not in tax_accounts:
Nabin Haitcaab5822017-08-24 16:22:28 +0530741 tax_accounts.append(tax.description)
742
Nabin Hait9c421612017-07-20 13:32:01 +0530743 headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530744
Nabin Hait9c421612017-07-20 13:32:01 +0530745 # get tax breakup data
746 itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
Nabin Haitcaab5822017-08-24 16:22:28 +0530747
748 get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
749
rohitwaghchaured4526682017-12-28 14:20:13 +0530750 update_itemised_tax_data(doc)
Nabin Hait9c421612017-07-20 13:32:01 +0530751 frappe.flags.company = None
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530752
Nabin Hait9c421612017-07-20 13:32:01 +0530753 return frappe.render_template(
754 "templates/includes/itemised_tax_breakup.html", dict(
755 headers=headers,
756 itemised_tax=itemised_tax,
757 itemised_taxable_amount=itemised_taxable_amount,
758 tax_accounts=tax_accounts,
Saqib Ansari10a6a2d2020-04-13 16:40:13 +0530759 doc=doc
Nabin Haitb962fc12017-07-17 18:02:31 +0530760 )
Nabin Hait9c421612017-07-20 13:32:01 +0530761 )
Nabin Hait852cb642017-07-05 12:58:19 +0530762
Deepesh Garg6a5ef262021-02-19 14:30:23 +0530763@frappe.whitelist()
764def get_round_off_applicable_accounts(company, account_list):
765 account_list = get_regional_round_off_accounts(company, account_list)
766
767 return account_list
768
769@erpnext.allow_regional
770def get_regional_round_off_accounts(company, account_list):
771 pass
rohitwaghchaured4526682017-12-28 14:20:13 +0530772
773@erpnext.allow_regional
774def update_itemised_tax_data(doc):
775 #Don't delete this method, used for localization
776 pass
777
Nabin Haitb962fc12017-07-17 18:02:31 +0530778@erpnext.allow_regional
779def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
780 return [_("Item"), _("Taxable Amount")] + tax_accounts
781
782@erpnext.allow_regional
783def get_itemised_tax_breakup_data(doc):
784 itemised_tax = get_itemised_tax(doc.taxes)
785
786 itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
787
788 return itemised_tax, itemised_taxable_amount
789
Nabin Hait34c551d2019-07-03 10:34:31 +0530790def get_itemised_tax(taxes, with_tax_account=False):
Nabin Haitb962fc12017-07-17 18:02:31 +0530791 itemised_tax = {}
792 for tax in taxes:
Nabin Haitcaab5822017-08-24 16:22:28 +0530793 if getattr(tax, "category", None) and tax.category=="Valuation":
794 continue
795
Nabin Haitb962fc12017-07-17 18:02:31 +0530796 item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
Nabin Hait2e4de832017-09-19 14:53:16 +0530797 if item_tax_map:
798 for item_code, tax_data in item_tax_map.items():
799 itemised_tax.setdefault(item_code, frappe._dict())
Vishal Dhayaguded42242d2017-11-29 16:09:59 +0530800
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530801 tax_rate = 0.0
802 tax_amount = 0.0
803
Nabin Hait2e4de832017-09-19 14:53:16 +0530804 if isinstance(tax_data, list):
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530805 tax_rate = flt(tax_data[0])
806 tax_amount = flt(tax_data[1])
Nabin Hait2e4de832017-09-19 14:53:16 +0530807 else:
Prateeksha Singhea7533f2018-06-11 13:31:33 +0530808 tax_rate = flt(tax_data)
809
810 itemised_tax[item_code][tax.description] = frappe._dict(dict(
811 tax_rate = tax_rate,
812 tax_amount = tax_amount
813 ))
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530814
Nabin Hait34c551d2019-07-03 10:34:31 +0530815 if with_tax_account:
816 itemised_tax[item_code][tax.description].tax_account = tax.account_head
817
Nabin Haitb962fc12017-07-17 18:02:31 +0530818 return itemised_tax
819
820def get_itemised_taxable_amount(items):
821 itemised_taxable_amount = frappe._dict()
822 for item in items:
Rohit Waghchaure296fbfe2017-07-10 13:03:29 +0530823 item_code = item.item_code or item.item_name
Nabin Haitb962fc12017-07-17 18:02:31 +0530824 itemised_taxable_amount.setdefault(item_code, 0)
825 itemised_taxable_amount[item_code] += item.net_amount
826
Nabin Haitcaab5822017-08-24 16:22:28 +0530827 return itemised_taxable_amount
828
829def get_rounded_tax_amount(itemised_tax, precision):
830 # Rounding based on tax_amount precision
831 for taxes in itemised_tax.values():
832 for tax_account in taxes:
Himanshu Mishra35b26272018-11-13 11:13:04 +0530833 taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530834
835class init_landed_taxes_and_totals(object):
836 def __init__(self, doc):
837 self.doc = doc
838 self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
839 self.set_account_currency()
840 self.set_exchange_rate()
841 self.set_amounts_in_company_currency()
842
843 def set_account_currency(self):
844 company_currency = erpnext.get_company_currency(self.doc.company)
845 for d in self.doc.get(self.tax_field):
846 if not d.account_currency:
847 account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
848 d.account_currency = account_currency or company_currency
849
850 def set_exchange_rate(self):
851 company_currency = erpnext.get_company_currency(self.doc.company)
852 for d in self.doc.get(self.tax_field):
853 if d.account_currency == company_currency:
854 d.exchange_rate = 1
Deepesh Garg22f5ff82021-03-17 10:56:52 +0530855 elif not d.exchange_rate:
Deepesh Gargbfc17e42020-12-25 18:34:39 +0530856 d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
857 account_currency=d.account_currency, company=self.doc.company)
858
859 if not d.exchange_rate:
860 frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
861
862 def set_amounts_in_company_currency(self):
863 for d in self.doc.get(self.tax_field):
864 d.amount = flt(d.amount, d.precision("amount"))
Walstan Baptista37b826b2021-04-03 19:48:46 +0530865 d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))