@accounter/server
Version:
Accounter GraphQL server
163 lines • 8.64 kB
JavaScript
import { endOfYear, startOfYear } from 'date-fns';
import { GraphQLError } from 'graphql';
import { Currency } from '../../../shared/enums.js';
import { dateToTimelessDateString, formatFinancialAmount } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { ScopeProvider } from '../../auth/providers/scope.provider.js';
import { CountriesProvider } from '../../countries/providers/countries.provider.js';
import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js';
import { TaxCategoriesProvider } from '../../financial-entities/providers/tax-categories.provider.js';
import { AnnualRevenueReportProvider } from '../providers/annual-revenue-report.provider.js';
export const annualRevenueResolvers = {
Query: {
annualRevenueReport: async (_, { filters: { year, adminBusinessId } }, { injector }) => {
// Per-business report: validate an explicitly requested business against
// the read scope and use its admin context; default to the primary.
let targetAdminContext;
if (adminBusinessId) {
// Validates the id is within the read scope (throws otherwise), so the
// requested id can be used directly.
await injector.get(ScopeProvider).getReadScope([adminBusinessId]);
targetAdminContext = await injector
.get(AdminContextProvider)
.adminContextByOwnerIdLoader.load(adminBusinessId);
if (!targetAdminContext) {
throw new GraphQLError('Admin context not found for the requested business', {
extensions: { code: 'NOT_FOUND' },
});
}
}
else {
targetAdminContext = await injector.get(AdminContextProvider).getVerifiedAdminContext();
}
const ownerId = targetAdminContext.ownerId;
const incomeTaxCategories = await injector
.get(TaxCategoriesProvider)
.taxCategoriesBySortCodeLoader.load(810);
const date = new Date(year, 6, 1);
const records = await injector.get(AnnualRevenueReportProvider).getNormalizedRevenueRecords({
incomeTaxCategoriesIDs: incomeTaxCategories.map(tc => tc.id),
incomeToCollectId: targetAdminContext.crossYear.incomeToCollectTaxCategoryId,
ownerId,
fromDate: dateToTimelessDateString(startOfYear(date)),
toDate: dateToTimelessDateString(endOfYear(date)),
});
// Normalize records (client, amount local currency, amount default foreign currency)
const businessesProvider = injector.get(BusinessesProvider);
const normalizedRecords = [];
const businesses = await Promise.all(records.map(r => r.business_id ? businessesProvider.getBusinessByIdLoader.load(r.business_id) : null));
records.map((record, index) => {
if (!record.business_id)
return;
const business = businesses[index];
if (!business)
return;
const amountIls = record.amount_local ? parseFloat(record.amount_local) : 0;
const amountUsd = record.amount_usd ? parseFloat(record.amount_usd) : 0;
normalizedRecords.push({
clientId: record.business_id,
countryCode: business.country,
amountIls,
amountUsd,
record: {
id: record.id ?? '',
chargeId: record.charge_id,
businessId: record.business_id,
date: dateToTimelessDateString(record.date),
description: record.description ?? null,
reference: record.reference ?? null,
amountIls,
amountUsd,
amountOriginal: record.amount_foreign ? parseFloat(record.amount_foreign) : 0,
currency: record.currency,
},
});
});
// Group and sum by client
const clientSums = new Map();
for (const normalized of normalizedRecords) {
const existing = clientSums.get(normalized.clientId);
if (existing) {
existing.amountIls += normalized.amountIls;
existing.amountUsd += normalized.amountUsd;
existing.records.push(normalized.record);
}
else {
clientSums.set(normalized.clientId, {
countryCode: normalized.countryCode,
amountIls: normalized.amountIls,
amountUsd: normalized.amountUsd,
records: [normalized.record],
});
}
}
// Group by country
const countrySums = new Map();
for (const [clientId, data] of clientSums.entries()) {
const existing = countrySums.get(data.countryCode);
if (existing) {
existing.amountIls += data.amountIls;
existing.amountUsd += data.amountUsd;
existing.clients.push({
id: clientId,
amountIls: data.amountIls,
amountUsd: data.amountUsd,
records: data.records,
});
}
else {
countrySums.set(data.countryCode, {
amountIls: data.amountIls,
amountUsd: data.amountUsd,
clients: [
{
id: clientId,
amountIls: data.amountIls,
amountUsd: data.amountUsd,
records: data.records,
},
],
});
}
}
// Build the response
const countriesProvider = injector.get(CountriesProvider);
const countries = await Promise.all(Array.from(countrySums.entries()).map(async ([countryCode, data]) => {
const country = await countriesProvider.getCountryByCodeLoader.load(countryCode);
const clients = await Promise.all(data.clients.map(async (client) => {
const business = await businessesProvider.getBusinessByIdLoader.load(client.id);
return {
id: `annual-revenue-report-${year}-${countryCode}-${client.id}`,
name: business?.name ?? '',
revenueLocal: formatFinancialAmount(client.amountIls, Currency.Ils),
revenueDefaultForeign: formatFinancialAmount(client.amountUsd, Currency.Usd),
records: client.records.map(record => ({
id: `annual-revenue-report-${year}-${countryCode}-${client.id}-record-${record.id}`,
chargeId: record.chargeId,
date: record.date,
description: record.description,
reference: record.reference,
revenueLocal: formatFinancialAmount(record.amountIls, Currency.Ils),
revenueDefaultForeign: formatFinancialAmount(record.amountUsd, Currency.Usd),
revenueOriginal: formatFinancialAmount(record.amountOriginal, record.currency),
})),
};
}));
return {
id: `annual-revenue-report-${year}-${countryCode}`,
code: countryCode,
name: country?.name ?? countryCode,
revenueLocal: formatFinancialAmount(data.amountIls, Currency.Ils),
revenueDefaultForeign: formatFinancialAmount(data.amountUsd, Currency.Usd),
clients,
};
}));
return {
id: year.toString(),
year,
countries,
};
},
},
};
//# sourceMappingURL=annual-revenue.resover.js.map