fix: Handle duplicate Items qty in Quotation
fix: Handle duplicate Items qty in Quotation
(cherry picked from commit 39f6d8ffb6)
This commit is contained in:
committed by
Mergify
parent
d7124779bf
commit
4c1b415b9d
@@ -174,29 +174,22 @@ class Quotation(SellingController):
|
||||
)
|
||||
|
||||
def get_ordered_status(self):
|
||||
status = "Open"
|
||||
ordered_items = frappe._dict(
|
||||
frappe.db.get_all(
|
||||
"Sales Order Item",
|
||||
{"prevdoc_docname": self.name, "docstatus": 1},
|
||||
["item_code", "sum(qty)"],
|
||||
group_by="item_code",
|
||||
as_list=1,
|
||||
)
|
||||
)
|
||||
ordered_items = get_ordered_items(self.name)
|
||||
|
||||
if not ordered_items:
|
||||
return status
|
||||
return "Open"
|
||||
|
||||
has_alternatives = any(row.is_alternative for row in self.get("items"))
|
||||
self._items = self.get_valid_items() if has_alternatives else self.get("items")
|
||||
self._items = (
|
||||
self.get_valid_items()
|
||||
if any(row.is_alternative for row in self.get("items"))
|
||||
else self.get("items")
|
||||
)
|
||||
|
||||
if any(row.qty > ordered_items.get(row.item_code, 0.0) for row in self._items):
|
||||
status = "Partially Ordered"
|
||||
else:
|
||||
status = "Ordered"
|
||||
for row in self._items:
|
||||
if row.name not in ordered_items or row.qty > ordered_items[row.name]:
|
||||
return "Partially Ordered"
|
||||
|
||||
return status
|
||||
return "Ordered"
|
||||
|
||||
def get_valid_items(self):
|
||||
"""
|
||||
@@ -371,15 +364,7 @@ def make_sales_order(source_name: str, target_doc=None):
|
||||
|
||||
def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
||||
customer = _make_customer(source_name, ignore_permissions)
|
||||
ordered_items = frappe._dict(
|
||||
frappe.db.get_all(
|
||||
"Sales Order Item",
|
||||
{"prevdoc_docname": source_name, "docstatus": 1},
|
||||
["item_code", "sum(qty)"],
|
||||
group_by="item_code",
|
||||
as_list=1,
|
||||
)
|
||||
)
|
||||
ordered_items = get_ordered_items(source_name)
|
||||
|
||||
selected_rows = [x.get("name") for x in frappe.flags.get("args", {}).get("selected_items", [])]
|
||||
|
||||
@@ -417,7 +402,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
||||
target.run_method("calculate_taxes_and_totals")
|
||||
|
||||
def update_item(obj, target, source_parent):
|
||||
balance_qty = obj.qty if is_unit_price_row(obj) else obj.qty - ordered_items.get(obj.item_code, 0.0)
|
||||
balance_qty = obj.qty if is_unit_price_row(obj) else obj.qty - ordered_items.get(obj.name, 0.0)
|
||||
target.qty = balance_qty if balance_qty > 0 else 0
|
||||
target.stock_qty = flt(target.qty) * flt(obj.conversion_factor)
|
||||
|
||||
@@ -433,10 +418,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
||||
2. If selections: Is Alternative Item/Has Alternative Item: Map if selected and adequate qty
|
||||
3. If no selections: Simple row: Map if adequate qty
|
||||
"""
|
||||
balance_qty = item.qty - ordered_items.get(item.item_code, 0.0)
|
||||
has_valid_qty: bool = (balance_qty > 0) or is_unit_price_row(item)
|
||||
|
||||
if not has_valid_qty:
|
||||
if not ((item.qty > ordered_items.get(item.name, 0.0)) or is_unit_price_row(item)):
|
||||
return False
|
||||
|
||||
if not selected_rows:
|
||||
@@ -603,3 +585,28 @@ def handle_mandatory_error(e, customer, lead_name):
|
||||
message += _("Please create Customer from Lead {0}.").format(get_link_to_form("Lead", lead_name))
|
||||
|
||||
frappe.throw(message, title=_("Mandatory Missing"))
|
||||
|
||||
|
||||
def get_ordered_items(quotation: str):
|
||||
"""
|
||||
Returns a dict of ordered items with their total qty based on quotation row name.
|
||||
|
||||
In `Sales Order Item`, `quotation_item` is the row name of `Quotation Item`.
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"refsdjhd2": 10,
|
||||
"ygdhdshrt": 5,
|
||||
}
|
||||
```
|
||||
"""
|
||||
return frappe._dict(
|
||||
frappe.get_all(
|
||||
"Sales Order Item",
|
||||
filters={"prevdoc_docname": quotation, "docstatus": 1},
|
||||
fields=["quotation_item", "sum(qty)"],
|
||||
group_by="quotation_item",
|
||||
as_list=1,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -815,6 +815,52 @@ class TestQuotation(FrappeTestCase):
|
||||
quotation.reload()
|
||||
self.assertEqual(quotation.status, "Ordered")
|
||||
|
||||
def test_duplicate_items_in_quotation(self):
|
||||
from erpnext.selling.doctype.quotation.quotation import make_sales_order
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
# item code same but description different
|
||||
make_item("_Test Item 2", {"is_stock_item": 1})
|
||||
|
||||
quotation = make_quotation(qty=1, rate=100, do_not_submit=1)
|
||||
|
||||
# duplicate items
|
||||
for qty in [1, 1, 2, 3]:
|
||||
quotation.append("items", {"item_code": "_Test Item", "qty": qty, "rate": 100})
|
||||
|
||||
quotation.append("items", {"item_code": "_Test Item 2", "qty": 5, "rate": 100})
|
||||
|
||||
quotation.submit()
|
||||
|
||||
sales_order = make_sales_order(quotation.name)
|
||||
sales_order.delivery_date = nowdate()
|
||||
|
||||
self.assertEqual(len(sales_order.items), 6)
|
||||
self.assertEqual(sales_order.items[0].qty, 1)
|
||||
self.assertEqual(sales_order.items[-1].qty, 5)
|
||||
|
||||
# Row 1: 10, Row 4: 1, Row 5: 1
|
||||
sales_order.items[0].qty = 10
|
||||
sales_order.items[3].qty = 1
|
||||
sales_order.items[4].qty = 1
|
||||
sales_order.submit()
|
||||
|
||||
quotation.reload()
|
||||
self.assertEqual(quotation.status, "Partially Ordered")
|
||||
|
||||
sales_order_2 = make_sales_order(quotation.name)
|
||||
sales_order_2.delivery_date = nowdate()
|
||||
self.assertEqual(len(sales_order_2.items), 2)
|
||||
self.assertEqual(sales_order_2.items[0].qty, 1)
|
||||
self.assertEqual(sales_order_2.items[1].qty, 2)
|
||||
|
||||
self.assertEqual(sales_order_2.items[0].quotation_item, quotation.items[3].name)
|
||||
self.assertEqual(sales_order_2.items[1].quotation_item, quotation.items[4].name)
|
||||
|
||||
sales_order_2.submit()
|
||||
quotation.reload()
|
||||
self.assertEqual(quotation.status, "Ordered")
|
||||
|
||||
|
||||
test_records = frappe.get_test_records("Quotation")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user