Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,17 @@ codeunit 8062 "Billing Proposal"
UsageDataBilling.FindLast();
if UsageDataBilling.Rebilling or (UsageDataBilling."Usage Base Pricing" = Enum::"Usage Based Pricing"::"Usage Quantity") then
BillingLine."Service Object Quantity" := UsageDataBilling.Quantity;
BillingLine."Unit Price" := BillingLine.Amount / BillingLine."Service Object Quantity";
if BillingLine."Service Object Quantity" <> 0 then
BillingLine."Unit Price" := BillingLine.Amount / BillingLine."Service Object Quantity"
else
BillingLine."Unit Price" := UsageDataBilling."Unit Price";
BillingLine."Discount %" := ServiceCommitment."Discount %";
// Apply discount from Subscription Line
BillingLine.Amount := BaseAmount * (1 - ServiceCommitment."Discount %" / 100);
BillingLine."Unit Cost" := UsageDataBilling."Cost Amount" / UsageDataBilling.Quantity;
if UsageDataBilling.Quantity <> 0 then
BillingLine."Unit Cost" := UsageDataBilling."Cost Amount" / UsageDataBilling.Quantity
else
BillingLine."Unit Cost" := UsageDataBilling."Unit Cost";
Currency.Initialize(ServiceCommitment."Currency Code");
Currency.TestField("Unit-Amount Rounding Precision");
BillingLine."Unit Cost (LCY)" := Round(CurrExchRate.ExchangeAmtFCYToLCY(ServiceCommitment."Currency Factor Date", ServiceCommitment."Currency Code", BillingLine."Unit Cost", ServiceCommitment."Currency Factor"), Currency."Unit-Amount Rounding Precision");
Expand All @@ -421,15 +427,9 @@ codeunit 8062 "Billing Proposal"
BillingLine."Unit Cost" := Round(BillingLine."Unit Cost", Currency."Unit-Amount Rounding Precision");
BillingLine."Unit Cost (LCY)" := Round(BillingLine."Unit Cost (LCY)", GLSetup."Unit-Amount Rounding Precision");

BillingLine.Amount := CalculateBillingLineServiceAmount(BillingLine);
BillingLine.Amount := Round(BillingLine.Amount, Currency."Amount Rounding Precision");
BillingLine.Amount := Round(BillingLine."Unit Price" * BillingLine."Service Object Quantity" * (1 - BillingLine."Discount %" / 100), Currency."Amount Rounding Precision");
end;

internal procedure CalculateBillingLineServiceAmount(var BillingLine: Record "Billing Line") ServiceAmount: Decimal
begin
BillingLine.TestField("Service Object Quantity");
ServiceAmount := BillingLine."Unit Price" * BillingLine."Service Object Quantity" * (1 - BillingLine."Discount %" / 100);
end;

local procedure UpdateBillingLineFromServiceCommitment(var BillingLine: Record "Billing Line"; ServiceCommitment: Record "Subscription Line")
var
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,6 @@ codeunit 8060 "Create Billing Documents"
var
UsageDataBilling: Record "Usage Data Billing";
ServiceCommitment: Record "Subscription Line";
NewSalesLineQuantity: Decimal;
NewSalesLineAmount: Decimal;
begin
if not ServiceCommitment.Get(BillingLine."Subscription Line Entry No.") then
exit;
Expand All @@ -328,15 +326,21 @@ codeunit 8060 "Create Billing Documents"
if not ServiceCommitment.IsUsageDataBillingFound(UsageDataBilling, BillingLine."Billing from", BillingLine."Billing to") then
exit;

UsageDataBilling.CalcSums(Amount, Quantity);
NewSalesLineQuantity := SalesLine.Quantity;
NewSalesLineAmount := UsageDataBilling.Amount;
UsageDataBilling.FindLast();
if UsageDataBilling.Rebilling then
NewSalesLineQuantity := UsageDataBilling.Quantity;
SalesLine.Validate(Quantity, UsageDataBilling.Quantity);
if SalesLine.Quantity = 0 then begin
UsageDataBilling.SetFilter(Quantity, '<>0');
if UsageDataBilling.FindLast() then
SalesLine.Validate(Quantity, UsageDataBilling.Quantity);
end;

