Compare commits

..

2 Commits

Author SHA1 Message Date
rohitwaghchaure
5ed768af19 feat: provision to add scrap item in job card (#27483)
(cherry picked from commit c5a77f60ed)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.json
#	erpnext/manufacturing/doctype/production_plan/test_production_plan.py
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2025-02-23 00:17:20 +00:00
mergify[bot]
f5160dc83d fix: use Stock Qty while getting POS Reserved Qty (backport #38962) (#38983)
fix: use `Stock Qty` while getting `POS Reserved Qty`

(cherry picked from commit 7223106417)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-28 12:00:04 +05:30
6 changed files with 148 additions and 3 deletions

View File

@@ -4,7 +4,7 @@ import frappe
from erpnext.hooks import regional_overrides
__version__ = "13.55.2"
__version__ = "13.54.4"
def get_default_company(user=None):

View File

@@ -704,7 +704,7 @@ def get_pos_reserved_qty(item_code, warehouse):
reserved_qty = (
frappe.qb.from_(p_inv)
.from_(p_item)
.select(Sum(p_item.qty).as_("qty"))
.select(Sum(p_item.stock_qty).as_("stock_qty"))
.where(
(p_inv.name == p_item.parent)
& (IfNull(p_inv.consolidated_invoice, "") == "")
@@ -715,7 +715,7 @@ def get_pos_reserved_qty(item_code, warehouse):
)
).run(as_dict=True)
return reserved_qty[0].qty or 0 if reserved_qty else 0
return flt(reserved_qty[0].stock_qty) if reserved_qty else 0
@frappe.whitelist()

View File

@@ -397,7 +397,10 @@
"options": "Batch"
},
{
<<<<<<< HEAD
"collapsible": 1,
=======
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
"fieldname": "scrap_items_section",
"fieldtype": "Section Break",
"label": "Scrap Items"
@@ -409,6 +412,7 @@
"no_copy": 1,
"options": "Job Card Scrap Item",
"print_hide": 1
<<<<<<< HEAD
},
{
"fetch_from": "operation.quality_inspection_template",
@@ -416,11 +420,17 @@
"fieldtype": "Link",
"label": "Quality Inspection Template",
"options": "Quality Inspection Template"
=======
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
}
],
"is_submittable": 1,
"links": [],
<<<<<<< HEAD
"modified": "2023-05-22 23:26:57.589331",
=======
"modified": "2021-09-14 00:38:46.873105",
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",

View File

@@ -844,6 +844,7 @@ def make_bom(**args):
)
for item in args.raw_materials:
<<<<<<< HEAD
item_doc = frappe.get_doc("Item", item)
bom.append(
"items",
@@ -856,6 +857,18 @@ def make_bom(**args):
"source_warehouse": args.source_warehouse,
},
)
=======
item_doc = frappe.get_doc('Item', item)
bom.append('items', {
'item_code': item,
'qty': args.rm_qty or 1.0,
'uom': item_doc.stock_uom,
'stock_uom': item_doc.stock_uom,
'rate': item_doc.valuation_rate or args.rate,
'source_warehouse': args.source_warehouse
})
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
if not args.do_not_save:
bom.insert(ignore_permissions=True)

View File

