fix: transferred batches are not fetched while making Manufacture stock entry
This commit is contained in:
parent
12edddbfd4
commit
333c62de72
@ -1656,118 +1656,58 @@ class StockEntry(StockController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_transfered_raw_materials(self):
|
def get_transfered_raw_materials(self):
|
||||||
transferred_materials = frappe.db.sql(
|
available_materials = get_available_materials(self.work_order)
|
||||||
"""
|
|
||||||
select
|
wo_data = frappe.db.get_value(
|
||||||
item_name, original_item, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
"Work Order",
|
||||||
description, stock_uom, expense_account, cost_center
|
|
||||||
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
|
||||||
where
|
|
||||||
se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
|
|
||||||
and se.work_order= %s and ifnull(sed.t_warehouse, '') != ''
|
|
||||||
group by sed.item_code, sed.t_warehouse
|
|
||||||
""",
|
|
||||||
self.work_order,
|
self.work_order,
|
||||||
|
["qty", "produced_qty", "material_transferred_for_manufacturing as trans_qty"],
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
materials_already_backflushed = frappe.db.sql(
|
for key, row in available_materials.items():
|
||||||
"""
|
qty = (flt(row.qty) * flt(self.fg_completed_qty)) / (
|
||||||
select
|
flt(wo_data.trans_qty) - flt(wo_data.produced_qty)
|
||||||
item_code, sed.s_warehouse as warehouse, sum(qty) as qty
|
|
||||||
from
|
|
||||||
`tabStock Entry` se, `tabStock Entry Detail` sed
|
|
||||||
where
|
|
||||||
se.name = sed.parent and se.docstatus=1
|
|
||||||
and (se.purpose='Manufacture' or se.purpose='Material Consumption for Manufacture')
|
|
||||||
and se.work_order= %s and ifnull(sed.s_warehouse, '') != ''
|
|
||||||
group by sed.item_code, sed.s_warehouse
|
|
||||||
""",
|
|
||||||
self.work_order,
|
|
||||||
as_dict=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
backflushed_materials = {}
|
|
||||||
for d in materials_already_backflushed:
|
|
||||||
backflushed_materials.setdefault(d.item_code, []).append({d.warehouse: d.qty})
|
|
||||||
|
|
||||||
po_qty = frappe.db.sql(
|
|
||||||
"""select qty, produced_qty, material_transferred_for_manufacturing from
|
|
||||||
`tabWork Order` where name=%s""",
|
|
||||||
self.work_order,
|
|
||||||
as_dict=1,
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
manufacturing_qty = flt(po_qty.qty) or 1
|
|
||||||
produced_qty = flt(po_qty.produced_qty)
|
|
||||||
trans_qty = flt(po_qty.material_transferred_for_manufacturing) or 1
|
|
||||||
|
|
||||||
for item in transferred_materials:
|
|
||||||
qty = item.qty
|
|
||||||
item_code = item.original_item or item.item_code
|
|
||||||
req_items = frappe.get_all(
|
|
||||||
"Work Order Item",
|
|
||||||
filters={"parent": self.work_order, "item_code": item_code},
|
|
||||||
fields=["required_qty", "consumed_qty"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
req_qty = flt(req_items[0].required_qty) if req_items else flt(4)
|
item = row.item_details
|
||||||
req_qty_each = flt(req_qty / manufacturing_qty)
|
|
||||||
consumed_qty = flt(req_items[0].consumed_qty) if req_items else 0
|
|
||||||
|
|
||||||
if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
|
|
||||||
if qty >= req_qty:
|
|
||||||
qty = (req_qty / trans_qty) * flt(self.fg_completed_qty)
|
|
||||||
else:
|
|
||||||
qty = qty - consumed_qty
|
|
||||||
|
|
||||||
if self.purpose == "Manufacture":
|
|
||||||
# If Material Consumption is booked, must pull only remaining components to finish product
|
|
||||||
if consumed_qty != 0:
|
|
||||||
remaining_qty = consumed_qty - (produced_qty * req_qty_each)
|
|
||||||
exhaust_qty = req_qty_each * produced_qty
|
|
||||||
if remaining_qty > exhaust_qty:
|
|
||||||
if (remaining_qty / (req_qty_each * flt(self.fg_completed_qty))) >= 1:
|
|
||||||
qty = 0
|
|
||||||
else:
|
|
||||||
qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty
|
|
||||||
else:
|
|
||||||
if self.flags.backflush_based_on == "Material Transferred for Manufacture":
|
|
||||||
qty = (item.qty / trans_qty) * flt(self.fg_completed_qty)
|
|
||||||
else:
|
|
||||||
qty = req_qty_each * flt(self.fg_completed_qty)
|
|
||||||
|
|
||||||
elif backflushed_materials.get(item.item_code):
|
|
||||||
precision = frappe.get_precision("Stock Entry Detail", "qty")
|
|
||||||
for d in backflushed_materials.get(item.item_code):
|
|
||||||
if d.get(item.warehouse) > 0:
|
|
||||||
if qty > req_qty:
|
|
||||||
qty = (
|
|
||||||
(flt(qty, precision) - flt(d.get(item.warehouse), precision))
|
|
||||||
/ (flt(trans_qty, precision) - flt(produced_qty, precision))
|
|
||||||
) * flt(self.fg_completed_qty)
|
|
||||||
|
|
||||||
d[item.warehouse] -= qty
|
|
||||||
|
|
||||||
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
|
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
|
||||||
qty = frappe.utils.ceil(qty)
|
qty = frappe.utils.ceil(qty)
|
||||||
|
|
||||||
if qty > 0:
|
if row.batch_details:
|
||||||
self.add_to_stock_entry_detail(
|
for batch_no, batch_qty in row.batch_details.items():
|
||||||
{
|
if qty <= 0:
|
||||||
item.item_code: {
|
continue
|
||||||
"from_warehouse": item.warehouse,
|
|
||||||
"to_warehouse": "",
|
if batch_qty > qty:
|
||||||
"qty": qty,
|
batch_qty = qty
|
||||||
"item_name": item.item_name,
|
|
||||||
"description": item.description,
|
item.batch_no = batch_no
|
||||||
"stock_uom": item.stock_uom,
|
self.update_item_in_stock_entry_detail(row, item, batch_qty)
|
||||||
"expense_account": item.expense_account,
|
|
||||||
"cost_center": item.buying_cost_center,
|
row.batch_details[batch_no] -= batch_qty
|
||||||
"original_item": item.original_item,
|
qty -= batch_qty
|
||||||
}
|
else:
|
||||||
}
|
self.update_item_in_stock_entry_detail(row, item, qty)
|
||||||
)
|
|
||||||
|
def update_item_in_stock_entry_detail(self, row, item, qty):
|
||||||
|
ste_item_details = {
|
||||||
|
"from_warehouse": item.warehouse,
|
||||||
|
"to_warehouse": "",
|
||||||
|
"qty": qty,
|
||||||
|
"item_name": item.item_name,
|
||||||
|
"batch_no": item.batch_no,
|
||||||
|
"description": item.description,
|
||||||
|
"stock_uom": item.stock_uom,
|
||||||
|
"expense_account": item.expense_account,
|
||||||
|
"cost_center": item.buying_cost_center,
|
||||||
|
"original_item": item.original_item,
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.serial_no:
|
||||||
|
ste_item_details["serial_no"] = "\n".join(item.serial_no[0 : cint(qty)])
|
||||||
|
|
||||||
|
self.add_to_stock_entry_detail({item.item_code: ste_item_details})
|
||||||
|
|
||||||
def get_pending_raw_materials(self, backflush_based_on=None):
|
def get_pending_raw_materials(self, backflush_based_on=None):
|
||||||
"""
|
"""
|
||||||
@ -2521,3 +2461,80 @@ def get_supplied_items(purchase_order):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return supplied_item_details
|
return supplied_item_details
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_materials(work_order) -> dict:
|
||||||
|
data = get_stock_entry_data(work_order)
|
||||||
|
|
||||||
|
available_materials = {}
|
||||||
|
for row in data:
|
||||||
|
key = (row.item_code, row.warehouse)
|
||||||
|
if row.purpose != "Material Transfer for Manufacture":
|
||||||
|
key = (row.item_code, row.s_warehouse)
|
||||||
|
|
||||||
|
if key not in available_materials:
|
||||||
|
available_materials.setdefault(
|
||||||
|
key,
|
||||||
|
frappe._dict(
|
||||||
|
{"item_details": row, "batch_details": defaultdict(float), "qty": 0, "serial_nos": []}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
item_data = available_materials[key]
|
||||||
|
|
||||||
|
if row.purpose == "Material Transfer for Manufacture":
|
||||||
|
item_data.qty += row.qty
|
||||||
|
if row.batch_no:
|
||||||
|
item_data.batch_details[row.batch_no] += row.qty
|
||||||
|
|
||||||
|
if row.serial_no:
|
||||||
|
item_data.serial_nos.extend(get_serial_nos(row.serial_no))
|
||||||
|
else:
|
||||||
|
# Consume raw material qty in case of 'Manufacture' or 'Material Consumption for Manufacture'
|
||||||
|
|
||||||
|
item_data.qty -= row.qty
|
||||||
|
if row.batch_no:
|
||||||
|
item_data.batch_details[row.batch_no] -= row.qty
|
||||||
|
|
||||||
|
if row.serial_no:
|
||||||
|
for serial_no in get_serial_nos(row.serial_no):
|
||||||
|
item_data.serial_nos.remove(serial_no)
|
||||||
|
|
||||||
|
return available_materials
|
||||||
|
|
||||||
|
|
||||||
|
def get_stock_entry_data(work_order):
|
||||||
|
stock_entry = frappe.qb.DocType("Stock Entry")
|
||||||
|
stock_entry_detail = frappe.qb.DocType("Stock Entry Detail")
|
||||||
|
|
||||||
|
return (
|
||||||
|
frappe.qb.from_(stock_entry)
|
||||||
|
.from_(stock_entry_detail)
|
||||||
|
.select(
|
||||||
|
stock_entry_detail.item_name,
|
||||||
|
stock_entry_detail.original_item,
|
||||||
|
stock_entry_detail.item_code,
|
||||||
|
stock_entry_detail.qty,
|
||||||
|
(stock_entry_detail.t_warehouse).as_("warehouse"),
|
||||||
|
(stock_entry_detail.s_warehouse).as_("s_warehouse"),
|
||||||
|
stock_entry_detail.description,
|
||||||
|
stock_entry_detail.stock_uom,
|
||||||
|
stock_entry_detail.expense_account,
|
||||||
|
stock_entry_detail.cost_center,
|
||||||
|
stock_entry_detail.batch_no,
|
||||||
|
stock_entry_detail.serial_no,
|
||||||
|
stock_entry.purpose,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(stock_entry.name == stock_entry_detail.parent)
|
||||||
|
& (stock_entry.work_order == work_order)
|
||||||
|
& (stock_entry.docstatus == 1)
|
||||||
|
& (stock_entry_detail.s_warehouse.isnotnull())
|
||||||
|
& (
|
||||||
|
stock_entry.purpose.isin(
|
||||||
|
["Manufacture", "Material Consumption for Manufacture", "Material Transfer for Manufacture"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orderby(stock_entry.creation, stock_entry_detail.item_code, stock_entry_detail.idx)
|
||||||
|
).run(as_dict=1, debug=1)
|
||||||
|
Loading…
Reference in New Issue
Block a user