mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-21 05:59:18 +00:00
fix: feat: multiple changes related to subcontracting inward
This commit is contained in:
@@ -9,7 +9,7 @@ from erpnext.stock.serial_batch_bundle import get_serial_batch_list_from_item
|
||||
class SubcontractingInwardController:
|
||||
def validate_subcontracting_inward(self):
|
||||
self.validate_inward_order()
|
||||
self.validate_customer_provided_item_for_inward()
|
||||
self.set_allow_zero_valuation_rate()
|
||||
self.validate_warehouse_()
|
||||
self.validate_serial_batch_for_return_or_delivery()
|
||||
self.validate_delivery()
|
||||
@@ -50,11 +50,22 @@ class SubcontractingInwardController:
|
||||
self.validate_material_receipt()
|
||||
case purpose if purpose in ["Return Raw Material to Customer", "Subcontracting Return"]:
|
||||
self.validate_returns()
|
||||
case "Material Transfer for Manufacture":
|
||||
self.validate_material_transfer()
|
||||
case "Manufacture":
|
||||
self.validate_manufacture()
|
||||
|
||||
def validate_material_receipt(self):
|
||||
rm_item_fg_combo = []
|
||||
for item in self.items:
|
||||
if not frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item"):
|
||||
frappe.throw(
|
||||
_("Row #{0}: Item {1} is not a Customer Provided Item.").format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
item.scio_detail
|
||||
and frappe.get_cached_value(
|
||||
@@ -65,17 +76,44 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Item {1} mismatch. Changing of item code is not permitted, add another row instead."
|
||||
).format(item.idx, bold(item.item_code))
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||
)
|
||||
|
||||
if not item.scio_detail: # item is additional
|
||||
if item.against_fg:
|
||||
if (item.item_code, item.against_fg) not in rm_item_fg_combo:
|
||||
rm_item_fg_combo.append((item.item_code, item.against_fg))
|
||||
else:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} against Subcontracting Inward Order Item {2} ({3}) cannot be added multiple times."
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
bold(item.against_fg),
|
||||
get_link_to_form(
|
||||
"Item",
|
||||
frappe.get_cached_value(
|
||||
"Subcontracting Inward Order Item", item.against_fg, "item_code"
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
else:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Please select the Finished Good Item against which this Customer Provided Item will be used."
|
||||
).format(item.idx)
|
||||
)
|
||||
|
||||
def validate_returns(self):
|
||||
for item in self.items:
|
||||
if not item.scio_detail:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Item {1} is not a part of Subcontracting Inward Order {2}").format(
|
||||
item.idx,
|
||||
bold(item.item_code),
|
||||
bold(self.subcontracting_inward_order),
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Subcontracting Inward Order", self.subcontracting_inward_order),
|
||||
)
|
||||
)
|
||||
elif item.item_code != (
|
||||
@@ -86,7 +124,7 @@ class SubcontractingInwardController:
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row #{0}: Item {1} mismatch. Changing of item code is not permitted.").format(
|
||||
item.idx, bold(item.item_code)
|
||||
item.idx, get_link_to_form("Item", item.item_code)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -101,7 +139,7 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Returned quantity cannot be greater than available quantity for Item {1}"
|
||||
).format(item.idx, bold(item.item_code))
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||
)
|
||||
else:
|
||||
data = frappe.get_value(
|
||||
@@ -114,16 +152,88 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Returned quantity cannot be greater than available quantity to return for Item {1}"
|
||||
).format(item.idx, bold(item.item_code))
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||
)
|
||||
|
||||
def validate_material_transfer(self):
|
||||
customer_warehouse = frappe.get_cached_value(
|
||||
"Subcontracting Inward Order", self.subcontracting_inward_order, "customer_warehouse"
|
||||
)
|
||||
item_codes = []
|
||||
for item in self.items:
|
||||
if not frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item"):
|
||||
continue
|
||||
elif item.s_warehouse != customer_warehouse:
|
||||
frappe.throw(
|
||||
_("Row #{0}: For Customer Provided Item {1}, Source Warehouse must be {2}").format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Warehouse", customer_warehouse),
|
||||
)
|
||||
)
|
||||
elif item.item_code in item_codes:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} cannot be added multiple times in the Subcontracting Inward process."
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
)
|
||||
)
|
||||
else:
|
||||
work_order_items = frappe.get_all(
|
||||
"Work Order Item",
|
||||
{"parent": self.work_order, "docstatus": 1, "is_customer_provided_item": 1},
|
||||
["item_code", "transferred_qty", "required_qty", "stock_reserved_qty"],
|
||||
)
|
||||
wo_item_dict = frappe._dict(
|
||||
{
|
||||
wo_item.item_code: frappe._dict(
|
||||
{
|
||||
"transferred_qty": wo_item.transferred_qty,
|
||||
"required_qty": wo_item.required_qty,
|
||||
"stock_reserved_qty": wo_item.stock_reserved_qty,
|
||||
}
|
||||
)
|
||||
for wo_item in work_order_items
|
||||
}
|
||||
)
|
||||
if wo_item := wo_item_dict.get(item.item_code):
|
||||
if wo_item.transferred_qty + item.transfer_qty > max(
|
||||
wo_item.required_qty, wo_item.stock_reserved_qty
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Overconsumption of Customer Provided Item {1} against Work Order {2} is not allowed in the Subcontracting Inward process."
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Work Order", self.work_order),
|
||||
)
|
||||
)
|
||||
else:
|
||||
item_codes.append(item.item_code)
|
||||
else:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Customer Provided Item {1} is not a part of Work Order {2}").format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Work Order", self.work_order),
|
||||
)
|
||||
)
|
||||
|
||||
def validate_manufacture(self):
|
||||
skip_transfer, customer_warehouse, wip_warehouse = frappe.get_cached_value(
|
||||
"Work Order",
|
||||
self.work_order,
|
||||
["skip_transfer", "source_warehouse", "wip_warehouse"],
|
||||
)
|
||||
warehouse = customer_warehouse if skip_transfer else wip_warehouse
|
||||
if next(item for item in self.items if item.is_finished_item).t_warehouse != (
|
||||
fg_warehouse := frappe.get_cached_value("Work Order", self.work_order, "fg_warehouse")
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Target Warehouse for Finished Good must be same as Finished Good Warehouse {1} in Work Order {2} linked to the Subcontracting Inward Order."
|
||||
).format(
|
||||
get_link_to_form("Warehouse", fg_warehouse),
|
||||
get_link_to_form("Work Order", self.work_order),
|
||||
)
|
||||
)
|
||||
|
||||
items = [
|
||||
item
|
||||
@@ -133,74 +243,133 @@ class SubcontractingInwardController:
|
||||
and frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item")
|
||||
]
|
||||
|
||||
table = frappe.qb.DocType("Subcontracting Inward Order Received Item")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(
|
||||
table.rm_item_code,
|
||||
(table.received_qty - table.returned_qty).as_("total_qty"),
|
||||
table.consumed_qty,
|
||||
table.name,
|
||||
)
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.parent == self.subcontracting_inward_order)
|
||||
& (table.main_item_code == frappe.get_cached_value("BOM", self.bom_no, "item"))
|
||||
& (table.warehouse == warehouse)
|
||||
& (table.rm_item_code.isin([item.item_code for item in items]))
|
||||
)
|
||||
customer_warehouse = frappe.get_cached_value(
|
||||
"Subcontracting Inward Order", self.subcontracting_inward_order, "customer_warehouse"
|
||||
)
|
||||
rm_item_dict = frappe._dict(
|
||||
{
|
||||
d.rm_item_code: frappe._dict(
|
||||
{"name": d.name, "total_qty": d.total_qty, "qty": d.consumed_qty}
|
||||
if frappe.get_cached_value("Work Order", self.work_order, "skip_transfer"):
|
||||
table = frappe.qb.DocType("Subcontracting Inward Order Received Item")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(
|
||||
table.rm_item_code,
|
||||
(table.received_qty - table.returned_qty).as_("total_qty"),
|
||||
table.consumed_qty,
|
||||
table.name,
|
||||
)
|
||||
for d in query.run(as_dict=True)
|
||||
}
|
||||
)
|
||||
|
||||
for item in items:
|
||||
if rm := rm_item_dict.get(item.item_code):
|
||||
if rm.qty + item.transfer_qty > rm.total_qty:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} exceeds quantity available through Subcontracting Inward Order"
|
||||
).format(item.idx, bold(item.item_code), item.transfer_qty)
|
||||
)
|
||||
elif item.s_warehouse != warehouse:
|
||||
frappe.throw(
|
||||
_("Row #{0}: For Customer Provided Item {1}, Source Warehouse must be {2}").format(
|
||||
item.idx,
|
||||
bold(item.item_code),
|
||||
bold(warehouse),
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.parent == self.subcontracting_inward_order)
|
||||
& (
|
||||
table.reference_name
|
||||
== frappe.get_cached_value(
|
||||
"Work Order", self.work_order, "subcontracting_inward_order_item"
|
||||
)
|
||||
)
|
||||
else:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} is not a part of Subcontracting Inward Order {2}"
|
||||
).format(
|
||||
item.idx,
|
||||
bold(item.item_code),
|
||||
bold(self.subcontracting_inward_order),
|
||||
)
|
||||
& (table.rm_item_code.isin([item.item_code for item in items]))
|
||||
)
|
||||
)
|
||||
rm_item_dict = frappe._dict(
|
||||
{
|
||||
d.rm_item_code: frappe._dict(
|
||||
{"name": d.name, "total_qty": d.total_qty, "qty": d.consumed_qty}
|
||||
)
|
||||
for d in query.run(as_dict=True)
|
||||
}
|
||||
)
|
||||
|
||||
def validate_customer_provided_item_for_inward(self):
|
||||
if self.subcontracting_inward_order:
|
||||
if self.purpose in ["Subcontracting Delivery", "Subcontracting Return"]:
|
||||
for item in self.items:
|
||||
if (item.is_finished_item or item.is_scrap_item) and item.valuation_rate == 0:
|
||||
item.allow_zero_valuation_rate = 1
|
||||
elif self.purpose == "Receive from Customer":
|
||||
for item in self.items:
|
||||
if not frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item"):
|
||||
item_codes = []
|
||||
for item in items:
|
||||
if rm := rm_item_dict.get(item.item_code):
|
||||
if rm.qty + item.transfer_qty > rm.total_qty:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Item {1} is not a customer provided item.").format(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} exceeds quantity available through Subcontracting Inward Order"
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code), item.transfer_qty)
|
||||
)
|
||||
elif item.s_warehouse != customer_warehouse:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: For Customer Provided Item {1}, Source Warehouse must be {2}"
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Warehouse", customer_warehouse),
|
||||
)
|
||||
)
|
||||
elif item.item_code in item_codes:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} cannot be added multiple times in the Subcontracting Inward process."
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
)
|
||||
)
|
||||
else:
|
||||
item_codes.append(item.item_code)
|
||||
else:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} is not a part of Subcontracting Inward Order {2}"
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Subcontracting Inward Order", self.subcontracting_inward_order),
|
||||
)
|
||||
)
|
||||
else:
|
||||
work_order_items = frappe.get_all(
|
||||
"Work Order Item",
|
||||
{"parent": self.work_order, "docstatus": 1, "is_customer_provided_item": 1},
|
||||
["item_code", "transferred_qty", "consumed_qty"],
|
||||
)
|
||||
wo_item_dict = frappe._dict(
|
||||
{
|
||||
wo_item.item_code: frappe._dict(
|
||||
{"transferred_qty": wo_item.transferred_qty, "consumed_qty": wo_item.consumed_qty}
|
||||
)
|
||||
for wo_item in work_order_items
|
||||
}
|
||||
)
|
||||
item_codes = []
|
||||
for item in items:
|
||||
if wo_item := wo_item_dict.get(item.item_code):
|
||||
if wo_item.consumed_qty + item.transfer_qty > wo_item.transferred_qty:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Overconsumption of Customer Provided Item {1} against Work Order {2} is not allowed in the Subcontracting Inward process."
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Work Order", self.work_order),
|
||||
)
|
||||
)
|
||||
elif item.item_code in item_codes:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Customer Provided Item {1} cannot be added multiple times in the Subcontracting Inward process."
|
||||
).format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
)
|
||||
)
|
||||
else:
|
||||
item_codes.append(item.item_code)
|
||||
else:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Customer Provided Item {1} is not a part of Work Order {2}").format(
|
||||
item.idx,
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Work Order", self.work_order),
|
||||
)
|
||||
)
|
||||
|
||||
def set_allow_zero_valuation_rate(self):
|
||||
if self.subcontracting_inward_order:
|
||||
if self.purpose in ["Subcontracting Delivery", "Subcontracting Return", "Manufacture"]:
|
||||
for item in self.items:
|
||||
if (item.is_finished_item or item.is_scrap_item) and item.valuation_rate == 0:
|
||||
item.allow_zero_valuation_rate = 1
|
||||
|
||||
def validate_warehouse_(self):
|
||||
if self.subcontracting_inward_order and self.purpose in [
|
||||
@@ -222,13 +391,13 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Target Warehouse must be same as Customer Warehouse {1} from the linked Subcontracting Inward Order"
|
||||
).format(item.idx, bold(customer_warehouse))
|
||||
).format(item.idx, get_link_to_form("Warehouse", customer_warehouse))
|
||||
)
|
||||
else:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Source Warehouse must be same as Customer Warehouse {1} from the linked Subcontracting Inward Order"
|
||||
).format(item.idx, bold(customer_warehouse))
|
||||
).format(item.idx, get_link_to_form("Warehouse", customer_warehouse))
|
||||
)
|
||||
|
||||
def validate_serial_batch_for_return_or_delivery(self):
|
||||
@@ -249,7 +418,10 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Serial No(s) {1} are not a part of the linked Subcontracting Inward Order. Please select valid Serial No(s)."
|
||||
).format(item.idx, ", ".join([bold(sn) for sn in incorrect_serial_nos]))
|
||||
).format(
|
||||
item.idx,
|
||||
", ".join([get_link_to_form("Serial No", sn) for sn in incorrect_serial_nos]),
|
||||
)
|
||||
)
|
||||
if batch_list and (
|
||||
incorrect_batch_nos := [bn for bn in batch_list if bn not in list(batch_nos.keys())]
|
||||
@@ -257,7 +429,10 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Batch No(s) {1} is not a part of the linked Subcontracting Inward Order. Please select valid Batch No(s)."
|
||||
).format(item.idx, ", ".join([bold(bn) for bn in incorrect_batch_nos]))
|
||||
).format(
|
||||
item.idx,
|
||||
", ".join([get_link_to_form("Batch No", bn) for bn in incorrect_batch_nos]),
|
||||
)
|
||||
)
|
||||
|
||||
def get_serial_nos_and_batches_from_sres(self, scio_detail, only_pending=True):
|
||||
@@ -302,7 +477,7 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Cannot cancel this Stock Entry as returned quantity cannot be greater than delivered quantity for Item {1} in the linked Subcontracting Inward Order"
|
||||
).format(item.idx, bold(item.item_code))
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||
)
|
||||
|
||||
def validate_delivery_on_save(self):
|
||||
@@ -315,8 +490,8 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Item {1} is not a part of Subcontracting Inward Order {2}").format(
|
||||
item.idx,
|
||||
bold(item.item_code),
|
||||
bold(self.subcontracting_inward_order),
|
||||
get_link_to_form("Item", item.item_code),
|
||||
get_link_to_form("Subcontracting Inward Order", self.subcontracting_inward_order),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -359,7 +534,7 @@ class SubcontractingInwardController:
|
||||
"Row #{0}: Quantity of Item {1} cannot be more than {2} {3} against Subcontracting Inward Order {4}"
|
||||
).format(
|
||||
item.idx,
|
||||
bold(item.item_code),
|
||||
get_link_to_form("Item", item.item_code),
|
||||
bold(max_allowed_qty),
|
||||
bold(
|
||||
frappe.get_cached_value(
|
||||
@@ -370,7 +545,7 @@ class SubcontractingInwardController:
|
||||
"stock_uom",
|
||||
)
|
||||
),
|
||||
bold(self.subcontracting_inward_order),
|
||||
get_link_to_form("Subcontracting Inward Order", self.subcontracting_inward_order),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -400,7 +575,6 @@ class SubcontractingInwardController:
|
||||
& (table.voucher_type == "Subcontracting Inward Order")
|
||||
& (table.voucher_no == self.subcontracting_inward_order)
|
||||
& (table.voucher_detail_no == item.scio_detail)
|
||||
& (table.warehouse == item.s_warehouse)
|
||||
)
|
||||
.orderby(table.creation)
|
||||
)
|
||||
@@ -522,7 +696,7 @@ class SubcontractingInwardController:
|
||||
) < scio_rm_item.work_order_qty:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Work Order exists against full or partial quantity of Item {1}").format(
|
||||
item.idx, bold(item.item_code)
|
||||
item.idx, get_link_to_form("Item", item.item_code)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -561,7 +735,7 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Cannot cancel this Manufacturing Stock Entry as quantity of Scrap Item {1} produced cannot be less than quantity delivered."
|
||||
).format(item.idx, bold(item.item_code))
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||
)
|
||||
else:
|
||||
scio_rm_item = frappe.get_value(
|
||||
@@ -570,7 +744,7 @@ class SubcontractingInwardController:
|
||||
"docstatus": 1,
|
||||
"rm_item_code": item.item_code,
|
||||
"warehouse": item.s_warehouse,
|
||||
"reference_name": fg_item_name, # if this field is set then the additional item is NOT customer provided
|
||||
"is_customer_provided_item": 0,
|
||||
"is_additional_item": 1,
|
||||
},
|
||||
["consumed_qty", "billed_qty", "returned_qty"],
|
||||
@@ -582,7 +756,7 @@ class SubcontractingInwardController:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: Cannot cancel this Manufacturing Stock Entry as billed quantity of Item {1} cannot be greater than consumed quantity."
|
||||
).format(item.idx, bold(item.item_code))
|
||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||
)
|
||||
|
||||
def update_inward_order_item(self):
|
||||
@@ -638,7 +812,9 @@ class SubcontractingInwardController:
|
||||
data = frappe._dict()
|
||||
for item in self.items:
|
||||
if item.scio_detail:
|
||||
data[item.scio_detail] = item.transfer_qty if self._action == "submit" else -item.transfer_qty
|
||||
data[item.scio_detail] = frappe._dict(
|
||||
{"transfer_qty": item.transfer_qty, "rate": item.customer_provided_item_cost}
|
||||
)
|
||||
else:
|
||||
scio_rm = frappe.new_doc(
|
||||
"Subcontracting Inward Order Received Item",
|
||||
@@ -657,9 +833,16 @@ class SubcontractingInwardController:
|
||||
consumed_qty=0,
|
||||
work_order_qty=0,
|
||||
returned_qty=0,
|
||||
rate=item.customer_provided_item_cost,
|
||||
is_customer_provided_item=True,
|
||||
is_additional_item=True,
|
||||
reference_name=item.against_fg,
|
||||
main_item_code=frappe.get_cached_value(
|
||||
"Subcontracting Inward Order Item", item.against_fg, "item_code"
|
||||
),
|
||||
)
|
||||
scio_rm.insert()
|
||||
scio_rm.submit()
|
||||
item.db_set("scio_detail", scio_rm.name)
|
||||
|
||||
if data:
|
||||
@@ -670,39 +853,47 @@ class SubcontractingInwardController:
|
||||
"name": ["in", list(data.keys())],
|
||||
"docstatus": 1,
|
||||
},
|
||||
fields=["name", "required_qty", "received_qty"],
|
||||
fields=["rate", "name", "required_qty", "received_qty"],
|
||||
)
|
||||
|
||||
deleted_docs = []
|
||||
table = frappe.qb.DocType("Subcontracting Inward Order Received Item")
|
||||
case_expr = Case()
|
||||
case_expr_qty, case_expr_rate = Case(), Case()
|
||||
for d in result:
|
||||
d.received_qty += data[d.name]
|
||||
d.received_qty += (
|
||||
data[d.name].transfer_qty if self._action == "submit" else -data[d.name].transfer_qty
|
||||
)
|
||||
d.rate += data[d.name].rate if self._action == "submit" else -data[d.name].rate
|
||||
|
||||
if not d.required_qty and not d.received_qty:
|
||||
deleted_docs.append(d.name)
|
||||
frappe.delete_doc("Subcontracting Inward Order Received Item", d.name)
|
||||
else:
|
||||
case_expr = case_expr.when(table.name == d.name, d.received_qty)
|
||||
case_expr_qty = case_expr_qty.when(table.name == d.name, d.received_qty)
|
||||
case_expr_rate = case_expr_rate.when(table.name == d.name, d.rate)
|
||||
|
||||
if len(list(set(data.keys()) - set(deleted_docs))) > 0:
|
||||
frappe.qb.update(table).set(table.received_qty, case_expr).where(
|
||||
(table.name.isin(list(set(data.keys()) - set(deleted_docs)))) & (table.docstatus == 1)
|
||||
).run()
|
||||
if final_list := list(set(data.keys()) - set(deleted_docs)):
|
||||
frappe.qb.update(table).set(table.received_qty, case_expr_qty).set(
|
||||
table.rate, case_expr_rate
|
||||
).where((table.name.isin(final_list)) & (table.docstatus == 1)).run()
|
||||
|
||||
def update_inward_order_received_items_for_manufacture(self):
|
||||
customer_warehouse = frappe.get_cached_value(
|
||||
"Subcontracting Inward Order", self.subcontracting_inward_order, "customer_warehouse"
|
||||
)
|
||||
items = [item for item in self.items if not item.is_finished_item and not item.is_scrap_item]
|
||||
item_code_wh = frappe._dict(
|
||||
{
|
||||
(item.item_code, item.s_warehouse): item.transfer_qty
|
||||
if self._action == "submit"
|
||||
else -item.transfer_qty
|
||||
(
|
||||
item.item_code,
|
||||
customer_warehouse
|
||||
if frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item")
|
||||
else item.s_warehouse,
|
||||
): item.transfer_qty if self._action == "submit" else -item.transfer_qty
|
||||
for item in items
|
||||
}
|
||||
)
|
||||
item_codes, warehouses = zip(*list(item_code_wh.keys()), strict=True)
|
||||
item_codes = list(item_codes)
|
||||
warehouses = list(warehouses)
|
||||
|
||||
table = frappe.qb.DocType("Subcontracting Inward Order Received Item")
|
||||
data = (
|
||||
@@ -712,22 +903,21 @@ class SubcontractingInwardController:
|
||||
table.rm_item_code,
|
||||
table.is_customer_provided_item,
|
||||
table.consumed_qty,
|
||||
table.required_qty,
|
||||
table.warehouse,
|
||||
table.is_additional_item,
|
||||
)
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.rm_item_code.isin(item_codes))
|
||||
& ((table.warehouse.isin(warehouses)) | (table.warehouse.isnull()))
|
||||
& (table.rm_item_code.isin(list(set(item_codes))))
|
||||
& (
|
||||
(table.warehouse.isin(list(set(warehouses)))) | (table.warehouse.isnull())
|
||||
) # warehouse will always be null for non additional self procured raw materials
|
||||
& (table.parent == self.subcontracting_inward_order)
|
||||
& (
|
||||
(
|
||||
table.reference_name
|
||||
== frappe.get_cached_value(
|
||||
"Work Order", self.work_order, "subcontracting_inward_order_item"
|
||||
)
|
||||
table.reference_name
|
||||
== frappe.get_cached_value(
|
||||
"Work Order", self.work_order, "subcontracting_inward_order_item"
|
||||
)
|
||||
| (table.reference_name.isnull())
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -745,25 +935,26 @@ class SubcontractingInwardController:
|
||||
used_item_wh.append((d.rm_item_code, d.warehouse))
|
||||
|
||||
qty = d.consumed_qty + item_code_wh[(d.rm_item_code, d.warehouse)]
|
||||
if qty or d.is_customer_provided_item:
|
||||
if qty or d.is_customer_provided_item or not d.is_additional_item:
|
||||
case_expr = case_expr.when((table.name == d.name), qty)
|
||||
else:
|
||||
deleted_docs.append(d.name)
|
||||
frappe.delete_doc("Subcontracting Inward Order Received Item", d.name)
|
||||
|
||||
final_name_list = list(set([d.name for d in data]) - set(deleted_docs))
|
||||
if len(final_name_list) > 0:
|
||||
if final_list := list(set([d.name for d in data]) - set(deleted_docs)):
|
||||
frappe.qb.update(table).set(table.consumed_qty, case_expr).where(
|
||||
(table.name.isin(final_name_list)) & (table.docstatus == 1)
|
||||
(table.name.isin(final_list)) & (table.docstatus == 1)
|
||||
).run()
|
||||
|
||||
main_item_code = next(fg for fg in self.items if fg.is_finished_item).item_code
|
||||
for extra_item in [
|
||||
item
|
||||
for item in items
|
||||
if (item.item_code, item.s_warehouse) not in [(d.rm_item_code, d.warehouse) for d in data]
|
||||
if not frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item")
|
||||
and (item.item_code, item.s_warehouse)
|
||||
not in [(d.rm_item_code, d.warehouse) for d in data if not d.is_customer_provided_item]
|
||||
]:
|
||||
frappe.new_doc(
|
||||
doc = frappe.new_doc(
|
||||
"Subcontracting Inward Order Received Item",
|
||||
parent=self.subcontracting_inward_order,
|
||||
parenttype="Subcontracting Inward Order",
|
||||
@@ -783,7 +974,9 @@ class SubcontractingInwardController:
|
||||
consumed_qty=extra_item.transfer_qty,
|
||||
warehouse=extra_item.s_warehouse,
|
||||
is_additional_item=True,
|
||||
).insert()
|
||||
)
|
||||
doc.insert()
|
||||
doc.submit()
|
||||
|
||||
def update_inward_order_scrap_items(self):
|
||||
if (scio := self.subcontracting_inward_order) and self.purpose == "Manufacture":
|
||||
@@ -835,8 +1028,9 @@ class SubcontractingInwardController:
|
||||
table.name == value.name, value.produced_qty + scrap_items.get(key)
|
||||
)
|
||||
|
||||
final_list = list(set([v.name for v in scrap_item_dict.values()]) - set(deleted_docs))
|
||||
if len(final_list) > 0:
|
||||
if final_list := list(
|
||||
set([v.name for v in scrap_item_dict.values()]) - set(deleted_docs)
|
||||
):
|
||||
frappe.qb.update(table).set(table.produced_qty, case_expr).where(
|
||||
(table.name.isin(final_list)) & (table.docstatus == 1)
|
||||
).run()
|
||||
@@ -847,7 +1041,7 @@ class SubcontractingInwardController:
|
||||
for item in scrap_items_list
|
||||
if (item.item_code, item.t_warehouse) not in [(d.item_code, d.warehouse) for d in result]
|
||||
]:
|
||||
frappe.new_doc(
|
||||
doc = frappe.new_doc(
|
||||
"Subcontracting Inward Order Scrap Item",
|
||||
parent=scio,
|
||||
parenttype="Subcontracting Inward Order",
|
||||
@@ -862,7 +1056,9 @@ class SubcontractingInwardController:
|
||||
reference_name=frappe.get_value(
|
||||
"Work Order", self.work_order, "subcontracting_inward_order_item"
|
||||
),
|
||||
).insert()
|
||||
)
|
||||
doc.insert()
|
||||
doc.submit()
|
||||
|
||||
def cancel_stock_reservation_entries_for_inward(self):
|
||||
if self.purpose == "Receive from Customer":
|
||||
@@ -1022,3 +1218,17 @@ class SubcontractingInwardController:
|
||||
)
|
||||
|
||||
update_subcontracting_inward_order_status(self.subcontracting_inward_order)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_fg_reference_names(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.get_all(
|
||||
"Subcontracting Inward Order Item",
|
||||
limit_start=start,
|
||||
limit_page_length=page_len,
|
||||
filters={"parent": filters.get("parent"), "item_code": ("like", "%%%s%%" % txt), "docstatus": 1},
|
||||
fields=["name", "item_code", "delivery_warehouse"],
|
||||
as_list=True,
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user