chore: sync seed with overdue invoices for new signups

This commit is contained in:
kevin-asprec
2026-05-04 14:55:55 +08:00
parent 1a0b4916d0
commit d7ab370bb5

View File

@@ -250,6 +250,12 @@ async function main() {
{ first: 'Ricardo', last: 'Flores', phone: '09291234567', email: null, address: '55 Katipunan Rd, San Isidro', area: 2, plan: 2, type: 'postpaid', latOff: 0.001, lngOff: -0.002 }, { first: 'Ricardo', last: 'Flores', phone: '09291234567', email: null, address: '55 Katipunan Rd, San Isidro', area: 2, plan: 2, type: 'postpaid', latOff: 0.001, lngOff: -0.002 },
{ first: 'Teresa', last: 'Navarro', phone: '09301234567', email: 'teresa@email.com', address: '66 Makabayan St, Riverside', area: 3, plan: 1, type: 'prepaid', latOff: -0.003, lngOff: 0.001 }, { first: 'Teresa', last: 'Navarro', phone: '09301234567', email: 'teresa@email.com', address: '66 Makabayan St, Riverside', area: 3, plan: 1, type: 'prepaid', latOff: -0.003, lngOff: 0.001 },
{ first: 'Fernando', last: 'Castillo', phone: '09311234567', email: null, address: '77 Silang Blvd, Hilltop', area: 4, plan: 2, type: 'postpaid', latOff: 0.002, lngOff: -0.001 }, { first: 'Fernando', last: 'Castillo', phone: '09311234567', email: null, address: '77 Silang Blvd, Hilltop', area: 4, plan: 2, type: 'postpaid', latOff: 0.002, lngOff: -0.001 },
// New signups — pending installation (no lat/lng, pending status, open tickets)
{ first: 'Rafael', last: 'Dimaculangan', phone: '09321234567', email: null, address: '88 Burgos St, Centro', area: 0, plan: 1, type: 'postpaid', latOff: 0.0015, lngOff: -0.001 },
{ first: 'Lorna', last: 'Perez', phone: '09331234567', email: 'lorna@email.com', address: '99 Villareal St, Poblacion', area: 1, plan: 2, type: 'postpaid', latOff: -0.001, lngOff: 0.002 },
{ first: 'Danilo', last: 'Rivera', phone: '09341234567', email: null, address: '101 Kapitan St, San Isidro', area: 2, plan: 0, type: 'prepaid', latOff: 0.002, lngOff: 0.001 },
{ first: 'Grace', last: 'Sison', phone: '09351234567', email: 'grace@email.com', address: '202 Magdalena St, Riverside', area: 3, plan: 3, type: 'postpaid', latOff: -0.001, lngOff: -0.002 },
{ first: 'Allan', last: 'Vergara', phone: '09361234567', email: null, address: '303 Gomez St, Hilltop', area: 4, plan: 2, type: 'postpaid', latOff: 0.001, lngOff: 0.0015 },
]; ];
const clients: any[] = []; const clients: any[] = [];
@@ -260,6 +266,7 @@ async function main() {
const c = clientDefs[i]; const c = clientDefs[i];
const accountNumber = `C-${String(i + 1).padStart(6, '0')}`; const accountNumber = `C-${String(i + 1).padStart(6, '0')}`;
const plan = plans[c.plan]; const plan = plans[c.plan];
const isNewSignup = i >= 15; // last 5 are new signups pending installation
const client = await prisma.client.create({ const client = await prisma.client.create({
data: { data: {
@@ -271,12 +278,62 @@ async function main() {
email: c.email, email: c.email,
address: c.address, address: c.address,
areaId: areas[c.area].id, areaId: areas[c.area].id,
latitude: areaCoords[c.area][0] + c.latOff, status: isNewSignup ? 'pending' : 'active',
longitude: areaCoords[c.area][1] + c.lngOff, latitude: isNewSignup ? null : (areaCoords[c.area][0] + c.latOff),
longitude: isNewSignup ? null : (areaCoords[c.area][1] + c.lngOff),
}, },
}); });
clients.push(client); clients.push(client);
if (isNewSignup) {
// ── New signup: pending subscription + open installation ticket ──
await prisma.subscription.create({
data: {
tenantId: tenant.id,
clientId: client.id,
planId: plan.id,
type: c.type,
status: 'pending',
},
});
// Open installation ticket (alternating assigned/unassigned)
const assignedTech = i % 2 === 0 ? users.technician.id : null;
await prisma.ticket.create({
data: {
tenantId: tenant.id,
clientId: client.id,
createdById: users.tenant_admin.id,
assigneeId: assignedTech,
type: 'installation',
title: `Installation for ${c.first} ${c.last}`,
description: `New installation at ${c.address}`,
status: assignedTech ? 'in_progress' : 'open',
priority: 'high',
},
});
// Overdue invoice for new signup (installation fee / first billing)
invoiceCount++;
const overdueDate = new Date(now);
overdueDate.setDate(overdueDate.getDate() - 7);
await prisma.invoice.create({
data: {
tenantId: tenant.id,
clientId: client.id,
number: `INV-${String(invoiceCount).padStart(6, '0')}`,
amount: plan ? Number(plan.price) : 999,
balance: plan ? Number(plan.price) : 999,
status: 'overdue',
dueDate: overdueDate,
periodStart: new Date(now.getTime() - 30 * 86400000),
periodEnd: new Date(now.getTime()),
},
});
} else {
// ── Existing client: active subscription, resolved tickets, invoices ──
// Create subscription // Create subscription
const installedAt = new Date(now); const installedAt = new Date(now);
installedAt.setDate(installedAt.getDate() - (30 + Math.floor(Math.random() * 60))); // 30-90 days ago installedAt.setDate(installedAt.getDate() - (30 + Math.floor(Math.random() * 60))); // 30-90 days ago
@@ -327,8 +384,8 @@ async function main() {
}, },
}); });
// Create 2 invoices per client // Create invoices per client (3-4 per client with varied statuses)
for (let m = 0; m < 2; m++) { for (let m = 0; m < 4; m++) {
invoiceCount++; invoiceCount++;
const periodStart = new Date(activatedAt); const periodStart = new Date(activatedAt);
periodStart.setMonth(periodStart.getMonth() + m); periodStart.setMonth(periodStart.getMonth() + m);
@@ -337,26 +394,51 @@ async function main() {
const dueDate = new Date(periodStart); const dueDate = new Date(periodStart);
dueDate.setDate(dueDate.getDate() + 15); dueDate.setDate(dueDate.getDate() + 15);
const isPaid = m === 0 || Math.random() > 0.4; // First invoice always paid, second 60% chance // Determine invoice status based on month
const balance = isPaid ? 0 : Number(plan.price); let status: string;
let balance: number;
let paidAt: Date | null = null;
const amount = Number(plan.price);
if (m === 0) {
// Month 1: always paid
status = 'paid';
balance = 0;
paidAt = new Date(dueDate.getTime() - 86400000 * 3);
} else if (m === 1) {
// Month 2: overdue (unpaid, past due)
status = 'overdue';
balance = amount;
} else if (m === 2) {
// Month 3: 50% paid → partial
status = 'partial';
balance = Math.round(amount / 2);
} else {
// Month 4: upcoming (due in near future)
const futureDue = new Date(now);
futureDue.setDate(futureDue.getDate() + 3);
status = 'sent';
balance = amount;
dueDate.setTime(futureDue.getTime());
}
const invoice = await prisma.invoice.create({ const invoice = await prisma.invoice.create({
data: { data: {
tenantId: tenant.id, tenantId: tenant.id,
clientId: client.id, clientId: client.id,
number: `INV-${String(invoiceCount).padStart(6, '0')}`, number: `INV-${String(invoiceCount).padStart(6, '0')}`,
amount: plan.price, amount,
balance, balance,
status: isPaid ? 'paid' : (dueDate < now ? 'overdue' : 'sent'), status,
dueDate, dueDate,
paidAt: isPaid ? new Date(dueDate.getTime() - 86400000 * 3) : null, paidAt,
periodStart, periodStart,
periodEnd, periodEnd,
}, },
}); });
// Create payment for paid invoices // Create payment(s) for paid/partial invoices
if (isPaid) { if (status === 'paid') {
const methods = ['gcash', 'maya', 'cash', 'bank_transfer']; const methods = ['gcash', 'maya', 'cash', 'bank_transfer'];
const method = methods[Math.floor(Math.random() * methods.length)]; const method = methods[Math.floor(Math.random() * methods.length)];
const paidDate = new Date(dueDate.getTime() - 86400000 * Math.floor(Math.random() * 5)); const paidDate = new Date(dueDate.getTime() - 86400000 * Math.floor(Math.random() * 5));
@@ -367,12 +449,31 @@ async function main() {
clientId: client.id, clientId: client.id,
invoiceId: invoice.id, invoiceId: invoice.id,
collectedById: users.technician.id, collectedById: users.technician.id,
amount: plan.price, amount,
method, method,
referenceNo: method !== 'cash' ? `REF-${String(Math.floor(Math.random() * 99999)).padStart(5, '0')}` : null, referenceNo: method !== 'cash' ? `REF-${String(Math.floor(Math.random() * 99999)).padStart(5, '0')}` : null,
createdAt: paidDate, createdAt: paidDate,
}, },
}); });
} else if (status === 'partial') {
// Partial payment - half the amount
const methods = ['gcash', 'cash'];
const method = methods[Math.floor(Math.random() * methods.length)];
const paidDate = new Date(dueDate.getTime() - 86400000 * 2);
await prisma.payment.create({
data: {
tenantId: tenant.id,
clientId: client.id,
invoiceId: invoice.id,
collectedById: users.technician.id,
amount: Math.round(amount / 2),
method,
referenceNo: method !== 'cash' ? `REF-${String(Math.floor(Math.random() * 99999)).padStart(5, '0')}` : null,
createdAt: paidDate,
},
});
}
} }
} }
} }
@@ -573,14 +674,61 @@ async function main() {
}); });
console.log('Fund transfers: 2'); console.log('Fund transfers: 2');
// ─── Remittances ─────────────────────────────────────── // ─── Remittances (properly linked to payments) ──────────
await prisma.remittance.create({ // Get all payments that were for paid invoices (these are candidates for remittances)
data: { tenantId: tenant.id, collectorId: users.technician.id, confirmedById: users.tenant_admin.id, totalAmount: 8995, status: 'confirmed', confirmedAt: new Date() }, const allPayments = await prisma.payment.findMany({
where: { tenantId: tenant.id },
orderBy: { createdAt: 'asc' },
}); });
await prisma.remittance.create({
data: { tenantId: tenant.id, collectorId: users.technician.id, totalAmount: 5497, status: 'pending' }, // Split payments: first 60% → remitted (confirmed), next 20% → remitted (pending), last 20% → unremitted
const confirmedEnd = Math.floor(allPayments.length * 0.6);
const pendingEnd = Math.floor(allPayments.length * 0.8);
const confirmedPayments = allPayments.slice(0, confirmedEnd);
const pendingPayments = allPayments.slice(confirmedEnd, pendingEnd);
// remaining payments (pendingEnd onward) stay unremitted
// Create confirmed remittance
if (confirmedPayments.length > 0) {
const confirmedTotal = confirmedPayments.reduce((s, p) => s + Number(p.amount), 0);
const confirmedRemittance = await prisma.remittance.create({
data: {
tenantId: tenant.id,
collectorId: users.technician.id,
confirmedById: users.tenant_admin.id,
totalAmount: confirmedTotal,
status: 'confirmed',
submittedAt: new Date(Date.now() - 86400000 * 7),
confirmedAt: new Date(Date.now() - 86400000 * 5),
payments: {
create: confirmedPayments.map((p) => ({ paymentId: p.id })),
},
},
}); });
console.log('Remittances: 2'); console.log(`Remittance (confirmed): ₱${confirmedTotal} (${confirmedPayments.length} payments)`);
}
// Create pending remittance
if (pendingPayments.length > 0) {
const pendingTotal = pendingPayments.reduce((s, p) => s + Number(p.amount), 0);
const pendingRemittance = await prisma.remittance.create({
data: {
tenantId: tenant.id,
collectorId: users.technician.id,
totalAmount: pendingTotal,
status: 'pending',
submittedAt: new Date(Date.now() - 86400000 * 2),
payments: {
create: pendingPayments.map((p) => ({ paymentId: p.id })),
},
},
});
console.log(`Remittance (pending): ₱${pendingTotal} (${pendingPayments.length} payments)`);
}
const unremittedCount = allPayments.length - pendingEnd;
console.log(`Unremitted payments: ${unremittedCount} (available for new remittance)`);
console.log('\n✅ Seed completed successfully!'); console.log('\n✅ Seed completed successfully!');
console.log(`\n📊 Summary:`); console.log(`\n📊 Summary:`);