feat: auto create PR on SCR submission (backport #38290) (#38428)

* feat: add field `Action on Purchase Order Submission`

(cherry picked from commit 628ea42b63)

# Conflicts:
#	erpnext/buying/doctype/buying_settings/buying_settings.json

* feat: auto create SCO on PO submission

(cherry picked from commit 9ec6f1e1d6)

# Conflicts:
#	erpnext/buying/doctype/buying_settings/buying_settings.json

* feat: add field `Action on Subcontracting Receipt Submission`

(cherry picked from commit 762906f240)

# Conflicts:
#	erpnext/buying/doctype/buying_settings/buying_settings.json

* chore: notify user on SCO creation

(cherry picked from commit 745e3bfb73)

* feat: add field `Purchase Order Item` in SCO Service Item

(cherry picked from commit 45d5cff47d)

* fix: hold PO item ref in SCO Service Item

(cherry picked from commit 7e4dd33ab0)

* feat: add field `Purchase Order Item` in SCO Item

(cherry picked from commit a2ede7d6d5)

* fix: maintain PO and PO Item ref in SCR Item

(cherry picked from commit e1cea25781)

* feat: auto create PR on SCR submission

(cherry picked from commit 040cc8d22f)

* feat: add `Purchase Order` link in SCR connections

(cherry picked from commit ca8a5b45ba)

* feat: add `Subcontracting Receipt` link in PO connections

(cherry picked from commit 98cba5ed30)

* fix: dont show `View` button on cancelled SCR

(cherry picked from commit dd80d3b9b9)

* fix: use checkbox instead of select field

(cherry picked from commit d366a91d9e)

# Conflicts:
#	erpnext/buying/doctype/buying_settings/buying_settings.json

* feat: Subcontracting Receipt ref in Purchase Receipt

(cherry picked from commit d891bd7fac)

* feat: SCR Item ref in PR Item

(cherry picked from commit 37b3ac7952)

* feat: provision to create PR from SCR

(cherry picked from commit 8052103197)

* chore: PR ref in SCR connections

(cherry picked from commit 096a2c8cd0)

* fix: map warehouses in return SCR

(cherry picked from commit 874766a82f)

* fix(ux): hide `Create Purchase Receipt` button for Subcontract Return

(cherry picked from commit 7145b040f1)

* chore: `linter`

(cherry picked from commit 857f2b5a01)

* test: auto create SCO on PO submit

(cherry picked from commit 68585f6f2b)

* test: auto create PR on SCR submit

(cherry picked from commit 7b0cd03f88)

* fix(test): `test_auto_create_purchase_receipt`

(cherry picked from commit 3da0aa6a0b)

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
This commit is contained in:
mergify[bot]
2023-11-30 12:11:05 +05:30
committed by GitHub
parent 277e81c68e
commit 7005d51af3
16 changed files with 531 additions and 200 deletions

View File

@@ -159,6 +159,7 @@ class SubcontractingOrder(SubcontractingController):
)
or item.default_bom
)
items.append(
{
"item_code": item.item_code,
@@ -168,7 +169,8 @@ class SubcontractingOrder(SubcontractingController):
"qty": si.fg_item_qty,
"stock_uom": item.stock_uom,
"bom": bom,
},
"purchase_order_item": si.purchase_order_item,
}
)
else:
frappe.throw(
@@ -176,11 +178,12 @@ class SubcontractingOrder(SubcontractingController):
si.item_name or si.item_code
)
)
else:
if items:
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:
@@ -222,9 +225,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",

View File

@@ -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",

View File

@@ -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": []
}

View File

@@ -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) {
@@ -24,64 +28,75 @@ 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_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'));
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');
}

View File

@@ -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"]:
@@ -532,9 +534,102 @@ class SubcontractingReceipt(SubcontractingController):
+ "\n".join(warehouse_with_no_account)
)
def auto_create_purchase_receipt(self):
if frappe.db.get_single_value("Buying Settings", "auto_create_purchase_receipt"):
make_purchase_receipt(self, save=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)
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False, notify=False):
if isinstance(source_name, str):
source_doc = frappe.get_doc("Subcontracting Receipt", source_name)
else:
source_doc = source_name
if not source_doc.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
target_doc = get_mapped_doc(
"Subcontracting Receipt",
source_doc.name,
{
"Subcontracting Receipt": {
"doctype": "Purchase Receipt",
"field_map": {
"posting_date": "posting_date",
"posting_time": "posting_time",
"name": "subcontracting_receipt",
"supplier_warehouse": "supplier_warehouse",
},
"field_no_map": ["total_qty", "total"],
},
},
target_doc,
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:
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,
"conversion_factor": 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,
"rejected_warehouse": item.rejected_warehouse,
"purchase_order": item.purchase_order,
"purchase_order_item": item.purchase_order_item,
"subcontracting_receipt_item": item.name,
}
target_doc.append("items", item_row)
if not target_doc.items:
frappe.throw(
_("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format(
source_doc.name
)
)
target_doc.set_missing_values()
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") + "<br><br>" + str(e))
if notify:
frappe.msgprint(
_("Purchase Receipt {0} created.").format(
get_link_to_form(target_doc.doctype, target_doc.name)
),
indicator="green",
alert=True,
)
return target_doc

View File

@@ -3,17 +3,27 @@ from frappe import _
def get_data():
return {
"fieldname": "subcontracting_receipt_no",
"fieldname": "subcontracting_receipt",
"non_standard_fieldnames": {
"Subcontracting Receipt": "return_against",
},
"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": [
"Purchase Order",
"Purchase Receipt",
"Subcontracting Order",
"Quality Inspection",
"Project",
],
},
{"label": _("Returns"), "items": ["Subcontracting Receipt"]},
],
}

View File

@@ -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)

View File

@@ -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",