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 @@ -470,6 +470,11 @@ table 8061 "Billing Line"
Rec.SetRange("Subscription Contract Line No.", ContractLineNo);
end;

internal procedure FilterBillingLineOnServiceCommitment(ServiceCommitmentEntryNo: Integer)
begin
Rec.SetRange("Subscription Line Entry No.", ServiceCommitmentEntryNo);
end;

local procedure RecalculateCustomerContractHarmonizedBillingFields()
var
CustomerContract: Record "Customer Subscription Contract";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ table 8064 "Billing Line Archive"
key(SK1; "Subscription Contract No.", "Subscription Contract Line No.", "Billing from")
{
}
key(SK2; "Subscription Line Entry No.")
{
}
}

fieldgroups
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ table 8059 "Subscription Line"

trigger OnValidate()
begin
CheckSubscriptionLineStartDateChangeAllowed();
DateFormulaManagement.ErrorIfDateEmpty("Subscription Line Start Date", FieldCaption("Subscription Line Start Date"));
UpdateNextBillingDate("Subscription Line Start Date" - 1);
CheckServiceDates();
Expand Down Expand Up @@ -1022,11 +1023,7 @@ table 8059 "Subscription Line"
FieldNo("Invoicing Item No."):
Validate("Invoicing Item No.", "Invoicing Item No.");
FieldNo("Subscription Line Start Date"):
begin
Rec.ErrorIfBillingLineArchiveForServiceCommitmentExist();
Rec.ErrorIfBillingLineForServiceCommitmentExist();
Validate("Subscription Line Start Date", "Subscription Line Start Date");
end;
Validate("Subscription Line Start Date", "Subscription Line Start Date");
FieldNo("Subscription Line End Date"):
Validate("Subscription Line End Date", "Subscription Line End Date");
FieldNo(Quantity):
Expand Down Expand Up @@ -1475,11 +1472,42 @@ table 8059 "Subscription Line"
end;

internal procedure ErrorIfBillingLineForServiceCommitmentExist()
var
BillingLine: Record "Billing Line";
begin
if BillingLineExists() then
BillingLine.FilterBillingLineOnServiceCommitment(Rec."Entry No.");
if not BillingLine.IsEmpty() then
Error(BillingLineForServiceCommitmentExistErr);
end;

/// <summary>
/// Checks whether the Subscription Line Start Date may still be changed.
/// The change is allowed as long as no billing has taken place for the Subscription Line, or as long as the
/// Next Billing Date is still on the Subscription Line Start Date, which is the state after a cancellation
/// or a credit memo and the case in which the billing period legitimately has to be corrected.
/// </summary>
local procedure CheckSubscriptionLineStartDateChangeAllowed()
begin
CheckBillingStateAllowsSubscriptionLineStartDateChange();
OnAfterCheckSubscriptionLineStartDateChangeAllowed(Rec);
end;

local procedure CheckBillingStateAllowsSubscriptionLineStartDateChange()
var
SubscriptionLine: Record "Subscription Line";
begin
if Rec.IsTemporary() then
exit;
SubscriptionLine.SetLoadFields("Subscription Line Start Date", "Next Billing Date");
if not SubscriptionLine.Get(Rec."Entry No.") then
exit;
if SubscriptionLine."Next Billing Date" = SubscriptionLine."Subscription Line Start Date" then
exit;

ErrorIfBillingLineArchiveForServiceCommitmentExist();
ErrorIfBillingLineForServiceCommitmentExist();
end;

internal procedure GetPartnerNoFromContract(): Code[20]
var
CustomerContract: Record "Customer Subscription Contract";
Expand All @@ -1504,8 +1532,7 @@ table 8059 "Subscription Line"
BillingLineArchive: Record "Billing Line Archive";
begin
BillingLineArchive.FilterBillingLineArchiveOnServiceCommitment(Rec."Entry No.");
BillingLineArchive.CalcSums(Amount);
if BillingLineArchive.Amount <> 0 then
if not BillingLineArchive.IsEmpty() then
Error(BillingLineArchiveForServiceCommitmentExistErr);
end;

