feat(company): assign to billing sub-account
ADD
src/assets/schemas/billing/billing.json
+{
+ "$id": "billing",
+ "definitions": {
+ "subAccount": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "$ref": "common#/definitions/id"
+ }
+ }
+ }
+ }
+}

MODIFY
src/assets/server/rest/v1/schemas/company/company-create.json
src/assets/server/rest/v1/schemas/company/company-create.json
"logo": {
"$ref": "common#/definitions/logo"
},
- "billingSubAccountID": {
- "$ref": "common#/definitions/id"
+ "billing": {
+ "$ref": "billing#/definitions/subAccount"
}
},
"required": [

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

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -218,7 +218,7 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
attributes: [
'id', 'name', 'issuer', 'logo',
'address.address1', 'address.address2', 'address.postalCode', 'address.city',
- 'address.department', 'address.region', 'address.country', 'address.coordinates', 'billingSubAccountID'
+ '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
@@ -150,12 +150,12 @@ export default class CompanyService {
createdOn: new Date()
} as Company;
// If the company is assigned to a billing sub-account, check if the billing is active
- if (filteredRequest.billingSubAccountID) {
- UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
+ if (filteredRequest.billing) {
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
Action.CREATE, Entity.COMPANY, MODULE_NAME, 'handleCreateCompany');
- const billingSubAccount = await BillingStorage.getSubAccountByID(req.tenant, filteredRequest.billingSubAccountID);
- UtilsService.assertObjectExists(action, billingSubAccount, `Billing Sub-Account ID '${filteredRequest.billingSubAccountID}' does not exist`, MODULE_NAME, 'handleCreateCompany', req.user);
- newCompany.billingSubAccountID = billingSubAccount.id;
+ 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);
+ newCompany.billing = { id: billingSubAccount.id };
}
// Save
newCompany.id = await CompanyStorage.saveCompany(req.tenant, newCompany);
@@ -187,6 +187,14 @@ export default class CompanyService {
}
company.lastChangedBy = { 'id': req.user.id };
company.lastChangedOn = new Date();
+ // 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');
+ 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);
+ company.billing = { id: billingSubAccount.id };
+ }
// Update Company
await CompanyStorage.saveCompany(req.tenant, company, Utils.objectHasProperty(filteredRequest, 'logo') ? true : false);
await Logging.logInfo({

MODIFY
src/storage/mongodb/BillingStorage.ts
src/storage/mongodb/BillingStorage.ts
@@ -345,11 +345,4 @@ export default class BillingStorage {
}, Constants.DB_PARAMS_SINGLE_RECORD);
return subAccountMDB.count === 1 ? subAccountMDB.result[0] : null;
}
-
- public static async getSubAccountByID(tenant: Tenant, id: string): Promise<BillingAccount> {
- const subAccountMDB = await BillingStorage.getSubAccounts(tenant, {
- subAccountIDs: [id]
- }, Constants.DB_PARAMS_SINGLE_RECORD);
- return subAccountMDB.count === 1 ? subAccountMDB.result[0] : null;
- }
}

MODIFY
src/storage/mongodb/CompanyStorage.ts
src/storage/mongodb/CompanyStorage.ts
@@ -59,7 +59,11 @@ export default class CompanyStorage {
country: companyToSave.address.country,
coordinates: Utils.hasValidGpsCoordinates(companyToSave.address.coordinates) ? companyToSave.address.coordinates.map(
(coordinate) => Utils.convertToFloat(coordinate)) : [],
- billingSubAccountID: companyToSave.billingSubAccountID ? DatabaseUtils.convertToObjectID(companyToSave.billingSubAccountID) : null,
+ };
+ }
+ if (companyToSave.billing) {
+ companyMDB.billing = {
+ id: DatabaseUtils.convertToObjectID(companyToSave.billing.id),
};
}
// Add Last Changed/Created props

MODIFY
src/types/Billing.ts
src/types/Billing.ts
@@ -161,3 +161,7 @@ export interface BillingAccount {
pending: boolean;
}
+export interface BillingAccountSetting {
+ id: string;
+}
+

MODIFY
src/types/Company.ts
src/types/Company.ts
import Address from './Address';
import { AuthorizationActions } from './Authorization';
+import { BillingAccountSetting } from './Billing';
import CreatedUpdatedProps from './CreatedUpdatedProps';
import { OpeningTimes } from './OpeningTimes';
import Site from './Site';
@@ -13,5 +14,5 @@ export default interface Company extends CreatedUpdatedProps, AuthorizationActio
sites?: Site[];
distanceMeters?: number;
openingTimes?: OpeningTimes;
- billingSubAccountID?: string;
+ billing?: BillingAccountSetting;
}

MODIFY
src/validator/SchemaValidator.ts
src/validator/SchemaValidator.ts
@@ -35,6 +35,7 @@ export default class SchemaValidator {
private static registrationTokenSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/registration-token/registration-token.json`, 'utf8'));
private static siteAreasSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/site-area/site-area.json`, 'utf8'));
private static siteSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/site/site.json`, 'utf8'));
+ private static billingSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/billing/billing.json`, 'utf8'));
protected moduleName: string;
private readonly ajv: Ajv;
@@ -79,6 +80,7 @@ export default class SchemaValidator {
SchemaValidator.registrationTokenSchema,
SchemaValidator.siteAreasSchema,
SchemaValidator.siteSchema,
+ SchemaValidator.billingSchema
]);
}

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -1023,12 +1023,31 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
});
expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
- let companyResponse = await billingTestHelper.userService.companyApi.create(
- { ...CompanyFactory.build(), billingSubAccountID: subAccountResponse.data.id }
- );
+ let companyResponse = await billingTestHelper.userService.companyApi.create({
+ ...CompanyFactory.build(),
+ billing: {
+ id: subAccountResponse.data.id
+ }
+ });
expect(companyResponse.status).to.be.eq(StatusCodes.CREATED);
companyResponse = await billingTestHelper.userService.companyApi.readById(companyResponse.data.id);
- expect(companyResponse.data.billingSubAccountID).to.eq(subAccountResponse.data.id);
+ expect(companyResponse.data.billing.id).to.eq(subAccountResponse.data.id);
+ });
+
+ it('should update a company 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);
+
+ let companyResponse = await billingTestHelper.userService.companyApi.create(CompanyFactory.build());
+ expect(companyResponse.status).to.be.eq(StatusCodes.CREATED);
+ const companyID = companyResponse.data.id;
+ companyResponse = await billingTestHelper.userService.companyApi.update({ id: companyID, ...CompanyFactory.build(), billing: { id: subAccountResponse.data.id } });
+ expect(companyResponse.status).to.be.eq(StatusCodes.OK);
+
+ companyResponse = await billingTestHelper.userService.companyApi.readById(companyID);
+ expect(companyResponse.data.billing.id).to.eq(subAccountResponse.data.id);
});
});
});

feat(company): assign to billing sub-account