import { prisma as db } from "@repo/db/client"; export interface EligiblePaymentRow { id: number; claimNumber: string | null; patientName: string; serviceDate: Date | null; mhPaidAmount: number; copayment: number; collectionAmount: number; paymentDate: Date; isOcr: boolean; } export interface CommissionBatchRecord { id: number; npiProviderId: number; totalCollection: number; commissionAmount: number; notes: string | null; createdAt: Date; providerName: string; itemCount: number; } export const commissionsStorage = { /** * Eligible payments for commission — two sources: * 1. Claims owned by this provider (Claim.npiProviderId) whose linked payment * has actual collection and has not been commissioned yet. * 2. OCR / PDF-import payments that have no linked claim but carry the * provider on Payment.npiProviderId directly. */ async getEligiblePayments( npiProviderId: number, from?: Date | null, to?: Date | null ): Promise { const dateFilter: any = {}; if (from) dateFilter.gte = from; if (to) { const toEnd = new Date(to); toEnd.setUTCHours(23, 59, 59, 999); dateFilter.lte = toEnd; } const hasDateFilter = Object.keys(dateFilter).length > 0; // ── 1. Claim-centric: provider lives on the Claim ────────────────────── const claimWhere: any = { npiProviderId, payment: { is: { commissionBatchItems: { none: {} }, OR: [{ mhPaidAmount: { gt: 0 } }, { copayment: { gt: 0 } }], ...(hasDateFilter ? { createdAt: dateFilter } : {}), }, }, }; const claims = await db.claim.findMany({ where: claimWhere, orderBy: { id: "desc" }, include: { payment: { select: { id: true, mhPaidAmount: true, copayment: true, createdAt: true, notes: true }, }, }, }); const rows: EligiblePaymentRow[] = claims .filter((c) => c.payment !== null) .map((c) => { const p = c.payment!; const mh = Number(p.mhPaidAmount ?? 0); const co = Number(p.copayment ?? 0); return { id: p.id, claimNumber: c.claimNumber ?? null, patientName: c.patientName, serviceDate: c.serviceDate, mhPaidAmount: mh, copayment: co, collectionAmount: mh + co, paymentDate: p.createdAt, isOcr: false, }; }); // ── 2. OCR / PDF payments — no claim, matched by Payment.npiProviderId ── const ocrWhere: any = { npiProviderId, claimId: null, commissionBatchItems: { none: {} }, OR: [{ mhPaidAmount: { gt: 0 } }, { copayment: { gt: 0 } }], ...(hasDateFilter ? { createdAt: dateFilter } : {}), }; const ocrPayments = await db.payment.findMany({ where: ocrWhere, orderBy: { createdAt: "desc" }, include: { patient: { select: { firstName: true, lastName: true } }, }, }); for (const p of ocrPayments) { const mh = Number(p.mhPaidAmount ?? 0); const co = Number(p.copayment ?? 0); const patientName = `${p.patient?.firstName ?? ""} ${p.patient?.lastName ?? ""}`.trim() || "Unknown"; rows.push({ id: p.id, claimNumber: null, patientName, serviceDate: null, mhPaidAmount: mh, copayment: co, collectionAmount: mh + co, paymentDate: p.createdAt, isOcr: true, }); } // Sort combined list by most recent first rows.sort((a, b) => b.paymentDate.getTime() - a.paymentDate.getTime()); return rows; }, /** Create a commission batch, linking the selected payments */ async createCommissionBatch(data: { npiProviderId: number; paymentIds: number[]; totalCollection: number; commissionAmount: number; notes?: string; }) { // Verify none of the payments are already commissioned const alreadyDone = await db.commissionBatchItem.count({ where: { paymentId: { in: data.paymentIds } }, }); if (alreadyDone > 0) { throw new Error("One or more selected payments have already been commissioned."); } // Calculate collection amounts per payment const payments = await db.payment.findMany({ where: { id: { in: data.paymentIds } }, select: { id: true, mhPaidAmount: true, copayment: true }, }); const batch = await db.commissionBatch.create({ data: { npiProviderId: data.npiProviderId, totalCollection: data.totalCollection, commissionAmount: data.commissionAmount, notes: data.notes ?? null, items: { create: payments.map((p) => ({ paymentId: p.id, collectionAmount: Number(p.mhPaidAmount ?? 0) + Number(p.copayment ?? 0), })), }, }, include: { items: true, npiProvider: true }, }); return batch; }, /** Past commission batches for a provider */ async getCommissionBatches(npiProviderId?: number): Promise { const batches = await db.commissionBatch.findMany({ where: npiProviderId ? { npiProviderId } : undefined, orderBy: { createdAt: "desc" }, include: { npiProvider: { select: { providerName: true } }, _count: { select: { items: true } }, }, }); return batches.map((b) => ({ id: b.id, npiProviderId: b.npiProviderId, totalCollection: Number(b.totalCollection), commissionAmount: Number(b.commissionAmount), notes: b.notes, createdAt: b.createdAt, providerName: b.npiProvider.providerName, itemCount: b._count.items, })); }, };