Page 99000883 Sales Order Planning

App
Base Application
Namespace
Microsoft.Sales.Document
Versions
17-28
Source table
99000800

Procedures, 4Events, 13

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Sales/Document/SalesOrderPlanning.Page.al479 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Sales.Document;

using Microsoft.Foundation.Navigate;
using Microsoft.Inventory.Analysis;
using Microsoft.Inventory.Availability;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Ledger;
using Microsoft.Inventory.Requisition;
using Microsoft.Inventory.Tracking;
using Microsoft.Purchases.Document;

/// <summary>
/// Displays planning information for sales order lines including availability and supply options.
/// </summary>
page 99000883 "Sales Order Planning"
{
    Caption = 'Sales Order Planning';
    DataCaptionExpression = Caption();
    DataCaptionFields = "Sales Order No.";
    Editable = false;
    PageType = List;
    SourceTable = "Sales Planning Line";
    SourceTableTemporary = true;

    layout
    {
        area(content)
        {
            repeater(Control2)
            {
                ShowCaption = false;
                field("Item No."; Rec."Item No.")
                {
                    ApplicationArea = Planning;
                }
                field("Variant Code"; Rec."Variant Code")
                {
                    ApplicationArea = Planning;
                    Visible = false;
                }
                field("Planning Status"; Rec."Planning Status")
                {
                    ApplicationArea = Planning;
                }
                field(Description; Rec.Description)
                {
                    ApplicationArea = Planning;
                }
                field("Shipment Date"; Rec."Shipment Date")
                {
                    ApplicationArea = Planning;
                }
                field("Planned Quantity"; Rec."Planned Quantity")
                {
                    ApplicationArea = Planning;
                    Visible = false;

                    trigger OnLookup(var Text: Text): Boolean
                    var
                        SalesLine: Record "Sales Line";
                    begin
                        SalesLine.Get(
                          SalesLine."Document Type"::Order,
                          Rec."Sales Order No.", Rec."Sales Order Line No.");
                        SalesLine.ShowReservationEntries(true);
                    end;
                }
                field(Available; Rec.Available)
                {
                    ApplicationArea = Planning;
                    DecimalPlaces = 0 : 5;
                }
                field("Next Planning Date"; Rec."Next Planning Date")
                {
                    ApplicationArea = Planning;
                }
                field("Expected Delivery Date"; Rec."Expected Delivery Date")
                {
                    ApplicationArea = Planning;
                }
                field("Needs Replanning"; Rec."Needs Replanning")
                {
                    ApplicationArea = Planning;
                    Visible = false;
                }
            }
        }
    }

    actions
    {
        area(navigation)
        {
            group("&Item")
            {
                Caption = '&Item';
                Image = Item;
                action(Card)
                {
                    ApplicationArea = Planning;
                    Caption = 'Card';
                    Image = EditLines;
                    RunObject = Page "Item Card";
                    RunPageLink = "No." = field("Item No.");
                    ShortCutKey = 'Shift+F7';
                    ToolTip = 'View or change detailed information about the record on the document or journal line.';
                }
                action("Ledger E&ntries")
                {
                    ApplicationArea = Planning;
                    Caption = 'Ledger E&ntries';
                    Image = CustomerLedger;
                    //The property 'PromotedIsBig' can only be set if the property 'Promoted' is set to 'true'
                    //PromotedIsBig = true;
                    RunObject = Page "Item Ledger Entries";
                    RunPageLink = "Item No." = field("Item No."),
                                  "Variant Code" = field("Variant Code");
                    RunPageView = sorting("Item No.", Open, "Variant Code");
                    ShortCutKey = 'Ctrl+F7';
                    ToolTip = 'View the history of transactions that have been posted for the selected record.';
                }
                group("<Action8>")
                {
                    Caption = '&Item Availability by';
                    Image = ItemAvailability;
                    action("<Action6>")
                    {
                        ApplicationArea = Planning;
                        Caption = 'Event';
                        Image = "Event";
                        ToolTip = 'View how the actual and the projected available balance of an item will develop over time according to supply and demand events.';

                        trigger OnAction()
                        var
                            Item: Record Item;
                        begin
                            if Item.Get(Rec."Item No.") then
                                ItemAvailFormsMgt.ShowItemAvailabilityFromItem(Item, "Item Availability Type"::"Event");
                        end;
                    }
                    action("<Action31>")
                    {
                        ApplicationArea = Planning;
                        Caption = 'Period';
                        Image = Period;
                        RunObject = Page "Item Availability by Periods";
                        RunPageLink = "No." = field("Item No.");
                        ToolTip = 'View the projected quantity of the item over time according to time periods, such as day, week, or month.';
                    }
                    action("BOM Level")
                    {
                        ApplicationArea = Planning;
                        Caption = 'BOM Level';
                        Image = BOMLevel;
                        ToolTip = 'View availability figures for items on bills of materials that show how many units of a parent item you can make based on the availability of child items.';

                        trigger OnAction()
                        var
                            Item: Record Item;
                        begin
                            if Item.Get(Rec."Item No.") then
                                ItemAvailFormsMgt.ShowItemAvailabilityFromItem(Item, "Item Availability Type"::BOM);
                        end;
                    }
                }
                separator(Action30)
                {
                }
                action(Statistics)
                {
                    ApplicationArea = Planning;
                    Caption = 'Statistics';
                    Image = Statistics;
                    ShortCutKey = 'F7';
                    ToolTip = 'View statistical information, such as the value of posted entries, for the record.';

                    trigger OnAction()
                    var
                        Item: Record Item;
                        ItemStatistics: Page "Item Statistics";
                    begin
                        if Item.Get(Rec."Item No.") then;
                        ItemStatistics.SetItem(Item);
                        ItemStatistics.RunModal();
                    end;
                }
            }
        }
        area(processing)
        {
            group("F&unctions")
            {
                Caption = 'F&unctions';
                Image = "Action";
                action("Update &Shipment Dates")
                {
                    ApplicationArea = Planning;
                    Caption = 'Update &Shipment Dates';
                    Ellipsis = true;
                    Image = UpdateShipment;
                    ToolTip = 'Update the Shipment Date field on lines with any changes that were made since you opened the Sales Order Planning window.';

                    trigger OnAction()
                    var
                        SalesLine: Record "Sales Line";
                        Choice: Integer;
                        LastShipmentDate: Date;
                    begin
                        Choice := StrMenu(Text000);

                        if Choice = 0 then
                            exit;

                        LastShipmentDate := WorkDate();

                        SalesHeader.LockTable();
                        SalesHeader.Get(SalesHeader."Document Type"::Order, SalesHeader."No.");

                        if Choice = 1 then begin
                            if Rec.Find('-') then
                                repeat
                                    if Rec."Expected Delivery Date" > LastShipmentDate then
                                        LastShipmentDate := Rec."Expected Delivery Date";
                                until Rec.Next() = 0;
                            SalesHeader.Validate("Shipment Date", LastShipmentDate);
                            SalesHeader.Modify();
                        end
                        else begin
                            SalesLine.LockTable();
                            if Rec.Find('-') then
                                repeat
                                    SalesLine.Get(
                                      SalesLine."Document Type"::Order,
                                      Rec."Sales Order No.",
                                      Rec."Sales Order Line No.");
                                    SalesLine."Shipment Date" := Rec."Expected Delivery Date";
                                    SalesLine.Modify();
                                until Rec.Next() = 0;
                        end;
                        BuildForm();
                    end;
                }
                separator(Action32)
                {
                }
                action("Order &Tracking")
                {
                    ApplicationArea = Planning;
                    Caption = 'Order &Tracking';
                    Image = OrderTracking;
                    ToolTip = 'Tracks the connection of a supply to its corresponding demand. This can help you find the original demand that created a specific production order or purchase order.';

                    trigger OnAction()
                    var
                        SalesOrderLine: Record "Sales Line";
                        OrderTracking: Page "Order Tracking";
                    begin
                        SalesOrderLine.Get("Sales Document Type"::Order, Rec."Sales Order No.", Rec."Sales Order Line No.");
                        OrderTracking.SetVariantRec(
                            SalesOrderLine, SalesOrderLine."No.", SalesOrderLine."Outstanding Qty. (Base)",
                            SalesOrderLine."Shipment Date", SalesOrderLine."Shipment Date");
                        OrderTracking.RunModal();
                        BuildForm();
                    end;
                }
            }
        }
        area(Promoted)
        {
            group(Category_Process)
            {
                Caption = 'Process', Comment = 'Generated from the PromotedActionCategories property index 1.';

                actionref("Order &Tracking_Promoted"; "Order &Tracking")
                {
                }
                actionref("Update &Shipment Dates_Promoted"; "Update &Shipment Dates")
                {
                }
                actionref(Statistics_Promoted; Statistics)
                {
                }
                group("Category_Item Availability by")
                {
                    Caption = 'Item Availability by';

                    actionref("<Action6>_Promoted"; "<Action6>")
                    {
                    }
                    actionref("BOM Level_Promoted"; "BOM Level")
                    {
                    }
                    actionref("<Action31>_Promoted"; "<Action31>")
                    {
                    }
                }
            }
        }
    }

    trigger OnOpenPage()
    begin
        BuildForm();
    end;

    var
        SalesHeader: Record "Sales Header";
        ReservEntry: Record "Reservation Entry";
        ItemAvailFormsMgt: Codeunit "Item Availability Forms Mgt";

#pragma warning disable AA0074
        Text000: Label 'All Lines to last Shipment Date,Each line own Shipment Date';
#pragma warning restore AA0074

    /// <summary>
    /// Sets the sales order to display planning information for.
    /// </summary>
    /// <param name="SalesOrderNo">The sales order number to retrieve.</param>
    procedure SetSalesOrder(SalesOrderNo: Code[20])
    begin
        SalesHeader.Get(SalesHeader."Document Type"::Order, SalesOrderNo);
    end;

    /// <summary>
    /// Builds the planning form by creating lines for all items in the sales order.
    /// </summary>
    procedure BuildForm()
    begin
        Rec.Reset();
        Rec.DeleteAll();
        MakeLines();
        Rec.SetRange("Sales Order No.", SalesHeader."No.");
    end;

    local procedure MakeLines()
    var
        SalesLine: Record "Sales Line";
        PurchLine: Record "Purchase Line";
        ReqLine: Record "Requisition Line";
        ReservEntry2: Record "Reservation Entry";
    begin
        SalesLine.SetRange("Document Type", SalesLine."Document Type"::Order);
        SalesLine.SetRange("Document No.", SalesHeader."No.");
        SalesLine.SetRange(Type, SalesLine.Type::Item);
        OnMakeLinesSetOnAfterSetFilters(SalesLine);
        if SalesLine.Find('-') then
            repeat
                Rec.Init();
                Rec."Sales Order No." := SalesLine."Document No.";
                Rec."Sales Order Line No." := SalesLine."Line No.";
                Rec."Item No." := SalesLine."No.";

                Rec."Variant Code" := SalesLine."Variant Code";
                Rec.Description := SalesLine.Description;
                Rec."Shipment Date" := SalesLine."Shipment Date";
                Rec."Planning Status" := Rec."Planning Status"::None;
                SalesLine.CalcFields("Reserved Qty. (Base)");
                Rec."Planned Quantity" := SalesLine."Reserved Qty. (Base)";
                ReservEntry.InitSortingAndFilters(false);
                SalesLine.SetReservationFilters(ReservEntry);
                ReservEntry.SetRange("Reservation Status", ReservEntry."Reservation Status"::Reservation);
                if ReservEntry.Find('-') then
                    repeat
                        if ReservEntry2.Get(ReservEntry."Entry No.", not ReservEntry.Positive) then
                            case ReservEntry2."Source Type" of
                                Database::"Item Ledger Entry":
                                    begin
                                        Rec."Planning Status" := Rec."Planning Status"::Inventory;
                                        Rec."Expected Delivery Date" := SalesLine."Shipment Date";
                                    end;
                                Database::"Requisition Line":
                                    begin
                                        ReqLine.Get(
                                          ReservEntry2."Source ID", ReservEntry2."Source Batch Name", ReservEntry2."Source Ref. No.");
                                        Rec."Planning Status" := Rec."Planning Status"::Planned;
                                        Rec."Expected Delivery Date" := ReqLine."Due Date";
                                    end;
                                Database::"Purchase Line":
                                    begin
                                        PurchLine.Get(
                                          ReservEntry2."Source Subtype", ReservEntry2."Source ID", ReservEntry2."Source Ref. No.");
                                        Rec."Planning Status" := Rec."Planning Status"::"Firm Planned";
                                        Rec."Expected Delivery Date" := PurchLine."Expected Receipt Date";
                                    end;
                                else
                                    OnMakeLinesOnSetFromSourceLine(Rec, ReservEntry2);
                            end;
                    until ReservEntry.Next() = 0;
                Rec."Needs Replanning" :=
                  (Rec."Planned Quantity" <> SalesLine."Outstanding Qty. (Base)") or
                  (Rec."Expected Delivery Date" > Rec."Shipment Date");
                CalculateDisposalPlan(SalesLine."Variant Code", SalesLine."Location Code");
                OnMakeLinesOnBeforeInsertSalesOrderPlanningLine(Rec, SalesLine);
                Rec.Insert();
            until SalesLine.Next() = 0;
    end;

    local procedure CalculateDisposalPlan(VariantCode: Code[20]; LocationCode: Code[10])
    var
        Item: Record Item;
        QtyOnComponentLines: Decimal;
        ScheduledReceiptQty: Decimal;
    begin
        if not Rec."Needs Replanning" then
            exit;

        Item.Get(Rec."Item No.");
        Item.SetRange("Variant Filter", VariantCode);
        Item.SetRange("Location Filter", LocationCode);
        Item.CalcFields(Inventory, "Qty. on Purch. Order", "Qty. on Sales Order");

        QtyOnComponentLines := Item.CalcQtyOnComponentLines();
        ScheduledReceiptQty := Item.CalcScheduledReceiptQty();

        if Item.Type = Item.Type::Inventory then
            Rec.Available :=
              Item.Inventory -
              Item."Qty. on Sales Order" +
              Item."Qty. on Purch. Order" -
              QtyOnComponentLines +
              ScheduledReceiptQty +
              Item.CalcPlannedOrderReceiptQty()
        else
            Rec.Available := 0;

        Rec."Next Planning Date" := WorkDate();

        CalculatePlanAndDelivDates(Item, Rec."Next Planning Date", Rec."Expected Delivery Date");
    end;

    local procedure CalculatePlanAndDelivDates(Item: Record Item; var NextPlanningDate: Date; var ExpectedDeliveryDate: Date)
    begin
        OnBeforeCalculatePlanAndDelivDates(Item);

        NextPlanningDate := CalcDate(Item."Lot Accumulation Period", NextPlanningDate);

        if (Rec.Available > 0) or (Rec."Planning Status" <> Rec."Planning Status"::None) then
            ExpectedDeliveryDate := CalcDate(Item."Safety Lead Time", WorkDate())
        else
            ExpectedDeliveryDate :=
              CalcDate(Item."Safety Lead Time",
                CalcDate(Item."Lead Time Calculation", NextPlanningDate))
    end;

    /// <summary>
    /// Returns the caption text for the page showing the sales order number and customer name.
    /// </summary>
    /// <returns>Returns the page caption combining sales order number and bill-to name.</returns>
    procedure Caption(): Text
    begin
        exit(StrSubstNo('%1 %2', SalesHeader."No.", SalesHeader."Bill-to Name"));
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalculatePlanAndDelivDates(var Item: Record Item)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnMakeLinesOnBeforeInsertSalesOrderPlanningLine(var SalesPlanningLine: Record "Sales Planning Line"; var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnMakeLinesSetOnAfterSetFilters(var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnMakeLinesOnSetFromSourceLine(var SalesPlanningLine: Record "Sales Planning Line"; ReservEntry: Record "Reservation Entry")
    begin
    end;
}