@@ -1285,6 +1285,60 @@ def update_job_card(job_card, jc_qty=None):
job_card_doc.submit()
def test_job_card_scrap_item(self):
items = ['Test FG Item for Scrap Item Test', 'Test RM Item 1 for Scrap Item Test',
'Test RM Item 2 for Scrap Item Test']
company = '_Test Company with perpetual inventory'
for item_code in items:
create_item(item_code = item_code, is_stock_item = 1,
is_purchase_item=1, opening_stock=100, valuation_rate=10, company=company, warehouse='Stores - TCP1')
item = 'Test FG Item for Scrap Item Test'
raw_materials = ['Test RM Item 1 for Scrap Item Test', 'Test RM Item 2 for Scrap Item Test']
if not frappe.db.get_value('BOM', {'item': item}):
bom = make_bom(item=item, source_warehouse='Stores - TCP1', raw_materials=raw_materials, do_not_save=True)
bom.with_operations = 1
bom.append('operations', {
'operation': '_Test Operation 1',
'workstation': '_Test Workstation 1',
'hour_rate': 20,
'time_in_mins': 60
})
bom.submit()
wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=now(), qty=20, skip_transfer=1)
job_card = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name')
update_job_card(job_card)
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
for row in stock_entry.items:
if row.is_scrap_item:
self.assertEqual(row.qty, 1)
def update_job_card(job_card):
job_card_doc = frappe.get_doc('Job Card', job_card)
job_card_doc.set('scrap_items', [
{
'item_code': 'Test RM Item 1 for Scrap Item Test',
'stock_qty': 2
},
{
'item_code': 'Test RM Item 2 for Scrap Item Test',
'stock_qty': 2
},
])
job_card_doc.append('time_logs', {
'from_time': now(),
'time_in_mins': 60,
'completed_qty': job_card_doc.for_quantity
})
job_card_doc.submit()
def get_scrap_item_details(bom_no):
scrap_items = {}
for item in frappe.db.sql(

View File

@@ -921,7 +921,11 @@ class StockEntry(StockController):
row.db_set("po_detail", po_detail)
def validate_bom(self):
<<<<<<< HEAD
for d in self.get("items"):
=======
for d in self.get('items'):
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
if d.bom_no and d.is_finished_item:
item_code = d.original_item or d.item_code
validate_bom_no(item_code, d.bom_no)
@@ -1548,10 +1552,15 @@ class StockEntry(StockController):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
# item dict = { item_code: {qty, description, stock_uom} }
<<<<<<< HEAD
item_dict = (
get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded=0, fetch_scrap_items=1)
or {}
)
=======
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
fetch_exploded = 0, fetch_scrap_items = 1) or {}
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
for item in itervalues(item_dict):
item.from_warehouse = ""
@@ -1565,6 +1574,7 @@ class StockEntry(StockController):
if not item_row:
item_row = frappe._dict({})
<<<<<<< HEAD
item_row.update(
{
"uom": row.stock_uom,
@@ -1577,6 +1587,18 @@ class StockEntry(StockController):
"allow_zero_valuation_rate": 1,
}
)
=======
item_row.update({
'uom': row.stock_uom,
'from_warehouse': '',
'qty': row.stock_qty + flt(item_row.stock_qty),
'converison_factor': 1,
'is_scrap_item': 1,
'item_name': row.item_name,
'description': row.description,
'allow_zero_valuation_rate': 1
})
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
item_dict[row.item_code] = item_row
@@ -1586,6 +1608,7 @@ class StockEntry(StockController):
if not self.pro_doc:
self.set_work_order_details()
<<<<<<< HEAD
if not self.pro_doc.operations:
return []
@@ -1613,6 +1636,24 @@ class StockEntry(StockController):
pending_qty = flt(self.get_completed_job_card_qty()) - flt(self.pro_doc.produced_qty)
=======
scrap_items = frappe.db.sql('''
SELECT
JCSI.item_code, JCSI.item_name, SUM(JCSI.stock_qty) as stock_qty, JCSI.stock_uom, JCSI.description
FROM
`tabJob Card` JC, `tabJob Card Scrap Item` JCSI
WHERE
JCSI.parent = JC.name AND JC.docstatus = 1
AND JCSI.item_code IS NOT NULL AND JC.work_order = %s
GROUP BY
JCSI.item_code
''', self.work_order, as_dict=1)
pending_qty = flt(self.pro_doc.qty) - flt(self.pro_doc.produced_qty)
if pending_qty <=0:
return []
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
used_scrap_items = self.get_used_scrap_items()
for row in scrap_items:
row.stock_qty -= flt(used_scrap_items.get(row.item_code))
@@ -1621,11 +1662,16 @@ class StockEntry(StockController):
if used_scrap_items.get(row.item_code):
used_scrap_items[row.item_code] -= row.stock_qty
<<<<<<< HEAD
if cint(frappe.get_cached_value("UOM", row.stock_uom, "must_be_whole_number")):
=======
if cint(frappe.get_cached_value('UOM', row.stock_uom, 'must_be_whole_number')):
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
row.stock_qty = frappe.utils.ceil(row.stock_qty)
return scrap_items
<<<<<<< HEAD
def get_completed_job_card_qty(self):
return flt(min([d.completed_qty for d in self.pro_doc.operations]))
@@ -1640,6 +1686,21 @@ class StockEntry(StockController):
["Stock Entry", "docstatus", "=", 1],
["Stock Entry", "purpose", "in", ["Repack", "Manufacture"]],
],
=======
def get_used_scrap_items(self):
used_scrap_items = defaultdict(float)
data = frappe.get_all(
'Stock Entry',
fields = [
'`tabStock Entry Detail`.`item_code`', '`tabStock Entry Detail`.`qty`'
],
filters = [
['Stock Entry', 'work_order', '=', self.work_order],
['Stock Entry Detail', 'is_scrap_item', '=', 1],
['Stock Entry', 'docstatus', '=', 1],
['Stock Entry', 'purpose', 'in', ['Repack', 'Manufacture']]
]
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
)
for row in data:
@@ -1929,6 +1990,7 @@ class StockEntry(StockController):
se_child.is_scrap_item = item_row.get("is_scrap_item", 0)
se_child.is_process_loss = item_row.get("is_process_loss", 0)
<<<<<<< HEAD
for field in [
"po_detail",
"original_item",
@@ -1941,6 +2003,12 @@ class StockEntry(StockController):
]:
if item_row.get(field):
se_child.set(field, item_row.get(field))
=======
for field in ["idx", "po_detail", "original_item", "expense_account",
"description", "item_name", "serial_no", "batch_no", "allow_zero_valuation_rate"]:
if item_dict[d].get(field):
se_child.set(field, item_dict[d].get(field))
>>>>>>> c5a77f60ed (feat: provision to add scrap item in job card (#27483))
if se_child.s_warehouse == None:
se_child.s_warehouse = self.from_warehouse