0

So we are trying to shore up some gaps with implementing Microsoft Dynamics Business Central 365. Currently testing out v19 W1 on-prem. One of our use cases involves serialized items. To help shortcut the process, since the Serial No. values are unique across all items, we'd like to be able to manage items at the serial level. Rather than employ the two-step process of first providing the Item No. value and then the Serial No. value for each transaction.

Here is a basic example. In the Transfer Order page we'd like to have the user barcode scan or hand-key the Serial No. into the Item No. field for a new transfer line. Through my AL code I'd then take that Serial No. value, backtrace it to the "parent" Item No., temporarily store the Serial No. value, and then replace the field contents with the proper Item No. value. That's step one.

Step two would then involve adding a Reservation Entry line for the underlying Serial No. So that the item tracking would properly reflect the Serial No. that's associated with the transfer line. When I load up my AL extension code I encounter a quantity error, due to serialization. Although I can manually provide the same exact Item No. in that particular field and the page allows me to then proceed along in providing the quantity and whatnot.

I was advised that I need to programmatically create the transfer line, so I have attempted to define all pertinent property values. I'll paste in my AL code, along with a screen shot of the validation error. Any advice would be appreciated, as always!

tableextension 50103 "DchApi_TransferTableExt" extends "Transfer Line"
{
    fields
    {
        modify("Item No.")
        {
            trigger OnBeforeValidate()
            var
                Rec_ILE: Record "Item Ledger Entry";
                Rec_ResEnt: Record "Reservation Entry" temporary;
                Rec_ResEnt_Lu: Record "Reservation Entry";
                Rec_TransLine: Record "Transfer Line";
                Rec_Item: Record Item;
                Rec_TransHdr: Record "Transfer Header";
                CreateReservEntry: Codeunit "Create Reserv. Entry";
                ItemTrackingMgt: Codeunit "Item Tracking Management";
                ReservStatus: Enum "Reservation Status";
                CurrentSourceRowID: Text[250];
                SecondSourceRowID: Text[250];
                SerialNo: Code[20];
                ItemNo: Code[20];
                ShortDim1Code: Code[20];
                ShortDim2Code: Code[20];
                Description: Text[100];
                GenProdPostGrp: Code[20];
                InvPostGrp: Code[20];
                ItemCatCode: Code[20];
                InTransCode: Code[10];
                TransFromCode: Code[10];
                TransToCode: Code[10];
                LineNo: Integer;
            begin
                SerialNo := Rec."Item No.";

                Rec_ILE.Reset();
                Rec_ILE.SetRange("Entry Type", Rec_ILE."Entry Type"::Purchase);
                Rec_ILE.SetRange("Item Tracking", Rec_ILE."Item Tracking"::"Serial No.");
                Rec_ILE.SetFilter("Serial No.", '%1', SerialNo);
                if Rec_ILE.FindFirst() then begin
                    ItemNo := Rec_ILE."Item No.";

                    Rec_Item.Reset();
                    Rec_Item.SetFilter("No.", ItemNo);
                    if Rec_Item.FindFirst() then begin
                        ShortDim1Code := Rec_Item."Global Dimension 1 Code";
                        ShortDim2Code := Rec_Item."Global Dimension 2 Code";
                        Description := Rec_Item.Description;
                        GenProdPostGrp := Rec_Item."Gen. Prod. Posting Group";
                        InvPostGrp := Rec_Item."Inventory Posting Group";
                        ItemCatCode := Rec_Item."Item Category Code";

                        Rec_TransHdr.Reset();
                        Rec_TransHdr.SetRange("No.", Rec."Document No.");
                        if Rec_TransHdr.FindFirst() then begin
                            InTransCode := Rec_TransHdr."In-Transit Code";
                            TransFromCode := Rec_TransHdr."Transfer-from Code";
                            TransToCode := Rec_TransHdr."Transfer-to Code";

                            Rec_TransLine.Reset();
                            Rec_TransLine.SetRange("Document No.", Rec."Document No.");
                            if Rec_TransLine.FindLast() then
                                LineNo := Rec_TransLine."Line No." + 10000
                            else
                                LineNo := 10000;

                            Validate(Rec."Document No.");
                            Validate(Rec."Line No.", LineNo);
                            Validate(Rec."Item No.", ItemNo);
                            Validate(Rec."Variant Code", '');
                            Validate(Rec."Shortcut Dimension 1 Code", ShortDim1Code);
                            Validate(Rec."Shortcut Dimension 2 Code", ShortDim2Code);
                            Validate(Rec.Description, Description);
                            Validate(Rec."Gen. Prod. Posting Group", GenProdPostGrp);
                            Validate(Rec."Inventory Posting Group", InvPostGrp);
                            Validate(Rec."Item Category Code", ItemCatCode);
                            Validate(Rec.Quantity, 1);
                            Validate(Rec."Unit of Measure Code", 'PCS');
                            Validate(Rec."Qty. to Ship", 1);
                            Validate(Rec."Qty. per Unit of Measure", 1);
                            Validate(Rec.Status, Rec.Status::Open);
                            Validate(Rec."In-Transit Code", InTransCode);
                            Validate(Rec."Transfer-from Code", TransFromCode);
                            Validate(Rec."Transfer-to Code", TransToCode);
                            Rec.Insert();

                            Rec_ResEnt.Init();
                            Rec_ResEnt_Lu.Reset();
                            if Rec_ResEnt_Lu.FindLast() then
                                Rec_ResEnt."Entry No." := Rec_ResEnt_Lu."Entry No." + 1
                            else
                                Rec_ResEnt."Entry No." := 1;
                            Rec_ResEnt."Expiration Date" := Today();
                            Rec_ResEnt.Quantity := 1;
                            Rec_ResEnt."Serial No." := SerialNo;
                            Rec_ResEnt.Insert();

                            CreateReservEntry.SetDates(0D, Rec_ResEnt."Expiration Date");
                            CreateReservEntry.CreateReservEntryFor(Database::"Transfer Line", 0, Rec."Document No.", '', Rec."Derived From Line No.", Rec."Line No.",
                                Rec."Qty. per Unit of Measure", Rec_ResEnt.Quantity, Rec."Qty. per Unit of Measure" * Rec_ResEnt.Quantity, Rec_ResEnt);
                            CreateReservEntry.CreateEntry(Rec."Item No.", Rec."Variant Code", Rec."Transfer-from Code", Rec.Description, Rec."Receipt Date", 0D, 0, ReservStatus::Surplus);
                            CurrentSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 0, Rec."Document No.", '', 0, Rec."Line No.");
                            SecondSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 1, Rec."Document No.", '', 0, Rec."Line No.");
                            ItemTrackingMgt.SynchronizeItemTracking(CurrentSourceRowID, SecondSourceRowID, '');
                        end;
                    end;
                end;
            end;
        }
    }
}

