Skip to content

Commit b487f69

Browse files
mergify[bot]sudarsan2001venkat102mihir-kandoi
authored
feat: add option to create production plan from sales order (backport #53662) (#54323)
Co-authored-by: sudarsan2001 <[email protected]> Co-authored-by: Venkatesh <[email protected]> Co-authored-by: Mihir Kandoi <[email protected]>
1 parent d74e632 commit b487f69

3 files changed

Lines changed: 69 additions & 2 deletions

File tree

erpnext/selling/doctype/sales_order/sales_order.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ frappe.ui.form.on("Sales Order", {
1818
Project: "Project",
1919
"Payment Entry": "Payment",
2020
"Work Order": "Work Order",
21+
"Production Plan": "Production Plan",
2122
};
2223
frm.add_fetch("customer", "tax_id", "tax_id");
2324

@@ -1059,6 +1060,14 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
10591060
__("Create")
10601061
);
10611062
}
1063+
1064+
if (frappe.model.can_create("Production Plan") && !doc.is_subcontracted) {
1065+
this.frm.add_custom_button(
1066+
__("Production Plan"),
1067+
() => this.make_production_plan(),
1068+
__("Create")
1069+
);
1070+
}
10621071
}
10631072

10641073
// sales invoice
@@ -1339,6 +1348,13 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
13391348
});
13401349
}
13411350

1351+
make_production_plan() {
1352+
frappe.model.open_mapped_doc({
1353+
method: "erpnext.selling.doctype.sales_order.sales_order.make_production_plan",
1354+
frm: this.frm,
1355+
});
1356+
}
1357+
13421358
order_type() {
13431359
this.toggle_delivery_date();
13441360
}

erpnext/selling/doctype/sales_order/sales_order.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
)
2828
from erpnext.manufacturing.doctype.production_plan.production_plan import (
2929
get_items_for_material_requests,
30+
get_sales_orders,
3031
)
3132
from erpnext.selling.doctype.customer.customer import check_credit_limit
3233
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):
18011802
return [p.name for p in out]
18021803

18031804

1805+
def make_production_plan(source_name, target_doc=None):
1806+
sales_order = frappe.get_doc("Sales Order", source_name)
1807+
1808+
production_plan = frappe.new_doc(
1809+
"Production Plan",
1810+
company=sales_order.company,
1811+
get_items_from="Sales Order",
1812+
posting_date=nowdate(),
1813+
)
1814+
1815+
open_so = [data.name for data in get_sales_orders(production_plan)]
1816+
if sales_order.name not in open_so:
1817+
frappe.throw(_("Sales Order {0} is not available for production").format(sales_order.name))
1818+
1819+
production_plan.append(
1820+
"sales_orders",
1821+
{
1822+
"sales_order": sales_order.name,
1823+
"sales_order_date": sales_order.transaction_date,
1824+
"customer": sales_order.customer,
1825+
"grand_total": sales_order.base_grand_total,
1826+
},
1827+
)
1828+
production_plan.get_items()
1829+
if not production_plan.get("po_items"):
1830+
frappe.throw(_("Sales Order {0} is not available for production").format(sales_order.name))
1831+
1832+
return production_plan
1833+
1834+
18041835
@frappe.whitelist()
18051836
def update_status(status, name):
18061837
so = frappe.get_doc("Sales Order", name, check_permission="submit")

erpnext/selling/doctype/sales_order/test_sales_order.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import frappe.permissions
99
from frappe.core.doctype.user_permission.test_user_permission import create_user
1010
from frappe.tests import change_settings
11-
from frappe.utils import add_days, flt, nowdate, today
11+
from frappe.utils import add_days, flt, getdate, nowdate, today
1212

1313
from erpnext.controllers.accounts_controller import InvalidQtyError, get_due_date, update_child_qty_rate
1414
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
@@ -24,6 +24,7 @@
2424
create_pick_list,
2525
make_delivery_note,
2626
make_material_request,
27+
make_production_plan,
2728
make_raw_material_request,
2829
make_sales_invoice,
2930
make_work_orders,
@@ -213,6 +214,26 @@ def test_make_delivery_note(self):
213214
self.assertEqual(dn.doctype, "Delivery Note")
214215
self.assertEqual(len(dn.get("items")), len(so.get("items")))
215216

217+
def test_make_production_plan(self):
218+
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
219+
220+
fg_item = make_item("Test PP FG Item", {"is_stock_item": 1}).name
221+
make_bom(item=fg_item, rate=100, raw_materials=["_Test Item"])
222+
223+
so = make_sales_order(item_code=fg_item, do_not_submit=True)
224+
self.assertRaises(frappe.ValidationError, make_production_plan, so.name)
225+
226+
so.submit()
227+
pp = make_production_plan(so.name)
228+
229+
self.assertEqual(pp.doctype, "Production Plan")
230+
self.assertGreater(len(pp.get("po_items")), 0)
231+
self.assertEqual(pp.get("po_items")[0].sales_order, so.name)
232+
self.assertEqual(pp.get("sales_orders")[0].sales_order, so.name)
233+
self.assertEqual(getdate(pp.get("sales_orders")[0].sales_order_date), getdate(so.transaction_date))
234+
self.assertEqual(pp.get("sales_orders")[0].customer, so.customer)
235+
self.assertEqual(pp.get("sales_orders")[0].grand_total, so.base_grand_total)
236+
216237
def test_make_sales_invoice(self):
217238
so = make_sales_order(do_not_submit=True)
218239

@@ -2787,7 +2808,6 @@ def make_sales_order(**args):
27872808
)
27882809

27892810
so.delivery_date = add_days(so.transaction_date, 10)
2790-
27912811
if not args.do_not_save:
27922812
so.insert()
27932813
if not args.do_not_submit:

0 commit comments

Comments
 (0)