From 7d1f8c8916343a1f174f5a3160502fae52977ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miljan=20Milosavljevi=C4=87?= Date: Wed, 4 Feb 2026 14:45:48 +0100 Subject: [PATCH 1/4] Subscription Header Traceability --- .../Codeunits/SalesDocuments.Codeunit.al | 113 ++++++++++-- .../SalesSubscriptionLineMgmt.Codeunit.al | 29 --- .../Pages/ServiceObject.Page.al | 35 ++++ .../Tables/SubscriptionHeader.Table.al | 171 ++++++++++++++++++ .../SalesServiceCommitmentTest.Codeunit.al | 150 ++++++++++++++- 5 files changed, 449 insertions(+), 49 deletions(-) diff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al index 9ed9b0a127..0c416205a1 100644 --- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al @@ -5,6 +5,8 @@ using Microsoft.Finance.GeneralLedger.Journal; using Microsoft.Finance.GeneralLedger.Posting; using Microsoft.Inventory; using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Posting; using Microsoft.Inventory.Tracking; using Microsoft.Purchases.Posting; using Microsoft.Sales.Document; @@ -20,8 +22,12 @@ codeunit 8063 "Sales Documents" var TempSubscriptionItemSalesLine: Record "Sales Line" temporary; + GlobalSalesHeader: Record "Sales Header"; + GlobalSalesLine: Record "Sales Line"; SalesServiceCommMgmt: Codeunit "Sales Subscription Line Mgmt."; CalledFromContractRenewal: Boolean; + NegativeQuantityMsgShown: Boolean; + SubscriptionHeaderNotCreatedMsg: Label 'For negative quantity the Subscription is not created.'; local procedure DeleteAllTempSubscriptionItemSalesLines() begin @@ -337,30 +343,59 @@ codeunit 8063 "Sales Documents" OnAfterSkipInsertingSalesInvoiceLineIfServiceCommitmentItemsExist(SalesHeader, SalesLine, IsHandled); end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnAfterInsertShipmentLine, '', false, false)] - local procedure CreateServiceObjectWithSerialNoOnAfterInsertShipmentLine(var SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; var SalesShptLine: Record "Sales Shipment Line") + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnBeforePostItemJnlLine, '', false, false)] + local procedure CacheSalesLineContextFromSalesPost(SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line") begin - //The function creates Subscription for Sales Line with Subscription Lines - CreateServiceObjectFromSales(SalesHeader, SalesLine, SalesShptLine); + GlobalSalesHeader := SalesHeader; + GlobalSalesLine := SalesLine; end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", OnAfterSalesShptLineInsert, '', false, false)] - local procedure CreateServiceObjectWithSerialNoOnAfterSalesShptLineInsert(var SalesShptLine: Record "Sales Shipment Line"; SalesShptHeader: Record "Sales Shipment Header"; SalesOrderLine: Record "Sales Line") + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", OnPostAssocItemJnlLineOnBeforePost, '', false, false)] + local procedure CacheSalesLineContextFromDropShipment(SalesOrderLine: Record "Sales Line") + begin + GlobalSalesHeader.Get(SalesOrderLine."Document Type", SalesOrderLine."Document No."); + GlobalSalesLine := SalesOrderLine; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Jnl.-Post Line", OnAfterPostItemJnlLine, '', false, false)] + local procedure CreateSubscriptionHeaderFromItemJournalLine(var ItemJournalLine: Record "Item Journal Line") var - SalesHeader: Record "Sales Header"; + SubscriptionHeader: Record "Subscription Header"; begin - //The function creates Subscription for Sales Line with Subscription Lines - SalesHeader.Get(SalesOrderLine."Document Type", SalesOrderLine."Document No."); - CreateServiceObjectFromSales(SalesHeader, SalesOrderLine, SalesShptLine); + if ItemJournalLine.Adjustment then + exit; + if ItemJournalLine."Entry Type" <> ItemJournalLine."Entry Type"::Sale then + exit; + if not SalesServiceCommMgmt.IsSalesLineWithSalesServiceCommitments(GlobalSalesLine, false) then + exit; + if ItemJournalLine.Quantity < 0 then begin + ShowNegativeQuantityMessageIfNeeded(ItemJournalLine.Quantity); + exit; + end; + + if ItemJournalLine.Quantity > 0 then + CreateServiceObjectFromSalesLine(GlobalSalesHeader, GlobalSalesLine, ItemJournalLine."Serial No.", ItemJournalLine.Quantity, ItemJournalLine."Item Shpt. Entry No."); + if ItemJournalLine."Invoiced Quantity" <> 0 then + if ItemJournalLine."Invoice No." <> '' then begin + SubscriptionHeader.SetRange("Item Ledger Entry No.", ItemJournalLine."Item Shpt. Entry No."); + SubscriptionHeader.ModifyAll("Last Sales Invoice No.", ItemJournalLine."Invoice No.", false); + end; end; +#if not CLEAN28 + [Obsolete('Use OnAfterPostItemJnlLine event subscriber to create Subscription Headers during item journal posting', '28.0')] procedure CreateServiceObjectFromSales(var SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; var SalesShptLine: Record "Sales Shipment Line") var TempTrackingSpecBuffer: Record "Tracking Specification" temporary; ItemTrackingDocMgt: Codeunit "Item Tracking Doc. Management"; begin //The function creates Subscription for Sales Line with Subscription Lines - if SalesServiceCommMgmt.IsSalesLineWithSalesServiceCommitmentsToShip(SalesLine, SalesShptLine.Quantity) then begin + if SalesServiceCommMgmt.IsSalesLineWithSalesServiceCommitmentsToShip(SalesLine) then begin + if SalesShptLine.Quantity < 0 then begin + ShowNegativeQuantityMessageIfNeeded(SalesShptLine.Quantity); + exit; + end; + ItemTrackingDocMgt.RetrieveDocumentItemTracking(TempTrackingSpecBuffer, SalesShptLine."Document No.", Database::"Sales Shipment Header", 0); TempTrackingSpecBuffer.SetRange("Source Ref. No.", SalesShptLine."Line No."); if not TempTrackingSpecBuffer.IsEmpty() then @@ -369,6 +404,7 @@ codeunit 8063 "Sales Documents" CreateServiceObjectFromSalesLine(SalesHeader, SalesLine); end; end; +#endif [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnPostUpdateOrderLineOnSetDefaultQtyBlank, '', false, false)] local procedure UpdateQuantitiesOnPostUpdateOrderLineOnSetDefaultQtyBlank(var TempSalesLine: Record "Sales Line" temporary) @@ -440,6 +476,8 @@ codeunit 8063 "Sales Documents" exit(SalesServiceCommMgmt.IsSalesLineWithServiceCommitmentItem(TempSalesLine, true) or ContractRenewalMgt.IsContractRenewal(TempSalesLine)); end; +#if not CLEAN28 + [Obsolete('Use OnAfterPostItemJnlLine event subscriber to create Subscription Headers during item journal posting', '28.0')] local procedure CreateServiceObjectFromSalesLine(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line") var Item: Record Item; @@ -450,10 +488,11 @@ codeunit 8063 "Sales Documents" exit; if Item.HasSNSpecificItemTracking() then exit; - CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, '', 0); + CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, '', 0, 0); end; +#endif - local procedure CreateServiceObjectFromSalesLine(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; SerialNo: Code[50]; QtyPerSerialNo: Decimal) + local procedure CreateServiceObjectFromSalesLine(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; SerialNo: Code[50]; QtyPerSerialNo: Decimal; ItemLedgerEntryNo: Integer) var ServiceObject: Record "Subscription Header"; IsHandled: Boolean; @@ -464,16 +503,17 @@ codeunit 8063 "Sales Documents" exit; if SerialNo = '' then - CreateServiceObject(ServiceObject, SalesHeader, SalesLine, SalesLine."Qty. to Ship", SerialNo) + CreateServiceObject(ServiceObject, SalesHeader, SalesLine, SalesLine."Qty. to Ship", SerialNo, ItemLedgerEntryNo) else - CreateServiceObject(ServiceObject, SalesHeader, SalesLine, QtyPerSerialNo, SerialNo); + CreateServiceObject(ServiceObject, SalesHeader, SalesLine, QtyPerSerialNo, SerialNo, ItemLedgerEntryNo); CreateSubscriptionLineFromSalesLine(ServiceObject, SalesHeader, SalesLine); OnAfterCreateSubscriptionHeaderFromSalesLine(ServiceObject, SalesHeader, SalesLine); end; - local procedure CreateServiceObject(var ServiceObject: Record "Subscription Header"; SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; Quantity: Decimal; SerialNo: Code[50]) + local procedure CreateServiceObject(var ServiceObject: Record "Subscription Header"; SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; Quantity: Decimal; SerialNo: Code[50]; ItemLedgerEntryNo: Integer) var + Item: Record Item; begin ServiceObject.Init(); ServiceObject.SetHideValidationDialog(true); @@ -512,6 +552,13 @@ codeunit 8063 "Sales Documents" ServiceObject."Customer Price Group" := SalesHeader."Customer Price Group"; ServiceObject."Customer Reference" := SalesHeader."Your Reference"; ServiceObject."Variant Code" := SalesLine."Variant Code"; + + if Item.Get(SalesLine."No.") then + ServiceObject.UpdateVendorAndManufacturerFromItem(Item); + ServiceObject."Salesperson Code" := SalesHeader."Salesperson Code"; + ServiceObject."Sales Order No." := SalesHeader."No."; + ServiceObject."Item Ledger Entry No." := ItemLedgerEntryNo; + OnCreateSubscriptionHeaderFromSalesLineBeforeInsertSubscriptionHeader(ServiceObject, SalesHeader, SalesLine); ServiceObject.Insert(true); OnCreateSubscriptionHeaderFromSalesLineAfterInsertSubscriptionHeader(ServiceObject, SalesHeader, SalesLine); @@ -604,15 +651,18 @@ codeunit 8063 "Sales Documents" end; end; +#if not CLEAN28 + [Obsolete('Creation of Subscription Header happens in OnAfterPostItemJnlLine when Item Journal Line is posted', '28.0')] local procedure CreateServiceObjectFromTrackingSpecification(var SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; var TempTrackingSpecBuffer: Record "Tracking Specification" temporary) begin if TempTrackingSpecBuffer.FindSet() then repeat - CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, TempTrackingSpecBuffer."Serial No.", TempTrackingSpecBuffer."Quantity (Base)"); + CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, TempTrackingSpecBuffer."Serial No.", TempTrackingSpecBuffer."Quantity (Base)", 0); until TempTrackingSpecBuffer.Next() = 0; TempTrackingSpecBuffer.Reset(); TempTrackingSpecBuffer.DeleteAll(false); end; +#endif [EventSubscriber(ObjectType::Codeunit, Codeunit::"Whse.-Activity-Post", OnUpdateSourceDocumentOnAfterSalesLineModify, '', false, false)] local procedure ModifyShipmentDateFromInventoryPickPostingDate(var SalesLine: Record "Sales Line"; WarehouseActivityLine: Record "Warehouse Activity Line") @@ -793,6 +843,35 @@ codeunit 8063 "Sales Documents" SearchFieldNo[3] := 0; end; + local procedure ShowNegativeQuantityMessageIfNeeded(Quantity: Decimal) + begin + if Quantity < 0 then + if not NegativeQuantityMsgShown then begin + Message(SubscriptionHeaderNotCreatedMsg); + NegativeQuantityMsgShown := true; + end; + end; + + local procedure ResetGlobalVariables() + begin + CalledFromContractRenewal := false; + NegativeQuantityMsgShown := false; + Clear(GlobalSalesHeader); + Clear(GlobalSalesLine); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnBeforePostSalesDoc, '', false, false)] + local procedure ResetVariablesOnBeforePostSalesDoc() + begin + ResetGlobalVariables(); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnAfterPostSalesDoc, '', false, false)] + local procedure ResetVariablesOnAfterPostSalesDoc() + begin + ResetGlobalVariables(); + end; + [IntegrationEvent(false, false)] local procedure OnCheckResetValueForSubscriptionItems(var TempSalesLine: Record "Sales Line"; var ResetValueForSubscriptionItems: Boolean; var IsHandled: Boolean) begin diff --git a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Codeunits/SalesSubscriptionLineMgmt.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Codeunits/SalesSubscriptionLineMgmt.Codeunit.al index 5813943a0d..17991f17c3 100644 --- a/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Codeunits/SalesSubscriptionLineMgmt.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Sales Service Commitments/Codeunits/SalesSubscriptionLineMgmt.Codeunit.al @@ -144,16 +144,6 @@ codeunit 8069 "Sales Subscription Line Mgmt." exit(true); end; - internal procedure IsSalesLineWithSalesServiceCommitmentsToShip(SalesLine: Record "Sales Line"; QuantityToCheck: Decimal): Boolean - begin - if not IsSalesLineWithSalesServiceCommitmentsToShip(SalesLine) then - exit(false); - if CheckNegativeQuantityAndShowMessageForServiceCommitment(QuantityToCheck) then - exit(false); - - exit(true); - end; - internal procedure IsSalesLineWithServiceCommitmentItemToShip(SalesLine: Record "Sales Line"): Boolean begin if not IsSalesLineWithServiceCommitmentItem(SalesLine, true) then @@ -413,23 +403,6 @@ codeunit 8069 "Sales Subscription Line Mgmt." begin end; - local procedure CheckNegativeQuantityAndShowMessageForServiceCommitment(Quantity: Decimal): Boolean - begin - if Quantity <= 0 then begin - if not ServiceCommitmentWithNegativeQtyMessageThrown then begin - Message(ServiceObjectNotCreatedMsg); - ServiceCommitmentWithNegativeQtyMessageThrown := true; - end; - exit(true); - end; - exit(false); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnAfterPostSalesLines, '', false, false)] - local procedure ResetServiceCommitmentWithNegativeQtyMessageThrownOnAfterPostSalesLines() - begin - ServiceCommitmentWithNegativeQtyMessageThrown := false; - end; [IntegrationEvent(false, false)] local procedure OnAddAdditionalSalesSubscriptionLinesForSalesLineAfterApplyFilters(var SubscriptionPackage: Record "Subscription Package"; var SalesLine: Record "Sales Line") @@ -517,7 +490,5 @@ codeunit 8069 "Sales Subscription Line Mgmt." var ItemManagement: Codeunit "Sub. Contracts Item Management"; - ServiceCommitmentWithNegativeQtyMessageThrown: Boolean; SalesLineRestoreInProgress: Boolean; - ServiceObjectNotCreatedMsg: Label 'For negative quantity the Subscription is not created.'; } diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al index 0c8c0c5748..77cffe5948 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al @@ -513,6 +513,41 @@ page 8060 "Service Object" } } } + group(Details) + { + Caption = 'Details'; + field("Vendor No."; Rec."Vendor No.") + { + Visible = false; + } + field("Vendor Name"; Rec."Vendor Name") + { + } + field("Vendor Item No."; Rec."Vendor Item No.") + { + } + field("Manufacturer Code"; Rec."Manufacturer Code") + { + Visible = false; + } + field("Manufacturer Name"; Rec."Manufacturer Name") + { + } + field("Salesperson Code"; Rec."Salesperson Code") + { + } + field("Sales Order No."; Rec."Sales Order No.") + { + } + field("Item Ledger Entry No."; Rec."Item Ledger Entry No.") + { + Importance = Additional; + } + field("Last Sales Invoice No."; Rec."Last Sales Invoice No.") + { + Importance = Additional; + } + } } area(FactBoxes) { diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al index 98fc27f383..469df9a482 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al @@ -3,13 +3,18 @@ namespace Microsoft.SubscriptionBilling; using Microsoft.CRM.BusinessRelation; using Microsoft.CRM.Contact; using Microsoft.CRM.Outlook; +using Microsoft.CRM.Team; using Microsoft.Finance.GeneralLedger.Account; using Microsoft.Foundation.Address; using Microsoft.Foundation.NoSeries; using Microsoft.Foundation.UOM; using Microsoft.Inventory.Item; using Microsoft.Inventory.Item.Attribute; +using Microsoft.Inventory.Item.Catalog; +using Microsoft.Inventory.Ledger; using Microsoft.Purchases.Document; +using Microsoft.Purchases.Setup; +using Microsoft.Purchases.Vendor; using Microsoft.Sales.Customer; using Microsoft.Sales.Document; using Microsoft.Sales.Pricing; @@ -72,6 +77,8 @@ table 8057 "Subscription Header" if not SkipEndUserContact then UpdateEndUserCont("End-User Customer No."); + "Salesperson Code" := Cust."Salesperson Code"; + if (xRec."End-User Customer No." <> '') and (xRec."End-User Customer No." <> "End-User Customer No.") then RecallModifyAddressNotification(GetModifyCustomerAddressNotificationId()); if "End-User Customer No." <> xRec."End-User Customer No." then begin @@ -436,6 +443,7 @@ table 8057 "Subscription Header" Error(ItemBlockedOrWithoutServiceCommitmentsErr, "Source No."); Description := ContractsItemManagement.GetItemTranslation("Source No.", "Variant Code", "End-User Customer No."); Validate("Unit of Measure", Item."Sales Unit of Measure"); + UpdateVendorAndManufacturerFromItem(Item); if CurrFieldNo = FieldNo("Source No.") then InsertServiceCommitmentsFromStandardServCommPackages(); if "Serial No." <> '' then @@ -908,6 +916,117 @@ table 8057 "Subscription Header" else if (Type = const("G/L Account")) "Unit of Measure"; } + field(8000; "Vendor No."; Code[20]) + { + Caption = 'Vendor No.'; + TableRelation = Vendor; + ToolTip = 'Specifies the no. of the default vendor of this item.'; + Editable = true; + + trigger OnValidate() + begin + UpdateVendorName(); + end; + } + field(8001; "Vendor Name"; Text[100]) + { + Caption = 'Vendor Name'; + FieldClass = Normal; + ToolTip = 'Specifies the name of the default vendor of this item.'; + Editable = true; + TableRelation = Vendor.Name; + ValidateTableRelation = false; + + trigger OnValidate() + var + Vendor: Record Vendor; + begin + if Rec."Vendor Name" <> xRec."Vendor Name" then + if ShouldSearchForVendorByName("Vendor No.") then + Validate("Vendor No.", Vendor.GetVendorNo("Vendor Name")); + end; + + trigger OnLookup() + var + Vendor: Record Vendor; + begin + if Page.RunModal(Page::"Vendor List", Vendor) = Action::LookupOK then + Validate("Vendor No.", Vendor."No."); + end; + } + field(8002; "Vendor Item No."; Text[50]) + { + Caption = 'Vendor Item No.'; + FieldClass = Normal; + Editable = false; + ToolTip = 'Specifies the the default vendor''s item no.'; + } + field(8003; "Manufacturer Code"; Code[10]) + { + Caption = 'Manufacturer Code'; + TableRelation = Manufacturer; + ToolTip = 'Specifies the code of the manufacturer of this item.'; + Editable = true; + + trigger OnValidate() + begin + UpdateManufacturerName(); + end; + } + field(8004; "Manufacturer Name"; Text[50]) + { + Caption = 'Manufacturer Name'; + FieldClass = Normal; + ToolTip = 'Specifies the name of the manufacturer of this item.'; + Editable = true; + TableRelation = Manufacturer.Name; + ValidateTableRelation = false; + + trigger OnValidate() + var + Manufacturer: Record Manufacturer; + begin + if "Manufacturer Name" <> '' then begin + Manufacturer.SetRange(Name, "Manufacturer Name"); + if Manufacturer.FindFirst() then + Validate("Manufacturer Code", Manufacturer.Code); + end; + end; + + trigger OnLookup() + var + Manufacturer: Record Manufacturer; + begin + if Page.RunModal(0, Manufacturer) = Action::LookupOK then + Validate("Manufacturer Code", Manufacturer.Code); + end; + } + field(8005; "Salesperson Code"; Code[20]) + { + Caption = 'Salesperson Code'; + TableRelation = "Salesperson/Purchaser"; + ToolTip = 'Specifies the salesperson who is assigned to the customer.'; + Editable = true; + } + field(8006; "Sales Order No."; Code[20]) + { + Caption = 'Sales Order No.'; + Editable = false; + ToolTip = 'Indicates the sales order used to create the subscription.'; + } + field(8008; "Item Ledger Entry No."; Integer) + { + Caption = 'Item Ledger Entry No.'; + TableRelation = "Item Ledger Entry"; + Editable = false; + ToolTip = 'Specifies the item ledger entry number that was used to create the subscription.'; + } + field(8009; "Last Sales Invoice No."; Code[20]) + { + Caption = 'Last Sales Invoice No.'; + Editable = false; + ToolTip = 'Indicates the last sales invoice for the sales order that was used to create the subscription.'; + } } keys { @@ -915,6 +1034,9 @@ table 8057 "Subscription Header" { Clustered = true; } + key(SK1; "Item Ledger Entry No.") + { + } } trigger OnInsert() @@ -939,6 +1061,7 @@ table 8057 "Subscription Header" ServiceContractSetup: Record "Subscription Contract Setup"; Cust: Record Customer; PostCode: Record "Post Code"; + PurchSetup: Record "Purchases & Payables Setup"; NoSeries: Codeunit "No. Series"; ContractsItemManagement: Codeunit "Sub. Contracts Item Management"; ConfirmManagement: Codeunit "Confirm Management"; @@ -2264,6 +2387,54 @@ table 8057 "Subscription Header" Rec.SetPrimaryAttributeValueAndCaption(PrimaryAttributeValue, PrimaryAttributeValueCaption); end; + internal procedure UpdateVendorAndManufacturerFromItem(Item: Record Item) + begin + "Vendor No." := Item."Vendor No."; + UpdateVendorName(); + "Vendor Item No." := Item."Vendor Item No."; + + "Manufacturer Code" := Item."Manufacturer Code"; + UpdateManufacturerName(); + end; + + local procedure UpdateVendorName() + var + Vendor: Record Vendor; + begin + if ("Vendor No." <> '') and Vendor.Get("Vendor No.") then + "Vendor Name" := Vendor.Name + else + "Vendor Name" := ''; + end; + + local procedure UpdateManufacturerName() + var + Manufacturer: Record Manufacturer; + begin + if ("Manufacturer Code" <> '') and Manufacturer.Get("Manufacturer Code") then + "Manufacturer Name" := Manufacturer.Name + else + "Manufacturer Name" := ''; + end; + + local procedure ShouldSearchForVendorByName(VendorNo: Code[20]): Boolean + var + Vendor: Record Vendor; + begin + if VendorNo = '' then + exit(true); + + if not PurchSetup.Get() then + PurchSetup.Init(); + if PurchSetup."Disable Search by Name" then + exit(false); + + if not Vendor.Get(VendorNo) then + exit(true); + + exit(not Vendor."Disable Search by Name"); + end; + [IntegrationEvent(false, false)] local procedure OnAfterIsShipToAddressEqualToEndUserAddress(EndUserSubscriptionHeader: Record "Subscription Header"; ShipToSubscriptionHeader: Record "Subscription Header"; var Result: Boolean) begin diff --git a/src/Apps/W1/Subscription Billing/Test/Service Commitments/SalesServiceCommitmentTest.Codeunit.al b/src/Apps/W1/Subscription Billing/Test/Service Commitments/SalesServiceCommitmentTest.Codeunit.al index 7ce8de9b1e..9d65f0c156 100644 --- a/src/Apps/W1/Subscription Billing/Test/Service Commitments/SalesServiceCommitmentTest.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/Test/Service Commitments/SalesServiceCommitmentTest.Codeunit.al @@ -1,11 +1,13 @@ namespace Microsoft.SubscriptionBilling; +using Microsoft.CRM.Team; using Microsoft.Finance.Currency; using Microsoft.Finance.GeneralLedger.Ledger; using Microsoft.Finance.VAT.Setup; using Microsoft.Foundation.NoSeries; using Microsoft.Inventory.Item; using Microsoft.Inventory.Item.Catalog; +using Microsoft.Inventory.Ledger; using Microsoft.Inventory.Location; using Microsoft.Inventory.Requisition; using Microsoft.Inventory.Setup; @@ -197,20 +199,24 @@ codeunit 139915 "Sales Service Commitment Test" RequisitionLine: Record "Requisition Line"; Vendor: Record Vendor; begin + // [SCENARIO] Subscriptions with serial numbers are created when posting a purchase order for a drop shipment sales order. Initialize(); + // [GIVEN] A released sales order with serial no. tracked subscription item set up for drop shipment CreateAndReleaseSalesDocumentWithSerialNoForDropShipment(); LibraryPurchase.CreateVendor(Vendor); Item."Vendor No." := Vendor."No."; Item.Modify(false); + // [WHEN] A purchase order is created from the sales order via requisition worksheet and received RunGetSalesOrders(RequisitionLine, SalesHeader); ReqWkshCarryOutActionMessage(RequisitionLine); PurchaseHeader.SetRange("Buy-from Vendor No.", Vendor."No."); PurchaseHeader.FindLast(); LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, false); + // [THEN] One Subscription per serial number is created with the correct serial number and quantity of 1 TestServiceObjectWithSerialNoExpectedCount(); TestServiceObjectWithSerialNoExists(); end; @@ -1128,10 +1134,10 @@ codeunit 139915 "Sales Service Commitment Test" CustomerPriceGroup: Record "Customer Price Group"; CustomerReference: Text; begin - // Create Item as Sales with Subscription - // Assign negative value to Quantity - // Ship Item - Subscription should not be created + // [SCENARIO] No Subscription is created when shipping a sales order with a negative quantity for a subscription item. Initialize(); + + // [GIVEN] A sales order with a subscription item that has a negative quantity ContractTestLibrary.CreateItemWithServiceCommitmentOption(Item, Enum::"Item Service Commitment Type"::"Sales with Service Commitment"); LibrarySales.CreateCustomer(Customer); LibrarySales.CreateCustomerPriceGroup(CustomerPriceGroup); @@ -1143,8 +1149,11 @@ codeunit 139915 "Sales Service Commitment Test" SalesHeader.Modify(false); LibrarySales.CreateSalesLine(SalesLine, SalesHeader, Enum::"Sales Line Type"::Item, Item."No.", -LibraryRandom.RandInt(100)); + + // [WHEN] The sales order is shipped LibrarySales.PostSalesDocument(SalesHeader, true, false); + // [THEN] No Subscription is created for the item ServiceObject.FilterOnItemNo(Item."No."); ServiceObject.SetRange("Customer Reference", CustomerReference); Assert.RecordIsEmpty(ServiceObject); @@ -1154,14 +1163,19 @@ codeunit 139915 "Sales Service Commitment Test" [HandlerFunctions('MessageHandler')] procedure DoNotCreateServiceObjectWithSerialNoOnShipSalesOrderWithNegativeQuantity() begin + // [SCENARIO] Shipping a sales order with a negative quantity for a serial no. tracked subscription item does not create duplicate Subscriptions. Initialize(); + + // [GIVEN] Subscriptions with serial numbers exist from a previously shipped sales order CreateAndPostSalesDocumentWithSerialNo(true, true); CheckThatOnlyOneServiceObjectWithSerialNoExists(); + // [WHEN] A new sales order with the same item and negative quantity is shipped with serial no. tracking LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, Customer."No."); LibrarySales.CreateSalesLine(SalesLine, SalesHeader, Enum::"Sales Line Type"::Item, Item."No.", -NoOfServiceObjects); CreateSalesLineItemTrackingAndPostSalesDocument(-1, true, false); + // [THEN] No additional Subscriptions are created; only the original ones still exist CheckThatOnlyOneServiceObjectWithSerialNoExists(); end; @@ -1737,6 +1751,128 @@ codeunit 139915 "Sales Service Commitment Test" SalesLine.Delete(true); end; + [Test] + procedure SubscriptionHeaderCreatedFromSalesOrderHasTraceabilityFieldsValues() + var + Vendor: Record Vendor; + Manufacturer: Record Manufacturer; + SalespersonPurchaser: Record "Salesperson/Purchaser"; + SalesInvoiceHeader: Record "Sales Invoice Header"; + ItemLedgerEntry: Record "Item Ledger Entry"; + SubscriptionHeader: Record "Subscription Header"; + TestItems: array[2] of Record Item; + ItemServiceCommitmentTypes: array[2] of Enum "Item Service Commitment Type"; + SalesOrderNo: Code[20]; + i: Integer; + begin + // [SCENARIO] Subscription Header created from Sales Order inherits Vendor, Manufacturer, Salesperson, Sales Order No., Item Ledger Entry No. and Last Sales Invoice No. + // [SCENARIO] Last Sales Invoice No. is only populated for "Sales with Service Commitment" items, not for "Service Commitment Item" items + Initialize(); + + // [GIVEN] Vendor and Manufacturer + LibraryPurchase.CreateVendor(Vendor); + CreateManufacturer(Manufacturer); + + // [GIVEN] Sales Order with Salesperson + LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, LibrarySales.CreateCustomerNo()); + LibrarySales.CreateSalesperson(SalespersonPurchaser); + SalesHeader.Validate("Salesperson Code", SalespersonPurchaser.Code); + SalesHeader.Modify(true); + SalesOrderNo := SalesHeader."No."; + + // [GIVEN] Items with different Service Commitment Types + ItemServiceCommitmentTypes[1] := Enum::"Item Service Commitment Type"::"Sales with Service Commitment"; + ItemServiceCommitmentTypes[2] := Enum::"Item Service Commitment Type"::"Service Commitment Item"; + for i := 1 to ArrayLen(ItemServiceCommitmentTypes) do begin + ContractTestLibrary.CreateItemWithServiceCommitmentOption(TestItems[i], ItemServiceCommitmentTypes[i]); + TestItems[i].Validate("Vendor No.", Vendor."No."); + TestItems[i].Validate("Vendor Item No.", LibraryUtility.GenerateRandomCode(TestItems[i].FieldNo("Vendor Item No."), Database::Item)); + TestItems[i].Validate("Manufacturer Code", Manufacturer.Code); + TestItems[i].Modify(true); + LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, TestItems[i]."No.", LibraryRandom.RandIntInRange(1, 10)); + end; + + // [WHEN] Sales Order is posted (Ship and Invoice) + LibrarySales.PostSalesDocument(SalesHeader, true, true); + + // [THEN] Subscription Headers are created with correct traceability fields + SalesInvoiceHeader.SetRange("Order No.", SalesOrderNo); + SalesInvoiceHeader.FindFirst(); + + for i := 1 to ArrayLen(TestItems) do begin + SubscriptionHeader.Reset(); + SubscriptionHeader.SetRange("Sales Order No.", SalesOrderNo); + SubscriptionHeader.SetRange("Source No.", TestItems[i]."No."); + Assert.RecordIsNotEmpty(SubscriptionHeader); + SubscriptionHeader.FindFirst(); + + Assert.AreEqual(Vendor."No.", SubscriptionHeader."Vendor No.", 'Vendor No. should match Item Vendor No.'); + Assert.AreEqual(Vendor.Name, SubscriptionHeader."Vendor Name", 'Vendor Name should match Vendor Name'); + Assert.AreEqual(TestItems[i]."Vendor Item No.", SubscriptionHeader."Vendor Item No.", 'Vendor Item No. should match Item Vendor Item No.'); + Assert.AreEqual(Manufacturer.Code, SubscriptionHeader."Manufacturer Code", 'Manufacturer Code should match Item Manufacturer Code'); + Assert.AreEqual(Manufacturer.Name, SubscriptionHeader."Manufacturer Name", 'Manufacturer Name should match Manufacturer Name'); + Assert.AreEqual(SalespersonPurchaser.Code, SubscriptionHeader."Salesperson Code", 'Salesperson Code should match Sales Header Salesperson Code'); + Assert.AreEqual(SalesOrderNo, SubscriptionHeader."Sales Order No.", 'Sales Order No. should match Sales Header No.'); + + ItemLedgerEntry.Reset(); + ItemLedgerEntry.SetRange("Item No.", TestItems[i]."No."); + ItemLedgerEntry.FindLast(); + Assert.AreEqual(ItemLedgerEntry."Entry No.", SubscriptionHeader."Item Ledger Entry No.", 'Item Ledger Entry No. should be populated'); + + if ItemServiceCommitmentTypes[i] = Enum::"Item Service Commitment Type"::"Sales with Service Commitment" then + Assert.AreEqual(SalesInvoiceHeader."No.", SubscriptionHeader."Last Sales Invoice No.", 'Last Sales Invoice No. should be populated for Sales with Service Commitment') + else + Assert.AreEqual('', SubscriptionHeader."Last Sales Invoice No.", 'Last Sales Invoice No. should be empty for Service Commitment Item'); + end; + end; + + [Test] + procedure ManualCreationOfSubscriptionHeaderPopulatesVendorManufacturerAndSalespersonFromCustomer() + var + Vendor: Record Vendor; + Manufacturer: Record Manufacturer; + SalespersonPurchaser: Record "Salesperson/Purchaser"; + begin + // [SCENARIO] Manually validating Source No. on Subscription Header populates Vendor and Manufacturer fields from Item + // [SCENARIO] Validating End-User Customer No. populates Salesperson Code from Customer + Initialize(); + + // [GIVEN] Vendor and Manufacturer + LibraryPurchase.CreateVendor(Vendor); + CreateManufacturer(Manufacturer); + + // [GIVEN] Item with Vendor and Manufacturer + ContractTestLibrary.CreateItemWithServiceCommitmentOption(Item, Enum::"Item Service Commitment Type"::"Service Commitment Item"); + Item.Validate("Vendor No.", Vendor."No."); + Item.Validate("Vendor Item No.", LibraryUtility.GenerateRandomCode(Item.FieldNo("Vendor Item No."), Database::Item)); + Item.Validate("Manufacturer Code", Manufacturer.Code); + Item.Modify(true); + + // [GIVEN] Customer with Salesperson + LibrarySales.CreateSalesperson(SalespersonPurchaser); + Customer.Get(LibrarySales.CreateCustomerNo()); + Customer.Validate("Salesperson Code", SalespersonPurchaser.Code); + Customer.Modify(true); + + // [WHEN] New Subscription Header is created and Source No. and Customer are validated + ServiceObject.Init(); + ServiceObject.Insert(true); + ServiceObject.Validate(Type, ServiceObject.Type::Item); + ServiceObject.Validate("Source No.", Item."No."); + ServiceObject.Validate("End-User Customer No.", Customer."No."); + ServiceObject.Modify(true); + + // [THEN] Vendor and Manufacturer fields are populated from Item + Assert.AreEqual(Vendor."No.", ServiceObject."Vendor No.", 'Vendor No. should be populated from Item'); + Assert.AreEqual(Vendor.Name, ServiceObject."Vendor Name", 'Vendor Name should be populated from Vendor'); + Assert.AreEqual(Item."Vendor Item No.", ServiceObject."Vendor Item No.", 'Vendor Item No. should be populated from Item'); + Assert.AreEqual(Manufacturer.Code, ServiceObject."Manufacturer Code", 'Manufacturer Code should be populated from Item'); + Assert.AreEqual(Manufacturer.Name, ServiceObject."Manufacturer Name", 'Manufacturer Name should be populated from Manufacturer'); + + // [THEN] Salesperson Code is populated from Customer + Assert.AreEqual(SalespersonPurchaser.Code, ServiceObject."Salesperson Code", 'Salesperson Code should be populated from Customer'); + end; + #endregion Tests #region Procedures @@ -1856,6 +1992,14 @@ codeunit 139915 "Sales Service Commitment Test" NewCustomer.Modify(true); end; + local procedure CreateManufacturer(var Manufacturer: Record Manufacturer) + begin + Manufacturer.Init(); + Manufacturer.Code := LibraryUtility.GenerateRandomCode(Manufacturer.FieldNo(Code), Database::Manufacturer); + Manufacturer.Name := CopyStr(LibraryUtility.GenerateRandomText(MaxStrLen(Manufacturer.Name)), 1, MaxStrLen(Manufacturer.Name)); + Manufacturer.Insert(true); + end; + local procedure CreateNoSeriesWithLine(): Code[20] var NoSeries: Record "No. Series"; From 2cbbed7af2ee4f358ff3a0de13060ea22bb6c774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miljan=20Milosavljevi=C4=87?= Date: Mon, 9 Feb 2026 13:47:21 +0100 Subject: [PATCH 2/4] Adressing feedback --- .../App/APIs/Pages/ServiceObjectAPI.Page.al | 30 ++++++++ .../Codeunits/SalesDocuments.Codeunit.al | 43 +++--------- .../Pages/ServiceCommPackageLines.Page.al | 2 +- .../Pages/ServiceCommitmentTemplates.Page.al | 2 +- .../Pages/ServiceObject.Page.al | 4 ++ .../Tables/SubscriptionHeader.Table.al | 69 +++++++++++++------ 6 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/Apps/W1/Subscription Billing/App/APIs/Pages/ServiceObjectAPI.Page.al b/src/Apps/W1/Subscription Billing/App/APIs/Pages/ServiceObjectAPI.Page.al index 1af2b86966..962fe50a8f 100644 --- a/src/Apps/W1/Subscription Billing/App/APIs/Pages/ServiceObjectAPI.Page.al +++ b/src/Apps/W1/Subscription Billing/App/APIs/Pages/ServiceObjectAPI.Page.al @@ -186,6 +186,36 @@ page 8020 "Service Object API" field(unitOfMeasure; Rec."Unit of Measure") { } + field(vendorNo; Rec."Vendor No.") + { + } + field(vendorName; Rec."Vendor Name") + { + } + field(vendorName2; Rec."Vendor Name 2") + { + } + field(vendorItemNo; Rec."Vendor Item No.") + { + } + field(manufacturerCode; Rec."Manufacturer Code") + { + } + field(manufacturerName; Rec."Manufacturer Name") + { + } + field(salespersonCode; Rec."Salesperson Code") + { + } + field(salesOrderNo; Rec."Sales Order No.") + { + } + field(itemLedgerEntryNo; Rec."Item Ledger Entry No.") + { + } + field(lastSalesInvoiceNo; Rec."Last Sales Invoice No.") + { + } } field(systemCreatedAt; Rec.SystemCreatedAt) { diff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al index 0c416205a1..304e6a27ad 100644 --- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al +++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/SalesDocuments.Codeunit.al @@ -370,6 +370,7 @@ codeunit 8063 "Sales Documents" exit; if ItemJournalLine.Quantity < 0 then begin ShowNegativeQuantityMessageIfNeeded(ItemJournalLine.Quantity); + ResetGlobalVariables(); exit; end; @@ -387,21 +388,28 @@ codeunit 8063 "Sales Documents" procedure CreateServiceObjectFromSales(var SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; var SalesShptLine: Record "Sales Shipment Line") var TempTrackingSpecBuffer: Record "Tracking Specification" temporary; + Item: Record Item; ItemTrackingDocMgt: Codeunit "Item Tracking Doc. Management"; begin //The function creates Subscription for Sales Line with Subscription Lines if SalesServiceCommMgmt.IsSalesLineWithSalesServiceCommitmentsToShip(SalesLine) then begin if SalesShptLine.Quantity < 0 then begin ShowNegativeQuantityMessageIfNeeded(SalesShptLine.Quantity); + ResetGlobalVariables(); exit; end; ItemTrackingDocMgt.RetrieveDocumentItemTracking(TempTrackingSpecBuffer, SalesShptLine."Document No.", Database::"Sales Shipment Header", 0); TempTrackingSpecBuffer.SetRange("Source Ref. No.", SalesShptLine."Line No."); - if not TempTrackingSpecBuffer.IsEmpty() then - CreateServiceObjectFromTrackingSpecification(SalesHeader, SalesLine, TempTrackingSpecBuffer) + if TempTrackingSpecBuffer.FindSet() then + repeat + CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, TempTrackingSpecBuffer."Serial No.", TempTrackingSpecBuffer."Quantity (Base)", 0); + until TempTrackingSpecBuffer.Next() = 0 else - CreateServiceObjectFromSalesLine(SalesHeader, SalesLine); + if SalesLine.Type = Enum::"Sales Line Type"::Item then + if Item.Get(SalesLine."No.") then + if not Item.HasSNSpecificItemTracking() then + CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, '', 0, 0); end; end; #endif @@ -476,22 +484,6 @@ codeunit 8063 "Sales Documents" exit(SalesServiceCommMgmt.IsSalesLineWithServiceCommitmentItem(TempSalesLine, true) or ContractRenewalMgt.IsContractRenewal(TempSalesLine)); end; -#if not CLEAN28 - [Obsolete('Use OnAfterPostItemJnlLine event subscriber to create Subscription Headers during item journal posting', '28.0')] - local procedure CreateServiceObjectFromSalesLine(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line") - var - Item: Record Item; - begin - if SalesLine.Type <> Enum::"Sales Line Type"::Item then - exit; - if not Item.Get(SalesLine."No.") then - exit; - if Item.HasSNSpecificItemTracking() then - exit; - CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, '', 0, 0); - end; -#endif - local procedure CreateServiceObjectFromSalesLine(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; SerialNo: Code[50]; QtyPerSerialNo: Decimal; ItemLedgerEntryNo: Integer) var ServiceObject: Record "Subscription Header"; @@ -651,19 +643,6 @@ codeunit 8063 "Sales Documents" end; end; -#if not CLEAN28 - [Obsolete('Creation of Subscription Header happens in OnAfterPostItemJnlLine when Item Journal Line is posted', '28.0')] - local procedure CreateServiceObjectFromTrackingSpecification(var SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; var TempTrackingSpecBuffer: Record "Tracking Specification" temporary) - begin - if TempTrackingSpecBuffer.FindSet() then - repeat - CreateServiceObjectFromSalesLine(SalesHeader, SalesLine, TempTrackingSpecBuffer."Serial No.", TempTrackingSpecBuffer."Quantity (Base)", 0); - until TempTrackingSpecBuffer.Next() = 0; - TempTrackingSpecBuffer.Reset(); - TempTrackingSpecBuffer.DeleteAll(false); - end; -#endif - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Whse.-Activity-Post", OnUpdateSourceDocumentOnAfterSalesLineModify, '', false, false)] local procedure ModifyShipmentDateFromInventoryPickPostingDate(var SalesLine: Record "Sales Line"; WarehouseActivityLine: Record "Warehouse Activity Line") var diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al index d0bc7ad458..80eac6183c 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommPackageLines.Page.al @@ -56,7 +56,7 @@ page 8058 "Service Comm. Package Lines" { Style = Strong; StyleExpr = Bold; - ToolTip = 'Specifies the percentage at which the price of the Subscription Line is calculated. 100% means that the the price is the same as the calculation base (item or document).'; + ToolTip = 'Specifies the percentage at which the price of the Subscription Line is calculated. 100% means that the price is the same as the calculation base (item or document).'; } field("Billing Base Period"; Rec."Billing Base Period") { diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentTemplates.Page.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentTemplates.Page.al index 5b8a058150..8322e90091 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentTemplates.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Pages/ServiceCommitmentTemplates.Page.al @@ -37,7 +37,7 @@ page 8055 "Service Commitment Templates" } field("Calculation Base %"; Rec."Calculation Base %") { - ToolTip = 'Specifies the percentage at which the price of the Subscription Line is calculated. 100% means that the the price is the same as the calculation base (item or document).'; + ToolTip = 'Specifies the percentage at which the price of the Subscription Line is calculated. 100% means that the price is the same as the calculation base (item or document).'; } field("Billing Base Period"; Rec."Billing Base Period") { diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al index 77cffe5948..1ab8b927c1 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Pages/ServiceObject.Page.al @@ -523,6 +523,10 @@ page 8060 "Service Object" field("Vendor Name"; Rec."Vendor Name") { } + field("Vendor Name 2"; Rec."Vendor Name 2") + { + Importance = Additional; + } field("Vendor Item No."; Rec."Vendor Item No.") { } diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al index 469df9a482..fb5d198236 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al @@ -925,7 +925,8 @@ table 8057 "Subscription Header" trigger OnValidate() begin - UpdateVendorName(); + if "Vendor No." <> xRec."Vendor No." then + UpdateVendorName(); end; } field(8001; "Vendor Name"; Text[100]) @@ -941,7 +942,7 @@ table 8057 "Subscription Header" var Vendor: Record Vendor; begin - if Rec."Vendor Name" <> xRec."Vendor Name" then + if "Vendor Name" <> xRec."Vendor Name" then if ShouldSearchForVendorByName("Vendor No.") then Validate("Vendor No.", Vendor.GetVendorNo("Vendor Name")); end; @@ -954,14 +955,21 @@ table 8057 "Subscription Header" Validate("Vendor No.", Vendor."No."); end; } - field(8002; "Vendor Item No."; Text[50]) + field(8002; "Vendor Name 2"; Text[50]) + { + Caption = 'Vendor Name 2'; + FieldClass = Normal; + ToolTip = 'Specifies an additional part of the name of the default vendor of this item.'; + Editable = false; + } + field(8003; "Vendor Item No."; Text[50]) { Caption = 'Vendor Item No.'; FieldClass = Normal; Editable = false; - ToolTip = 'Specifies the the default vendor''s item no.'; + ToolTip = 'Specifies the default vendor''s item no.'; } - field(8003; "Manufacturer Code"; Code[10]) + field(8010; "Manufacturer Code"; Code[10]) { Caption = 'Manufacturer Code'; TableRelation = Manufacturer; @@ -970,10 +978,11 @@ table 8057 "Subscription Header" trigger OnValidate() begin - UpdateManufacturerName(); + if "Manufacturer Code" <> xRec."Manufacturer Code" then + UpdateManufacturerName(); end; } - field(8004; "Manufacturer Name"; Text[50]) + field(8011; "Manufacturer Name"; Text[50]) { Caption = 'Manufacturer Name'; FieldClass = Normal; @@ -986,11 +995,12 @@ table 8057 "Subscription Header" var Manufacturer: Record Manufacturer; begin - if "Manufacturer Name" <> '' then begin - Manufacturer.SetRange(Name, "Manufacturer Name"); - if Manufacturer.FindFirst() then - Validate("Manufacturer Code", Manufacturer.Code); - end; + if "Manufacturer Name" <> xRec."Manufacturer Name" then + if "Manufacturer Name" <> '' then begin + Manufacturer.SetRange(Name, "Manufacturer Name"); + if Manufacturer.FindFirst() then + Validate("Manufacturer Code", Manufacturer.Code); + end; end; trigger OnLookup() @@ -1001,27 +1011,33 @@ table 8057 "Subscription Header" Validate("Manufacturer Code", Manufacturer.Code); end; } - field(8005; "Salesperson Code"; Code[20]) + field(8020; "Salesperson Code"; Code[20]) { Caption = 'Salesperson Code'; - TableRelation = "Salesperson/Purchaser"; + TableRelation = "Salesperson/Purchaser" where(Blocked = const(false)); ToolTip = 'Specifies the salesperson who is assigned to the customer.'; Editable = true; + + trigger OnValidate() + begin + if "Salesperson Code" <> xRec."Salesperson Code" then + ValidateSalespersonCode(); + end; } - field(8006; "Sales Order No."; Code[20]) + field(8021; "Sales Order No."; Code[20]) { Caption = 'Sales Order No.'; Editable = false; ToolTip = 'Indicates the sales order used to create the subscription.'; } - field(8008; "Item Ledger Entry No."; Integer) + field(8022; "Item Ledger Entry No."; Integer) { Caption = 'Item Ledger Entry No.'; TableRelation = "Item Ledger Entry"; Editable = false; ToolTip = 'Specifies the item ledger entry number that was used to create the subscription.'; } - field(8009; "Last Sales Invoice No."; Code[20]) + field(8023; "Last Sales Invoice No."; Code[20]) { Caption = 'Last Sales Invoice No.'; Editable = false; @@ -2401,10 +2417,13 @@ table 8057 "Subscription Header" var Vendor: Record Vendor; begin - if ("Vendor No." <> '') and Vendor.Get("Vendor No.") then - "Vendor Name" := Vendor.Name - else + if ("Vendor No." <> '') and Vendor.Get("Vendor No.") then begin + "Vendor Name" := Vendor.Name; + "Vendor Name 2" := Vendor."Name 2"; + end else begin "Vendor Name" := ''; + "Vendor Name 2" := ''; + end; end; local procedure UpdateManufacturerName() @@ -2417,6 +2436,16 @@ table 8057 "Subscription Header" "Manufacturer Name" := ''; end; + local procedure ValidateSalespersonCode() + var + SalespersonPurchaser: Record "Salesperson/Purchaser"; + begin + if "Salesperson Code" <> '' then + if SalespersonPurchaser.Get("Salesperson Code") then + if SalespersonPurchaser.VerifySalesPersonPurchaserPrivacyBlocked(SalespersonPurchaser) then + Error(SalespersonPurchaser.GetPrivacyBlockedGenericText(SalespersonPurchaser, true)); + end; + local procedure ShouldSearchForVendorByName(VendorNo: Code[20]): Boolean var Vendor: Record Vendor; From e89d358eac206abc6614b494c42987c74efdb289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miljan=20Milosavljevi=C4=87?= Date: Mon, 9 Feb 2026 13:50:21 +0100 Subject: [PATCH 3/4] Adressing feedback *2 --- .../App/Service Objects/Tables/SubscriptionHeader.Table.al | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al index fb5d198236..a8795dabde 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al @@ -960,7 +960,6 @@ table 8057 "Subscription Header" Caption = 'Vendor Name 2'; FieldClass = Normal; ToolTip = 'Specifies an additional part of the name of the default vendor of this item.'; - Editable = false; } field(8003; "Vendor Item No."; Text[50]) { From 6a9affb0c945247c1d63de06ce3aeaf1e00fdeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miljan=20Milosavljevi=C4=87?= Date: Thu, 12 Feb 2026 10:27:44 +0100 Subject: [PATCH 4/4] replaced word "Indicates" with "Specifies" --- .../App/Service Objects/Tables/SubscriptionHeader.Table.al | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al index a8795dabde..011f2c1ea6 100644 --- a/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al +++ b/src/Apps/W1/Subscription Billing/App/Service Objects/Tables/SubscriptionHeader.Table.al @@ -1027,7 +1027,7 @@ table 8057 "Subscription Header" { Caption = 'Sales Order No.'; Editable = false; - ToolTip = 'Indicates the sales order used to create the subscription.'; + ToolTip = 'Specifies the sales order used to create the subscription.'; } field(8022; "Item Ledger Entry No."; Integer) { @@ -1040,7 +1040,7 @@ table 8057 "Subscription Header" { Caption = 'Last Sales Invoice No.'; Editable = false; - ToolTip = 'Indicates the last sales invoice for the sales order that was used to create the subscription.'; + ToolTip = 'Specifies the last sales invoice for the sales order that was used to create the subscription.'; } } keys