mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 18:21:22 +00:00
feat: Filter out alternative item rows in taxes and totals for Quotation
- Added a Quotation Item field `is_alternative_item` - Use filtered rows for taxes and totals computation
This commit is contained in:
@@ -24,11 +24,19 @@ class calculate_taxes_and_totals(object):
|
|||||||
def __init__(self, doc: Document):
|
def __init__(self, doc: Document):
|
||||||
self.doc = doc
|
self.doc = doc
|
||||||
frappe.flags.round_off_applicable_accounts = []
|
frappe.flags.round_off_applicable_accounts = []
|
||||||
|
|
||||||
|
self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
|
||||||
|
|
||||||
get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
|
get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
|
||||||
self.calculate()
|
self.calculate()
|
||||||
|
|
||||||
|
def filter_rows(self):
|
||||||
|
"""Exclude rows, that do not fulfill the filter criteria, from totals computation."""
|
||||||
|
items = list(filter(lambda item: not item.get("is_alternative_item"), self.doc.get("items")))
|
||||||
|
return items
|
||||||
|
|
||||||
def calculate(self):
|
def calculate(self):
|
||||||
if not len(self.doc.get("items")):
|
if not len(self._items):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.discount_amount_applied = False
|
self.discount_amount_applied = False
|
||||||
@@ -70,7 +78,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
if hasattr(self.doc, "tax_withholding_net_total"):
|
if hasattr(self.doc, "tax_withholding_net_total"):
|
||||||
sum_net_amount = 0
|
sum_net_amount = 0
|
||||||
sum_base_net_amount = 0
|
sum_base_net_amount = 0
|
||||||
for item in self.doc.get("items"):
|
for item in self._items:
|
||||||
if hasattr(item, "apply_tds") and item.apply_tds:
|
if hasattr(item, "apply_tds") and item.apply_tds:
|
||||||
sum_net_amount += item.net_amount
|
sum_net_amount += item.net_amount
|
||||||
sum_base_net_amount += item.base_net_amount
|
sum_base_net_amount += item.base_net_amount
|
||||||
@@ -79,7 +87,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
self.doc.base_tax_withholding_net_total = sum_base_net_amount
|
self.doc.base_tax_withholding_net_total = sum_base_net_amount
|
||||||
|
|
||||||
def validate_item_tax_template(self):
|
def validate_item_tax_template(self):
|
||||||
for item in self.doc.get("items"):
|
for item in self._items:
|
||||||
if item.item_code and item.get("item_tax_template"):
|
if item.item_code and item.get("item_tax_template"):
|
||||||
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
||||||
args = {
|
args = {
|
||||||
@@ -137,7 +145,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if not self.discount_amount_applied:
|
if not self.discount_amount_applied:
|
||||||
for item in self.doc.get("items"):
|
for item in self._items:
|
||||||
self.doc.round_floats_in(item)
|
self.doc.round_floats_in(item)
|
||||||
|
|
||||||
if item.discount_percentage == 100:
|
if item.discount_percentage == 100:
|
||||||
@@ -236,7 +244,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
|
if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
|
||||||
return
|
return
|
||||||
|
|
||||||
for item in self.doc.get("items"):
|
for item in self._items:
|
||||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||||
cumulated_tax_fraction = 0
|
cumulated_tax_fraction = 0
|
||||||
total_inclusive_tax_amount_per_qty = 0
|
total_inclusive_tax_amount_per_qty = 0
|
||||||
@@ -317,7 +325,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
self.doc.total
|
self.doc.total
|
||||||
) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
|
) = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
|
||||||
|
|
||||||
for item in self.doc.get("items"):
|
for item in self._items:
|
||||||
self.doc.total += item.amount
|
self.doc.total += item.amount
|
||||||
self.doc.total_qty += item.qty
|
self.doc.total_qty += item.qty
|
||||||
self.doc.base_total += item.base_amount
|
self.doc.base_total += item.base_amount
|
||||||
@@ -354,7 +362,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
for n, item in enumerate(self.doc.get("items")):
|
for n, item in enumerate(self._items):
|
||||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||||
for i, tax in enumerate(self.doc.get("taxes")):
|
for i, tax in enumerate(self.doc.get("taxes")):
|
||||||
# tax_amount represents the amount of tax for the current step
|
# tax_amount represents the amount of tax for the current step
|
||||||
@@ -363,7 +371,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
# Adjust divisional loss to the last item
|
# Adjust divisional loss to the last item
|
||||||
if tax.charge_type == "Actual":
|
if tax.charge_type == "Actual":
|
||||||
actual_tax_dict[tax.idx] -= current_tax_amount
|
actual_tax_dict[tax.idx] -= current_tax_amount
|
||||||
if n == len(self.doc.get("items")) - 1:
|
if n == len(self._items) - 1:
|
||||||
current_tax_amount += actual_tax_dict[tax.idx]
|
current_tax_amount += actual_tax_dict[tax.idx]
|
||||||
|
|
||||||
# accumulate tax amount into tax.tax_amount
|
# accumulate tax amount into tax.tax_amount
|
||||||
@@ -391,7 +399,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# set precision in the last item iteration
|
# set precision in the last item iteration
|
||||||
if n == len(self.doc.get("items")) - 1:
|
if n == len(self._items) - 1:
|
||||||
self.round_off_totals(tax)
|
self.round_off_totals(tax)
|
||||||
self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
|
self._set_in_company_currency(tax, ["tax_amount", "tax_amount_after_discount_amount"])
|
||||||
|
|
||||||
@@ -570,7 +578,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
def calculate_total_net_weight(self):
|
def calculate_total_net_weight(self):
|
||||||
if self.doc.meta.get_field("total_net_weight"):
|
if self.doc.meta.get_field("total_net_weight"):
|
||||||
self.doc.total_net_weight = 0.0
|
self.doc.total_net_weight = 0.0
|
||||||
for d in self.doc.items:
|
for d in self._items:
|
||||||
if d.total_weight:
|
if d.total_weight:
|
||||||
self.doc.total_net_weight += d.total_weight
|
self.doc.total_net_weight += d.total_weight
|
||||||
|
|
||||||
@@ -630,7 +638,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
|
|
||||||
if total_for_discount_amount:
|
if total_for_discount_amount:
|
||||||
# calculate item amount after Discount Amount
|
# calculate item amount after Discount Amount
|
||||||
for i, item in enumerate(self.doc.get("items")):
|
for i, item in enumerate(self._items):
|
||||||
distributed_amount = (
|
distributed_amount = (
|
||||||
flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
|
flt(self.doc.discount_amount) * item.net_amount / total_for_discount_amount
|
||||||
)
|
)
|
||||||
@@ -643,7 +651,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
self.doc.apply_discount_on == "Net Total"
|
self.doc.apply_discount_on == "Net Total"
|
||||||
or not taxes
|
or not taxes
|
||||||
or total_for_discount_amount == self.doc.net_total
|
or total_for_discount_amount == self.doc.net_total
|
||||||
) and i == len(self.doc.get("items")) - 1:
|
) and i == len(self._items) - 1:
|
||||||
discount_amount_loss = flt(
|
discount_amount_loss = flt(
|
||||||
self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
|
self.doc.net_total - net_total - self.doc.discount_amount, self.doc.precision("net_total")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"stock_uom_rate",
|
"stock_uom_rate",
|
||||||
"is_free_item",
|
"is_free_item",
|
||||||
|
"is_alternative_item",
|
||||||
"section_break_43",
|
"section_break_43",
|
||||||
"valuation_rate",
|
"valuation_rate",
|
||||||
"column_break_45",
|
"column_break_45",
|
||||||
@@ -643,12 +644,19 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "is_alternative_item",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Alternative Item",
|
||||||
|
"print_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-12-25 02:49:53.926625",
|
"modified": "2023-01-24 08:48:06.290335",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation Item",
|
"name": "Quotation Item",
|
||||||
@@ -656,5 +664,6 @@
|
|||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user