make erpnext.SerialNoBatchSelector
This commit is contained in:
parent
60d9446656
commit
70eca9462b
@ -30,6 +30,7 @@
|
||||
"public/js/payment/payment_details.html",
|
||||
"public/js/templates/item_selector.html",
|
||||
"public/js/utils/item_selector.js",
|
||||
"public/js/utils/serial_no_batch_selector.js",
|
||||
"public/js/help_links.js",
|
||||
"public/js/schools/student_button.html",
|
||||
"public/js/schools/assessment_result_tool.html"
|
||||
|
294
erpnext/public/js/utils/serial_no_batch_selector.js
Normal file
294
erpnext/public/js/utils/serial_no_batch_selector.js
Normal file
@ -0,0 +1,294 @@
|
||||
|
||||
erpnext.SerialNoBatchSelector = Class.extend({
|
||||
init: function(opts) {
|
||||
$.extend(this, opts);
|
||||
// frm, item, warehouse_details, has_batch, oldest
|
||||
this.setup();
|
||||
},
|
||||
|
||||
setup: function() {
|
||||
this.item_code = this.item.item_code;
|
||||
this.qty = this.item.qty;
|
||||
this.make_dialog();
|
||||
},
|
||||
|
||||
make_dialog: function() {
|
||||
var me = this;
|
||||
|
||||
this.data = this.oldest ? this.oldest : [];
|
||||
let title = "";
|
||||
let fields = [
|
||||
{fieldname: 'item_code', read_only: 1, fieldtype:'Link', options: 'Item',
|
||||
label: __('Item Code'), 'default': me.item_code},
|
||||
{fieldtype:'Column Break'},
|
||||
{
|
||||
fieldname: 'warehouse',
|
||||
fieldtype:'Link',
|
||||
options: 'Warehouse',
|
||||
label: __(me.warehouse_details.type),
|
||||
default: me.warehouse_details.name,
|
||||
onchange: function(e) {
|
||||
me.warehouse_details.name = this.get_value();
|
||||
var batches = this.layout.fields_dict.batches;
|
||||
if(batches) {
|
||||
batches.grid.df.data = [];
|
||||
batches.grid.refresh();
|
||||
batches.grid.add_new_row(null, null, null);
|
||||
}
|
||||
}
|
||||
},
|
||||
{fieldtype:'Column Break'},
|
||||
{fieldname: 'qty', fieldtype:'Float', label: __(me.has_batch ? 'Total Qty' : 'Qty'), 'default': me.qty},
|
||||
];
|
||||
|
||||
if(this.has_batch) {
|
||||
title = __("Select Batch Numbers");
|
||||
fields = fields.concat(this.get_batch_fields());
|
||||
} else {
|
||||
title = __("Select Serial Numbers");
|
||||
fields = fields.concat(this.get_serial_no_fields());
|
||||
}
|
||||
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
title: title,
|
||||
fields: fields
|
||||
});
|
||||
|
||||
this.bind_qty();
|
||||
|
||||
this.dialog.set_primary_action(__('Insert'), function() {
|
||||
me.values = me.dialog.get_values();
|
||||
if(!me.validate()) return;
|
||||
me.set_items();
|
||||
refresh_field("items");
|
||||
me.dialog.hide();
|
||||
});
|
||||
|
||||
this.dialog.show();
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
let values = this.values;
|
||||
if(!values.warehouse) {
|
||||
frappe.throw(__("Please select a warehouse"));
|
||||
return false;
|
||||
}
|
||||
if(this.has_batch) {
|
||||
if(values.batches.length === 0 || !values.batches) {
|
||||
frappe.throw(__("Please select batches for batched item "
|
||||
+ values.item_code));
|
||||
return false;
|
||||
}
|
||||
values.batches.map((batch, i) => {
|
||||
if(!batch.selected_qty || batch.selected_qty === 0 ) {
|
||||
frappe.throw(__("Please select quantity on row " + (i+1)));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
} else {
|
||||
let serial_nos = values.serial_no || '';
|
||||
if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
|
||||
frappe.throw(__("Please enter serial numbers for serialized item "
|
||||
+ values.item_code));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
set_items: function() {
|
||||
if(this.has_batch) {
|
||||
this.values.batches.map((batch, i) => {
|
||||
if(i === 0) {
|
||||
this.map_item_values(this.item, batch, 'batch_no',
|
||||
'selected_qty', this.values.warehouse);
|
||||
} else {
|
||||
let row = this.frm.add_child("items");
|
||||
row.item_code = this.item_code;
|
||||
this.map_item_values(row, batch, 'batch_no',
|
||||
'selected_qty', this.values.warehouse);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.map_item_values(this.item, this.values, 'serial_no', 'qty');
|
||||
}
|
||||
},
|
||||
|
||||
map_item_values: function(item, values, attribute, qty_field, warehouse) {
|
||||
item[attribute] = values[attribute];
|
||||
if(this.warehouse_details.type === 'Source Warehouse') {
|
||||
item.s_warehouse = values.warehouse || warehouse;
|
||||
} else {
|
||||
item.t_warehouse = values.warehouse || warehouse;
|
||||
}
|
||||
item.qty = values[qty_field];
|
||||
},
|
||||
|
||||
bind_qty: function() {
|
||||
let serial_no_link = this.dialog.fields_dict.serial_no_select;
|
||||
let serial_no_list_field = this.dialog.fields_dict.serial_no;
|
||||
let batches_field = this.dialog.fields_dict.batches;
|
||||
|
||||
let qty_field = this.dialog.fields_dict.qty;
|
||||
let item_code = this.item_code;
|
||||
|
||||
let update_quantity = (batch) => {
|
||||
if(batch) {
|
||||
let total_qty = 0;
|
||||
batches_field.grid.wrapper.find(
|
||||
'input[data-fieldname="selected_qty"]').each(function() {
|
||||
|
||||
total_qty += Number($(this).val());
|
||||
});
|
||||
qty_field.set_input(total_qty);
|
||||
} else {
|
||||
let serial_numbers = serial_no_list_field.get_value()
|
||||
.replace(/\n/g, ' ').match(/\S+/g) || [];
|
||||
qty_field.set_input(serial_numbers.length);
|
||||
}
|
||||
}
|
||||
|
||||
if(serial_no_link) {
|
||||
let serial_list = [];
|
||||
serial_no_link.$input.on('awesomplete-selectcomplete', function() {
|
||||
if(serial_no_link.get_value().length > 0) {
|
||||
let new_no = serial_no_link.get_value();
|
||||
let list_value = serial_no_list_field.get_value();
|
||||
let new_line = '\n';
|
||||
if(!serial_no_list_field.get_value()) {
|
||||
new_line = '';
|
||||
} else {
|
||||
serial_list = list_value.replace(/\s+/g, ' ').split(' ');
|
||||
}
|
||||
if(!serial_list.includes(new_no)) {
|
||||
serial_no_link.set_new_description('');
|
||||
serial_no_list_field.set_value(list_value + new_line + new_no);
|
||||
update_quantity(0);
|
||||
} else {
|
||||
serial_no_link.set_new_description(new_no + ' is already selected.');
|
||||
}
|
||||
}
|
||||
|
||||
// Should, but doesn't work
|
||||
serial_no_link.set_input('');
|
||||
serial_no_link.$input.blur();
|
||||
});
|
||||
|
||||
serial_no_list_field.$input.on('input', function() {
|
||||
serial_list = serial_no_list_field.get_value().replace(/\s+/g, ' ').split(' ');
|
||||
update_quantity(0);
|
||||
});
|
||||
}
|
||||
|
||||
if(batches_field) {
|
||||
batches_field.grid.wrapper.on('change', function() {
|
||||
update_quantity(1);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
get_batch_fields: function() {
|
||||
var me = this;
|
||||
return [
|
||||
{fieldtype:'Section Break', label: __('Batches')},
|
||||
{fieldname: 'batches', fieldtype: 'Table',
|
||||
fields: [
|
||||
{
|
||||
fieldtype:'Link',
|
||||
fieldname:'batch_no',
|
||||
options: 'Batch',
|
||||
label: __('Select Batch'),
|
||||
in_list_view:1,
|
||||
get_query: function() {
|
||||
return {filters: {item: me.item_code }};
|
||||
},
|
||||
onchange: function(e) {
|
||||
if(this.get_value().length === 0) {
|
||||
this.grid_row.on_grid_fields_dict
|
||||
.available_qty.set_value(0);
|
||||
return;
|
||||
}
|
||||
if(me.warehouse_details.name) {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
|
||||
args: {
|
||||
batch_no: this.doc.batch_no,
|
||||
warehouse: me.warehouse_details.name,
|
||||
item_code: me.item_code
|
||||
},
|
||||
callback: (r) => {
|
||||
this.grid_row.on_grid_fields_dict
|
||||
.available_qty.set_value(r.message || 0);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
frappe.throw(__(`Please select a warehouse to get available
|
||||
quantities`));
|
||||
}
|
||||
// e.stopImmediatePropagation();
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldtype:'Float',
|
||||
read_only:1,
|
||||
fieldname:'available_qty',
|
||||
label: __('Available'),
|
||||
in_list_view:1,
|
||||
default: 0,
|
||||
onchange: function() {
|
||||
this.grid_row.on_grid_fields_dict.selected_qty.set_value('0');
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldtype:'Float',
|
||||
fieldname:'selected_qty',
|
||||
label: __('Qty'),
|
||||
in_list_view:1,
|
||||
'default': 0,
|
||||
onchange: function(e) {
|
||||
var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value();
|
||||
var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value();
|
||||
var selected_qty = this.grid_row.on_grid_fields_dict.selected_qty.get_value();
|
||||
|
||||
if(batch_no.length === 0 && parseInt(selected_qty)!==0) {
|
||||
frappe.throw(__("Please select a batch"));
|
||||
}
|
||||
if(me.warehouse_details.type === 'Source Warehouse' &&
|
||||
parseFloat(available_qty) < parseFloat(selected_qty)) {
|
||||
this.set_value('0');
|
||||
frappe.throw(__(`For transfer from source, selected quantity cannot be
|
||||
greater than available quantity`));
|
||||
} else {
|
||||
this.grid.refresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
in_place_edit: true,
|
||||
data: this.data,
|
||||
get_data: function() {
|
||||
return this.data;
|
||||
},
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
get_serial_no_fields: function() {
|
||||
var me = this;
|
||||
return [
|
||||
{fieldtype: 'Section Break', label: __('Serial No')},
|
||||
{
|
||||
fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No',
|
||||
label: __('Select'),
|
||||
get_query: function() {
|
||||
return { filters: {item_code: me.item_code}};
|
||||
}
|
||||
},
|
||||
{fieldtype: 'Column Break'},
|
||||
{fieldname: 'serial_no', fieldtype: 'Small Text'}
|
||||
];
|
||||
}
|
||||
});
|
@ -546,8 +546,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.stock.select_batch_and_serial_no = (frm, d = undefined) => {
|
||||
let get_warehouse = (item) => {
|
||||
erpnext.stock.select_batch_and_serial_no = (frm, item) => {
|
||||
let get_warehouse_type_and_name = (item) => {
|
||||
let value = '';
|
||||
if(frm.fields_dict.from_warehouse.disp_status === "Write") {
|
||||
value = cstr(item.s_warehouse) || '';
|
||||
@ -564,275 +564,20 @@ erpnext.stock.select_batch_and_serial_no = (frm, d = undefined) => {
|
||||
}
|
||||
}
|
||||
|
||||
if(d && d.has_batch_no && !d.batch_no) {
|
||||
erpnext.stock.show_batch_serial_modal(frm, d, d.item_code, d.qty, get_warehouse(d), 1);
|
||||
} else if(d && d.has_serial_no && !d.serial_no && frm.doc.purpose !== 'Material Receipt') {
|
||||
erpnext.stock.show_batch_serial_modal(frm, d, d.item_code, d.qty, get_warehouse(d), 0);
|
||||
let opts = {
|
||||
frm: frm,
|
||||
item: item,
|
||||
warehouse_details: get_warehouse_type_and_name(item),
|
||||
}
|
||||
|
||||
if(item && item.has_batch_no && !item.batch_no) {
|
||||
opts.has_batch = 1;
|
||||
} else if(item && item.has_serial_no && !item.serial_no
|
||||
&& frm.doc.purpose !== 'Material Receipt') {
|
||||
|
||||
opts.has_batch = 0;
|
||||
}
|
||||
|
||||
let serial_no_batch_selector = new erpnext.SerialNoBatchSelector(opts);
|
||||
|
||||
}
|
||||
|
||||
erpnext.stock.show_batch_serial_modal = (frm, item, item_code, qty, warehouse_details,
|
||||
has_batch, oldest = undefined) => {
|
||||
|
||||
let data = oldest ? oldest : [];
|
||||
let title = "";
|
||||
let fields = [
|
||||
{fieldname: 'item_code', read_only: 1, fieldtype:'Link', options: 'Item',
|
||||
label: __('Item Code'), 'default': item_code},
|
||||
{fieldtype:'Column Break'},
|
||||
{
|
||||
fieldname: 'warehouse',
|
||||
fieldtype:'Link',
|
||||
options: 'Warehouse',
|
||||
label: __(warehouse_details.type),
|
||||
default: warehouse_details.name,
|
||||
onchange: function(e) {
|
||||
warehouse_details.name = this.get_value();
|
||||
var batches = this.layout.fields_dict.batches;
|
||||
if(batches) {
|
||||
batches.grid.df.data = [];
|
||||
batches.grid.refresh();
|
||||
batches.grid.add_new_row(null, null, null);
|
||||
}
|
||||
}
|
||||
},
|
||||
{fieldtype:'Column Break'},
|
||||
{fieldname: 'qty', fieldtype:'Float', label: __(has_batch ? 'Total Qty' : 'Qty'), 'default': qty},
|
||||
];
|
||||
|
||||
if(has_batch) {
|
||||
title = __("Select Batch Numbers");
|
||||
fields = fields.concat([
|
||||
{fieldtype:'Section Break', label: __('Batches')},
|
||||
{fieldname: 'batches', fieldtype: 'Table',
|
||||
fields: [
|
||||
{
|
||||
fieldtype:'Link',
|
||||
fieldname:'batch_no',
|
||||
options: 'Batch',
|
||||
label: __('Select Batch'),
|
||||
in_list_view:1,
|
||||
get_query: function() {
|
||||
return {filters: {item: item_code }};
|
||||
},
|
||||
onchange: function(e) {
|
||||
if(this.get_value().length === 0) {
|
||||
this.grid_row.on_grid_fields_dict
|
||||
.available_qty.set_value(0);
|
||||
return;
|
||||
}
|
||||
if(warehouse_details.name) {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
|
||||
args: {
|
||||
batch_no: this.doc.batch_no,
|
||||
warehouse: warehouse_details.name,
|
||||
item_code: item_code
|
||||
},
|
||||
callback: (r) => {
|
||||
this.grid_row.on_grid_fields_dict
|
||||
.available_qty.set_value(r.message || 0);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
frappe.throw(__("Please select a warehouse to get available quantities"));
|
||||
}
|
||||
// e.stopImmediatePropagation();
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldtype:'Float',
|
||||
read_only:1,
|
||||
fieldname:'available_qty',
|
||||
label: __('Available'),
|
||||
in_list_view:1,
|
||||
default: 0,
|
||||
onchange: function() {
|
||||
this.grid_row.on_grid_fields_dict.selected_qty.set_value('0');
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldtype:'Float',
|
||||
fieldname:'selected_qty',
|
||||
label: __('Qty'),
|
||||
in_list_view:1,
|
||||
'default': 0,
|
||||
onchange: function(e) {
|
||||
var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value();
|
||||
var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value();
|
||||
var selected_qty = this.grid_row.on_grid_fields_dict.selected_qty.get_value();
|
||||
|
||||
if(batch_no.length === 0 && parseInt(selected_qty)!==0) {
|
||||
frappe.throw(__("Please select a batch"));
|
||||
}
|
||||
if(warehouse_details.type === 'Source Warehouse' &&
|
||||
parseFloat(available_qty) < parseFloat(selected_qty)) {
|
||||
this.set_value('0');
|
||||
frappe.throw(__(`For transfer from source, selected quantity cannot be
|
||||
greater than available quantity`));
|
||||
} else {
|
||||
this.grid.refresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
in_place_edit: true,
|
||||
data: data,
|
||||
get_data: function() {
|
||||
return this.data;
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
||||
} else {
|
||||
title = __("Select Serial Numbers");
|
||||
fields = fields.concat([
|
||||
{fieldtype: 'Section Break', label: __('Serial No')},
|
||||
{
|
||||
fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No',
|
||||
label: __('Select'),
|
||||
get_query: function() {
|
||||
return { filters: {item_code: item_code}};
|
||||
}
|
||||
},
|
||||
{fieldtype: 'Column Break'},
|
||||
{fieldname: 'serial_no', fieldtype: 'Small Text'}
|
||||
]);
|
||||
}
|
||||
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: title,
|
||||
fields: fields
|
||||
});
|
||||
|
||||
erpnext.stock.bind_batch_serial_dialog_qty(dialog, warehouse_details);
|
||||
|
||||
let map_item_values = (item, values, attribute, qty_field, warehouse) => {
|
||||
item[attribute] = values[attribute];
|
||||
if(warehouse_details.type === 'Source Warehouse') {
|
||||
item.s_warehouse = values.warehouse || warehouse;
|
||||
} else {
|
||||
item.t_warehouse = values.warehouse || warehouse;
|
||||
}
|
||||
item.qty = values[qty_field];
|
||||
}
|
||||
|
||||
let validate_batch_dialog = (values) => {
|
||||
if(values.batches.length === 0 || !values.batches) {
|
||||
frappe.throw(__("Please select batches for batched item " + values.item_code));
|
||||
return false;
|
||||
}
|
||||
values.batches.map((batch, i) => {
|
||||
if(!batch.selected_qty || batch.selected_qty === 0 ) {
|
||||
frappe.throw(__("Please select quantity on row " + (i+1)));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
let set_batched_items = () => {
|
||||
let values = dialog.get_values();
|
||||
if(!validate_batch_dialog(values)) {
|
||||
return;
|
||||
}
|
||||
|
||||
values.batches.map((batch, i) => {
|
||||
if(i === 0) {
|
||||
map_item_values(item, batch, 'batch_no',
|
||||
'selected_qty', values.warehouse);
|
||||
} else {
|
||||
let row = frm.add_child("items");
|
||||
row.item_code = item.item_code;
|
||||
map_item_values(row, batch, 'batch_no',
|
||||
'selected_qty', values.warehouse);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let validate_serial_no_dialog = (values) => {
|
||||
let serial_nos = values.serial_no || '';
|
||||
if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
|
||||
frappe.throw(__("Please enter serial numbers for serialized item " + values.item_code));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let set_serialized_items = () => {
|
||||
let values = dialog.get_values();
|
||||
if (!validate_serial_no_dialog(values)) {
|
||||
return;
|
||||
}
|
||||
map_item_values(item, values, 'serial_no', 'qty');
|
||||
}
|
||||
|
||||
dialog.set_primary_action(__('Get Items'), function() {
|
||||
if(!dialog.get_values().warehouse) {
|
||||
frappe.throw(__("Please select a warehouse"));
|
||||
}
|
||||
has_batch ? set_batched_items() : set_serialized_items();
|
||||
refresh_field("items");
|
||||
dialog.hide();
|
||||
})
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
erpnext.stock.bind_batch_serial_dialog_qty = (dialog, warehouse_details) => {
|
||||
let serial_no_link = dialog.fields_dict.serial_no_select;
|
||||
let serial_no_list_field = dialog.fields_dict.serial_no;
|
||||
let batches_field = dialog.fields_dict.batches;
|
||||
|
||||
let qty_field = dialog.fields_dict.qty;
|
||||
let item_code = dialog.get_value('item_code');
|
||||
|
||||
let update_quantity = (batch) => {
|
||||
if(batch) {
|
||||
let total_qty = 0;
|
||||
batches_field.grid.wrapper.find('input[data-fieldname="selected_qty"]').each(function() {
|
||||
total_qty += Number($(this).val());
|
||||
});
|
||||
qty_field.set_input(total_qty);
|
||||
} else {
|
||||
let serial_numbers = serial_no_list_field.get_value().replace(/\n/g, ' ').match(/\S+/g) || [];
|
||||
qty_field.set_input(serial_numbers.length);
|
||||
}
|
||||
}
|
||||
|
||||
if(serial_no_link) {
|
||||
let serial_list = [];
|
||||
serial_no_link.$input.on('awesomplete-selectcomplete', function() {
|
||||
if(serial_no_link.get_value().length > 0) {
|
||||
let new_no = serial_no_link.get_value();
|
||||
let list_value = serial_no_list_field.get_value();
|
||||
let new_line = '\n';
|
||||
if(!serial_no_list_field.get_value()) {
|
||||
new_line = '';
|
||||
} else {
|
||||
serial_list = list_value.replace(/\s+/g, ' ').split(' ');
|
||||
}
|
||||
if(!serial_list.includes(new_no)) {
|
||||
serial_no_link.set_new_description('');
|
||||
serial_no_list_field.set_value(list_value + new_line + new_no);
|
||||
update_quantity(0);
|
||||
} else {
|
||||
serial_no_link.set_new_description(new_no + ' is already selected.');
|
||||
}
|
||||
}
|
||||
serial_no_link.set_input('');
|
||||
serial_no_link.$input.blur();
|
||||
});
|
||||
|
||||
serial_no_list_field.$input.on('input', function() {
|
||||
serial_list = serial_no_list_field.get_value().replace(/\s+/g, ' ').split(' ');
|
||||
update_quantity(0);
|
||||
});
|
||||
}
|
||||
|
||||
if(batches_field) {
|
||||
batches_field.grid.wrapper.on('change', function() {
|
||||
update_quantity(1);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user