Expand Down Expand Up @@ -2111,6 +2138,16 @@ table 8059 "Subscription Line"
begin
end;

/// <summary>
/// Raised after the Subscription Line Start Date change has passed the billing state check.
/// Subscribers can only tighten the rule, by raising an error of their own.
/// </summary>
/// <param name="SubscriptionLine">The Subscription Line carrying the new Subscription Line Start Date.</param>
[IntegrationEvent(false, false)]
local procedure OnAfterCheckSubscriptionLineStartDateChangeAllowed(SubscriptionLine: Record "Subscription Line")
begin
end;

[IntegrationEvent(false, false)]
local procedure OnAfterCalculateServiceAmount(var SubscriptionLine: Record "Subscription Line"; CalledByFieldNo: Integer)
begin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ codeunit 148156 "Service Commitment Test"
DiscountCanBeInvoicedViaContractErr: Label 'Recurring discounts can only be granted for Invoicing via Contract.', Locked = true;
DiscountCannotBeAssignedErr: Label 'Subscription Package Lines, which are discounts, can only be assigned to Subscription Items.', Locked = true;
RecurringDiscountCannotBeGrantedErr: Label 'Recurring discounts cannot be granted in conjunction with Usage Based Billing', Locked = true;
BillingLineForServiceCommitmentExistErr: Label 'The contract line is in the current billing. Delete the billing line to be able to adjust the Subscription Line start date.', Locked = true;
BillingLineArchiveForServiceCommitmentExistErr: Label 'The contract line has already been billed. The Subscription Line start date can no longer be changed.', Locked = true;

#region Tests

Expand Down Expand Up @@ -597,6 +599,139 @@ codeunit 148156 "Service Commitment Test"
Assert.ExpectedError(RecurringDiscountCannotBeGrantedErr);
end;

[Test]
[HandlerFunctions('CreateCustomerBillingDocsPageHandler,ExchangeRateSelectionModalPageHandler,MessageHandler')]
procedure PreventStartDateChangeOnSubscriptionLinesPageAfterBilling()
var
BillingLine: Record "Billing Line";
BillingTemplate: Record "Billing Template";
SalesHeader: Record "Sales Header";
SubscriptionLine: Record "Subscription Line";
ServiceCommitmentsPage: TestPage "Service Commitments";
begin
// [SCENARIO] The Subscription Line Start Date can no longer be changed on the Subscription Lines page after the Subscription Line has been billed

// [GIVEN] A Customer Subscription Contract with a Subscription Line for which an invoice has been posted
Initialize();
BillContractAndPostInvoice(BillingTemplate, BillingLine, SalesHeader);
SubscriptionLine.Get(BillingLine."Subscription Line Entry No.");
Assert.AreNotEqual(SubscriptionLine."Subscription Line Start Date", SubscriptionLine."Next Billing Date", 'The Subscription Line should have been billed.');
Commit(); // retain data after asserterror

// [WHEN] The Subscription Line Start Date is changed on the Subscription Lines page
ServiceCommitmentsPage.OpenEdit();
ServiceCommitmentsPage.GoToRecord(SubscriptionLine);

// [THEN] The change is rejected with the same error as on the contract line list
asserterror ServiceCommitmentsPage."Service Start Date".SetValue(GetDifferentDateAllowedByLicense(SubscriptionLine."Subscription Line Start Date"));
Assert.ExpectedError(BillingLineArchiveForServiceCommitmentExistErr);
end;

[Test]
procedure UT_PreventStartDateChangeWhenBilledSubscriptionLineHasZeroAmount()
var
SubscriptionLine: Record "Subscription Line";
begin
// [SCENARIO] The Subscription Line Start Date can no longer be changed for a Subscription Line which has been billed at zero value

// [GIVEN] A billed Subscription Line whose archived billing lines add up to zero
Initialize();
MockBilledSubscriptionLine(SubscriptionLine);
MockBillingLineArchive(SubscriptionLine."Entry No.", 0);

