fix: use serial batch fields for subcontracting receipt (#40311)
This commit is contained in:
@@ -439,9 +439,21 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
||||
filtered_batches = get_filterd_batches(batches)
|
||||
|
||||
if filters.get("is_inward"):
|
||||
filtered_batches.extend(get_empty_batches(filters))
|
||||
|
||||
return filtered_batches
|
||||
|
||||
|
||||
def get_empty_batches(filters):
|
||||
return frappe.get_all(
|
||||
"Batch",
|
||||
fields=["name", "batch_qty"],
|
||||
filters={"item": filters.get("item_code"), "batch_qty": 0.0},
|
||||
as_list=1,
|
||||
)
|
||||
|
||||
|
||||
def get_filterd_batches(data):
|
||||
batches = OrderedDict()
|
||||
|
||||
|
||||
@@ -190,43 +190,23 @@ class StockController(AccountsController):
|
||||
if row.use_serial_batch_fields and (
|
||||
not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle")
|
||||
):
|
||||
if self.doctype == "Stock Reconciliation":
|
||||
qty = row.qty
|
||||
type_of_transaction = "Inward"
|
||||
warehouse = row.warehouse
|
||||
elif table_name == "packed_items":
|
||||
qty = row.qty
|
||||
warehouse = row.warehouse
|
||||
type_of_transaction = "Outward"
|
||||
if self.is_return:
|
||||
type_of_transaction = "Inward"
|
||||
else:
|
||||
qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty
|
||||
type_of_transaction = get_type_of_transaction(self, row)
|
||||
warehouse = (
|
||||
row.warehouse if self.doctype != "Stock Entry" else row.s_warehouse or row.t_warehouse
|
||||
)
|
||||
bundle_details = {
|
||||
"item_code": row.item_code,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": row.name,
|
||||
"company": self.company,
|
||||
"is_rejected": 1 if row.get("rejected_warehouse") else 0,
|
||||
"serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None,
|
||||
"batch_no": row.batch_no,
|
||||
"use_serial_batch_fields": row.use_serial_batch_fields,
|
||||
"do_not_submit": True,
|
||||
}
|
||||
|
||||
sn_doc = SerialBatchCreation(
|
||||
{
|
||||
"item_code": row.item_code,
|
||||
"warehouse": warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": row.name,
|
||||
"qty": qty,
|
||||
"type_of_transaction": type_of_transaction,
|
||||
"company": self.company,
|
||||
"is_rejected": 1 if row.get("rejected_warehouse") else 0,
|
||||
"serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None,
|
||||
"batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None,
|
||||
"batch_no": row.batch_no,
|
||||
"use_serial_batch_fields": row.use_serial_batch_fields,
|
||||
"do_not_submit": True,
|
||||
}
|
||||
).make_serial_and_batch_bundle()
|
||||
self.update_bundle_details(bundle_details, table_name, row)
|
||||
sn_doc = SerialBatchCreation(bundle_details).make_serial_and_batch_bundle()
|
||||
|
||||
if sn_doc.is_rejected:
|
||||
row.rejected_serial_and_batch_bundle = sn_doc.name
|
||||
@@ -243,6 +223,34 @@ class StockController(AccountsController):
|
||||
}
|
||||
)
|
||||
|
||||
def update_bundle_details(self, bundle_details, table_name, row):
|
||||
# Since qty field is different for different doctypes
|
||||
qty = row.get("qty")
|
||||
warehouse = row.get("warehouse")
|
||||
|
||||
if table_name == "packed_items":
|
||||
type_of_transaction = "Inward"
|
||||
if not self.is_return:
|
||||
type_of_transaction = "Outward"
|
||||
else:
|
||||
type_of_transaction = get_type_of_transaction(self, row)
|
||||
|
||||
if hasattr(row, "stock_qty"):
|
||||
qty = row.stock_qty
|
||||
|
||||
if self.doctype == "Stock Entry":
|
||||
qty = row.transfer_qty
|
||||
warehouse = row.s_warehouse or row.t_warehouse
|
||||
|
||||
bundle_details.update(
|
||||
{
|
||||
"qty": qty,
|
||||
"type_of_transaction": type_of_transaction,
|
||||
"warehouse": warehouse,
|
||||
"batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None,
|
||||
}
|
||||
)
|
||||
|
||||
def validate_serial_nos_and_batches_with_bundle(self, row):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
|
||||
@@ -371,11 +371,18 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
||||
label: __('Batch No'),
|
||||
in_list_view: 1,
|
||||
get_query: () => {
|
||||
let is_inward = false;
|
||||
if ((["Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype) && !this.frm.doc.is_return)
|
||||
|| (this.frm.doc.doctype === 'Stock Entry' && this.frm.doc.purpose === 'Material Receipt')) {
|
||||
is_inward = true;
|
||||
}
|
||||
|
||||
return {
|
||||
query : "erpnext.controllers.queries.get_batch_no",
|
||||
filters: {
|
||||
'item_code': this.item.item_code,
|
||||
'warehouse': this.item.s_warehouse || this.item.t_warehouse,
|
||||
'warehouse': this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse,
|
||||
'is_inward': is_inward
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1263,6 +1263,13 @@ def get_type_of_transaction(parent_doc, child_row):
|
||||
if parent_doc.get("is_return"):
|
||||
type_of_transaction = "Inward" if type_of_transaction == "Outward" else "Outward"
|
||||
|
||||
if parent_doc.get("doctype") == "Subcontracting Receipt":
|
||||
type_of_transaction = "Outward"
|
||||
if child_row.get("doctype") == "Subcontracting Receipt Item":
|
||||
type_of_transaction = "Inward"
|
||||
elif parent_doc.get("doctype") == "Stock Reconciliation":
|
||||
type_of_transaction = "Inward"
|
||||
|
||||
return type_of_transaction
|
||||
|
||||
|
||||
|
||||
@@ -10,17 +10,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import (
|
||||
cint,
|
||||
comma_or,
|
||||
cstr,
|
||||
flt,
|
||||
format_time,
|
||||
formatdate,
|
||||
getdate,
|
||||
month_diff,
|
||||
nowdate,
|
||||
)
|
||||
from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
@@ -237,41 +227,6 @@ class StockEntry(StockController):
|
||||
self.reset_default_field_value("from_warehouse", "items", "s_warehouse")
|
||||
self.reset_default_field_value("to_warehouse", "items", "t_warehouse")
|
||||
|
||||
def submit(self):
|
||||
if self.is_enqueue_action():
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Draft stage"
|
||||
)
|
||||
)
|
||||
self.queue_action("submit", timeout=2000)
|
||||
else:
|
||||
self._submit()
|
||||
|
||||
def cancel(self):
|
||||
if self.is_enqueue_action():
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Submitted stage"
|
||||
)
|
||||
)
|
||||
self.queue_action("cancel", timeout=2000)
|
||||
else:
|
||||
self._cancel()
|
||||
|
||||
def is_enqueue_action(self, force=False) -> bool:
|
||||
if force:
|
||||
return True
|
||||
|
||||
if frappe.flags.in_test:
|
||||
return False
|
||||
|
||||
# If line items are more than 100 or record is older than 6 months
|
||||
if len(self.items) > 50 or month_diff(nowdate(), self.posting_date) > 6:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def on_submit(self):
|
||||
self.validate_closed_subcontracting_order()
|
||||
self.make_bundle_using_old_serial_batch_fields()
|
||||
|
||||
@@ -1642,36 +1642,6 @@ class TestStockEntry(FrappeTestCase):
|
||||
|
||||
self.assertRaises(frappe.ValidationError, sr_doc.submit)
|
||||
|
||||
def test_enqueue_action(self):
|
||||
frappe.flags.in_test = False
|
||||
item_code = "Test Enqueue Item - 001"
|
||||
create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
|
||||
|
||||
doc = make_stock_entry(
|
||||
item_code=item_code,
|
||||
posting_date=add_to_date(today(), months=-7),
|
||||
posting_time="00:00:00",
|
||||
purpose="Material Receipt",
|
||||
qty=10,
|
||||
to_warehouse="_Test Warehouse - _TC",
|
||||
do_not_submit=True,
|
||||
)
|
||||
|
||||
self.assertTrue(doc.is_enqueue_action())
|
||||
|
||||
doc = make_stock_entry(
|
||||
item_code=item_code,
|
||||
posting_date=today(),
|
||||
posting_time="00:00:00",
|
||||
purpose="Material Receipt",
|
||||
qty=10,
|
||||
to_warehouse="_Test Warehouse - _TC",
|
||||
do_not_submit=True,
|
||||
)
|
||||
|
||||
self.assertFalse(doc.is_enqueue_action())
|
||||
frappe.flags.in_test = True
|
||||
|
||||
def test_negative_batch(self):
|
||||
item_code = "Test Negative Batch Item - 001"
|
||||
make_item(
|
||||
|
||||
@@ -1061,6 +1061,77 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
||||
|
||||
self.assertTrue(frappe.db.get_value("Purchase Receipt", {"subcontracting_receipt": scr.name}))
|
||||
|
||||
def test_use_serial_batch_fields_for_subcontracting_receipt(self):
|
||||
fg_item = make_item(
|
||||
"Test Subcontracted Item With Batch No",
|
||||
properties={
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "BATCH-BNGS-.####",
|
||||
"is_sub_contracted_item": 1,
|
||||
},
|
||||
).name
|
||||
|
||||
make_item(
|
||||
"Test Subcontracted Item With Batch No Service Item 1",
|
||||
properties={"is_stock_item": 0},
|
||||
)
|
||||
|
||||
make_bom(
|
||||
item=fg_item,
|
||||
raw_materials=[
|
||||
make_item(
|
||||
"Test Subcontracted Item With Batch No RM Item 1",
|
||||
properties={
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "BATCH-RM-BNGS-.####",
|
||||
},
|
||||
).name
|
||||
],
|
||||
)
|
||||
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Test Subcontracted Item With Batch No Service Item 1",
|
||||
"qty": 1,
|
||||
"rate": 100,
|
||||
"fg_item": fg_item,
|
||||
"fg_item_qty": 1,
|
||||
},
|
||||
]
|
||||
sco = get_subcontracting_order(service_items=service_items)
|
||||
rm_items = get_rm_items(sco.supplied_items)
|
||||
itemwise_details = make_stock_in_entry(rm_items=rm_items)
|
||||
make_stock_transfer_entry(
|
||||
sco_no=sco.name,
|
||||
rm_items=rm_items,
|
||||
itemwise_details=copy.deepcopy(itemwise_details),
|
||||
)
|
||||
|
||||
batch_no = "BATCH-BNGS-0001"
|
||||
if not frappe.db.exists("Batch", batch_no):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Batch",
|
||||
"batch_id": batch_no,
|
||||
"item": fg_item,
|
||||
}
|
||||
).insert()
|
||||
|
||||
scr = make_subcontracting_receipt(sco.name)
|
||||
self.assertFalse(scr.items[0].serial_and_batch_bundle)
|
||||
scr.items[0].use_serial_batch_fields = 1
|
||||
scr.items[0].batch_no = batch_no
|
||||
|
||||
scr.save()
|
||||
scr.submit()
|
||||
scr.reload()
|
||||
self.assertTrue(scr.items[0].serial_and_batch_bundle)
|
||||
|
||||
|
||||
def make_return_subcontracting_receipt(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
Reference in New Issue
Block a user