2017-06-22 10:19:42 +00:00
|
|
|
|
|
|
|
erpnext.SerialNoBatchSelector = Class.extend({
|
2017-08-28 11:49:28 +00:00
|
|
|
init: function(opts, show_dialog) {
|
2017-06-22 10:19:42 +00:00
|
|
|
$.extend(this, opts);
|
2017-08-28 11:49:28 +00:00
|
|
|
this.show_dialog = show_dialog;
|
2017-06-22 10:19:42 +00:00
|
|
|
// frm, item, warehouse_details, has_batch, oldest
|
2017-06-23 06:09:03 +00:00
|
|
|
let d = this.item;
|
|
|
|
|
|
|
|
// Don't show dialog if batch no or serial no already set
|
2017-08-28 11:49:28 +00:00
|
|
|
if(d && d.has_batch_no && (!d.batch_no || this.show_dialog)) {
|
2017-06-23 06:09:03 +00:00
|
|
|
this.has_batch = 1;
|
|
|
|
this.setup();
|
2017-08-28 11:49:28 +00:00
|
|
|
} else if(d && d.has_serial_no && (!d.serial_no || this.show_dialog)) {
|
2017-06-23 06:09:03 +00:00
|
|
|
this.has_batch = 0;
|
|
|
|
this.setup();
|
|
|
|
}
|
2017-06-22 10:19:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
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 = [
|
2017-06-26 10:01:46 +00:00
|
|
|
{
|
|
|
|
fieldname: 'item_code',
|
|
|
|
read_only: 1,
|
|
|
|
fieldtype:'Link',
|
|
|
|
options: 'Item',
|
|
|
|
label: __('Item Code'),
|
|
|
|
default: me.item_code
|
|
|
|
},
|
2017-06-22 10:19:42 +00:00
|
|
|
{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);
|
|
|
|
}
|
2017-07-12 08:40:43 +00:00
|
|
|
},
|
|
|
|
get_query: function() {
|
|
|
|
return {
|
|
|
|
filters: {
|
|
|
|
is_group: 0,
|
|
|
|
company: me.frm.doc.company
|
|
|
|
}
|
|
|
|
};
|
2017-06-22 10:19:42 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{fieldtype:'Column Break'},
|
2017-06-26 10:01:46 +00:00
|
|
|
{
|
|
|
|
fieldname: 'qty',
|
|
|
|
fieldtype:'Float',
|
|
|
|
read_only: 1,
|
|
|
|
label: __(me.has_batch ? 'Total Qty' : 'Qty'),
|
2017-07-17 16:03:04 +00:00
|
|
|
default: 0
|
2017-06-26 10:01:46 +00:00
|
|
|
},
|
2017-06-22 10:19:42 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
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();
|
2017-06-22 10:24:18 +00:00
|
|
|
if(me.validate()) {
|
|
|
|
me.set_items();
|
|
|
|
me.dialog.hide();
|
|
|
|
}
|
2017-06-22 10:19:42 +00:00
|
|
|
});
|
|
|
|
|
2017-08-28 11:49:28 +00:00
|
|
|
if(this.show_dialog) {
|
|
|
|
let d = this.item;
|
|
|
|
this.dialog.set_value('serial_no', d.serial_no);
|
|
|
|
}
|
|
|
|
|
2017-06-22 10:19:42 +00:00
|
|
|
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() {
|
2017-06-27 04:18:28 +00:00
|
|
|
var me = this;
|
2017-06-22 10:19:42 +00:00
|
|
|
if(this.has_batch) {
|
|
|
|
this.values.batches.map((batch, i) => {
|
2017-06-27 04:18:28 +00:00
|
|
|
let item_code_field = {};
|
2017-06-27 08:33:56 +00:00
|
|
|
let row = (i !== 0) ? this.frm.add_child("items", this.item) : this.item;
|
|
|
|
this.map_row_values(row, batch, 'batch_no',
|
2017-06-23 06:09:03 +00:00
|
|
|
'selected_qty', this.values.warehouse);
|
2017-06-22 10:19:42 +00:00
|
|
|
});
|
|
|
|
} else {
|
2017-06-26 08:08:24 +00:00
|
|
|
this.map_row_values(this.item, this.values, 'serial_no', 'qty');
|
2017-06-22 10:19:42 +00:00
|
|
|
}
|
2017-06-26 08:08:24 +00:00
|
|
|
refresh_field("items");
|
2017-08-30 13:26:26 +00:00
|
|
|
this.callback && this.callback(this.item);
|
2017-06-22 10:19:42 +00:00
|
|
|
},
|
|
|
|
|
2017-06-27 08:33:56 +00:00
|
|
|
map_row_values: function(row, values, number, qty_field, warehouse) {
|
2017-06-23 06:09:03 +00:00
|
|
|
row.qty = values[qty_field];
|
2017-06-26 08:08:24 +00:00
|
|
|
row[number] = values[number];
|
2017-06-22 10:19:42 +00:00
|
|
|
if(this.warehouse_details.type === 'Source Warehouse') {
|
2017-06-23 06:09:03 +00:00
|
|
|
row.s_warehouse = values.warehouse || warehouse;
|
2017-06-22 10:24:18 +00:00
|
|
|
} else if(this.warehouse_details.type === 'Target Warehouse') {
|
2017-06-23 06:09:03 +00:00
|
|
|
row.t_warehouse = values.warehouse || warehouse;
|
2017-06-22 10:24:18 +00:00
|
|
|
} else {
|
2017-06-23 06:09:03 +00:00
|
|
|
row.warehouse = values.warehouse || warehouse;
|
|
|
|
}
|
2017-06-22 10:19:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
bind_qty: function() {
|
|
|
|
let batches_field = this.dialog.fields_dict.batches;
|
|
|
|
let qty_field = this.dialog.fields_dict.qty;
|
2017-07-16 16:34:16 +00:00
|
|
|
if(batches_field) {
|
|
|
|
batches_field.grid.wrapper.on('change', function() {
|
2017-06-22 10:19:42 +00:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
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) {
|
2017-06-26 10:01:46 +00:00
|
|
|
let val = this.get_value();
|
|
|
|
if(val.length === 0) {
|
2017-06-22 10:19:42 +00:00
|
|
|
this.grid_row.on_grid_fields_dict
|
|
|
|
.available_qty.set_value(0);
|
|
|
|
return;
|
|
|
|
}
|
2017-06-26 10:01:46 +00:00
|
|
|
let selected_batches = this.grid.grid_rows.map((row) => {
|
|
|
|
if(row === this.grid_row) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return row.on_grid_fields_dict.batch_no.get_value();
|
|
|
|
});
|
|
|
|
if(selected_batches.includes(val)) {
|
|
|
|
this.set_value("");
|
|
|
|
frappe.throw(__(`Batch ${val} already selected.`));
|
|
|
|
return;
|
|
|
|
}
|
2017-06-22 10:19:42 +00:00
|
|
|
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 {
|
2017-06-26 10:01:46 +00:00
|
|
|
this.set_value("");
|
2017-06-22 10:19:42 +00:00
|
|
|
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)) {
|
2017-06-26 12:15:49 +00:00
|
|
|
|
|
|
|
this.set_value('0');
|
|
|
|
frappe.throw(__(`For transfer from source, selected quantity cannot be
|
|
|
|
greater than available quantity`));
|
2017-06-22 10:19:42 +00:00
|
|
|
} 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;
|
2017-07-16 16:34:16 +00:00
|
|
|
this.serial_list = [];
|
2017-06-22 10:19:42 +00:00
|
|
|
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}};
|
2017-07-16 16:34:16 +00:00
|
|
|
},
|
|
|
|
onchange: function(e) {
|
|
|
|
if(this.in_local_change) return;
|
|
|
|
this.in_local_change = 1;
|
|
|
|
|
|
|
|
let serial_no_list_field = this.layout.fields_dict.serial_no;
|
|
|
|
let qty_field = this.layout.fields_dict.qty;
|
|
|
|
|
|
|
|
let new_number = this.get_value();
|
|
|
|
let list_value = serial_no_list_field.get_value();
|
|
|
|
let new_line = '\n';
|
|
|
|
if(!list_value) {
|
|
|
|
new_line = '';
|
|
|
|
} else {
|
|
|
|
me.serial_list = list_value.replace(/\n/g, ' ').match(/\S+/g) || [];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!me.serial_list.includes(new_number)) {
|
|
|
|
this.set_new_description('');
|
|
|
|
serial_no_list_field.set_value(me.serial_list.join('\n') + new_line + new_number);
|
|
|
|
me.serial_list = serial_no_list_field.get_value().replace(/\n/g, ' ').match(/\S+/g) || [];
|
|
|
|
} else {
|
|
|
|
this.set_new_description(new_number + ' is already selected.');
|
|
|
|
}
|
|
|
|
|
|
|
|
qty_field.set_input(me.serial_list.length);
|
|
|
|
this.$input.val("");
|
|
|
|
this.in_local_change = 0;
|
2017-06-22 10:19:42 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{fieldtype: 'Column Break'},
|
2017-07-16 16:34:16 +00:00
|
|
|
{
|
|
|
|
fieldname: 'serial_no',
|
|
|
|
fieldtype: 'Small Text',
|
|
|
|
onchange: function() {
|
|
|
|
me.serial_list = this.get_value()
|
|
|
|
.replace(/\n/g, ' ').match(/\S+/g) || [];
|
|
|
|
this.layout.fields_dict.qty.set_input(me.serial_list.length);
|
|
|
|
}
|
|
|
|
}
|
2017-06-22 10:19:42 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
});
|