// [WHEN] The Subscription Line Start Date is changed
// [THEN] The change is rejected
asserterror SubscriptionLine.Validate("Subscription Line Start Date", CalcDate('<+2M>', SubscriptionLine."Subscription Line Start Date"));
Assert.ExpectedError(BillingLineArchiveForServiceCommitmentExistErr);
end;

[Test]
procedure UT_PreventStartDateChangeWhenSubscriptionLineIsInCurrentBilling()
var
SubscriptionLine: Record "Subscription Line";
begin
// [SCENARIO] The Subscription Line Start Date cannot be changed as long as the Subscription Line is part of the current billing

// [GIVEN] A Subscription Line with an open Billing Line
Initialize();
MockBilledSubscriptionLine(SubscriptionLine);
MockBillingLine(SubscriptionLine."Entry No.");

// [WHEN] The Subscription Line Start Date is changed
// [THEN] The change is rejected
asserterror SubscriptionLine.Validate("Subscription Line Start Date", CalcDate('<+2M>', SubscriptionLine."Subscription Line Start Date"));
Assert.ExpectedError(BillingLineForServiceCommitmentExistErr);
end;

[Test]
procedure UT_AllowStartDateChangeWhenNextBillingDateIsOnStartDate()
var
SubscriptionLine: Record "Subscription Line";
NewStartDate: Date;
begin
// [SCENARIO] The Subscription Line Start Date may be corrected as long as the Next Billing Date is back on the Subscription Line Start Date

// [GIVEN] A billed Subscription Line whose Next Billing Date has been reset to the Subscription Line Start Date by a credit memo
Initialize();
MockBilledSubscriptionLine(SubscriptionLine);
MockBillingLineArchive(SubscriptionLine."Entry No.", LibraryRandom.RandDec(100, 2));
SubscriptionLine."Next Billing Date" := SubscriptionLine."Subscription Line Start Date";
SubscriptionLine.Modify(false);

// [WHEN] The Subscription Line Start Date is changed
NewStartDate := CalcDate('<+2M>', SubscriptionLine."Subscription Line Start Date");
SubscriptionLine.Validate("Subscription Line Start Date", NewStartDate);

// [THEN] The change is accepted and the Next Billing Date follows the new Subscription Line Start Date
SubscriptionLine.TestField("Subscription Line Start Date", NewStartDate);
SubscriptionLine.TestField("Next Billing Date", NewStartDate);
end;

[Test]
procedure UT_AllowStartDateChangeWhenSubscriptionLineHasNotBeenBilled()
var
SubscriptionLine: Record "Subscription Line";
NewStartDate: Date;
begin
// [SCENARIO] The Subscription Line Start Date may be changed as long as no billing has been performed for the Subscription Line

// [GIVEN] A Subscription Line without any Billing Line and without any Billing Line Archive
Initialize();
MockBilledSubscriptionLine(SubscriptionLine);

// [WHEN] The Subscription Line Start Date is changed
NewStartDate := CalcDate('<+2M>', SubscriptionLine."Subscription Line Start Date");
SubscriptionLine.Validate("Subscription Line Start Date", NewStartDate);

// [THEN] The change is accepted and the Next Billing Date follows the new Subscription Line Start Date
SubscriptionLine.TestField("Subscription Line Start Date", NewStartDate);
SubscriptionLine.TestField("Next Billing Date", NewStartDate);
end;

[Test]
procedure UT_AllowStartDateChangeOnTemporarySubscriptionLine()
var
SubscriptionLine: Record "Subscription Line";
TempSubscriptionLine: Record "Subscription Line" temporary;
NewStartDate: Date;
begin
// [SCENARIO] Buffering a billed Subscription Line in a temporary record, as the contract renewal does, is not blocked by the start date check

// [GIVEN] A billed Subscription Line and a temporary Subscription Line carrying its Entry No.
Initialize();
MockBilledSubscriptionLine(SubscriptionLine);
MockBillingLineArchive(SubscriptionLine."Entry No.", LibraryRandom.RandDec(100, 2));
TempSubscriptionLine.Init();
TempSubscriptionLine."Entry No." := SubscriptionLine."Entry No.";

