Merge pull request #36786 from s-aga-r/SCR-QI
feat: `Quality Inspection` for Subcontracting Receipt
This commit is contained in:
commit
49be1190d9
@ -599,6 +599,7 @@ class StockController(AccountsController):
|
|||||||
inspection_fieldname_map = {
|
inspection_fieldname_map = {
|
||||||
"Purchase Receipt": "inspection_required_before_purchase",
|
"Purchase Receipt": "inspection_required_before_purchase",
|
||||||
"Purchase Invoice": "inspection_required_before_purchase",
|
"Purchase Invoice": "inspection_required_before_purchase",
|
||||||
|
"Subcontracting Receipt": "inspection_required_before_purchase",
|
||||||
"Sales Invoice": "inspection_required_before_delivery",
|
"Sales Invoice": "inspection_required_before_delivery",
|
||||||
"Delivery Note": "inspection_required_before_delivery",
|
"Delivery Note": "inspection_required_before_delivery",
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_quality_inspection() {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
|
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";
|
? "Incoming" : "Outgoing";
|
||||||
|
|
||||||
let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
|
let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
|
||||||
@ -2067,6 +2067,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
const me = this;
|
const me = this;
|
||||||
const dialog = new frappe.ui.Dialog({
|
const dialog = new frappe.ui.Dialog({
|
||||||
title: __("Select Items for Quality Inspection"),
|
title: __("Select Items for Quality Inspection"),
|
||||||
|
size: "extra-large",
|
||||||
fields: fields,
|
fields: fields,
|
||||||
primary_action: function () {
|
primary_action: function () {
|
||||||
const data = dialog.get_values();
|
const data = dialog.get_values();
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
"fieldname": "reference_type",
|
"fieldname": "reference_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Reference Type",
|
"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
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -245,7 +245,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-10-04 22:00:13.995221",
|
"modified": "2023-08-23 11:56:50.282878",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Quality Inspection",
|
"name": "Quality Inspection",
|
||||||
|
@ -3,14 +3,91 @@
|
|||||||
|
|
||||||
frappe.provide('erpnext.buying');
|
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', {
|
frappe.ui.form.on('Subcontracting Receipt', {
|
||||||
setup: (frm) => {
|
setup: (frm) => {
|
||||||
frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle'];
|
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.cannot_add_rows = true;
|
||||||
frm.get_field('supplied_items').grid.only_sortable();
|
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', () => {
|
frm.set_query('set_warehouse', () => {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@ -52,38 +129,36 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
frm.set_query('expense_account', 'items', function () {
|
frm.set_query('expense_account', 'items', () => ({
|
||||||
return {
|
|
||||||
query: 'erpnext.controllers.queries.get_expense_account',
|
query: 'erpnext.controllers.queries.get_expense_account',
|
||||||
filters: { 'company': frm.doc.company }
|
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];
|
var row = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
item: row.item_code
|
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];
|
var row = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
item: row.rm_item_code
|
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];
|
let row = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'item_code': row.rm_item_code,
|
'item_code': row.rm_item_code,
|
||||||
'voucher_type': doc.doctype,
|
'voucher_type': doc.doctype,
|
||||||
'voucher_no': ["in", [doc.name, ""]],
|
'voucher_no': ['in', [doc.name, '']],
|
||||||
'is_cancelled': 0,
|
'is_cancelled': 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +176,7 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
|||||||
|
|
||||||
let batch_no_field = frm.get_docfield('items', 'batch_no');
|
let batch_no_field = frm.get_docfield('items', 'batch_no');
|
||||||
if (batch_no_field) {
|
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 {
|
return {
|
||||||
'item': row.doc.item_code
|
'item': row.doc.item_code
|
||||||
}
|
}
|
||||||
@ -109,85 +184,20 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: (frm) => {
|
setup_quality_inspection: (frm) => {
|
||||||
if (frm.doc.docstatus > 0) {
|
if (!frm.is_new() && frm.doc.docstatus === 0 && !frm.doc.is_return) {
|
||||||
frm.add_custom_button(__('Stock Ledger'), function () {
|
let transaction_controller = new erpnext.TransactionController({ frm: frm });
|
||||||
frappe.route_options = {
|
transaction_controller.setup_quality_inspection();
|
||||||
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'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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', {
|
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);
|
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);
|
frm.events.set_account_currency(frm, cdt, cdn);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -81,6 +81,9 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
self.validate_rejected_warehouse()
|
self.validate_rejected_warehouse()
|
||||||
|
|
||||||
|
if not self.get("is_return"):
|
||||||
|
self.validate_inspection()
|
||||||
|
|
||||||
if getdate(self.posting_date) > getdate(nowdate()):
|
if getdate(self.posting_date) > getdate(nowdate()):
|
||||||
frappe.throw(_("Posting Date cannot be future date"))
|
frappe.throw(_("Posting Date cannot be future date"))
|
||||||
|
|
||||||
|
@ -567,6 +567,64 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
|||||||
self.assertEqual(rm_item.rate, 100)
|
self.assertEqual(rm_item.rate, 100)
|
||||||
self.assertEqual(rm_item.amount, rm_item.consumed_qty * rm_item.rate)
|
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):
|
def make_return_subcontracting_receipt(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user