metallkart-erp/scripts/backfill-invoice-documents.ts
louis 4027cf0ee9 feat(client-invoices): INVOICE-DOC-SYNC sync ClientInvoice with Document
- create() wraps in $transaction, creates Document INVOICE/CLIENT alongside
- update() propagates invoiceNumber change to Document title/reference
- issue() sets Document.sentAt
- cancel() sets Document.closedAt
- delete() removes Document before deleting ClientInvoice
- Backfill script for existing invoices (idempotent)
- Downgrade @fastify/static to v7.0.4 (Fastify 4 compat)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 11:33:59 +03:00

51 lines
1.4 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
async function main() {
const prisma = new PrismaClient();
const invoicesWithoutDoc = await prisma.clientInvoice.findMany({
where: {
documents: { none: { documentType: 'INVOICE' } },
},
include: {
order: { select: { id: true, clientId: true, managerId: true } },
},
});
console.log(`Found ${invoicesWithoutDoc.length} invoices without Document.`);
let created = 0;
for (const inv of invoicesWithoutDoc) {
if (!inv.order) {
console.warn(`Invoice ${inv.id} has no order — skipping.`);
continue;
}
await prisma.document.create({
data: {
documentType: 'INVOICE',
direction: 'CLIENT',
title: `Счёт ${inv.invoiceNumber}`,
documentReference: inv.invoiceNumber,
orderId: inv.order.id,
clientId: inv.order.clientId,
clientInvoiceId: inv.id,
responsibleId: inv.order.managerId,
createdAt: inv.createdAt,
sentAt: inv.status === 'ISSUED' || inv.status === 'PAID' ? inv.updatedAt : null,
closedAt: inv.status === 'CANCELLED' ? inv.updatedAt : null,
},
});
console.log(` ✓ Invoice #${inv.id} (${inv.invoiceNumber}) → Document created`);
created++;
}
console.log(`Created ${created} Documents (backfill complete).`);
await prisma.$disconnect();
}
main().catch((err) => {
console.error(err);
process.exit(1);
});