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:
parent
2aa401826e
commit
55c2fec683
@ -74,9 +74,10 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
fieldname: 'qty',
|
||||
fieldtype:'Float',
|
||||
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),
|
||||
},
|
||||
...get_pending_qty_fields(me),
|
||||
{
|
||||
fieldname: 'uom',
|
||||
read_only: 1,
|
||||
@ -181,6 +182,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
|
||||
if (this.has_batch && !this.has_serial_no) {
|
||||
this.update_total_qty();
|
||||
this.update_pending_qtys();
|
||||
}
|
||||
|
||||
this.dialog.show();
|
||||
@ -321,7 +323,21 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
|
||||
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() {
|
||||
var me = this;
|
||||
|
||||
@ -423,6 +439,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
attach_bom_items(frm.doc.bom_no);
|
||||
},
|
||||
|
||||
setup_quality_inspection: function(frm) {
|
||||
@ -311,6 +312,7 @@ frappe.ui.form.on('Stock Entry', {
|
||||
}
|
||||
|
||||
frm.trigger("setup_quality_inspection");
|
||||
attach_bom_items(frm.doc.bom_no)
|
||||
},
|
||||
|
||||
stock_entry_type: function(frm){
|
||||
@ -919,6 +921,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
method: "get_items",
|
||||
callback: function(r) {
|
||||
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}));
|
||||
|
Loading…
Reference in New Issue
Block a user