Taxes and totals calculation in party currency
This commit is contained in:
@@ -11,7 +11,6 @@ from erpnext.controllers.accounts_controller import validate_conversion_rate
|
||||
class calculate_taxes_and_totals(object):
|
||||
def __init__(self, doc):
|
||||
self.doc = doc
|
||||
|
||||
self.calculate()
|
||||
|
||||
def calculate(self):
|
||||
@@ -25,6 +24,15 @@ class calculate_taxes_and_totals(object):
|
||||
self.calculate_total_advance()
|
||||
|
||||
def _calculate(self):
|
||||
self.calculate_item_values()
|
||||
self.initialize_taxes()
|
||||
self.determine_exclusive_rate()
|
||||
self.calculate_net_total()
|
||||
self.calculate_taxes()
|
||||
self.calculate_totals()
|
||||
self._cleanup()
|
||||
|
||||
def validate_conversion_rate(self):
|
||||
# validate conversion rate
|
||||
company_currency = get_company_currency(self.doc.company)
|
||||
if not self.doc.currency or self.doc.currency == company_currency:
|
||||
@@ -36,17 +44,6 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
self.doc.conversion_rate = flt(self.doc.conversion_rate)
|
||||
|
||||
self.calculate_item_values()
|
||||
self.initialize_taxes()
|
||||
|
||||
if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
|
||||
self.determine_exclusive_rate()
|
||||
|
||||
self.calculate_net_total()
|
||||
self.calculate_taxes()
|
||||
self.calculate_totals()
|
||||
self._cleanup()
|
||||
|
||||
def calculate_item_values(self):
|
||||
if not self.discount_amount_applied:
|
||||
for item in self.doc.get("items"):
|
||||
@@ -55,21 +52,22 @@ class calculate_taxes_and_totals(object):
|
||||
if item.discount_percentage == 100:
|
||||
item.rate = 0.0
|
||||
elif not item.rate:
|
||||
item.rate = flt(item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)),
|
||||
self.doc.precision("rate", item))
|
||||
item.rate = flt(item.price_list_rate *
|
||||
(1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||
|
||||
item.amount = flt(item.rate * item.qty, self.doc.precision("amount", item))
|
||||
item.item_tax_amount = 0.0;
|
||||
item.net_rate = item.rate
|
||||
item.amount = flt(item.rate * item.qty, item.precision("amount"))
|
||||
item.net_amount = item.amount
|
||||
|
||||
self._set_in_company_currency(item, "price_list_rate", "base_price_list_rate")
|
||||
self._set_in_company_currency(item, "rate", "base_rate")
|
||||
self._set_in_company_currency(item, "amount", "base_amount")
|
||||
self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
|
||||
|
||||
def _set_in_company_currency(self, item, print_field, base_field):
|
||||
item.item_tax_amount = 0.0
|
||||
|
||||
def _set_in_company_currency(self, doc, fields):
|
||||
"""set values in base currency"""
|
||||
value_in_company_currency = flt(self.doc.conversion_rate *
|
||||
flt(item.get(print_field), self.doc.precision(print_field, item)), self.doc.precision(base_field, item))
|
||||
item.set(base_field, value_in_company_currency)
|
||||
for f in fields:
|
||||
val = flt(flt(doc.get(f), doc.precision(f)) * self.doc.conversion_rate, doc.precision("base_" + f))
|
||||
doc.set("base_" + f, val)
|
||||
|
||||
def initialize_taxes(self):
|
||||
for tax in self.doc.get("taxes"):
|
||||
@@ -116,9 +114,9 @@ class calculate_taxes_and_totals(object):
|
||||
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
||||
|
||||
def determine_exclusive_rate(self):
|
||||
if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))):
|
||||
# no inclusive tax
|
||||
return
|
||||
if not any((cint(tax.included_in_print_rate) for tax in self.doc.get("taxes"))) or \
|
||||
self.doc.doctype not in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
|
||||
return
|
||||
|
||||
for item in self.doc.get("items"):
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
@@ -136,18 +134,24 @@ class calculate_taxes_and_totals(object):
|
||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||
|
||||
if cumulated_tax_fraction and not self.discount_amount_applied and item.qty:
|
||||
item.base_amount = flt((item.amount * self.doc.conversion_rate) /
|
||||
(1 + cumulated_tax_fraction), self.doc.precision("base_amount", item))
|
||||
item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction), item.precision("net_amount"))
|
||||
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
|
||||
item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
|
||||
|
||||
item.base_rate = flt(item.base_amount / item.qty, self.doc.precision("base_rate", item))
|
||||
item.discount_percentage = flt(item.discount_percentage, self.doc.precision("discount_percentage", item))
|
||||
|
||||
if item.discount_percentage == 100:
|
||||
item.base_price_list_rate = item.base_rate
|
||||
item.base_rate = 0.0
|
||||
else:
|
||||
item.base_price_list_rate = flt(item.base_rate / (1 - (item.discount_percentage / 100.0)),
|
||||
self.doc.precision("base_price_list_rate", item))
|
||||
self._set_in_company_currency(item, ["net_rate", "net_amount"])
|
||||
|
||||
# below part need to be fixed???
|
||||
|
||||
# if item.discount_percentage == 100:
|
||||
# item.price_list_rate = item.net_rate
|
||||
# item.base_price_list_rate = flt(item.price_list_rate*self.doc.conversion_rate,
|
||||
# self.doc.precision("base_price_list_rate", item))
|
||||
# item.rate = item.base_rate = item.net_rate = item.base_net_rate = 0.0
|
||||
# else:
|
||||
# item.base_price_list_rate = flt(item.net_rate / (1 - (item.discount_percentage / 100.0)),
|
||||
# self.doc.precision("price_list_rate", item))
|
||||
|
||||
|
||||
def _load_item_tax_rate(self, item_tax_rate):
|
||||
return json.loads(item_tax_rate) if item_tax_rate else {}
|
||||
@@ -182,17 +186,19 @@ class calculate_taxes_and_totals(object):
|
||||
return tax.rate
|
||||
|
||||
def calculate_net_total(self):
|
||||
self.doc.base_net_total = self.doc.net_total = 0.0
|
||||
self.doc.print_total = self.doc.base_print_total = self.doc.net_total = self.doc.base_net_total = 0.0
|
||||
|
||||
for item in self.doc.get("items"):
|
||||
self.doc.base_net_total += item.base_amount
|
||||
self.doc.net_total += item.amount
|
||||
self.doc.print_total += item.amount
|
||||
self.doc.base_print_total += item.base_amount
|
||||
self.doc.net_total += item.net_amount
|
||||
self.doc.base_net_total += item.base_net_amount
|
||||
|
||||
self.doc.round_floats_in(self.doc, ["base_net_total", "net_total"])
|
||||
self.doc.round_floats_in(self.doc, ["print_total", "base_print_total", "net_total", "base_net_total"])
|
||||
|
||||
def calculate_taxes(self):
|
||||
# maintain actual tax rate based on idx
|
||||
actual_tax_dict = dict([[tax.idx, flt(tax.rate, self.doc.precision("tax_amount", tax))]
|
||||
actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
|
||||
for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
|
||||
|
||||
for n, item in enumerate(self.doc.get("items")):
|
||||
@@ -230,12 +236,10 @@ class calculate_taxes_and_totals(object):
|
||||
# note: grand_total_for_current_item contains the contribution of
|
||||
# item's amount, previously applied tax and the current tax on that item
|
||||
if i==0:
|
||||
tax.grand_total_for_current_item = flt(item.base_amount + current_tax_amount,
|
||||
self.doc.precision("total", tax))
|
||||
tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount)
|
||||
else:
|
||||
tax.grand_total_for_current_item = \
|
||||
flt(self.doc.get("taxes")[i-1].grand_total_for_current_item +
|
||||
current_tax_amount, self.doc.precision("total", tax))
|
||||
self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount
|
||||
|
||||
# in tax.total, accumulate grand total of each item
|
||||
tax.total += tax.grand_total_for_current_item
|
||||
@@ -254,12 +258,11 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
if tax.charge_type == "Actual":
|
||||
# distribute the tax amount proportionally to each item row
|
||||
actual = flt(tax.rate, self.doc.precision("tax_amount", tax))
|
||||
current_tax_amount = (self.doc.base_net_total
|
||||
and ((item.base_amount / self.doc.base_net_total) * actual)
|
||||
or 0)
|
||||
actual = flt(tax.tax_amount, tax.precision("tax_amount"))
|
||||
current_tax_amount = item.net_amount*actual / self.doc.net_total if self.doc.net_total else 0.0
|
||||
|
||||
elif tax.charge_type == "On Net Total":
|
||||
current_tax_amount = (tax_rate / 100.0) * item.base_amount
|
||||
current_tax_amount = (tax_rate / 100.0) * item.net_amount
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
|
||||
@@ -267,72 +270,66 @@ class calculate_taxes_and_totals(object):
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
|
||||
|
||||
current_tax_amount = flt(current_tax_amount, self.doc.precision("tax_amount", tax))
|
||||
# current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount", tax))
|
||||
|
||||
# store tax breakup for each item
|
||||
key = item.item_code or item.item_name
|
||||
if tax.item_wise_tax_detail.get(key):
|
||||
item_wise_tax_amount = tax.item_wise_tax_detail[key][1] + current_tax_amount
|
||||
tax.item_wise_tax_detail[key] = [tax_rate,item_wise_tax_amount]
|
||||
else:
|
||||
tax.item_wise_tax_detail[key] = [tax_rate,current_tax_amount]
|
||||
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
|
||||
|
||||
return current_tax_amount
|
||||
|
||||
def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
|
||||
# store tax breakup for each item
|
||||
key = item.item_code or item.item_name
|
||||
item_wise_tax_amount = current_tax_amount*self.doc.conversion_rate
|
||||
if tax.item_wise_tax_detail.get(key):
|
||||
item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
|
||||
|
||||
tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount, tax.precision("base_tax_amount"))]
|
||||
|
||||
def round_off_totals(self, tax):
|
||||
tax.total = flt(tax.total, self.doc.precision("total", tax))
|
||||
tax.tax_amount = flt(tax.tax_amount, self.doc.precision("tax_amount", tax))
|
||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
|
||||
self.doc.precision("tax_amount", tax))
|
||||
tax.total = flt(tax.total, tax.precision("total"))
|
||||
tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
|
||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount"))
|
||||
|
||||
def adjust_discount_amount_loss(self, tax):
|
||||
discount_amount_loss = self.doc.base_grand_total - flt(self.doc.base_discount_amount) - tax.total
|
||||
discount_amount_loss = self.doc.grand_total - flt(self.doc.discount_amount) - tax.total
|
||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount +
|
||||
discount_amount_loss, self.doc.precision("tax_amount", tax))
|
||||
tax.total = flt(tax.total + discount_amount_loss, self.doc.precision("total", tax))
|
||||
|
||||
def calculate_totals(self):
|
||||
self.doc.base_grand_total = flt(self.doc.get("taxes")[-1].total
|
||||
if self.doc.get("taxes") else self.doc.base_net_total)
|
||||
|
||||
self.doc.base_total_taxes_and_charges = flt(self.doc.base_grand_total - self.doc.base_net_total,
|
||||
self.doc.precision("base_total_taxes_and_charges"))
|
||||
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total
|
||||
if self.doc.get("taxes") else self.doc.net_total)
|
||||
|
||||
if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
|
||||
self.doc.grand_total = flt(self.doc.base_grand_total / self.doc.conversion_rate) \
|
||||
if (self.doc.base_total_taxes_and_charges or self.doc.discount_amount) else self.doc.net_total
|
||||
|
||||
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total +
|
||||
flt(self.doc.discount_amount), self.doc.precision("total_taxes_and_charges"))
|
||||
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
|
||||
if self.doc.total_taxes_and_charges else self.doc.base_net_total
|
||||
else:
|
||||
self.doc.base_taxes_and_charges_added, self.doc.base_taxes_and_charges_deducted = 0.0, 0.0
|
||||
self.doc.taxes_and_charges_added, self.taxes_and_charges_deducted = 0.0, 0.0
|
||||
for tax in self.doc.get("taxes"):
|
||||
if tax.category in ["Valuation and Total", "Total"]:
|
||||
if tax.add_deduct_tax == "Add":
|
||||
self.doc.base_taxes_and_charges_added += flt(tax.tax_amount)
|
||||
self.doc.taxes_and_charges_added += flt(tax.tax_amount)
|
||||
else:
|
||||
self.doc.base_taxes_and_charges_deducted += flt(tax.tax_amount)
|
||||
self.doc.taxes_and_charges_deducted += flt(tax.tax_amount)
|
||||
|
||||
self.doc.round_floats_in(self.doc, ["base_taxes_and_charges_added", "base_taxes_and_charges_deducted"])
|
||||
self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
|
||||
|
||||
self.doc.grand_total = flt(self.doc.base_grand_total / self.doc.conversion_rate) \
|
||||
if (self.doc.base_taxes_and_charges_added or self.doc.base_taxes_and_charges_deducted) else self.doc.net_total
|
||||
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
|
||||
if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
|
||||
else self.doc.base_net_total
|
||||
|
||||
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total,
|
||||
self.doc.precision("total_taxes_and_charges"))
|
||||
self._set_in_company_currency(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
|
||||
|
||||
self.doc.taxes_and_charges_added = flt(self.doc.base_taxes_and_charges_added / self.doc.conversion_rate,
|
||||
self.doc.precision("taxes_and_charges_added"))
|
||||
self.doc.taxes_and_charges_deducted = flt(self.doc.base_taxes_and_charges_deducted / self.doc.conversion_rate,
|
||||
self.doc.precision("taxes_and_charges_deducted"))
|
||||
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total,
|
||||
self.doc.precision("total_taxes_and_charges"))
|
||||
|
||||
self.doc.base_grand_total = flt(self.doc.base_grand_total, self.doc.precision("base_grand_total"))
|
||||
self.doc.grand_total = flt(self.doc.grand_total, self.doc.precision("grand_total"))
|
||||
self._set_in_company_currency(self.doc, ["total_taxes_and_charges"])
|
||||
self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
|
||||
|
||||
if self.doc.meta.get_field("base_rounded_total"):
|
||||
self.doc.base_rounded_total = rounded(self.doc.base_grand_total)
|
||||
if self.doc.meta.get_field("rounded_total"):
|
||||
self.doc.rounded_total = rounded(self.doc.grand_total)
|
||||
if self.doc.meta.get_field("base_rounded_total"):
|
||||
self.doc.base_rounded_total = rounded(self.doc.base_grand_total)
|
||||
|
||||
def _cleanup(self):
|
||||
for tax in self.doc.get("taxes"):
|
||||
@@ -343,40 +340,44 @@ class calculate_taxes_and_totals(object):
|
||||
self.doc.base_discount_amount = flt(self.doc.discount_amount * self.doc.conversion_rate,
|
||||
self.doc.precision("base_discount_amount"))
|
||||
|
||||
grand_total_for_discount_amount = self.get_grand_total_for_discount_amount()
|
||||
total_for_discount_amount = self.get_total_for_discount_amount()
|
||||
|
||||
if grand_total_for_discount_amount:
|
||||
if total_for_discount_amount:
|
||||
# calculate item amount after Discount Amount
|
||||
for item in self.doc.get("items"):
|
||||
distributed_amount = flt(self.doc.base_discount_amount) * item.base_amount / grand_total_for_discount_amount
|
||||
item.base_amount = flt(item.base_amount - distributed_amount, self.doc.precision("base_amount", item))
|
||||
distributed_amount = flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
|
||||
item.net_amount = flt(item.net_amount - distributed_amount, item.precision("net_amount"))
|
||||
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
|
||||
|
||||
self._set_in_company_currency(item, ["net_rate", "net_amount"])
|
||||
|
||||
self.discount_amount_applied = True
|
||||
self._calculate()
|
||||
else:
|
||||
self.doc.base_discount_amount = 0
|
||||
|
||||
def get_grand_total_for_discount_amount(self):
|
||||
actual_taxes_dict = {}
|
||||
def get_total_for_discount_amount(self):
|
||||
if self.doc.apply_discount_on == "Print Total":
|
||||
return self.net_total
|
||||
else:
|
||||
actual_taxes_dict = {}
|
||||
|
||||
for tax in self.doc.get("taxes"):
|
||||
if tax.charge_type == "Actual":
|
||||
actual_taxes_dict.setdefault(tax.idx, tax.tax_amount)
|
||||
elif tax.row_id in actual_taxes_dict:
|
||||
actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
|
||||
actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
|
||||
for tax in self.doc.get("taxes"):
|
||||
if tax.charge_type == "Actual":
|
||||
actual_taxes_dict.setdefault(tax.idx, tax.tax_amount)
|
||||
elif tax.row_id in actual_taxes_dict:
|
||||
actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
|
||||
actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
|
||||
|
||||
grand_total_for_discount_amount = flt(self.doc.base_grand_total - sum(actual_taxes_dict.values()),
|
||||
self.doc.precision("base_grand_total"))
|
||||
return grand_total_for_discount_amount
|
||||
return flt(self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total"))
|
||||
|
||||
|
||||
def calculate_total_advance(self):
|
||||
if self.doc.docstatus < 2:
|
||||
sum_of_allocated_amount = sum([flt(adv.allocated_amount, self.doc.precision("allocated_amount", adv))
|
||||
def calculate_total_advance(self, parenttype, advance_parentfield):
|
||||
if self.docstatus < 2:
|
||||
total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
|
||||
for adv in self.doc.get("advances")])
|
||||
|
||||
self.doc.total_advance = flt(sum_of_allocated_amount, self.doc.precision("total_advance"))
|
||||
self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
|
||||
|
||||
if self.doc.docstatus == 0:
|
||||
self.calculate_outstanding_amount()
|
||||
|
||||
Reference in New Issue
Block a user