feat(site): assign to sub-account
MODIFY
src/assets/server/rest/v1/schemas/site/site-create.json
src/assets/server/rest/v1/schemas/site/site-create.json
"image": {
"$ref": "site#/definitions/image"
},
- "subAccountID": {
- "$ref": "common#/definitions/id"
+ "billing": {
+ "$ref": "billing#/definitions/subAccount"
}
},
"required": [

MODIFY
src/assets/server/rest/v1/schemas/site/site-update.json
src/assets/server/rest/v1/schemas/site/site-update.json
"image": {
"$ref": "site#/definitions/image"
},
- "subAccountID": {
- "$ref": "common#/definitions/id"
+ "billing": {
+ "$ref": "billing#/definitions/subAccount"
}
},
"required": [

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -250,7 +250,7 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
'id', 'name', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn', 'tariffID',
'address.address1', 'address.address2', 'address.postalCode', 'address.city',
- 'address.department', 'address.region', 'address.country', 'address.coordinates'
+ 'address.department', 'address.region', 'address.country', 'address.coordinates', 'billing.id'
]
},
{

MODIFY
src/server/rest/v1/service/CompanyService.ts
src/server/rest/v1/service/CompanyService.ts
@@ -190,9 +190,9 @@ export default class CompanyService {
// If the company is assigned to a billing sub-account, check if the billing is active
if (filteredRequest.billing) {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
- Action.CREATE, Entity.COMPANY, MODULE_NAME, 'handleCreateCompany');
+ Action.CREATE, Entity.COMPANY, MODULE_NAME, 'handleUpdateCompany');
const billingSubAccount = await BillingStorage.getSubAccountByID(req.tenant, filteredRequest.billing.id);
- UtilsService.assertObjectExists(action, billingSubAccount, `Billing Sub-Account ID '${filteredRequest.billing.id}' does not exist`, MODULE_NAME, 'handleCreateCompany', req.user);
+ UtilsService.assertObjectExists(action, billingSubAccount, `Billing Sub-Account ID '${filteredRequest.billing.id}' does not exist`, MODULE_NAME, 'handleUpdateCompany', req.user);
company.billing = { id: billingSubAccount.id };
}
// Update Company

MODIFY
src/server/rest/v1/service/SiteService.ts
src/server/rest/v1/service/SiteService.ts
@@ -3,6 +3,7 @@ import { NextFunction, Request, Response } from 'express';
import AppError from '../../../../exception/AppError';
import AuthorizationService from './AuthorizationService';
+import BillingStorage from '../../../../storage/mongodb/BillingStorage';
import ChargingStationStorage from '../../../../storage/mongodb/ChargingStationStorage';
import Constants from '../../../../utils/Constants';
import DynamicAuthorizationFactory from '../../../../authorization/DynamicAuthorizationFactory';
@@ -305,6 +306,14 @@ export default class SiteService {
createdBy: { id: req.user.id },
createdOn: new Date()
} as Site;
+ // If the site is assigned to a billing sub-account, check if the billing is active
+ if (filteredRequest.billing) {
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
+ Action.CREATE, Entity.SITE, MODULE_NAME, 'handleCreateSite');
+ const billingSubAccount = await BillingStorage.getSubAccountByID(req.tenant, filteredRequest.billing.id);
+ UtilsService.assertObjectExists(action, billingSubAccount, `Billing Sub-Account ID '${filteredRequest.billing.id}' does not exist`, MODULE_NAME, 'handleCreateSite', req.user);
+ site.billing = { id: billingSubAccount.id };
+ }
// Save
site.id = await SiteStorage.saveSite(req.tenant, site, Utils.objectHasProperty(filteredRequest, 'image'));
await Logging.logInfo({
@@ -369,6 +378,14 @@ export default class SiteService {
}
site.lastChangedBy = { 'id': req.user.id };
site.lastChangedOn = new Date();
+ // If the site is assigned to a billing sub-account, check if the billing is active
+ if (filteredRequest.billing) {
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
+ Action.CREATE, Entity.SITE, MODULE_NAME, 'handleUpdateSite');
+ const billingSubAccount = await BillingStorage.getSubAccountByID(req.tenant, filteredRequest.billing.id);
+ UtilsService.assertObjectExists(action, billingSubAccount, `Billing Sub-Account ID '${filteredRequest.billing.id}' does not exist`, MODULE_NAME, 'handleUpdateSite', req.user);
+ site.billing = { id: billingSubAccount.id };
+ }
// Save
await SiteStorage.saveSite(req.tenant, site, Utils.objectHasProperty(filteredRequest, 'image'));
// Update all refs

MODIFY
src/storage/mongodb/SiteStorage.ts
src/storage/mongodb/SiteStorage.ts
@@ -271,6 +271,11 @@ export default class SiteStorage {
(coordinate) => Utils.convertToFloat(coordinate)) : [],
};
}
+ if (siteToSave.billing) {
+ siteMDB.billing = {
+ id: DatabaseUtils.convertToObjectID(siteToSave.billing.id)
+ };
+ }
// Add Last Changed/Created props
DatabaseUtils.addLastChangedCreatedProps(siteMDB, siteToSave);
// Modify and return the modified document

