Merge branch 'develop' of https://github.com/frappe/erpnext into dev_fr_translation
This commit is contained in:
commit
d1c69aa229
@ -154,6 +154,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.set_dynamic_labels(frm);
|
frm.events.set_dynamic_labels(frm);
|
||||||
frm.events.show_general_ledger(frm);
|
frm.events.show_general_ledger(frm);
|
||||||
erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm);
|
erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm);
|
||||||
|
if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) {
|
||||||
|
frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() {
|
||||||
|
frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name});
|
||||||
|
}, __('Actions'));
|
||||||
|
|
||||||
|
}
|
||||||
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
|
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.validate_future_closing_vouchers()
|
self.validate_future_closing_vouchers()
|
||||||
self.db_set("gle_processing_status", "In Progress")
|
self.db_set("gle_processing_status", "In Progress")
|
||||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
|
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||||
gle_count = frappe.db.count(
|
gle_count = frappe.db.count(
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
|
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
|
||||||
|
|||||||
@ -1801,6 +1801,10 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
||||||
|
"""Test impact of advance PE submission/cancellation on SI and SO."""
|
||||||
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
|
|
||||||
|
sales_order = make_sales_order(item_code="138-CMS Shoe", qty=1, price_list_rate=500)
|
||||||
pe = frappe.get_doc(
|
pe = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Payment Entry",
|
"doctype": "Payment Entry",
|
||||||
@ -1820,10 +1824,25 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"paid_to": "_Test Cash - _TC",
|
"paid_to": "_Test Cash - _TC",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
pe.append(
|
||||||
|
"references",
|
||||||
|
{
|
||||||
|
"reference_doctype": "Sales Order",
|
||||||
|
"reference_name": sales_order.name,
|
||||||
|
"total_amount": sales_order.grand_total,
|
||||||
|
"outstanding_amount": sales_order.grand_total,
|
||||||
|
"allocated_amount": 300,
|
||||||
|
},
|
||||||
|
)
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
|
sales_order.reload()
|
||||||
|
self.assertEqual(sales_order.advance_paid, 300)
|
||||||
|
|
||||||
si = frappe.copy_doc(test_records[0])
|
si = frappe.copy_doc(test_records[0])
|
||||||
|
si.items[0].sales_order = sales_order.name
|
||||||
|
si.items[0].so_detail = sales_order.get("items")[0].name
|
||||||
si.is_pos = 0
|
si.is_pos = 0
|
||||||
si.append(
|
si.append(
|
||||||
"advances",
|
"advances",
|
||||||
@ -1831,6 +1850,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"doctype": "Sales Invoice Advance",
|
"doctype": "Sales Invoice Advance",
|
||||||
"reference_type": "Payment Entry",
|
"reference_type": "Payment Entry",
|
||||||
"reference_name": pe.name,
|
"reference_name": pe.name,
|
||||||
|
"reference_row": pe.references[0].name,
|
||||||
"advance_amount": 300,
|
"advance_amount": 300,
|
||||||
"allocated_amount": 300,
|
"allocated_amount": 300,
|
||||||
"remarks": pe.remarks,
|
"remarks": pe.remarks,
|
||||||
@ -1839,7 +1859,13 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.insert()
|
si.insert()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
si.load_from_db()
|
si.reload()
|
||||||
|
pe.reload()
|
||||||
|
sales_order.reload()
|
||||||
|
|
||||||
|
# Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0
|
||||||
|
self.assertEqual(pe.references[0].reference_name, si.name)
|
||||||
|
self.assertEqual(sales_order.advance_paid, 0.0)
|
||||||
|
|
||||||
# check outstanding after advance allocation
|
# check outstanding after advance allocation
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1847,11 +1873,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
|
flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# added to avoid Document has been modified exception
|
|
||||||
pe = frappe.get_doc("Payment Entry", pe.name)
|
|
||||||
pe.cancel()
|
pe.cancel()
|
||||||
|
si.reload()
|
||||||
|
|
||||||
si.load_from_db()
|
|
||||||
# check outstanding after advance cancellation
|
# check outstanding after advance cancellation
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
flt(si.outstanding_amount),
|
flt(si.outstanding_amount),
|
||||||
|
|||||||
@ -581,6 +581,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
|||||||
"""
|
"""
|
||||||
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||||
|
|
||||||
|
# Update Advance Paid in SO/PO since they might be getting unlinked
|
||||||
|
if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
|
||||||
|
frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
|
||||||
|
|
||||||
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
|
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
|
||||||
# adjust the unreconciled balance
|
# adjust the unreconciled balance
|
||||||
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
|
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
|
||||||
@ -647,6 +651,13 @@ def update_reference_in_payment_entry(
|
|||||||
|
|
||||||
if d.voucher_detail_no:
|
if d.voucher_detail_no:
|
||||||
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
||||||
|
|
||||||
|
# Update Advance Paid in SO/PO since they are getting unlinked
|
||||||
|
if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"):
|
||||||
|
frappe.get_doc(
|
||||||
|
existing_row.reference_doctype, existing_row.reference_name
|
||||||
|
).set_total_advance_paid()
|
||||||
|
|
||||||
original_row = existing_row.as_dict().copy()
|
original_row = existing_row.as_dict().copy()
|
||||||
existing_row.update(reference_details)
|
existing_row.update(reference_details)
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ erpnext.accounts.unreconcile_payments = {
|
|||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
frm.add_custom_button(__("Un-Reconcile"), function() {
|
frm.add_custom_button(__("UnReconcile"), function() {
|
||||||
erpnext.accounts.unreconcile_payments.build_unreconcile_dialog(frm);
|
erpnext.accounts.unreconcile_payments.build_unreconcile_dialog(frm);
|
||||||
}, __('Actions'));
|
}, __('Actions'));
|
||||||
}
|
}
|
||||||
@ -87,11 +87,11 @@ erpnext.accounts.unreconcile_payments = {
|
|||||||
unreconcile_dialog_fields[0].get_data = function(){ return r.message};
|
unreconcile_dialog_fields[0].get_data = function(){ return r.message};
|
||||||
|
|
||||||
let d = new frappe.ui.Dialog({
|
let d = new frappe.ui.Dialog({
|
||||||
title: 'Un-Reconcile Allocations',
|
title: 'UnReconcile Allocations',
|
||||||
fields: unreconcile_dialog_fields,
|
fields: unreconcile_dialog_fields,
|
||||||
size: 'large',
|
size: 'large',
|
||||||
cannot_add_rows: true,
|
cannot_add_rows: true,
|
||||||
primary_action_label: 'Un-Reconcile',
|
primary_action_label: 'UnReconcile',
|
||||||
primary_action(values) {
|
primary_action(values) {
|
||||||
|
|
||||||
let selected_allocations = values.allocations.filter(x=>x.__checked);
|
let selected_allocations = values.allocations.filter(x=>x.__checked);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from frappe.model.db_query import DatabaseQuery
|
|||||||
from frappe.utils import cint, flt
|
from frappe.utils import cint, flt
|
||||||
|
|
||||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||||
get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock,
|
get_sre_reserved_qty_for_items_and_warehouses as get_reserved_stock_details,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +61,10 @@ def get_data(
|
|||||||
limit_page_length=21,
|
limit_page_length=21,
|
||||||
)
|
)
|
||||||
|
|
||||||
sre_reserved_stock_details = get_reserved_stock(item_code, warehouse)
|
item_code_list = [item_code] if item_code else [i.item_code for i in items]
|
||||||
|
warehouse_list = [warehouse] if warehouse else [i.warehouse for i in items]
|
||||||
|
|
||||||
|
sre_reserved_stock_details = get_reserved_stock_details(item_code_list, warehouse_list)
|
||||||
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
|
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
@ -75,7 +78,8 @@ def get_data(
|
|||||||
"reserved_qty_for_production": flt(item.reserved_qty_for_production, precision),
|
"reserved_qty_for_production": flt(item.reserved_qty_for_production, precision),
|
||||||
"reserved_qty_for_sub_contract": flt(item.reserved_qty_for_sub_contract, precision),
|
"reserved_qty_for_sub_contract": flt(item.reserved_qty_for_sub_contract, precision),
|
||||||
"actual_qty": flt(item.actual_qty, precision),
|
"actual_qty": flt(item.actual_qty, precision),
|
||||||
"reserved_stock": sre_reserved_stock_details,
|
"reserved_stock": flt(sre_reserved_stock_details.get((item.item_code, item.warehouse))),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|||||||
@ -346,7 +346,7 @@ class StockReconciliation(StockController):
|
|||||||
"""Raises an exception if there is any reserved stock for the items in the Stock Reconciliation."""
|
"""Raises an exception if there is any reserved stock for the items in the Stock Reconciliation."""
|
||||||
|
|
||||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||||
get_sre_reserved_qty_for_item_and_warehouse as get_sre_reserved_qty_details,
|
get_sre_reserved_qty_for_items_and_warehouses as get_sre_reserved_qty_details,
|
||||||
)
|
)
|
||||||
|
|
||||||
item_code_list, warehouse_list = [], []
|
item_code_list, warehouse_list = [], []
|
||||||
|
|||||||
@ -1,42 +1,42 @@
|
|||||||
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Stock Reservation Entry", {
|
frappe.ui.form.on('Stock Reservation Entry', {
|
||||||
refresh(frm) {
|
refresh(frm) {
|
||||||
frm.trigger("set_queries");
|
frm.trigger('set_queries');
|
||||||
frm.trigger("toggle_read_only_fields");
|
frm.trigger('toggle_read_only_fields');
|
||||||
frm.trigger("hide_rate_related_fields");
|
frm.trigger('hide_rate_related_fields');
|
||||||
frm.trigger("hide_primary_action_button");
|
frm.trigger('hide_primary_action_button');
|
||||||
frm.trigger("make_sb_entries_warehouse_read_only");
|
frm.trigger('make_sb_entries_warehouse_read_only');
|
||||||
},
|
},
|
||||||
|
|
||||||
has_serial_no(frm) {
|
has_serial_no(frm) {
|
||||||
frm.trigger("toggle_read_only_fields");
|
frm.trigger('toggle_read_only_fields');
|
||||||
},
|
},
|
||||||
|
|
||||||
has_batch_no(frm) {
|
has_batch_no(frm) {
|
||||||
frm.trigger("toggle_read_only_fields");
|
frm.trigger('toggle_read_only_fields');
|
||||||
},
|
},
|
||||||
|
|
||||||
warehouse(frm) {
|
warehouse(frm) {
|
||||||
if (frm.doc.warehouse) {
|
if (frm.doc.warehouse) {
|
||||||
frm.doc.sb_entries.forEach((row) => {
|
frm.doc.sb_entries.forEach((row) => {
|
||||||
frappe.model.set_value(row.doctype, row.name, "warehouse", frm.doc.warehouse);
|
frappe.model.set_value(row.doctype, row.name, 'warehouse', frm.doc.warehouse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
set_queries(frm) {
|
set_queries(frm) {
|
||||||
frm.set_query("warehouse", () => {
|
frm.set_query('warehouse', () => {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"is_group": 0,
|
'is_group': 0,
|
||||||
"company": frm.doc.company,
|
'company': frm.doc.company,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("serial_no", "sb_entries", function(doc, cdt, cdn) {
|
frm.set_query('serial_no', 'sb_entries', function(doc, cdt, cdn) {
|
||||||
var selected_serial_nos = doc.sb_entries.map(row => {
|
var selected_serial_nos = doc.sb_entries.map(row => {
|
||||||
return row.serial_no;
|
return row.serial_no;
|
||||||
});
|
});
|
||||||
@ -45,16 +45,16 @@ frappe.ui.form.on("Stock Reservation Entry", {
|
|||||||
filters: {
|
filters: {
|
||||||
item_code: doc.item_code,
|
item_code: doc.item_code,
|
||||||
warehouse: row.warehouse,
|
warehouse: row.warehouse,
|
||||||
status: "Active",
|
status: 'Active',
|
||||||
name: ["not in", selected_serial_nos],
|
name: ['not in', selected_serial_nos],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("batch_no", "sb_entries", function(doc, cdt, cdn) {
|
frm.set_query('batch_no', 'sb_entries', function(doc, cdt, cdn) {
|
||||||
let filters = {
|
let filters = {
|
||||||
item: doc.item_code,
|
item: doc.item_code,
|
||||||
batch_qty: [">", 0],
|
batch_qty: ['>', 0],
|
||||||
disabled: 0,
|
disabled: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ frappe.ui.form.on("Stock Reservation Entry", {
|
|||||||
return row.batch_no;
|
return row.batch_no;
|
||||||
});
|
});
|
||||||
|
|
||||||
filters.name = ["not in", selected_batch_nos];
|
filters.name = ['not in', selected_batch_nos];
|
||||||
}
|
}
|
||||||
|
|
||||||
return { filters: filters }
|
return { filters: filters }
|
||||||
@ -74,37 +74,37 @@ frappe.ui.form.on("Stock Reservation Entry", {
|
|||||||
if (frm.doc.has_serial_no) {
|
if (frm.doc.has_serial_no) {
|
||||||
frm.doc.sb_entries.forEach(row => {
|
frm.doc.sb_entries.forEach(row => {
|
||||||
if (row.qty !== 1) {
|
if (row.qty !== 1) {
|
||||||
frappe.model.set_value(row.doctype, row.name, "qty", 1);
|
frappe.model.set_value(row.doctype, row.name, 'qty', 1);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
||||||
"serial_no", "read_only", !frm.doc.has_serial_no
|
'serial_no', 'read_only', !frm.doc.has_serial_no
|
||||||
);
|
);
|
||||||
|
|
||||||
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
||||||
"batch_no", "read_only", !frm.doc.has_batch_no
|
'batch_no', 'read_only', !frm.doc.has_batch_no
|
||||||
);
|
);
|
||||||
|
|
||||||
// Qty will always be 1 for Serial No.
|
// Qty will always be 1 for Serial No.
|
||||||
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
||||||
"qty", "read_only", frm.doc.has_serial_no
|
'qty', 'read_only', frm.doc.has_serial_no
|
||||||
);
|
);
|
||||||
|
|
||||||
frm.set_df_property("sb_entries", "allow_on_submit", frm.doc.against_pick_list ? 0 : 1);
|
frm.set_df_property('sb_entries', 'allow_on_submit', frm.doc.against_pick_list ? 0 : 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_rate_related_fields(frm) {
|
hide_rate_related_fields(frm) {
|
||||||
["incoming_rate", "outgoing_rate", "stock_value_difference", "is_outward", "stock_queue"].forEach(field => {
|
['incoming_rate', 'outgoing_rate', 'stock_value_difference', 'is_outward', 'stock_queue'].forEach(field => {
|
||||||
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
||||||
field, "hidden", 1
|
field, 'hidden', 1
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
hide_primary_action_button(frm) {
|
hide_primary_action_button(frm) {
|
||||||
// Hide "Amend" button on cancelled document
|
// Hide 'Amend' button on cancelled document
|
||||||
if (frm.doc.docstatus == 2) {
|
if (frm.doc.docstatus == 2) {
|
||||||
frm.page.btn_primary.hide()
|
frm.page.btn_primary.hide()
|
||||||
}
|
}
|
||||||
@ -112,15 +112,15 @@ frappe.ui.form.on("Stock Reservation Entry", {
|
|||||||
|
|
||||||
make_sb_entries_warehouse_read_only(frm) {
|
make_sb_entries_warehouse_read_only(frm) {
|
||||||
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
frm.fields_dict.sb_entries.grid.update_docfield_property(
|
||||||
"warehouse", "read_only", 1
|
'warehouse', 'read_only', 1
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Serial and Batch Entry", {
|
frappe.ui.form.on('Serial and Batch Entry', {
|
||||||
sb_entries_add(frm, cdt, cdn) {
|
sb_entries_add(frm, cdt, cdn) {
|
||||||
if (frm.doc.warehouse) {
|
if (frm.doc.warehouse) {
|
||||||
frappe.model.set_value(cdt, cdn, "warehouse", frm.doc.warehouse);
|
frappe.model.set_value(cdt, cdn, 'warehouse', frm.doc.warehouse);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -14,7 +14,7 @@ class StockReservationEntry(Document):
|
|||||||
|
|
||||||
self.validate_amended_doc()
|
self.validate_amended_doc()
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
self.validate_for_group_warehouse()
|
self.validate_group_warehouse()
|
||||||
validate_disabled_warehouse(self.warehouse)
|
validate_disabled_warehouse(self.warehouse)
|
||||||
validate_warehouse_company(self.warehouse, self.company)
|
validate_warehouse_company(self.warehouse, self.company)
|
||||||
self.validate_uom_is_integer()
|
self.validate_uom_is_integer()
|
||||||
@ -74,7 +74,7 @@ class StockReservationEntry(Document):
|
|||||||
msg = _("{0} is required").format(self.meta.get_label(d))
|
msg = _("{0} is required").format(self.meta.get_label(d))
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
def validate_for_group_warehouse(self) -> None:
|
def validate_group_warehouse(self) -> None:
|
||||||
"""Raises an exception if `Warehouse` is a Group Warehouse."""
|
"""Raises an exception if `Warehouse` is a Group Warehouse."""
|
||||||
|
|
||||||
if frappe.get_cached_value("Warehouse", self.warehouse, "is_group"):
|
if frappe.get_cached_value("Warehouse", self.warehouse, "is_group"):
|
||||||
@ -544,10 +544,36 @@ def get_available_serial_nos_to_reserve(
|
|||||||
return available_serial_nos_list
|
return available_serial_nos_list
|
||||||
|
|
||||||
|
|
||||||
def get_sre_reserved_qty_for_item_and_warehouse(
|
def get_sre_reserved_qty_for_item_and_warehouse(item_code: str, warehouse: str = None) -> float:
|
||||||
item_code: str | list, warehouse: str | list = None
|
"""Returns current `Reserved Qty` for Item and Warehouse combination."""
|
||||||
) -> float | dict:
|
|
||||||
"""Returns `Reserved Qty` for Item and Warehouse combination OR a dict like {("item_code", "warehouse"): "reserved_qty", ... }."""
|
sre = frappe.qb.DocType("Stock Reservation Entry")
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(sre)
|
||||||
|
.select(Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_qty"))
|
||||||
|
.where(
|
||||||
|
(sre.docstatus == 1)
|
||||||
|
& (sre.item_code == item_code)
|
||||||
|
& (sre.status.notin(["Delivered", "Cancelled"]))
|
||||||
|
)
|
||||||
|
.groupby(sre.item_code, sre.warehouse)
|
||||||
|
)
|
||||||
|
|
||||||
|
if warehouse:
|
||||||
|
query = query.where(sre.warehouse == warehouse)
|
||||||
|
|
||||||
|
reserved_qty = query.run(as_list=True)
|
||||||
|
|
||||||
|
return flt(reserved_qty[0][0]) if reserved_qty else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def get_sre_reserved_qty_for_items_and_warehouses(
|
||||||
|
item_code_list: list, warehouse_list: list = None
|
||||||
|
) -> dict:
|
||||||
|
"""Returns a dict like {("item_code", "warehouse"): "reserved_qty", ... }."""
|
||||||
|
|
||||||
|
if not item_code_list:
|
||||||
|
return {}
|
||||||
|
|
||||||
sre = frappe.qb.DocType("Stock Reservation Entry")
|
sre = frappe.qb.DocType("Stock Reservation Entry")
|
||||||
query = (
|
query = (
|
||||||
@ -557,28 +583,19 @@ def get_sre_reserved_qty_for_item_and_warehouse(
|
|||||||
sre.warehouse,
|
sre.warehouse,
|
||||||
Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_qty"),
|
Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_qty"),
|
||||||
)
|
)
|
||||||
.where((sre.docstatus == 1) & (sre.status.notin(["Delivered", "Cancelled"])))
|
.where(
|
||||||
|
(sre.docstatus == 1)
|
||||||
|
& sre.item_code.isin(item_code_list)
|
||||||
|
& (sre.status.notin(["Delivered", "Cancelled"]))
|
||||||
|
)
|
||||||
.groupby(sre.item_code, sre.warehouse)
|
.groupby(sre.item_code, sre.warehouse)
|
||||||
)
|
)
|
||||||
|
|
||||||
query = (
|
if warehouse_list:
|
||||||
query.where(sre.item_code.isin(item_code))
|
query = query.where(sre.warehouse.isin(warehouse_list))
|
||||||
if isinstance(item_code, list)
|
|
||||||
else query.where(sre.item_code == item_code)
|
|
||||||
)
|
|
||||||
|
|
||||||
if warehouse:
|
|
||||||
query = (
|
|
||||||
query.where(sre.warehouse.isin(warehouse))
|
|
||||||
if isinstance(warehouse, list)
|
|
||||||
else query.where(sre.warehouse == warehouse)
|
|
||||||
)
|
|
||||||
|
|
||||||
data = query.run(as_dict=True)
|
data = query.run(as_dict=True)
|
||||||
|
|
||||||
if isinstance(item_code, str) and isinstance(warehouse, str):
|
|
||||||
return data[0]["reserved_qty"] if data else 0.0
|
|
||||||
else:
|
|
||||||
return {(d["item_code"], d["warehouse"]): d["reserved_qty"] for d in data} if data else {}
|
return {(d["item_code"], d["warehouse"]): d["reserved_qty"] for d in data} if data else {}
|
||||||
|
|
||||||
|
|
||||||
@ -711,7 +728,7 @@ def get_serial_batch_entries_for_voucher(sre_name: str) -> list[dict]:
|
|||||||
).run(as_dict=True)
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
def get_ssb_bundle_for_voucher(sre: dict) -> object | None:
|
def get_ssb_bundle_for_voucher(sre: dict) -> object:
|
||||||
"""Returns a new `Serial and Batch Bundle` against the provided SRE."""
|
"""Returns a new `Serial and Batch Bundle` against the provided SRE."""
|
||||||
|
|
||||||
sb_entries = get_serial_batch_entries_for_voucher(sre["name"])
|
sb_entries = get_serial_batch_entries_for_voucher(sre["name"])
|
||||||
|
|||||||
@ -11,6 +11,7 @@ frappe.listview_settings['Stock Reservation Entry'] = {
|
|||||||
'Delivered': 'green',
|
'Delivered': 'green',
|
||||||
'Cancelled': 'red',
|
'Cancelled': 'red',
|
||||||
};
|
};
|
||||||
|
|
||||||
return [__(doc.status), status_colors[doc.status], 'status,=,' + doc.status];
|
return [__(doc.status), status_colors[doc.status], 'status,=,' + doc.status];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -165,7 +165,7 @@ class StockBalanceReport(object):
|
|||||||
|
|
||||||
def get_sre_reserved_qty_details(self) -> dict:
|
def get_sre_reserved_qty_details(self) -> dict:
|
||||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||||
get_sre_reserved_qty_for_item_and_warehouse as get_reserved_qty_details,
|
get_sre_reserved_qty_for_items_and_warehouses as get_reserved_qty_details,
|
||||||
)
|
)
|
||||||
|
|
||||||
item_code_list, warehouse_list = [], []
|
item_code_list, warehouse_list = [], []
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user