Merge pull request #36786 from s-aga-r/SCR-QI
feat: `Quality Inspection` for Subcontracting Receipt
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index d669abe..ae54b80 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -599,6 +599,7 @@
inspection_fieldname_map = {
"Purchase Receipt": "inspection_required_before_purchase",
"Purchase Invoice": "inspection_required_before_purchase",
+ "Subcontracting Receipt": "inspection_required_before_purchase",
"Sales Invoice": "inspection_required_before_delivery",
"Delivery Note": "inspection_required_before_delivery",
}
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 59d2b15..ac5735b 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -277,7 +277,7 @@
}
setup_quality_inspection() {
- if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
+ if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"], this.frm.doc.doctype)) {
return;
}
@@ -289,7 +289,7 @@
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
}
- const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)
+ const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"], this.frm.doc.doctype)
? "Incoming" : "Outgoing";
let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
@@ -2067,6 +2067,7 @@
const me = this;
const dialog = new frappe.ui.Dialog({
title: __("Select Items for Quality Inspection"),
+ size: "extra-large",
fields: fields,
primary_action: function () {
const data = dialog.get_values();
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index db9322f..914a9f3 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -74,7 +74,7 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
- "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry\nJob Card",
+ "options": "\nPurchase Receipt\nPurchase Invoice\nSubcontracting Receipt\nDelivery Note\nSales Invoice\nStock Entry\nJob Card",
"reqd": 1
},
{
@@ -245,7 +245,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-10-04 22:00:13.995221",
+ "modified": "2023-08-23 11:56:50.282878",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index 94a2589..e374077 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -3,14 +3,91 @@
frappe.provide('erpnext.buying');
-erpnext.landed_cost_taxes_and_charges.setup_triggers("Subcontracting Receipt");
+erpnext.landed_cost_taxes_and_charges.setup_triggers('Subcontracting Receipt');
frappe.ui.form.on('Subcontracting Receipt', {
setup: (frm) => {
frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle'];
frm.get_field('supplied_items').grid.cannot_add_rows = true;
frm.get_field('supplied_items').grid.only_sortable();
+ frm.trigger('set_queries');
+ },
+ refresh: (frm) => {
+ if (frm.doc.docstatus > 0) {
+ 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'));
+
+ 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'));
+ }
+
+ 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'));
+ 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
+ }
+ });
+ }, __('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');
+ }
+
+ frm.trigger('setup_quality_inspection');
+ },
+
+ set_warehouse: (frm) => {
+ set_warehouse_in_children(frm.doc.items, 'warehouse', frm.doc.set_warehouse);
+ },
+
+ rejected_warehouse: (frm) => {
+ set_warehouse_in_children(frm.doc.items, 'rejected_warehouse', frm.doc.rejected_warehouse);
+ },
+
+ set_queries: (frm) => {
frm.set_query('set_warehouse', () => {
return {
filters: {
@@ -52,38 +129,36 @@
}
}));
- frm.set_query('expense_account', 'items', function () {
- return {
+ frm.set_query('expense_account', 'items', () => ({
query: 'erpnext.controllers.queries.get_expense_account',
filters: { 'company': frm.doc.company }
- };
- });
+ }));
- frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
+ frm.set_query('batch_no', 'items', (doc, cdt, cdn) => {
var row = locals[cdt][cdn];
return {
filters: {
item: row.item_code
}
- }
+ };
});
- frm.set_query('batch_no', 'supplied_items', function(doc, cdt, cdn) {
+ frm.set_query('batch_no', 'supplied_items', (doc, cdt, cdn) => {
var row = locals[cdt][cdn];
return {
filters: {
item: row.rm_item_code
}
- }
+ };
});
- frm.set_query("serial_and_batch_bundle", "supplied_items", (doc, cdt, cdn) => {
+ frm.set_query('serial_and_batch_bundle', 'supplied_items', (doc, cdt, cdn) => {
let row = locals[cdt][cdn];
return {
filters: {
'item_code': row.rm_item_code,
'voucher_type': doc.doctype,
- 'voucher_no': ["in", [doc.name, ""]],
+ 'voucher_no': ['in', [doc.name, '']],
'is_cancelled': 0,
}
}
@@ -101,7 +176,7 @@
let batch_no_field = frm.get_docfield('items', 'batch_no');
if (batch_no_field) {
- batch_no_field.get_route_options_for_new_doc = function(row) {
+ batch_no_field.get_route_options_for_new_doc = (row) => {
return {
'item': row.doc.item_code
}
@@ -109,85 +184,20 @@
}
},
- refresh: (frm) => {
- if (frm.doc.docstatus > 0) {
- frm.add_custom_button(__('Stock Ledger'), function () {
- 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'), function () {
- 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'));
+ setup_quality_inspection: (frm) => {
+ if (!frm.is_new() && frm.doc.docstatus === 0 && !frm.doc.is_return) {
+ let transaction_controller = new erpnext.TransactionController({ frm: frm });
+ transaction_controller.setup_quality_inspection();
}
-
- if (!frm.doc.is_return && frm.doc.docstatus == 1 && frm.doc.per_returned < 100) {
- frm.add_custom_button(__('Subcontract Return'), function () {
- 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'), function () {
- 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
- }
- });
- }, __('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');
- }
- },
-
- set_warehouse: (frm) => {
- set_warehouse_in_children(frm.doc.items, 'warehouse', frm.doc.set_warehouse);
- },
-
- rejected_warehouse: (frm) => {
- set_warehouse_in_children(frm.doc.items, 'rejected_warehouse', frm.doc.rejected_warehouse);
},
});
frappe.ui.form.on('Landed Cost Taxes and Charges', {
- amount: function (frm, cdt, cdn) {
+ amount: (frm, cdt, cdn) => {
frm.events.set_base_amount(frm, cdt, cdn);
},
- expense_account: function (frm, cdt, cdn) {
+ expense_account: (frm, cdt, cdn) => {
frm.events.set_account_currency(frm, cdt, cdn);
}
});
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index d2bf7e8..afe1b60 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -81,6 +81,9 @@
self.validate_posting_time()
self.validate_rejected_warehouse()
+ if not self.get("is_return"):
+ self.validate_inspection()
+
if getdate(self.posting_date) > getdate(nowdate()):
frappe.throw(_("Posting Date cannot be future date"))
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 887cba5..a170527 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -567,6 +567,64 @@
self.assertEqual(rm_item.rate, 100)
self.assertEqual(rm_item.amount, rm_item.consumed_qty * rm_item.rate)
+ def test_quality_inspection_for_subcontracting_receipt(self):
+ from erpnext.stock.doctype.quality_inspection.test_quality_inspection import (
+ create_quality_inspection,
+ )
+
+ set_backflush_based_on("BOM")
+ 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),
+ )
+ scr1 = make_subcontracting_receipt(sco.name)
+ scr1.save()
+
+ # Enable `Inspection Required before Purchase` in Item Master
+ frappe.db.set_value("Item", fg_item, "inspection_required_before_purchase", 1)
+
+ # ValidationError should be raised as Quality Inspection is not created/linked
+ self.assertRaises(frappe.ValidationError, scr1.submit)
+
+ qa = create_quality_inspection(
+ reference_type="Subcontracting Receipt",
+ reference_name=scr1.name,
+ inspection_type="Incoming",
+ item_code=fg_item,
+ )
+ scr1.reload()
+ self.assertEqual(scr1.items[0].quality_inspection, qa.name)
+
+ # SCR should be submitted successfully as Quality Inspection is set
+ scr1.submit()
+ qa.cancel()
+ scr1.reload()
+ scr1.cancel()
+
+ scr2 = make_subcontracting_receipt(sco.name)
+ scr2.save()
+
+ # Disable `Inspection Required before Purchase` in Item Master
+ frappe.db.set_value("Item", fg_item, "inspection_required_before_purchase", 0)
+
+ # ValidationError should not be raised as `Inspection Required before Purchase` is disabled
+ scr2.submit()
+
def make_return_subcontracting_receipt(**args):
args = frappe._dict(args)