Skip to main content

Notifications

Announcements

No record found.

Small and medium business | Business Central, N...
Answered

Add serialized item tracking details to a transfer

(0) ShareShare
ReportReport
Posted on by 289

So I am attempting to implement similar methodology to what's described here --> https://chrisdsilvablog.wordpress.com/2018/11/09/bar-code-integration-with-business-central/. In my case the user provides a Serial No. value into the item transfer order line, which is then backtraced so that the Item No. is then replaced into that field. And I am also needing to take an extra step, of adding the Serial No. item tracking details for the item transfer order line.

I ran across this helpful blog post -->   which provides an example of creating those reservation entries. I will paste my AL code below and was wondering if any insight could be offered. When I am attempting to paste in a Serial No. into the transfer line's Item No. field, the translation doesn't occur. I just run into an error stating the transfer line for Line No. = 0 doesn't exist. I did place a Message() line right after the cross-reference lookup completes and can see the Item No. does pop up. But for some reason it's not pushing back into that field. Perhaps I should be just creating an extension of the form and not the table? I'm using BC365 v19 W1 on-prem.

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";
                CreateReservEntry: Codeunit "Create Reserv. Entry";
                ItemTrackingMgt: Codeunit "Item Tracking Management";
                ReservStatus: Enum "Reservation Status";
                CurrentSourceRowID: Text[250];
                SecondSourceRowID: Text[250];
                SerialNo: Code[20];
            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', Rec."Item No.");
                if Rec_ILE.FindFirst() then begin
                    Message('The Item No. value is '   Rec_ILE."Item No.");
                    
                    // Replace the Item No. field with the true Item No. value for the item transfer line.
                    Validate(Rec."Item No.", Rec_ILE."Item No.");

                    // Add to the Quantity field value for the item transfer line.
                    Validate(Rec.Quantity, 1);

                    // Create a tracking specification entry for the item transfer line for the Serial No. value.
                    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;
        }
    }
}
  

Transfer_2D00_Order_2D00_Error.png Transfer_2D00_Order_2D00_MsgBox.png

  • Suggested answer
    YUN ZHU Profile Picture
    YUN ZHU 76,095 Super User 2024 Season 2 on at
    RE: Add serialized item tracking details to a transfer

    Thanks for sharing.

  • Suggested answer
    Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer

    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. Including the 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];
    }
    

  • Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer

    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. :)

  • Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer

    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.

    Insert_2D00_Success.png

  • Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer
    [quote user="ZHU YUN"]

    Hi, just to add some info.

    If you want to create Item Tracking Lines automatically, hope the following will help.

    https://yzhums.com/7943/

    Thanks.

    ZHU

    [/quote]

    Thanks for this. I did run across your helpful article when I was searching for a solution. This would presumably take care of that piece. But where I'm getting stuck is initially creating a new item transfer line based on the user providing the Serial No. value. That line won't properly validate and save, so I can't get to the item tracking part yet.

  • Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer

    So now am attempting to programmatically assign all assumedly required fields for the new transfer line. Below is my revised source code, as you can see I'm pulling more fields as to the item specifics as well as the transfer specifics defined in the transfer header.

    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;
            }
        }
    }

    And below is a screen shot of the validation error. If I manually enter in this Item No. value that associated with serialized "children," then I am allowed to manually enter in the quantity and whatnot. My previous post where the Item No. value was plugged into the page was associated with a Line No. = 0 in the underlying SQL table. Now the record isn't added to the SQL table due to the validation error.

    Validation-Results.png

  • Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer

    TeddyH So I made the following change to my AL code:

        Validate(Rec."Document No.");
        Validate(Rec."Item No.", Rec_ILE."Item No.");
        Validate(Rec.Quantity, 1);
        Rec.Insert();

    When I tested this out, I pasted an assigned serial number into the item transfer order line's Item No. field.

    Serial_2D00_No.png

    When I tabbed over the Item No. field, the serial number was indeed translated back to its parent Item No. value. And this was pushed into the Item No. field for the item transfer order line. But the quantity field wasn't updated as expected. See below, with the translated Item No. boxed in green and the Quantity field boxed in red. Any suggestions as to why? Once I am able to get the item transfer order line fully populated then I'm thinking the reservation entry should work fine. Thanks again for your expertise!

    Item_2D00_No.png

  • Greg Kujawa Profile Picture
    Greg Kujawa 289 on at
    RE: Add serialized item tracking details to a transfer

    [quote user="Teddy H"]

    The reason why you have that error is because the transfer line does not exist yet. You need to insert first before creating the reservation entry.

    Also why you are trying to do it on OnBeforeValidate ? OnAfterValidate makes more sense.

    [/quote]

    Thanks for your response. I am attempting to trigger based on the user entering in a serial number in the item transfer order line's Item No. field. If that value matches a serial number assigned to an item, then I am storing that serial number value and replacing it with the item number. I thought that the record insert would be handled by the user as they continue to populate the item transfer order line, right? If you check that one URL I listed it shows something similar with a sales order line. Where in their AL code example they are replacing the user-provided serial number value with the corresponding item number it's associated with. Also, I am using the before validate trigger, since the after trigger would cause an error I'd think. Seeing the serial number put into the Item No. field won't be found against any of my item numbers.

    I will try adding the record insert to my procedure and let you know the outcome!

  • Suggested answer
    YUN ZHU Profile Picture
    YUN ZHU 76,095 Super User 2024 Season 2 on at
    RE: Add serialized item tracking details to a transfer

    Hi, just to add some info.

    If you want to create Item Tracking Lines automatically, hope the following will help.

    https://yzhums.com/7943/

    Thanks.

    ZHU

  • Verified answer
    TeddyH Profile Picture
    TeddyH 12,864 Super User 2024 Season 1 on at
    RE: Add serialized item tracking details to a transfer

    The reason why you have that error is because the transfer line does not exist yet. You need to insert first before creating the reservation entry.

    Also why you are trying to do it on OnBeforeValidate ? OnAfterValidate makes more sense.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Congratulations 2024 Spotlight Honorees

Kudos to all of our 2024 community stars! 🎉

Meet the Top 10 leaders for December

Congratulations to our December super stars! 🥳

Start Your Super User Journey Pt 2

Join the ranks of our community heros! 🦹

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 291,861 Super User 2024 Season 2

#2
Martin Dráb Profile Picture

Martin Dráb 230,540 Most Valuable Professional

#3
nmaenpaa Profile Picture

nmaenpaa 101,156

Leaderboard

Featured topics

Product updates

Dynamics 365 release plans