fix(api): decouple mobile Act list from document workflow
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
import type { ListInspectionActsQueryDto } from './dto/list-inspection-acts-query.dto';
|
||||
|
||||
/**
|
||||
* Read model intentionally kept small for the field client.
|
||||
*
|
||||
* The Android app only needs the Act identity/lifecycle, deadlines and counters to
|
||||
* open an Inspection. It must not fail because an office-document/report module is
|
||||
* unavailable or temporarily schema-drifted. The canonical office endpoints keep
|
||||
* their richer projection in InspectionActsService.
|
||||
*/
|
||||
@Injectable()
|
||||
export class MobileInspectionActsService {
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async listForVisit(visitId: string, query: ListInspectionActsQueryDto) {
|
||||
const [visit] = (await this.dataSource.query(
|
||||
'SELECT id FROM inspection_visits WHERE id = $1::uuid',
|
||||
[visitId],
|
||||
)) as Array<{ id: string }>;
|
||||
if (!visit) {
|
||||
throw new NotFoundException({
|
||||
code: 'INSPECTION_VISIT_NOT_FOUND',
|
||||
message: 'Visita de inspección no encontrada',
|
||||
});
|
||||
}
|
||||
|
||||
const conditions = ['act.visit_id = $1::uuid'];
|
||||
const parameters: unknown[] = [visitId];
|
||||
const add = (value: unknown): string => {
|
||||
parameters.push(value);
|
||||
return `$${parameters.length}`;
|
||||
};
|
||||
|
||||
if (query.search?.trim()) {
|
||||
const search = add(`%${query.search.trim()}%`);
|
||||
conditions.push(`(act.code ILIKE ${search} OR act.title ILIKE ${search})`);
|
||||
}
|
||||
if (query.status) conditions.push(`act.status = ${add(query.status)}`);
|
||||
if (query.year) conditions.push(`act.act_year = ${add(query.year)}`);
|
||||
if (query.dateFrom) conditions.push(`act.occurred_at::date >= ${add(query.dateFrom)}::date`);
|
||||
if (query.dateTo) conditions.push(`act.occurred_at::date <= ${add(query.dateTo)}::date`);
|
||||
|
||||
const where = `WHERE ${conditions.join(' AND ')}`;
|
||||
const [countRow] = (await this.dataSource.query(
|
||||
`SELECT COUNT(*)::integer AS total FROM inspection_acts act ${where}`,
|
||||
parameters,
|
||||
)) as Array<{ total: number }>;
|
||||
const total = Number(countRow?.total ?? 0);
|
||||
|
||||
const limit = add(query.pageSize);
|
||||
const offset = add((query.page - 1) * query.pageSize);
|
||||
const data = await this.dataSource.query(`
|
||||
SELECT
|
||||
act.id,
|
||||
act.visit_id AS "visitId",
|
||||
act.code,
|
||||
act.status,
|
||||
act.occurred_at AS "occurredAt",
|
||||
act.title,
|
||||
act.summary,
|
||||
act.observations,
|
||||
act.urgency,
|
||||
act.deadline_days AS "deadlineDays",
|
||||
act.deadline_day_type AS "deadlineDayType",
|
||||
act.deadline_basis AS "deadlineBasis",
|
||||
act.deadline_base_at AS "deadlineBaseAt",
|
||||
act.deadline_at AS "deadlineAt",
|
||||
act.locked_at AS "lockedAt",
|
||||
act.locked_sha256 AS "lockedSha256",
|
||||
act.sealed_at AS "sealedAt",
|
||||
act.current_version AS "currentVersion",
|
||||
act.closed_at AS "closedAt",
|
||||
act.closure_sha256 AS "closureSha256",
|
||||
COALESCE((
|
||||
SELECT COUNT(*)::integer
|
||||
FROM inspection_act_assets link
|
||||
WHERE link.act_id = act.id AND link.included = true
|
||||
), 0)::integer AS "assetCount",
|
||||
COALESCE((
|
||||
SELECT COUNT(*)::integer
|
||||
FROM inspection_findings finding
|
||||
WHERE finding.act_id = act.id AND finding.status <> 'VOIDED'
|
||||
), 0)::integer AS "findingCount"
|
||||
FROM inspection_acts act
|
||||
${where}
|
||||
ORDER BY act.act_year DESC, act.act_number DESC
|
||||
LIMIT ${limit} OFFSET ${offset}
|
||||
`, parameters);
|
||||
|
||||
return {
|
||||
data,
|
||||
meta: {
|
||||
page: query.page,
|
||||
pageSize: query.pageSize,
|
||||
total,
|
||||
totalPages: total === 0 ? 0 : Math.ceil(total / query.pageSize),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user