Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using Microsoft.Manufacturing.Routing;
using Microsoft.Manufacturing.Setup;
using Microsoft.Manufacturing.WorkCenter;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.History;
using Microsoft.Purchases.Setup;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Document;
Expand Down Expand Up @@ -3903,6 +3904,69 @@ codeunit 137063 "SCM Manufacturing 7.0"
Assert.ExpectedTestFieldError(ProductionOrder.FieldCaption("Variant Code"), '');
end;

[Test]
[HandlerFunctions('PostedPurchaseDocumentLinesPageHandler')]
procedure PostPurchaseReturnOrderForSubcontractingWhenLastOperationIsNotSubcontracted()
var
CapacityUnitOfMeasure: Record "Capacity Unit of Measure";
Item: Record Item;
ProductionOrder: Record "Production Order";
PurchaseHeader: Record "Purchase Header";
PurchaseLine: Record "Purchase Line";
PurchInvLine: Record "Purch. Inv. Line";
RequisitionLine: Record "Requisition Line";
RoutingHeader: Record "Routing Header";
RoutingLine: Record "Routing Line";
SubcontractingWorkCenter: Record "Work Center";
WorkCenter: Record "Work Center";
OperationNo: Code[10];
begin
// [FEATURE] [AI test 0.4]
// [SCENARIO 647380] A subcontracting purchase return order can be posted when a later operation is not subcontracted.
Initialize();

// [GIVEN] A released production order whose subcontracting operation is followed by a non-subcontracting operation.
OperationNo := Format(10 + LibraryRandom.RandInt(10));
CreateSubcontractingSetup(SubcontractingWorkCenter, RoutingHeader, OperationNo);
UpdateRoutingStatus(RoutingHeader, RoutingHeader.Status::"Under Development");
CreateWorkCenterSetup(WorkCenter, CapacityUnitOfMeasure.Type::Minutes, 160000T, 235959T);
CreateRoutingLine(RoutingLine, RoutingHeader, WorkCenter."No.");
UpdateRoutingStatus(RoutingHeader, RoutingHeader.Status::Certified);
CreateProdItem(Item, RoutingHeader."No.");
CreateAndRefreshProdOrder(
ProductionOrder, ProductionOrder.Status::Released, Item."No.", LibraryRandom.RandInt(10),
ProductionOrder."Source Type"::Item, false);

// [GIVEN] A subcontracting purchase order created from the subcontracting worksheet is received and invoiced.
CalculateSubcontractOrder(RequisitionLine, SubcontractingWorkCenter."No.", ProductionOrder);
LibraryPlanning.CarryOutAMSubcontractWksh(RequisitionLine);
PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order);
PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No.");
PurchaseLine.SetRange("Operation No.", OperationNo);
PurchaseLine.FindFirst();
PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No.");
PurchaseHeader.Validate("Vendor Invoice No.", PurchaseHeader."No.");
PurchaseHeader.Modify(true);
LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);

// [GIVEN] A purchase return order populated from the posted subcontracting invoice.
PurchInvLine.SetRange("Order No.", PurchaseHeader."No.");
PurchInvLine.SetRange("Prod. Order No.", ProductionOrder."No.");
PurchInvLine.SetRange("Operation No.", OperationNo);
PurchInvLine.FindFirst();
LibraryPurchase.CreatePurchHeader(
PurchaseHeader, PurchaseHeader."Document Type"::"Return Order", SubcontractingWorkCenter."Subcontractor No.");
LibraryVariableStorage.Enqueue(PurchInvLine."Document No.");
PurchaseHeader.GetPstdDocLinesToReverse();
PurchaseHeader.Validate("Vendor Cr. Memo No.", PurchaseHeader."Buy-from Vendor No.");
PurchaseHeader.Modify(true);

// [WHEN] The purchase return order is shipped and invoiced.
LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);

// [THEN] Posting succeeds.
end;

local procedure Initialize()
var
LibraryERMCountryData: Codeunit "Library - ERM Country Data";
Expand Down Expand Up @@ -6242,6 +6306,14 @@ codeunit 137063 "SCM Manufacturing 7.0"
ProdOrderComponents.OK().Invoke();
end;