MODIFY
src/types/Site.ts
src/types/Site.ts
import Address from './Address';
+import { BillingAccountSetting } from './Billing';
import Company from './Company';
import ConnectorStats from './ConnectorStats';
import CreatedUpdatedProps from './CreatedUpdatedProps';
@@ -22,7 +23,7 @@ export default interface Site extends CreatedUpdatedProps, SiteAuthorizationActi
public?: boolean;
openingTimes?: OpeningTimes;
tariffID?: string;
- billingSubAccountID?: string;
+ billing?: BillingAccountSetting;
}
export interface SiteUser {

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -11,8 +11,8 @@ import Constants from '../../src/utils/Constants';
import ContextDefinition from './context/ContextDefinition';
import ContextProvider from './context/ContextProvider';
import Factory from '../factories/Factory';
-import { HTTPError } from '../../src/types/HTTPError';
import MongoDBStorage from '../../src/storage/mongodb/MongoDBStorage';
+import SiteFactory from '../factories/SiteFactory';
import { StatusCodes } from 'http-status-codes';
import StripeTestHelper from './StripeTestHelper';
import TestConstants from './client/utils/TestConstants';
@@ -1049,6 +1049,57 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
companyResponse = await billingTestHelper.userService.companyApi.readById(companyID);
expect(companyResponse.data.billing.id).to.eq(subAccountResponse.data.id);
});
+
+ it('should create a site assigned to a sub-account', async () => {
+ const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
+ userID: billingTestHelper.userContext.id
+ });
+ expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
+
+ // Create a company
+ const companyResponse = await billingTestHelper.userService.companyApi.create(CompanyFactory.build());
+ expect(companyResponse.status).to.be.eq(StatusCodes.CREATED);
+ // Create a site
+ let siteResponse = await billingTestHelper.userService.siteApi.create({
+ ...SiteFactory.build(),
+ companyID: companyResponse.data.id,
+ billing: {
+ id: subAccountResponse.data.id
+ }
+ });
+ expect(siteResponse.status).to.be.eq(StatusCodes.OK);
+ siteResponse = await billingTestHelper.userService.siteApi.readById(siteResponse.data.id);
+ expect(siteResponse.data.billing.id).to.eq(subAccountResponse.data.id);
+ });
+
+ it('should update a site to assign a sub-account', async () => {
+ const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
+ userID: billingTestHelper.userContext.id
+ });
+ expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
+
+ // Create a company
+ const companyResponse = await billingTestHelper.userService.companyApi.create(CompanyFactory.build());
+ expect(companyResponse.status).to.be.eq(StatusCodes.CREATED);
+ // Create a site
+ let siteResponse = await billingTestHelper.userService.siteApi.create({
+ ...SiteFactory.build(),
+ companyID: companyResponse.data.id,
+ });
+ expect(siteResponse.status).to.be.eq(StatusCodes.OK);
+ const siteID = siteResponse.data.id;
+
+ siteResponse = await billingTestHelper.userService.siteApi.update({
+ id: siteID,
+ ...SiteFactory.build(),
+ companyID: companyResponse.data.id,
+ billing: { id: subAccountResponse.data.id }
+ });
+ expect(siteResponse.status).to.be.eq(StatusCodes.OK);
+
+ siteResponse = await billingTestHelper.userService.siteApi.readById(siteID);
+ expect(siteResponse.data.billing.id).to.eq(subAccountResponse.data.id);
+ });
});
});

feat(site): assign to sub-account