@accounter/server
Version:
Accounter GraphQL server
148 lines (132 loc) • 4.82 kB
text/typescript
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { TimelessDateString } from '../../../shared/types/index.js';
import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js';
import { AuthContextProvider } from '../../auth/providers/auth-context.provider.js';
import type {
IDeleteCorporateTaxParams,
IDeleteCorporateTaxQuery,
IGetCorporateTaxesByCorporateIdsQuery,
IGetCorporateTaxesByCorporateIdsResult,
IInsertCorporateTaxParams,
IInsertCorporateTaxQuery,
IUpdateCorporateTaxParams,
IUpdateCorporateTaxQuery,
} from '../types.js';
const getCorporateTaxesByCorporateIds = sql<IGetCorporateTaxesByCorporateIdsQuery>`
SELECT *
FROM accounter_schema.corporate_tax_variables
WHERE corporate_id IN $$corporateIds
ORDER BY date DESC;`;
const updateCorporateTax = sql<IUpdateCorporateTaxQuery>`
UPDATE accounter_schema.corporate_tax_variables
SET
date = COALESCE(
$date,
date
),
tax_rate = COALESCE(
$taxRate,
tax_rate
),
original_tax_rate = COALESCE(
$originalTaxRate,
original_tax_rate
)
WHERE
corporate_id = $corporateId
AND date = $originalDate
RETURNING *;`;
const insertCorporateTax = sql<IInsertCorporateTaxQuery>`
INSERT INTO accounter_schema.corporate_tax_variables (corporate_id, date, tax_rate, original_tax_rate, owner_id)
VALUES ($corporateId, $date, $taxRate, $originalTaxRate, $ownerId)
RETURNING *`;
const deleteCorporateTax = sql<IDeleteCorporateTaxQuery>`
DELETE FROM accounter_schema.corporate_tax_variables
WHERE corporate_id = $corporateId
AND date = $date
RETURNING *`;
({
scope: Scope.Operation,
global: true,
})
export class CorporateTaxesProvider {
private businessIdCache: string | null = null;
constructor(
private db: TenantAwareDBClient,
private authContextProvider: AuthContextProvider,
) {}
private async getBusinessId(): Promise<string> {
if (this.businessIdCache !== null) {
return this.businessIdCache;
}
const authContext = await this.authContextProvider.getAuthContext();
this.businessIdCache = authContext?.tenant.businessId ?? null;
if (!this.businessIdCache) {
throw new Error('Missing businessId in AuthContext');
}
return this.businessIdCache;
}
private allCorporateTaxesCache = new Map<
string,
Promise<IGetCorporateTaxesByCorporateIdsResult[]>
>();
public getAllCorporateTaxes(corporateId: string) {
if (this.allCorporateTaxesCache.has(corporateId)) {
return this.allCorporateTaxesCache.get(corporateId)!;
}
this.allCorporateTaxesCache.set(
corporateId,
getCorporateTaxesByCorporateIds.run({ corporateIds: [corporateId] }, this.db),
);
return this.allCorporateTaxesCache.get(corporateId)!;
}
private async batchCorporateTaxesByDates(
taxRates: readonly { date: TimelessDateString; corporateId: string }[],
) {
const corporateIds = [...new Set(taxRates.map(t => t.corporateId))];
const rates = (await Promise.all(corporateIds.map(id => this.getAllCorporateTaxes(id)))).flat();
return taxRates.map(({ date, corporateId }) => {
const time = new Date(date).getTime();
return rates
.filter(rate => rate.corporate_id === corporateId)
.sort((a, b) => b.date.getTime() - a.date.getTime())
.find(rate => rate.date.getTime() <= time);
});
}
public getCorporateTaxesByDateLoader = new DataLoader(
async (dates: readonly TimelessDateString[]) => {
const businessId = await this.getBusinessId();
return this.batchCorporateTaxesByDates(
dates.map(date => ({ date, corporateId: businessId })),
);
},
);
private async batchCorporateTaxesByCorporateIds(corporateIds: readonly string[]) {
const taxes = await getCorporateTaxesByCorporateIds.run({ corporateIds }, this.db);
return corporateIds.map(id => taxes.filter(expense => expense.corporate_id === id));
}
public getCorporateTaxesByCorporateIdLoader = new DataLoader((corporateIds: readonly string[]) =>
this.batchCorporateTaxesByCorporateIds(corporateIds),
);
public updateCorporateTax(params: IUpdateCorporateTaxParams) {
this.allCorporateTaxesCache = new Map();
return updateCorporateTax.run(params, this.db);
}
public async insertCorporateTax(params: IInsertCorporateTaxParams) {
this.allCorporateTaxesCache = new Map();
const ownerId = params.ownerId ?? (await this.getBusinessId());
return insertCorporateTax.run(
{
...params,
ownerId,
},
this.db,
);
}
public deleteCorporateTax(params: IDeleteCorporateTaxParams) {
this.allCorporateTaxesCache = new Map();
return deleteCorporateTax.run(params, this.db);
}
}