SalesLine.Validate(Quantity, NewSalesLineQuantity);
SalesLine.Validate("Unit Price", SalesLine.GetSalesDocumentSign() * NewSalesLineAmount / NewSalesLineQuantity);
UsageDataBilling.SetRange(Quantity);
UsageDataBilling.CalcSums(Amount);
if SalesLine.Quantity <> 0 then
SalesLine.Validate("Unit Price", SalesLine.GetSalesDocumentSign() * UsageDataBilling.Amount / SalesLine.Quantity)
else
SalesLine.Validate("Unit Price", UsageDataBilling."Unit Price");
SalesLine.Validate("Line Discount %", ServiceCommitment."Discount %");
end;

Expand Down Expand Up @@ -419,8 +423,6 @@ codeunit 8060 "Create Billing Documents"
var
UsageDataBilling: Record "Usage Data Billing";
ServiceCommitment: Record "Subscription Line";
NewPurchaseLineQuantity: Decimal;
NewPurchaseLineAmount: Decimal;
begin
if not ServiceCommitment.Get(BillingLine."Subscription Line Entry No.") then
exit;
Expand All @@ -430,15 +432,21 @@ codeunit 8060 "Create Billing Documents"
if not ServiceCommitment.IsUsageDataBillingFound(UsageDataBilling, BillingLine."Billing from", BillingLine."Billing to") then
exit;

UsageDataBilling.CalcSums("Cost Amount", Quantity);
NewPurchaseLineQuantity := PurchLine.Quantity;
NewPurchaseLineAmount := UsageDataBilling."Cost Amount";
UsageDataBilling.FindLast();
if UsageDataBilling.Rebilling then
NewPurchaseLineQuantity := UsageDataBilling.Quantity;
PurchLine.Validate(Quantity, UsageDataBilling.Quantity);
if PurchLine.Quantity = 0 then begin
UsageDataBilling.SetFilter(Quantity, '<>0');
if UsageDataBilling.FindLast() then
PurchLine.Validate(Quantity, UsageDataBilling.Quantity);
end;

