diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts index 89aa7e3..6cf8d7e 100644 --- a/packages/db/prisma/seed.ts +++ b/packages/db/prisma/seed.ts @@ -271,6 +271,17 @@ 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 + { 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 }, + { first: 'Rita', last: 'Santillan', phone: '09541234567', email: 'rita@email.com', address: '192 Luna St, Riverside', area: 3, plan: 0, type: 'prepaid', latOff: -0.001, lngOff: -0.001 }, + { first: 'Samuel', last: 'Torres', phone: '09551234567', email: null, address: '203 Del Pilar Blvd, Hilltop', area: 4, plan: 2, type: 'postpaid', latOff: 0.0015, lngOff: 0.001 }, + { first: 'Tina', last: 'Uy', phone: '09561234567', email: 'tina@email.com', address: '214 Quezon Rd, Centro', area: 0, plan: 4, type: 'postpaid', latOff: -0.002, lngOff: -0.0015 }, + { first: 'Ulysses', last: 'Velasco', phone: '09571234567', email: null, address: '225 Magsaysay Ext, Poblacion', area: 1, plan: 1, type: 'prepaid', latOff: 0.003, lngOff: -0.002 }, + { first: 'Vivian', last: 'Walsh', phone: '09581234567', email: 'vivian@email.com', address: '236 Roxas Ave, San Isidro', area: 2, plan: 2, type: 'postpaid', latOff: -0.001, lngOff: 0.003 }, + { first: 'Walter', last: 'Xavier', phone: '09591234567', email: null, address: '247 Laurel Blvd, Riverside', area: 3, plan: 3, type: 'postpaid', latOff: 0.002, lngOff: -0.001 }, + { first: 'Yolanda', last: 'Yap', phone: '09601234567', email: 'yolanda@email.com', address: '258 Osmena Rd, Hilltop', area: 4, plan: 1, type: 'postpaid', latOff: -0.0015, lngOff: 0.002 }, ]; const clients: any[] = []; @@ -281,7 +292,7 @@ async function main() { const c = clientDefs[i]; const accountNumber = `C-${String(i + 1).padStart(6, '0')}`; const plan = plans[c.plan]; - const isNewSignup = i >= 30; // last 5 are new signups pending installation + const isNewSignup = i >= 30; // 15 new signups pending installation const client = await prisma.client.create({ data: { @@ -511,6 +522,12 @@ async function main() { { clientIdx: 26, title: 'High latency for gaming', desc: 'Ping above 100ms during evenings', priority: 'normal', status: 'in_progress', type: 'support', assignee: 'tech' }, { clientIdx: 19, title: 'ONT replacement needed', desc: 'ONT showing red fault light intermittently', priority: 'high', status: 'open', type: 'maintenance', assignee: null }, { clientIdx: 21, title: 'Monthly service credit request', desc: 'Requesting credit for 2-day outage last month', priority: 'low', status: 'open', type: 'support', assignee: null }, + { clientIdx: 3, title: 'Second floor extension installation', desc: 'Client wants additional fiber drop to 2nd floor office', priority: 'normal', status: 'open', type: 'installation', assignee: null }, + { clientIdx: 9, title: 'Fiber relocation due to renovation', desc: 'House renovation requires moving fiber entry point', priority: 'normal', status: 'in_progress', type: 'installation', assignee: 'tech2' }, + { clientIdx: 14, title: 'ONT upgrade to GPON', desc: 'Current ONT outdated, needs GPON-compatible replacement', priority: 'low', status: 'open', type: 'installation', assignee: null }, + { clientIdx: 7, title: 'New branch office fiber install', desc: 'Client opened sari-sari store next door, wants 2nd connection', priority: 'high', status: 'open', type: 'installation', assignee: null }, + { clientIdx: 24, title: 'Intermittent packet loss', desc: 'Ping shows 5-10% packet loss during daytime', priority: 'high', status: 'in_progress', type: 'maintenance', assignee: 'tech' }, + { clientIdx: 12, title: 'Cable exposed across driveway', desc: 'Fiber cable hanging low across client driveway, safety hazard', priority: 'urgent', status: 'open', type: 'maintenance', assignee: null }, ]; for (const t of supportTickets) { diff --git a/src/invoice/invoice.controller.ts b/src/invoice/invoice.controller.ts index e620795..83eb638 100644 --- a/src/invoice/invoice.controller.ts +++ b/src/invoice/invoice.controller.ts @@ -25,8 +25,9 @@ export class InvoiceController { @CurrentUser() user: CurrentUserPayload, @Query('clientId') clientId?: string, @Query('status') status?: string, + @Query('sort') sort?: string, ) { - return this.invoiceService.findAll(user.tenantId, { clientId, status }); + return this.invoiceService.findAll(user.tenantId, { clientId, status, sort }); } @Get(':id') diff --git a/src/invoice/invoice.service.ts b/src/invoice/invoice.service.ts index 6dce30b..7c59cbc 100644 --- a/src/invoice/invoice.service.ts +++ b/src/invoice/invoice.service.ts @@ -10,13 +10,23 @@ import { paginationArgs, paginatedResult } from '../common/dto/pagination.dto'; export class InvoiceService { constructor(private readonly prisma: PrismaService) {} - async findAll(tenantId: string, filters?: { clientId?: string; status?: string; page?: number; limit?: number }) { + async findAll(tenantId: string, filters?: { clientId?: string; status?: string; sort?: string; page?: number; limit?: number }) { const { skip, take, page, limit } = paginationArgs({ page: filters?.page, limit: filters?.limit }); const db = this.prisma.forTenant(tenantId); + + const statusFilter = filters?.status + ? (filters.status.includes(',') ? { in: filters.status.split(',') } : filters.status) + : undefined; + const where = { ...(filters?.clientId && { clientId: filters.clientId }), - ...(filters?.status && { status: filters.status }), + ...(statusFilter && { status: statusFilter }), }; + + const orderBy = filters?.sort === 'dueDate:asc' + ? { dueDate: 'asc' as const } + : { createdAt: 'desc' as const }; + const [items, total] = await Promise.all([ db.invoice.findMany({ where, @@ -26,7 +36,7 @@ export class InvoiceService { client: { select: { id: true, firstName: true, lastName: true, accountNumber: true, phone: true, latitude: true, longitude: true } }, _count: { select: { payments: true } }, }, - orderBy: { createdAt: 'desc' }, + orderBy, }), db.invoice.count({ where }), ]); diff --git a/src/payment/payment.service.ts b/src/payment/payment.service.ts index a09c465..9fad73e 100644 --- a/src/payment/payment.service.ts +++ b/src/payment/payment.service.ts @@ -159,7 +159,10 @@ export class PaymentService { } async getUnremittedPayments(tenantId: string, collectorId: string) { - const remittedIds = (await this.prisma.remittancePayment.findMany({ select: { paymentId: true } })) + const remittedIds = (await this.prisma.remittancePayment.findMany({ + where: { remittance: { tenantId } }, + select: { paymentId: true }, + })) .map((r) => r.paymentId); return this.prisma.payment.findMany({ diff --git a/src/ticket/ticket.controller.ts b/src/ticket/ticket.controller.ts index 7cb9f5a..db6098a 100644 --- a/src/ticket/ticket.controller.ts +++ b/src/ticket/ticket.controller.ts @@ -43,7 +43,7 @@ export class TicketController { } @Post() - @Roles('manager') + @Roles('technician') async create( @CurrentUser() user: CurrentUserPayload, @Body() dto: CreateTicketDto,