Move the lock outside the implementation, don't get the tenant in BillingService but get it from the token
MODIFY
src/integration/billing/BillingIntegration.ts
src/integration/billing/BillingIntegration.ts
import BackendError from '../../exception/BackendError';
-import LockingManager from '../../locking/LockingManager';
import BillingStorage from '../../storage/mongodb/BillingStorage';
import SettingStorage from '../../storage/mongodb/SettingStorage';
import UserStorage from '../../storage/mongodb/UserStorage';
import { BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingTax, BillingUser, BillingUserSynchronizeAction } from '../../types/Billing';
-import { ActionsResponse } from '../../types/GlobalType';
import { UserInErrorType } from '../../types/InError';
-import { LockEntity } from '../../types/Locking';
import { ServerAction } from '../../types/Server';
import { BillingSetting } from '../../types/Setting';
import Transaction from '../../types/Transaction';
@@ -27,8 +24,6 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
}
public async synchronizeUsers(tenantID: string): Promise<BillingUserSynchronizeAction> {
- const billingLock = LockingManager.createExclusiveLock(this.tenantID, LockEntity.USER, 'synchronize-billing-users');
- if (await LockingManager.acquire(billingLock)) {
await this.checkConnection();
// Check
const actionsDone: BillingUserSynchronizeAction = {
@@ -55,7 +50,7 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
for (const user of newUsersToSyncInBilling.result) {
// Synchronize user
try {
- await this.synchronizeUser(user, tenantID);
+ await this.synchronizeUser(tenantID, user);
Logging.logInfo({
tenantID: tenantID,
actionOnUser: user,
@@ -123,7 +118,7 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
}
// Synchronize several times the user in case of fail before setting it in error
try {
- await this.synchronizeUser(user, tenantID);
+ await this.synchronizeUser(tenantID, user);
Logging.logInfo({
tenantID: tenantID,
actionOnUser: user,
@@ -146,7 +141,6 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
}
}
}
-
// Log
Utils.logActionsResponse(tenantID, ServerAction.BILLING_SYNCHRONIZE_USERS,
MODULE_NAME, 'synchronizeUsers', actionsDone,
@@ -159,13 +153,11 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
const billingSettings = await SettingStorage.getBillingSettings(tenantID);
billingSettings.stripe.usersLastSynchronizedOn = new Date();
await SettingStorage.saveBillingSettings(tenantID, billingSettings);
- await LockingManager.release(billingLock);
// Result
return actionsDone;
}
- }
- public async synchronizeUser(user: User, tenantID: string): Promise<void> {
+ public async synchronizeUser(tenantID: string, user: User): Promise<void> {
try {
const exists = await this.userExists(user);
let newUser: BillingUser;
@@ -214,9 +206,7 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
}
}
- public async forceSynchronizeUser(user: User, tenantID: string): Promise<void> {
- const billingLock = LockingManager.createExclusiveLock(this.tenantID, LockEntity.USER, 'force-synchronize-billing-users');
- if (await LockingManager.acquire(billingLock)) {
+ public async forceSynchronizeUser(tenantID: string, user: User): Promise<void> {
let billingUser: BillingUser;
try {
billingUser = await this.getUserByEmail(user.email);
@@ -242,7 +232,6 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
message: `Successfully forced the synchronization of the user '${user.email}'`,
});
} catch (error) {
- await LockingManager.release(billingLock);
throw new BackendError({
source: Constants.CENTRAL_SERVER,
module: MODULE_NAME, method: 'forceSynchronizeUser',
@@ -252,140 +241,9 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
detailedMessages: { error: error.message, stack: error.stack }
});
}
-
- // Synchronize user's invoices
- try {
- await this.synchronizeInvoices(this.tenantID, user);
- } catch (error) {
- throw new BackendError({
- source: Constants.CENTRAL_SERVER,
- module: MODULE_NAME, method: 'forceSynchronizeUser',
- action: ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- actionOnUser: user,
- message: `Cannot force synchronize invoices for user '${user.email}'`,
- detailedMessages: { error: error.message, stack: error.stack }
- });
- } finally {
- await LockingManager.release(billingLock);
- }
- }
- }
-
- public async forceSynchronizeUserInvoices(tenantID: string, user: User): Promise<ActionsResponse> {
- const billingLock = LockingManager.createExclusiveLock(this.tenantID, LockEntity.USER, 'force-synchronize-billing-invoices');
- if (await LockingManager.acquire(billingLock)) {
- let billingUser: BillingUser = null;
- const actionsDone: ActionsResponse = {
- inSuccess: 0,
- inError: 0
- };
- await this.checkConnection();
- // Check billing user
- billingUser = await this.checkAndGetBillingUser(user);
- // Get all user invoices from Billing application
- // TODO: retrieve all the invoices 100 by 100
- const invoiceIDsInBilling = await this.getUpdatedInvoiceIDsInBilling(billingUser);
- if (invoiceIDsInBilling && invoiceIDsInBilling.length > 0) {
- Logging.logInfo({
- tenantID: tenantID,
- user: user,
- source: Constants.CENTRAL_SERVER,
- action: ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- module: MODULE_NAME, method: 'forceSynchronizeUserInvoices',
- message: `${invoiceIDsInBilling.length} billing invoice(s) are going to be synchronized with e-Mobility invoices`
- });
- for (const invoiceIDInBilling of invoiceIDsInBilling) {
- try {
- // Get billing invoice
- const invoiceInBilling = await this.getInvoice(invoiceIDInBilling);
- // Get e-Mobility invoice
- const invoice = await BillingStorage.getInvoiceByBillingInvoiceID(tenantID, invoiceIDInBilling);
- if (!invoiceInBilling && !invoice) {
- actionsDone.inError++;
- Logging.logError({
- tenantID: tenantID,
- user: user,
- source: Constants.CENTRAL_SERVER,
- action: ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- module: MODULE_NAME, method: 'forceSynchronizeUserInvoices',
- message: `Billing invoice with ID '${invoiceIDInBilling}' does not exist anymore`
- });
- continue;
- }
- // Delete in e-Mobility
- if (!invoiceInBilling && invoice) {
- await BillingStorage.deleteInvoiceByInvoiceID(tenantID, invoiceIDInBilling);
- Logging.logDebug({
- tenantID: tenantID,
- user: user,
- source: Constants.CENTRAL_SERVER,
- action: ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- module: MODULE_NAME, method: 'forceSynchronizeUserInvoices',
- message: `Billing invoice with ID '${invoiceIDInBilling}' has been deleted in e-Mobility`
- });
- actionsDone.inSuccess++;
- continue;
- }
- // Create / Update
- let userInInvoice: User;
- if (invoice) {
- // If invoice already exists, set back its e-Mobility ID before saving
- invoiceInBilling.id = invoice.id;
- } else {
- // Associate e-Mobility user to invoice according to invoice customer ID
- if (user) {
- // Can only be the invoice of the user
- userInInvoice = user;
- } else {
- // Get user
- userInInvoice = await UserStorage.getUserByBillingID(tenantID, invoiceInBilling.customerID);
- }
- Object.assign(invoiceInBilling, { user: user });
- }
- await BillingStorage.saveInvoice(tenantID, invoiceInBilling);
- Logging.logDebug({
- tenantID: tenantID,
- user: user,
- source: Constants.CENTRAL_SERVER,
- action: ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- module: MODULE_NAME, method: 'forceSynchronizeUserInvoices',
- message: `Billing invoice with ID '${invoiceIDInBilling}' does not exist`
- });
- if (userInInvoice) {
- userInInvoice.billingData.invoicesLastSynchronizedOn = new Date();
- await UserStorage.saveUserBillingData(tenantID, userInInvoice.id, userInInvoice.billingData);
- }
- actionsDone.inSuccess++;
- } catch (error) {
- actionsDone.inError++;
- Logging.logError({
- tenantID: tenantID,
- user: user,
- source: Constants.CENTRAL_SERVER,
- action: ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- module: MODULE_NAME, method: 'forceSynchronizeUserInvoices',
- message: `Unable to process the invoice with ID '${invoiceIDInBilling}'`,
- detailedMessages: { error: error.message, stack: error.stack, invoiceIDInBilling }
- });
- }
- }
- }
- // Log
- Utils.logActionsResponse(tenantID, ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES,
- MODULE_NAME, 'forceSynchronizeUserInvoices', actionsDone,
- '{{inSuccess}} invoice(s) were successfully synchronized',
- '{{inError}} invoice(s) failed to be synchronized',
- '{{inSuccess}} invoice(s) were successfully synchronized and {{inError}} failed to be synchronized',
- 'All the invoices are up to date'
- );
- await LockingManager.release(billingLock);
- return actionsDone;
- }
}
public async synchronizeInvoices(tenantID: string, user?: User): Promise<BillingUserSynchronizeAction> {
- const billingLock = LockingManager.createExclusiveLock(this.tenantID, LockEntity.USER, 'synchronize-billing-invoices');
- if (await LockingManager.acquire(billingLock)) {
let billingUser: BillingUser = null;
const actionsDone: BillingUserSynchronizeAction = {
inSuccess: 0,
@@ -516,10 +374,8 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
billingSettings.stripe.invoicesLastSynchronizedOn = new Date();
await SettingStorage.saveBillingSettings(tenantID, billingSettings);
}
- await LockingManager.release(billingLock);
return actionsDone;
}
- }
public async sendInvoiceToUser(invoice: BillingInvoice): Promise<BillingInvoice> {
// Save generated pdf

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
-import { BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingInvoiceStatus, BillingMethod, BillingStatus, BillingTax, BillingUser } from '../../../types/Billing';
+import axios from 'axios';
+import moment from 'moment';
import Stripe, { IResourceObject } from 'stripe';
-
import BackendError from '../../../exception/BackendError';
-import BillingIntegration from '../BillingIntegration';
import BillingStorage from '../../../storage/mongodb/BillingStorage';
-import Constants from '../../../utils/Constants';
-import Cypher from '../../../utils/Cypher';
-import I18nManager from '../../../utils/I18nManager';
-import Logging from '../../../utils/Logging';
+import UserStorage from '../../../storage/mongodb/UserStorage';
+import { BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingInvoiceStatus, BillingMethod, BillingStatus, BillingTax, BillingUser } from '../../../types/Billing';
+import { DocumentEncoding, DocumentType } from '../../../types/GlobalType';
import { ServerAction } from '../../../types/Server';
import { StripeBillingSetting } from '../../../types/Setting';
import Transaction from '../../../types/Transaction';
-import { DocumentEncoding, DocumentType } from '../../../types/GlobalType';
import User from '../../../types/User';
-import UserStorage from '../../../storage/mongodb/UserStorage';
+import Constants from '../../../utils/Constants';
+import Cypher from '../../../utils/Cypher';
+import I18nManager from '../../../utils/I18nManager';
+import Logging from '../../../utils/Logging';
import Utils from '../../../utils/Utils';
-import axios from 'axios';
-import moment from 'moment';
+import BillingIntegration from '../BillingIntegration';
+
import ICustomerListOptions = Stripe.customers.ICustomerListOptions;
import ItaxRateSearchOptions = Stripe.taxRates.ItaxRateSearchOptions;

MODIFY
src/locking/LockingHelper.ts
src/locking/LockingHelper.ts
@@ -4,11 +4,27 @@ import LockingManager from './LockingManager';
import SiteArea from '../types/SiteArea';
export default class LockingHelper {
- public static async createAndAquireExclusiveLockForSiteArea(tenantID: string, siteArea: SiteArea): Promise<Lock | null> {
- const siteAreaLock = LockingManager.createExclusiveLock(tenantID, LockEntity.SITE_AREA, siteArea.id);
- if (!(await LockingManager.acquire(siteAreaLock))) {
+ public static async createSiteAreaLock(tenantID: string, siteArea: SiteArea): Promise<Lock|null> {
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.SITE_AREA, siteArea.id);
+ if (!(await LockingManager.acquire(lock))) {
return null;
}
- return siteAreaLock;
+ return lock;
+ }
+
+ public static async createBillingSyncUsersLock(tenantID: string): Promise<Lock|null> {
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.USER, 'synchronize-billing-users');
+ if (!(await LockingManager.acquire(lock))) {
+ return null;
+ }
+ return lock;
+ }
+
+ public static async createBillingSyncInvoicesLock(tenantID: string): Promise<Lock|null> {
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.INVOICE, 'synchronize-billing-invoices');
+ if (!(await LockingManager.acquire(lock))) {
+ return null;
+ }
+ return lock;
}
}

MODIFY
src/scheduler/tasks/CheckAndComputeSmartChargingTask.ts
src/scheduler/tasks/CheckAndComputeSmartChargingTask.ts
@@ -22,7 +22,7 @@ export default class CheckAndComputeSmartChargingTask extends SchedulerTask {
Constants.DB_PARAMS_MAX_LIMIT);
// Get Site Area
for (const siteArea of siteAreas.result) {
- const siteAreaLock = await LockingHelper.createAndAquireExclusiveLockForSiteArea(tenant.id, siteArea);
+ const siteAreaLock = await LockingHelper.createSiteAreaLock(tenant.id, siteArea);
if (siteAreaLock) {
try {
// Get implementation

MODIFY
src/scheduler/tasks/SynchronizeBillingInvoicesTask.ts
src/scheduler/tasks/SynchronizeBillingInvoicesTask.ts
import BillingFactory from '../../integration/billing/BillingFactory';
-import { LockEntity } from '../../types/Locking';
+import LockingHelper from '../../locking/LockingHelper';
import LockingManager from '../../locking/LockingManager';
-import Logging from '../../utils/Logging';
import NotificationHandler from '../../notification/NotificationHandler';
-import SchedulerTask from '../SchedulerTask';
import { ServerAction } from '../../types/Server';
import { TaskConfig } from '../../types/TaskConfig';
import Tenant from '../../types/Tenant';
+import Logging from '../../utils/Logging';
import Utils from '../../utils/Utils';
+import SchedulerTask from '../SchedulerTask';
export default class SynchronizeBillingInvoicesTask extends SchedulerTask {
async processTenant(tenant: Tenant, config: TaskConfig): Promise<void> {
// Get the lock
- const billingLock = LockingManager.createExclusiveLock(tenant.id, LockEntity.INVOICE, 'synchronize-billing');
- if (await LockingManager.acquire(billingLock)) {
+ const billingLock = await LockingHelper.createBillingSyncInvoicesLock(tenant.id);
+ if (billingLock) {
try {
const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
if (billingImpl) {

MODIFY
src/scheduler/tasks/SynchronizeBillingUsersTask.ts
src/scheduler/tasks/SynchronizeBillingUsersTask.ts
import BillingFactory from '../../integration/billing/BillingFactory';
-import { LockEntity } from '../../types/Locking';
+import LockingHelper from '../../locking/LockingHelper';
import LockingManager from '../../locking/LockingManager';
-import Logging from '../../utils/Logging';
import NotificationHandler from '../../notification/NotificationHandler';
-import SchedulerTask from '../SchedulerTask';
import { ServerAction } from '../../types/Server';
import { TaskConfig } from '../../types/TaskConfig';
import Tenant from '../../types/Tenant';
+import Logging from '../../utils/Logging';
import Utils from '../../utils/Utils';
+import SchedulerTask from '../SchedulerTask';
export default class SynchronizeBillingUsersTask extends SchedulerTask {
async processTenant(tenant: Tenant, config: TaskConfig): Promise<void> {
// Get the lock
- const billingLock = LockingManager.createExclusiveLock(tenant.id, LockEntity.USER, 'synchronize-billing');
- if (await LockingManager.acquire(billingLock)) {
+ const billingLock = await LockingHelper.createBillingSyncUsersLock(tenant.id);
+ if (billingLock) {
try {
const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
if (billingImpl) {

MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -1737,7 +1737,7 @@ export default class OCPPService {
// Get Site Area
const siteArea = await SiteAreaStorage.getSiteArea(tenantID, chargingStation.siteAreaID);
if (siteArea && siteArea.smartCharging) {
- const siteAreaLock = await LockingHelper.createAndAquireExclusiveLockForSiteArea(tenantID, siteArea);
+ const siteAreaLock = await LockingHelper.createSiteAreaLock(tenantID, siteArea);
if (siteAreaLock) {
try {
const smartCharging = await SmartChargingFactory.getSmartChargingImpl(tenantID);

MODIFY
src/server/rest/service/BillingService.ts
src/server/rest/service/BillingService.ts
-import { Action, Entity } from '../../../types/Authorization';
-import { HTTPAuthError, HTTPError } from '../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
-
+import fs from 'fs';
+import Authorizations from '../../../authorization/Authorizations';
import AppAuthError from '../../../exception/AppAuthError';
import AppError from '../../../exception/AppError';
-import Authorizations from '../../../authorization/Authorizations';
import BillingFactory from '../../../integration/billing/BillingFactory';
-import { BillingInvoiceStatus } from '../../../types/Billing';
-import BillingSecurity from './security/BillingSecurity';
+import LockingHelper from '../../../locking/LockingHelper';
+import LockingManager from '../../../locking/LockingManager';
import BillingStorage from '../../../storage/mongodb/BillingStorage';
-import Constants from '../../../utils/Constants';
-import Logging from '../../../utils/Logging';
+import UserStorage from '../../../storage/mongodb/UserStorage';
+import { Action, Entity } from '../../../types/Authorization';
+import { BillingInvoiceStatus, BillingUserSynchronizeAction } from '../../../types/Billing';
+import { HTTPAuthError, HTTPError } from '../../../types/HTTPError';
import { ServerAction } from '../../../types/Server';
import TenantComponents from '../../../types/TenantComponents';
-import TenantStorage from '../../../storage/mongodb/TenantStorage';
import User from '../../../types/User';
-import UserStorage from '../../../storage/mongodb/UserStorage';
+import Constants from '../../../utils/Constants';
+import Logging from '../../../utils/Logging';
+import BillingSecurity from './security/BillingSecurity';
import UtilsService from './UtilsService';
-import fs from 'fs';
+
const MODULE_NAME = 'BillingService';
@@ -78,8 +79,7 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.SYNCHRONIZE_USERS, Entity.BILLING, MODULE_NAME, 'handleSynchronizeUsers');
- const tenant = await TenantStorage.getTenant(req.user.tenantID);
- const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
+ const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (!billingImpl) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -90,8 +90,18 @@ export default class BillingService {
user: req.user
});
}
+ // Get the lock
+ let synchronizeAction: BillingUserSynchronizeAction;
+ const billingLock = await LockingHelper.createBillingSyncUsersLock(req.user.tenantID);
+ if (billingLock) {
+ try {
// Sync users
- const synchronizeAction = await billingImpl.synchronizeUsers(tenant.id);
+ synchronizeAction = await billingImpl.synchronizeUsers(req.user.tenantID);
+ } finally {
+ // Release the lock
+ await LockingManager.release(billingLock);
+ }
+ }
// Ok
res.json(Object.assign(synchronizeAction, Constants.REST_RESPONSE_SUCCESS));
next();
@@ -110,8 +120,7 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.SYNCHRONIZE_USERS, Entity.BILLING, MODULE_NAME, 'handleSynchronizeUser');
- const tenant = await TenantStorage.getTenant(req.user.tenantID);
- const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
+ const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (!billingImpl) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -123,11 +132,20 @@ export default class BillingService {
});
}
// Get user
- const userToSynchronize = await UserStorage.getUser(tenant.id, filteredRequest.id);
+ const userToSynchronize = await UserStorage.getUser(req.user.tenantID, filteredRequest.id);
UtilsService.assertObjectExists(action, userToSynchronize, `User '${filteredRequest.id}' does not exist`,
MODULE_NAME, 'handleSynchronizeUser', req.user);
+ // Get the lock
+ const billingLock = await LockingHelper.createBillingSyncUsersLock(req.user.tenantID);
+ if (billingLock) {
+ try {
// Sync user
- await billingImpl.synchronizeUser(userToSynchronize, tenant.id);
+ await billingImpl.synchronizeUser(req.user.tenantID, userToSynchronize);
+ } finally {
+ // Release the lock
+ await LockingManager.release(billingLock);
+ }
+ }
// Ok
res.json(Constants.REST_RESPONSE_SUCCESS);
next();
@@ -146,8 +164,7 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.SYNCHRONIZE_USER, Entity.BILLING, MODULE_NAME, 'handleForceSynchronizeUser');
- const tenant = await TenantStorage.getTenant(req.user.tenantID);
- const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
+ const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (!billingImpl) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -159,11 +176,29 @@ export default class BillingService {
});
}
// Get user
- const userToSynchronize = await UserStorage.getUser(tenant.id, filteredRequest.id);
- UtilsService.assertObjectExists(action, userToSynchronize, `User '${filteredRequest.id}' does not exist`,
+ const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.id);
+ UtilsService.assertObjectExists(action, user, `User '${filteredRequest.id}' does not exist`,
MODULE_NAME, 'handleSynchronizeUser', req.user);
+ // Get the User lock
+ let billingLock = await LockingHelper.createBillingSyncUsersLock(req.user.tenantID);
+ if (billingLock) {
+ try {
// Sync user
- await billingImpl.forceSynchronizeUser(userToSynchronize, tenant.id);
+ await billingImpl.forceSynchronizeUser(req.user.tenantID, user);
+ } finally {
+ await LockingManager.release(billingLock);
+ }
+ }
+ // Get the Invoice lock
+ billingLock = await LockingHelper.createBillingSyncInvoicesLock(req.user.tenantID);
+ if (billingLock) {
+ try {
+ // Sync invoices
+ await billingImpl.synchronizeInvoices(req.user.tenantID, user);
+ } finally {
+ await LockingManager.release(billingLock);
+ }
+ }
// Ok
res.json(Constants.REST_RESPONSE_SUCCESS);
next();
@@ -181,9 +216,8 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.LIST, Entity.TAXES, MODULE_NAME, 'handleGetBillingTaxes');
- const tenant = await TenantStorage.getTenant(req.user.tenantID);
// Get Billing implementation from factory
- const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
+ const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (!billingImpl) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -269,8 +303,7 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.SYNCHRONIZE_INVOICES, Entity.BILLING, MODULE_NAME, 'handleSynchronizeInvoices');
- const tenant = await TenantStorage.getTenant(req.user.tenantID);
- const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
+ const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (!billingImpl) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -289,8 +322,17 @@ export default class BillingService {
UtilsService.assertObjectExists(action, user, `User '${req.user.id}' does not exist`,
MODULE_NAME, 'handleSynchronizeUserInvoices', req.user);
}
+ // Get the Invoice lock
+ let synchronizeAction: BillingUserSynchronizeAction;
+ const billingLock = await LockingHelper.createBillingSyncInvoicesLock(req.user.tenantID);
+ if (billingLock) {
+ try {
// Sync invoices
- const synchronizeAction = await billingImpl.synchronizeInvoices(req.user.tenantID, user);
+ await billingImpl.synchronizeInvoices(req.user.tenantID, user);
+ } finally {
+ await LockingManager.release(billingLock);
+ }
+ }
// Ok
res.json(Object.assign(synchronizeAction, Constants.REST_RESPONSE_SUCCESS));
next();
@@ -308,8 +350,7 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.SYNCHRONIZE_INVOICES, Entity.BILLING, MODULE_NAME, 'handleForceSynchronizeUserInvoices');
- const tenant = await TenantStorage.getTenant(req.user.tenantID);
- const billingImpl = await BillingFactory.getBillingImpl(tenant.id);
+ const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (!billingImpl) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -325,8 +366,17 @@ export default class BillingService {
const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID);
UtilsService.assertObjectExists(action, user, `User '${filteredRequest.userID}' does not exist`,
MODULE_NAME, 'handleForceSynchronizeUserInvoices', req.user);
- // Sync user invoices
- const synchronizeAction = await billingImpl.forceSynchronizeUserInvoices(req.user.tenantID, user);
+ // Get the Invoice lock
+ let synchronizeAction: BillingUserSynchronizeAction;
+ const billingLock = await LockingHelper.createBillingSyncInvoicesLock(req.user.tenantID);
+ if (billingLock) {
+ try {
+ // Sync invoices
+ await billingImpl.synchronizeInvoices(req.user.tenantID, user);
+ } finally {
+ await LockingManager.release(billingLock);
+ }
+ }
// Ok
res.json(Object.assign(synchronizeAction, Constants.REST_RESPONSE_SUCCESS));
next();

MODIFY
src/server/rest/service/ChargingStationService.ts
src/server/rest/service/ChargingStationService.ts
@@ -382,7 +382,7 @@ export default class ChargingStationService {
user: req.user
});
}
- const siteAreaLock = await LockingHelper.createAndAquireExclusiveLockForSiteArea(req.user.tenantID, siteArea);
+ const siteAreaLock = await LockingHelper.createSiteAreaLock(req.user.tenantID, siteArea);
if (siteAreaLock) {
try {
// Call

MODIFY
src/server/rest/service/SiteAreaService.ts
src/server/rest/service/SiteAreaService.ts
@@ -479,7 +479,7 @@ export default class SiteAreaService {
if (filteredRequest.smartCharging) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises, no-undef
setTimeout(async () => {
- const siteAreaLock = await LockingHelper.createAndAquireExclusiveLockForSiteArea(req.user.tenantID, siteArea);
+ const siteAreaLock = await LockingHelper.createSiteAreaLock(req.user.tenantID, siteArea);
if (siteAreaLock) {
try {
const smartCharging = await SmartChargingFactory.getSmartChargingImpl(req.user.tenantID);

MODIFY
src/types/Locking.ts
src/types/Locking.ts
@@ -22,5 +22,5 @@ export enum LockEntity {
LOGGING = 'logging',
TRANSACTION = 'transaction',
CAR = 'car',
- INVOICE = 'invoice'
+ INVOICE = 'invoice',
}

MODIFY
test/api/LockingTest.ts
test/api/LockingTest.ts
@@ -100,7 +100,7 @@ describe('Locking Tests', function() {
// Get the Site Area
const siteArea = testData.siteAreaContext.getSiteArea();
// Create and Aquire lock
- testData.siteExclusiveLock = await LockingHelper.createAndAquireExclusiveLockForSiteArea(
+ testData.siteExclusiveLock = await LockingHelper.createSiteAreaLock(
testData.tenantContext.getTenant().id, siteArea);
expect(testData.siteExclusiveLock).not.null;
expect(testData.siteExclusiveLock.id).not.null;

MODIFY
test/api/context/BillingContext.ts
test/api/context/BillingContext.ts
@@ -61,8 +61,8 @@ export default class BillingContext {
const adminUser: User = this.tenantContext.getUserContext(BillingContext.USERS[0]);
const basicUser: User = this.tenantContext.getUserContext(BillingContext.USERS[1]);
- await billingImpl.synchronizeUser(adminUser, this.tenantContext.getTenant().id);
- await billingImpl.synchronizeUser(basicUser, this.tenantContext.getTenant().id);
+ await billingImpl.synchronizeUser(this.tenantContext.getTenant().id, adminUser);
+ await billingImpl.synchronizeUser(this.tenantContext.getTenant().id, basicUser);
const adminBillingUser = await billingImpl.getUserByEmail(adminUser.email);
const basicBillingUser = await billingImpl.getUserByEmail(basicUser.email);

Move the lock outside the implementation, don't get the tenant in BillingService but get it from the token