PurchLine.Validate(Quantity, NewPurchaseLineQuantity);
PurchLine.Validate("Direct Unit Cost", PurchLine.GetPurchaseDocumentSign() * NewPurchaseLineAmount / NewPurchaseLineQuantity);
UsageDataBilling.SetRange(Quantity);
UsageDataBilling.CalcSums("Cost Amount");
if PurchLine.Quantity <> 0 then
PurchLine.Validate("Direct Unit Cost", PurchLine.GetPurchaseDocumentSign() * UsageDataBilling."Cost Amount" / PurchLine.Quantity)
else
PurchLine.Validate("Direct Unit Cost", 0);
PurchLine.Validate("Line Discount %", ServiceCommitment."Discount %");
end;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,41 @@ codeunit 8066 "Purchase Documents"
end
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", OnBeforeCheckHeaderPostingType, '', false, false)]
local procedure SkipInvoiceOrShipFlagCheckForSubscriptionBillingOnBeforeCheckHeaderPostingType(var PurchaseHeader: Record "Purchase Header"; var IsHandled: Boolean)
begin
// Allow posting without Invoice or Ship flags being set for subscription billing documents
if PurchaseHeader."Recurring Billing" then
IsHandled := true;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Purchase Document", OnCodeOnAfterPurchLineSetFilters, '', false, false)]
local procedure SkipQuantityCheckForSubscriptionBillingOnCodeOnAfterPurchLineSetFilters(PurchaseHeader: Record "Purchase Header"; var PurchaseLine: Record "Purchase Line"; var IsHandled: Boolean)
begin
// Skip quantity check for subscription billing documents
if PurchaseHeader."Recurring Billing" then
IsHandled := true;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", OnBeforeCalcInvoice, '', false, false)]
local procedure ForceInvoiceCreationForZeroQtyDocumentOnBeforeCalcInvoice(var PurchHeader: Record "Purchase Header"; var NewInvoice: Boolean; var IsHandled: Boolean)
begin
// For subscription billing documents with zero quantity lines, force invoice creation
// so that the posted invoice header is always generated
if PurchHeader."Recurring Billing" then begin
NewInvoice := true;
IsHandled := true;
end;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", OnPostPurchLineOnAfterSetEverythingInvoiced, '', false, false)]
local procedure SetEverythingInvoicedForZeroQtyDocumentOnAfterSetEverythingInvoiced(PurchaseHeader: Record "Purchase Header"; var EverythingInvoiced: Boolean)
begin
// Treat zero-qty subscription billing lines as fully invoiced so BC cleans up the source document
if PurchaseHeader."Recurring Billing" then
EverythingInvoiced := true;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", OnBeforeDeleteAfterPosting, '', false, false)]
local procedure PurchasePostOnBeforePurchaseLineDeleteAll(var PurchaseHeader: Record "Purchase Header"; var PurchInvHeader: Record "Purch. Inv. Header"; var PurchCrMemoHdr: Record "Purch. Cr. Memo Hdr.")
var
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,41 @@ codeunit 8063 "Sales Documents"
TempSalesLine.Validate("Qty. to Invoice", 0);
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnBeforeCheckHeaderPostingType, '', false, false)]
local procedure SkipInvoiceOrShipFlagCheckForSubscriptionBillingOnBeforeCheckHeaderPostingType(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
begin
// Allow posting without Invoice or Ship flags being set for subscription billing documents
if SalesHeader."Recurring Billing" then
IsHandled := true;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", OnBeforeSalesLineFind, '', false, false)]
local procedure SkipQuantityCheckForSubscriptionBillingOnBeforeSalesLineFind(var SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; var IsHandled: Boolean)
begin
// Skip quantity check for subscription billing documents
if SalesHeader."Recurring Billing" then
IsHandled := true;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnBeforeCalcInvoice, '', false, false)]
local procedure ForceInvoiceCreationForZeroQtyDocumentOnBeforeCalcInvoice(SalesHeader: Record "Sales Header"; var TempSalesLineGlobal: Record "Sales Line" temporary; var NewInvoice: Boolean; var IsHandled: Boolean)
begin
// For subscription billing documents with zero quantity lines, force invoice creation
// so that the posted invoice header is always generated
if SalesHeader."Recurring Billing" then begin
NewInvoice := true;
IsHandled := true;
end;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnPostSalesLineOnAfterSetEverythingInvoiced, '', false, false)]
local procedure SetEverythingInvoicedForZeroQtyDocumentOnAfterSetEverythingInvoiced(SalesHeader: Record "Sales Header"; var EverythingInvoiced: Boolean)
begin
// Treat zero-qty subscription billing lines as fully invoiced so BC cleans up the source document
if SalesHeader."Recurring Billing" then
EverythingInvoiced := true;
end;

