UNPKG

@accounter/server

Version:
309 lines (303 loc) • 15.1 kB
import { GraphQLError } from 'graphql'; import { type Injector } from 'graphql-modules'; import type { Resolvers } from '../../../__generated__/types.js'; import type { Currency } from '../../../shared/enums.js'; import { 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 { ChargesProvider } from '../../charges/providers/charges.provider.js'; import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js'; import { SalariesProvider } from '../providers/salaries.provider.js'; import type { SalariesModule } from '../types.js'; import { insertSalaryRecordsFromFile } from './insert-salary-records-from-file.resolvers.js'; import { insertSalaryRecords } from './insert-salary.resolver.js'; import { updateSalaryRecord } from './update-salary.resolver.js'; // Resolve a salary record's local currency from its OWNING business so // multi-business reads format each record correctly; falls back to the // request's primary business currency when the per-business value is absent. async function salaryLocalCurrency( injector: Injector, ownerId: string | null | undefined, ): Promise<Currency> { if (ownerId) { const currency = await injector .get(ScopeProvider) .getBusinessPreference(ownerId, 'defaultLocalCurrency'); if (currency) { return currency; } } const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); return defaultLocalCurrency; } export const salariesResolvers: SalariesModule.Resolvers & Pick<Resolvers, 'UpdateSalaryRecordResult' | 'InsertSalaryRecordsResult'> = { Query: { salaryRecordsByCharge: async (_, { chargeId }, { injector }) => { const salaryRecords = await injector .get(SalariesProvider) .getSalaryRecordsByChargeIdLoader.load(chargeId); return salaryRecords; }, salaryRecordsByDates: async (_, { fromDate, toDate, employeeIDs }, { injector }) => { const fromDateMonth = fromDate.slice(0, 7); const toDateMonth = toDate.slice(0, 7); try { const salaryRecords = await injector .get(SalariesProvider) .getSalaryRecordsByDates({ fromDate: fromDateMonth, toDate: toDateMonth }); return salaryRecords .filter(record => (employeeIDs ? employeeIDs.includes(record.employee_id) : true)) .sort((a, b) => a.month.localeCompare(b.month)); } catch (e) { throw new GraphQLError(`Failed to get salary records by dates: ${(e as Error).message}`); } }, }, Mutation: { insertSalaryRecords, updateSalaryRecord, insertOrUpdateSalaryRecords: async (_, params, { injector }) => { try { const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext(); const recordsPromises = params.salaryRecords.map(async salaryRecord => { try { const res = await injector.get(SalariesProvider).updateSalaryRecord(salaryRecord); if (res.length !== 1) { throw new Error('Failed to update salary record'); } return res[0]; } catch (e) { const message = `Failed to update salary record of employee ID=${salaryRecord.employeeId} on month ${salaryRecord.month}`; console.error(`${message}: ${e}`); } try { const salaryRecordToUpdate = { ...salaryRecord, ownerId, employee: salaryRecord.employee ?? null, addedVacationDays: salaryRecord.addedVacationDays ?? null, baseSalary: salaryRecord.baseSalary ?? null, bonus: salaryRecord.bonus ?? null, chargeId: salaryRecord.chargeId ?? null, compensationsEmployerAmount: salaryRecord.compensationsEmployerAmount ?? null, compensationsEmployerPercentage: salaryRecord.compensationsEmployerPercentage ?? null, gift: salaryRecord.gift ?? null, globalAdditionalHours: salaryRecord.globalAdditionalHours ?? null, healthPaymentAmount: salaryRecord.healthPaymentAmount ?? null, hourlyRate: salaryRecord.hourlyRate ?? null, hours: salaryRecord.hours ?? null, jobPercentage: salaryRecord.jobPercentage ?? null, pensionEmployeeAmount: salaryRecord.pensionEmployeeAmount ?? null, pensionEmployeePercentage: salaryRecord.pensionEmployeePercentage ?? null, pensionEmployerAmount: salaryRecord.pensionEmployerAmount ?? null, pensionEmployerPercentage: salaryRecord.pensionEmployerPercentage ?? null, pensionFundId: salaryRecord.pensionFundId ?? null, recovery: salaryRecord.recovery ?? null, sicknessDaysBalance: salaryRecord.sicknessDaysBalance ?? null, socialSecurityAmountEmployee: salaryRecord.socialSecurityAmountEmployee ?? null, socialSecurityAmountEmployer: salaryRecord.socialSecurityAmountEmployer ?? null, taxAmount: salaryRecord.taxAmount ?? null, trainingFundEmployeeAmount: salaryRecord.trainingFundEmployeeAmount ?? null, trainingFundEmployeePercentage: salaryRecord.trainingFundEmployeePercentage ?? null, trainingFundEmployerAmount: salaryRecord.trainingFundEmployerAmount ?? null, trainingFundEmployerPercentage: salaryRecord.trainingFundEmployerPercentage ?? null, trainingFundId: salaryRecord.trainingFundId ?? null, travelAndSubsistence: salaryRecord.travelAndSubsistence ?? null, vacationDaysBalance: salaryRecord.vacationDaysBalance ?? null, vacationTakeout: salaryRecord.vacationTakeout ?? null, workDays: salaryRecord.workDays ?? null, zkufot: salaryRecord.zkufot ?? null, }; const res = await injector .get(SalariesProvider) .insertSalaryRecords({ salaryRecords: [salaryRecordToUpdate] }); if (res.length !== 1) { throw new Error('Failed to insert salary record'); } return res[0]; } catch (e) { const message = `Failed to insert salary record of employee ID=${salaryRecord.employeeId} on month ${salaryRecord.month}`; console.error(`${message}: ${e}`); } throw new Error( `Failed to update salary record of employee ID=${salaryRecord.employeeId} on month ${salaryRecord.month}`, ); }); const updatedRecords = await Promise.all(recordsPromises); return { salaryRecords: updatedRecords, }; } catch (e) { return { __typename: 'CommonError', message: (e as Error)?.message ?? (e as { errors: Error[] })?.errors.map(e => e.message).toString() ?? 'Unknown error', }; } }, insertSalaryRecordsFromFile, }, UpdateSalaryRecordResult: { __resolveType: obj => { if ('__typename' in obj && obj.__typename === 'CommonError') return 'CommonError'; return 'UpdateSalaryRecordSuccessfulResult'; }, }, InsertSalaryRecordsResult: { __resolveType: obj => { if ('__typename' in obj && obj.__typename === 'CommonError') return 'CommonError'; return 'InsertSalaryRecordsSuccessfulResult'; }, }, SalaryCharge: { salaryRecords: (DbCharge, _, { injector }) => { return injector.get(SalariesProvider).getSalaryRecordsByChargeIdLoader.load(DbCharge.id); }, employees: async (DbCharge, _, { injector }) => { const salaryRecords = await injector .get(SalariesProvider) .getSalaryRecordsByChargeIdLoader.load(DbCharge.id); return injector .get(BusinessesProvider) .getBusinessByIdLoader.loadMany(salaryRecords.map(salaryRecord => salaryRecord.employee_id)) .then( res => res.map(employee => { if (!employee || employee instanceof Error) { throw new GraphQLError( `Employee not found for one of the salary record of charge ${DbCharge.id}`, ); } return employee; }) ?? [], ); }, }, Salary: { directAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.direct_payment_amount, defaultLocalCurrency); }, baseAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return DbSalary.base_salary ? formatFinancialAmount(DbSalary.base_salary, defaultLocalCurrency) : null; }, employee: (DbSalary, _, { injector }) => injector .get(BusinessesProvider) .getBusinessByIdLoader.load(DbSalary.employee_id) .then(res => res ?? null), employer: (DbSalary, _, { injector }) => injector .get(BusinessesProvider) .getBusinessByIdLoader.load(DbSalary.employer) .then(res => res ?? null), pensionFund: (DbSalary, _, { injector }) => DbSalary.pension_fund_id ? injector .get(BusinessesProvider) .getBusinessByIdLoader.load(DbSalary.pension_fund_id) .then(res => res ?? null) : null, pensionEmployeeAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.pension_employee_amount, defaultLocalCurrency); }, pensionEmployeePercentage: DbSalary => DbSalary.pension_employee_percentage, pensionEmployerAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.pension_employer_amount, defaultLocalCurrency); }, pensionEmployerPercentage: DbSalary => DbSalary.pension_employer_percentage, compensationsAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.compensations_employer_amount, defaultLocalCurrency); }, compensationsPercentage: DbSalary => DbSalary.compensations_employer_percentage, trainingFund: (DbSalary, _, { injector }) => DbSalary.training_fund_id ? injector .get(BusinessesProvider) .getBusinessByIdLoader.load(DbSalary.training_fund_id) .then(res => res ?? null) : null, trainingFundEmployeeAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.training_fund_employee_amount, defaultLocalCurrency); }, trainingFundEmployeePercentage: DbSalary => DbSalary.training_fund_employee_percentage, trainingFundEmployerAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.training_fund_employer_amount, defaultLocalCurrency); }, trainingFundEmployerPercentage: DbSalary => DbSalary.training_fund_employer_percentage, socialSecurityEmployeeAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.social_security_amount_employee, defaultLocalCurrency); }, socialSecurityEmployerAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.social_security_amount_employer, defaultLocalCurrency); }, incomeTaxAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.tax_amount, defaultLocalCurrency); }, healthInsuranceAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.health_payment_amount, defaultLocalCurrency); }, globalAdditionalHoursAmount: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.global_additional_hours, defaultLocalCurrency); }, bonus: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.bonus, defaultLocalCurrency); }, gift: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.gift, defaultLocalCurrency); }, travelAndSubsistence: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.travel_and_subsistence, defaultLocalCurrency); }, recovery: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.recovery, defaultLocalCurrency); }, vacationTakeout: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.vacation_takeout, defaultLocalCurrency); }, notionalExpense: async (DbSalary, _, { injector }) => { const defaultLocalCurrency = await salaryLocalCurrency(injector, DbSalary.owner_id); return formatFinancialAmount(DbSalary.zkufot, defaultLocalCurrency); }, vacationDays: DbSalary => ({ added: DbSalary.added_vacation_days ? Number(DbSalary.added_vacation_days) : null, balance: DbSalary.vacation_days_balance ? Number(DbSalary.vacation_days_balance) : null, }), workDays: DbSalary => (DbSalary.work_days ? Number(DbSalary.work_days) : null), sicknessDays: DbSalary => ({ balance: DbSalary.sickness_days_balance ? Number(DbSalary.sickness_days_balance) : null, }), charge: (DbSalary, _, { injector }) => { if (!DbSalary.charge_id) { return null; } return injector .get(ChargesProvider) .getChargeByIdLoader.load(DbSalary.charge_id) .then(res => res ?? null); }, }, };