diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts index 86a5b95..0afb4d3 100644 --- a/packages/db/prisma/seed.ts +++ b/packages/db/prisma/seed.ts @@ -162,6 +162,7 @@ async function main() { tenant_admin: 'tenant_admin', manager: 'manager', technician: 'technician', + collector: 'collector', // collector user gets collector tenant role viewer: 'collector', // viewer user gets collector role for demo }; @@ -224,7 +225,7 @@ async function main() { ]); console.log(`Plans: ${plans.length}`); - // ─── Clients (20 clients across various areas/plans) ── + // ─── Clients (45 clients across various areas/plans) ── // Area center coordinates (Lipa City, Batangas area) const areaCoords: [number, number][] = [ [14.0785, 121.1760], // Barangay 1 - Centro @@ -256,7 +257,7 @@ async function main() { { 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 }, - // Additional clients for richer testing + // Additional clients (indices 20-29) { first: 'Angelo', last: 'Manalo', phone: '09371234567', email: 'angelo@email.com', address: '15 Rizal Ave, Centro', area: 0, plan: 3, type: 'postpaid', latOff: 0.0025, lngOff: -0.0015 }, { first: 'Bella', last: 'Cruz', phone: '09381234567', email: 'bella@email.com', address: '26 Mabini Ext, Poblacion', area: 1, plan: 1, type: 'postpaid', latOff: -0.0015, lngOff: 0.002 }, { first: 'Claudio', last: 'Diaz', phone: '09391234567', email: null, address: '37 Bonifacio Rd, San Isidro', area: 2, plan: 4, type: 'postpaid', latOff: 0.001, lngOff: 0.003 }, @@ -271,7 +272,7 @@ async function main() { { first: 'Linda', last: 'Madrid', phone: '09481234567', email: 'linda@email.com', address: '126 Andres St, Poblacion', area: 1, plan: 0, type: 'postpaid', latOff: -0.0015, lngOff: 0.002 }, { first: 'Mario', last: 'Ng', phone: '09491234567', email: null, address: '137 Katipunan Rd, San Isidro', area: 2, plan: 3, type: 'postpaid', latOff: 0.002, lngOff: -0.002 }, { first: 'Nancy', last: 'Ong', phone: '09501234567', email: 'nancy@email.com', address: '148 Makabayan Blvd, Riverside', area: 3, plan: 2, type: 'postpaid', latOff: -0.001, lngOff: 0.003 }, - // Additional new signups — pending installation + // New signups — pending installation (indices 30-44) { first: 'Oscar', last: 'Pineda', phone: '09511234567', email: 'oscar@email.com', address: '159 Rizal Ext, Centro', area: 0, plan: 2, type: 'postpaid', latOff: 0.001, lngOff: -0.002 }, { first: 'Patricia', last: 'Quintos', phone: '09521234567', email: 'patricia@email.com', address: '170 Mabini Rd, Poblacion', area: 1, plan: 1, type: 'postpaid', latOff: -0.002, lngOff: 0.001 }, { first: 'Quentin', last: 'Reyes Jr', phone: '09531234567', email: null, address: '181 Bonifacio Ext, San Isidro', area: 2, plan: 3, type: 'postpaid', latOff: 0.002, lngOff: 0.002 }, @@ -464,11 +465,11 @@ async function main() { }); // Create payment(s) for paid/partial invoices + const collector = i % 2 === 0 ? usersByEmail['collector@demo-isp.com'] : usersByEmail['tech@demo-isp.com']; if (status === 'paid') { const methods = ['gcash', 'maya', 'cash', 'bank_transfer']; const method = methods[Math.floor(Math.random() * methods.length)]; const paidDate = new Date(dueDate.getTime() - 86400000 * Math.floor(Math.random() * 5)); - const collector = i % 2 === 0 ? usersByEmail['collector@demo-isp.com'] : usersByEmail['tech@demo-isp.com']; await prisma.payment.create({ data: { @@ -486,7 +487,6 @@ async function main() { const methods = ['gcash', 'cash']; const method = methods[Math.floor(Math.random() * methods.length)]; const paidDate = new Date(dueDate.getTime() - 86400000 * 2); - const collector = i % 2 === 0 ? usersByEmail['collector@demo-isp.com'] : usersByEmail['tech@demo-isp.com']; await prisma.payment.create({ data: { @@ -506,7 +506,7 @@ async function main() { } console.log(`Clients: ${clients.length} (with subscriptions, tickets, invoices, payments)`); - // ─── Open support tickets ────────────────────────────── + // ─── Support tickets ─────────────────────────────────── const supportTickets = [ { clientIdx: 2, title: 'Intermittent connection drops', desc: 'Internet keeps disconnecting every 30 minutes', priority: 'high', status: 'open', type: 'support', assignee: null }, { clientIdx: 5, title: 'Slow speed during peak hours', desc: 'Speed drops to 5 Mbps from 8-10 PM', priority: 'normal', status: 'open', type: 'support', assignee: null }, @@ -772,7 +772,95 @@ async function main() { } const unremittedCount = allPayments.length - pendingEnd; - console.log(`Unremitted payments: ${unremittedCount} (available for new remittance)`); + console.log(`Unremitted payments: ${unremittedCount} (from invoice loop)`); + + // ─── Additional unremitted payments (recent, for testing) ── + const recentPaymentDefs = [ + { clientIdx: 0, amount: 699, method: 'gcash' as const, ref: 'GCASH-44221', daysAgo: 0 }, + { clientIdx: 2, amount: 999, method: 'cash' as const, ref: null, daysAgo: 0 }, + { clientIdx: 5, amount: 1499, method: 'maya' as const, ref: 'MAYA-88312', daysAgo: 1 }, + { clientIdx: 7, amount: 999, method: 'bank_transfer' as const, ref: 'BDO-10293', daysAgo: 1 }, + { clientIdx: 9, amount: 2499, method: 'gcash' as const, ref: 'GCASH-44228', daysAgo: 2 }, + { clientIdx: 10, amount: 699, method: 'cash' as const, ref: null, daysAgo: 2 }, + { clientIdx: 14, amount: 1499, method: 'gcash' as const, ref: 'GCASH-44235', daysAgo: 3 }, + { clientIdx: 3, amount: 999, method: 'maya' as const, ref: 'MAYA-88319', daysAgo: 4 }, + ]; + + // Find or create overdue invoices for these clients to attach payments to + for (const rp of recentPaymentDefs) { + const client = clients[rp.clientIdx]; + const paidDate = new Date(); + paidDate.setDate(paidDate.getDate() - rp.daysAgo); + + // Find an existing overdue or partial invoice for this client + let invoice = await prisma.invoice.findFirst({ + where: { tenantId: tenant.id, clientId: client.id, status: { in: ['overdue', 'partial'] } }, + }); + + // If no overdue invoice, create one + if (!invoice) { + invoiceCount++; + const dueDate = new Date(); + dueDate.setDate(dueDate.getDate() - 5); + invoice = await prisma.invoice.create({ + data: { + tenantId: tenant.id, + clientId: client.id, + number: `INV-${String(invoiceCount).padStart(6, '0')}`, + amount: rp.amount, + balance: rp.amount, + status: 'overdue', + dueDate, + periodStart: new Date(dueDate.getTime() - 30 * 86400000), + periodEnd: dueDate, + }, + }); + } + + await prisma.payment.create({ + data: { + tenantId: tenant.id, + clientId: client.id, + invoiceId: invoice.id, + collectedById: users.collector.id, + amount: rp.amount, + method: rp.method, + referenceNo: rp.ref, + createdAt: paidDate, + }, + }); + } + console.log(`Recent unremitted payments: ${recentPaymentDefs.length}`); + + // ─── Notifications (unread, for testing) ────────────────── + const notifDefs = [ + { userId: users.collector.id, type: 'in_app', channel: 'payment_confirmation', title: 'Payment Recorded', message: 'Payment of ₱699 for Juan Dela Cruz has been recorded.', daysAgo: 0 }, + { userId: users.collector.id, type: 'in_app', channel: 'billing_reminder', title: 'Overdue Reminder', message: '3 invoices are overdue in Barangay 1 - Centro.', daysAgo: 1 }, + { userId: users.collector.id, type: 'in_app', channel: 'ticket_update', title: 'Ticket Assigned', message: 'DNS resolution issues ticket has been assigned to you.', daysAgo: 1 }, + { userId: users.technician.id, type: 'in_app', channel: 'ticket_update', title: 'New Ticket', message: 'Fiber cable repair - Poblacion ticket needs attention.', daysAgo: 0 }, + { userId: users.technician.id, type: 'in_app', channel: 'ticket_update', title: 'Ticket Resolved', message: 'Cannot connect after reboot ticket has been resolved.', daysAgo: 0 }, + { userId: users.manager.id, type: 'in_app', channel: 'billing_reminder', title: 'Weekly Summary', message: '12 payments collected this week totaling ₱14,988.', daysAgo: 2 }, + { userId: users.manager.id, type: 'in_app', channel: 'ticket_update', title: 'Urgent Maintenance', message: 'Node outage - Riverside sector affecting 8 subscribers.', daysAgo: 0 }, + { userId: users.tenant_admin.id, type: 'in_app', channel: 'payment_confirmation', title: 'Remittance Confirmed', message: 'Remittance of ₱5,000 has been confirmed by Admin.', daysAgo: 3 }, + ]; + + for (const n of notifDefs) { + const sentAt = new Date(); + sentAt.setDate(sentAt.getDate() - n.daysAgo); + await prisma.notification.create({ + data: { + tenantId: tenant.id, + userId: n.userId, + type: n.type, + channel: n.channel, + title: n.title, + message: n.message, + isRead: false, + sentAt, + }, + }); + } + console.log(`Notifications: ${notifDefs.length} (unread)`); console.log('\n✅ Seed completed successfully!'); console.log(`\n📊 Summary:`); @@ -786,8 +874,12 @@ async function main() { console.log(` Expenses: ${expDefs.length} (approved + pending)`); console.log(` Assets: ${assetDefs.length}`); console.log(` Company Accounts: ${accts.length}`); - console.log(`\n🔑 Login: admin@demo-isp.com / admin123!`); - console.log(`🌐 Portal: C-000001 / 09171234567`); + console.log(`\n🔑 Logins (all passwords: admin123!):`); + console.log(` admin@demo-isp.com (tenant_admin) - full access`); + console.log(` manager@demo-isp.com (manager) - operational management`); + console.log(` collector@demo-isp.com (collector) - payment collection`); + console.log(` tech@demo-isp.com (technician) - field operations`); + console.log(` tech2@demo-isp.com (technician) - field operations`); } main()