@accounter/server
Version:
Accounter GraphQL server
193 lines • 9.07 kB
JavaScript
import { lastDayOfMonth } from 'date-fns';
import { LedgerError } from './utils.helper.js';
function generateEntryRaw(accountId, amount, month, transactionDate, ownerId, isCreditor, chargeId, defaultLocalCurrency) {
return {
id: `${accountId}|${month}`,
invoiceDate: transactionDate,
valueDate: transactionDate,
currency: defaultLocalCurrency,
...(isCreditor ? { creditAccountID1: accountId } : { debitAccountID1: accountId }),
localCurrencyCreditAmount1: amount,
localCurrencyDebitAmount1: amount,
description: `${month} salary: ${accountId}`,
isCreditorCounterparty: false,
ownerId,
chargeId,
};
}
function getTotalPension(salaryRecord) {
const pensionEmployerAmount = Number(salaryRecord.pension_employer_amount ?? '0');
const pensionEmployeeAmount = Number(salaryRecord.pension_employee_amount ?? '0');
const compensationsEmployerAmount = Number(salaryRecord.compensations_employer_amount ?? '0');
const totalPension = pensionEmployeeAmount + pensionEmployerAmount + compensationsEmployerAmount;
return totalPension;
}
function getTotalTrainingFund(salaryRecord) {
const trainingFundEmployerAmount = Number(salaryRecord.training_fund_employer_amount ?? '0');
const trainingFundEmployeeAmount = Number(salaryRecord.training_fund_employee_amount ?? '0');
const totalTrainingFund = trainingFundEmployeeAmount + trainingFundEmployerAmount;
return totalTrainingFund;
}
export function generateEntriesFromSalaryRecords(salaryRecords, charge, adminContext) {
const { salaries: { taxDeductionsBusinessId }, authorities: { socialSecurityBusinessId }, defaultLocalCurrency, salaries, } = adminContext;
const chargeId = charge.id;
if (!salaryRecords.length) {
throw new LedgerError(`No salary records found for charge ${chargeId}`);
}
if (!taxDeductionsBusinessId) {
throw new LedgerError('Tax deductions business is not set');
}
const entries = [];
function generateEntry(chargeId, accountId, amount, month, isCreditor = true) {
return generateEntryRaw(accountId, amount, month, lastDayOfMonth(new Date(`${month}-01`)), charge.owner_id, isCreditor, chargeId, defaultLocalCurrency);
}
const amountPerBusiness = {};
const taxAmountPerMonth = {};
let month = undefined;
// expenses
let salaryExpensesAmount = 0;
let socialSecurityExpensesAmount = 0;
let pensionFundExpensesAmount = 0;
let compensationProvidentFundExpensesAmount = 0;
let trainingFundExpensesAmount = 0;
let zkufotAmount = 0;
for (const salaryRecord of salaryRecords) {
// record validations
if (!salaryRecord.base_salary) {
throw new LedgerError(`Base salary record for ${salaryRecord.month}, employee ID=${salaryRecord.employee_id} is missing amount`);
}
month ??= salaryRecord.month;
const salaryExpense = Number(salaryRecord.base_salary ?? '0') +
Number(salaryRecord.global_additional_hours ?? '0') +
Number(salaryRecord.bonus ?? '0') +
Number(salaryRecord.gift ?? '0') +
Number(salaryRecord.recovery ?? '0') +
Number(salaryRecord.vacation_takeout ?? '0') +
Number(salaryRecord.travel_and_subsistence ?? '0');
const directPayment = salaryExpense -
Number(salaryRecord.social_security_amount_employee ?? '0') -
Number(salaryRecord.health_payment_amount ?? '0') -
Number(salaryRecord.tax_amount ?? '0') -
Number(salaryRecord.pension_employee_amount ?? '0') -
Number(salaryRecord.training_fund_employee_amount ?? '0');
entries.push(generateEntry(charge.id, salaryRecord.employee_id, directPayment, salaryRecord.month));
// salary expenses handling
salaryExpensesAmount += salaryExpense;
// social security handling
const socialSecurityEmployeeAmount = Number(salaryRecord.social_security_amount_employee ?? '0');
const socialSecurityEmployerAmount = Number(salaryRecord.social_security_amount_employer ?? '0');
const HealthPaymentAmount = Number(salaryRecord.health_payment_amount ?? '0');
amountPerBusiness[socialSecurityBusinessId] ??= 0;
amountPerBusiness[socialSecurityBusinessId] +=
socialSecurityEmployeeAmount + socialSecurityEmployerAmount + HealthPaymentAmount;
socialSecurityExpensesAmount += Number(salaryRecord.social_security_amount_employer ?? '0');
// pension handling
const totalPension = getTotalPension(salaryRecord);
const pensionAccount = salaryRecord.pension_fund_id;
if (totalPension > 0) {
if (!pensionAccount) {
throw new LedgerError(`Missing pension account for charge ID=${chargeId}`);
}
amountPerBusiness[pensionAccount] ??= 0;
amountPerBusiness[pensionAccount] += totalPension;
}
pensionFundExpensesAmount += Number(salaryRecord.pension_employer_amount ?? '0');
compensationProvidentFundExpensesAmount += Number(salaryRecord.compensations_employer_amount ?? '0');
// training fund handling
const totalTrainingFund = getTotalTrainingFund(salaryRecord);
const trainingFundAccount = salaryRecord.training_fund_id;
if (totalTrainingFund > 0) {
if (!trainingFundAccount) {
throw new LedgerError(`Missing training fund account for charge ID=${chargeId}`);
}
amountPerBusiness[trainingFundAccount] ??= 0;
amountPerBusiness[trainingFundAccount] += totalTrainingFund;
}
trainingFundExpensesAmount += Number(salaryRecord.training_fund_employer_amount ?? '0');
// tax handling
taxAmountPerMonth[salaryRecord.month] ??= 0;
taxAmountPerMonth[salaryRecord.month] += Number(salaryRecord.tax_amount ?? '0');
// zkufot handling
zkufotAmount += Number(salaryRecord.zkufot ?? '0');
}
if (!month) {
throw new LedgerError(`No month found for salary charge ID=${chargeId}`);
}
// generate pension/training funds entries
for (const [businessId, amount] of Object.entries(amountPerBusiness)) {
if (amount) {
entries.push(generateEntry(charge.id, businessId, amount, month));
}
}
// generate tax entries
for (const [month, amount] of Object.entries(taxAmountPerMonth)) {
if (amount > 0) {
entries.push(generateEntry(charge.id, taxDeductionsBusinessId, amount, month));
}
}
const monthlyEntriesProto = getMonthlyExpensesEntriesProto({
salaryExpensesAmount,
socialSecurityExpensesAmount,
pensionFundExpensesAmount,
compensationProvidentFundExpensesAmount,
trainingFundExpensesAmount,
zkufotAmount,
salaries,
});
return {
entries,
monthlyEntriesProto,
month,
};
}
export function getMonthlyExpensesEntriesProto({ salaryExpensesAmount, socialSecurityExpensesAmount, pensionFundExpensesAmount, compensationProvidentFundExpensesAmount, trainingFundExpensesAmount, zkufotAmount, salaries, }) {
const { zkufotExpensesTaxCategoryId, zkufotIncomeTaxCategoryId, socialSecurityExpensesTaxCategoryId, salaryExpensesTaxCategoryId, trainingFundExpensesTaxCategoryId, pensionExpensesTaxCategoryId, compensationFundExpensesTaxCategoryId, } = salaries;
if (!zkufotExpensesTaxCategoryId ||
!zkufotIncomeTaxCategoryId ||
!socialSecurityExpensesTaxCategoryId ||
!salaryExpensesTaxCategoryId ||
!trainingFundExpensesTaxCategoryId ||
!pensionExpensesTaxCategoryId ||
!compensationFundExpensesTaxCategoryId) {
throw new LedgerError('Missing salary tax categories');
}
const monthlyEntriesProto = [
{
taxCategoryId: zkufotExpensesTaxCategoryId,
amount: zkufotAmount,
isCredit: false,
},
{
taxCategoryId: zkufotIncomeTaxCategoryId,
amount: zkufotAmount,
isCredit: true,
},
{
taxCategoryId: socialSecurityExpensesTaxCategoryId,
amount: socialSecurityExpensesAmount,
isCredit: false,
},
{
taxCategoryId: salaryExpensesTaxCategoryId,
amount: salaryExpensesAmount,
isCredit: false,
},
{
taxCategoryId: trainingFundExpensesTaxCategoryId,
amount: trainingFundExpensesAmount,
isCredit: false,
},
{
taxCategoryId: pensionExpensesTaxCategoryId,
amount: pensionFundExpensesAmount,
isCredit: false,
},
{
taxCategoryId: compensationFundExpensesTaxCategoryId,
amount: compensationProvidentFundExpensesAmount,
isCredit: false,
},
];
return monthlyEntriesProto;
}
//# sourceMappingURL=salary-charge-ledger.helper.js.map