// [WHEN] The Subscription Line Start Date is set on the temporary record
NewStartDate := CalcDate('<+2M>', SubscriptionLine."Subscription Line Start Date");
TempSubscriptionLine.Validate("Subscription Line Start Date", NewStartDate);

// [THEN] The value is accepted
TempSubscriptionLine.TestField("Subscription Line Start Date", NewStartDate);
end;

#endregion Tests

#region Procedures
Expand Down Expand Up @@ -637,6 +772,58 @@ codeunit 148156 "Service Commitment Test"
until ServiceCommitment.Next() = 0;
end;

local procedure BillContractAndPostInvoice(var BillingTemplate: Record "Billing Template"; var BillingLine: Record "Billing Line"; var SalesHeader: Record "Sales Header")
begin
ContractTestLibrary.CreateCustomerContractAndCreateContractLinesForItems(CustomerContract, ServiceObject, '');
ContractTestLibrary.DisableDeferralsForCustomerContract(CustomerContract, false);
ContractTestLibrary.CreateBillingProposal(BillingTemplate, Enum::"Service Partner"::Customer);
BillingLine.SetRange("Billing Template Code", BillingTemplate.Code);
BillingLine.SetRange(Partner, BillingLine.Partner::Customer);
Codeunit.Run(Codeunit::"Create Billing Documents", BillingLine);
BillingLine.FindLast();
SalesHeader.Get(SalesHeader."Document Type"::Invoice, BillingLine."Document No.");
LibrarySales.PostSalesDocument(SalesHeader, true, true);
end;

local procedure GetDifferentDateAllowedByLicense(ReferenceDate: Date) NewDate: Date
begin
// the date filter of the demo license only allows dates in November, December, January and February
NewDate := DMY2Date(1, 2, Date2DMY(ReferenceDate, 3));
if NewDate = ReferenceDate then
NewDate := DMY2Date(1, 1, Date2DMY(ReferenceDate, 3));
end;

local procedure MockBilledSubscriptionLine(var SubscriptionLine: Record "Subscription Line")
begin
SubscriptionLine.Init();
SubscriptionLine."Entry No." := 0;
SubscriptionLine."Invoicing via" := SubscriptionLine."Invoicing via"::Contract;
SubscriptionLine."Subscription Line Start Date" := WorkDate();
SubscriptionLine."Next Billing Date" := CalcDate('<+1M>', WorkDate());
SubscriptionLine.Insert(false);
end;

local procedure MockBillingLine(SubscriptionLineEntryNo: Integer)
var
BillingLine: Record "Billing Line";
begin
BillingLine.Init();
BillingLine."Entry No." := 0;
BillingLine."Subscription Line Entry No." := SubscriptionLineEntryNo;
BillingLine.Insert(false);
end;

local procedure MockBillingLineArchive(SubscriptionLineEntryNo: Integer; ArchivedAmount: Decimal)
var
BillingLineArchive: Record "Billing Line Archive";
begin
BillingLineArchive.Init();
BillingLineArchive."Entry No." := 0;
BillingLineArchive."Subscription Line Entry No." := SubscriptionLineEntryNo;
BillingLineArchive.Amount := ArchivedAmount;
BillingLineArchive.Insert(false);
end;

local procedure MockSubscriptionLine(var SubscriptionLine: Record "Subscription Line")
begin
SubscriptionLine.Init();
Expand Down Expand Up @@ -681,6 +868,12 @@ codeunit 148156 "Service Commitment Test"
Reply := true;
end;

[ModalPageHandler]
procedure CreateCustomerBillingDocsPageHandler(var CreateCustomerBillingDocs: TestPage "Create Customer Billing Docs")
begin
CreateCustomerBillingDocs.OK().Invoke();
end;

[ModalPageHandler]
procedure ExchangeRateSelectionModalPageHandler(var ExchangeRateSelectionPage: TestPage "Exchange Rate Selection")
begin
Expand Down
Loading