import { err, ok } from "@tailor-platform/erp-kit/core"; import { Decimal } from "decimal.js"; import type { Transaction } from "../generated/kysely-tailordb"; import { IncomingPaymentTargetIneligibleError } from "../lib/errors.generated"; export type IncomingPaymentSettlementTargetInput = { accountReceivableDueScheduleLineId: string; }; export type IncomingPaymentIdentity = { companyId: string; customerAccountId: string; currencyId: string; }; export type IncomingPaymentSettlementTargetDocument = IncomingPaymentIdentity & { documentType: string; status: string; }; export type IncomingPaymentSettlementDocumentType = "INVOICE" | "CREDIT_MEMO"; export function isIncomingPaymentSettlementTargetEligible( document: IncomingPaymentSettlementTargetDocument, payment: IncomingPaymentIdentity, ) { return ( (document.documentType === "INVOICE" || document.documentType === "CREDIT_MEMO") && document.status === "POSTED" && document.companyId === payment.companyId && document.customerAccountId === payment.customerAccountId && document.currencyId === payment.currencyId ); } export function toSignedSettlementAmount( documentType: IncomingPaymentSettlementDocumentType, settledAmount: string, ) { const amount = new Decimal(settledAmount); return documentType === "CREDIT_MEMO" ? amount.negated() : amount; } export async function validateIncomingPaymentSettlementTargets( db: Transaction, payment: IncomingPaymentIdentity, settlements: IncomingPaymentSettlementTargetInput[], ) { if (settlements.length === 0) return ok({}); const dueScheduleLineIds = settlements.map( (settlement) => settlement.accountReceivableDueScheduleLineId, ); const targets = await db .selectFrom("AccountReceivableDueScheduleLine as dueSchedule") .innerJoin( "AccountReceivableDocument as document", "document.id", "dueSchedule.accountReceivableDocumentId", ) .select([ "dueSchedule.id as dueScheduleLineId", "document.id as accountReceivableDocumentId", "document.documentType as documentType", "document.status as status", "document.companyId as companyId", "document.customerAccountId as customerAccountId", "document.currencyId as currencyId", ]) .where("dueSchedule.id", "in", dueScheduleLineIds) .execute(); const targetByDueScheduleLineId = new Map( targets.map((target) => [target.dueScheduleLineId, target]), ); for (const dueScheduleLineId of dueScheduleLineIds) { const target = targetByDueScheduleLineId.get(dueScheduleLineId); if (!target || !isIncomingPaymentSettlementTargetEligible(target, payment)) { return err(new IncomingPaymentTargetIneligibleError(dueScheduleLineId)); } } return ok({}); }