[ModalPageHandler]
procedure PostedPurchaseDocumentLinesPageHandler(var PostedPurchaseDocumentLines: TestPage "Posted Purchase Document Lines")
begin
PostedPurchaseDocumentLines.PostedReceiptsBtn.SetValue('Posted Invoices');
PostedPurchaseDocumentLines.PostedInvoices.Filter.SetFilter("Document No.", LibraryVariableStorage.DequeueText());
PostedPurchaseDocumentLines.OK().Invoke();
end;

local procedure GetShopCalendarCodeForProductionOrder(ProductionOrder: Record "Production Order"): Code[10]
var
ProdOrderRoutingLine: Record "Prod. Order Routing Line";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using Microsoft.Manufacturing.Routing;
using Microsoft.Manufacturing.Setup;
using Microsoft.Manufacturing.WorkCenter;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.History;
using Microsoft.Purchases.Setup;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Document;
Expand Down Expand Up @@ -3897,6 +3898,69 @@ codeunit 137063 "SCM Manufacturing 7.0"
Assert.ExpectedTestFieldError(ProductionOrder.FieldCaption("Variant Code"), '');
end;

[Test]
[HandlerFunctions('PostedPurchaseDocumentLinesPageHandler')]
procedure PostPurchaseReturnOrderForSubcontractingWhenLastOperationIsNotSubcontracted()
var
CapacityUnitOfMeasure: Record "Capacity Unit of Measure";
Item: Record Item;
ProductionOrder: Record "Production Order";
PurchaseHeader: Record "Purchase Header";
PurchaseLine: Record "Purchase Line";
PurchInvLine: Record "Purch. Inv. Line";
RequisitionLine: Record "Requisition Line";
RoutingHeader: Record "Routing Header";
RoutingLine: Record "Routing Line";
SubcontractingWorkCenter: Record "Work Center";
WorkCenter: Record "Work Center";
OperationNo: Code[10];
begin
// [FEATURE] [AI test 0.4]
// [SCENARIO 647380] A subcontracting purchase return order can be posted when a later operation is not subcontracted.
Initialize();

// [GIVEN] A released production order whose subcontracting operation is followed by a non-subcontracting operation.
OperationNo := Format(10 + LibraryRandom.RandInt(10));
CreateSubcontractingSetup(SubcontractingWorkCenter, RoutingHeader, OperationNo);
UpdateRoutingStatus(RoutingHeader, RoutingHeader.Status::"Under Development");
CreateWorkCenterSetup(WorkCenter, CapacityUnitOfMeasure.Type::Minutes, 160000T, 235959T);
CreateRoutingLine(RoutingLine, RoutingHeader, WorkCenter."No.");
UpdateRoutingStatus(RoutingHeader, RoutingHeader.Status::Certified);
CreateProdItem(Item, RoutingHeader."No.");
CreateAndRefreshProdOrder(
ProductionOrder, ProductionOrder.Status::Released, Item."No.", LibraryRandom.RandInt(10),
ProductionOrder."Source Type"::Item, false);

// [GIVEN] A subcontracting purchase order created from the subcontracting worksheet is received and invoiced.
CalculateSubcontractOrder(RequisitionLine, SubcontractingWorkCenter."No.", ProductionOrder);
LibraryPlanning.CarryOutAMSubcontractWksh(RequisitionLine);
PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order);
PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No.");
PurchaseLine.SetRange("Operation No.", OperationNo);
PurchaseLine.FindFirst();
PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No.");
PurchaseHeader.Validate("Vendor Invoice No.", PurchaseHeader."No.");
PurchaseHeader.Modify(true);
LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);

// [GIVEN] A purchase return order populated from the posted subcontracting invoice.
PurchInvLine.SetRange("Order No.", PurchaseHeader."No.");
PurchInvLine.SetRange("Prod. Order No.", ProductionOrder."No.");
PurchInvLine.SetRange("Operation No.", OperationNo);
PurchInvLine.FindFirst();
LibraryPurchase.CreatePurchHeader(
PurchaseHeader, PurchaseHeader."Document Type"::"Return Order", SubcontractingWorkCenter."Subcontractor No.");
LibraryVariableStorage.Enqueue(PurchInvLine."Document No.");
PurchaseHeader.GetPstdDocLinesToReverse();
PurchaseHeader.Validate("Vendor Cr. Memo No.", PurchaseHeader."Buy-from Vendor No.");
PurchaseHeader.Modify(true);

// [WHEN] The purchase return order is shipped and invoiced.
LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);

// [THEN] Posting succeeds.
end;

