Merge dev to main #1

Open
kibin wants to merge 33 commits from dev into main
5 changed files with 38 additions and 7 deletions
Showing only changes of commit 58c01d4580 - Show all commits

View File

@@ -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: '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: '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 }, { 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[] = []; const clients: any[] = [];
@@ -281,7 +292,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 >= 30; // last 5 are new signups pending installation const isNewSignup = i >= 30; // 15 new signups pending installation
const client = await prisma.client.create({ const client = await prisma.client.create({
data: { 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: 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: 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: 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) { for (const t of supportTickets) {

View File

@@ -25,8 +25,9 @@ export class InvoiceController {
@CurrentUser() user: CurrentUserPayload, @CurrentUser() user: CurrentUserPayload,
@Query('clientId') clientId?: string, @Query('clientId') clientId?: string,
@Query('status') status?: 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') @Get(':id')

View File

@@ -10,13 +10,23 @@ import { paginationArgs, paginatedResult } from '../common/dto/pagination.dto';
export class InvoiceService { export class InvoiceService {
constructor(private readonly prisma: PrismaService) {} 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 { skip, take, page, limit } = paginationArgs({ page: filters?.page, limit: filters?.limit });
const db = this.prisma.forTenant(tenantId); const db = this.prisma.forTenant(tenantId);
const statusFilter = filters?.status
? (filters.status.includes(',') ? { in: filters.status.split(',') } : filters.status)
: undefined;
const where = { const where = {
...(filters?.clientId && { clientId: filters.clientId }), ...(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([ const [items, total] = await Promise.all([
db.invoice.findMany({ db.invoice.findMany({
where, where,
@@ -26,7 +36,7 @@ export class InvoiceService {
client: { select: { id: true, firstName: true, lastName: true, accountNumber: true, phone: true, latitude: true, longitude: true } }, client: { select: { id: true, firstName: true, lastName: true, accountNumber: true, phone: true, latitude: true, longitude: true } },
_count: { select: { payments: true } }, _count: { select: { payments: true } },
}, },
orderBy: { createdAt: 'desc' }, orderBy,
}), }),
db.invoice.count({ where }), db.invoice.count({ where }),
]); ]);

View File

@@ -159,7 +159,10 @@ export class PaymentService {
} }
async getUnremittedPayments(tenantId: string, collectorId: string) { 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); .map((r) => r.paymentId);
return this.prisma.payment.findMany({ return this.prisma.payment.findMany({

View File

@@ -43,7 +43,7 @@ export class TicketController {
} }
@Post() @Post()
@Roles('manager') @Roles('technician')
async create( async create(
@CurrentUser() user: CurrentUserPayload, @CurrentUser() user: CurrentUserPayload,
@Body() dto: CreateTicketDto, @Body() dto: CreateTicketDto,