local procedure CheckResetValueForServiceCommitmentItems(var TempSalesLine: Record "Sales Line") ResetValueForServiceCommitmentItems: Boolean
var
ContractRenewalMgt: Codeunit "Sub. Contract Renewal Mgt.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,17 @@ table 8068 "Sales Subscription Line"
if Amount > MaxServiceAmount then
Error(ServiceAmountIncreaseErr, FieldCaption(Amount), Format(MaxServiceAmount));
"Discount Amount" := Round(MaxServiceAmount - Amount, Currency."Amount Rounding Precision");
"Discount %" := Round(100 - (Amount / MaxServiceAmount * 100), 0.00001);
if MaxServiceAmount <> 0 then
"Discount %" := Round(100 - (Amount / MaxServiceAmount * 100), 0.00001);
end else begin
Amount := Round((Price * SalesLine.Quantity), Currency."Amount Rounding Precision");
if CalledByFieldNo = FieldNo("Discount %") then
"Discount Amount" := Round(Amount * "Discount %" / 100, Currency."Amount Rounding Precision");
if CalledByFieldNo = FieldNo("Discount Amount") then
"Discount %" := Round("Discount Amount" / Amount * 100, 0.00001);
if Amount <> 0 then
"Discount %" := Round("Discount Amount" / Amount * 100, 0.00001)
else
"Discount %" := 0;
Amount := Round((Price * SalesLine.Quantity) - "Discount Amount", Currency."Amount Rounding Precision");
if Amount > MaxServiceAmount then
Error(ServiceAmountIncreaseErr, FieldCaption(Amount), Format(MaxServiceAmount));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,6 @@ table 8059 "Subscription Line"
if MaxServiceAmount <> 0 then
"Discount %" := Round(100 - (Amount / MaxServiceAmount * 100), 0.00001);
end else begin
ServiceObject.TestField(Quantity);
Amount := Price * ServiceObject.Quantity;
if not "Usage Based Billing" then
Amount := Round(Amount, Currency."Amount Rounding Precision");
Expand All @@ -867,7 +866,10 @@ table 8059 "Subscription Line"
"Discount Amount" := Round("Discount Amount", Currency."Amount Rounding Precision");
end;
if CalledByFieldNo = FieldNo("Discount Amount") then
"Discount %" := Round("Discount Amount" / Amount * 100, 0.00001);
if Amount <> 0 then
"Discount %" := Round("Discount Amount" / Amount * 100, 0.00001)
else
"Discount %" := 0;
if ("Discount Amount" > MaxServiceAmount) and ("Discount Amount" <> 0) then
Error(CannotBeGreaterThanErr, FieldCaption("Discount Amount"), Format(MaxServiceAmount));
Amount := Amount - "Discount Amount";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,13 @@ table 8057 "Subscription Header"
{
Caption = 'Quantity';
InitValue = 1;
NotBlank = true;
AutoFormatType = 0;
DecimalPlaces = 0 : 5;

trigger OnValidate()
begin
if Quantity <= 0 then
Error(QtyZeroOrNegativeErr);
if Quantity < 0 then
Error(QtyNegativeErr);
if (Quantity <> 1) and ("Serial No." <> '') then
Error(SerialQtyErr);
Rec.ArchiveServiceCommitments();
Expand Down Expand Up @@ -948,7 +947,7 @@ table 8057 "Subscription Header"
SkipBillToContact: Boolean;
SkipInsertServiceCommitments: Boolean;
ConfirmChangeQst: Label 'Do you want to change %1?', Comment = '%1 = a Field Caption like Currency Code';
QtyZeroOrNegativeErr: Label 'The quantity cannot be zero or negative.';
QtyNegativeErr: Label 'The quantity cannot be negative.';
EndUserCustomerTxt: Label 'End-User Customer';
BillToCustomerTxt: Label 'Bill-to Customer';
SerialQtyErr: Label 'Only Subscriptions with quantity 1 may have a serial number.';
Expand Down Expand Up @@ -2216,14 +2215,18 @@ table 8057 "Subscription Header"
local procedure GetRecalculateLinesDialog(ChangedFieldName: Text): Text
var
RecalculateLinesQst: Label 'If you change %1, the existing Subscription Lines prices will be recalculated.\\Do you want to continue?', Comment = '%1: FieldCaption';
RecalculateLinesFromQuantityQst: Label 'If you change %1, only the Amount for existing service commitments will be recalculated.\\Do you want to continue?', Comment = '%1= Changed Field Name.';
RecalculateLinesFromQuantityQst: Label 'If you change the %1, the amount for open subscription lines will be recalculated.\\Do you want to continue?', Comment = '%1: FieldCaption';
PauseSubscriptionQst: Label 'If you set the %1 to 0, the billing will pause until further notice but the subscription lines are not terminated.\\Do you want to continue?', Comment = '%1: FieldCaption';
RecalculateLinesFromVariantCodeQst: Label 'The %1 has been changed.\\Do you want to update the price?', Comment = '%1= Changed Field Name.';
begin
case ChangedFieldName of
Rec.FieldName(Rec."Variant Code"):
exit(StrSubstNo(RecalculateLinesFromVariantCodeQst, ChangedFieldName));
Rec.FieldName(Rec.Quantity):
exit(StrSubstNo(RecalculateLinesFromQuantityQst, ChangedFieldName));
if Rec.Quantity = 0 then
exit(StrSubstNo(PauseSubscriptionQst, ChangedFieldName))
else
exit(StrSubstNo(RecalculateLinesFromQuantityQst, ChangedFieldName));
else
exit(StrSubstNo(RecalculateLinesQst, ChangedFieldName));
end;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ codeunit 8033 "Generic Connector Processing" implements "Usage Data Processing"
ProcessingSetupErr: Label 'You must specify either a reading/writing XMLport or a reading/writing codeunit.';
UsageDataLinesProcessingErr: Label 'Errors were found while processing the Usage Data Lines.';
NoDataFoundErr: Label 'No data found for processing step %1.', Comment = '%1 = Name of the processing step';
UsageDataWithZeroQuantityCannotBeProcessedErr: Label 'Usage data with Quantity 0 cannot be processed.';
NoServiceObjectErr: Label 'The %1 ''%2'' is not linked to an %3.', Comment = '%1 = Table name, %2 = Entry number, %3 = Table name';
ServiceObjectProvisionEndDateErr: Label 'The %1 ''%2'' is deinstalled.', Comment = '%1 = Table name, %2 = Entry number';
ReferenceNotFoundErr: Label 'For %1 ''%2'' no linked %3 was found.', Comment = '%1 = Field name, %2 = Entry description, %3 = Table name';
Expand Down Expand Up @@ -85,7 +84,6 @@ codeunit 8033 "Generic Connector Processing" implements "Usage Data Processing"
if UsageDataGenericImport.FindSet() then
repeat
UsageDataGenericImport.Validate("Processing Status", Enum::"Processing Status"::None);
ErrorIfUsageDataGenericImportQuantityIsZero(UsageDataGenericImport);
GenericImportSettings.Get(UsageDataImport."Supplier No.");
CreateUsageDataCustomers(GenericImportSettings, UsageDataGenericImport, UsageDataSupplierReference, UsageDataImport."Supplier No.");
CreateUsageDataSubscriptions(GenericImportSettings, UsageDataGenericImport, UsageDataSupplierReference, UsageDataImport);
Expand Down Expand Up @@ -160,14 +158,6 @@ codeunit 8033 "Generic Connector Processing" implements "Usage Data Processing"
end;
end;

local procedure ErrorIfUsageDataGenericImportQuantityIsZero(var UsageDataGenericImport: Record "Usage Data Generic Import")
begin
if UsageDataGenericImport.Quantity <> 0 then
exit;
UsageDataGenericImport."Processing Status" := UsageDataGenericImport."Processing Status"::Error;
UsageDataGenericImport.SetReason(UsageDataWithZeroQuantityCannotBeProcessedErr);
end;

local procedure CheckServiceCommitment(var UsageDataGenericImport: Record "Usage Data Generic Import"; var UsageDataImport: Record "Usage Data Import"; var ServiceCommitment: Record "Subscription Line")
begin
if ImportAndProcessUsageData.GetServiceCommitmentForSubscription(UsageDataImport."Supplier No.", UsageDataGenericImport."Supp. Subscription ID", ServiceCommitment) then
Expand Down
Loading
Loading