local procedure Initialize()
var
LibraryERMCountryData: Codeunit "Library - ERM Country Data";
Expand Down Expand Up @@ -6251,6 +6315,14 @@ codeunit 137063 "SCM Manufacturing 7.0"
ProdOrderComponents.OK().Invoke();
end;

[ModalPageHandler]
procedure PostedPurchaseDocumentLinesPageHandler(var PostedPurchaseDocumentLines: TestPage "Posted Purchase Document Lines")
begin
PostedPurchaseDocumentLines.PostedReceiptsBtn.SetValue('Posted Invoices');
PostedPurchaseDocumentLines.PostedInvoices.Filter.SetFilter("Document No.", LibraryVariableStorage.DequeueText());
PostedPurchaseDocumentLines.OK().Invoke();
end;

local procedure GetShopCalendarCodeForProductionOrder(ProductionOrder: Record "Production Order"): Code[10]
var
ProdOrderRoutingLine: Record "Prod. Order Routing Line";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ codeunit 99000789 "Mfg. Purchase Document Mgt."
[EventSubscriber(ObjectType::Table, Database::"Purchase Line", 'OnIsSubcontractingCreditMemo', '', true, false)]
local procedure OnIsSubcontractingCreditMemo(var PurchaseLine: Record "Purchase Line"; var Result: Boolean)
begin
if (PurchaseLine."Document Type" = PurchaseLine."Document Type"::"Credit Memo") and PurchaseLine.IsProdOrder() and
if PurchaseLine.IsCreditDocType() and PurchaseLine.IsProdOrder() and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Breaking\ Changes}$

MfgPurchaseDocumentMgt.Codeunit.al's OnIsSubcontractingCreditMemo event subscriber changed its condition from an exact 'Document Type = Credit Memo' check to PurchaseLine.IsCreditDocType(), which also matches 'Return Order'. This is an intentional fix (validated by the new PostPurchaseReturnOrderForSubcontractingWhenLastOperationIsNotSubcontracted test) enabling posting of subcontracting purchase return orders, but it silently widens what the event named 'OnIsSubcontractingCreditMemo' now considers true (Return Orders too), which may surprise other subscribers/callers relying on the event's literal name/semantics. Consider whether the event name/documentation should be updated to reflect it now also covers Return Orders, or whether Return Order handling should be a distinct, separately named condition.

Agent judgement — not directly backed by a BCQuality knowledge article.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.36.6

(PurchaseLine."Operation No." <> '') and (PurchaseLine."Work Center No." <> '') then
Result := true
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using Microsoft.Manufacturing.Routing;
using Microsoft.Manufacturing.Setup;
using Microsoft.Manufacturing.WorkCenter;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.History;
using Microsoft.Purchases.Setup;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Document;
Expand Down Expand Up @@ -3793,6 +3794,69 @@ codeunit 137063 "SCM Manufacturing 7.0"
Assert.AreEqual(SKURoutingHeader."No.", ProdOrderLine."Routing No.", ProductionRoutingErr);
end;

[Test]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Testing}$

The new test PostPurchaseReturnOrderForSubcontractingWhenLastOperationIsNotSubcontracted() calls CreateSubcontractingSetup(...), but that local procedure is defined only inside #if not CLEAN29 ... #endif blocks (e.g. src/Layers/W1/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al lines 3960-3976; equivalently in the BE and IT copies). The new test itself is NOT wrapped in the same #if not CLEAN29 guard, so in a CLEAN29 compilation configuration the call resolves to an undefined procedure and the codeunit fails to compile. This affects all three duplicated copies (W1, BE, IT) identically.

Suggested fix (apply manually — could not be anchored as a one-click suggestion):

#if not CLEAN29
    [Test]
    [HandlerFunctions('PostedPurchaseDocumentLinesPageHandler')]
    procedure PostPurchaseReturnOrderForSubcontractingWhenLastOperationIsNotSubcontracted()
    ...
    end;
#endif

Agent judgement — not directly backed by a BCQuality knowledge article.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.36.6

[HandlerFunctions('PostedPurchaseDocumentLinesPageHandler')]
procedure PostPurchaseReturnOrderForSubcontractingWhenLastOperationIsNotSubcontracted()
var
CapacityUnitOfMeasure: Record "Capacity Unit of Measure";
Item: Record Item;
ProductionOrder: Record "Production Order";
PurchaseHeader: Record "Purchase Header";
PurchaseLine: Record "Purchase Line";
PurchInvLine: Record "Purch. Inv. Line";
RequisitionLine: Record "Requisition Line";
RoutingHeader: Record "Routing Header";
RoutingLine: Record "Routing Line";
SubcontractingWorkCenter: Record "Work Center";
WorkCenter: Record "Work Center";
OperationNo: Code[10];
begin
// [FEATURE] [AI test 0.4]
// [SCENARIO 647380] A subcontracting purchase return order can be posted when a later operation is not subcontracted.
Initialize();

// [GIVEN] A released production order whose subcontracting operation is followed by a non-subcontracting operation.
OperationNo := Format(10 + LibraryRandom.RandInt(10));
CreateSubcontractingSetup(SubcontractingWorkCenter, RoutingHeader, OperationNo);
UpdateRoutingStatus(RoutingHeader, RoutingHeader.Status::"Under Development");
CreateWorkCenterSetup(WorkCenter, CapacityUnitOfMeasure.Type::Minutes, 160000T, 235959T);
CreateRoutingLine(RoutingLine, RoutingHeader, WorkCenter."No.");
UpdateRoutingStatus(RoutingHeader, RoutingHeader.Status::Certified);
CreateProdItem(Item, RoutingHeader."No.");
CreateAndRefreshProdOrder(
ProductionOrder, ProductionOrder.Status::Released, Item."No.", LibraryRandom.RandInt(10),
ProductionOrder."Source Type"::Item, false);

// [GIVEN] A subcontracting purchase order created from the subcontracting worksheet is received and invoiced.
CalculateSubcontractOrder(RequisitionLine, SubcontractingWorkCenter."No.", ProductionOrder);
LibraryPlanning.CarryOutAMSubcontractWksh(RequisitionLine);
PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order);
PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No.");
PurchaseLine.SetRange("Operation No.", OperationNo);
PurchaseLine.FindFirst();
PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No.");
PurchaseHeader.Validate("Vendor Invoice No.", PurchaseHeader."No.");
PurchaseHeader.Modify(true);
LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);

// [GIVEN] A purchase return order populated from the posted subcontracting invoice.
PurchInvLine.SetRange("Order No.", PurchaseHeader."No.");
PurchInvLine.SetRange("Prod. Order No.", ProductionOrder."No.");
PurchInvLine.SetRange("Operation No.", OperationNo);
PurchInvLine.FindFirst();
LibraryPurchase.CreatePurchHeader(
PurchaseHeader, PurchaseHeader."Document Type"::"Return Order", SubcontractingWorkCenter."Subcontractor No.");
LibraryVariableStorage.Enqueue(PurchInvLine."Document No.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

The new test enqueues an expectation for the PostedPurchaseDocumentLinesPageHandler modal page handler but never calls LibraryVariableStorage.AssertEmpty() to confirm the queued value was consumed. This leaves the UI-handler interaction only partially verified across all three duplicated copies (W1, BE, IT).

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.36.6

PurchaseHeader.GetPstdDocLinesToReverse();
PurchaseHeader.Validate("Vendor Cr. Memo No.", PurchaseHeader."Buy-from Vendor No.");
PurchaseHeader.Modify(true);

// [WHEN] The purchase return order is shipped and invoiced.
LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);

// [THEN] Posting succeeds.
end;

local procedure Initialize()
var
LibraryERMCountryData: Codeunit "Library - ERM Country Data";
Expand Down Expand Up @@ -6132,6 +6196,14 @@ codeunit 137063 "SCM Manufacturing 7.0"
ProdOrderComponents.OK().Invoke();
end;

[ModalPageHandler]
procedure PostedPurchaseDocumentLinesPageHandler(var PostedPurchaseDocumentLines: TestPage "Posted Purchase Document Lines")
begin
PostedPurchaseDocumentLines.PostedReceiptsBtn.SetValue('Posted Invoices');
PostedPurchaseDocumentLines.PostedInvoices.Filter.SetFilter("Document No.", LibraryVariableStorage.DequeueText());
PostedPurchaseDocumentLines.OK().Invoke();
end;

local procedure GetShopCalendarCodeForProductionOrder(ProductionOrder: Record "Production Order"): Code[10]
var
ProdOrderRoutingLine: Record "Prod. Order Routing Line";
Expand Down
Loading