/** * engine-workorder — composed **by call**, not by event. * * The in-scope functions are the surface: `createWorkOrder`, `assignWorkOrder`, * `startWorkOrder`, `reportTime`, `reportMaterial`, `completeWorkOrder`, * `closeWorkOrder`, `getReportedLines`, `listOrders`. A vertical imports those into * its OWN operations and runs them inside its own transaction — Callout's * `callout/complete-workorder` is a permission check and one call. * * So the registered operations below are thin by design: each is * `assertAllowed(await ctx.check(PERM.…))` plus the matching export, and it is the * export that carries the invariant. That is what lets a vertical extend by * composition rather than by forking — and why the lifecycle check lives with the * in-scope function (`lifecycle.ts`), where both routes in pass through it. * * This engine emits events as every engine does, but nothing composes it by * consuming them: `invoicing` is the by-event engine downstream of * `workorder.completed`, which is a different relationship from being composed. */ import { z } from 'zod'; import { type Page, type Money } from '@substrat-run/contracts'; import type { PageParams } from '@substrat-run/kernel'; /** * The conflict reasons this engine raises — its own vocabulary, narrowing the platform's * `conflict` code (#113). Exported so a vertical can branch on WHY a refusal happened * without importing this engine's types or matching on its prose; `as const` so a typo * is a compile error here rather than a slug nobody ever matches. * * Additive only, like every other engine surface: new reasons may appear, existing ones * do not change spelling. */ export declare const WORKORDER_CONFLICT_REASONS: readonly ["invalid_transition"]; export type WorkorderConflictReason = (typeof WORKORDER_CONFLICT_REASONS)[number]; import { type BillableLine, type WorkOrder, type TimeEntry, type MaterialLine } from './schemas.js'; export { workorderEntities, workorderRow } from './entities.js'; export { billableLine, decimal, workOrder, timeEntry, materialLine, type BillableLine, type WorkOrder, type TimeEntry, type MaterialLine, } from './schemas.js'; export { workorderOperations, WORKORDER_PERMISSIONS } from './operations.js'; /** * The event contract (#696) — what a VERTICAL imports so that consuming this * engine by event is checked rather than guessed. `events.ts` says what these * are and what they are not: types only, vertical-facing only, and a sibling * engine (invoicing, downstream of `workorder.completed`) still writes its own * Zod view. */ export { type WorkorderEvents, type WorkorderEventType, type WorkorderCreatedPayload, type WorkorderAssignedPayload, type WorkorderStartedPayload, type WorkorderTimeReportedPayload, type WorkorderMaterialReportedPayload, type WorkorderCompletedPayload, type WorkorderClosedPayload, } from './events.js'; export { workorderLifecycles, workorderLifecycle } from './lifecycle.js'; import { type ModuleRegistration, type OperationContext } from '@substrat-run/kernel'; export declare const PERM: { create: string & z.$brand<"PermissionKey">; read: string & z.$brand<"PermissionKey">; assign: string & z.$brand<"PermissionKey">; report: string & z.$brand<"PermissionKey">; complete: string & z.$brand<"PermissionKey">; close: string & z.$brand<"PermissionKey">; }; export declare const workorderManifest: { id: string & z.$brand<"ModuleId">; version: string; kernelContract: string; permissions: { key: string & z.$brand<"PermissionKey">; description: string; }[]; events: { emits: { type: string; schemaVersion: number; }[]; consumes: { type: string; schemaVersion: number; }[]; }; migrations: { journalDir: string; compatibleFrom: string; }; attachmentTargets: { entityType: string; readPermission: string & z.$brand<"PermissionKey">; writePermission?: (string & z.$brand<"PermissionKey">) | undefined; }[]; liveTargets?: { entityType: string; readPermission: string & z.$brand<"PermissionKey">; }[] | undefined; entityRelations?: { entityType: string; parentType: string; }[] | undefined; guards?: { before: string; predicate: string; config: Record; }[] | undefined; schedules?: { operation: string; cadence: { everyMinutes: number; }; input?: Record | undefined; permissions: (string & z.$brand<"PermissionKey">)[]; }[] | undefined; freshness?: { eventType: string; within: { hours: number; }; }[] | undefined; withdraws?: string[] | undefined; entitlementKey: string; envSpec?: { key: string; label?: string | undefined; description: string; placeholder?: string | undefined; required: boolean; secret: boolean; default?: string | undefined; group?: string | undefined; }[] | undefined; ownerGrants?: (string & z.$brand<"PermissionKey">)[] | undefined; entitlements?: string[] | undefined; provides?: string[] | undefined; requires?: string[] | undefined; api?: string | undefined; searchables?: { entityType: string; fields: string[]; table?: string | undefined; idColumn?: string | undefined; tokenizer?: "prefix" | "substring" | undefined; }[] | undefined; lists?: { entityType: string; sortable: string[]; filterable?: string[] | undefined; table?: string | undefined; idColumn?: string | undefined; }[] | undefined; ui?: { routes?: { path: string; screen: string; permission: string & z.$brand<"PermissionKey">; }[] | undefined; nav?: { label: string; icon?: string | undefined; to: string; permission: string & z.$brand<"PermissionKey">; }[] | undefined; entityViews?: { entityType: string; view: string; }[] | undefined; widgets?: { slot: string; component: string; permission: string & z.$brand<"PermissionKey">; }[] | undefined; settingsPanels?: { label: string; component: string; permission: string & z.$brand<"PermissionKey">; }[] | undefined; } | undefined; }; export declare const workorderMigrations: { version: string; sql: string; }[]; export declare const createWorkOrderInput: z.ZodObject<{ facility: z.ZodObject<{ entityType: z.ZodString; entityId: z.ZodString; }, z.core.$strip>; customer: z.ZodObject<{ entityType: z.ZodString; entityId: z.ZodString; }, z.core.$strip>; kind: z.ZodString; title: z.ZodString; description: z.ZodOptional; }, z.core.$strip>; export type CreateWorkOrderInput = z.infer; /** * The inputs of the four in-scope functions extracted in #975 are the schemas * their default operation bindings DECLARE — read from `operations.ts`, never * restated here. One description of each input, parsed on the way in whether * the call arrives through the operation or from a vertical's own code. */ export declare const assignWorkOrderInput: z.ZodObject<{ orderId: z.ZodString; technician: z.ZodString; }, z.core.$strip>; export type AssignWorkOrderInput = z.infer; export declare const startWorkOrderInput: z.ZodObject<{ orderId: z.ZodString; }, z.core.$strip>; export type StartWorkOrderInput = z.infer; export declare const reportTimeInput: z.ZodObject<{ orderId: z.ZodString; hours: z.ZodString; note: z.ZodOptional; }, z.core.$strip>; export type ReportTimeInput = z.infer; export declare const reportMaterialInput: z.ZodObject<{ orderId: z.ZodString; article: z.ZodString; qty: z.ZodString; note: z.ZodOptional; }, z.core.$strip>; export type ReportMaterialInput = z.infer; export declare function createWorkOrder(ctx: OperationContext, rawInput: CreateWorkOrderInput): WorkOrder; /** * Everything reported against one order — the read a vertical prices FROM (§4). * * Both halves name their columns and parse their rows (#771). This function was * the sharpest case of the seam being typed by assertion alone: `SELECT *` typed * `` returned whatever the table held, so a column added upstream * crossed the seam and a column renamed arrived as `undefined` — in a shape a * vertical had already declared an operation `output` with. */ export declare function getReportedLines(ctx: OperationContext, orderId: string): { time: TimeEntry[]; material: MaterialLine[]; }; /** * A page of work orders (#811). * * The `WHERE`, the `ORDER BY`, the keyset comparison and the `LIMIT` are the * kernel's, composed from this operation's declared `paged.over` vocabulary and * running over indexes it provisioned. What stays here is the projection — a * stored row is not a `WorkOrder` — which is why `mapPage` exists: it re-shapes * the entries and leaves the walk alone. * * The old positional `status?` is gone rather than kept beside this. It filtered * on one hard-coded column with a hand-written `ORDER BY number DESC` and no * bound at all, which is the shape #811 was filed against; a vertical wanting a * different sort had no path but to fork. */ export declare function listOrders(ctx: OperationContext, page: PageParams): Page; /** * One work order, by id (#811). * * Added because paging exposed two verticals doing `listOrders(ctx).find(o => o.id * === …)` — reading every row in the scope to return one, which was wasteful when * the list was unbounded and is WRONG once it is a page: the row you want is * simply not on page one. There was no exported single-order read to reach for, * which is why both reached for the list. * * Throws rather than returning undefined, like every other read here: an id that * names nothing is a caller's mistake, and `getRow` already refuses it. */ export declare function getWorkOrder(ctx: OperationContext, orderId: string): WorkOrder; /** * Assign a technician. Records and emits; the order stays `planned` (§3). * * In-scope (K-16) since #975: `assignWorkOrder`, `startWorkOrder`, `reportTime` * and `reportMaterial` used to live inline in their operation handlers, so a * vertical could not assign, start or report inside its own transaction without * forking — the exact failure the by-call shape exists to prevent. Each is now a * plain export, and `workorder/assign` … `workorder/report-material` below are * their default bindings. The CALLER owns the permission check, as with every * other function in this section. */ export declare function assignWorkOrder(ctx: OperationContext, rawInput: AssignWorkOrderInput): WorkOrder; /** planned → in_progress. See `assignWorkOrder` for why this is exported. */ export declare function startWorkOrder(ctx: OperationContext, rawInput: StartWorkOrderInput): WorkOrder; /** * Append a time entry, attributed to the acting principal. Append-only: there * is no update path, so a correction is another entry (§2). */ export declare function reportTime(ctx: OperationContext, rawInput: ReportTimeInput): TimeEntry; /** Append a material line, reported by the acting principal. Append-only, as above. */ export declare function reportMaterial(ctx: OperationContext, rawInput: ReportMaterialInput): MaterialLine; export declare function completeWorkOrder(ctx: OperationContext, input: { orderId: string; billable: BillableLine[]; currency?: string; }): { order: WorkOrder; total: Money; }; /** * completed → closed. In-scope (K-16) so a vertical can compose the close into * its own operation — e.g. a pickup ceremony that must satisfy a manifest guard * first. `workorder/close` below is this function's default binding; the CALLER * owns the permission check. */ export declare function closeWorkOrder(ctx: OperationContext, input: { orderId: string; }): WorkOrder; export declare const workorderModule: ModuleRegistration; //# sourceMappingURL=index.d.ts.map