feat: UOM specific barcodes (#30988)

This commit is contained in:
Ankush Menat 2022-06-01 16:43:56 +05:30 committed by GitHub
parent 37433aad48
commit 3974fbbb6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 10 deletions

View File

@ -423,7 +423,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
item.barcode = null; item.barcode = null;
if(item.item_code || item.barcode || item.serial_no) { if(item.item_code || item.serial_no) {
if(!this.validate_company_and_party()) { if(!this.validate_company_and_party()) {
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove(); this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
} else { } else {
@ -463,6 +463,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
stock_qty: item.stock_qty, stock_qty: item.stock_qty,
conversion_factor: item.conversion_factor, conversion_factor: item.conversion_factor,
weight_per_unit: item.weight_per_unit, weight_per_unit: item.weight_per_unit,
uom: item.uom,
weight_uom: item.weight_uom, weight_uom: item.weight_uom,
manufacturer: item.manufacturer, manufacturer: item.manufacturer,
stock_uom: item.stock_uom, stock_uom: item.stock_uom,

View File

@ -9,6 +9,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
this.barcode_field = opts.barcode_field || "barcode"; this.barcode_field = opts.barcode_field || "barcode";
this.serial_no_field = opts.serial_no_field || "serial_no"; this.serial_no_field = opts.serial_no_field || "serial_no";
this.batch_no_field = opts.batch_no_field || "batch_no"; this.batch_no_field = opts.batch_no_field || "batch_no";
this.uom_field = opts.uom_field || "uom";
this.qty_field = opts.qty_field || "qty"; this.qty_field = opts.qty_field || "qty";
// field name on row which defines max quantity to be scanned e.g. picklist // field name on row which defines max quantity to be scanned e.g. picklist
this.max_qty_field = opts.max_qty_field; this.max_qty_field = opts.max_qty_field;
@ -26,6 +27,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
// bar_code: "123456", // present if barcode was scanned // bar_code: "123456", // present if barcode was scanned
// batch_no: "LOT12", // present if batch was scanned // batch_no: "LOT12", // present if batch was scanned
// serial_no: "987XYZ", // present if serial no was scanned // serial_no: "987XYZ", // present if serial no was scanned
// uom: "Kg", // present if barcode UOM is different from default
// } // }
this.scan_api = opts.scan_api || "erpnext.stock.utils.scan_barcode"; this.scan_api = opts.scan_api || "erpnext.stock.utils.scan_barcode";
} }
@ -67,9 +69,9 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
return new Promise(resolve => { return new Promise(resolve => {
let cur_grid = this.frm.fields_dict[this.items_table_name].grid; let cur_grid = this.frm.fields_dict[this.items_table_name].grid;
const {item_code, barcode, batch_no, serial_no} = data; const {item_code, barcode, batch_no, serial_no, uom} = data;
let row = this.get_row_to_modify_on_scan(item_code, batch_no); let row = this.get_row_to_modify_on_scan(item_code, batch_no, uom);
if (!row) { if (!row) {
if (this.dont_allow_new_row) { if (this.dont_allow_new_row) {
@ -90,10 +92,11 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
} }
frappe.run_serially([ frappe.run_serially([
() => this.set_selector_trigger_flag(row, data), () => this.set_selector_trigger_flag(data),
() => this.set_item(row, item_code).then(qty => { () => this.set_item(row, item_code).then(qty => {
this.show_scan_message(row.idx, row.item_code, qty); this.show_scan_message(row.idx, row.item_code, qty);
}), }),
() => this.set_barcode_uom(row, uom),
() => this.set_serial_no(row, serial_no), () => this.set_serial_no(row, serial_no),
() => this.set_batch_no(row, batch_no), () => this.set_batch_no(row, batch_no),
() => this.set_barcode(row, barcode), () => this.set_barcode(row, barcode),
@ -106,7 +109,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
// batch and serial selector is reduandant when all info can be added by scan // batch and serial selector is reduandant when all info can be added by scan
// this flag on item row is used by transaction.js to avoid triggering selector // this flag on item row is used by transaction.js to avoid triggering selector
set_selector_trigger_flag(row, data) { set_selector_trigger_flag(data) {
const {batch_no, serial_no, has_batch_no, has_serial_no} = data; const {batch_no, serial_no, has_batch_no, has_serial_no} = data;
const require_selecting_batch = has_batch_no && !batch_no; const require_selecting_batch = has_batch_no && !batch_no;
@ -154,6 +157,12 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
} }
} }
async set_barcode_uom(row, uom) {
if (uom && frappe.meta.has_field(row.doctype, this.uom_field)) {
await frappe.model.set_value(row.doctype, row.name, this.uom_field, uom);
}
}
async set_batch_no(row, batch_no) { async set_batch_no(row, batch_no) {
if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) { if (batch_no && frappe.meta.has_field(row.doctype, this.batch_no_field)) {
await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no); await frappe.model.set_value(row.doctype, row.name, this.batch_no_field, batch_no);
@ -184,7 +193,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
return is_duplicate; return is_duplicate;
} }
get_row_to_modify_on_scan(item_code, batch_no) { get_row_to_modify_on_scan(item_code, batch_no, uom) {
let cur_grid = this.frm.fields_dict[this.items_table_name].grid; let cur_grid = this.frm.fields_dict[this.items_table_name].grid;
// Check if batch is scanned and table has batch no field // Check if batch is scanned and table has batch no field
@ -193,10 +202,12 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
const matching_row = (row) => { const matching_row = (row) => {
const item_match = row.item_code == item_code; const item_match = row.item_code == item_code;
const batch_match = row.batch_no == batch_no; const batch_match = row[this.batch_no_field] == batch_no;
const uom_match = !uom || row[this.uom_field] == uom;
const qty_in_limit = flt(row[this.qty_field]) < flt(row[this.max_qty_field]); const qty_in_limit = flt(row[this.qty_field]) < flt(row[this.max_qty_field]);
return item_match return item_match
&& uom_match
&& (!is_batch_no_scan || batch_match) && (!is_batch_no_scan || batch_match)
&& (!check_max_qty || qty_in_limit) && (!check_max_qty || qty_in_limit)
} }

View File

@ -6,7 +6,8 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"barcode", "barcode",
"barcode_type" "barcode_type",
"uom"
], ],
"fields": [ "fields": [
{ {
@ -24,11 +25,18 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Barcode Type", "label": "Barcode Type",
"options": "\nEAN\nUPC-A" "options": "\nEAN\nUPC-A"
},
{
"fieldname": "uom",
"fieldtype": "Link",
"in_list_view": 1,
"label": "UOM",
"options": "UOM"
} }
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-04-01 05:54:27.314030", "modified": "2022-06-01 06:24:33.969534",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item Barcode", "name": "Item Barcode",

View File

@ -558,7 +558,7 @@ def scan_barcode(search_value: str) -> Dict[str, Optional[str]]:
barcode_data = frappe.db.get_value( barcode_data = frappe.db.get_value(
"Item Barcode", "Item Barcode",
{"barcode": search_value}, {"barcode": search_value},
["barcode", "parent as item_code"], ["barcode", "parent as item_code", "uom"],
as_dict=True, as_dict=True,
) )
if barcode_data: if barcode_data: