Integration: Stubs von L2/L3/L4/L5 gegen echte Services getauscht
- Aufträge: createWorkOrder/transitionWorkOrder/getCompletionBlockers aus L2 - Dokumente: storeFile aus L1; neu services/documents/read.ts (readStoredBytes, readDocumentBytes mit Prüfsummenprüfung); L2-Upload nutzt zentrale Ablage - Dubletten aus L1 (findDuplicateCustomers(ctx)); Objekt-Kandidaten als imports/site-candidates.ts; Dateityp-Erkennung mobil als field/mime.ts - Objekt-Historie mobil als Adapter auf L1 getSiteHistory, PDF über /api/v1/reports/:id/pdf - L4-Upload-Idempotenz: fester Upload-Lineage nach storeFile, Race → Soft-Delete + Replay - PDF-Worker-Kontext: document:write zum Ablegen des Berichts-PDF - Import: doppeltes Work-Order-Audit entfernt; bestätigte Aufträge starten planned - Dateilinks in Auftragsdetail auf /files/[documentId] - proxy: /api/v1 ohne Session → 401 JSON statt Login-Redirect - ARCHITEKTUR §2: Objekt-Historie für Feldrollen (freigegebene Einsätze aller Teams) Gate: tsc, lint, build, 42/42 Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,39 @@ import { allowedDocumentVisibility, requireVisibleWorkOrder, workOrderScope } fr
|
||||
import { STATUS_GROUP, type CompletionBlocker, type StatusGroup, type WorkOrderStatus } from "@/lib/work-orders/status";
|
||||
import { ACTIVE_SESSION_STATUSES } from "./sessions";
|
||||
// TODO(merge L2/L1): replace with the lane implementations
|
||||
import { completionBlockers } from "./stubs/work-order-transition";
|
||||
import { getSiteHistory, type SiteHistoryEntry } from "./stubs/site-history";
|
||||
import { getCompletionBlockers } from "@/server/services/work-orders/completion";
|
||||
import { getSiteHistory } from "@/server/services/sites/history";
|
||||
|
||||
/** One released report at the site, as shown in the mobile order detail (US-005). */
|
||||
export type SiteHistoryEntry = {
|
||||
reportId: string;
|
||||
reportType: "daily" | "completion";
|
||||
reportDate: Date;
|
||||
workOrderId: string;
|
||||
workOrderNumber: string;
|
||||
workOrderTitle: string;
|
||||
/** Immutable PDF of the approved report (lane reports API; requires report:read). */
|
||||
pdfHref: string;
|
||||
};
|
||||
|
||||
/** Adapter over the site history service (lane master data): flattens approved reports, newest first. */
|
||||
async function fieldSiteHistory(ctx: ServiceCtx, siteId: string, limit = 20): Promise<SiteHistoryEntry[]> {
|
||||
const { items } = await getSiteHistory(ctx, siteId, { onlyApproved: true, pageSize: Math.min(100, limit * 2) });
|
||||
return items
|
||||
.flatMap((i) =>
|
||||
i.approvedReports.map((r) => ({
|
||||
reportId: r.id,
|
||||
reportType: r.type,
|
||||
reportDate: r.reportDate,
|
||||
workOrderId: i.workOrderId,
|
||||
workOrderNumber: i.number,
|
||||
workOrderTitle: i.title,
|
||||
pdfHref: `/api/v1/reports/${encodeURIComponent(r.id)}/pdf`,
|
||||
})),
|
||||
)
|
||||
.sort((a, b) => b.reportDate.getTime() - a.reportDate.getTime())
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
/** Read models of the mobile app (Spec §11.2, §22, US-005). All reads go through workOrderScope. */
|
||||
|
||||
@@ -208,8 +239,8 @@ export async function getFieldOrderDetail(ctx: ServiceCtx, workOrderId: string):
|
||||
const [orderDocs, siteDocs, siteHistory, blockers] = await Promise.all([
|
||||
ctx.db.document.findMany({ where: { ...docWhere, workOrderId: wo.id }, orderBy: { createdAt: "desc" }, select: docSelect }),
|
||||
wo.siteId ? ctx.db.document.findMany({ where: { ...docWhere, siteId: wo.siteId, workOrderId: null }, orderBy: { createdAt: "desc" }, select: docSelect }) : Promise.resolve([]),
|
||||
wo.siteId ? getSiteHistory(ctx, wo.siteId, { onlyApproved: true }).catch((err) => (err instanceof ServiceError ? [] : Promise.reject(err))) : Promise.resolve([]),
|
||||
completionBlockers(ctx, wo.id),
|
||||
wo.siteId ? fieldSiteHistory(ctx, wo.siteId).catch((err) => (err instanceof ServiceError ? [] : Promise.reject(err))) : Promise.resolve([]),
|
||||
getCompletionBlockers(ctx, wo.id),
|
||||
]);
|
||||
|
||||
// only the newest version per lineage
|
||||
@@ -268,7 +299,7 @@ export async function getFieldBundle(ctx: ServiceCtx, since?: Date | null) {
|
||||
|
||||
const siteIds = [...new Set(orders.map((o) => o.site?.id).filter((id): id is string => !!id))];
|
||||
const histories = Object.fromEntries(
|
||||
await Promise.all(siteIds.map(async (id) => [id, await getSiteHistory(ctx, id, { onlyApproved: true, limit: 5 }).catch(() => [])] as const)),
|
||||
await Promise.all(siteIds.map(async (id) => [id, await fieldSiteHistory(ctx, id, 5).catch(() => [])] as const)),
|
||||
);
|
||||
return {
|
||||
serverTime: serverTime.toISOString(),
|
||||
|
||||
Reference in New Issue
Block a user