Validation Error

gregarican
  • 105
  • 1
  • 11
  • I think I'm getting closer. My trigger code is operating from the transfer line, and I can see the new line is inserted okay if I place a breakpoint right after the Insert() line. But the subsequent code that's placing a reservation entry line and then item tracking lines is where things are choking. When I comment out those subsequent code portions then the transfer line is added without issue. I'll need to split out those other actions so they aren't located in the same trigger. I tried adding Commit() lines after each Insert() but that isn't sufficient. – gregarican Apr 26 '22 at 17:41
  • Finally on the right track. Actually the Commit() lines do properly stow away things on the back-end. The front end Transfer Order page throws the validation error, but when I go back into the particular Transfer Order, the line is properly defined against the Item No. and then the Serial No. in item tracking. I just need to figure out how to flush the table changes back up to the page so that it looks correct. :) – gregarican Apr 26 '22 at 17:54

1 Answers1

0

I figured it out. I added the Serial No. value as a global variable, and broke out the reservation entry and item tracking into an OnAfterValidate() trigger. Everything works fine now. Full source code below.

tableextension 50103 "DchApi_TransferTableExt" extends "Transfer Line"
{
    fields
    {
        modify("Item No.")
        {
            trigger OnBeforeValidate()
            var
                Rec_ILE: Record "Item Ledger Entry";
                Rec_TransLine: Record "Transfer Line";
                Rec_Item: Record Item;
                Rec_TransHdr: Record "Transfer Header";
                ItemNo: Code[20];
                SerialNo: Code[20];
                ShortDim1Code: Code[20];
                ShortDim2Code: Code[20];
                Description: Text[100];
                GenProdPostGrp: Code[20];
                InvPostGrp: Code[20];
                ItemCatCode: Code[20];
                InTransCode: Code[10];
                TransFromCode: Code[10];
                TransToCode: Code[10];
                LineNo: Integer;
                InsertResult: Boolean;
            begin
                SerialNo := Rec."Item No.";
                Rec_ILE.Reset();
                Rec_ILE.SetRange("Entry Type", Rec_ILE."Entry Type"::Purchase);
                Rec_ILE.SetRange("Item Tracking", Rec_ILE."Item Tracking"::"Serial No.");
                Rec_ILE.SetFilter("Serial No.", '%1', SerialNo);
                if Rec_ILE.FindFirst() then begin
                    ItemNo := Rec_ILE."Item No.";

                    Rec_Item.Reset();
                    Rec_Item.SetFilter("No.", ItemNo);
                    if Rec_Item.FindFirst() then begin
                        ShortDim1Code := Rec_Item."Global Dimension 1 Code";
                        ShortDim2Code := Rec_Item."Global Dimension 2 Code";
                        Description := Rec_Item.Description;
                        GenProdPostGrp := Rec_Item."Gen. Prod. Posting Group";
                        InvPostGrp := Rec_Item."Inventory Posting Group";
                        ItemCatCode := Rec_Item."Item Category Code";

                        Rec_TransHdr.Reset();
                        Rec_TransHdr.SetRange("No.", Rec."Document No.");
                        if Rec_TransHdr.FindFirst() then begin
                            InTransCode := Rec_TransHdr."In-Transit Code";
                            TransFromCode := Rec_TransHdr."Transfer-from Code";
                            TransToCode := Rec_TransHdr."Transfer-to Code";

                            Rec_TransLine.Reset();
                            Rec_TransLine.SetRange("Document No.", Rec."Document No.");
                            if Rec_TransLine.FindLast() then
                                LineNo := Rec_TransLine."Line No." + 10000
                            else
                                LineNo := 10000;

                            Validate(Rec."Document No.");
                            Validate(Rec."Line No.", LineNo);
                            Validate(Rec."Item No.", ItemNo);
                            Validate(Rec."Description 2", SerialNo);
                            gblSerialNo := Rec."Description 2";
                            Validate(Rec."Variant Code", '');
                            Validate(Rec."Shortcut Dimension 1 Code", '200');
                            Validate(Rec."Shortcut Dimension 2 Code", '01');
                            Validate(Rec.Description, Description);
                            Validate(Rec."Gen. Prod. Posting Group", GenProdPostGrp);
                            Validate(Rec."Inventory Posting Group", InvPostGrp);
                            Validate(Rec."Item Category Code", ItemCatCode);
                            Validate(Rec.Quantity, 1);
                            Validate(Rec."Unit of Measure Code", 'PCS');
                            Validate(Rec."Qty. to Ship", 1);
                            Validate(Rec."Qty. per Unit of Measure", 1);
                            Validate(Rec.Status, Rec.Status::Open);
                            Validate(Rec."In-Transit Code", InTransCode);
                            Validate(Rec."Transfer-from Code", TransFromCode);
                            Validate(Rec."Transfer-to Code", TransToCode);
                            InsertResult := Rec.Insert();
                            Commit();
                        end;
                    end;
                end;
            end;

            trigger OnAfterValidate()
            var
                Rec_ResEnt: Record "Reservation Entry" temporary;
                Rec_ResEnt_Lu: Record "Reservation Entry";
                CreateReservEntry: Codeunit "Create Reserv. Entry";
                ItemTrackingMgt: Codeunit "Item Tracking Management";
                ReservStatus: Enum "Reservation Status";
                CurrentSourceRowID: Text[250];
                SecondSourceRowID: Text[250];
            begin
                Rec_ResEnt.Init();
                Rec_ResEnt_Lu.Reset();
                if Rec_ResEnt_Lu.FindLast() then
                    Rec_ResEnt."Entry No." := Rec_ResEnt_Lu."Entry No." + 1
                else
                    Rec_ResEnt."Entry No." := 1;
                Rec_ResEnt."Expiration Date" := Today();
                Rec_ResEnt.Quantity := 1;
                Rec_ResEnt."Serial No." := gblSerialNo;
                Rec_ResEnt.Insert();
                Commit();

                CreateReservEntry.SetDates(0D, Rec_ResEnt."Expiration Date");
                CreateReservEntry.CreateReservEntryFor(Database::"Transfer Line", 0, Rec."Document No.", '', Rec."Derived From Line No.", Rec."Line No.",
                    Rec."Qty. per Unit of Measure", Rec_ResEnt.Quantity, Rec."Qty. per Unit of Measure" * Rec_ResEnt.Quantity, Rec_ResEnt);
                CreateReservEntry.CreateEntry(Rec."Item No.", Rec."Variant Code", Rec."Transfer-from Code", Rec.Description, Rec."Receipt Date", 0D, 0, ReservStatus::Surplus);
                CurrentSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 0, Rec."Document No.", '', 0, Rec."Line No.");
                SecondSourceRowID := ItemTrackingMgt.ComposeRowID(5741, 1, Rec."Document No.", '', 0, Rec."Line No.");
                ItemTrackingMgt.SynchronizeItemTracking(CurrentSourceRowID, SecondSourceRowID, '');
            end;
        }
    }
    var
        gblSerialNo: Code[20];
}
gregarican
  • 105
  • 1
  • 11