-
Notifications
You must be signed in to change notification settings - Fork 358
[Bug][SubscriptionBilling]: Multiple Issues with Usage-Based Billing in Subscription Billing #7299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -194,6 +194,26 @@ table 8069 "Sales Sub. Line Archive" | |
| AutoFormatExpression = ''; | ||
| Caption = 'Unit Cost (LCY)'; | ||
| } | ||
| field(8000; "Usage Based Billing"; Boolean) | ||
| { | ||
| Caption = 'Usage Based Billing'; | ||
| ToolTip = 'Specifies whether usage data is used as the basis for billing via contracts.'; | ||
| DataClassification = CustomerContent; | ||
| } | ||
| field(8001; "Usage Based Pricing"; Enum "Usage Based Pricing") | ||
| { | ||
| Caption = 'Usage Based Pricing'; | ||
| ToolTip = 'Specifies the method for customer based pricing.'; | ||
| DataClassification = CustomerContent; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant DataClassification set on a table, no need to set it on a field level |
||
| } | ||
| field(8002; "Pricing Unit Cost Surcharge %"; Decimal) | ||
| { | ||
| Caption = 'Pricing Unit Cost Surcharge %'; | ||
| ToolTip = 'Specifies the surcharge in percent for the debit-side price calculation, if a EK surcharge is to be used.'; | ||
| DataClassification = CustomerContent; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant DataClassification set on a table, no need to set it on a field level |
||
| AutoFormatType = 0; | ||
| DecimalPlaces = 0 : 5; | ||
| } | ||
| } | ||
|
|
||
| keys | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -295,6 +295,29 @@ table 8073 "Subscription Line Archive" | |
| Editable = false; | ||
| TableRelation = "Dimension Set Entry"; | ||
| } | ||
| field(8000; "Usage Based Billing"; Boolean) | ||
| { | ||
| Caption = 'Usage Based Billing'; | ||
| ToolTip = 'Specifies whether usage data is used as the basis for billing via contracts.'; | ||
| DataClassification = CustomerContent; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant DataClassification set on a table, no need to set it on a field level |
||
| Editable = false; | ||
| } | ||
| field(8001; "Usage Based Pricing"; Enum "Usage Based Pricing") | ||
| { | ||
| Caption = 'Usage Based Pricing'; | ||
| ToolTip = 'Specifies the method for customer based pricing.'; | ||
| DataClassification = CustomerContent; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant DataClassification set on a table, no need to set it on a field level |
||
| Editable = false; | ||
| } | ||
| field(8002; "Pricing Unit Cost Surcharge %"; Decimal) | ||
| { | ||
| Caption = 'Pricing Unit Cost Surcharge %'; | ||
| ToolTip = 'Specifies the surcharge in percent for the debit-side price calculation, if a EK surcharge is to be used.'; | ||
| DataClassification = CustomerContent; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant DataClassification set on a table, no need to set it on a field level |
||
| AutoFormatType = 0; | ||
| DecimalPlaces = 0 : 5; | ||
| Editable = false; | ||
| } | ||
| } | ||
| keys | ||
| { | ||
|
|
@@ -355,6 +378,9 @@ table 8073 "Subscription Line Archive" | |
| Rec."Unit Cost" := ServiceCommitment."Unit Cost"; | ||
| Rec."Unit Cost (LCY)" := ServiceCommitment."Unit Cost (LCY)"; | ||
| Rec.Closed := ServiceCommitment.Closed; | ||
| Rec."Usage Based Billing" := ServiceCommitment."Usage Based Billing"; | ||
| Rec."Usage Based Pricing" := ServiceCommitment."Usage Based Pricing"; | ||
| Rec."Pricing Unit Cost Surcharge %" := ServiceCommitment."Pricing Unit Cost Surcharge %"; | ||
| OnAfterCopyFromSubscriptionLine(Rec, ServiceCommitment); | ||
| end; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,5 @@ | ||
| namespace Microsoft.SubscriptionBilling; | ||
|
|
||
| using System.Utilities; | ||
|
|
||
| codeunit 8023 "Create Usage Data Billing" | ||
| { | ||
| TableNo = "Usage Data Import"; | ||
|
|
@@ -17,38 +15,25 @@ codeunit 8023 "Create Usage Data Billing" | |
| end; | ||
|
|
||
| local procedure Code() | ||
| var | ||
| UsageDataSupplier: Record "Usage Data Supplier"; | ||
| begin | ||
| UsageDataImport.SetFilter("Processing Status", '<>%1', Enum::"Processing Status"::Closed); | ||
| if UsageDataImport.FindSet() then | ||
| repeat | ||
| CheckRetryFailedUsageLines(); | ||
| if not RetryFailedUsageDataImport then | ||
| TestUsageDataImport(); | ||
| UsageDataSupplier.Get(UsageDataImport."Supplier No."); | ||
| UsageDataProcessing := UsageDataSupplier.Type; | ||
| ValidateImportedData(); | ||
| if not (UsageDataImport."Processing Status" = "Processing Status"::Error) then | ||
| FindAndProcessUsageDataImport(); | ||
| CreateUsageDataBillingFromImport(); | ||
| if not (UsageDataImport."Processing Status" = "Processing Status"::Error) then | ||
| SetUsageDataImportError(); | ||
| UpdateImportStatus(); | ||
| until UsageDataImport.Next() = 0; | ||
| end; | ||
|
|
||
| local procedure CheckRetryFailedUsageLines() | ||
| var | ||
| UsageDataBilling: Record "Usage Data Billing"; | ||
| begin | ||
| UsageDataBilling.SetRange("Usage Data Import Entry No.", UsageDataImport."Entry No."); | ||
| if not UsageDataBilling.IsEmpty() then | ||
| if GuiAllowed then | ||
| if ConfirmManagement.GetResponse(StrSubstNo(RetryFailedUsageDataImportTxt, UsageDataImport."Entry No."), false) then | ||
| RetryFailedUsageDataImport := true; | ||
| end; | ||
|
|
||
| local procedure FindAndProcessUsageDataImport() | ||
| var | ||
| UsageDataSupplier: Record "Usage Data Supplier"; | ||
| local procedure CreateUsageDataBillingFromImport() | ||
| begin | ||
| UsageDataSupplier.Get(UsageDataImport."Supplier No."); | ||
| UsageDataProcessing := UsageDataSupplier.Type; | ||
| UsageDataProcessing.FindAndProcessUsageDataImport(UsageDataImport); | ||
| UsageDataProcessing.CreateBillingData(UsageDataImport); | ||
| end; | ||
|
|
||
| internal procedure CollectServiceCommitments(var TempServiceCommitment: Record "Subscription Line" temporary; ServiceObjectNo: Code[20]; SubscriptionEndDate: Date) | ||
|
|
@@ -76,21 +61,24 @@ codeunit 8023 "Create Usage Data Billing" | |
| begin | ||
| UsageDataSupplier.Get(SupplierNo); | ||
|
|
||
| UsageDataBilling.InitFrom(UsageDataImportEntryNo, SubscriptionNo, ProductID, ProductName, BillingPeriodStartDate, BillingPeriodEndDate, UnitCost, NewQuantity, CostAmount, UnitPrice, NewAmount, CurrencyCode); | ||
| UsageDataBilling.InitFrom(UsageDataImportEntryNo, SubscriptionNo, ProductID, ProductName, BillingPeriodStartDate, BillingPeriodEndDate, NewQuantity); | ||
| UsageDataBilling."Supplier No." := SupplierNo; | ||
| UsageDataBilling."Subscription Header No." := TempServiceCommitment."Subscription Header No."; | ||
| UsageDataBilling.Partner := TempServiceCommitment.Partner; | ||
| UsageDataBilling."Subscription Header No." := TempServiceCommitment."Subscription Header No."; | ||
| UsageDataBilling."Subscription Contract No." := TempServiceCommitment."Subscription Contract No."; | ||
| UsageDataBilling."Subscription Contract Line No." := TempServiceCommitment."Subscription Contract Line No."; | ||
| UsageDataBilling."Subscription Header No." := TempServiceCommitment."Subscription Header No."; | ||
| UsageDataBilling."Subscription Line Entry No." := TempServiceCommitment."Entry No."; | ||
| UsageDataBilling."Subscription Line Description" := TempServiceCommitment.Description; | ||
| UsageDataBilling."Usage Base Pricing" := TempServiceCommitment."Usage Based Pricing"; | ||
| UsageDataBilling."Pricing Unit Cost Surcharge %" := TempServiceCommitment."Pricing Unit Cost Surcharge %"; | ||
| if UsageDataBilling.IsPartnerVendor() or not UsageDataSupplier."Unit Price from Import" then begin | ||
| UsageDataBilling."Unit Price" := 0; | ||
| UsageDataBilling.Amount := 0; | ||
| end; | ||
| if CurrencyCode = TempServiceCommitment."Currency Code" then | ||
| UsageDataBilling."Currency Code" := CurrencyCode | ||
| else | ||
| if UsageDataBilling.IsPartnerCustomer() then | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would create only one procedure AlignCurrencyCode, and then check there for the Partner type. |
||
| UsageDataBilling.AlignCustomerContractCurrency(TempServiceCommitment, CurrencyCode) | ||
| else | ||
| UsageDataBilling.AlignVendorContractCurrency(TempServiceCommitment, CurrencyCode); | ||
| UsageDataBilling.CalculateAmounts(UsageDataSupplier, CurrencyCode, UnitCost, CostAmount, UnitPrice, NewAmount); | ||
| UsageDataBilling.UpdateRebilling(); | ||
| UsageDataBilling."Entry No." := 0; | ||
| UsageDataBilling.Insert(true); | ||
|
|
@@ -117,23 +105,24 @@ codeunit 8023 "Create Usage Data Billing" | |
| until ServiceCommitment.Next() = 0; | ||
| end; | ||
|
|
||
| local procedure TestUsageDataImport() | ||
| var | ||
| UsageDataSupplier: Record "Usage Data Supplier"; | ||
| begin | ||
| UsageDataSupplier.Get(UsageDataImport."Supplier No."); | ||
| UsageDataProcessing := UsageDataSupplier.Type; | ||
| UsageDataProcessing.TestUsageDataImport(UsageDataImport); | ||
| end; | ||
|
|
||
| local procedure SetUsageDataImportError() | ||
| local procedure ValidateImportedData() | ||
| begin | ||
| UsageDataProcessing.SetUsageDataImportError(UsageDataImport); | ||
| UsageDataProcessing.ValidateImportedData(UsageDataImport); | ||
| end; | ||
|
|
||
| internal procedure GetRetryFailedUsageDataImport(): Boolean | ||
| local procedure UpdateImportStatus() | ||
| var | ||
| UsageDataBilling: Record "Usage Data Billing"; | ||
| begin | ||
| exit(RetryFailedUsageDataImport); | ||
| UsageDataProcessing.UpdateImportStatus(UsageDataImport); | ||
| if UsageDataImport."Processing Status" = Enum::"Processing Status"::Error then | ||
| exit; | ||
| UsageDataBilling.SetRange("Usage Data Import Entry No.", UsageDataImport."Entry No."); | ||
| UsageDataBilling.SetRange("Processing Status", Enum::"Processing Status"::Error); | ||
| if not UsageDataBilling.IsEmpty() then begin | ||
| UsageDataImport.SetErrorReason(UsageDataLinesProcessingErr); | ||
| UsageDataImport.Modify(false); | ||
| end; | ||
| end; | ||
|
|
||
| [IntegrationEvent(false, false)] | ||
|
|
@@ -153,7 +142,5 @@ codeunit 8023 "Create Usage Data Billing" | |
|
|
||
| var | ||
| UsageDataImport: Record "Usage Data Import"; | ||
| ConfirmManagement: Codeunit "Confirm Management"; | ||
| RetryFailedUsageDataImportTxt: Label 'Usage Data Billing for Import %1 already exist. Do you want to try to create new entries for the failed Usage Data Generic Import only?', Comment = '%1=Usage Data Import Entry No.'; | ||
| RetryFailedUsageDataImport: Boolean; | ||
| UsageDataLinesProcessingErr: Label 'Errors were detected when processing the usage data lines.'; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant DataClassification set on a table, no need to set it on a field level