From 628ea42b63217fcf64c9803fdfdad09c610b00c3 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 22 Nov 2023 16:07:00 +0530 Subject: [PATCH 01/23] feat: add field `Action on Purchase Order Submission` --- .../doctype/buying_settings/buying_settings.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 059999245d..b20257cfb7 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -28,7 +28,9 @@ "subcontract", "backflush_raw_materials_of_subcontract_based_on", "column_break_11", - "over_transfer_allowance" + "over_transfer_allowance", + "section_break_xcug", + "purchase_order_submit_action" ], "fields": [ { @@ -172,6 +174,16 @@ "fieldname": "blanket_order_allowance", "fieldtype": "Float", "label": "Blanket Order Allowance (%)" + }, + { + "fieldname": "section_break_xcug", + "fieldtype": "Section Break" + }, + { + "fieldname": "purchase_order_submit_action", + "fieldtype": "Select", + "label": "Purchase Order Submit Action", + "options": "\nCreate Subcontracting Order\nCreate and Submit Subcontracting Order" } ], "icon": "fa fa-cog", @@ -179,7 +191,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-10-25 14:03:32.520418", + "modified": "2023-11-22 16:05:33.262940", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", From 9ec6f1e1d63ccb8087cb3f95c6a100b02d22ba79 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 22 Nov 2023 17:06:19 +0530 Subject: [PATCH 02/23] feat: auto create SCO on PO submission --- .../buying_settings/buying_settings.json | 8 +++--- .../doctype/purchase_order/purchase_order.py | 26 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index b20257cfb7..2791e32730 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -30,7 +30,7 @@ "column_break_11", "over_transfer_allowance", "section_break_xcug", - "purchase_order_submit_action" + "action_on_purchase_order_submission" ], "fields": [ { @@ -180,9 +180,9 @@ "fieldtype": "Section Break" }, { - "fieldname": "purchase_order_submit_action", + "fieldname": "action_on_purchase_order_submission", "fieldtype": "Select", - "label": "Purchase Order Submit Action", + "label": "Action on Purchase Order Submission", "options": "\nCreate Subcontracting Order\nCreate and Submit Subcontracting Order" } ], @@ -191,7 +191,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-22 16:05:33.262940", + "modified": "2023-11-22 17:42:28.978582", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 961697c0ac..86a652b487 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -357,6 +357,8 @@ class PurchaseOrder(BuyingController): update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) + self.auto_create_subcontracting_order() + def on_cancel(self): self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry") super(PurchaseOrder, self).on_cancel() @@ -484,6 +486,16 @@ class PurchaseOrder(BuyingController): return result + def auto_create_subcontracting_order(self): + if self.is_subcontracted and not self.is_old_subcontracting_flow: + if action := frappe.db.get_single_value( + "Buying Settings", "action_on_purchase_order_submission" + ): + if action == "Create Subcontracting Order": + make_subcontracting_order(self.name, save=True) + elif action == "Create and Submit Subcontracting Order": + make_subcontracting_order(self.name, submit=True) + def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): """get last purchase rate for an item""" @@ -686,8 +698,18 @@ def make_inter_company_sales_order(source_name, target_doc=None): @frappe.whitelist() -def make_subcontracting_order(source_name, target_doc=None): - return get_mapped_subcontracting_order(source_name, target_doc) +def make_subcontracting_order(source_name, target_doc=None, save=False, submit=False): + target_doc = get_mapped_subcontracting_order(source_name, target_doc) + + if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): + target_doc.save() + if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): + try: + target_doc.submit() + except Exception as e: + target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) + + return target_doc def get_mapped_subcontracting_order(source_name, target_doc=None): From 762906f24023af3f0fdba70c5dbf61951f45dd68 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 22 Nov 2023 17:47:49 +0530 Subject: [PATCH 03/23] feat: add field `Action on Subcontracting Receipt Submission` --- .../doctype/buying_settings/buying_settings.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 2791e32730..bb2476d8f2 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -30,7 +30,9 @@ "column_break_11", "over_transfer_allowance", "section_break_xcug", - "action_on_purchase_order_submission" + "action_on_purchase_order_submission", + "column_break_izrr", + "action_on_subcontracting_receipt_submission" ], "fields": [ { @@ -184,6 +186,16 @@ "fieldtype": "Select", "label": "Action on Purchase Order Submission", "options": "\nCreate Subcontracting Order\nCreate and Submit Subcontracting Order" + }, + { + "fieldname": "column_break_izrr", + "fieldtype": "Column Break" + }, + { + "fieldname": "action_on_subcontracting_receipt_submission", + "fieldtype": "Select", + "label": "Action on Subcontracting Receipt Submission", + "options": "\nCreate Purchase Receipt\nCreate and Submit Purchase Receipt" } ], "icon": "fa fa-cog", @@ -191,7 +203,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-22 17:42:28.978582", + "modified": "2023-11-22 17:46:26.474708", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", From 745e3bfb733bdbce034b9b1fb3cd83a502a423a7 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 23 Nov 2023 11:40:46 +0530 Subject: [PATCH 04/23] chore: notify user on SCO creation --- .../doctype/purchase_order/purchase_order.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 86a652b487..5db048ee62 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -8,7 +8,7 @@ import frappe from frappe import _, msgprint from frappe.desk.notifications import clear_doctype_notifications from frappe.model.mapper import get_mapped_doc -from frappe.utils import cint, cstr, flt +from frappe.utils import cint, cstr, flt, get_link_to_form from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( unlink_inter_company_doc, @@ -492,9 +492,9 @@ class PurchaseOrder(BuyingController): "Buying Settings", "action_on_purchase_order_submission" ): if action == "Create Subcontracting Order": - make_subcontracting_order(self.name, save=True) + make_subcontracting_order(self.name, save=True, notify=True) elif action == "Create and Submit Subcontracting Order": - make_subcontracting_order(self.name, submit=True) + make_subcontracting_order(self.name, submit=True, notify=True) def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): @@ -698,17 +698,29 @@ def make_inter_company_sales_order(source_name, target_doc=None): @frappe.whitelist() -def make_subcontracting_order(source_name, target_doc=None, save=False, submit=False): +def make_subcontracting_order( + source_name, target_doc=None, save=False, submit=False, notify=False +): target_doc = get_mapped_subcontracting_order(source_name, target_doc) if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): target_doc.save() + if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): try: target_doc.submit() except Exception as e: target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) + if notify: + frappe.msgprint( + _("Subcontracting Order {0} created.").format( + get_link_to_form(target_doc.doctype, target_doc.name) + ), + indicator="green", + alert=True, + ) + return target_doc From 45d5cff47d7fc53fcf710afac281907ba9ab9e00 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 23 Nov 2023 13:39:49 +0530 Subject: [PATCH 05/23] feat: add field `Purchase Order Item` in SCO Service Item --- .../subcontracting_order_service_item.json | 268 +++++++++--------- 1 file changed, 139 insertions(+), 129 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json index f213313ef6..dc18543b04 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json @@ -1,131 +1,141 @@ { - "actions": [], - "autoname": "hash", - "creation": "2022-04-01 19:23:05.728354", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "item_code", - "column_break_2", - "item_name", - "section_break_4", - "qty", - "column_break_6", - "rate", - "column_break_8", - "amount", - "section_break_10", - "fg_item", - "column_break_12", - "fg_item_qty" - ], - "fields": [ - { - "bold": 1, - "columns": 2, - "fieldname": "item_code", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Item Code", - "options": "Item", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "item_code.item_name", - "fieldname": "item_name", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "label": "Item Name", - "print_hide": 1, - "reqd": 1 - }, - { - "bold": 1, - "columns": 1, - "fieldname": "qty", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Quantity", - "print_width": "60px", - "reqd": 1, - "width": "60px" - }, - { - "bold": 1, - "columns": 2, - "fetch_from": "item_code.standard_rate", - "fetch_if_empty": 1, - "fieldname": "rate", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Rate", - "options": "currency", - "reqd": 1 - }, - { - "columns": 2, - "fieldname": "amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Amount", - "options": "currency", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "fg_item", - "fieldtype": "Link", - "label": "Finished Good Item", - "options": "Item", - "reqd": 1 - }, - { - "default": "1", - "fieldname": "fg_item_qty", - "fieldtype": "Float", - "label": "Finished Good Item Quantity", - "reqd": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_8", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_10", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - } - ], - "istable": 1, - "links": [], - "modified": "2022-04-07 11:43:43.094867", - "modified_by": "Administrator", - "module": "Subcontracting", - "name": "Subcontracting Order Service Item", - "naming_rule": "Random", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "search_fields": "item_name", - "sort_field": "modified", - "sort_order": "DESC", - "states": [] + "actions": [], + "autoname": "hash", + "creation": "2022-04-01 19:23:05.728354", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "column_break_2", + "item_name", + "section_break_4", + "qty", + "column_break_6", + "rate", + "column_break_8", + "amount", + "section_break_10", + "fg_item", + "column_break_12", + "fg_item_qty", + "purchase_order_item" + ], + "fields": [ + { + "bold": 1, + "columns": 2, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Item Name", + "print_hide": 1, + "reqd": 1 + }, + { + "bold": 1, + "columns": 1, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity", + "print_width": "60px", + "reqd": 1, + "width": "60px" + }, + { + "bold": 1, + "columns": 2, + "fetch_from": "item_code.standard_rate", + "fetch_if_empty": 1, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "options": "currency", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "fg_item", + "fieldtype": "Link", + "label": "Finished Good Item", + "options": "Item", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "fg_item_qty", + "fieldtype": "Float", + "label": "Finished Good Item Quantity", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "purchase_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Order Item", + "no_copy": 1, + "read_only": 1, + "search_index": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2023-11-23 17:05:04.561948", + "modified_by": "Administrator", + "module": "Subcontracting", + "name": "Subcontracting Order Service Item", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "search_fields": "item_name", + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file From 7e4dd33ab0da2800fdb4aeb89ef9d183bff19d4d Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 23 Nov 2023 13:40:41 +0530 Subject: [PATCH 06/23] fix: hold PO item ref in SCO Service Item --- erpnext/buying/doctype/purchase_order/purchase_order.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 5db048ee62..7f764e939e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -747,7 +747,9 @@ def get_mapped_subcontracting_order(source_name, target_doc=None): }, "Purchase Order Item": { "doctype": "Subcontracting Order Service Item", - "field_map": {}, + "field_map": { + "name": "purchase_order_item", + }, "field_no_map": [], }, }, From a2ede7d6d5241665d996447a1c9e2d1447190066 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 23 Nov 2023 16:59:42 +0530 Subject: [PATCH 07/23] feat: add field `Purchase Order Item` in SCO Item --- .../subcontracting_order/subcontracting_order.py | 13 +++++-------- .../subcontracting_order_item.json | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index faf0cadb75..376a936e68 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -123,8 +123,6 @@ class SubcontractingOrder(SubcontractingController): stock_bin.update_reserved_qty_for_sub_contracting() def populate_items_table(self): - items = [] - for si in self.service_items: if si.fg_item: item = frappe.get_doc("Item", si.fg_item) @@ -134,7 +132,8 @@ class SubcontractingOrder(SubcontractingController): ) or item.default_bom ) - items.append( + self.append( + "items", { "item_code": item.item_code, "item_name": item.item_name, @@ -143,6 +142,7 @@ class SubcontractingOrder(SubcontractingController): "qty": si.fg_item_qty, "stock_uom": item.stock_uom, "bom": bom, + "purchase_order_item": si.purchase_order_item, }, ) else: @@ -151,11 +151,8 @@ class SubcontractingOrder(SubcontractingController): si.item_name or si.item_code ) ) - else: - for item in items: - self.append("items", item) - else: - self.set_missing_values() + + self.set_missing_values() def update_status(self, status=None, update_modified=True): if self.docstatus >= 1 and not status: diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json index 46c229bfd3..911e903d23 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json @@ -45,7 +45,8 @@ "dimension_col_break", "project", "section_break_34", - "page_break" + "page_break", + "purchase_order_item" ], "fields": [ { @@ -332,13 +333,22 @@ "fieldtype": "Link", "label": "Project", "options": "Project" + }, + { + "fieldname": "purchase_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Order Item", + "no_copy": 1, + "read_only": 1, + "search_index": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:38:37.640677", + "modified": "2023-11-23 16:56:22.182698", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order Item", From e1cea2578151ee0f43e1809a7f99d2e90bf774a6 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 23 Nov 2023 17:48:34 +0530 Subject: [PATCH 08/23] fix: maintain PO and PO Item ref in SCR Item --- .../subcontracting_order.py | 8 +++--- .../subcontracting_receipt_item.json | 25 +++++++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index 376a936e68..cc99085486 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -194,9 +194,11 @@ def make_subcontracting_receipt(source_name, target_doc=None): def get_mapped_subcontracting_receipt(source_name, target_doc=None): - def update_item(obj, target, source_parent): - target.qty = flt(obj.qty) - flt(obj.received_qty) - target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate) + def update_item(source, target, source_parent): + target.purchase_order = source_parent.purchase_order + target.purchase_order_item = source.purchase_order_item + target.qty = flt(source.qty) - flt(source.received_qty) + target.amount = (flt(source.qty) - flt(source.received_qty)) * flt(source.rate) target_doc = get_mapped_doc( "Subcontracting Order", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index 26a29dd811..35a79a9308 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -63,7 +63,9 @@ "dimension_col_break", "project", "section_break_80", - "page_break" + "page_break", + "purchase_order", + "purchase_order_item" ], "fields": [ { @@ -517,12 +519,31 @@ "label": "Reference Name", "no_copy": 1, "read_only": 1 + }, + { + "fieldname": "purchase_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Order Item", + "no_copy": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "purchase_order", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Order", + "no_copy": 1, + "options": "Purchase Order", + "read_only": 1, + "search_index": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:38:26.459669", + "modified": "2023-11-23 17:38:55.134685", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 040cc8d22f700a1823425d1534b91d50289d2489 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 23 Nov 2023 22:16:27 +0530 Subject: [PATCH 09/23] feat: auto create PR on SCR submission --- .../subcontracting_receipt.py | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 8d705aa97d..a38acb27d4 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -3,7 +3,8 @@ import frappe from frappe import _ -from frappe.utils import cint, flt, getdate, nowdate +from frappe.model.mapper import get_mapped_doc +from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate import erpnext from erpnext.accounts.utils import get_account_currency @@ -80,6 +81,7 @@ class SubcontractingReceipt(SubcontractingController): self.make_gl_entries() self.repost_future_sle_and_gle() self.update_status() + self.auto_create_purchase_receipt() def on_update(self): for table_field in ["items", "supplied_items"]: @@ -528,9 +530,96 @@ class SubcontractingReceipt(SubcontractingController): + "\n".join(warehouse_with_no_account) ) + def auto_create_purchase_receipt(self): + if action := frappe.db.get_single_value( + "Buying Settings", "action_on_subcontracting_receipt_submission" + ): + if action == "Create Purchase Receipt": + make_purchase_receipt(self, save=True, notify=True) + elif action == "Create and Submit Purchase Receipt": + make_purchase_receipt(self, submit=True, notify=True) + @frappe.whitelist() def make_subcontract_return(source_name, target_doc=None): from erpnext.controllers.sales_and_purchase_return import make_return_doc return make_return_doc("Subcontracting Receipt", source_name, target_doc) + + +def make_purchase_receipt(subcontracting_receipt, save=False, submit=False, notify=False): + if isinstance(subcontracting_receipt, str): + subcontracting_receipt = frappe.get_doc("Subcontracting Receipt", subcontracting_receipt) + + if not subcontracting_receipt.is_return: + purchase_receipt = frappe.new_doc("Purchase Receipt") + purchase_receipt.is_subcontracted = 1 + purchase_receipt.is_old_subcontracting_flow = 0 + + purchase_receipt = get_mapped_doc( + "Subcontracting Receipt", + subcontracting_receipt.name, + { + "Subcontracting Receipt": { + "doctype": "Purchase Receipt", + "field_map": { + "posting_date": "posting_date", + "posting_time": "posting_time", + "supplier_warehouse": "supplier_warehouse", + }, + "field_no_map": ["total_qty", "total"], + }, + }, + purchase_receipt, + ignore_child_tables=True, + ) + + po_items_details = {} + for item in subcontracting_receipt.items: + if item.purchase_order and item.purchase_order_item: + if item.purchase_order not in po_items_details: + po_doc = frappe.get_doc("Purchase Order", item.purchase_order) + po_items_details[item.purchase_order] = {po_item.name: po_item for po_item in po_doc.items} + + if po_item := po_items_details[item.purchase_order].get(item.purchase_order_item): + conversion_factor = flt(po_item.qty) / flt(po_item.fg_item_qty) + item_row = { + "item_code": po_item.item_code, + "item_name": po_item.item_name, + "qty": item.qty * conversion_factor, + "rejected_qty": item.rejected_qty * conversion_factor, + "uom": po_item.uom, + "rate": po_item.rate, + "warehouse": item.warehouse, + "rejected_warehouse": item.rejected_warehouse, + "purchase_order": item.purchase_order, + "purchase_order_item": item.purchase_order_item, + } + purchase_receipt.append("items", item_row) + + if not purchase_receipt.items: + frappe.throw( + _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( + subcontracting_receipt.name + ) + ) + + if (save or submit) and frappe.has_permission(purchase_receipt.doctype, "create"): + purchase_receipt.save() + + if submit and frappe.has_permission(purchase_receipt.doctype, "submit", purchase_receipt): + try: + purchase_receipt.submit() + except Exception as e: + purchase_receipt.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) + + if notify: + frappe.msgprint( + _("Purchase Receipt {0} created.").format( + get_link_to_form(purchase_receipt.doctype, purchase_receipt.name) + ), + indicator="green", + alert=True, + ) + + return purchase_receipt From ca8a5b45ba58b62cd661dae508af9b0e26ff7973 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 24 Nov 2023 12:09:09 +0530 Subject: [PATCH 10/23] feat: add `Purchase Order` link in SCR connections --- .../subcontracting_receipt_dashboard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py index deb8342b83..f6e1356ed9 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py @@ -9,11 +9,15 @@ def get_data(): }, "internal_links": { "Subcontracting Order": ["items", "subcontracting_order"], + "Purchase Order": ["items", "purchase_order"], "Project": ["items", "project"], "Quality Inspection": ["items", "quality_inspection"], }, "transactions": [ - {"label": _("Reference"), "items": ["Subcontracting Order", "Quality Inspection", "Project"]}, + { + "label": _("Reference"), + "items": ["Subcontracting Order", "Purchase Order", "Quality Inspection", "Project"], + }, {"label": _("Returns"), "items": ["Subcontracting Receipt"]}, ], } From 98cba5ed30ed2c8f5191d7cfaac54b16997cf09e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 24 Nov 2023 12:14:38 +0530 Subject: [PATCH 11/23] feat: add `Subcontracting Receipt` link in PO connections --- .../doctype/purchase_order/purchase_order_dashboard.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py index 05b5a8e7b8..36fe079fc9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py @@ -22,7 +22,10 @@ def get_data(): "label": _("Reference"), "items": ["Material Request", "Supplier Quotation", "Project", "Auto Repeat"], }, - {"label": _("Sub-contracting"), "items": ["Subcontracting Order", "Stock Entry"]}, + { + "label": _("Sub-contracting"), + "items": ["Subcontracting Order", "Subcontracting Receipt", "Stock Entry"], + }, {"label": _("Internal"), "items": ["Sales Order"]}, ], } From dd80d3b9b94c970d5070e1a5fac03cf9f1fbc455 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 24 Nov 2023 12:17:05 +0530 Subject: [PATCH 12/23] fix: dont show `View` button on cancelled SCR --- .../doctype/subcontracting_receipt/subcontracting_receipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 36001eb78f..5585a4a84b 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -24,7 +24,7 @@ frappe.ui.form.on('Subcontracting Receipt', { }, refresh: (frm) => { - if (frm.doc.docstatus > 0) { + if (frm.doc.docstatus === 1) { frm.add_custom_button(__('Stock Ledger'), () => { frappe.route_options = { voucher_no: frm.doc.name, From d366a91d9eae52175cd7f10f8a77b96dc09bfd00 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 28 Nov 2023 13:02:27 +0530 Subject: [PATCH 13/23] fix: use checkbox instead of select field --- .../buying_settings/buying_settings.json | 28 ++++++++++--------- .../doctype/purchase_order/purchase_order.py | 9 ++---- .../subcontracting_receipt.py | 9 ++---- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index bb2476d8f2..eba9f24e34 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -30,9 +30,9 @@ "column_break_11", "over_transfer_allowance", "section_break_xcug", - "action_on_purchase_order_submission", + "auto_create_subcontracting_order", "column_break_izrr", - "action_on_subcontracting_receipt_submission" + "auto_create_purchase_receipt" ], "fields": [ { @@ -181,21 +181,23 @@ "fieldname": "section_break_xcug", "fieldtype": "Section Break" }, - { - "fieldname": "action_on_purchase_order_submission", - "fieldtype": "Select", - "label": "Action on Purchase Order Submission", - "options": "\nCreate Subcontracting Order\nCreate and Submit Subcontracting Order" - }, { "fieldname": "column_break_izrr", "fieldtype": "Column Break" }, { - "fieldname": "action_on_subcontracting_receipt_submission", - "fieldtype": "Select", - "label": "Action on Subcontracting Receipt Submission", - "options": "\nCreate Purchase Receipt\nCreate and Submit Purchase Receipt" + "default": "0", + "description": "Subcontracting Order (Draft) will be auto-created on submission of Purchase Order.", + "fieldname": "auto_create_subcontracting_order", + "fieldtype": "Check", + "label": "Auto Create Subcontracting Order" + }, + { + "default": "0", + "description": "Purchase Receipt (Draft) will be auto-created on submission of Subcontracting Receipt.", + "fieldname": "auto_create_purchase_receipt", + "fieldtype": "Check", + "label": "Auto Create Purchase Receipt" } ], "icon": "fa fa-cog", @@ -203,7 +205,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-22 17:46:26.474708", + "modified": "2023-11-28 13:01:18.403492", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 7f764e939e..f000185393 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -488,13 +488,8 @@ class PurchaseOrder(BuyingController): def auto_create_subcontracting_order(self): if self.is_subcontracted and not self.is_old_subcontracting_flow: - if action := frappe.db.get_single_value( - "Buying Settings", "action_on_purchase_order_submission" - ): - if action == "Create Subcontracting Order": - make_subcontracting_order(self.name, save=True, notify=True) - elif action == "Create and Submit Subcontracting Order": - make_subcontracting_order(self.name, submit=True, notify=True) + if frappe.db.get_single_value("Buying Settings", "auto_create_subcontracting_order"): + make_subcontracting_order(self.name, save=True, notify=True) def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index a38acb27d4..3a8ce01237 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -531,13 +531,8 @@ class SubcontractingReceipt(SubcontractingController): ) def auto_create_purchase_receipt(self): - if action := frappe.db.get_single_value( - "Buying Settings", "action_on_subcontracting_receipt_submission" - ): - if action == "Create Purchase Receipt": - make_purchase_receipt(self, save=True, notify=True) - elif action == "Create and Submit Purchase Receipt": - make_purchase_receipt(self, submit=True, notify=True) + if frappe.db.get_single_value("Buying Settings", "auto_create_purchase_receipt"): + make_purchase_receipt(self, save=True, notify=True) @frappe.whitelist() From d891bd7fac1ab9e46a94959dedcc45e18ac69de5 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 28 Nov 2023 13:17:24 +0530 Subject: [PATCH 14/23] feat: Subcontracting Receipt ref in Purchase Receipt --- .../doctype/purchase_receipt/purchase_receipt.js | 8 ++++++++ .../doctype/purchase_receipt/purchase_receipt.json | 11 ++++++++++- .../subcontracting_receipt/subcontracting_receipt.py | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 6552cd7fce..8943066152 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -49,6 +49,14 @@ frappe.ui.form.on("Purchase Receipt", { } }); + frm.set_query("subcontracting_receipt", function() { + return { + filters: { + 'docstatus': 1, + 'supplier': frm.doc.supplier, + } + } + }); }, onload: function(frm) { erpnext.queries.setup_queries(frm, "Warehouse", function() { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index c8a9e3e82e..c7ad660497 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -16,6 +16,7 @@ "supplier", "supplier_name", "supplier_delivery_note", + "subcontracting_receipt", "column_break1", "posting_date", "posting_time", @@ -1236,13 +1237,21 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "depends_on": "eval: (doc.is_subcontracted && !doc.is_old_subcontracting_flow)", + "fieldname": "subcontracting_receipt", + "fieldtype": "Link", + "label": "Subcontracting Receipt", + "options": "Subcontracting Receipt", + "search_index": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2023-10-01 21:00:44.556816", + "modified": "2023-11-28 13:14:15.243474", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 3a8ce01237..fc9b38d2ff 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -560,6 +560,7 @@ def make_purchase_receipt(subcontracting_receipt, save=False, submit=False, noti "field_map": { "posting_date": "posting_date", "posting_time": "posting_time", + "name": "subcontracting_receipt", "supplier_warehouse": "supplier_warehouse", }, "field_no_map": ["total_qty", "total"], From 37b3ac795284c0ee9593753ececcfe4871e538a4 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 28 Nov 2023 13:39:15 +0530 Subject: [PATCH 15/23] feat: SCR Item ref in PR Item --- .../purchase_receipt_item.json | 16 ++++++++++++++-- .../subcontracting_receipt.py | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index ce2e5d7f84..a86e63daf4 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -127,7 +127,8 @@ "section_break_80", "page_break", "sales_order", - "sales_order_item" + "sales_order_item", + "subcontracting_receipt_item" ], "fields": [ { @@ -1086,12 +1087,23 @@ "print_hide": 1, "read_only": 1, "search_index": 1 + }, + { + "fieldname": "subcontracting_receipt_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Subcontracting Receipt Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1, + "search_index": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:38:15.251994", + "modified": "2023-11-28 13:37:29.245204", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index fc9b38d2ff..b980dbd2a3 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -590,6 +590,7 @@ def make_purchase_receipt(subcontracting_receipt, save=False, submit=False, noti "rejected_warehouse": item.rejected_warehouse, "purchase_order": item.purchase_order, "purchase_order_item": item.purchase_order_item, + "subcontracting_receipt_item": item.name, } purchase_receipt.append("items", item_row) From 8052103197112d2e1abe7ac35a831267ad9549b7 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 28 Nov 2023 16:28:54 +0530 Subject: [PATCH 16/23] feat: provision to create PR from SCR --- .../purchase_receipt/purchase_receipt.js | 14 ++++++ .../subcontracting_receipt.js | 47 +++++++++++-------- .../subcontracting_receipt.py | 46 +++++++++--------- .../subcontracting_receipt_dashboard.py | 2 +- 4 files changed, 68 insertions(+), 41 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 8943066152..6c9d3392e3 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -122,6 +122,20 @@ frappe.ui.form.on("Purchase Receipt", { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, + subcontracting_receipt: (frm) => { + if (frm.doc.is_subcontracted === 1 && frm.doc.is_old_subcontracting_flow === 0 && frm.doc.subcontracting_receipt) { + frm.set_value('items', null); + + erpnext.utils.map_current_doc({ + method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_purchase_receipt', + source_name: frm.doc.subcontracting_receipt, + target_doc: frm, + freeze: true, + freeze_message: __('Mapping Purchase Receipt ...'), + }); + } + }, + toggle_display_account_head: function(frm) { var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company) frm.fields_dict["items"].grid.set_column_disp(["cost_center"], enabled); diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 5585a4a84b..762cdc96af 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -26,27 +26,36 @@ frappe.ui.form.on('Subcontracting Receipt', { refresh: (frm) => { if (frm.doc.docstatus === 1) { frm.add_custom_button(__('Stock Ledger'), () => { - frappe.route_options = { - voucher_no: frm.doc.name, - from_date: frm.doc.posting_date, - to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), - company: frm.doc.company, - show_cancelled_entries: frm.doc.docstatus === 2 - } - frappe.set_route('query-report', 'Stock Ledger'); - }, __('View')); + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + company: frm.doc.company, + show_cancelled_entries: frm.doc.docstatus === 2 + } + frappe.set_route('query-report', 'Stock Ledger'); + }, __('View')); frm.add_custom_button(__('Accounting Ledger'), () => { - frappe.route_options = { - voucher_no: frm.doc.name, - from_date: frm.doc.posting_date, - to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), - company: frm.doc.company, - group_by: 'Group by Voucher (Consolidated)', - show_cancelled_entries: frm.doc.docstatus === 2 - } - frappe.set_route('query-report', 'General Ledger'); - }, __('View')); + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + company: frm.doc.company, + group_by: 'Group by Voucher (Consolidated)', + show_cancelled_entries: frm.doc.docstatus === 2 + } + frappe.set_route('query-report', 'General Ledger'); + }, __('View')); + + frm.add_custom_button(__('Purchase Receipt'), () => { + frappe.model.open_mapped_doc({ + method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_purchase_receipt', + frm: frm, + freeze: true, + freeze_message: __('Creating Purchase Receipt ...') + }); + }, __('Create')); } if (!frm.doc.is_return && frm.doc.docstatus === 1 && frm.doc.per_returned < 100) { diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index b980dbd2a3..000078f241 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -542,18 +542,20 @@ def make_subcontract_return(source_name, target_doc=None): return make_return_doc("Subcontracting Receipt", source_name, target_doc) -def make_purchase_receipt(subcontracting_receipt, save=False, submit=False, notify=False): - if isinstance(subcontracting_receipt, str): - subcontracting_receipt = frappe.get_doc("Subcontracting Receipt", subcontracting_receipt) +@frappe.whitelist() +def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False, notify=False): + if isinstance(source_name, str): + source_name = frappe.get_doc("Subcontracting Receipt", source_name) - if not subcontracting_receipt.is_return: - purchase_receipt = frappe.new_doc("Purchase Receipt") - purchase_receipt.is_subcontracted = 1 - purchase_receipt.is_old_subcontracting_flow = 0 + if not source_name.is_return: + if not target_doc: + target_doc = frappe.new_doc("Purchase Receipt") + target_doc.is_subcontracted = 1 + target_doc.is_old_subcontracting_flow = 0 - purchase_receipt = get_mapped_doc( + target_doc = get_mapped_doc( "Subcontracting Receipt", - subcontracting_receipt.name, + source_name.name, { "Subcontracting Receipt": { "doctype": "Purchase Receipt", @@ -566,12 +568,12 @@ def make_purchase_receipt(subcontracting_receipt, save=False, submit=False, noti "field_no_map": ["total_qty", "total"], }, }, - purchase_receipt, + target_doc, ignore_child_tables=True, ) po_items_details = {} - for item in subcontracting_receipt.items: + for item in source_name.items: if item.purchase_order and item.purchase_order_item: if item.purchase_order not in po_items_details: po_doc = frappe.get_doc("Purchase Order", item.purchase_order) @@ -592,31 +594,33 @@ def make_purchase_receipt(subcontracting_receipt, save=False, submit=False, noti "purchase_order_item": item.purchase_order_item, "subcontracting_receipt_item": item.name, } - purchase_receipt.append("items", item_row) + target_doc.append("items", item_row) - if not purchase_receipt.items: + if not target_doc.items: frappe.throw( _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( - subcontracting_receipt.name + source_name.name ) ) - if (save or submit) and frappe.has_permission(purchase_receipt.doctype, "create"): - purchase_receipt.save() + target_doc.set_missing_values() - if submit and frappe.has_permission(purchase_receipt.doctype, "submit", purchase_receipt): + if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): + target_doc.save() + + if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): try: - purchase_receipt.submit() + target_doc.submit() except Exception as e: - purchase_receipt.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) + target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) if notify: frappe.msgprint( _("Purchase Receipt {0} created.").format( - get_link_to_form(purchase_receipt.doctype, purchase_receipt.name) + get_link_to_form(target_doc.doctype, target_doc.name) ), indicator="green", alert=True, ) - return purchase_receipt + return target_doc diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py index f6e1356ed9..4d15938e4b 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py @@ -3,7 +3,7 @@ from frappe import _ def get_data(): return { - "fieldname": "subcontracting_receipt_no", + "fieldname": "subcontracting_receipt", "non_standard_fieldnames": { "Subcontracting Receipt": "return_against", }, From 096a2c8cd0afafb28e916a5c9e92ef1eb041e0a3 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 12:46:31 +0530 Subject: [PATCH 17/23] chore: PR ref in SCR connections --- .../subcontracting_receipt/subcontracting_receipt.js | 4 ++++ .../subcontracting_receipt_dashboard.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 762cdc96af..86b4d9bf1c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -11,6 +11,10 @@ frappe.ui.form.on('Subcontracting Receipt', { frm.get_field('supplied_items').grid.cannot_add_rows = true; frm.get_field('supplied_items').grid.only_sortable(); frm.trigger('set_queries'); + + frm.custom_make_buttons = { + 'Purchase Receipt': 'Purchase Receipt', + } }, on_submit(frm) { diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py index 4d15938e4b..6b67a1621a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py @@ -16,7 +16,13 @@ def get_data(): "transactions": [ { "label": _("Reference"), - "items": ["Subcontracting Order", "Purchase Order", "Quality Inspection", "Project"], + "items": [ + "Purchase Order", + "Purchase Receipt", + "Subcontracting Order", + "Quality Inspection", + "Project", + ], }, {"label": _("Returns"), "items": ["Subcontracting Receipt"]}, ], From 874766a82f1b31a55b8d5c9c998896748ad49cdf Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 13:07:39 +0530 Subject: [PATCH 18/23] fix: map warehouses in return SCR --- erpnext/controllers/sales_and_purchase_return.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index e91212b031..8e3a15a0ad 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -391,7 +391,10 @@ def make_return_doc( if doc.get("discount_amount"): doc.discount_amount = -1 * source.discount_amount - if doctype != "Subcontracting Receipt": + if doctype == "Subcontracting Receipt": + doc.set_warehouse = source.set_warehouse + doc.supplier_warehouse = source.supplier_warehouse + else: doc.run_method("calculate_taxes_and_totals") def update_item(source_doc, target_doc, source_parent): From 7145b040f12a62be9f0ac945c69ac221debd7c03 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 13:21:24 +0530 Subject: [PATCH 19/23] fix(ux): hide `Create Purchase Receipt` button for Subcontract Return --- .../subcontracting_receipt.js | 68 ++++++++++--------- .../subcontracting_receipt.py | 12 ++-- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 86b4d9bf1c..575c4eda73 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -52,49 +52,51 @@ frappe.ui.form.on('Subcontracting Receipt', { frappe.set_route('query-report', 'General Ledger'); }, __('View')); - frm.add_custom_button(__('Purchase Receipt'), () => { - frappe.model.open_mapped_doc({ - method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_purchase_receipt', - frm: frm, - freeze: true, - freeze_message: __('Creating Purchase Receipt ...') - }); - }, __('Create')); + if (frm.doc.is_return === 0) { + frm.add_custom_button(__('Purchase Receipt'), () => { + frappe.model.open_mapped_doc({ + method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_purchase_receipt', + frm: frm, + freeze: true, + freeze_message: __('Creating Purchase Receipt ...') + }); + }, __('Create')); + } } if (!frm.doc.is_return && frm.doc.docstatus === 1 && frm.doc.per_returned < 100) { frm.add_custom_button(__('Subcontract Return'), () => { - frappe.model.open_mapped_doc({ - method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_subcontract_return', - frm: frm - }); - }, __('Create')); + frappe.model.open_mapped_doc({ + method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_subcontract_return', + frm: frm + }); + }, __('Create')); frm.page.set_inner_btn_group_as_primary(__('Create')); } if (frm.doc.docstatus === 0) { frm.add_custom_button(__('Subcontracting Order'), () => { - if (!frm.doc.supplier) { - frappe.throw({ - title: __('Mandatory'), - message: __('Please Select a Supplier') - }); - } - - erpnext.utils.map_current_doc({ - method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt', - source_doctype: 'Subcontracting Order', - target: frm, - setters: { - supplier: frm.doc.supplier, - }, - get_query_filters: { - docstatus: 1, - per_received: ['<', 100], - company: frm.doc.company - } + if (!frm.doc.supplier) { + frappe.throw({ + title: __('Mandatory'), + message: __('Please Select a Supplier') }); - }, __('Get Items From')); + } + + erpnext.utils.map_current_doc({ + method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt', + source_doctype: 'Subcontracting Order', + target: frm, + setters: { + supplier: frm.doc.supplier, + }, + get_query_filters: { + docstatus: 1, + per_received: ['<', 100], + company: frm.doc.company + } + }); + }, __('Get Items From')); frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM'); } diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 000078f241..4f4a65044d 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -545,9 +545,11 @@ def make_subcontract_return(source_name, target_doc=None): @frappe.whitelist() def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False, notify=False): if isinstance(source_name, str): - source_name = frappe.get_doc("Subcontracting Receipt", source_name) + source_doc = frappe.get_doc("Subcontracting Receipt", source_name) + else: + source_doc = source_name - if not source_name.is_return: + if not source_doc.is_return: if not target_doc: target_doc = frappe.new_doc("Purchase Receipt") target_doc.is_subcontracted = 1 @@ -555,7 +557,7 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False target_doc = get_mapped_doc( "Subcontracting Receipt", - source_name.name, + source_doc.name, { "Subcontracting Receipt": { "doctype": "Purchase Receipt", @@ -573,7 +575,7 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False ) po_items_details = {} - for item in source_name.items: + for item in source_doc.items: if item.purchase_order and item.purchase_order_item: if item.purchase_order not in po_items_details: po_doc = frappe.get_doc("Purchase Order", item.purchase_order) @@ -599,7 +601,7 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False if not target_doc.items: frappe.throw( _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( - source_name.name + source_doc.name ) ) From 857f2b5a014b4d14d21bca793ea569f8bdc1c398 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 13:39:02 +0530 Subject: [PATCH 20/23] chore: `linter` --- .../subcontracting_order/subcontracting_order.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index cc99085486..33a2061320 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -123,6 +123,8 @@ class SubcontractingOrder(SubcontractingController): stock_bin.update_reserved_qty_for_sub_contracting() def populate_items_table(self): + items = [] + for si in self.service_items: if si.fg_item: item = frappe.get_doc("Item", si.fg_item) @@ -132,8 +134,8 @@ class SubcontractingOrder(SubcontractingController): ) or item.default_bom ) - self.append( - "items", + + items.append( { "item_code": item.item_code, "item_name": item.item_name, @@ -143,7 +145,7 @@ class SubcontractingOrder(SubcontractingController): "stock_uom": item.stock_uom, "bom": bom, "purchase_order_item": si.purchase_order_item, - }, + } ) else: frappe.throw( @@ -152,6 +154,10 @@ class SubcontractingOrder(SubcontractingController): ) ) + if items: + for item in items: + self.append("items", item) + self.set_missing_values() def update_status(self, status=None, update_modified=True): From 68585f6f2b05e0b8dc3429741950ef31bbc42e17 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 13:58:21 +0530 Subject: [PATCH 21/23] test: auto create SCO on PO submit --- .../purchase_order/test_purchase_order.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 0f8574c84d..f80a00a95f 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -981,6 +981,38 @@ class TestPurchaseOrder(FrappeTestCase): self.assertEqual(po.items[0].qty, 30) self.assertEqual(po.items[0].fg_item_qty, 30) + @change_settings("Buying Settings", {"auto_create_subcontracting_order": 1}) + def test_auto_create_subcontracting_order(self): + from erpnext.controllers.tests.test_subcontracting_controller import ( + make_bom_for_subcontracted_items, + make_raw_materials, + make_service_items, + make_subcontracted_items, + ) + + make_subcontracted_items() + make_raw_materials() + make_service_items() + make_bom_for_subcontracted_items() + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 7", + "qty": 10, + "rate": 100, + "fg_item": "Subcontracted Item SA7", + "fg_item_qty": 10, + }, + ] + po = create_purchase_order( + rm_items=service_items, + is_subcontracted=1, + supplier_warehouse="_Test Warehouse 1 - _TC", + ) + + self.assertTrue(frappe.db.get_value("Subcontracting Order", {"purchase_order": po.name})) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From 7b0cd03f88a7052460438ee70390799fae5c336c Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 14:10:48 +0530 Subject: [PATCH 22/23] test: auto create PR on SCR submit --- .../subcontracting_receipt.py | 4 +-- .../test_subcontracting_receipt.py | 29 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 4f4a65044d..4d7b15717e 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -586,8 +586,8 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False item_row = { "item_code": po_item.item_code, "item_name": po_item.item_name, - "qty": item.qty * conversion_factor, - "rejected_qty": item.rejected_qty * conversion_factor, + "qty": flt(item.qty) * conversion_factor, + "rejected_qty": flt(item.rejected_qty) * conversion_factor, "uom": po_item.uom, "rate": po_item.rate, "warehouse": item.warehouse, diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index f0e4e00074..1d007fe3c8 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -5,7 +5,7 @@ import copy import frappe -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, cint, cstr, flt, nowtime, today import erpnext @@ -953,6 +953,33 @@ class TestSubcontractingReceipt(FrappeTestCase): scr.submit() + @change_settings("Buying Settings", {"auto_create_purchase_receipt": 1}) + def test_auto_create_purchase_receipt(self): + fg_item = "Subcontracted Item SA1" + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 5, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 5, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + scr = make_subcontracting_receipt(sco.name) + scr.save() + scr.submit() + + self.assertTrue(frappe.db.get_value("Purchase Receipt", {"subcontracting_receipt": scr.name})) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From 3da0aa6a0b21c775e5356ae7d359f7378f190d92 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 29 Nov 2023 17:35:21 +0530 Subject: [PATCH 23/23] fix(test): `test_auto_create_purchase_receipt` --- .../doctype/subcontracting_receipt/subcontracting_receipt.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 4d7b15717e..33e5c3ad0f 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -574,6 +574,8 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False ignore_child_tables=True, ) + target_doc.currency = frappe.get_cached_value("Company", target_doc.company, "default_currency") + po_items_details = {} for item in source_doc.items: if item.purchase_order and item.purchase_order_item: @@ -586,6 +588,7 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False item_row = { "item_code": po_item.item_code, "item_name": po_item.item_name, + "conversion_factor": conversion_factor, "qty": flt(item.qty) * conversion_factor, "rejected_qty": flt(item.rejected_qty) * conversion_factor, "uom": po_item.uom,