Merge pull request #39496 from frappe/mergify/bp/version-15-hotfix/pr-39478

fix: UX improvements for Serial and Batch Bundle (backport #39478)
This commit is contained in:
rohitwaghchaure 2024-01-23 13:18:16 +05:30 committed by GitHub
commit 1331fb75a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 196 additions and 42 deletions

View File

@ -105,11 +105,27 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
this.frm.has_items = false;
}
if (serial_no && this.is_duplicate_serial_no(row, item_code, serial_no)) {
if (serial_no) {
this.is_duplicate_serial_no(row, item_code, serial_no)
.then((is_duplicate) => {
if (!is_duplicate) {
this.run_serially_tasks(row, data, resolve);
} else {
this.clean_up();
reject();
return;
}
});
} else {
this.run_serially_tasks(row, data, resolve);
}
});
}
run_serially_tasks(row, data, resolve) {
const {item_code, barcode, batch_no, serial_no, uom} = data;
frappe.run_serially([
() => this.set_serial_and_batch(row, item_code, serial_no, batch_no),
@ -119,16 +135,15 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
}),
() => this.set_barcode_uom(row, uom),
() => this.clean_up(),
() => resolve(row),
() => {
if (row.serial_and_batch_bundle && !this.frm.is_new()) {
this.frm.save();
}
frappe.flags.trigger_from_barcode_scanner = false;
}
},
() => resolve(row),
]);
});
}
set_item(row, item_code, barcode, batch_no, serial_no) {
@ -475,26 +490,32 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
}
}
is_duplicate_serial_no(row, item_code, serial_no) {
async is_duplicate_serial_no(row, item_code, serial_no) {
let is_duplicate = false;
const promise = new Promise((resolve, reject) => {
if (this.frm.is_new() || !row.serial_and_batch_bundle) {
let is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
if (is_duplicate) {
this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
}
return is_duplicate;
resolve(is_duplicate);
} else if (row.serial_and_batch_bundle) {
this.check_duplicate_serial_no_in_db(row, serial_no, (r) => {
if (r.message) {
this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
}
return r.message;
is_duplicate = r.message;
resolve(is_duplicate);
})
}
});
return await promise;
}
async check_duplicate_serial_no_in_db(row, serial_no, response) {
check_duplicate_serial_no_in_db(row, serial_no, response) {
frappe.call({
method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_duplicate_serial_no",
args: {
@ -504,7 +525,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
callback(r) {
response(r);
}
})
});
}
check_duplicate_serial_no_in_localstorage(item_code, serial_no) {

View File

@ -135,7 +135,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
filters: this.get_serial_no_filters()
};
},
onchange: () => this.update_serial_batch_no()
onchange: () => this.scan_barcode_data()
});
}
@ -145,7 +145,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
options: 'Barcode',
fieldname: 'scan_batch_no',
label: __('Scan Batch No'),
onchange: () => this.update_serial_batch_no()
onchange: () => this.scan_barcode_data()
});
}
@ -179,11 +179,54 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
label = __('Serial Nos / Batch Nos');
}
return [
let fields = [
{
fieldtype: 'Section Break',
label: __('{0} {1} via CSV File', [primary_label, label])
}
]
if (this.item?.has_serial_no) {
fields = [...fields,
{
fieldtype: 'Check',
label: __('Import Using CSV file'),
fieldname: 'import_using_csv_file',
default: 0,
},
{
fieldtype: 'Section Break',
label: __('{0} {1} Manually', [primary_label, label]),
depends_on: 'eval:doc.import_using_csv_file === 0',
},
{
fieldtype: 'Small Text',
label: __('Enter Serial Nos'),
fieldname: 'upload_serial_nos',
depends_on: 'eval:doc.import_using_csv_file === 0',
description: __('Enter each serial no in a new line'),
},
{
fieldtype: 'Column Break',
depends_on: 'eval:doc.import_using_csv_file === 0',
},
{
fieldtype: 'Button',
fieldname: 'make_serial_nos',
label: __('Create Serial Nos'),
depends_on: 'eval:doc.import_using_csv_file === 0',
click: () => {
this.create_serial_nos();
}
},
{
fieldtype: 'Section Break',
depends_on: 'eval:doc.import_using_csv_file === 1',
}
];
}
fields = [...fields,
{
fieldtype: 'Button',
fieldname: 'download_csv',
@ -199,7 +242,32 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
label: __('Attach CSV File'),
onchange: () => this.upload_csv_file()
}
]
];
return fields;
}
create_serial_nos() {
let {upload_serial_nos} = this.dialog.get_values();
if (!upload_serial_nos) {
frappe.throw(__('Please enter Serial Nos'));
}
frappe.call({
method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.create_serial_nos',
args: {
item_code: this.item.item_code,
serial_nos: upload_serial_nos
},
callback: (r) => {
if (r.message) {
this.dialog.fields_dict.entries.df.data = [];
this.set_data(r.message);
this.update_bundle_entries();
}
}
});
}
download_csv_file() {
@ -374,6 +442,26 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
}
}
scan_barcode_data() {
const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
if (scan_serial_no || scan_batch_no) {
frappe.call({
method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_serial_batch_no_exists',
args: {
item_code: this.item.item_code,
type_of_transaction: this.item.type_of_transaction,
serial_no: scan_serial_no,
batch_no: scan_batch_no,
},
callback: (r) => {
this.update_serial_batch_no();
}
})
}
}
update_serial_batch_no() {
const { scan_serial_no, scan_batch_no } = this.dialog.get_values();

View File

@ -74,7 +74,7 @@ frappe.ui.form.on('Serial and Batch Bundle', {
let fields = [
{
"label": __("Using CSV File"),
"label": __("Import Using CSV file"),
"fieldname": "using_csv_file",
"default": 1,
"fieldtype": "Check",

View File

@ -999,9 +999,25 @@ def get_serial_batch_from_data(item_code, kwargs):
make_serial_nos(item_code, serial_nos)
if kwargs.get("_has_serial_nos"):
return serial_nos
return serial_nos, batch_nos
@frappe.whitelist()
def create_serial_nos(item_code, serial_nos):
serial_nos = get_serial_batch_from_data(
item_code,
{
"serial_nos": serial_nos,
"_has_serial_nos": True,
},
)
return serial_nos
def make_serial_nos(item_code, serial_nos):
item = frappe.get_cached_value("Item", item_code, ["description", "item_code"], as_dict=1)
@ -2079,6 +2095,35 @@ def get_batch_no_from_serial_no(serial_no):
return frappe.get_cached_value("Serial No", serial_no, "batch_no")
@frappe.whitelist()
def is_serial_batch_no_exists(item_code, type_of_transaction, serial_no=None, batch_no=None):
if serial_no and not frappe.db.exists("Serial No", serial_no):
if type_of_transaction != "Inward":
frappe.throw(_("Serial No {0} does not exists").format(serial_no))
make_serial_no(serial_no, item_code)
if batch_no and frappe.db.exists("Batch", batch_no):
if type_of_transaction != "Inward":
frappe.throw(_("Batch No {0} does not exists").format(batch_no))
make_batch_no(batch_no, item_code)
def make_serial_no(serial_no, item_code):
serial_no_doc = frappe.new_doc("Serial No")
serial_no_doc.serial_no = serial_no
serial_no_doc.item_code = item_code
serial_no_doc.save(ignore_permissions=True)
def make_batch_no(batch_no, item_code):
batch_doc = frappe.new_doc("Batch")
batch_doc.batch_id = batch_no
batch_doc.item = item_code
batch_doc.save(ignore_permissions=True)
@frappe.whitelist()
def is_duplicate_serial_no(bundle_id, serial_no):
return frappe.db.exists("Serial and Batch Entry", {"parent": bundle_id, "serial_no": serial_no})