mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-30 02:14:48 +00:00
feat: SCR return
This commit is contained in:
@@ -77,7 +77,7 @@ def validate_returned_items(doc):
|
|||||||
if doc.doctype != "Purchase Invoice":
|
if doc.doctype != "Purchase Invoice":
|
||||||
select_fields += ",serial_no, batch_no"
|
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"
|
select_fields += ",rejected_qty, received_qty"
|
||||||
|
|
||||||
for d in frappe.db.sql(
|
for d in frappe.db.sql(
|
||||||
@@ -161,7 +161,7 @@ def validate_returned_items(doc):
|
|||||||
|
|
||||||
def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
||||||
fields = ["stock_qty"]
|
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"])
|
fields.extend(["received_qty", "rejected_qty"])
|
||||||
|
|
||||||
already_returned_data = already_returned_items.get(args.item_code) or {}
|
already_returned_data = already_returned_items.get(args.item_code) or {}
|
||||||
@@ -224,7 +224,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
|
|||||||
if ref_item_row.get("rate", 0) > item_dict["rate"]:
|
if ref_item_row.get("rate", 0) > item_dict["rate"]:
|
||||||
item_dict["rate"] = ref_item_row.get("rate", 0)
|
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["received_qty"] += ref_item_row.received_qty
|
||||||
item_dict["rejected_qty"] += ref_item_row.rejected_qty
|
item_dict["rejected_qty"] += ref_item_row.rejected_qty
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
|
|||||||
|
|
||||||
def get_already_returned_items(doc):
|
def get_already_returned_items(doc):
|
||||||
column = "child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty"
|
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,
|
column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
|
||||||
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
|
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
|
||||||
|
|
||||||
@@ -281,17 +281,21 @@ def get_returned_qty_map_for_row(return_against, party, row_name, doctype):
|
|||||||
child_doctype = doctype + " Item"
|
child_doctype = doctype + " Item"
|
||||||
reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
|
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"
|
party_type = "supplier"
|
||||||
else:
|
else:
|
||||||
party_type = "customer"
|
party_type = "customer"
|
||||||
|
|
||||||
fields = [
|
fields = [
|
||||||
"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
|
"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 += [
|
fields += [
|
||||||
"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
|
"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
|
||||||
"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
|
"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
|
||||||
@@ -397,7 +401,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
if serial_nos:
|
if serial_nos:
|
||||||
target_doc.serial_no = "\n".join(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(
|
returned_qty_map = get_returned_qty_map_for_row(
|
||||||
source_parent.name, source_parent.supplier, source_doc.name, doctype
|
source_parent.name, source_parent.supplier, source_doc.name, doctype
|
||||||
)
|
)
|
||||||
@@ -409,15 +413,24 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
)
|
)
|
||||||
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
|
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))
|
if hasattr(target_doc, "stock_qty"):
|
||||||
target_doc.received_stock_qty = -1 * flt(
|
target_doc.stock_qty = -1 * flt(
|
||||||
source_doc.received_stock_qty - (returned_qty_map.get("received_stock_qty") or 0)
|
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
|
if doctype == "Subcontracting Receipt":
|
||||||
target_doc.purchase_order_item = source_doc.purchase_order_item
|
target_doc.subcontracting_order = source_doc.subcontracting_order
|
||||||
target_doc.rejected_warehouse = source_doc.rejected_warehouse
|
target_doc.subcontracting_order_item = source_doc.subcontracting_order_item
|
||||||
target_doc.purchase_receipt_item = source_doc.name
|
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":
|
elif doctype == "Purchase Invoice":
|
||||||
returned_qty_map = get_returned_qty_map_for_row(
|
returned_qty_map = get_returned_qty_map_for_row(
|
||||||
@@ -529,7 +542,7 @@ def get_rate_for_return(
|
|||||||
item_row,
|
item_row,
|
||||||
)
|
)
|
||||||
|
|
||||||
if voucher_type in ("Purchase Receipt", "Purchase Invoice"):
|
if voucher_type in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
|
||||||
select_field = "incoming_rate"
|
select_field = "incoming_rate"
|
||||||
else:
|
else:
|
||||||
select_field = "abs(stock_value_difference / actual_qty)"
|
select_field = "abs(stock_value_difference / actual_qty)"
|
||||||
@@ -564,6 +577,7 @@ def get_return_against_item_fields(voucher_type):
|
|||||||
"Purchase Invoice": "purchase_invoice_item",
|
"Purchase Invoice": "purchase_invoice_item",
|
||||||
"Delivery Note": "dn_detail",
|
"Delivery Note": "dn_detail",
|
||||||
"Sales Invoice": "sales_invoice_item",
|
"Sales Invoice": "sales_invoice_item",
|
||||||
|
"Subcontracting Receipt": "subcontracting_receipt_item",
|
||||||
}
|
}
|
||||||
return return_against_item_fields[voucher_type]
|
return return_against_item_fields[voucher_type]
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
|||||||
}, __("View"));
|
}, __("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 () {
|
frm.add_custom_button('Subcontract Return', function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_subcontract_return',
|
method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_subcontract_return',
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from erpnext.controllers.tests.test_subcontracting_controller import (
|
|||||||
set_backflush_based_on,
|
set_backflush_based_on,
|
||||||
)
|
)
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
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.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import make_subcontracting_receipt
|
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import make_subcontracting_receipt
|
||||||
|
|
||||||
@@ -272,6 +273,64 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
|||||||
for row in scr.supplied_items:
|
for row in scr.supplied_items:
|
||||||
self.assertEqual(transferred_batch.get(row.batch_no), row.consumed_qty)
|
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):
|
def get_items(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
Reference in New Issue
Block a user