@accounter/server
Version:
216 lines • 8.29 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { DBProvider } from '../../app-providers/db.provider.js';
import { sql } from '@pgtyped/runtime';
import { getCacheInstance } from '../../../shared/helpers/index.js';
import { BusinessesProvider } from './businesses.provider.js';
import { TaxCategoriesProvider } from './tax-categories.provider.js';
const getFinancialEntitiesByIds = sql `
SELECT *
FROM accounter_schema.financial_entities
WHERE id IN $$ids;`;
const getAllFinancialEntities = sql `
SELECT *
FROM accounter_schema.financial_entities;`;
const updateFinancialEntity = sql `
UPDATE accounter_schema.financial_entities
SET
name = COALESCE(
$name,
name
),
owner_id = COALESCE(
$ownerId,
owner_id
),
sort_code = COALESCE(
$sortCode,
sort_code
),
type = COALESCE(
$type,
type
)
WHERE
id = $financialEntityId
RETURNING *;
`;
const insertFinancialEntities = sql `
INSERT INTO accounter_schema.financial_entities (type, owner_id, name, sort_code)
VALUES $$financialEntities(type, ownerId, name, sortCode)
RETURNING *;`;
const deleteFinancialEntity = sql `
DELETE FROM accounter_schema.financial_entities
WHERE id = $financialEntityId
RETURNING id;
`;
const replaceFinancialEntities = sql `
WITH ledger_debit1 AS (
UPDATE accounter_schema.ledger_records
SET debit_entity1 = $targetEntityId
WHERE debit_entity1 = $entityIdToReplace
RETURNING id
),
ledger_debit2 AS (
UPDATE accounter_schema.ledger_records
SET debit_entity2 = $targetEntityId
WHERE debit_entity2 = $entityIdToReplace
RETURNING id
),
ledger_credit1 AS (
UPDATE accounter_schema.ledger_records
SET credit_entity1 = $targetEntityId
WHERE credit_entity1 = $entityIdToReplace
RETURNING id
),
ledger_credit2 AS (
UPDATE accounter_schema.ledger_records
SET credit_entity2 = $targetEntityId
WHERE credit_entity2 = $entityIdToReplace
RETURNING id
),
misc_expenses_creditor AS (
UPDATE accounter_schema.misc_expenses
SET creditor_id = $targetEntityId
WHERE creditor_id = $entityIdToReplace
RETURNING id
)
UPDATE accounter_schema.misc_expenses
SET debtor_id = $targetEntityId
WHERE debtor_id = $entityIdToReplace
RETURNING id;
`;
let FinancialEntitiesProvider = class FinancialEntitiesProvider {
dbProvider;
businessesProvider;
taxCategoriesProvider;
cache = getCacheInstance({
stdTTL: 60 * 5,
});
constructor(dbProvider, businessesProvider, taxCategoriesProvider) {
this.dbProvider = dbProvider;
this.businessesProvider = businessesProvider;
this.taxCategoriesProvider = taxCategoriesProvider;
}
async batchFinancialEntitiesByIds(ids) {
const financialEntities = await getFinancialEntitiesByIds.run({
ids,
}, this.dbProvider);
return ids.map(id => financialEntities.find(fe => fe.id === id));
}
getFinancialEntityByIdLoader = new DataLoader((keys) => this.batchFinancialEntitiesByIds(keys), {
cacheKeyFn: key => `financial-entity-id-${key}`,
cacheMap: this.cache,
});
getAllFinancialEntities() {
const data = this.cache.get('all-financial-entities');
if (data) {
return data;
}
return getAllFinancialEntities.run(undefined, this.dbProvider).then(data => {
this.cache.set('all-financial-entities', data);
data.map(fe => {
this.cache.set(`financial-entity-id-${fe.id}`, fe);
});
return data;
});
}
updateFinancialEntity(params) {
if (params.financialEntityId) {
this.invalidateFinancialEntityById(params.financialEntityId);
}
return updateFinancialEntity.run(params, this.dbProvider);
}
insertFinancialEntity(params) {
this.cache.delete('all-financial-entities');
return insertFinancialEntities.run({ financialEntities: [params] }, this.dbProvider);
}
async batchInsertFinancialEntities(newFinancialEntities) {
const financialEntities = await insertFinancialEntities.run({
financialEntities: newFinancialEntities,
}, this.dbProvider);
return newFinancialEntities.map(fe => financialEntities.find(f => f.name === fe.name) ?? null);
}
insertFinancialEntitiesLoader = new DataLoader((financialEntities) => this.batchInsertFinancialEntities(financialEntities), {
cache: false,
});
async deleteFinancialEntityById(financialEntityId) {
const entity = await this.getFinancialEntityByIdLoader.load(financialEntityId);
if (!entity) {
throw new Error(`Financial entity with id ${financialEntityId} not found`);
}
if (entity.id === entity.owner_id) {
throw new Error('Cannot delete owner entity');
}
this.invalidateFinancialEntityById(financialEntityId);
// remove business
const deleteBusiness = entity.type === 'business'
? this.businessesProvider.deleteBusinessById(financialEntityId)
: Promise.resolve();
// remove tax category
const deleteTaxCategory = entity.type === 'tax_category'
? this.taxCategoriesProvider.deleteTaxCategoryById(financialEntityId)
: Promise.resolve();
Promise.all([deleteBusiness, deleteTaxCategory]);
// TODO: should remove ledger, misc expenses?
// delete entity
deleteFinancialEntity.run({ financialEntityId }, this.dbProvider);
}
async replaceFinancialEntity(targetEntityId, entityIdToReplace, deleteEntity = false) {
const [entityToReplace, entity] = await Promise.all([
this.getFinancialEntityByIdLoader.load(entityIdToReplace),
this.getFinancialEntityByIdLoader.load(targetEntityId),
]);
if (!entityToReplace) {
throw new Error(`Financial entity with id ${entityIdToReplace} not found`);
}
if (!entity) {
throw new Error(`Financial entity with id ${targetEntityId} not found`);
}
if (entityToReplace.type !== entity.type) {
throw new Error('Cannot replace entities of different types');
}
if (entity.id === entity.owner_id) {
throw new Error('Cannot replace owner entity');
}
this.invalidateFinancialEntityById(entityIdToReplace);
this.invalidateFinancialEntityById(targetEntityId);
// convert ledger, misc expenses
await replaceFinancialEntities.run({ targetEntityId, entityIdToReplace }, this.dbProvider);
// convert business
const businessReplacementPromise = entity.type === 'business'
? this.businessesProvider.replaceBusiness(targetEntityId, entityIdToReplace, deleteEntity)
: Promise.resolve();
// convert tax category
const taxCategoryReplacementPromise = entity.type === 'tax_category'
? this.taxCategoriesProvider.replaceTaxCategory(targetEntityId, entityIdToReplace, deleteEntity)
: Promise.resolve();
await Promise.all([businessReplacementPromise, taxCategoryReplacementPromise]);
if (deleteEntity) {
await this.deleteFinancialEntityById(entityIdToReplace);
}
}
invalidateFinancialEntityById(financialEntityId) {
this.businessesProvider.invalidateBusinessById(financialEntityId);
this.taxCategoriesProvider.invalidateTaxCategoryById(financialEntityId);
this.cache.delete('all-financial-entities');
this.cache.delete(`financial-entity-id-${financialEntityId}`);
}
clearCache() {
this.taxCategoriesProvider.clearCache();
this.businessesProvider.clearCache();
this.cache.clear();
}
};
FinancialEntitiesProvider = __decorate([
Injectable({
scope: Scope.Operation,
global: true,
}),
__metadata("design:paramtypes", [DBProvider,
BusinessesProvider,
TaxCategoriesProvider])
], FinancialEntitiesProvider);
export { FinancialEntitiesProvider };
//# sourceMappingURL=financial-entities.provider.js.map