billing - stripe - nbrOfItems replaced by sessions.length
MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -237,12 +237,6 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
return stripeInvoice;
}
- // private getNumberOfItems(stripeInvoice: Stripe.Invoice): number {
- // // STRIPE version 8.137.0 - total_count property is deprecated - TODO - find another way to get it!
- // const nbrOfItems: number = stripeInvoice.lines['total_count'];
- // return nbrOfItems;
- // }
-
// ----------------------------------------------------------
// TODO - get rid of this logic and use webhooks instead!
// ----------------------------------------------------------
@@ -873,10 +867,11 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
private shrinkInvoiceItem(fatInvoiceItem: BillingInvoiceItem): BillingInvoiceItem {
// The initial invoice item includes redundant transaction data
- const { description, pricingData } = fatInvoiceItem;
+ const { description, transactionID, pricingData } = fatInvoiceItem;
// Let's return only essential information
const lightInvoiceItem: BillingInvoiceItem = {
description,
+ transactionID,
pricingData
};
return lightInvoiceItem;
@@ -884,6 +879,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
private convertToBillingInvoiceItem(transaction: Transaction) : BillingInvoiceItem {
// Destructuring transaction.stop
+ const transactionID = transaction.id;
const { price, priceUnit, roundedPrice, totalConsumptionWh, timestamp } = transaction.stop;
const description = this.buildLineItemDescription(transaction);
// -------------------------------------------------------------------------------
@@ -897,6 +893,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
// Build a billing invoice item based on the transaction
const billingInvoiceItem: BillingInvoiceItem = {
description,
+ transactionID,
pricingData: {
quantity,
amount,

MODIFY
src/integration/billing/stripe/StripeHelpers.ts
src/integration/billing/stripe/StripeHelpers.ts
-import { BillingAdditionalData, BillingError, BillingErrorCode, BillingErrorType, BillingInvoice, BillingInvoiceItem, BillingOperationResult } from '../../../types/Billing';
+import { BillingAdditionalData, BillingError, BillingErrorCode, BillingErrorType, BillingInvoice, BillingInvoiceItem, BillingOperationResult, BillingSessionData } from '../../../types/Billing';
import BillingStorage from '../../../storage/mongodb/BillingStorage';
import Stripe from 'stripe';
export default class StripeHelpers {
- public static async updateInvoiceAdditionalData(tenantID: string, billingInvoice: BillingInvoice,
- operationResult: BillingOperationResult, billingInvoiceItem?: BillingInvoiceItem): Promise<void> {
- if (!billingInvoice) {
- return;
- }
- let billingError = null;
+ public static async updateInvoiceAdditionalData(tenantID: string,
+ billingInvoice: BillingInvoice,
+ operationResult: BillingOperationResult,
+ billingInvoiceItem?: BillingInvoiceItem): Promise<void> {
+ // Do we have an error to preserve
+ let billingError: BillingError;
if (operationResult && !operationResult.succeeded) {
// The operation failed
billingError = StripeHelpers.convertToBillingError(operationResult.error);
}
- if (billingInvoiceItem || billingError) {
+ // Do we have a new charging session?
+ let session: BillingSessionData;
+ if (billingInvoiceItem) {
+ session = {
+ transactionID: billingInvoiceItem.transactionID,
+ description: billingInvoiceItem.description,
+ pricingData: billingInvoiceItem.pricingData,
+ };
+ }
+ // Is there anything to update?
+ if (session || billingError) {
const additionalData: BillingAdditionalData = {
- item: billingInvoiceItem,
+ session,
lastError: billingError
};
await BillingStorage.updateInvoiceAdditionalData(tenantID, billingInvoice, additionalData);

MODIFY
src/server/rest/v1/service/BillingService.ts
src/server/rest/v1/service/BillingService.ts
@@ -292,7 +292,7 @@ export default class BillingService {
onlyRecordCount: filteredRequest.OnlyRecordCount
},
[
- 'id', 'number', 'status', 'amount', 'createdOn', 'nbrOfItems', 'currency', 'downloadable',
+ 'id', 'number', 'status', 'amount', 'createdOn', 'currency', 'downloadable', 'sessions',
...userProject
]);
// Return

MODIFY
src/storage/mongodb/BillingStorage.ts
src/storage/mongodb/BillingStorage.ts
-import { BillingAdditionalData, BillingInvoice, BillingInvoiceItem, BillingInvoiceStatus } from '../../types/Billing';
+import { BillingAdditionalData, BillingInvoice, BillingInvoiceStatus, BillingSessionData } from '../../types/Billing';
import global, { FilterParams } from '../../types/GlobalType';
import Constants from '../../utils/Constants';
@@ -173,7 +173,6 @@ export default class BillingStorage {
status: invoiceToSave.status,
currency: invoiceToSave.currency,
createdOn: Utils.convertToDate(invoiceToSave.createdOn),
- // nbrOfItems: Utils.convertToInt(invoiceToSave.nbrOfItems),
downloadable: Utils.convertToBoolean(invoiceToSave.downloadable),
downloadUrl: invoiceToSave.downloadUrl
};
@@ -193,15 +192,14 @@ export default class BillingStorage {
const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveInvoiceAdditionalData');
// Check Tenant
await DatabaseUtils.checkTenant(tenantID);
- // Preserve the previous list of items
- const items: BillingInvoiceItem[] = invoiceToUpdate.items || [];
- if (additionalData.item) {
- items.push(additionalData.item);
+ // Preserve the previous list of sessions
+ const sessions: BillingSessionData[] = invoiceToUpdate.sessions || [];
+ if (additionalData.session) {
+ sessions.push(additionalData.session);
}
// Set data
const updatedInvoiceMDB: any = {
- nbrOfItems: items.length,
- items,
+ sessions,
lastError: additionalData.lastError
};
await global.database.getCollection(tenantID, 'invoices').findOneAndUpdate(

MODIFY
src/types/Billing.ts
src/types/Billing.ts
@@ -78,20 +78,16 @@ export interface BillingInvoice {
currency?: string;
customerID?: string;
createdOn?: Date;
- nbrOfItems?: number;
downloadable?: boolean
downloadUrl?: string;
- items?: BillingInvoiceItem[];
+ sessions?: BillingSessionData[];
lastError?: BillingError;
}
export interface BillingInvoiceItem {
description: string;
- pricingData: {
- quantity: number,
- amount: number,
- currency: string
- }
+ transactionID: number;
+ pricingData: BillingPricingData;
taxes?: string[];
metadata?: {
// Just a flat list of key/value pairs!
@@ -99,6 +95,18 @@ export interface BillingInvoiceItem {
}
}
+export interface BillingSessionData {
+ transactionID: number;
+ description: string;
+ pricingData: BillingPricingData;
+}
+
+export interface BillingPricingData {
+ quantity: number,
+ amount: number,
+ currency: string
+}
+
export enum BillingInvoiceStatus {
PAID = 'paid',
OPEN = 'open',
@@ -153,6 +161,6 @@ export enum BillingErrorCode {
}
export interface BillingAdditionalData {
- item?: BillingInvoiceItem,
+ session?: BillingSessionData,
lastError?: BillingError,
}

MODIFY
test/api/BillingStripeIntegrationTest.ts
test/api/BillingStripeIntegrationTest.ts
-import chai, { expect } from 'chai';
-
-import { BillingInvoiceStatus } from '../../src/types/Billing';
import MongoDBStorage from '../../src/storage/mongodb/MongoDBStorage';
import StripeIntegrationTestData from './BillingStripeTestData';
-import TestConstants from './client/utils/TestConstants';
+import chai from 'chai';
import chaiSubset from 'chai-subset';
import config from '../config';
import global from '../../src/types/GlobalType';

MODIFY
test/api/BillingStripeTestData.ts
test/api/BillingStripeTestData.ts
@@ -246,6 +246,7 @@ export default class StripeIntegrationTestData {
// array of tax ids to apply to the line item
const invoiceItem:BillingInvoiceItem = {
description: `Stripe Integration - ${quantity} kWh * ${price} Eur`,
+ transactionID: 777,
pricingData: {
quantity, // kW.h
amount, // total amount to bill - not yet in cents

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -154,11 +154,11 @@ class TestData {
return (draftInvoices && draftInvoices.length > 0) ? draftInvoices[0] : null;
}
- public async getNumberOfItems(userId?: string): Promise<number> {
+ public async getNumberOfSessions(userId?: string): Promise<number> {
// ACHTUNG: There is no data after running: npm run mochatest:createContext
// In that situation we return 0!
const draftInvoice = await this.getLatestDraftInvoice(userId);
- return (draftInvoice) ? draftInvoice.nbrOfItems : 0;
+ return (draftInvoice) ? draftInvoice.sessions?.length : 0;
}
}
@@ -199,11 +199,11 @@ describe('Billing Service', function() {
it('should add an item to a DRAFT invoice after a transaction', async () => {
await testData.userService.billingApi.forceSynchronizeUser({ id: testData.userContext.id });
- const itemsBefore = await testData.getNumberOfItems(testData.userContext.id);
+ const itemsBefore = await testData.getNumberOfSessions(testData.userContext.id);
const transactionID = await testData.generateTransaction(testData.userContext);
expect(transactionID).to.not.be.null;
// await testData.userService.billingApi.synchronizeInvoices({});
- const itemsAfter = await testData.getNumberOfItems(testData.userContext.id);
+ const itemsAfter = await testData.getNumberOfSessions(testData.userContext.id);
expect(itemsAfter).to.be.gt(itemsBefore);
});
@@ -264,11 +264,11 @@ describe('Billing Service', function() {
it('should add an item to the existing invoice after a transaction', async () => {
await testData.userService.billingApi.forceSynchronizeUser({ id: testData.userContext.id });
- const itemsBefore = await testData.getNumberOfItems(testData.userContext.id);
+ const itemsBefore = await testData.getNumberOfSessions(testData.userContext.id);
const transactionID = await testData.generateTransaction(testData.userContext);
expect(transactionID).to.not.be.null;
// await testData.userService.billingApi.synchronizeInvoices({});
- const itemsAfter = await testData.getNumberOfItems(testData.userContext.id);
+ const itemsAfter = await testData.getNumberOfSessions(testData.userContext.id);
expect(itemsAfter).to.be.eq(itemsBefore + 1);
});
@@ -424,11 +424,11 @@ describe('Billing Service', function() {
testData.userContext
);
await testData.userService.billingApi.synchronizeInvoices({});
- const itemsBefore = await testData.getNumberOfItems(basicUser.id);
+ const itemsBefore = await testData.getNumberOfSessions(basicUser.id);
const transactionID = await testData.generateTransaction(testData.userContext);
expect(transactionID).to.not.be.null;
await testData.userService.billingApi.synchronizeInvoices({});
- const itemsAfter = await testData.getNumberOfItems(basicUser.id);
+ const itemsAfter = await testData.getNumberOfSessions(basicUser.id);
expect(itemsAfter).to.be.eq(itemsBefore + 1);
});
});

billing - stripe - nbrOfItems replaced by sessions.length