UNPKG

@accounter/server

Version:
167 lines (150 loc) 5.73 kB
import { Injector } from 'graphql-modules'; import type { ResolversTypes } from '../../../__generated__/types.js'; import { ChargeTypeEnum, MissingChargeInfo } from '../../../shared/enums.js'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { isInvoice } from '../../documents/helpers/common.helper.js'; import { validateDocumentAllocation } from '../../documents/helpers/validate-document.helper.js'; import { DocumentsProvider } from '../../documents/providers/documents.provider.js'; import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js'; import { ChargeTagsProvider } from '../../tags/providers/charge-tags.provider.js'; import type { IGetChargesByIdsResult } from '../types.js'; import { getChargeType } from './charge-type.js'; import { getChargeBusinesses, getChargeDocumentsMeta, getChargeTaxCategoryId, getChargeTransactionsMeta, } from './common.helper.js'; export const validateCharge = async ( charge: IGetChargesByIdsResult, injector: Injector, ): Promise<ResolversTypes['ValidationData']> => { const missingInfo: Array<MissingChargeInfo> = []; const [chargeType, { mainBusinessId }, taxCategoryId] = await Promise.all([ getChargeType(charge, injector), getChargeBusinesses(charge.id, injector), getChargeTaxCategoryId(charge.id, injector), ]); const adminContext = await injector.get(AdminContextProvider).getVerifiedAdminContext(); const isGeneralFees = taxCategoryId === adminContext.general.taxCategories.generalFeeTaxCategoryId; // check for consistent counterparty business const businessNotRequired = [ChargeTypeEnum.InternalTransfer, ChargeTypeEnum.Salary, ChargeTypeEnum.Financial].includes( chargeType, ) || isGeneralFees; const businessPromise = mainBusinessId && !businessNotRequired ? injector.get(BusinessesProvider).getBusinessByIdLoader.load(mainBusinessId) : Promise.resolve(undefined); const [ business, { invalidTransactions, transactionsCount }, { documentsVatAmount, invoiceCount, receiptCount, invalidDocuments }, ] = await Promise.all([ businessPromise, getChargeTransactionsMeta(charge.id, injector), getChargeDocumentsMeta(charge.id, injector), ]); const businessIsFine = businessNotRequired || !!business; if (!businessIsFine) { missingInfo.push(MissingChargeInfo.Counterparty); } // validate documents let shouldHaveDocuments = true; switch (chargeType) { case ChargeTypeEnum.Salary: case ChargeTypeEnum.InternalTransfer: case ChargeTypeEnum.Dividend: case ChargeTypeEnum.Conversion: case ChargeTypeEnum.MonthlyVat: case ChargeTypeEnum.CreditcardBankCharge: case ChargeTypeEnum.Financial: case ChargeTypeEnum.BusinessTrip: case ChargeTypeEnum.BankDeposit: shouldHaveDocuments = false; } if (isGeneralFees || business?.no_invoices_required === true || charge.documents_optional_flag) { shouldHaveDocuments = false; } let documentsAreFine: boolean; if (shouldHaveDocuments) { const documents = await injector .get(DocumentsProvider) .getDocumentsByChargeIdLoader.load(charge.id); let missingAllocationNumber = false; await Promise.all( documents.map(async doc => { if (isInvoice(doc.type)) { const validAllocation = await validateDocumentAllocation(doc, injector); if (!validAllocation) { missingAllocationNumber = true; } } }), ); const isReceiptEnough = !!(business?.can_settle_with_receipt && receiptCount > 0); const dbDocumentsAreValid = !invalidDocuments; documentsAreFine = dbDocumentsAreValid && (invoiceCount > 0 || isReceiptEnough) && !missingAllocationNumber; } else { documentsAreFine = true; } if (!documentsAreFine) { missingInfo.push(MissingChargeInfo.Documents); } // validate transactions const hasTransaction = transactionsCount >= 1; const transactionsNotRequired = [ChargeTypeEnum.Financial].includes(chargeType); const transactionsAreFine = transactionsNotRequired || (hasTransaction && !invalidTransactions); if (!transactionsAreFine) { missingInfo.push(MissingChargeInfo.Transactions); } // validate description const descriptionIsFine = (charge.user_description?.trim().length ?? 0) > 0; if (!descriptionIsFine) { missingInfo.push(MissingChargeInfo.Description); } // validate tags const tags = await injector.get(ChargeTagsProvider).getTagsByChargeIDLoader.load(charge.id); const tagsAreFine = tags.length > 0; if (!tagsAreFine) { missingInfo.push(MissingChargeInfo.Tags); } // validate vat const isVATlessBusiness = charge.optional_vat || (business && (business.country !== adminContext.locality || business.exempt_dealer || business.optional_vat)); const vatIsFine = !shouldHaveDocuments || isGeneralFees || (documentsVatAmount != null && (isVATlessBusiness || documentsVatAmount !== 0)); if (!vatIsFine) { missingInfo.push(MissingChargeInfo.Vat); } // validate tax category const shouldHaveTaxCategory = ![ChargeTypeEnum.Salary, ChargeTypeEnum.InternalTransfer].includes( chargeType, ); const taxCategoryIsFine = !shouldHaveTaxCategory || !!taxCategoryId; if (!taxCategoryIsFine) { missingInfo.push(MissingChargeInfo.TaxCategory); } //TODO(Gil): validate balance //TODO(Gil): validate ledger const allFine = documentsAreFine && businessIsFine && descriptionIsFine && tagsAreFine && vatIsFine && transactionsAreFine && taxCategoryIsFine; return { isValid: allFine, missingInfo, }; };