@accounter/server
Version:
185 lines • 9.01 kB
JavaScript
import { differenceInDays } from 'date-fns';
import { GraphQLError } from 'graphql';
import { BusinessTripsProvider } from '../../business-trips/providers/business-trips.provider.js';
import { businessTripSummary } from '../../business-trips/resolvers/business-trip-summary.resolver.js';
import { CorporateTaxesProvider } from '../../corporate-taxes/providers/corporate-taxes.provider.js';
import { DepreciationCategoriesProvider } from '../../depreciation/providers/depreciation-categories.provider.js';
import { DepreciationProvider } from '../../depreciation/providers/depreciation.provider.js';
import { amountByFinancialEntityIdValidation, amountBySortCodeValidation, updateRecords, } from './profit-and-loss.helper.js';
export async function calculateTaxAmounts(context, year, decoratedLedgerRecords, yearlyResearchAndDevelopmentExpensesAmount, researchAndDevelopmentExpensesForTax, profitBeforeTaxAmount) {
const { injector, adminContext: { general: { taxCategories: { fineTaxCategoryId, untaxableGiftsTaxCategoryId, salaryExcessExpensesTaxCategoryId, }, }, }, } = context;
const taxRateVariablesPromise = injector
.get(CorporateTaxesProvider)
.getCorporateTaxesByDateLoader.load(`${year}-01-01`);
const businessTripsPromise = injector
.get(BusinessTripsProvider)
.getBusinessTripsByDates({
fromDate: `${year}-01-01`,
toDate: `${year}-12-31`,
})
.then(async (businessTrips) => await Promise.all(businessTrips.map(trip => businessTripSummary(context, trip))));
const [taxRateVariables, businessTrips] = await Promise.all([
taxRateVariablesPromise,
businessTripsPromise,
]);
if (!taxRateVariables) {
throw new GraphQLError('No tax rate for year');
}
let finesAmount = 0;
const finesRecords = new Map();
let untaxableGiftsAmount = 0;
const untaxableGiftsRecords = new Map();
decoratedLedgerRecords.map(record => {
if (record.credit_entity1 === untaxableGiftsTaxCategoryId) {
const amount = -Number(record.credit_local_amount1);
updateRecords(untaxableGiftsRecords, amount, record.credit_entity_sort_code1, record.credit_entity1);
untaxableGiftsAmount += amount;
}
if (record.credit_entity2 === untaxableGiftsTaxCategoryId) {
const amount = -Number(record.credit_local_amount2);
updateRecords(untaxableGiftsRecords, amount, record.credit_entity_sort_code2, record.credit_entity2);
untaxableGiftsAmount += amount;
}
if (record.debit_entity1 === untaxableGiftsTaxCategoryId) {
const amount = Number(record.debit_local_amount1);
updateRecords(untaxableGiftsRecords, amount, record.debit_entity_sort_code1, record.debit_entity1);
untaxableGiftsAmount += amount;
}
if (record.debit_entity2 === untaxableGiftsTaxCategoryId) {
const amount = Number(record.debit_local_amount2);
updateRecords(untaxableGiftsRecords, amount, record.debit_entity_sort_code2, record.debit_entity2);
untaxableGiftsAmount += amount;
}
if (record.credit_entity1 === fineTaxCategoryId) {
const amount = -Number(record.credit_local_amount1);
updateRecords(finesRecords, amount, record.credit_entity_sort_code1, record.credit_entity1);
finesAmount += amount;
}
if (record.credit_entity2 === fineTaxCategoryId) {
const amount = -Number(record.credit_local_amount2);
updateRecords(finesRecords, amount, record.credit_entity_sort_code2, record.credit_entity2);
finesAmount += amount;
}
if (record.debit_entity1 === fineTaxCategoryId) {
const amount = Number(record.debit_local_amount1);
updateRecords(finesRecords, amount, record.debit_entity_sort_code1, record.debit_entity1);
finesAmount += amount;
}
if (record.debit_entity2 === fineTaxCategoryId) {
const amount = Number(record.debit_local_amount2);
updateRecords(finesRecords, amount, record.debit_entity_sort_code2, record.debit_entity2);
finesAmount += amount;
}
});
const untaxableGifts = {
amount: untaxableGiftsAmount,
records: Array.from(untaxableGiftsRecords.entries()).map(([sortCode, data]) => ({
sortCode,
amount: data.amount,
records: Array.from(data.records.entries()).map(([financialEntityId, amount]) => ({
financialEntityId,
amount,
})),
})),
};
const fines = {
amount: finesAmount,
records: Array.from(finesRecords.entries()).map(([sortCode, data]) => ({
sortCode,
amount: data.amount,
records: Array.from(data.records.entries()).map(([financialEntityId, amount]) => ({
financialEntityId,
amount,
})),
})),
};
let businessTripsExcessExpensesAmount = 0;
businessTrips.map(summary => {
const amount = summary.rows.find(row => row.type === 'TOTAL')?.excessExpenditure?.raw ?? 0;
businessTripsExcessExpensesAmount += amount;
});
const salaryExcessExpensesAmount = amountByFinancialEntityIdValidation(decoratedLedgerRecords, financialEntityId => financialEntityId === salaryExcessExpensesTaxCategoryId, true);
const reserves = amountBySortCodeValidation(decoratedLedgerRecords, sortCode => sortCode === 931, true);
const taxableIncomeAmount = profitBeforeTaxAmount +
yearlyResearchAndDevelopmentExpensesAmount +
fines.amount +
untaxableGifts.amount +
businessTripsExcessExpensesAmount +
salaryExcessExpensesAmount +
reserves.amount +
researchAndDevelopmentExpensesForTax;
const taxRate = Number(taxRateVariables.tax_rate) / 100;
const annualTaxExpenseAmount = taxableIncomeAmount * taxRate;
return {
researchAndDevelopmentExpensesForTax,
fines,
untaxableGifts,
businessTripsExcessExpensesAmount,
salaryExcessExpensesAmount,
reserves,
taxableIncomeAmount,
taxRate,
annualTaxExpenseAmount,
};
}
export async function calculateDepreciationAmount(injector, year) {
const yearBeginning = new Date(year, 0, 1);
const yearEnd = new Date(year, 11, 31);
const depreciationRecords = await injector
.get(DepreciationProvider)
.getDepreciationRecordsByDates({
fromDate: yearBeginning,
toDate: yearEnd,
});
let rndDepreciationYearlyAmount = 0;
let gnmDepreciationYearlyAmount = 0;
let marketingDepreciationYearlyAmount = 0;
await Promise.all(depreciationRecords.map(async (record) => {
if (!record.expiration_date) {
console.error('No expiration date for depreciation record', record);
return;
}
const depreciationCategory = await injector
.get(DepreciationCategoriesProvider)
.getDepreciationCategoriesByIdLoader.load(record.category);
if (!depreciationCategory) {
console.error('No depreciation category for depreciation record', record);
return;
}
const yearDays = differenceInDays(yearEnd, yearBeginning) + 1;
let daysOfRelevance = yearDays;
if (record.activation_date.getTime() > yearBeginning.getTime()) {
daysOfRelevance -= differenceInDays(record.activation_date, yearBeginning);
}
if (record.expiration_date.getTime() < yearEnd.getTime()) {
daysOfRelevance -= differenceInDays(yearEnd, record.expiration_date);
}
if (daysOfRelevance <= 0) {
console.error('No days of relevance for depreciation record', record);
return;
}
const part = daysOfRelevance / yearDays;
const yearlyPercent = Number(depreciationCategory.percentage) / 100;
switch (record.type) {
case 'RESEARCH_AND_DEVELOPMENT':
rndDepreciationYearlyAmount += Number(record.amount) * part * yearlyPercent;
break;
case 'GENERAL_AND_MANAGEMENT':
gnmDepreciationYearlyAmount += Number(record.amount) * part * yearlyPercent;
break;
case 'MARKETING':
marketingDepreciationYearlyAmount += Number(record.amount) * part * yearlyPercent;
break;
default:
throw new GraphQLError(`Unknown depreciation record type ${record.type}`);
}
}));
const totalDepreciationYearlyAmount = rndDepreciationYearlyAmount + gnmDepreciationYearlyAmount + marketingDepreciationYearlyAmount;
return {
rndDepreciationYearlyAmount,
gnmDepreciationYearlyAmount,
marketingDepreciationYearlyAmount,
totalDepreciationYearlyAmount,
};
}
//# sourceMappingURL=tax.helper.js.map