feat: SCR return
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 9642c24..ca968e9 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -77,7 +77,7 @@
if doc.doctype != "Purchase Invoice":
select_fields += ",serial_no, batch_no"
- if doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
+ if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
select_fields += ",rejected_qty, received_qty"
for d in frappe.db.sql(
@@ -161,7 +161,7 @@
def validate_quantity(doc, args, ref, valid_items, already_returned_items):
fields = ["stock_qty"]
- if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
+ if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
fields.extend(["received_qty", "rejected_qty"])
already_returned_data = already_returned_items.get(args.item_code) or {}
@@ -224,7 +224,7 @@
if ref_item_row.get("rate", 0) > item_dict["rate"]:
item_dict["rate"] = ref_item_row.get("rate", 0)
- if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt"]:
+ if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
item_dict["received_qty"] += ref_item_row.received_qty
item_dict["rejected_qty"] += ref_item_row.rejected_qty
@@ -239,7 +239,7 @@
def get_already_returned_items(doc):
column = "child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty"
- if doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
+ if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
@@ -281,17 +281,21 @@
child_doctype = doctype + " Item"
reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
- if doctype in ("Purchase Receipt", "Purchase Invoice"):
+ if doctype in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
party_type = "supplier"
else:
party_type = "customer"
fields = [
"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
- "sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype),
]
- if doctype in ("Purchase Receipt", "Purchase Invoice"):
+ if doctype != "Subcontracting Receipt":
+ fields += [
+ "sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype),
+ ]
+
+ if doctype in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
fields += [
"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
@@ -397,7 +401,7 @@
if serial_nos:
target_doc.serial_no = "\n".join(serial_nos)
- if doctype == "Purchase Receipt":
+ if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
returned_qty_map = get_returned_qty_map_for_row(
source_parent.name, source_parent.supplier, source_doc.name, doctype
)
@@ -409,15 +413,24 @@
)
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
- target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0))
- target_doc.received_stock_qty = -1 * flt(
- source_doc.received_stock_qty - (returned_qty_map.get("received_stock_qty") or 0)
- )
+ if hasattr(target_doc, "stock_qty"):
+ target_doc.stock_qty = -1 * flt(
+ source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0)
+ )
+ target_doc.received_stock_qty = -1 * flt(
+ source_doc.received_stock_qty - (returned_qty_map.get("received_stock_qty") or 0)
+ )
- target_doc.purchase_order = source_doc.purchase_order
- target_doc.purchase_order_item = source_doc.purchase_order_item
- target_doc.rejected_warehouse = source_doc.rejected_warehouse
- target_doc.purchase_receipt_item = source_doc.name
+ if doctype == "Subcontracting Receipt":
+ target_doc.subcontracting_order = source_doc.subcontracting_order
+ target_doc.subcontracting_order_item = source_doc.subcontracting_order_item
+ target_doc.rejected_warehouse = source_doc.rejected_warehouse
+ target_doc.subcontracting_receipt_item = source_doc.name
+ else:
+ target_doc.purchase_order = source_doc.purchase_order
+ target_doc.purchase_order_item = source_doc.purchase_order_item
+ target_doc.rejected_warehouse = source_doc.rejected_warehouse
+ target_doc.purchase_receipt_item = source_doc.name
elif doctype == "Purchase Invoice":
returned_qty_map = get_returned_qty_map_for_row(
@@ -529,7 +542,7 @@
item_row,
)
- if voucher_type in ("Purchase Receipt", "Purchase Invoice"):
+ if voucher_type in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
select_field = "incoming_rate"
else:
select_field = "abs(stock_value_difference / actual_qty)"
@@ -564,6 +577,7 @@
"Purchase Invoice": "purchase_invoice_item",
"Delivery Note": "dn_detail",
"Sales Invoice": "sales_invoice_item",
+ "Subcontracting Receipt": "subcontracting_receipt_item",
}
return return_against_item_fields[voucher_type]
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index b98f979..87a19a1 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -76,7 +76,7 @@
}, __("View"));
}
- if (!frm.doc.is_return && frm.doc.docstatus == 1) {
+ if (!frm.doc.is_return && frm.doc.docstatus == 1 && frm.doc.per_returned < 100) {
frm.add_custom_button('Subcontract Return', function () {
frappe.model.open_mapped_doc({
method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_subcontract_return',
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 8680311..dd17902 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -21,6 +21,7 @@
set_backflush_based_on,
)
from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import make_subcontracting_receipt
@@ -272,6 +273,64 @@
for row in scr.supplied_items:
self.assertEqual(transferred_batch.get(row.batch_no), row.consumed_qty)
+ def test_subcontracting_order_partial_return(self):
+ sco = get_subcontracting_order()
+ 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),
+ )
+ scr1 = make_subcontracting_receipt(sco.name)
+ scr1.save()
+ scr1.submit()
+
+ scr1_return = make_return_subcontracting_receipt(scr_name=scr1.name, qty=-3)
+ scr1.load_from_db()
+ self.assertEqual(scr1_return.status, "Return")
+ self.assertEqual(scr1.items[0].returned_qty, 3)
+
+ scr2_return = make_return_subcontracting_receipt(scr_name=scr1.name, qty=-7)
+ scr1.load_from_db()
+ self.assertEqual(scr2_return.status, "Return")
+ self.assertEqual(scr1.status, "Return Issued")
+ self.assertEqual(scr1.items[0].returned_qty, 10)
+
+ def test_subcontracting_order_over_return(self):
+ sco = get_subcontracting_order()
+ 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),
+ )
+ scr1 = make_subcontracting_receipt(sco.name)
+ scr1.save()
+ scr1.submit()
+
+ from erpnext.controllers.status_updater import OverAllowanceError
+ args = frappe._dict(scr_name=scr1.name, qty=-15)
+ self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args)
+
+
+def make_return_subcontracting_receipt(**args):
+ args = frappe._dict(args)
+ return_doc = make_return_doc("Subcontracting Receipt", args.scr_name)
+ return_doc.supplier_warehouse = args.supplier_warehouse or args.warehouse or "_Test Warehouse 1 - _TC"
+
+ if args.qty:
+ for item in return_doc.items:
+ item.qty = args.qty
+
+ if not args.do_not_save:
+ return_doc.save()
+ if not args.do_not_submit:
+ return_doc.submit()
+
+ return_doc.load_from_db()
+ return return_doc
def get_items(**args):
args = frappe._dict(args)