Table 5310 Item Statistics Cache

App
Base Application
Namespace
Microsoft.Inventory.Analysis
Versions
17-25, 28
Name by version
Outlook Synch. Setup Detail from 17, Item Statistics Cache from 28

Fields, 22Keys, 2Procedures, 5Obsolete, 1

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Inventory/Analysis/ItemStatisticsCache.Table.al256 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.Inventory.Analysis;

using Microsoft.Inventory.Item;
using Microsoft.Inventory.Ledger;

table 5310 "Item Statistics Cache"
{
    Caption = 'Item Statistics Cache';
    DataClassification = CustomerContent;
    ReplicateData = false;
    InherentEntitlements = RIMD;
    InherentPermissions = RIMD;

    DataCaptionFields = "Item No.", Description;

    fields
    {
        field(1; "Item No."; Code[20])
        {
        }
        field(22; Description; Text[100])
        {
        }
        field(2; ItemLedgerEntryNoLatest; Integer)
        {
        }
        field(3; ValueEntryNoLatest; Integer)
        {
        }
        field(4; LastUpdated; Date)
        {
        }
        field(5; "CurrentInventoryValue"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the current inventory value (in local currency), calculated as the sum of Cost Amount (Actual) + Cost Amount (Expected) on posted Value Entries for this item. Run the Adjust Cost - Item Entries batch job to ensure the amount is up to date.';
        }
        field(6; "ExpiredStockValue"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the inventory value (in local currency) of quantities whose expiration date is earlier than the work date. Calculated as the sum of Cost Amount (Actual) + Cost Amount (Expected) from Value Entries applied to open Item Ledger Entries (Remaining Qty. > 0) with Expiration Date < Work Date. Run the Adjust Cost - Item Entries batch job to ensure the value is up to date. Only meaningful for items where expiration/lot dates are tracked.';
        }
        #region This period
        field(7; "SalesGrowthRateThisPeriod"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage change in sales compared to the previous period in the fiscal year, calculated as ((Sales in the current period in the fiscal year - Sales in the prior period in the fiscal year) ÷ Sales in the prior period in the fiscal year) x 100%. A positive value indicates growth, while a negative value indicates a decline in sales.';
        }
        field(8; "NetSalesLCYThisPeriod"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the total revenue (in local currency) from sales for the current period in fiscal year after deducting given discounts and returns. This value represents the actual income generated from sales transactions during the fiscal year. Calculated as: Net Sales = Total sales in the period  - Total returns - Total given discounts.';
        }
        field(9; "GrossMarginThisPeriod"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of revenue remaining after deducting the cost of goods sold (COGS) for the current period in the fiscal year. This metric indicates how efficiently the company produces and sells its products. Calculated as: Gross Margin (%) = ((Net Sales - COGS) ÷ Net Sales) x 100%. A higher percentage reflects better profitability.';
        }
        field(10; "ReturnRateThisPeriod"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of sold quantity that were returned during the current period in the fiscal year. This metric helps measure product quality and customer satisfaction. Calculated as: Return Rate (%) = (Returned Quantity ÷ Total Sold Quantity) x 100%. A lower percentage indicates fewer returns and higher product acceptance.';
        }
        #endregion
        #region This fiscal year
        field(11; "SalesGrowthRateThisFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage change in sales compared to the previous fiscal year, calculated as ((Sales in current fiscal year - Sales in the last fiscal year) ÷ Sales in the last fiscal year) x 100%. A positive value indicates growth, while a negative value indicates a decline in sales.';
        }
        field(12; "NetSalesLCYThisFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the total revenue (in local currency) from sales for the current fiscal year after deducting given discounts and returns. This value represents the actual income generated from sales transactions during the fiscal year. Calculated as: Net Sales = Total sales in the fiscal year - Total returns in the fiscal year - Total given discounts in the fiscal year.';
        }
        field(13; "GrossMarginThisFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of revenue remaining after deducting the cost of goods sold (COGS) for the current fiscal year. This metric indicates how efficiently the company produces and sells its products. Calculated as: Gross Margin (%) = ((Net Sales - COGS) ÷ Net Sales) x 100%. A higher percentage reflects better profitability.';
        }
        field(14; "ReturnRateThisFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of sold quantity that were returned during the current fiscal year. This metric helps measure product quality and customer satisfaction. Calculated as: Return Rate (%) = (Returned Quantity ÷ Total Sold Quantity) x 100%. A lower percentage indicates fewer returns and higher product acceptance.';
        }
        #endregion
        #region Last fiscal year
        field(15; "SalesGrowthRateLastFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage change in sales compared to the previous fiscal year, calculated as ((Sales in the last fiscal year - Sales in the prior fiscal year) ÷ Sales in the prior fiscal year) x 100%. A positive value indicates growth, while a negative value indicates a decline in sales.';
        }
        field(16; "NetSalesLCYLastFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the total revenue (in local currency) from sales for the last fiscal year after deducting given discounts and returns. This value represents the actual income generated from sales transactions during the last fiscal year. Calculated as: Net Sales = Total sales in the last fiscal year - Total returns in the last fiscal year - Total given discounts in the last fiscal year.';
        }
        field(17; "GrossMarginLastFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of revenue remaining after deducting the cost of goods sold (COGS) for the last fiscal year. This metric indicates how efficiently the company produces and sells its products. Calculated as: Gross Margin (%) = ((Net Sales - COGS) ÷ Net Sales) x 100%. A higher percentage reflects better profitability.';
        }
        field(18; "ReturnRateLastFY"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of sold quantity that were returned during the last fiscal year. This metric helps measure product quality and customer satisfaction. Calculated as: Return Rate (%) = (Returned Quantity ÷ Total Sold Quantity) x 100%. A lower percentage indicates fewer returns and higher product acceptance.';
        }
        #endregion
        #region Lifetime
        field(19; "NetSalesLCYLifetime"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the total revenue (in local currency) from sales after deducting given discounts and returns. This value represents the actual income generated from sales transactions during the lifetime. Calculated as: Net Sales = Total sales so far - Total returns so far - Total given discounts so far.';
        }
        field(20; "GrossMarginLifetime"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of revenue remaining after deducting the cost of goods sold (COGS) for the lifetime. This metric indicates how efficiently the company produces and sells its products. Calculated as: Gross Margin (%) = ((Net Sales - COGS) ÷ Net Sales) x 100%. A higher percentage reflects better profitability.';
        }
        field(21; "ReturnRateLifetime"; Decimal)
        {
            AutoFormatType = 0;
            ToolTip = 'Specifies the percentage of sold quantity that were returned during the lifetime. This metric helps measure product quality and customer satisfaction. Calculated as: Return Rate (%) = (Returned Quantity ÷ Total Sold Quantity) x 100%. A lower percentage indicates fewer returns and higher product acceptance.';
        }
        #endregion
    }

    keys
    {
        key(Key1; "Item No.")
        {
            Clustered = true;
        }
    }

#if not CLEAN29
    [Obsolete('Use InitAndInsert with ItemNo and ItemDescription parameters instead.', '29.0')]
    procedure InitAndInsert(ItemNo: Code[20])
    var
        Item: Record Item;
    begin
        if Item.Get(ItemNo) then
            InitAndInsert(ItemNo, Item.Description)
        else
            InitAndInsert(ItemNo, '');
    end;
#endif

    procedure InitAndInsert(ItemNo: Code[20]; ItemDescription: Text[100])
    begin
        Rec.Init();
        Rec."Item No." := ItemNo;
        Rec.Description := ItemDescription;
        Rec.Insert();
    end;

    /// <summary>
    /// Update the Item Statistics Cache record if there are new Item Ledger entries or Value entries, or if the month has changed.
    /// </summary>
    procedure UpdateIfNeeded(Item: Record Item; CurrentDate: Date; ItemDateFilters: array[4] of Text[30]; PriorPeriodItemDateFilters: array[4] of Text[30])
    var
        WasUpdated: Boolean;
        ShouldUpdateAll: Boolean;
    begin
        if Rec.LastUpdated = 0D then
            ShouldUpdateAll := true
        else
            ShouldUpdateAll := (CurrentDate > CalcDate('<+1M>', Rec.LastUpdated));

        if ShouldUpdateAll then begin
            UpdateForItemLedgerEntry(Item, ItemDateFilters);
            UpdateForValueEntry(Item, ItemDateFilters, PriorPeriodItemDateFilters);
            WasUpdated := true;
        end else begin
            if IsUpdateNeededItemLedgerEntry() then begin
                UpdateForItemLedgerEntry(Item, ItemDateFilters);
                WasUpdated := true;
            end;
            if IsUpdateNeededValueEntry() then begin
                UpdateForValueEntry(Item, ItemDateFilters, PriorPeriodItemDateFilters);
                WasUpdated := true;
            end;
        end;

        if WasUpdated then begin
            Rec.LastUpdated := CurrentDate;
            Rec.Modify();
        end;
    end;

    local procedure IsUpdateNeededItemLedgerEntry(): Boolean
    var
        ItemLedgerEntry: Record "Item Ledger Entry";
    begin
        ItemLedgerEntry.ReadIsolation := IsolationLevel::ReadUncommitted;
        ItemLedgerEntry.SetRange("Item No.", Rec."Item No.");
        if ItemLedgerEntry.FindLast() then
            if ItemLedgerEntry."Entry No." > Rec.ItemLedgerEntryNoLatest then begin
                Rec.ItemLedgerEntryNoLatest := ItemLedgerEntry."Entry No.";
                exit(true);
            end;

        exit(false);
    end;

    local procedure IsUpdateNeededValueEntry(): Boolean
    var
        ValueEntry: Record "Value Entry";
    begin
        ValueEntry.ReadIsolation := IsolationLevel::ReadUncommitted;
        ValueEntry.SetRange("Item No.", Rec."Item No.");
        if ValueEntry.FindLast() then
            if ValueEntry."Entry No." > Rec.ValueEntryNoLatest then begin
                Rec.ValueEntryNoLatest := ValueEntry."Entry No.";
                exit(true);
            end;

        exit(false);
    end;

    local procedure UpdateForItemLedgerEntry(Item: Record Item; ItemDateFilters: array[4] of Text[30])
    var
        ItemStatistics: Codeunit "Item Statistics";
    begin
        ReturnRateThisPeriod := ItemStatistics.CalculateProductReturnRate(Item, ItemDateFilters[1]) / 100;
        ReturnRateThisFY := ItemStatistics.CalculateProductReturnRate(Item, ItemDateFilters[2]) / 100;
        ReturnRateLastFY := ItemStatistics.CalculateProductReturnRate(Item, ItemDateFilters[3]) / 100;
        ReturnRateLifetime := ItemStatistics.CalculateProductReturnRate(Item, ItemDateFilters[4]) / 100;
    end;

    local procedure UpdateForValueEntry(Item: Record Item; ItemDateFilters: array[4] of Text[30]; PriorPeriodItemDateFilters: array[4] of Text[30])
    var
        ItemStatistics: Codeunit "Item Statistics";
    begin
        CurrentInventoryValue := ItemStatistics.CalculateCurrentInventoryValue(Item);
        ExpiredStockValue := ItemStatistics.CalculateExpiredStockValue(Item);

        SalesGrowthRateThisPeriod := ItemStatistics.CalculateSalesGrowthRate(Item, ItemDateFilters[1], PriorPeriodItemDateFilters[1]) / 100;
        SalesGrowthRateThisFY := ItemStatistics.CalculateSalesGrowthRate(Item, ItemDateFilters[2], PriorPeriodItemDateFilters[2]) / 100;
        SalesGrowthRateLastFY := ItemStatistics.CalculateSalesGrowthRate(Item, ItemDateFilters[3], PriorPeriodItemDateFilters[3]) / 100;

        NetSalesLCYThisPeriod := ItemStatistics.CalculateNetSales(Item, ItemDateFilters[1]);
        NetSalesLCYThisFY := ItemStatistics.CalculateNetSales(Item, ItemDateFilters[2]);
        NetSalesLCYLastFY := ItemStatistics.CalculateNetSales(Item, ItemDateFilters[3]);
        NetSalesLCYLifetime := ItemStatistics.CalculateNetSales(Item, ItemDateFilters[4]);

        GrossMarginThisPeriod := ItemStatistics.CalculateGrossMarginPercentage(Item, ItemDateFilters[1]) / 100;
        GrossMarginThisFY := ItemStatistics.CalculateGrossMarginPercentage(Item, ItemDateFilters[2]) / 100;
        GrossMarginLastFY := ItemStatistics.CalculateGrossMarginPercentage(Item, ItemDateFilters[3]) / 100;
        GrossMarginLifetime := ItemStatistics.CalculateGrossMarginPercentage(Item, ItemDateFilters[4]) / 100;
    end;
}