From 8ed9210aba149596a9ddb53b1ad0f39c40f47c11 Mon Sep 17 00:00:00 2001 From: Chethan Thopaiah Date: Mon, 27 Apr 2026 15:38:16 +0200 Subject: [PATCH 1/3] Bug 620643: [Subcontracting] Fix deletion of Subcontractor Prices when Work Center or Item is deleted SetCurrentKey("Work Center No.") and SetCurrentKey("Item No.") in SubcWorkCenterExtension and SubcItemExtension failed at runtime because neither field was the first field of any key on the SubcontractorPrice table. This caused a runtime error whenever a Work Center or Item with associated Subcontractor Prices was deleted, preventing the deletion from completing. Added secondary keys Key03 (Work Center No.) and Key04 (Item No.) to the SubcontractorPrice table so the SetCurrentKey calls resolve correctly and deletion cascades as intended. Added tests DeleteWorkCenterWithPricesDeletesRelatedPrices and DeleteItemWithPricesDeletesRelatedPrices in SubcSubcontractingTest to cover both scenarios. Co-Authored-By: Claude Sonnet 4.6 --- .../Tables/SubcontractorPrice.Table.al | 6 ++ .../Tests/SubcSubcontractingTest.Codeunit.al | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al b/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al index 563d691730..5345b523cd 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al @@ -127,6 +127,12 @@ table 99001500 "Subcontractor Price" key(Key02; "Vendor No.", "Item No.", "Work Center No.", "Variant Code", "Unit of Measure Code", "Currency Code") { } + key(Key03; "Work Center No.") + { + } + key(Key04; "Item No.") + { + } } fieldgroups { diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al index ecb610a1a6..7b7e7ac76a 100644 --- a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al @@ -1537,6 +1537,64 @@ Comment = '|%1 = Transfer Order No.'; Assert.AreEqual(SubPurchaseLineFactbox.SubcontractingPrices.Value, Format(SubcontractorPrice.Count()), ''); end; + [Test] + [Scope('OnPrem')] + procedure DeleteWorkCenterWithPricesDeletesRelatedPrices() + var + Item: Record Item; + SubcontractorPrice: Record "Subcontractor Price"; + WorkCenter: Record "Work Center"; + WorkCenterNo: Code[20]; + begin + // [SCENARIO 620643] Deleting a Work Center deletes all associated Subcontractor Prices + + // [GIVEN] A work center with a subcontractor and multiple Subcontractor Prices + Initialize(); + LibraryMfgManagement.CreateWorkCenterWithCalendar(WorkCenter, 0); + WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency('')); + WorkCenter.Modify(true); + LibraryInventory.CreateItem(Item); + WorkCenterNo := WorkCenter."No."; + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenterNo, WorkCenter."Subcontractor No.", Item."No.", '', '', WorkDate(), '', 0, ''); + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenterNo, WorkCenter."Subcontractor No.", Item."No.", '', '', WorkDate(), '', 10, ''); + + // [WHEN] The work center is deleted + WorkCenter.Delete(true); + + // [THEN] All Subcontractor Prices for the work center are deleted + SubcontractorPrice.SetRange("Work Center No.", WorkCenterNo); + Assert.IsTrue(SubcontractorPrice.IsEmpty(), 'Subcontractor prices must be deleted when work center is deleted'); + end; + + [Test] + [Scope('OnPrem')] + procedure DeleteItemWithPricesDeletesRelatedPrices() + var + Item: Record Item; + SubcontractorPrice: Record "Subcontractor Price"; + WorkCenter: Record "Work Center"; + ItemNo: Code[20]; + begin + // [SCENARIO 620643] Deleting an Item deletes all associated Subcontractor Prices + + // [GIVEN] An item with multiple Subcontractor Prices + Initialize(); + LibraryMfgManagement.CreateWorkCenterWithCalendar(WorkCenter, 0); + WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency('')); + WorkCenter.Modify(true); + LibraryInventory.CreateItem(Item); + ItemNo := Item."No."; + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenter."No.", WorkCenter."Subcontractor No.", ItemNo, '', '', WorkDate(), '', 0, ''); + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenter."No.", WorkCenter."Subcontractor No.", ItemNo, '', '', WorkDate(), '', 10, ''); + + // [WHEN] The item is deleted + Item.Delete(true); + + // [THEN] All Subcontractor Prices for the item are deleted + SubcontractorPrice.SetRange("Item No.", ItemNo); + Assert.IsTrue(SubcontractorPrice.IsEmpty(), 'Subcontractor prices must be deleted when item is deleted'); + end; + [Test] [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,SubcontrDispatchingListDefaultRequestPageHandler')] procedure TestSubcontrDispatchingList() From 5a587eaf4a6767d01c070b8fdfac908311cefd97 Mon Sep 17 00:00:00 2001 From: Chethan Thopaiah Date: Mon, 27 Apr 2026 15:45:21 +0200 Subject: [PATCH 2/3] Bug 620643: [Subcontracting] Centralize Subcontractor Price deletion logic Move the SetCurrentKey/SetRange/DeleteAll pattern out of the three separate extension codeunits into dedicated procedures on the SubcontractorPrice table, eliminating duplication and making future maintenance a single-point change. - SubcontractorPrice.Table.al: add DeletePricesForVendor, DeletePricesForWorkCenter, DeletePricesForItem - SubcVendorExtension, SubcWorkCenterExtension, SubcItemExtension: each now calls the corresponding table procedure instead of repeating the same pattern inline Co-Authored-By: Claude Sonnet 4.6 --- .../SubcWorkCenterExtension.Codeunit.al | 5 +--- .../MasterData/SubcItemExtension.Codeunit.al | 14 +++-------- .../SubcVendorExtension.Codeunit.al | 5 +--- .../Tables/SubcontractorPrice.Table.al | 24 +++++++++++++++++++ 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/Manufacturing/SubcWorkCenterExtension.Codeunit.al b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/Manufacturing/SubcWorkCenterExtension.Codeunit.al index 768ed1b039..ede288117e 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/Manufacturing/SubcWorkCenterExtension.Codeunit.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/Manufacturing/SubcWorkCenterExtension.Codeunit.al @@ -19,9 +19,6 @@ codeunit 99001519 "Subc. Work Center Extension" if not RunTrigger then exit; - SubcontractorPrice.SetCurrentKey("Work Center No."); - SubcontractorPrice.SetRange("Work Center No.", Rec."No."); - if not SubcontractorPrice.IsEmpty() then - SubcontractorPrice.DeleteAll(true); + SubcontractorPrice.DeletePricesForWorkCenter(Rec."No."); end; } \ No newline at end of file diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcItemExtension.Codeunit.al b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcItemExtension.Codeunit.al index 5912a44241..e3350534d9 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcItemExtension.Codeunit.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcItemExtension.Codeunit.al @@ -10,6 +10,8 @@ codeunit 99001532 "Subc. Item Extension" { [EventSubscriber(ObjectType::Table, Database::Item, OnAfterDeleteEvent, '', false, false)] local procedure OnAfterDeleteItem(var Rec: Record Item; RunTrigger: Boolean) + var + SubcontractorPrice: Record "Subcontractor Price"; begin if Rec.IsTemporary() then exit; @@ -17,16 +19,6 @@ codeunit 99001532 "Subc. Item Extension" if not RunTrigger then exit; - DeleteRelatedSubcontractorPrices(Rec); - end; - - local procedure DeleteRelatedSubcontractorPrices(var Item: Record Item) - var - SubcontractorPrice: Record "Subcontractor Price"; - begin - SubcontractorPrice.SetCurrentKey("Item No."); - SubcontractorPrice.SetRange("Item No.", Item."No."); - if not SubcontractorPrice.IsEmpty() then - SubcontractorPrice.DeleteAll(true); + SubcontractorPrice.DeletePricesForItem(Rec."No."); end; } diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcVendorExtension.Codeunit.al b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcVendorExtension.Codeunit.al index 5fddeee822..9234be45a0 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcVendorExtension.Codeunit.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/Extensions/MasterData/SubcVendorExtension.Codeunit.al @@ -19,9 +19,6 @@ codeunit 99001531 "Subc. Vendor Extension" if not RunTrigger then exit; - SubcontractorPrice.SetCurrentKey("Vendor No."); - SubcontractorPrice.SetRange("Vendor No.", Rec."No."); - if not SubcontractorPrice.IsEmpty() then - SubcontractorPrice.DeleteAll(true); + SubcontractorPrice.DeletePricesForVendor(Rec."No."); end; } \ No newline at end of file diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al b/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al index 5345b523cd..c2e8c310c1 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al @@ -168,4 +168,28 @@ table 99001500 "Subcontractor Price" until SubcontractorPrice.Next() = 0; end; + procedure DeletePricesForVendor(VendorNo: Code[20]) + begin + SetCurrentKey("Vendor No."); + SetRange("Vendor No.", VendorNo); + if not IsEmpty() then + DeleteAll(true); + end; + + procedure DeletePricesForWorkCenter(WorkCenterNo: Code[20]) + begin + SetCurrentKey("Work Center No."); + SetRange("Work Center No.", WorkCenterNo); + if not IsEmpty() then + DeleteAll(true); + end; + + procedure DeletePricesForItem(ItemNo: Code[20]) + begin + SetCurrentKey("Item No."); + SetRange("Item No.", ItemNo); + if not IsEmpty() then + DeleteAll(true); + end; + } \ No newline at end of file From 19ff2155ccd99a3a2afb4d022ee600db9e3e1d54 Mon Sep 17 00:00:00 2001 From: Chethan Thopaiah Date: Mon, 27 Apr 2026 15:53:25 +0200 Subject: [PATCH 3/3] Mark SubcontractorPrice deletion procedures as internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three new centralized deletion procedures (DeletePricesForVendor, DeletePricesForWorkCenter, DeletePricesForItem) should not be part of the public API surface — mark them internal so they are only callable within the Subcontracting app. Co-Authored-By: Claude Sonnet 4.6 --- .../App/src/Process/Tables/SubcontractorPrice.Table.al | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al b/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al index c2e8c310c1..6e1c011713 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Tables/SubcontractorPrice.Table.al @@ -168,7 +168,7 @@ table 99001500 "Subcontractor Price" until SubcontractorPrice.Next() = 0; end; - procedure DeletePricesForVendor(VendorNo: Code[20]) + internal procedure DeletePricesForVendor(VendorNo: Code[20]) begin SetCurrentKey("Vendor No."); SetRange("Vendor No.", VendorNo); @@ -176,7 +176,7 @@ table 99001500 "Subcontractor Price" DeleteAll(true); end; - procedure DeletePricesForWorkCenter(WorkCenterNo: Code[20]) + internal procedure DeletePricesForWorkCenter(WorkCenterNo: Code[20]) begin SetCurrentKey("Work Center No."); SetRange("Work Center No.", WorkCenterNo); @@ -184,7 +184,7 @@ table 99001500 "Subcontractor Price" DeleteAll(true); end; - procedure DeletePricesForItem(ItemNo: Code[20]) + internal procedure DeletePricesForItem(ItemNo: Code[20]) begin SetCurrentKey("Item No."); SetRange("Item No.", ItemNo);