From 2add72ba3675ac507f87bed4054b636a7af5d805 Mon Sep 17 00:00:00 2001 From: Sudharsanan11 Date: Sun, 13 Sep 2026 12:12:10 +0530 Subject: [PATCH] fix: resolve conflicts --- .../test_subcontracting_order.py | 61 ------ .../doctype/subcontracting_receipt/mapper.py | 174 ------------------ .../subcontracting_receipt.py | 6 +- 3 files changed, 3 insertions(+), 238 deletions(-) delete mode 100644 erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py index 98e1d776b32..1b84cf75a1d 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py @@ -113,8 +113,6 @@ class TestSubcontractingOrder(ERPNextTestSuite): sco.load_from_db() self.assertEqual(sco.status, "Partially Received") -<<<<<<< HEAD -======= def test_project_is_carried_over_from_purchase_order(self): project = make_project({"project_name": "_Test SCO Project"}).name po = make_subcontracted_purchase_order(project) @@ -133,65 +131,6 @@ class TestSubcontractingOrder(ERPNextTestSuite): sco.items[0].project = other_project self.assertRaises(frappe.ValidationError, sco.save) - def test_sco_requires_a_subcontracting_purchase_order(self): - sco = get_subcontracting_order(do_not_save=1) - sco.purchase_order = None - self.assertRaises(frappe.ValidationError, sco.validate_purchase_order_for_subcontracting) - - def test_service_item_must_be_non_stock(self): - sco = get_subcontracting_order(do_not_submit=1) - sco.service_items[0].item_code = "_Test Item" # a stock item - self.assertRaises(frappe.ValidationError, sco.validate_service_items) - - def test_reserve_warehouse_must_differ_from_supplier_warehouse(self): - sco = get_subcontracting_order(do_not_submit=1) - sco.supplied_items[0].reserve_warehouse = sco.supplier_warehouse - self.assertRaises(frappe.ValidationError, sco.validate_supplied_items) - - def test_subcontracting_receipt_applies_bom_process_loss(self): - sco = get_subcontracting_order() - frappe.db.set_value("BOM", sco.items[0].bom, "process_loss_percentage", 10) - - scr = make_subcontracting_receipt(sco.name) - - # 10% of the ordered 10 qty is lost in processing - self.assertEqual(scr.items[0].received_qty, 10) - self.assertEqual(scr.items[0].process_loss_qty, 1) - self.assertEqual(scr.items[0].qty, 9) - - def test_service_cost_is_matched_by_purchase_order_item(self): - service_items = [ - { - "warehouse": "_Test Warehouse - _TC", - "item_code": "Subcontracted Service Item 7", - "qty": 10, - "rate": 100, - "fg_item": "Subcontracted Item SA7", - "fg_item_qty": 10, - }, - { - "warehouse": "_Test Warehouse - _TC", - "item_code": "Subcontracted Service Item 1", - "qty": 10, - "rate": 200, - "fg_item": "Subcontracted Item SA1", - "fg_item_qty": 10, - }, - ] - sco = get_subcontracting_order(service_items=service_items) - expected = {item.purchase_order_item: item.service_cost_per_qty for item in sco.items} - - # The two finished goods have distinct service costs, so a position-based pairing would swap them - self.assertEqual(len(set(expected.values())), 2) - - # Service costs must follow purchase_order_item, not list position - sco.service_items.reverse() - sco.calculate_service_costs() - - for item in sco.items: - self.assertEqual(item.service_cost_per_qty, expected[item.purchase_order_item]) - ->>>>>>> fe25746 (fix(subcontracting): validate project across the subcontracting flow (#58965)) def test_make_rm_stock_entry(self): sco = get_subcontracting_order() rm_items = get_rm_items(sco.supplied_items) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py b/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py deleted file mode 100644 index ae1b7653d4f..00000000000 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/mapper.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc -from frappe.utils import flt, get_link_to_form - - -@frappe.whitelist() -def make_subcontract_return_against_rejected_warehouse(source_name: str): - from erpnext.controllers.sales_and_purchase_return import make_return_doc - - return make_return_doc("Subcontracting Receipt", source_name, return_against_rejected_qty=True) - - -@frappe.whitelist() -def make_subcontract_return(source_name: str, target_doc: str | dict | Document | None = None): - from erpnext.controllers.sales_and_purchase_return import make_return_doc - - return make_return_doc("Subcontracting Receipt", source_name, target_doc) - - -@frappe.whitelist(methods=["POST"]) -def make_purchase_receipt( - source_name: Document | str, - target_doc: str | dict | Document | None = None, - save: bool = False, - submit: bool = False, - notify: bool = False, -): - if isinstance(source_name, str): - source_doc = frappe.get_doc("Subcontracting Receipt", source_name) - else: - source_doc = source_name - - if source_doc.is_return: - return - - po_sr_item_dict = {} - po_name = None - for item in source_doc.items: - if not item.purchase_order: - continue - - if not po_name: - po_name = item.purchase_order - - po_sr_item_dict[item.purchase_order_item] = { - "qty": flt(item.qty), - "rejected_qty": flt(item.rejected_qty), - "warehouse": item.warehouse, - "rejected_warehouse": item.rejected_warehouse, - "subcontracting_receipt_item": item.name, - } - - if not po_name: - frappe.throw( - _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( - source_doc.name - ) - ) - - def update_item(obj, target, source_parent): - sr_item_details = po_sr_item_dict.get(obj.name) - ratio = flt(obj.qty) / flt(obj.fg_item_qty) - - target.update( - { - "qty": ratio * sr_item_details["qty"], - "rejected_qty": ratio * sr_item_details["rejected_qty"], - "warehouse": sr_item_details["warehouse"], - "rejected_warehouse": sr_item_details["rejected_warehouse"], - "subcontracting_receipt_item": sr_item_details["subcontracting_receipt_item"], - } - ) - - def post_process(source, target): - target.set_missing_values() - target.update( - { - "posting_date": source_doc.posting_date, - "posting_time": source_doc.posting_time, - "subcontracting_receipt": source_doc.name, - "supplier_warehouse": source_doc.supplier_warehouse, - "is_subcontracted": 1, - "currency": frappe.get_cached_value("Company", target.company, "default_currency"), - } - ) - - target_doc = get_mapped_doc( - "Purchase Order", - po_name, - { - "Purchase Order": { - "doctype": "Purchase Receipt", - "field_map": {"supplier_warehouse": "supplier_warehouse"}, - "validation": { - "docstatus": ["=", 1], - }, - }, - "Purchase Order Item": { - "doctype": "Purchase Receipt Item", - "field_map": { - "name": "purchase_order_item", - "parent": "purchase_order", - "bom": "bom", - }, - "postprocess": update_item, - "condition": lambda doc: doc.name in po_sr_item_dict, - }, - "Purchase Taxes and Charges": { - "doctype": "Purchase Taxes and Charges", - "reset_value": True, - # for POs created in earlier version with tax_withholding_row - "condition": lambda doc: not doc.is_tax_withholding_account, - }, - }, - postprocess=post_process, - ) - - if not target_doc.get("items"): - add_po_items_to_pr(source_doc, target_doc) - - if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): - target_doc.save() - - if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): - frappe.db.savepoint("submit_subcontracting_receipt") - try: - target_doc.submit() - except Exception as e: - frappe.db.rollback(save_point="submit_subcontracting_receipt") - target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) - - if notify: - frappe.msgprint( - _("Purchase Receipt {0} created.").format( - get_link_to_form(target_doc.doctype, target_doc.name) - ), - indicator="green", - alert=True, - ) - - return target_doc - - -def add_po_items_to_pr(scr_doc, target_doc): - fg_items = {(item.item_code, item.purchase_order): item.qty for item in scr_doc.items} - - for (item_code, po_name), fg_qty in fg_items.items(): - po_doc = frappe.get_doc("Purchase Order", po_name) - for item in po_doc.items: - if item.fg_item != item_code: - continue - - qty = (item.stock_qty - item.received_qty) * fg_qty / item.fg_item_qty - if qty: - target_doc.append( - "items", - { - "item_code": item.item_code, - "item_name": item.item_name, - "description": item.description, - "qty": qty, - "rate": item.rate, - "warehouse": item.warehouse, - "purchase_order": item.parent, - "purchase_order_item": item.name, - "project": item.project, - "cost_center": item.cost_center, - }, - ) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 7911e4ca987..b681dd777fe 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -165,13 +165,12 @@ class SubcontractingReceipt(SubcontractingController): self.set_supplied_items_cost_center() self.set_supplied_items_inventory_dimensions() -<<<<<<< HEAD # SubcontractingController.validate() does not call super() for Subcontracting Receipt, so # the shared mandatory inventory dimension check must be invoked explicitly here. It runs # last so auto-populated supplied-item dimensions (set_supplied_items_inventory_dimensions) # are already in place. self.validate_inventory_dimension_mandatory() -======= + def validate_with_previous_doc(self): super().validate_with_previous_doc( { @@ -189,7 +188,6 @@ class SubcontractingReceipt(SubcontractingController): }, } ) ->>>>>>> fe25746 (fix(subcontracting): validate project across the subcontracting flow (#58965)) def on_submit(self): self.validate_closed_subcontracting_order() @@ -1232,5 +1230,7 @@ def add_po_items_to_pr(scr_doc, target_doc): "warehouse": item.warehouse, "purchase_order": item.parent, "purchase_order_item": item.name, + "project": item.project, + "cost_center": item.cost_center, }, )