mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 08:24:47 +00:00
feat: add pending qty section to batch/serial selector dialog (#25519)
* feat: add pending qty section to batch/serial selector dialog * fix: call attach in setup and refresh, fix conditional * refactor: camel to snake casing
This commit is contained in:
@@ -74,9 +74,10 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
fieldname: 'qty',
|
fieldname: 'qty',
|
||||||
fieldtype:'Float',
|
fieldtype:'Float',
|
||||||
read_only: me.has_batch && !me.has_serial_no,
|
read_only: me.has_batch && !me.has_serial_no,
|
||||||
label: __(me.has_batch && !me.has_serial_no ? 'Total Qty' : 'Qty'),
|
label: __(me.has_batch && !me.has_serial_no ? 'Selected Qty' : 'Qty'),
|
||||||
default: flt(me.item.stock_qty),
|
default: flt(me.item.stock_qty),
|
||||||
},
|
},
|
||||||
|
...get_pending_qty_fields(me),
|
||||||
{
|
{
|
||||||
fieldname: 'uom',
|
fieldname: 'uom',
|
||||||
read_only: 1,
|
read_only: 1,
|
||||||
@@ -181,6 +182,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
|
|
||||||
if (this.has_batch && !this.has_serial_no) {
|
if (this.has_batch && !this.has_serial_no) {
|
||||||
this.update_total_qty();
|
this.update_total_qty();
|
||||||
|
this.update_pending_qtys();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dialog.show();
|
this.dialog.show();
|
||||||
@@ -321,7 +323,21 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
|
|
||||||
qty_field.set_input(total_qty);
|
qty_field.set_input(total_qty);
|
||||||
},
|
},
|
||||||
|
update_pending_qtys: function() {
|
||||||
|
const pending_qty_field = this.dialog.fields_dict.pending_qty;
|
||||||
|
const total_selected_qty_field = this.dialog.fields_dict.total_selected_qty;
|
||||||
|
|
||||||
|
if (!pending_qty_field || !total_selected_qty_field) return;
|
||||||
|
|
||||||
|
const me = this;
|
||||||
|
const required_qty = this.dialog.fields_dict.required_qty.value;
|
||||||
|
const selected_qty = this.dialog.fields_dict.qty.value;
|
||||||
|
const total_selected_qty = selected_qty + calc_total_selected_qty(me);
|
||||||
|
const pending_qty = required_qty - total_selected_qty;
|
||||||
|
|
||||||
|
pending_qty_field.set_input(pending_qty);
|
||||||
|
total_selected_qty_field.set_input(total_selected_qty);
|
||||||
|
},
|
||||||
get_batch_fields: function() {
|
get_batch_fields: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
@@ -423,6 +439,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
me.update_total_qty();
|
me.update_total_qty();
|
||||||
|
me.update_pending_qtys();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -519,3 +536,60 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function get_pending_qty_fields(me) {
|
||||||
|
if (!check_can_calculate_pending_qty(me)) return [];
|
||||||
|
const { frm: { doc: { fg_completed_qty }}, item: { item_code, stock_qty }} = me;
|
||||||
|
const { qty_consumed_per_unit } = erpnext.stock.bom.items[item_code];
|
||||||
|
|
||||||
|
const total_selected_qty = calc_total_selected_qty(me);
|
||||||
|
const required_qty = flt(fg_completed_qty) * flt(qty_consumed_per_unit);
|
||||||
|
const pending_qty = required_qty - (flt(stock_qty) + total_selected_qty);
|
||||||
|
|
||||||
|
const pending_qty_fields = [
|
||||||
|
{ fieldtype: 'Section Break', label: __('Pending Quantity') },
|
||||||
|
{
|
||||||
|
fieldname: 'required_qty',
|
||||||
|
read_only: 1,
|
||||||
|
fieldtype: 'Float',
|
||||||
|
label: __('Required Qty'),
|
||||||
|
default: required_qty
|
||||||
|
},
|
||||||
|
{ fieldtype: 'Column Break' },
|
||||||
|
{
|
||||||
|
fieldname: 'total_selected_qty',
|
||||||
|
read_only: 1,
|
||||||
|
fieldtype: 'Float',
|
||||||
|
label: __('Total Selected Qty'),
|
||||||
|
default: total_selected_qty
|
||||||
|
},
|
||||||
|
{ fieldtype: 'Column Break' },
|
||||||
|
{
|
||||||
|
fieldname: 'pending_qty',
|
||||||
|
read_only: 1,
|
||||||
|
fieldtype: 'Float',
|
||||||
|
label: __('Pending Qty'),
|
||||||
|
default: pending_qty
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return pending_qty_fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calc_total_selected_qty(me) {
|
||||||
|
const { frm: { doc: { items }}, item: { name, item_code }} = me;
|
||||||
|
const totalSelectedQty = items
|
||||||
|
.filter( item => ( item.name !== name ) && ( item.item_code === item_code ) )
|
||||||
|
.map( item => flt(item.qty) )
|
||||||
|
.reduce( (i, j) => i + j, 0);
|
||||||
|
return totalSelectedQty;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_can_calculate_pending_qty(me) {
|
||||||
|
const { frm: { doc }, item } = me;
|
||||||
|
const docChecks = doc.bom_no
|
||||||
|
&& doc.fg_completed_qty
|
||||||
|
&& erpnext.stock.bom
|
||||||
|
&& erpnext.stock.bom.name === doc.bom_no;
|
||||||
|
const itemChecks = !!item;
|
||||||
|
return docChecks && itemChecks;
|
||||||
|
}
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
frappe.flags.hide_serial_batch_dialog = true;
|
frappe.flags.hide_serial_batch_dialog = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
attach_bom_items(frm.doc.bom_no);
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_quality_inspection: function(frm) {
|
setup_quality_inspection: function(frm) {
|
||||||
@@ -311,6 +312,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frm.trigger("setup_quality_inspection");
|
frm.trigger("setup_quality_inspection");
|
||||||
|
attach_bom_items(frm.doc.bom_no)
|
||||||
},
|
},
|
||||||
|
|
||||||
stock_entry_type: function(frm){
|
stock_entry_type: function(frm){
|
||||||
@@ -919,6 +921,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
method: "get_items",
|
method: "get_items",
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(!r.exc) refresh_field("items");
|
if(!r.exc) refresh_field("items");
|
||||||
|
if(me.frm.doc.bom_no) attach_bom_items(me.frm.doc.bom_no)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1064,4 +1067,22 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function attach_bom_items(bom_no) {
|
||||||
|
if (check_should_not_attach_bom_items(bom_no)) return
|
||||||
|
frappe.db.get_doc("BOM",bom_no).then(bom => {
|
||||||
|
const {name, items} = bom
|
||||||
|
erpnext.stock.bom = {name, items:{}}
|
||||||
|
items.forEach(item => {
|
||||||
|
erpnext.stock.bom.items[item.item_code] = item;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_should_not_attach_bom_items(bom_no) {
|
||||||
|
return (
|
||||||
|
bom_no === undefined ||
|
||||||
|
(erpnext.stock.bom && erpnext.stock.bom.name === bom_no)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, new erpnext.stock.StockEntry({frm: cur_frm}));
|
$.extend(cur_frm.cscript, new erpnext.stock.StockEntry({frm: cur_frm}));
|
||||||
|
|||||||
Reference in New Issue
Block a user