fix(f4): expose recurrence state and close ci type gaps
This commit is contained in:
@@ -16,12 +16,12 @@ import type {
|
|||||||
} from './dto/inventory-function.dto';
|
} from './dto/inventory-function.dto';
|
||||||
import { AssetHistoryService } from './asset-history.service';
|
import { AssetHistoryService } from './asset-history.service';
|
||||||
|
|
||||||
interface InventoryFunctionRow {
|
export interface InventoryFunctionRow {
|
||||||
id: string; code: string; name: string; description: string | null;
|
id: string; code: string; name: string; description: string | null;
|
||||||
isActive: boolean; sortOrder: number; createdAt: Date; updatedAt: Date;
|
isActive: boolean; sortOrder: number; createdAt: Date; updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FunctionAssignmentRow {
|
export interface FunctionAssignmentRow {
|
||||||
id: string; assetId: string; functionId: string; functionCode: string; functionName: string;
|
id: string; assetId: string; functionId: string; functionCode: string; functionName: string;
|
||||||
validFrom: Date; validUntil: Date | null; reason: string | null;
|
validFrom: Date; validUntil: Date | null; reason: string | null;
|
||||||
changedBy: string | null; changedByUsername: string | null; createdAt: Date;
|
changedBy: string | null; changedByUsername: string | null; createdAt: Date;
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ import { FindingRecurrenceService } from './finding-recurrence.service';
|
|||||||
export class FindingRecurrenceController {
|
export class FindingRecurrenceController {
|
||||||
constructor(private readonly recurrence: FindingRecurrenceService) {}
|
constructor(private readonly recurrence: FindingRecurrenceService) {}
|
||||||
|
|
||||||
|
@Get(':id/recurrence')
|
||||||
|
@RequirePermissions('inspection_findings.read')
|
||||||
|
state(@Param('id', new ParseUUIDPipe({ version: '4' })) id: string) {
|
||||||
|
return this.recurrence.state(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Get(':id/recurrence-candidates')
|
@Get(':id/recurrence-candidates')
|
||||||
@RequirePermissions('inspection_findings.read')
|
@RequirePermissions('inspection_findings.read')
|
||||||
candidates(@Param('id', new ParseUUIDPipe({ version: '4' })) id: string) {
|
candidates(@Param('id', new ParseUUIDPipe({ version: '4' })) id: string) {
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ interface FindingRecurrenceContext {
|
|||||||
visitStatus: string;
|
visitStatus: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FindingRecurrenceState {
|
||||||
|
isRecurrence: boolean;
|
||||||
|
recurrenceOfFindingId: string | null;
|
||||||
|
recurrenceOfFindingCode: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
function comparable(value: string): string {
|
function comparable(value: string): string {
|
||||||
return value
|
return value
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
@@ -44,6 +50,25 @@ export class FindingRecurrenceService {
|
|||||||
private readonly findings: InspectionFindingsService,
|
private readonly findings: InspectionFindingsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async state(id: string): Promise<FindingRecurrenceState> {
|
||||||
|
const [row] = await this.dataSource.query(`
|
||||||
|
SELECT
|
||||||
|
finding.is_recurrence AS "isRecurrence",
|
||||||
|
finding.recurrence_of_finding_id AS "recurrenceOfFindingId",
|
||||||
|
antecedent.code AS "recurrenceOfFindingCode"
|
||||||
|
FROM inspection_findings finding
|
||||||
|
LEFT JOIN inspection_findings antecedent ON antecedent.id = finding.recurrence_of_finding_id
|
||||||
|
WHERE finding.id = $1
|
||||||
|
`, [id]) as FindingRecurrenceState[];
|
||||||
|
if (!row) {
|
||||||
|
throw new NotFoundException({
|
||||||
|
code: 'INSPECTION_FINDING_NOT_FOUND',
|
||||||
|
message: 'Hallazgo no encontrado',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
async candidates(id: string) {
|
async candidates(id: string) {
|
||||||
const current = await this.requireFinding(this.dataSource.manager, id, false);
|
const current = await this.requireFinding(this.dataSource.manager, id, false);
|
||||||
const rows = await this.dataSource.query(`
|
const rows = await this.dataSource.query(`
|
||||||
@@ -138,7 +163,11 @@ export class FindingRecurrenceService {
|
|||||||
metadata: { actId: current.actId, visitId: current.visitId, recurrenceAction: 'LINKED' },
|
metadata: { actId: current.actId, visitId: current.visitId, recurrenceAction: 'LINKED' },
|
||||||
}, manager);
|
}, manager);
|
||||||
});
|
});
|
||||||
return this.findings.getById(id);
|
const [finding, recurrence] = await Promise.all([
|
||||||
|
this.findings.getById(id),
|
||||||
|
this.state(id),
|
||||||
|
]);
|
||||||
|
return { ...finding, ...recurrence };
|
||||||
}
|
}
|
||||||
|
|
||||||
async clear(
|
async clear(
|
||||||
@@ -171,7 +200,11 @@ export class FindingRecurrenceService {
|
|||||||
metadata: { actId: current.actId, visitId: current.visitId, recurrenceAction: 'CLEARED' },
|
metadata: { actId: current.actId, visitId: current.visitId, recurrenceAction: 'CLEARED' },
|
||||||
}, manager);
|
}, manager);
|
||||||
});
|
});
|
||||||
return this.findings.getById(id);
|
const [finding, recurrence] = await Promise.all([
|
||||||
|
this.findings.getById(id),
|
||||||
|
this.state(id),
|
||||||
|
]);
|
||||||
|
return { ...finding, ...recurrence };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async requireFinding(
|
private async requireFinding(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { CloseInspectionFindingDto } from './dto/close-inspection-finding.dto';
|
|||||||
import { CreateInspectionFindingDto } from './dto/create-inspection-finding.dto';
|
import { CreateInspectionFindingDto } from './dto/create-inspection-finding.dto';
|
||||||
import { ListInspectionFindingsQueryDto } from './dto/list-inspection-findings-query.dto';
|
import { ListInspectionFindingsQueryDto } from './dto/list-inspection-findings-query.dto';
|
||||||
import { UpdateInspectionFindingDto } from './dto/update-inspection-finding.dto';
|
import { UpdateInspectionFindingDto } from './dto/update-inspection-finding.dto';
|
||||||
|
import { FindingRecurrenceService } from './finding-recurrence.service';
|
||||||
import { InspectionFindingWorklistService } from './inspection-finding-worklist.service';
|
import { InspectionFindingWorklistService } from './inspection-finding-worklist.service';
|
||||||
import { InspectionFindingsService } from './inspection-findings.service';
|
import { InspectionFindingsService } from './inspection-findings.service';
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ export class InspectionFindingsController {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly findings: InspectionFindingsService,
|
private readonly findings: InspectionFindingsService,
|
||||||
private readonly worklist: InspectionFindingWorklistService,
|
private readonly worklist: InspectionFindingWorklistService,
|
||||||
|
private readonly recurrence: FindingRecurrenceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@@ -56,8 +58,12 @@ export class InspectionFindingsController {
|
|||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@RequirePermissions('inspection_findings.read')
|
@RequirePermissions('inspection_findings.read')
|
||||||
get(@Param('id', new ParseUUIDPipe({ version: '4' })) id: string) {
|
async get(@Param('id', new ParseUUIDPipe({ version: '4' })) id: string) {
|
||||||
return this.findings.getById(id);
|
const [finding, recurrence] = await Promise.all([
|
||||||
|
this.findings.getById(id),
|
||||||
|
this.recurrence.state(id),
|
||||||
|
]);
|
||||||
|
return { ...finding, ...recurrence };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id/verification-history')
|
@Get(':id/verification-history')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export type IconName =
|
export type IconName =
|
||||||
| 'home' | 'map' | 'layers' | 'calendar' | 'clipboard' | 'alert'
|
| 'home' | 'map' | 'layers' | 'calendar' | 'clipboard' | 'alert'
|
||||||
| 'history' | 'users' | 'shield' | 'audit' | 'logout' | 'menu'
|
| 'history' | 'users' | 'shield' | 'audit' | 'logout' | 'menu'
|
||||||
| 'plus' | 'search' | 'edit' | 'chevron' | 'check' | 'key' | 'upload' | 'mail';
|
| 'plus' | 'search' | 'edit' | 'chevron' | 'check' | 'key' | 'upload' | 'mail' | 'lock';
|
||||||
|
|
||||||
export function Icon({ name, size = 18 }: { name: IconName; size?: number }) {
|
export function Icon({ name, size = 18 }: { name: IconName; size?: number }) {
|
||||||
const paths: Record<IconName, React.ReactNode> = {
|
const paths: Record<IconName, React.ReactNode> = {
|
||||||
@@ -25,6 +25,7 @@ export function Icon({ name, size = 18 }: { name: IconName; size?: number }) {
|
|||||||
key: <><circle cx="8" cy="15" r="4"/><path d="m11 12 9-9M15 8l3 3M17 6l2 2"/></>,
|
key: <><circle cx="8" cy="15" r="4"/><path d="m11 12 9-9M15 8l3 3M17 6l2 2"/></>,
|
||||||
upload: <><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M5 20h14"/></>,
|
upload: <><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M5 20h14"/></>,
|
||||||
mail: <><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></>,
|
mail: <><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></>,
|
||||||
|
lock: <><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></>,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<svg className="icon" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
<svg className="icon" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
import './api';
|
||||||
|
|
||||||
|
declare module './api' {
|
||||||
|
interface InspectionFinding {
|
||||||
|
isRecurrence: boolean;
|
||||||
|
recurrenceOfFindingId: string | null;
|
||||||
|
recurrenceOfFindingCode: string | null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
Reference in New Issue
Block a user