refactor: serial no ledger and batchwise balance history report
This commit is contained in:
parent
ba6e1447ef
commit
467046436b
@ -2573,7 +2573,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"posting_date": si.posting_date,
|
"posting_date": si.posting_date,
|
||||||
"posting_time": si.posting_time,
|
"posting_time": si.posting_time,
|
||||||
"qty": -1 * flt(d.get("stock_qty")),
|
"qty": -1 * flt(d.get("stock_qty")),
|
||||||
"serial_no": d.serial_no,
|
"serial_and_batch_bundle": d.serial_and_batch_bundle,
|
||||||
"company": si.company,
|
"company": si.company,
|
||||||
"voucher_type": "Sales Invoice",
|
"voucher_type": "Sales Invoice",
|
||||||
"voucher_no": si.name,
|
"voucher_no": si.name,
|
||||||
|
|||||||
@ -323,8 +323,6 @@ def get_returned_qty_map_for_row(return_against, party, row_name, doctype):
|
|||||||
def make_return_doc(doctype: str, source_name: str, target_doc=None):
|
def make_return_doc(doctype: str, source_name: str, target_doc=None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
|
||||||
|
|
||||||
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
||||||
default_warehouse_for_sales_return = frappe.get_cached_value(
|
default_warehouse_for_sales_return = frappe.get_cached_value(
|
||||||
"Company", company, "default_warehouse_for_sales_return"
|
"Company", company, "default_warehouse_for_sales_return"
|
||||||
@ -392,23 +390,51 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
|
|||||||
doc.run_method("calculate_taxes_and_totals")
|
doc.run_method("calculate_taxes_and_totals")
|
||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
def update_item(source_doc, target_doc, source_parent):
|
||||||
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
|
|
||||||
target_doc.qty = -1 * source_doc.qty
|
target_doc.qty = -1 * source_doc.qty
|
||||||
|
|
||||||
if source_doc.serial_no:
|
if source_doc.get("serial_and_batch_bundle"):
|
||||||
returned_serial_nos = get_returned_serial_nos(source_doc, source_parent)
|
type_of_transaction = "Inward"
|
||||||
serial_nos = list(set(get_serial_nos(source_doc.serial_no)) - set(returned_serial_nos))
|
if (
|
||||||
if serial_nos:
|
frappe.db.get_value(
|
||||||
target_doc.serial_no = "\n".join(serial_nos)
|
"Serial and Batch Bundle", source_doc.serial_and_batch_bundle, "type_of_transaction"
|
||||||
|
)
|
||||||
|
== "Inward"
|
||||||
|
):
|
||||||
|
type_of_transaction = "Outward"
|
||||||
|
|
||||||
if source_doc.get("rejected_serial_no"):
|
cls_obj = SerialBatchCreation(
|
||||||
returned_serial_nos = get_returned_serial_nos(
|
{
|
||||||
source_doc, source_parent, serial_no_field="rejected_serial_no"
|
"type_of_transaction": type_of_transaction,
|
||||||
|
"serial_and_batch_bundle": source_doc.serial_and_batch_bundle,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
rejected_serial_nos = list(
|
|
||||||
set(get_serial_nos(source_doc.rejected_serial_no)) - set(returned_serial_nos)
|
cls_obj.duplicate_package()
|
||||||
|
if cls_obj.serial_and_batch_bundle:
|
||||||
|
target_doc.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
|
||||||
|
|
||||||
|
if source_doc.get("rejected_serial_and_batch_bundle"):
|
||||||
|
type_of_transaction = "Inward"
|
||||||
|
if (
|
||||||
|
frappe.db.get_value(
|
||||||
|
"Serial and Batch Bundle", source_doc.rejected_serial_and_batch_bundle, "type_of_transaction"
|
||||||
)
|
)
|
||||||
if rejected_serial_nos:
|
== "Inward"
|
||||||
target_doc.rejected_serial_no = "\n".join(rejected_serial_nos)
|
):
|
||||||
|
type_of_transaction = "Outward"
|
||||||
|
|
||||||
|
cls_obj = SerialBatchCreation(
|
||||||
|
{
|
||||||
|
"type_of_transaction": type_of_transaction,
|
||||||
|
"serial_and_batch_bundle": source_doc.rejected_serial_and_batch_bundle,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
cls_obj.duplicate_package()
|
||||||
|
if cls_obj.serial_and_batch_bundle:
|
||||||
|
target_doc.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
|
||||||
|
|
||||||
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
|
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
|
||||||
returned_qty_map = get_returned_qty_map_for_row(
|
returned_qty_map = get_returned_qty_map_for_row(
|
||||||
|
|||||||
@ -294,13 +294,13 @@ class SubcontractingController(StockController):
|
|||||||
for batch_no, qty in consumed_bundles.batch_nos.items():
|
for batch_no, qty in consumed_bundles.batch_nos.items():
|
||||||
self.available_materials[key]["batch_no"][batch_no] -= abs(qty)
|
self.available_materials[key]["batch_no"][batch_no] -= abs(qty)
|
||||||
|
|
||||||
# Will be deperecated in v16
|
# Will be deprecated in v16
|
||||||
if row.serial_no:
|
if row.serial_no:
|
||||||
self.available_materials[key]["serial_no"] = list(
|
self.available_materials[key]["serial_no"] = list(
|
||||||
set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
|
set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Will be deperecated in v16
|
# Will be deprecated in v16
|
||||||
if row.batch_no:
|
if row.batch_no:
|
||||||
self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
|
self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
|
||||||
|
|
||||||
@ -814,8 +814,7 @@ class SubcontractingController(StockController):
|
|||||||
"posting_date": self.posting_date,
|
"posting_date": self.posting_date,
|
||||||
"posting_time": self.posting_time,
|
"posting_time": self.posting_time,
|
||||||
"qty": -1 * item.consumed_qty,
|
"qty": -1 * item.consumed_qty,
|
||||||
"serial_no": item.serial_no,
|
"serial_and_batch_bundle": item.serial_and_batch_bundle,
|
||||||
"batch_no": item.batch_no,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from frappe.utils import flt
|
|||||||
|
|
||||||
|
|
||||||
class DeprecatedSerialNoValuation:
|
class DeprecatedSerialNoValuation:
|
||||||
# Will be deperecated in v16
|
# Will be deprecated in v16
|
||||||
|
|
||||||
def calculate_stock_value_from_deprecarated_ledgers(self):
|
def calculate_stock_value_from_deprecarated_ledgers(self):
|
||||||
serial_nos = list(
|
serial_nos = list(
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from frappe.query_builder.functions import CombineDatetime, Sum
|
|||||||
from frappe.utils import add_days, cint, flt, get_link_to_form, today
|
from frappe.utils import add_days, cint, flt, get_link_to_form, today
|
||||||
from pypika import Case
|
from pypika import Case
|
||||||
|
|
||||||
from erpnext.stock.serial_batch_bundle import BatchNoBundleValuation, SerialNoBundleValuation
|
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
||||||
|
|
||||||
|
|
||||||
class SerialNoExistsInFutureTransactionError(frappe.ValidationError):
|
class SerialNoExistsInFutureTransactionError(frappe.ValidationError):
|
||||||
@ -81,14 +81,14 @@ class SerialandBatchBundle(Document):
|
|||||||
def set_incoming_rate_for_outward_transaction(self, row=None, save=False):
|
def set_incoming_rate_for_outward_transaction(self, row=None, save=False):
|
||||||
sle = self.get_sle_for_outward_transaction(row)
|
sle = self.get_sle_for_outward_transaction(row)
|
||||||
if self.has_serial_no:
|
if self.has_serial_no:
|
||||||
sn_obj = SerialNoBundleValuation(
|
sn_obj = SerialNoValuation(
|
||||||
sle=sle,
|
sle=sle,
|
||||||
warehouse=self.item_code,
|
warehouse=self.item_code,
|
||||||
item_code=self.warehouse,
|
item_code=self.warehouse,
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
sn_obj = BatchNoBundleValuation(
|
sn_obj = BatchNoValuation(
|
||||||
sle=sle,
|
sle=sle,
|
||||||
warehouse=self.item_code,
|
warehouse=self.item_code,
|
||||||
item_code=self.warehouse,
|
item_code=self.warehouse,
|
||||||
@ -187,9 +187,12 @@ class SerialandBatchBundle(Document):
|
|||||||
self.set_incoming_rate(save=True, row=row)
|
self.set_incoming_rate(save=True, row=row)
|
||||||
self.calculate_qty_and_amount(save=True)
|
self.calculate_qty_and_amount(save=True)
|
||||||
self.validate_quantity(row)
|
self.validate_quantity(row)
|
||||||
self.set_warranty_expiry_date(row)
|
self.set_warranty_expiry_date()
|
||||||
|
|
||||||
def set_warranty_expiry_date(self):
|
def set_warranty_expiry_date(self):
|
||||||
|
if self.type_of_transaction != "Outward":
|
||||||
|
return
|
||||||
|
|
||||||
if not (self.docstatus == 1 and self.voucher_type == "Delivery Note" and self.has_serial_no):
|
if not (self.docstatus == 1 and self.voucher_type == "Delivery Note" and self.has_serial_no):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@ -493,8 +493,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
'item_code': child.item_code,
|
'item_code': child.item_code,
|
||||||
'warehouse': cstr(child.s_warehouse) || cstr(child.t_warehouse),
|
'warehouse': cstr(child.s_warehouse) || cstr(child.t_warehouse),
|
||||||
'transfer_qty': child.transfer_qty,
|
'transfer_qty': child.transfer_qty,
|
||||||
'serial_no': child.serial_no,
|
'serial_and_batch_bundle': child.serial_and_batch_bundle,
|
||||||
'batch_no': child.batch_no,
|
|
||||||
'qty': child.s_warehouse ? -1* child.transfer_qty : child.transfer_qty,
|
'qty': child.s_warehouse ? -1* child.transfer_qty : child.transfer_qty,
|
||||||
'posting_date': frm.doc.posting_date,
|
'posting_date': frm.doc.posting_date,
|
||||||
'posting_time': frm.doc.posting_time,
|
'posting_time': frm.doc.posting_time,
|
||||||
|
|||||||
@ -92,6 +92,16 @@ class StockLedgerEntry(Document):
|
|||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
values_to_be_change = {}
|
||||||
|
if self.has_batch_no != item_detail.has_batch_no:
|
||||||
|
values_to_be_change["has_batch_no"] = item_detail.has_batch_no
|
||||||
|
|
||||||
|
if self.has_serial_no != item_detail.has_serial_no:
|
||||||
|
values_to_be_change["has_serial_no"] = item_detail.has_serial_no
|
||||||
|
|
||||||
|
if values_to_be_change:
|
||||||
|
self.db_set(values_to_be_change)
|
||||||
|
|
||||||
if not item_detail:
|
if not item_detail:
|
||||||
frappe.throw(_("Item {0} not found").format(self.item_code))
|
frappe.throw(_("Item {0} not found").format(self.item_code))
|
||||||
|
|
||||||
|
|||||||
@ -157,7 +157,9 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
|||||||
item_code=serial_item_code, warehouse=serial_warehouse, qty=5, rate=200
|
item_code=serial_item_code, warehouse=serial_warehouse, qty=5, rate=200
|
||||||
)
|
)
|
||||||
|
|
||||||
serial_nos = get_serial_nos(sr.items[0].serial_no)
|
serial_nos = frappe.get_doc(
|
||||||
|
"Serial and Batch Bundle", sr.items[0].serial_and_batch_bundle
|
||||||
|
).get_serial_nos()
|
||||||
self.assertEqual(len(serial_nos), 5)
|
self.assertEqual(len(serial_nos), 5)
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
@ -165,7 +167,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
|||||||
"warehouse": serial_warehouse,
|
"warehouse": serial_warehouse,
|
||||||
"posting_date": nowdate(),
|
"posting_date": nowdate(),
|
||||||
"posting_time": nowtime(),
|
"posting_time": nowtime(),
|
||||||
"serial_no": sr.items[0].serial_no,
|
"serial_and_batch_bundle": sr.items[0].serial_and_batch_bundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
valuation_rate = get_incoming_rate(args)
|
valuation_rate = get_incoming_rate(args)
|
||||||
@ -177,7 +179,10 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
|||||||
item_code=serial_item_code, warehouse=serial_warehouse, qty=5, rate=300
|
item_code=serial_item_code, warehouse=serial_warehouse, qty=5, rate=300
|
||||||
)
|
)
|
||||||
|
|
||||||
serial_nos1 = get_serial_nos(sr.items[0].serial_no)
|
serial_nos1 = frappe.get_doc(
|
||||||
|
"Serial and Batch Bundle", sr.items[0].serial_and_batch_bundle
|
||||||
|
).get_serial_nos()
|
||||||
|
|
||||||
self.assertEqual(len(serial_nos1), 5)
|
self.assertEqual(len(serial_nos1), 5)
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
@ -185,7 +190,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
|||||||
"warehouse": serial_warehouse,
|
"warehouse": serial_warehouse,
|
||||||
"posting_date": nowdate(),
|
"posting_date": nowdate(),
|
||||||
"posting_time": nowtime(),
|
"posting_time": nowtime(),
|
||||||
"serial_no": sr.items[0].serial_no,
|
"serial_and_batch_bundle": sr.items[0].serial_and_batch_bundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
valuation_rate = get_incoming_rate(args)
|
valuation_rate = get_incoming_rate(args)
|
||||||
@ -257,7 +262,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
|||||||
sr.save()
|
sr.save()
|
||||||
sr.submit()
|
sr.submit()
|
||||||
|
|
||||||
batch_no = sr.items[0].batch_no
|
batch_no = sr.items[0].serial_and_batch_bundle
|
||||||
self.assertTrue(batch_no)
|
self.assertTrue(batch_no)
|
||||||
to_delete_records.append(sr.name)
|
to_delete_records.append(sr.name)
|
||||||
|
|
||||||
|
|||||||
@ -67,8 +67,16 @@ def get_columns(filters):
|
|||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
|
||||||
# get all details
|
|
||||||
def get_stock_ledger_entries(filters):
|
def get_stock_ledger_entries(filters):
|
||||||
|
# Will be deprecated in v16
|
||||||
|
entries = get_stock_ledger_entries_for_batch_no(filters)
|
||||||
|
|
||||||
|
entries += get_stock_ledger_entries_for_batch_bundle(filters)
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
# get all details
|
||||||
|
def get_stock_ledger_entries_for_batch_no(filters):
|
||||||
if not filters.get("from_date"):
|
if not filters.get("from_date"):
|
||||||
frappe.throw(_("'From Date' is required"))
|
frappe.throw(_("'From Date' is required"))
|
||||||
if not filters.get("to_date"):
|
if not filters.get("to_date"):
|
||||||
@ -99,7 +107,43 @@ def get_stock_ledger_entries(filters):
|
|||||||
if filters.get(field):
|
if filters.get(field):
|
||||||
query = query.where(sle[field] == filters.get(field))
|
query = query.where(sle[field] == filters.get(field))
|
||||||
|
|
||||||
return query.run(as_dict=True)
|
return query.run(as_dict=True) or []
|
||||||
|
|
||||||
|
|
||||||
|
def get_stock_ledger_entries_for_batch_bundle(filters):
|
||||||
|
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
batch_package = frappe.qb.DocType("Serial and Batch Entry")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(sle)
|
||||||
|
.inner_join(batch_package)
|
||||||
|
.on(batch_package.parent == sle.serial_and_batch_bundle)
|
||||||
|
.select(
|
||||||
|
sle.item_code,
|
||||||
|
sle.warehouse,
|
||||||
|
batch_package.batch_no,
|
||||||
|
sle.posting_date,
|
||||||
|
fn.Sum(batch_package.qty).as_("actual_qty"),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(sle.docstatus < 2)
|
||||||
|
& (sle.is_cancelled == 0)
|
||||||
|
& (sle.has_batch_no == 1)
|
||||||
|
& (sle.posting_date <= filters["to_date"])
|
||||||
|
)
|
||||||
|
.groupby(sle.voucher_no, sle.batch_no, sle.item_code, sle.warehouse)
|
||||||
|
.orderby(sle.item_code, sle.warehouse)
|
||||||
|
)
|
||||||
|
|
||||||
|
query = apply_warehouse_filter(query, sle, filters)
|
||||||
|
for field in ["item_code", "batch_no", "company"]:
|
||||||
|
if filters.get(field):
|
||||||
|
if field == "batch_no":
|
||||||
|
query = query.where(batch_package[field] == filters.get(field))
|
||||||
|
else:
|
||||||
|
query = query.where(sle[field] == filters.get(field))
|
||||||
|
|
||||||
|
return query.run(as_dict=True) or []
|
||||||
|
|
||||||
|
|
||||||
def get_item_warehouse_batch_map(filters, float_precision):
|
def get_item_warehouse_batch_map(filters, float_precision):
|
||||||
|
|||||||
@ -18,13 +18,6 @@ frappe.query_reports["Serial No Ledger"] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'label': __('Serial No'),
|
|
||||||
'fieldtype': 'Link',
|
|
||||||
'fieldname': 'serial_no',
|
|
||||||
'options': 'Serial No',
|
|
||||||
'reqd': 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'label': __('Warehouse'),
|
'label': __('Warehouse'),
|
||||||
'fieldtype': 'Link',
|
'fieldtype': 'Link',
|
||||||
@ -42,11 +35,36 @@ frappe.query_reports["Serial No Ledger"] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'label': __('Serial No'),
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'fieldname': 'serial_no',
|
||||||
|
'options': 'Serial No',
|
||||||
|
get_query: function() {
|
||||||
|
let item_code = frappe.query_report.get_filter_value('item_code');
|
||||||
|
let warehouse = frappe.query_report.get_filter_value('warehouse');
|
||||||
|
|
||||||
|
let query_filters = {'item_code': item_code};
|
||||||
|
if (warehouse) {
|
||||||
|
query_filters['warehouse'] = warehouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: query_filters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'label': __('As On Date'),
|
'label': __('As On Date'),
|
||||||
'fieldtype': 'Date',
|
'fieldtype': 'Date',
|
||||||
'fieldname': 'posting_date',
|
'fieldname': 'posting_date',
|
||||||
'default': frappe.datetime.get_today()
|
'default': frappe.datetime.get_today()
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'label': __('Posting Time'),
|
||||||
|
'fieldtype': 'Time',
|
||||||
|
'fieldname': 'posting_time',
|
||||||
|
'default': frappe.datetime.get_time()
|
||||||
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
from erpnext.stock.stock_ledger import get_stock_ledger_entries
|
from erpnext.stock.stock_ledger import get_stock_ledger_entries
|
||||||
@ -45,10 +45,71 @@ def get_columns(filters):
|
|||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"width": 220,
|
"width": 220,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": _("Serial No"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"fieldname": "serial_no",
|
||||||
|
"options": "Serial No",
|
||||||
|
"width": 220,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
return get_stock_ledger_entries(filters, "<=", order="asc") or []
|
stock_ledgers = get_stock_ledger_entries(filters, "<=", order="asc", check_serial_no=False)
|
||||||
|
|
||||||
|
if not stock_ledgers:
|
||||||
|
return []
|
||||||
|
|
||||||
|
data = []
|
||||||
|
serial_bundle_ids = [
|
||||||
|
d.serial_and_batch_bundle for d in stock_ledgers if d.serial_and_batch_bundle
|
||||||
|
]
|
||||||
|
|
||||||
|
bundle_wise_serial_nos = get_serial_nos(filters, serial_bundle_ids)
|
||||||
|
|
||||||
|
for row in stock_ledgers:
|
||||||
|
args = frappe._dict(
|
||||||
|
{
|
||||||
|
"posting_date": row.posting_date,
|
||||||
|
"posting_time": row.posting_time,
|
||||||
|
"voucher_type": row.voucher_type,
|
||||||
|
"voucher_no": row.voucher_no,
|
||||||
|
"company": row.company,
|
||||||
|
"warehouse": row.warehouse,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
serial_nos = bundle_wise_serial_nos.get(row.serial_and_batch_bundle, [])
|
||||||
|
|
||||||
|
for index, serial_no in enumerate(serial_nos):
|
||||||
|
if index == 0:
|
||||||
|
args.serial_no = serial_no
|
||||||
|
data.append(args)
|
||||||
|
else:
|
||||||
|
data.append(
|
||||||
|
{
|
||||||
|
"serial_no": serial_no,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_serial_nos(filters, serial_bundle_ids):
|
||||||
|
bundle_wise_serial_nos = {}
|
||||||
|
bundle_filters = {"parent": ["in", serial_bundle_ids]}
|
||||||
|
if filters.get("serial_no"):
|
||||||
|
bundle_filters["serial_no"] = filters.get("serial_no")
|
||||||
|
|
||||||
|
for d in frappe.get_all(
|
||||||
|
"Serial and Batch Entry",
|
||||||
|
fields=["serial_no", "parent"],
|
||||||
|
filters=bundle_filters,
|
||||||
|
order_by="idx asc",
|
||||||
|
):
|
||||||
|
bundle_wise_serial_nos.setdefault(d.parent, []).append(d.serial_no)
|
||||||
|
|
||||||
|
return bundle_wise_serial_nos
|
||||||
|
|||||||
@ -341,7 +341,7 @@ def get_serial_nos(serial_and_batch_bundle, check_outward=True):
|
|||||||
return [d.serial_no for d in entries]
|
return [d.serial_no for d in entries]
|
||||||
|
|
||||||
|
|
||||||
class SerialNoBundleValuation(DeprecatedSerialNoValuation):
|
class SerialNoValuation(DeprecatedSerialNoValuation):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
@ -470,7 +470,7 @@ def is_rejected(voucher_type, voucher_detail_no, warehouse):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class BatchNoBundleValuation(DeprecatedBatchNoValuation):
|
class BatchNoValuation(DeprecatedBatchNoValuation):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
@ -567,11 +567,11 @@ class BatchNoBundleValuation(DeprecatedBatchNoValuation):
|
|||||||
|
|
||||||
|
|
||||||
def get_empty_batches_based_work_order(work_order, item_code):
|
def get_empty_batches_based_work_order(work_order, item_code):
|
||||||
batches = get_batches_from_work_order(work_order)
|
batches = get_batches_from_work_order(work_order, item_code)
|
||||||
if not batches:
|
if not batches:
|
||||||
return batches
|
return batches
|
||||||
|
|
||||||
entries = get_batches_from_stock_entries(work_order)
|
entries = get_batches_from_stock_entries(work_order, item_code)
|
||||||
if not entries:
|
if not entries:
|
||||||
return batches
|
return batches
|
||||||
|
|
||||||
@ -589,15 +589,18 @@ def get_empty_batches_based_work_order(work_order, item_code):
|
|||||||
return batches
|
return batches
|
||||||
|
|
||||||
|
|
||||||
def get_batches_from_work_order(work_order):
|
def get_batches_from_work_order(work_order, item_code):
|
||||||
return frappe._dict(
|
return frappe._dict(
|
||||||
frappe.get_all(
|
frappe.get_all(
|
||||||
"Batch", fields=["name", "qty_to_produce"], filters={"reference_name": work_order}, as_list=1
|
"Batch",
|
||||||
|
fields=["name", "qty_to_produce"],
|
||||||
|
filters={"reference_name": work_order, "item": item_code},
|
||||||
|
as_list=1,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_batches_from_stock_entries(work_order):
|
def get_batches_from_stock_entries(work_order, item_code):
|
||||||
entries = frappe.get_all(
|
entries = frappe.get_all(
|
||||||
"Stock Entry",
|
"Stock Entry",
|
||||||
filters={"work_order": work_order, "docstatus": 1, "purpose": "Manufacture"},
|
filters={"work_order": work_order, "docstatus": 1, "purpose": "Manufacture"},
|
||||||
@ -610,6 +613,7 @@ def get_batches_from_stock_entries(work_order):
|
|||||||
filters={
|
filters={
|
||||||
"parent": ("in", [d.name for d in entries]),
|
"parent": ("in", [d.name for d in entries]),
|
||||||
"is_finished_item": 1,
|
"is_finished_item": 1,
|
||||||
|
"item_code": item_code,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -623,3 +627,21 @@ def set_batch_details_from_package(ids, batches):
|
|||||||
|
|
||||||
for d in entries:
|
for d in entries:
|
||||||
batches[d.batch_no] -= d.qty
|
batches[d.batch_no] -= d.qty
|
||||||
|
|
||||||
|
|
||||||
|
class SerialBatchCreation:
|
||||||
|
def __init__(self, args):
|
||||||
|
for key, value in args.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def duplicate_package(self):
|
||||||
|
if not self.serial_and_batch_bundle:
|
||||||
|
return
|
||||||
|
|
||||||
|
id = self.serial_and_batch_bundle
|
||||||
|
package = frappe.get_doc("Serial and Batch Bundle", id)
|
||||||
|
new_package = frappe.copy_doc(package)
|
||||||
|
new_package.type_of_transaction = self.type_of_transaction
|
||||||
|
new_package.save()
|
||||||
|
|
||||||
|
self.serial_and_batch_bundle = new_package.name
|
||||||
|
|||||||
@ -27,7 +27,7 @@ from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
|
|||||||
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_item_and_warehouse as get_reserved_stock,
|
||||||
)
|
)
|
||||||
from erpnext.stock.serial_batch_bundle import BatchNoBundleValuation, SerialNoBundleValuation
|
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
||||||
from erpnext.stock.utils import (
|
from erpnext.stock.utils import (
|
||||||
get_incoming_outgoing_rate_for_cancel,
|
get_incoming_outgoing_rate_for_cancel,
|
||||||
get_or_make_bin,
|
get_or_make_bin,
|
||||||
@ -693,7 +693,7 @@ class update_entries_after(object):
|
|||||||
|
|
||||||
if sle.serial_and_batch_bundle:
|
if sle.serial_and_batch_bundle:
|
||||||
if frappe.get_cached_value("Item", sle.item_code, "has_serial_no"):
|
if frappe.get_cached_value("Item", sle.item_code, "has_serial_no"):
|
||||||
SerialNoBundleValuation(
|
SerialNoValuation(
|
||||||
sle=sle,
|
sle=sle,
|
||||||
sle_self=self,
|
sle_self=self,
|
||||||
wh_data=self.wh_data,
|
wh_data=self.wh_data,
|
||||||
@ -701,7 +701,7 @@ class update_entries_after(object):
|
|||||||
item_code=sle.item_code,
|
item_code=sle.item_code,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
BatchNoBundleValuation(
|
BatchNoValuation(
|
||||||
sle=sle,
|
sle=sle,
|
||||||
sle_self=self,
|
sle_self=self,
|
||||||
wh_data=self.wh_data,
|
wh_data=self.wh_data,
|
||||||
|
|||||||
@ -12,7 +12,7 @@ from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
|
|||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
from erpnext.stock.serial_batch_bundle import BatchNoBundleValuation, SerialNoBundleValuation
|
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
||||||
from erpnext.stock.valuation import FIFOValuation, LIFOValuation
|
from erpnext.stock.valuation import FIFOValuation, LIFOValuation
|
||||||
|
|
||||||
BarcodeScanResult = Dict[str, Optional[str]]
|
BarcodeScanResult = Dict[str, Optional[str]]
|
||||||
@ -264,7 +264,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
|
|||||||
|
|
||||||
if item_details.has_serial_no and args.get("serial_and_batch_bundle"):
|
if item_details.has_serial_no and args.get("serial_and_batch_bundle"):
|
||||||
args.actual_qty = args.qty
|
args.actual_qty = args.qty
|
||||||
sn_obj = SerialNoBundleValuation(
|
sn_obj = SerialNoValuation(
|
||||||
sle=args,
|
sle=args,
|
||||||
warehouse=args.get("warehouse"),
|
warehouse=args.get("warehouse"),
|
||||||
item_code=args.get("item_code"),
|
item_code=args.get("item_code"),
|
||||||
@ -274,7 +274,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
|
|||||||
|
|
||||||
elif item_details.has_batch_no and args.get("serial_and_batch_bundle"):
|
elif item_details.has_batch_no and args.get("serial_and_batch_bundle"):
|
||||||
args.actual_qty = args.qty
|
args.actual_qty = args.qty
|
||||||
batch_obj = BatchNoBundleValuation(
|
batch_obj = BatchNoValuation(
|
||||||
sle=args,
|
sle=args,
|
||||||
warehouse=args.get("warehouse"),
|
warehouse=args.get("warehouse"),
|
||||||
item_code=args.get("item_code"),
|
item_code=args.get("item_code"),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user