feat: add option to create production plan from sales order (backport #53662) (#54323)

Co-authored-by: sudarsan2001 <frankel9675@gmail.com>
Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
This commit is contained in:
mergify[bot]
2026-04-16 15:16:34 +05:30
committed by GitHub
parent d74e632934
commit b487f69b59
3 changed files with 69 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ frappe.ui.form.on("Sales Order", {
Project: "Project",
"Payment Entry": "Payment",
"Work Order": "Work Order",
"Production Plan": "Production Plan",
};
frm.add_fetch("customer", "tax_id", "tax_id");
@@ -1059,6 +1060,14 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
__("Create")
);
}
if (frappe.model.can_create("Production Plan") && !doc.is_subcontracted) {
this.frm.add_custom_button(
__("Production Plan"),
() => this.make_production_plan(),
__("Create")
);
}
}
// sales invoice
@@ -1339,6 +1348,13 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
});
}
make_production_plan() {
frappe.model.open_mapped_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_production_plan",
frm: this.frm,
});
}
order_type() {
this.toggle_delivery_date();
}

View File

@@ -27,6 +27,7 @@ from erpnext.manufacturing.doctype.blanket_order.blanket_order import (
)
from erpnext.manufacturing.doctype.production_plan.production_plan import (
get_items_for_material_requests,
get_sales_orders,
)
from erpnext.selling.doctype.customer.customer import check_credit_limit
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@@ -1801,6 +1802,36 @@ def make_work_orders(items, sales_order, company, project=None):
return [p.name for p in out]
def make_production_plan(source_name, target_doc=None):
sales_order = frappe.get_doc("Sales Order", source_name)
production_plan = frappe.new_doc(
"Production Plan",
company=sales_order.company,
get_items_from="Sales Order",
posting_date=nowdate(),
)
open_so = [data.name for data in get_sales_orders(production_plan)]
if sales_order.name not in open_so:
frappe.throw(_("Sales Order {0} is not available for production").format(sales_order.name))
production_plan.append(
"sales_orders",
{
"sales_order": sales_order.name,
"sales_order_date": sales_order.transaction_date,
"customer": sales_order.customer,
"grand_total": sales_order.base_grand_total,
},
)
production_plan.get_items()
if not production_plan.get("po_items"):
frappe.throw(_("Sales Order {0} is not available for production").format(sales_order.name))
return production_plan
@frappe.whitelist()
def update_status(status, name):
so = frappe.get_doc("Sales Order", name, check_permission="submit")

View File

@@ -8,7 +8,7 @@ import frappe
import frappe.permissions
from frappe.core.doctype.user_permission.test_user_permission import create_user
from frappe.tests import change_settings
from frappe.utils import add_days, flt, nowdate, today
from frappe.utils import add_days, flt, getdate, nowdate, today
from erpnext.controllers.accounts_controller import InvalidQtyError, get_due_date, update_child_qty_rate
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
@@ -24,6 +24,7 @@ from erpnext.selling.doctype.sales_order.sales_order import (
create_pick_list,
make_delivery_note,
make_material_request,
make_production_plan,
make_raw_material_request,
make_sales_invoice,
make_work_orders,
@@ -213,6 +214,26 @@ class TestSalesOrder(ERPNextTestSuite):
self.assertEqual(dn.doctype, "Delivery Note")
self.assertEqual(len(dn.get("items")), len(so.get("items")))
def test_make_production_plan(self):
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
fg_item = make_item("Test PP FG Item", {"is_stock_item": 1}).name
make_bom(item=fg_item, rate=100, raw_materials=["_Test Item"])
so = make_sales_order(item_code=fg_item, do_not_submit=True)
self.assertRaises(frappe.ValidationError, make_production_plan, so.name)
so.submit()
pp = make_production_plan(so.name)
self.assertEqual(pp.doctype, "Production Plan")
self.assertGreater(len(pp.get("po_items")), 0)
self.assertEqual(pp.get("po_items")[0].sales_order, so.name)
self.assertEqual(pp.get("sales_orders")[0].sales_order, so.name)
self.assertEqual(getdate(pp.get("sales_orders")[0].sales_order_date), getdate(so.transaction_date))
self.assertEqual(pp.get("sales_orders")[0].customer, so.customer)
self.assertEqual(pp.get("sales_orders")[0].grand_total, so.base_grand_total)
def test_make_sales_invoice(self):
so = make_sales_order(do_not_submit=True)
@@ -2787,7 +2808,6 @@ def make_sales_order(**args):
)
so.delivery_date = add_days(so.transaction_date, 10)
if not args.do_not_save:
so.insert()
if not args.do_not_submit: