Skip to content
Merged
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 @@ -45,7 +45,10 @@ using System.Utilities;

codeunit 6620 "Copy Document Mgt."
{
Permissions = TableData "Cust. Ledger Entry" = rim;
Permissions = TableData "Cust. Ledger Entry" = rim,
tabledata "Assembly Header" = rim,
tabledata "Assembly Line" = rim,
tabledata "Assemble-to-Order Link" = rim;

trigger OnRun()
begin
Expand Down
5 changes: 5 additions & 0 deletions src/Layers/IT/BaseApp/Utilities/CopyDocumentMgt.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ using System.Utilities;
codeunit 6620 "Copy Document Mgt."
{

Permissions =
tabledata "Assembly Header" = rim,
tabledata "Assembly Line" = rim,
tabledata "Assemble-to-Order Link" = rim;

trigger OnRun()
begin
end;
Expand Down
5 changes: 5 additions & 0 deletions src/Layers/NA/BaseApp/Utilities/CopyDocumentMgt.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ using System.Utilities;
codeunit 6620 "Copy Document Mgt."
{

Permissions =
tabledata "Assembly Header" = rim,
tabledata "Assembly Line" = rim,
tabledata "Assemble-to-Order Link" = rim;

trigger OnRun()
begin
end;
Expand Down
5 changes: 5 additions & 0 deletions src/Layers/NL/BaseApp/Utilities/CopyDocumentMgt.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ using System.Utilities;
codeunit 6620 "Copy Document Mgt."
{

Permissions =
tabledata "Assembly Header" = rim,
tabledata "Assembly Line" = rim,
tabledata "Assemble-to-Order Link" = rim;

trigger OnRun()
begin
end;
Expand Down
5 changes: 5 additions & 0 deletions src/Layers/RU/BaseApp/Utilities/CopyDocumentMgt.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ using System.Utilities;
codeunit 6620 "Copy Document Mgt."
{

Permissions =
tabledata "Assembly Header" = rim,
tabledata "Assembly Line" = rim,
tabledata "Assemble-to-Order Link" = rim;

trigger OnRun()
begin
end;
Expand Down
6 changes: 6 additions & 0 deletions src/Layers/W1/BaseApp/Utilities/CopyDocumentMgt.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ using System.Utilities;
codeunit 6620 "Copy Document Mgt."
{

Permissions =
tabledata "Assembly Header" = rim,
tabledata "Assembly Line" = rim,
tabledata "Assemble-to-Order Link" = rim;

trigger OnRun()
begin
end;
Expand Down Expand Up @@ -438,6 +443,7 @@ codeunit 6620 "Copy Document Mgt."
FromDocType.AsInteger(), FromDocNo, ToSalesHeader, FromDocOccurrenceNo, FromDocVersionNo, IncludeHeader, RecalculateLines, MoveNegLines);
end;


procedure CopySalesDocSalesLine(FromSalesHeader: Record "Sales Header"; var ToSalesHeader: Record "Sales Header"; var LinesNotCopied: Integer; NextLineNo: Integer)
var
AssemblyHeader: Record "Assembly Header";
Expand Down
206 changes: 206 additions & 0 deletions src/Layers/W1/Tests/SCM/SCMCopyDocumentMgt.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ codeunit 137212 "SCM Copy Document Mgt."
LibrarySetupStorage: Codeunit "Library - Setup Storage";
LibraryRandom: Codeunit "Library - Random";
LibraryWarehouse: Codeunit "Library - Warehouse";
LibraryE2EPlanPermissions: Codeunit "Library - E2E Plan Permissions";
IsInitialized: Boolean;
MsgCorrectedInvoiceNo: Label 'have a Corrected Invoice No. Do you want to continue?';
WrongDimensionsCopiedErr: Label 'Wrong dimensions in copied document';
Expand Down Expand Up @@ -1726,6 +1727,211 @@ codeunit 137212 "SCM Copy Document Mgt."
ReturnSalesLine.TestField(Quantity, TotalQty);
end;

[Test]
[HandlerFunctions('MessageHandler')]
[Scope('OnPrem')]
procedure CopySalesQuoteWithATOAsTeamMember()
var
Item: Record Item;
AssemblyItem: Record Item;
FromSalesHeader: Record "Sales Header";
FromSalesLine: Record "Sales Line";
ToSalesHeader: Record "Sales Header";
ToSalesLine: Record "Sales Line";
AssemblyHeader: Record "Assembly Header";
LibraryLowerPermissions: Codeunit "Library - Lower Permissions";
Qty: Decimal;
begin
// [FEATURE] [Assemble-to-Order] [Copy Document] [Permissions]
// [SCENARIO] AB#630947: A user restricted to the D365 Team Member permission set can copy a sales quote whose
// line has an assemble-to-order link, and the copy reproduces the ATO link on the target line.
Initialize();

// [GIVEN] Assemble-to-order item with a resource component (resource-only BOM avoids the
// component availability engine that would read Prod. Order tables outside the fix under test)
LibraryInventory.CreateItem(Item);
Item.Validate("Replenishment System", Item."Replenishment System"::Assembly);
Item.Validate("Assembly Policy", Item."Assembly Policy"::"Assemble-to-Order");
Item.Modify(true);
LibraryAssembly.CreateAssemblyList(
AssemblyItem."Costing Method"::Standard, Item."No.", true, 0, 1, 0, LibraryRandom.RandInt(5), '', '');

// [GIVEN] Source sales quote with the ATO item; validating the item auto-creates the ATO link
Qty := LibraryRandom.RandIntInRange(2, 10);
LibrarySales.CreateSalesDocumentWithItem(
FromSalesHeader, FromSalesLine, FromSalesHeader."Document Type"::Quote, LibrarySales.CreateCustomerNo(),
Item."No.", Qty, '', WorkDate());

// [GIVEN] Empty target sales quote created while the user still has full permissions
CreateEmptySalesHeader(ToSalesHeader, ToSalesHeader."Document Type"::Quote);
Commit();

// [GIVEN] User is now restricted to the D365 Team Member plan (regression harness for AB#630947)
LibraryE2EPlanPermissions.SetTeamMemberPlan();
Comment thread
Shikhverma marked this conversation as resolved.
Comment thread
Shikhverma marked this conversation as resolved.

// [WHEN] Copy Document is executed for the source quote into the target quote
CopyDocumentMgt.SetProperties(true, false, false, false, true, false, false);
CopyDocumentMgt.CopySalesDoc("Sales Document Type From"::Quote, FromSalesHeader."No.", ToSalesHeader);

// [THEN] The copied line exists on the target quote with the same quantity and Qty. to Assemble to Order...
LibraryLowerPermissions.SetO365Full();
ToSalesLine.SetRange("Document Type", ToSalesHeader."Document Type");
ToSalesLine.SetRange("Document No.", ToSalesHeader."No.");
ToSalesLine.SetRange(Type, ToSalesLine.Type::Item);
ToSalesLine.SetRange("No.", Item."No.");
Assert.RecordCount(ToSalesLine, 1);
ToSalesLine.FindFirst();
ToSalesLine.TestField(Quantity, Qty);
ToSalesLine.TestField("Qty. to Assemble to Order", Qty);

// [THEN] ...and an Assembly Header is linked to the copied sales line
Assert.IsTrue(
ToSalesLine.AsmToOrderExists(AssemblyHeader),
'Assemble-to-Order link should exist for the copied sales line.');
end;

[Test]
[HandlerFunctions('MessageHandler')]
[Scope('OnPrem')]
procedure CopySalesQuoteWithItemComponentATOAsTeamMember()
var
Item: Record Item;
AssemblyItem: Record Item;
FromSalesHeader: Record "Sales Header";
FromSalesLine: Record "Sales Line";
ToSalesHeader: Record "Sales Header";
ToSalesLine: Record "Sales Line";
AssemblyHeader: Record "Assembly Header";
AssemblyLine: Record "Assembly Line";
LibraryLowerPermissions: Codeunit "Library - Lower Permissions";
Qty: Decimal;
begin
// [FEATURE] [Assemble-to-Order] [Copy Document] [Permissions]
// [SCENARIO] AB#630947: A user restricted to the D365 Team Member permission set can copy a sales quote whose
// line has an assemble-to-order link built from an item component, and the copy reproduces the ATO link and
// the item assembly line on the target line.
Initialize();

// [GIVEN] Assemble-to-order item with a single item component (typical ATO BOM that reads item data)
LibraryInventory.CreateItem(Item);
Item.Validate("Replenishment System", Item."Replenishment System"::Assembly);
Item.Validate("Assembly Policy", Item."Assembly Policy"::"Assemble-to-Order");
Item.Modify(true);
LibraryAssembly.CreateAssemblyList(
AssemblyItem."Costing Method"::Standard, Item."No.", true, 1, 0, 0, LibraryRandom.RandInt(5), '', '');

// [GIVEN] Source sales quote with the ATO item; validating the item auto-creates the ATO link
Qty := LibraryRandom.RandIntInRange(2, 10);
LibrarySales.CreateSalesDocumentWithItem(
FromSalesHeader, FromSalesLine, FromSalesHeader."Document Type"::Quote, LibrarySales.CreateCustomerNo(),
Item."No.", Qty, '', WorkDate());

// [GIVEN] Empty target sales quote created while the user still has full permissions
CreateEmptySalesHeader(ToSalesHeader, ToSalesHeader."Document Type"::Quote);
Commit();

// [GIVEN] User is now restricted to the D365 Team Member plan (regression harness for AB#630947)
LibraryE2EPlanPermissions.SetTeamMemberPlan();
Comment thread
Shikhverma marked this conversation as resolved.

// [WHEN] Copy Document is executed for the source quote into the target quote
CopyDocumentMgt.SetProperties(true, false, false, false, true, false, false);
CopyDocumentMgt.CopySalesDoc("Sales Document Type From"::Quote, FromSalesHeader."No.", ToSalesHeader);

// [THEN] The copied line exists on the target quote with the same quantity and Qty. to Assemble to Order...
LibraryLowerPermissions.SetO365Full();
ToSalesLine.SetRange("Document Type", ToSalesHeader."Document Type");
ToSalesLine.SetRange("Document No.", ToSalesHeader."No.");
ToSalesLine.SetRange(Type, ToSalesLine.Type::Item);
ToSalesLine.SetRange("No.", Item."No.");
Assert.RecordCount(ToSalesLine, 1);
ToSalesLine.FindFirst();
ToSalesLine.TestField(Quantity, Qty);
ToSalesLine.TestField("Qty. to Assemble to Order", Qty);

// [THEN] ...an Assembly Header is linked to the copied sales line...
Assert.IsTrue(
ToSalesLine.AsmToOrderExists(AssemblyHeader),
'Assemble-to-Order link should exist for the copied sales line.');

// [THEN] ...and the linked assembly order reproduces the item component line
Comment thread
Shikhverma marked this conversation as resolved.
AssemblyLine.SetRange("Document Type", AssemblyHeader."Document Type");
AssemblyLine.SetRange("Document No.", AssemblyHeader."No.");
AssemblyLine.SetRange(Type, AssemblyLine.Type::Item);
Assert.RecordCount(AssemblyLine, 1);
end;

[Test]
[HandlerFunctions('MessageHandler')]
[Scope('OnPrem')]
procedure CopyArchivedSalesQuoteWithATOAsTeamMember()
var
Item: Record Item;
AssemblyItem: Record Item;
FromSalesHeader: Record "Sales Header";
FromSalesLine: Record "Sales Line";
SalesHeaderArchive: Record "Sales Header Archive";
ToSalesHeader: Record "Sales Header";
ToSalesLine: Record "Sales Line";
AssemblyHeader: Record "Assembly Header";
ArchiveManagement: Codeunit ArchiveManagement;
LibraryLowerPermissions: Codeunit "Library - Lower Permissions";
Qty: Decimal;
begin
// [FEATURE] [Assemble-to-Order] [Copy Document] [Permissions] [Archive]
// [SCENARIO] AB#630947: A user restricted to the D365 Team Member permission set can copy an archived sales quote
// whose line has an assemble-to-order link, exercising the separately elevated CopySalesDocSalesLineArchive path,
// and the copy reproduces the ATO link on the target line.
Initialize();

// [GIVEN] Assemble-to-order item with a resource component
LibraryInventory.CreateItem(Item);
Item.Validate("Replenishment System", Item."Replenishment System"::Assembly);
Item.Validate("Assembly Policy", Item."Assembly Policy"::"Assemble-to-Order");
Item.Modify(true);
LibraryAssembly.CreateAssemblyList(
AssemblyItem."Costing Method"::Standard, Item."No.", true, 0, 1, 0, LibraryRandom.RandInt(5), '', '');

// [GIVEN] Source sales quote with the ATO item; validating the item auto-creates the ATO link
Qty := LibraryRandom.RandIntInRange(2, 10);
LibrarySales.CreateSalesDocumentWithItem(
FromSalesHeader, FromSalesLine, FromSalesHeader."Document Type"::Quote, LibrarySales.CreateCustomerNo(),
Item."No.", Qty, '', WorkDate());

// [GIVEN] The source quote is archived
ArchiveManagement.StoreSalesDocument(FromSalesHeader, false);
SalesHeaderArchive.Get(FromSalesHeader."Document Type", FromSalesHeader."No.", 1, 1);

// [GIVEN] Empty target sales quote created while the user still has full permissions
CreateEmptySalesHeader(ToSalesHeader, ToSalesHeader."Document Type"::Quote);
Commit();

// [GIVEN] User is now restricted to the D365 Team Member plan (regression harness for AB#630947)
LibraryE2EPlanPermissions.SetTeamMemberPlan();

// [WHEN] Copy Document is executed for the archived quote into the target quote
// Recalculate lines is required: the sales line archive does not persist "Qty. to Assemble to Order",
// so the ATO quantity is re-derived from the item's assembly policy during the copy.
CopyDocumentMgt.SetProperties(true, true, false, false, true, false, false);
CopyDocumentMgt.SetArchDocVal(SalesHeaderArchive."Doc. No. Occurrence", SalesHeaderArchive."Version No.");
CopyDocumentMgt.CopySalesDoc("Sales Document Type From"::"Arch. Quote", SalesHeaderArchive."No.", ToSalesHeader);

// [THEN] The copied line exists on the target quote with the same quantity and Qty. to Assemble to Order...
LibraryLowerPermissions.SetO365Full();
ToSalesLine.SetRange("Document Type", ToSalesHeader."Document Type");
ToSalesLine.SetRange("Document No.", ToSalesHeader."No.");
ToSalesLine.SetRange(Type, ToSalesLine.Type::Item);
ToSalesLine.SetRange("No.", Item."No.");
Assert.RecordCount(ToSalesLine, 1);
ToSalesLine.FindFirst();
ToSalesLine.TestField(Quantity, Qty);
ToSalesLine.TestField("Qty. to Assemble to Order", Qty);

// [THEN] ...and an Assembly Header is linked to the copied sales line
Assert.IsTrue(
ToSalesLine.AsmToOrderExists(AssemblyHeader),
'Assemble-to-Order link should exist for the copied sales line.');
end;

local procedure Initialize()
var
LibraryERMCountryData: Codeunit "Library - ERM Country Data";
Expand Down
Loading