feat(f6.8): harden offline field flow and act documents
Android CI / RC / Android · lint, tests, debug APK, release compile (push) Successful in 3m41s
DH V2 CI / API · typecheck, tests, build (push) Successful in 31s
DH V2 CI / WEB · typecheck, build (push) Successful in 19s
Production dependency audit / API · production dependencies (push) Successful in 9s
Production dependency audit / WEB · production dependencies (push) Successful in 8s
DH V2 CI / Docker / scripts contract (push) Successful in 1m14s
Android CI / RC / Android · lint, tests, debug APK, release compile (push) Successful in 3m41s
DH V2 CI / API · typecheck, tests, build (push) Successful in 31s
DH V2 CI / WEB · typecheck, build (push) Successful in 19s
Production dependency audit / API · production dependencies (push) Successful in 9s
Production dependency audit / WEB · production dependencies (push) Successful in 8s
DH V2 CI / Docker / scripts contract (push) Successful in 1m14s
This commit is contained in:
@@ -273,6 +273,23 @@ export class FieldInventoryService {
|
||||
request: RequestWithContext,
|
||||
) {
|
||||
const context = await this.requireVisitContext(visitId, principal, true);
|
||||
if (dto.clientGeneratedId) {
|
||||
const [existing] = await this.dataSource.query(`
|
||||
SELECT discovery.asset_id AS "assetId", discovery.visit_id AS "visitId"
|
||||
FROM asset_field_discoveries discovery
|
||||
WHERE discovery.asset_id=$1::uuid
|
||||
LIMIT 1
|
||||
`, [dto.clientGeneratedId]) as Array<{ assetId: string; visitId: string }>;
|
||||
if (existing) {
|
||||
if (existing.visitId !== visitId) {
|
||||
throw new ConflictException({
|
||||
code: 'FIELD_INVENTORY_CLIENT_ID_REUSED',
|
||||
message: 'El identificador offline del Inventario ya fue utilizado en otra inspección',
|
||||
});
|
||||
}
|
||||
return this.detail(visitId, existing.assetId, principal);
|
||||
}
|
||||
}
|
||||
const parentId = dto.parentId ?? context.scopeAssetId;
|
||||
await this.requireParentInContext(parentId, context);
|
||||
|
||||
@@ -286,6 +303,7 @@ export class FieldInventoryService {
|
||||
|
||||
const code = dto.code?.trim().toUpperCase() || await this.nextFieldCode(capturedAt);
|
||||
const discoveryDto: CreateFieldDiscoveryDto = {
|
||||
clientGeneratedId: dto.clientGeneratedId,
|
||||
visitId,
|
||||
code,
|
||||
name: dto.name,
|
||||
@@ -351,6 +369,28 @@ export class FieldInventoryService {
|
||||
): Promise<{ media: AssetMediaView; capture: FieldInventoryCaptureStatus }> {
|
||||
const context = await this.requireVisitContext(visitId, principal, true);
|
||||
await this.requireAssetInContext(assetId, context);
|
||||
if (dto.operationId) {
|
||||
const [existing] = await this.dataSource.query(`
|
||||
SELECT media_id AS "mediaId", asset_id AS "assetId"
|
||||
FROM asset_field_capture_events
|
||||
WHERE client_operation_id=$1::uuid
|
||||
LIMIT 1
|
||||
`, [dto.operationId]) as Array<{ mediaId: string | null; assetId: string }>;
|
||||
if (existing) {
|
||||
if (existing.assetId !== assetId || !existing.mediaId) {
|
||||
throw new ConflictException({
|
||||
code: 'FIELD_PHOTO_OPERATION_REUSED',
|
||||
message: 'La operación offline de fotografía ya fue utilizada en otro Inventario',
|
||||
});
|
||||
}
|
||||
const media = (await this.media.list(assetId)).data.find((item) => item.id === existing.mediaId);
|
||||
if (!media) throw new ConflictException({
|
||||
code: 'FIELD_PHOTO_OPERATION_MEDIA_MISSING',
|
||||
message: 'La fotografía sincronizada no está disponible',
|
||||
});
|
||||
return { media, capture: await this.captureStatus(visitId, assetId) };
|
||||
}
|
||||
}
|
||||
if (!await this.isSelected(visitId, assetId)) {
|
||||
throw new ConflictException({
|
||||
code: 'FIELD_INVENTORY_NOT_SELECTED',
|
||||
@@ -383,8 +423,8 @@ export class FieldInventoryService {
|
||||
device_latitude, device_longitude, device_accuracy_m,
|
||||
device_captured_at, device_label,
|
||||
exif_latitude, exif_longitude, exif_captured_at,
|
||||
created_by
|
||||
) VALUES ($1,$2,$3,'PHOTO',$4,$5,$6,$7,$8,$9,$10,$11,$12)
|
||||
created_by, client_operation_id
|
||||
) VALUES ($1,$2,$3,'PHOTO',$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
|
||||
`, [
|
||||
visitId,
|
||||
assetId,
|
||||
@@ -398,6 +438,7 @@ export class FieldInventoryService {
|
||||
dto.exifLongitude ?? null,
|
||||
dto.exifCapturedAt ? new Date(dto.exifCapturedAt) : null,
|
||||
principal.userId,
|
||||
dto.operationId ?? null,
|
||||
]);
|
||||
|
||||
return { media: uploaded, capture: await this.captureStatus(visitId, assetId) };
|
||||
|
||||
Reference in New Issue
Block a user