mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-12 17:23:38 +00:00
fix: validate over ordering of quotation
This commit is contained in:
@@ -443,7 +443,7 @@ class StatusUpdater(Document):
|
||||
):
|
||||
return
|
||||
|
||||
if args["source_dt"] != "Pick List Item":
|
||||
if args["source_dt"] != "Pick List Item" and args["target_dt"] != "Quotation Item":
|
||||
if qty_or_amount == "qty":
|
||||
action_msg = _(
|
||||
'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.'
|
||||
|
||||
@@ -460,3 +460,4 @@ erpnext.patches.v16_0.set_post_change_gl_entries_on_pos_settings
|
||||
erpnext.patches.v15_0.create_accounting_dimensions_in_advance_taxes_and_charges
|
||||
execute:frappe.delete_doc_if_exists("Workspace Sidebar", "Opening & Closing")
|
||||
erpnext.patches.v16_0.migrate_transaction_deletion_task_flags_to_status # 2
|
||||
erpnext.patches.v16_0.set_ordered_qty_in_quotation_item
|
||||
|
||||
16
erpnext/patches/v16_0/set_ordered_qty_in_quotation_item.py
Normal file
16
erpnext/patches/v16_0/set_ordered_qty_in_quotation_item.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
data = frappe.get_all(
|
||||
"Sales Order Item",
|
||||
filters={"quotation_item": ["is", "set"], "docstatus": 1},
|
||||
fields=["quotation_item", {"SUM": "stock_qty", "as": "ordered_qty"}],
|
||||
group_by="quotation_item",
|
||||
)
|
||||
if data:
|
||||
frappe.db.auto_commit_on_many_writes = 1
|
||||
frappe.db.bulk_update(
|
||||
"Quotation Item", {d.quotation_item: {"ordered_qty": d.ordered_qty} for d in data}
|
||||
)
|
||||
frappe.db.auto_commit_on_many_writes = 0
|
||||
@@ -450,7 +450,10 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False, ar
|
||||
"Quotation",
|
||||
source_name,
|
||||
{
|
||||
"Quotation": {"doctype": "Sales Order", "validation": {"docstatus": ["=", 1]}},
|
||||
"Quotation": {
|
||||
"doctype": "Sales Order",
|
||||
"validation": {"docstatus": ["=", 1]},
|
||||
},
|
||||
"Quotation Item": {
|
||||
"doctype": "Sales Order Item",
|
||||
"field_map": {"parent": "prevdoc_docname", "name": "quotation_item"},
|
||||
@@ -553,6 +556,8 @@ def _make_customer(source_name, ignore_permissions=False):
|
||||
|
||||
if quotation.quotation_to == "Customer":
|
||||
return frappe.get_doc("Customer", quotation.party_name)
|
||||
elif quotation.quotation_to == "CRM Deal":
|
||||
return frappe.get_doc("Customer", {"crm_deal": quotation.party_name})
|
||||
|
||||
# Check if a Customer already exists for the Lead or Prospect.
|
||||
existing_customer = None
|
||||
@@ -613,27 +618,9 @@ def handle_mandatory_error(e, customer, lead_name):
|
||||
frappe.throw(message, title=_("Mandatory Missing"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
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,
|
||||
"Quotation Item", {"docstatus": 1, "parent": quotation}, ["name", "ordered_qty"], as_list=True
|
||||
)
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"uom",
|
||||
"conversion_factor",
|
||||
"stock_qty",
|
||||
"ordered_qty",
|
||||
"available_quantity_section",
|
||||
"actual_qty",
|
||||
"column_break_ylrv",
|
||||
@@ -694,19 +695,31 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "ordered_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 1,
|
||||
"label": "Ordered Qty",
|
||||
"no_copy": 1,
|
||||
"non_negative": 1,
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-08-26 20:31:47.775890",
|
||||
"modified": "2026-01-30 12:56:08.320190",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ class QuotationItem(Document):
|
||||
margin_type: DF.Literal["", "Percentage", "Amount"]
|
||||
net_amount: DF.Currency
|
||||
net_rate: DF.Currency
|
||||
ordered_qty: DF.Float
|
||||
page_break: DF.Check
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
|
||||
@@ -194,6 +194,16 @@ class SalesOrder(SellingController):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.status_updater = [
|
||||
{
|
||||
"source_dt": "Sales Order Item",
|
||||
"target_dt": "Quotation Item",
|
||||
"join_field": "quotation_item",
|
||||
"target_field": "ordered_qty",
|
||||
"target_ref_field": "stock_qty",
|
||||
"source_field": "stock_qty",
|
||||
}
|
||||
]
|
||||
|
||||
def onload(self) -> None:
|
||||
super().onload()
|
||||
@@ -481,6 +491,7 @@ class SalesOrder(SellingController):
|
||||
frappe.throw(_("Row #{0}: Set Supplier for item {1}").format(d.idx, d.item_code))
|
||||
|
||||
def on_submit(self):
|
||||
super().update_prevdoc_status()
|
||||
self.check_credit_limit()
|
||||
self.update_reserved_qty()
|
||||
self.delete_removed_delivery_schedule_items()
|
||||
|
||||
Reference in New Issue
Block a user