Report 713 Inventory - Customer Sales
- App
- Base Application
- Namespace
- Microsoft.Inventory.Reports
- Versions
- 17-28
Versions171819202122232425262728
Source29
Source in 29
src/Layers/W1/BaseApp/Inventory/Reports/InventoryCustomerSales.Report.al486 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.Reports;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Ledger;
using Microsoft.Sales.Customer;
using System.Utilities;
report 713 "Inventory - Customer Sales"
{
ApplicationArea = Basic, Suite;
Caption = 'Inventory Customer Sales';
ToolTip = 'View, print, or save a list of customers that have purchased selected items within a selected period, for example, to analyze customers'' purchasing patterns. The report specifies quantity, amount, discount, profit percentage, and profit.';
DefaultRenderingLayout = Excel;
UsageCategory = ReportsAndAnalysis;
dataset
{
dataitem(ReportHeader; "Integer")
{
DataItemTableView = sorting(Number) where(Number = const(0));
#if not CLEAN27
column(CompanyName; COMPANYPROPERTY.DisplayName())
{
ObsoleteState = Pending;
ObsoleteReason = 'RDLC Only layout column. To be removed along with the RDLC layout.';
ObsoleteTag = '27.0';
}
#endif
#if not CLEAN28
column(PeriodText; PeriodText)
{
ObsoleteState = Pending;
ObsoleteReason = 'RDLC Only layout column. To be removed along with the RDLC layout.';
ObsoleteTag = '28.0';
}
#endif
column(ItemFilter; ItemFilter)
{
}
column(ItemLedgEntryFilter; ItemLedgEntryFilter)
{
}
}
dataitem(Item; Item)
{
DataItemTableView = sorting("No.");
PrintOnlyIfDetail = true;
RequestFilterFields = "No.", "No. 2", "Search Description", "Assembly BOM", "Inventory Posting Group";
column(No_Item; "No.")
{
IncludeCaption = true;
}
column(Description_Item; Description)
{
IncludeCaption = true;
}
column(BaseUnitofMeasure_Item; "Base Unit of Measure")
{
IncludeCaption = true;
}
dataitem("Item Ledger Entry"; "Item Ledger Entry")
{
DataItemLink = "Item No." = field("No."), "Variant Code" = field("Variant Filter"), "Location Code" = field("Location Filter"), "Global Dimension 1 Code" = field("Global Dimension 1 Filter"), "Global Dimension 2 Code" = field("Global Dimension 2 Filter");
DataItemTableView = sorting("Item No.", "Source No.", "Posting Date", "Source Type") where("Source Type" = const(Customer));
RequestFilterFields = "Posting Date", "Source No.";
dataitem("Integer"; "Integer")
{
column(SourceNo_ItemLedgEntry; TempValueEntryBuf."Source No.")
{
IncludeCaption = true;
}
column(CustName; GetCustName(TempValueEntryBuf."Source No."))
{
}
column(InvQty_ItemLedgEntry; -TempValueEntryBuf."Invoiced Quantity")
{
DecimalPlaces = 0 : 5;
}
column(SalesAmtActual_ItemLedgEntry; TempValueEntryBuf."Sales Amount (Actual)")
{
IncludeCaption = true;
AutoFormatType = 1;
}
column(Profit_ItemLedgEntry; TempValueEntryBuf."Sales Amount (Expected)")
{
IncludeCaption = true;
AutoFormatType = 1;
}
column(DiscountAmount; -TempValueEntryBuf."Purchase Amount (Expected)")
{
AutoFormatType = 1;
}
column(ProfitPct_ItemLedgEntry; ProfitPct)
{
DecimalPlaces = 1 : 1;
}
trigger OnAfterGetRecord()
begin
if Number = 1 then
TempValueEntryBuf.FindFirst()
else
TempValueEntryBuf.Next();
ProfitPct := 0;
if TempValueEntryBuf."Sales Amount (Actual)" <> 0 then
ProfitPct := TempValueEntryBuf."Sales Amount (Expected)" / TempValueEntryBuf."Sales Amount (Actual)" * 100;
end;
trigger OnPostDataItem()
begin
TempValueEntryBuf.DeleteAll();
end;
trigger OnPreDataItem()
begin
TempValueEntryBuf.Reset();
SetRange(Number, 1, TempValueEntryBuf.Count());
end;
}
trigger OnAfterGetRecord()
begin
if IsNewGroup() then
AddReportLine(ValueEntryBuf);
IncrLineAmounts(ValueEntryBuf, "Item Ledger Entry");
if IsLastEntry() then
AddReportLine(ValueEntryBuf);
if not ReportHasData then
ReportHasData := true;
end;
trigger OnPreDataItem()
begin
LastItemLedgEntryNo := GetLastItemLedgerEntryNo("Item Ledger Entry");
Clear(ValueEntryBuf);
ReportLineNo := 0;
end;
}
dataitem(SubTotals; Integer)
{
DataItemTableView = sorting(Number) where(Number = const(1));
column(SubTotals_InvQty; SubtotalsInvQty)
{
DecimalPlaces = 0 : 5;
}
column(SubTotals_SalesAmtActual; SubtotalsSalesAmtActual)
{
DecimalPlaces = 2 : 2;
}
column(SubTotals_DiscountAmount; SubtotalsDiscountAmount)
{
DecimalPlaces = 2 : 2;
}
column(SubTotals_Profit; SubtotalsItemProfit)
{
DecimalPlaces = 2 : 2;
}
column(SubTotals_ProfitPct; SubtotalsItemProfitPct)
{
DecimalPlaces = 1 : 1;
}
column(SubTotals_Description; Item.Description)
{
}
trigger OnPreDataItem()
begin
if TempValueEntryBuf2.IsEmpty() then
CurrReport.Break();
SubtotalsInvQty := 0;
SubtotalsSalesAmtActual := 0;
SubtotalsDiscountAmount := 0;
SubtotalsItemProfit := 0;
SubtotalsItemProfitPct := 0;
TempValueEntryBuf2.Reset();
if TempValueEntryBuf2.FindSet() then
repeat
SubtotalsInvQty += (-TempValueEntryBuf2."Invoiced Quantity");
SubtotalsSalesAmtActual += TempValueEntryBuf2."Sales Amount (Actual)";
SubtotalsDiscountAmount += (-TempValueEntryBuf2."Purchase Amount (Expected)");
SubtotalsItemProfit += TempValueEntryBuf2."Sales Amount (Expected)";
until TempValueEntryBuf2.Next() = 0;
if SubtotalsSalesAmtActual <> 0 then
SubtotalsItemProfitPct := SubtotalsItemProfit / SubtotalsSalesAmtActual * 100;
end;
trigger OnAfterGetRecord()
begin
TotalsSalesAmtActual += SubtotalsSalesAmtActual;
TotalsDiscountAmount += SubtotalsDiscountAmount;
TotalsProfit += SubtotalsItemProfit;
end;
trigger OnPostDataItem()
begin
TempValueEntryBuf2.DeleteAll();
end;
}
}
dataitem(Totals; Integer)
{
DataItemTableView = sorting(Number) where(Number = const(1));
column(Totals_SalesAmtActual; TotalsSalesAmtActual)
{
DecimalPlaces = 2 : 2;
}
column(Totals_DiscountAmount; TotalsDiscountAmount)
{
DecimalPlaces = 2 : 2;
}
column(Totals_Profit; TotalsProfit)
{
DecimalPlaces = 2 : 2;
}
column(Totals_ProfitPct; TotalsProfitPct)
{
DecimalPlaces = 1 : 1;
}
trigger OnPreDataItem()
begin
if not ReportHasData then
CurrReport.Break();
if TotalsSalesAmtActual <> 0 then
TotalsProfitPct := TotalsProfit / TotalsSalesAmtActual * 100;
end;
}
}
requestpage
{
AboutTitle = 'About Inventory Customer Sales';
AboutText = 'Analyse your customer sales per item to understand sales trends, optimise inventory management and improve marketing efforts. Assess the relationship between discounts, sales amount and volume of sales for each customer/item combination in the given period.';
layout
{
area(content)
{
group(Options)
{
Visible = false;
Caption = 'Options';
#if not CLEAN28
field(PostingDateFilter; PostingDateFilter)
{
ApplicationArea = Basic, Suite;
Caption = 'Posting Date Filter';
ObsoleteState = Pending;
ObsoleteReason = 'This request page field will be removed in a future release.';
ObsoleteTag = '28.0';
}
#endif
// Used to set report headers across multiple languages
field(RequestItemFilter; ItemFilter)
{
ApplicationArea = All;
Caption = 'Item Filter';
ToolTip = 'Specifies the Item Filters applied to this report.';
Visible = false;
}
field(RequestItemLedgEntryFilter; ItemLedgEntryFilter)
{
ApplicationArea = All;
Caption = 'Item Ledger Entry Filter';
ToolTip = 'Specifies the Item Ledger Entry Filters applied to this report.';
Visible = false;
}
}
}
}
actions
{
}
#if not CLEAN28
trigger OnQueryClosePage(CloseAction: Action): Boolean
begin
PostingDateFilter := "Item Ledger Entry".GetFilter("Posting Date");
end;
#endif
trigger OnClosePage()
begin
UpdateRequestPageFilterValues();
end;
}
rendering
{
layout(Excel)
{
Caption = 'Inventory Customer Sales Excel';
LayoutFile = '.\Inventory\Reports\InventoryCustomerSales.xlsx';
Type = Excel;
Summary = 'Built in layout for the Inventory Customer Sales Excel report.';
}
layout(Word)
{
Caption = 'Inventory Customer Sales Word';
LayoutFile = '.\Inventory\Reports\InventoryCustomerSales.docx';
Type = Word;
Summary = 'Built in layout for the Inventory Customer Sales Word report.';
}
#if not CLEAN27
layout(RDLC)
{
Caption = 'Inventory Customer Sales RDLC (Obsolete)';
Type = RDLC;
LayoutFile = '.\Inventory\Reports\InventoryCustomerSales.rdlc';
ObsoleteState = Pending;
ObsoleteReason = 'The RDLC layout has been replaced by the Excel and Word layouts and will be removed in a future release.';
ObsoleteTag = '27.0';
Summary = 'Built in layout for the Inventory Customer Sales RDLC (Obsolete) report.';
}
#endif
}
labels
{
DataRetrievedLbl = 'Data retrieved:';
InventoryCustomerSalesLbl = 'Inventory Customer Sales';
InvCustomerSalesPrintLbl = 'Inv. Cust. Sales (Print)', MaxLength = 31, Comment = 'Excel worksheet name.';
InvCustomerSalesAnalysisLbl = 'Inv. Cust. Sales (Analysis)', MaxLength = 31, Comment = 'Excel worksheet name.';
CustomerNoLbl = 'Customer No.';
CustNameLbl = 'Name';
InvQtyLbl = 'Invoiced Quantity';
AmountLbl = 'Amount';
DiscountAmtLbl = 'Discount Amount';
ProfitLbl = 'Profit';
ProfitPctLbl = 'Profit %';
PeriodLbl = 'Period';
// About the report labels
AboutTheReportLbl = 'About the report', MaxLength = 31, Comment = 'Excel worksheet name.';
EnvironmentLbl = 'Environment';
CompanyLbl = 'Company';
UserLbl = 'User';
RunOnLbl = 'Run on';
ReportNameLbl = 'Report name';
DocumentationLbl = 'Documentation';
#if not CLEAN27
ReportTitle = 'Inventory - Customer Sales';
Page = 'Page';
CustomerNo = 'Customer No.';
Name = 'Name';
InvoicedQty = 'Invoiced Quantity';
Amount = 'Amount';
DiscountAmt = 'Discount Amount';
Profit = 'Profit';
ProfitPct = 'Profit %';
Total = 'Total';
#endif
}
trigger OnPreReport()
begin
UpdateRequestPageFilterValues();
end;
var
ValueEntryBuf: Record "Value Entry";
TempValueEntryBuf: Record "Value Entry" temporary;
TempValueEntryBuf2: Record "Value Entry" temporary;
ItemFilter: Text;
ItemLedgEntryFilter: Text;
#if not CLEAN28
PostingDateFilter: Text;
#endif
LastItemLedgEntryNo: Integer;
ReportLineNo: Integer;
ProfitPct: Decimal;
SubtotalsInvQty: Decimal;
SubtotalsSalesAmtActual: Decimal;
SubtotalsDiscountAmount: Decimal;
SubtotalsItemProfit: Decimal;
SubtotalsItemProfitPct: Decimal;
TotalsSalesAmtActual: Decimal;
TotalsDiscountAmount: Decimal;
TotalsProfit: Decimal;
TotalsProfitPct: Decimal;
ReportHasData: Boolean;
#if not CLEAN28
PeriodText: Text;
PeriodInfoTxt: Label 'Period: %1', Comment = '%1 - period name';
#endif
TableFiltersTxt: Label '%1: %2', Locked = true;
local procedure CalcDiscountAmount(ItemLedgerEntryNo: Integer): Decimal
var
ValueEntry: Record "Value Entry";
begin
ValueEntry.SetCurrentKey("Item Ledger Entry No.");
ValueEntry.SetRange("Item Ledger Entry No.", ItemLedgerEntryNo);
ValueEntry.CalcSums("Discount Amount");
exit(ValueEntry."Discount Amount");
end;
local procedure GetLastItemLedgerEntryNo(var ItemLedgerEntry: Record "Item Ledger Entry"): Integer
var
LastItemLedgerEntry: Record "Item Ledger Entry";
begin
LastItemLedgerEntry.Copy(ItemLedgerEntry);
if LastItemLedgerEntry.FindLast() then
exit(LastItemLedgerEntry."Entry No.");
exit(0);
end;
local procedure IncrLineAmounts(var ValueEntryBuf2: Record "Value Entry"; CurrItemLedgerEntry: Record "Item Ledger Entry")
var
Profit: Decimal;
DiscountAmount: Decimal;
begin
CurrItemLedgerEntry.CalcFields("Sales Amount (Actual)", "Cost Amount (Actual)", "Cost Amount (Non-Invtbl.)");
Profit := CurrItemLedgerEntry."Sales Amount (Actual)" + CurrItemLedgerEntry."Cost Amount (Actual)" + CurrItemLedgerEntry."Cost Amount (Non-Invtbl.)";
DiscountAmount := CalcDiscountAmount(CurrItemLedgerEntry."Entry No.");
if ValueEntryBuf2."Item No." = '' then begin
ValueEntryBuf2.Init();
ValueEntryBuf2."Item No." := CurrItemLedgerEntry."Item No.";
ValueEntryBuf2."Source No." := CurrItemLedgerEntry."Source No.";
end;
ValueEntryBuf2."Invoiced Quantity" += CurrItemLedgerEntry."Invoiced Quantity";
ValueEntryBuf2."Sales Amount (Actual)" += CurrItemLedgerEntry."Sales Amount (Actual)";
ValueEntryBuf2."Sales Amount (Expected)" += Profit;
ValueEntryBuf2."Purchase Amount (Expected)" += DiscountAmount;
end;
local procedure AddReportLine(var ValueEntryBuf2: Record "Value Entry")
begin
TempValueEntryBuf := ValueEntryBuf2;
ReportLineNo += 1;
TempValueEntryBuf."Entry No." := ReportLineNo;
TempValueEntryBuf.Insert();
TempValueEntryBuf2.Init();
TempValueEntryBuf2.TransferFields(TempValueEntryBuf);
TempValueEntryBuf2.Insert();
Clear(ValueEntryBuf2);
end;
local procedure IsNewGroup(): Boolean
begin
exit(("Item Ledger Entry"."Source No." <> ValueEntryBuf."Source No.") and (ValueEntryBuf."Source No." <> ''));
end;
local procedure IsLastEntry(): Boolean
begin
exit("Item Ledger Entry"."Entry No." = LastItemLedgEntryNo);
end;
local procedure GetCustName(CustNo: Code[20]): Text[100]
var
Customer: Record Customer;
begin
if Customer.Get(CustNo) then
exit(Customer.Name);
exit('');
end;
local procedure GetTableFilters(TableName: Text; Filters: Text): Text
begin
if Filters <> '' then
exit(StrSubstNo(TableFiltersTxt, TableName, Filters));
exit('');
end;
// Ensures Layout Filter Headings are up to date
local procedure UpdateRequestPageFilterValues()
begin
ItemFilter := GetTableFilters(Item.TableCaption(), Item.GetFilters);
ItemLedgEntryFilter := GetTableFilters("Item Ledger Entry".TableCaption(), "Item Ledger Entry".GetFilters);
#if not CLEAN28
PeriodText := StrSubstNo(PeriodInfoTxt, "Item Ledger Entry".GetFilter("Posting Date"));
#endif
end;
}