@happyvertical/smrt-timesheets 0.57.12 → 0.57.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attendance.js +2 -2
- package/dist/chunks/{attendance-C8JT1wTF.js → attendance-CtOOtfs1.js} +3 -3
- package/dist/chunks/{attendance-C8JT1wTF.js.map → attendance-CtOOtfs1.js.map} +1 -1
- package/dist/chunks/{attendance-Cd57Eh1K.js → attendance-DWDsQpBR.js} +3 -3
- package/dist/chunks/{attendance-Cd57Eh1K.js.map → attendance-DWDsQpBR.js.map} +1 -1
- package/dist/chunks/{models-DIwQkYyq.js → models-DERZIpZh.js} +2 -2
- package/dist/chunks/{models-DIwQkYyq.js.map → models-DERZIpZh.js.map} +1 -1
- package/dist/chunks/{rollup-BYDBTTc_.js → rollup-CjtRNwp9.js} +3 -3
- package/dist/chunks/{rollup-BYDBTTc_.js.map → rollup-CjtRNwp9.js.map} +1 -1
- package/dist/chunks/{service-time-entry-CKSxd5hn.js → service-time-entry-CVjZDt9I.js} +5 -5
- package/dist/chunks/{service-time-entry-CKSxd5hn.js.map → service-time-entry-CVjZDt9I.js.map} +1 -1
- package/dist/chunks/{time-entry-actions-BeK6MEMU.js → time-entry-actions-ClC8tMau.js} +2 -2
- package/dist/chunks/{time-entry-actions-BeK6MEMU.js.map → time-entry-actions-ClC8tMau.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/manifest.json +1 -1
- package/dist/rollup.js +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/package.json +7 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as validateDurationHours, i as ServiceTimeEntryCollection, t as SERVICE_DURATION_HOURS_EVIDENCE } from "./service-time-entry-
|
|
1
|
+
import { a as validateDurationHours, i as ServiceTimeEntryCollection, t as SERVICE_DURATION_HOURS_EVIDENCE } from "./service-time-entry-CVjZDt9I.js";
|
|
2
2
|
import { SmrtCollection, SmrtObject, ValidationError, field, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
3
3
|
import { TenantScoped, getCurrentTenant, tenantId, withTenant } from "@happyvertical/smrt-tenancy";
|
|
4
4
|
import { OperationPermissionError, UserCollection, assertOperationPermission, getCurrentSessionPermissionContext } from "@happyvertical/smrt-users";
|
|
@@ -573,4 +573,4 @@ async function approveServiceTimeAction(entries, input) {
|
|
|
573
573
|
//#endregion
|
|
574
574
|
export { setServiceTimeEntryCommercialResolver as a, ServiceEvidenceService as c, ServiceChargeSnapshotCollection as d, ServiceCompensationSnapshot as f, recordServiceTimeAction as i, TimesheetActionError as l, approveServiceTimeAction as n, submitServiceTimeAction as o, ServiceCompensationSnapshotCollection as p, getServiceTimeEntryCommercialResolver as r, time_entry_actions_exports as s, SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH as t, ServiceChargeSnapshot as u };
|
|
575
575
|
|
|
576
|
-
//# sourceMappingURL=time-entry-actions-
|
|
576
|
+
//# sourceMappingURL=time-entry-actions-ClC8tMau.js.map
|
package/dist/chunks/{time-entry-actions-BeK6MEMU.js.map → time-entry-actions-ClC8tMau.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-entry-actions-BeK6MEMU.js","names":[],"sources":["../../src/models/service-snapshots.ts","../../src/services/action-principal.ts","../../src/services/service-evidence-service.ts","../../src/services/time-entry-actions.ts"],"sourcesContent":["import {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Refuse an amount that is not a JavaScript-safe integer number of minor\n * units. SQLite stores `19.99` in an INTEGER column without complaint and\n * PostgreSQL rejects it only at the driver, so the model boundary is the one\n * place both engines (and every writer: collections, `ServiceEvidenceService`,\n * resolvers) get the same answer. Sign is not constrained here.\n */\nexport function assertSnapshotMinorUnits(model: string, amount: unknown): void {\n if (typeof amount !== 'number' || !Number.isSafeInteger(amount)) {\n throw new ValidationError(\n `${model}.amount must be a safe integer number of minor units (cents) — ` +\n `got ${String(amount)}. $19.99 is 1999.`,\n 'VALIDATION_INVALID_VALUE',\n {\n fieldName: 'amount',\n value: amount,\n expectedType: 'safe integer minor units',\n },\n );\n }\n}\n\nconst chargeSnapshot = new WeakMap<ServiceChargeSnapshot, string>();\nconst compensationSnapshot = new WeakMap<ServiceCompensationSnapshot, string>();\n\nasync function immutableCommercialSnapshot(\n object: SmrtObject,\n fields: string[],\n): Promise<string | null> {\n if (!object.id) return null;\n try {\n const row = await object.db.get(object.tableName, { id: object.id });\n if (!row) return null;\n return JSON.stringify(\n Object.fromEntries(\n fields.map((key) => [\n key,\n row[key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)] ?? null,\n ]),\n ),\n );\n } catch {\n return null;\n }\n}\n\nfunction commercialSnapshot(\n object: Record<string, unknown>,\n fields: string[],\n): string {\n return JSON.stringify(\n Object.fromEntries(fields.map((key) => [key, object[key] ?? null])),\n );\n}\n\nconst CHARGE_FIELDS = [\n 'timeEntryId',\n 'amount',\n 'currency',\n 'pricingVersion',\n 'strategy',\n 'rateSnapshot',\n 'sourceChargeRef',\n];\nconst COMPENSATION_FIELDS = [\n 'timeEntryId',\n 'amount',\n 'currency',\n 'termsVersion',\n 'rateSnapshot',\n];\n\n/**\n * Immutable client-charge snapshot for one approved time entry. Money is\n * integer minor units, consistent with smrt-commerce (#2401).\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'service_charge_snapshots',\n // Moved from smrt-projects in #3288; stored references keep resolving.\n previousQualifiedNames: [\n '@happyvertical/smrt-projects:ServiceChargeSnapshot',\n ],\n conflictColumns: ['time_entry_id'],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class ServiceChargeSnapshot extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId({ nullable: true }) tenantId: string | null = null;\n @foreignKey('ServiceTimeEntry', { required: true }) timeEntryId: string = '';\n /**\n * Client charge in **integer minor units** of {@link currency} — `$19.99` is\n * `1999`. The `= 0` initializer is load-bearing: SMRT maps an integer literal\n * to INTEGER and a decimal literal to DECIMAL (#2401).\n *\n * Copied verbatim from the upstream `ClientCharge.amount`, so the two carry\n * the same unit by construction; a split would land cents in a major-units\n * column with no error anywhere.\n */\n amount: number = 0;\n @field({ type: 'text' }) currency: string = 'USD';\n @field({ type: 'text' }) pricingVersion: string = '';\n @field({ type: 'text' }) strategy: string = '';\n @field({ type: 'text' }) rateSnapshot: string = '{}';\n @field({ type: 'text' }) sourceChargeRef: string = '';\n createdAt: Date = new Date();\n\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved())\n chargeSnapshot.set(\n this,\n commercialSnapshot(\n this as unknown as Record<string, unknown>,\n CHARGE_FIELDS,\n ),\n );\n return this;\n }\n\n override async save(): Promise<this> {\n assertSnapshotMinorUnits('ServiceChargeSnapshot', this.amount);\n const current = commercialSnapshot(\n this as unknown as Record<string, unknown>,\n CHARGE_FIELDS,\n );\n const prior =\n (await immutableCommercialSnapshot(this, CHARGE_FIELDS)) ??\n chargeSnapshot.get(this) ??\n null;\n if (prior && prior !== current)\n throw new Error(\n 'ServiceChargeSnapshot is immutable; append an upstream adjustment instead.',\n );\n const result = (await super.save()) as this;\n chargeSnapshot.set(this, current);\n return result;\n }\n}\n\n/**\n * Immutable provider-compensation snapshot for one approved time entry,\n * kept separate from the client charge so the margin is `charge - compensation`.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'service_compensation_snapshots',\n // Moved from smrt-projects in #3288; stored references keep resolving.\n previousQualifiedNames: [\n '@happyvertical/smrt-projects:ServiceCompensationSnapshot',\n ],\n conflictColumns: ['time_entry_id'],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class ServiceCompensationSnapshot extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId({ nullable: true }) tenantId: string | null = null;\n @foreignKey('ServiceTimeEntry', { required: true }) timeEntryId: string = '';\n /**\n * Provider earning in **integer minor units** of {@link currency} (#2401).\n *\n * Converts with {@link ServiceChargeSnapshot.amount}: the delivery margin is\n * `charge - compensation`, so a mixed pair would make that subtraction\n * meaningless.\n */\n amount: number = 0;\n @field({ type: 'text' }) currency: string = 'USD';\n @field({ type: 'text' }) termsVersion: string = '';\n @field({ type: 'text' }) rateSnapshot: string = '{}';\n createdAt: Date = new Date();\n\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved())\n compensationSnapshot.set(\n this,\n commercialSnapshot(\n this as unknown as Record<string, unknown>,\n COMPENSATION_FIELDS,\n ),\n );\n return this;\n }\n\n override async save(): Promise<this> {\n assertSnapshotMinorUnits('ServiceCompensationSnapshot', this.amount);\n const current = commercialSnapshot(\n this as unknown as Record<string, unknown>,\n COMPENSATION_FIELDS,\n );\n const prior =\n (await immutableCommercialSnapshot(this, COMPENSATION_FIELDS)) ??\n compensationSnapshot.get(this) ??\n null;\n if (prior && prior !== current)\n throw new Error(\n 'ServiceCompensationSnapshot is immutable; create explicit settlement evidence instead.',\n );\n const result = (await super.save()) as this;\n compensationSnapshot.set(this, current);\n return result;\n }\n}\n\n/** Collection over `service_charge_snapshots`. */\n// Registered collection class: without its own surface it published\n// generated create/update/delete MCP tools, and a forged snapshot would make\n// approval skip pricing (#4001). Reads only, like the model.\n@smrt({ mcp: { include: ['list', 'get'] }, cli: false })\nexport class ServiceChargeSnapshotCollection extends SmrtCollection<ServiceChargeSnapshot> { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n static readonly _itemClass = ServiceChargeSnapshot;\n}\n/** Collection over `service_compensation_snapshots`. */\n// Registered collection class: without its own surface it published\n// generated create/update/delete MCP tools, and a forged snapshot would make\n// approval skip pricing (#4001). Reads only, like the model.\n@smrt({ mcp: { include: ['list', 'get'] }, cli: false })\nexport class ServiceCompensationSnapshotCollection extends SmrtCollection<ServiceCompensationSnapshot> { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n static readonly _itemClass = ServiceCompensationSnapshot;\n}\n","/**\n * The acting principal for the generated service-backed time entry actions\n * (#4001).\n *\n * Identity always comes from the ambient request context, never from the\n * request body: the tenancy ALS (`withTenant`, the SvelteKit/Express\n * adapters) first, then the authenticated smrt-users session-permission ALS\n * (`createSessionHandler()` without `enterTenantContext`). This is the\n * smrt-fields / smrt-preferences precedent. Authorization goes through\n * smrt-users' `assertOperationPermission()` against the principal's published\n * permission snapshot, so ceilings, super-admin and catalog presence behave\n * exactly as they do for every other guarded operation.\n */\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport {\n assertOperationPermission,\n getCurrentSessionPermissionContext,\n type OperationPermissionCollectionInput,\n OperationPermissionError,\n UserCollection,\n} from '@happyvertical/smrt-users';\n\n/** A refused action with a 4xx status the generated transports serialize. */\nexport class TimesheetActionError extends Error {\n readonly status: number;\n readonly code: string;\n readonly publicMessage: string;\n\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'TimesheetActionError';\n this.status = status;\n this.code = code;\n this.publicMessage = message;\n }\n}\n\n/** Who is acting, resolved from the ambient context. */\nexport interface TimesheetActionPrincipal {\n tenantId: string;\n userId: string;\n permissions: ReadonlySet<string>;\n /** smrt-profiles Profile linked to the user; stamped into audit fields. */\n profileId: string;\n /** True when the tenancy ALS already carries this principal. */\n fromTenancy: boolean;\n}\n\ninterface AmbientPrincipal {\n tenantId: string | null;\n userId: string | null;\n permissions: ReadonlySet<string>;\n profileId: string | null;\n fromTenancy: boolean;\n}\n\nfunction profileIdOf(user: unknown): string | null {\n if (!user || typeof user !== 'object') return null;\n const value = (user as { profileId?: unknown }).profileId;\n return typeof value === 'string' && value ? value : null;\n}\n\nfunction ambientPrincipal(): AmbientPrincipal {\n const tenant = getCurrentTenant();\n const session = getCurrentSessionPermissionContext();\n if (tenant?.tenantId) {\n // Generated SvelteKit routes enter `{ tenantId, userId, permissions }`\n // without the user object; the same person's session still carries it.\n const sessionProfile =\n session?.userId && session.userId === tenant.userId\n ? profileIdOf(session.user)\n : null;\n return {\n tenantId: tenant.tenantId,\n userId: tenant.userId ?? null,\n permissions: tenant.permissions,\n profileId: profileIdOf(tenant.user) ?? sessionProfile,\n fromTenancy: true,\n };\n }\n return {\n tenantId: session?.tenantId ?? null,\n userId: session?.userId ?? null,\n permissions: session?.permissionSet ?? new Set<string>(),\n profileId: profileIdOf(session?.user),\n fromTenancy: false,\n };\n}\n\nasync function loadProfileId(\n userId: string,\n options: SmrtClassOptions,\n): Promise<string | null> {\n try {\n const users = await UserCollection.create({\n db: options.db ?? options.persistence,\n } as SmrtClassOptions);\n return profileIdOf(await users.get({ id: userId }));\n } catch {\n return null;\n }\n}\n\n/** True when the ambient principal holds `<collection>.<action>`. */\nexport async function principalHolds(\n collection: OperationPermissionCollectionInput,\n action: string,\n options: SmrtClassOptions,\n): Promise<boolean> {\n const ambient = ambientPrincipal();\n try {\n await assertOperationPermission({\n collection,\n action,\n db: options.db ?? options.persistence,\n tenantId: ambient.tenantId,\n userId: ambient.userId,\n permissionSet: ambient.permissions,\n allowSystemContextBypass: false,\n allowSuperAdminBypass: false,\n });\n return true;\n } catch (error) {\n if (error instanceof OperationPermissionError) return false;\n throw error;\n }\n}\n\n/**\n * Authorize `<collection>.<action>` for the ambient principal and resolve the\n * profile it acts as. Fails closed (403) without an authenticated,\n * tenant-bound principal, without the permission, or without a linked\n * profile — an action always records who took it.\n *\n * System context never bypasses: trusted server code without a person calls\n * `ServiceEvidenceService` directly. `allowSuperAdminBypass: false` is for\n * money and separation-of-duties operations that need an explicit grant.\n */\nexport async function authorizeTimesheetAction(\n collection: OperationPermissionCollectionInput,\n action: string,\n options: SmrtClassOptions,\n guard: { allowSuperAdminBypass: boolean },\n): Promise<TimesheetActionPrincipal> {\n const ambient = ambientPrincipal();\n try {\n await assertOperationPermission({\n collection,\n action,\n db: options.db ?? options.persistence,\n tenantId: ambient.tenantId,\n userId: ambient.userId,\n permissionSet: ambient.permissions,\n allowSystemContextBypass: false,\n allowSuperAdminBypass: guard.allowSuperAdminBypass,\n });\n } catch (error) {\n if (error instanceof OperationPermissionError) {\n throw new TimesheetActionError(403, 'permission_denied', error.message);\n }\n throw error;\n }\n if (!ambient.tenantId || !ambient.userId) {\n throw new TimesheetActionError(\n 403,\n 'permission_denied',\n 'Time entry actions need an authenticated tenant context.',\n );\n }\n const profileId =\n ambient.profileId ?? (await loadProfileId(ambient.userId, options));\n if (!profileId) {\n throw new TimesheetActionError(\n 403,\n 'actor_profile_required',\n 'Time entry actions need a user with a linked profile.',\n );\n }\n return {\n tenantId: ambient.tenantId,\n userId: ambient.userId,\n permissions: ambient.permissions,\n profileId,\n fromTenancy: ambient.fromTenancy,\n };\n}\n\n/**\n * Run `fn` with the tenancy ALS carrying `principal`, so the tenancy\n * interceptor scopes every read and write. A session-only principal is\n * entered with the same tenant, user and permissions; nothing is widened.\n */\nexport async function withActionPrincipal<T>(\n principal: TimesheetActionPrincipal,\n fn: () => Promise<T>,\n): Promise<T> {\n if (principal.fromTenancy) return fn();\n return withTenant(\n {\n tenantId: principal.tenantId,\n userId: principal.userId,\n permissions: new Set(principal.permissions),\n },\n fn,\n );\n}\n","import type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n ServiceChargeSnapshotCollection,\n ServiceCompensationSnapshotCollection,\n} from '../models/service-snapshots.js';\nimport type { ServiceTimeEntry } from '../models/service-time-entry.js';\nimport {\n SERVICE_DURATION_HOURS_EVIDENCE,\n ServiceTimeEntryCollection,\n validateDurationHours,\n} from '../models/service-time-entry.js';\nimport type {\n ServiceEvidence,\n ServiceParticipantKind,\n ServiceTimeEntrySource,\n} from '../types.js';\n\n/**\n * Input for {@link ServiceEvidenceService.record}. The work reference is the\n * qualified class name and id of whatever the time was spent on.\n */\nexport interface RecordServiceTimeInput {\n tenantId?: string | null;\n workRefType?: string | null;\n workRefId?: string | null;\n participantKind: ServiceParticipantKind;\n participantProfileId?: string | null;\n agentRef?: string;\n source: ServiceTimeEntrySource;\n description: string;\n startedAt?: Date | null;\n endedAt?: Date | null;\n durationSeconds?: number | null;\n /** Exact positive decimal text; exclusive with seconds and timestamps. */\n durationHours?: string;\n evidence?: ServiceEvidence[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface CommercialSnapshot {\n /**\n * Integer minor units of `currency` — `$19.99` is `1999` (#2401).\n *\n * Written straight into `ServiceChargeSnapshot.amount` /\n * `ServiceCompensationSnapshot.amount`, INTEGER columns whose `save()`\n * refuses anything but a safe integer on every engine. A fractional\n * major-unit value (`19.99`) is rejected; a whole one (`150` for $150.00)\n * is not detectable and is stored 100× too small, so return minor units.\n */\n amount: number;\n currency?: string;\n version: string;\n strategy?: string;\n terms: Record<string, unknown>;\n sourceRef?: string;\n}\n\nexport interface ServiceCommercialResolver {\n /** Pure preflight: reject unsupported evidence before approval or snapshot writes. */\n validateEntry?(entry: ServiceTimeEntry): void | Promise<void>;\n priceClient(entry: ServiceTimeEntry): Promise<CommercialSnapshot>;\n compensateProvider(entry: ServiceTimeEntry): Promise<CommercialSnapshot>;\n}\n\n/**\n * Records, submits, approves, and corrects time entries. Approval writes one\n * immutable client-charge and one provider-compensation snapshot per entry;\n * a correction is a new entry pointing at the approved one, which flips to\n * `corrected` with its snapshots intact.\n */\nexport class ServiceEvidenceService {\n constructor(\n private readonly entries: ServiceTimeEntryCollection,\n private readonly charges: ServiceChargeSnapshotCollection,\n private readonly compensation: ServiceCompensationSnapshotCollection,\n private readonly commercial?: ServiceCommercialResolver,\n ) {}\n\n static async create(\n options: SmrtClassOptions = {},\n commercial?: ServiceCommercialResolver,\n ): Promise<ServiceEvidenceService> {\n const [entries, charges, compensation] = await Promise.all([\n ServiceTimeEntryCollection.create(options),\n ServiceChargeSnapshotCollection.create(options),\n ServiceCompensationSnapshotCollection.create(options),\n ]);\n return new ServiceEvidenceService(\n entries,\n charges,\n compensation,\n commercial,\n );\n }\n\n async record(input: RecordServiceTimeInput): Promise<ServiceTimeEntry> {\n if (!['timer', 'manual', 'import', 'agent'].includes(input.source))\n throw new Error('Unsupported service time source.');\n if (Boolean(input.workRefType) !== Boolean(input.workRefId))\n throw new Error('workRefType and workRefId must be provided together.');\n if (!(input.workRefType && input.workRefId))\n throw new Error('Service time requires a work reference.');\n if (input.participantKind === 'human' && !input.participantProfileId)\n throw new Error('Human service time requires participantProfileId.');\n if (input.participantKind === 'agent' && !input.agentRef)\n throw new Error('Agent service time requires agentRef.');\n if (input.source === 'timer' && (!input.startedAt || !input.endedAt))\n throw new Error('Timer service time requires startedAt and endedAt.');\n if (input.startedAt && input.endedAt && input.startedAt >= input.endedAt)\n throw new Error('Service time startedAt must be before endedAt.');\n const { durationHours, ...fields } = input;\n if (\n input.evidence?.some(\n (item) => item?.kind === SERVICE_DURATION_HOURS_EVIDENCE,\n )\n )\n throw new Error(\n 'Use durationHours to supply reserved decimal-hours evidence.',\n );\n const hours =\n durationHours === undefined ? null : validateDurationHours(durationHours);\n if (\n hours !== null &&\n (input.durationSeconds != null || input.startedAt || input.endedAt)\n )\n throw new Error(\n 'Decimal-hours-only service time cannot also supply seconds or timestamps.',\n );\n if (hours === null && input.durationSeconds === null)\n throw new Error('Null durationSeconds requires durationHours.');\n const duration =\n hours !== null\n ? null\n : (input.durationSeconds ??\n (input.startedAt && input.endedAt\n ? Math.round(\n (input.endedAt.getTime() - input.startedAt.getTime()) / 1000,\n )\n : 0));\n if (duration !== null && (!Number.isSafeInteger(duration) || duration <= 0))\n throw new Error(\n 'Service time durationSeconds must be a positive integer.',\n );\n const entry = await this.entries.create({\n ...fields,\n durationSeconds: duration,\n evidence: JSON.stringify([\n ...(input.evidence ?? []),\n ...(hours === null\n ? []\n : [{ kind: SERVICE_DURATION_HOURS_EVIDENCE, hours }]),\n ]),\n metadata: JSON.stringify(input.metadata ?? {}),\n status: 'draft',\n submittedAt: null,\n submittedByProfileId: null,\n approvedAt: null,\n approvedByProfileId: null,\n approvalPath: '',\n rejectedAt: null,\n rejectedByProfileId: null,\n rejectionReason: '',\n correctionOfId: null,\n });\n await entry.save();\n return entry;\n }\n\n async submit(\n entry: ServiceTimeEntry,\n profileId?: string,\n ): Promise<ServiceTimeEntry> {\n if (entry.status !== 'draft' && entry.status !== 'rejected')\n throw new Error('Only draft or rejected service time may be submitted.');\n entry.status = 'submitted';\n entry.submittedAt = new Date();\n entry.submittedByProfileId = profileId ?? null;\n return entry.save();\n }\n\n async approve(\n entry: ServiceTimeEntry,\n options: { actorProfileId?: string; approvalPath: string },\n ): Promise<ServiceTimeEntry> {\n if (entry.status !== 'submitted' && entry.status !== 'approved')\n throw new Error(\n 'Only submitted service time or an incomplete approval may be approved.',\n );\n if (!this.commercial)\n throw new Error(\n 'A ServiceCommercialResolver is required to approve service time.',\n );\n const timeEntryId = entry.id;\n if (!timeEntryId)\n throw new Error('ServiceTimeEntry must be saved before approval.');\n const existingCharge = (\n await this.charges.list({ where: { timeEntryId }, limit: 1 })\n )[0];\n const existingCompensation = (\n await this.compensation.list({ where: { timeEntryId }, limit: 1 })\n )[0];\n if (entry.status === 'approved' && existingCharge && existingCompensation)\n return entry;\n\n await this.commercial.validateEntry?.(entry);\n\n // Commit approval before either commercial resolver runs. The subscription\n // resolver creates an approved ClientCharge as part of priceClient(), so a\n // failed approval must never leave billable spend behind.\n if (entry.status === 'submitted') {\n entry.status = 'approved';\n entry.approvedAt = new Date();\n entry.approvedByProfileId = options.actorProfileId ?? null;\n entry.approvalPath = options.approvalPath;\n try {\n await entry.save();\n } catch (error) {\n const revisionConflict =\n error instanceof Error &&\n 'code' in error &&\n error.code === 'RUNTIME_REVISION_CONFLICT';\n if (!revisionConflict) throw error;\n const concurrent = await this.entries.get(timeEntryId);\n if (concurrent?.status !== 'approved') throw error;\n entry = concurrent;\n await this.commercial.validateEntry?.(entry);\n }\n }\n const provider = existingCompensation\n ? null\n : await this.commercial.compensateProvider(entry);\n if (provider) {\n const snapshotId = await deterministicSnapshotId(\n 'service-compensation-snapshot',\n timeEntryId,\n );\n try {\n await this.compensation.create({\n id: snapshotId,\n tenantId: entry.tenantId,\n timeEntryId,\n amount: provider.amount,\n currency: provider.currency ?? 'USD',\n termsVersion: provider.version,\n rateSnapshot: JSON.stringify(provider.terms),\n _insertOnly: true,\n });\n } catch (error) {\n const concurrent =\n (await this.compensation.get(snapshotId)) ??\n (\n await this.compensation.list({\n where: { timeEntryId },\n limit: 1,\n })\n )[0];\n if (!concurrent) throw error;\n }\n }\n const client = existingCharge\n ? null\n : await this.commercial.priceClient(entry);\n if (client) {\n const snapshotId = await deterministicSnapshotId(\n 'service-charge-snapshot',\n timeEntryId,\n );\n try {\n await this.charges.create({\n id: snapshotId,\n tenantId: entry.tenantId,\n timeEntryId,\n amount: client.amount,\n currency: client.currency ?? 'USD',\n pricingVersion: client.version,\n strategy: client.strategy ?? '',\n rateSnapshot: JSON.stringify(client.terms),\n sourceChargeRef: client.sourceRef ?? '',\n _insertOnly: true,\n });\n } catch (error) {\n const concurrent =\n (await this.charges.get(snapshotId)) ??\n (\n await this.charges.list({\n where: { timeEntryId },\n limit: 1,\n })\n )[0];\n if (!concurrent) throw error;\n }\n }\n return entry;\n }\n\n async correct(\n entry: ServiceTimeEntry,\n input: RecordServiceTimeInput,\n ): Promise<ServiceTimeEntry> {\n if (entry.status !== 'approved')\n throw new Error('Only approved service time may be corrected.');\n if (input.tenantId !== undefined && input.tenantId !== entry.tenantId) {\n throw new Error(\n 'Service time corrections must remain in the original tenant.',\n );\n }\n const correction = await this.record({\n ...input,\n tenantId: entry.tenantId,\n });\n correction.correctionOfId = entry.id ?? null;\n await correction.save();\n entry.status = 'corrected';\n await entry.save();\n return correction;\n }\n}\n\nasync function deterministicSnapshotId(\n kind: string,\n timeEntryId: string,\n): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify([kind, timeEntryId]));\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n const uuid = digest.slice(0, 16);\n uuid[6] = (uuid[6] & 0x0f) | 0x50;\n uuid[8] = (uuid[8] & 0x3f) | 0x80;\n const hex = Array.from(uuid, (byte) =>\n byte.toString(16).padStart(2, '0'),\n ).join('');\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20),\n ].join('-');\n}\n","/**\n * Service-backed write actions for `ServiceTimeEntry` (#4001).\n *\n * The generated CRUD writes stay closed: these are the only generated write\n * path, and each delegates to {@link ServiceEvidenceService}, so the status\n * machine, the submission/approval trail and the charge/compensation\n * snapshots are set in exactly one place. The thin collection methods in\n * `models/service-time-entry.ts` call in here.\n */\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n ServiceChargeSnapshotCollection,\n ServiceCompensationSnapshotCollection,\n} from '../models/service-snapshots.js';\nimport type {\n ServiceTimeEntry,\n ServiceTimeEntryCollection,\n} from '../models/service-time-entry.js';\nimport type { ServiceEvidence } from '../types.js';\nimport {\n authorizeTimesheetAction,\n principalHolds,\n TimesheetActionError,\n type TimesheetActionPrincipal,\n withActionPrincipal,\n} from './action-principal.js';\nimport {\n type ServiceCommercialResolver,\n ServiceEvidenceService,\n} from './service-evidence-service.js';\n\n/** Approval path stamped by the generated `approve` action. */\nexport const SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH = 'operator';\n\n/**\n * What `approve` prices with: a resolver, or a factory the action calls per\n * request with the collection's options. Use the factory whenever the\n * resolver writes (e.g. smrt-projects' `SubscriptionServiceCommercialResolver`\n * records client charges): `options.db` is the request-bound executor, so\n * those writes share the request's transaction and PostgreSQL RLS state\n * instead of a connection captured at startup.\n */\nexport type ServiceTimeEntryCommercialResolverSource =\n | ServiceCommercialResolver\n | ((context: {\n options: SmrtClassOptions;\n }) => ServiceCommercialResolver | Promise<ServiceCommercialResolver>);\n\nlet actionCommercialResolver: ServiceTimeEntryCommercialResolverSource | null =\n null;\n\n/**\n * Install what the generated `approve` action prices with. Pass `null` to\n * remove it. Without one, `approve` refuses with 409\n * (`approval_not_configured`) and writes nothing.\n *\n * @example\n * ```ts\n * setServiceTimeEntryCommercialResolver(({ options }) =>\n * SubscriptionServiceCommercialResolver.create(options, provider),\n * );\n * ```\n */\nexport function setServiceTimeEntryCommercialResolver(\n resolver: ServiceTimeEntryCommercialResolverSource | null,\n): void {\n actionCommercialResolver = resolver;\n}\n\n/** The source installed by {@link setServiceTimeEntryCommercialResolver}. */\nexport function getServiceTimeEntryCommercialResolver(): ServiceTimeEntryCommercialResolverSource | null {\n return actionCommercialResolver;\n}\n\n/** Body of the `record` action. Identity and tenant come from the session. */\nexport interface RecordServiceTimeActionInput {\n workRefType: string;\n workRefId: string;\n /** Defaults to the acting user's profile; another person needs `approve`. */\n participantProfileId?: string | null;\n /** `manual` (default) or `timer`; imports and agents use the service. */\n source?: 'manual' | 'timer';\n description?: string;\n /** ISO 8601 instant. */\n startedAt?: string | null;\n /** ISO 8601 instant. */\n endedAt?: string | null;\n durationSeconds?: number | null;\n /** Exact positive decimal text; exclusive with seconds and timestamps. */\n durationHours?: string;\n evidence?: ServiceEvidence[];\n metadata?: Record<string, unknown>;\n}\n\n/** Body of the `submit` and `approve` actions. */\nexport interface ServiceTimeEntryActionTarget {\n entryId: string;\n}\n\ntype ActionOptions = SmrtClassOptions;\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'The action body must be a JSON object.',\n );\n }\n return value as Record<string, unknown>;\n}\n\nfunction optionalString(\n body: Record<string, unknown>,\n key: string,\n): string | undefined {\n const value = body[key];\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'string') {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n `${key} must be a string.`,\n );\n }\n return value;\n}\n\nfunction requiredString(body: Record<string, unknown>, key: string): string {\n const value = optionalString(body, key)?.trim();\n if (!value) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n `${key} is required.`,\n );\n }\n return value;\n}\n\nfunction optionalInstant(\n body: Record<string, unknown>,\n key: string,\n): Date | null {\n const value = optionalString(body, key);\n if (value === undefined) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n `${key} must be an ISO 8601 instant.`,\n );\n }\n return date;\n}\n\n/**\n * Plain `Error`s from {@link ServiceEvidenceService} are its own input and\n * state preconditions; surface them as 400 instead of an opaque 500. Typed\n * errors (database, revision conflicts) pass through unchanged.\n */\nasync function serviceCall<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (error instanceof Error && error.constructor === Error) {\n throw new TimesheetActionError(400, 'invalid_request', error.message);\n }\n throw error;\n }\n}\n\nasync function evidenceService(\n entries: ServiceTimeEntryCollection,\n commercial?: ServiceCommercialResolver,\n): Promise<ServiceEvidenceService> {\n const options = entries.options;\n const [charges, compensation] = await Promise.all([\n ServiceChargeSnapshotCollection.create(options),\n ServiceCompensationSnapshotCollection.create(options),\n ]);\n return new ServiceEvidenceService(entries, charges, compensation, commercial);\n}\n\nfunction itemClass(entries: ServiceTimeEntryCollection) {\n return (entries.constructor as unknown as { _itemClass: unknown })\n ._itemClass as typeof ServiceTimeEntry;\n}\n\nasync function loadEntry(\n entries: ServiceTimeEntryCollection,\n principal: TimesheetActionPrincipal,\n body: Record<string, unknown>,\n): Promise<ServiceTimeEntry> {\n const entryId = requiredString(body, 'entryId');\n const entry = await entries.get({ id: entryId });\n // Optional tenancy also admits global (NULL-tenant) rows in a tenant\n // context; a write must land on the principal's own tenant only.\n if (!entry || (entry.tenantId ?? null) !== principal.tenantId) {\n throw new TimesheetActionError(\n 404,\n 'not_found',\n `Service time entry ${entryId} was not found.`,\n );\n }\n return entry;\n}\n\n/** `record`: create a draft entry through `ServiceEvidenceService.record()`. */\nexport async function recordServiceTimeAction(\n entries: ServiceTimeEntryCollection,\n input: unknown,\n): Promise<ServiceTimeEntry> {\n const body = asRecord(input);\n const principal = await authorizeTimesheetAction(\n itemClass(entries),\n 'record',\n entries.options as ActionOptions,\n { allowSuperAdminBypass: true },\n );\n const participantProfileId =\n optionalString(body, 'participantProfileId')?.trim() || principal.profileId;\n if (\n participantProfileId !== principal.profileId &&\n !(await principalHolds(itemClass(entries), 'approve', entries.options))\n ) {\n throw new TimesheetActionError(\n 403,\n 'permission_denied',\n \"Recording another person's time needs the approve permission.\",\n );\n }\n const source = optionalString(body, 'source') ?? 'manual';\n if (source !== 'manual' && source !== 'timer') {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n \"source must be 'manual' or 'timer'.\",\n );\n }\n const durationSeconds = body.durationSeconds;\n if (\n durationSeconds !== undefined &&\n durationSeconds !== null &&\n typeof durationSeconds !== 'number'\n ) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'durationSeconds must be a number.',\n );\n }\n const evidence = body.evidence;\n if (evidence !== undefined && !Array.isArray(evidence)) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'evidence must be an array.',\n );\n }\n const metadata = body.metadata;\n if (\n metadata !== undefined &&\n (metadata === null ||\n typeof metadata !== 'object' ||\n Array.isArray(metadata))\n ) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'metadata must be an object.',\n );\n }\n return withActionPrincipal(principal, async () => {\n const service = await evidenceService(entries);\n return serviceCall(() =>\n service.record({\n tenantId: principal.tenantId,\n workRefType: requiredString(body, 'workRefType'),\n workRefId: requiredString(body, 'workRefId'),\n participantKind: 'human',\n participantProfileId,\n source,\n description: optionalString(body, 'description') ?? '',\n startedAt: optionalInstant(body, 'startedAt'),\n endedAt: optionalInstant(body, 'endedAt'),\n ...(durationSeconds !== undefined\n ? { durationSeconds: durationSeconds as number | null }\n : {}),\n ...(body.durationHours !== undefined\n ? { durationHours: optionalString(body, 'durationHours') }\n : {}),\n ...(evidence ? { evidence: evidence as ServiceEvidence[] } : {}),\n ...(metadata ? { metadata: metadata as Record<string, unknown> } : {}),\n }),\n );\n });\n}\n\n/**\n * `submit`: `draft | rejected → submitted` through\n * `ServiceEvidenceService.submit()`, stamping `submittedAt` and the acting\n * profile. Only the entry's participant may submit it, unless the caller\n * holds `approve` (a supervisor submitting on someone's behalf).\n */\nexport async function submitServiceTimeAction(\n entries: ServiceTimeEntryCollection,\n input: unknown,\n): Promise<ServiceTimeEntry> {\n const body = asRecord(input);\n const principal = await authorizeTimesheetAction(\n itemClass(entries),\n 'submit',\n entries.options,\n { allowSuperAdminBypass: true },\n );\n return withActionPrincipal(principal, async () => {\n const entry = await loadEntry(entries, principal, body);\n if (\n entry.participantProfileId !== principal.profileId &&\n !(await principalHolds(itemClass(entries), 'approve', entries.options))\n ) {\n throw new TimesheetActionError(\n 403,\n 'permission_denied',\n \"Submitting another person's time needs the approve permission.\",\n );\n }\n const service = await evidenceService(entries);\n return serviceCall(() => service.submit(entry, principal.profileId));\n });\n}\n\n/**\n * `approve`: `submitted → approved` through `ServiceEvidenceService.approve()`,\n * which stamps `approvedAt`, the acting profile and the approval path, then\n * writes the immutable charge and compensation snapshots through the\n * installed {@link setServiceTimeEntryCommercialResolver resolver} (a factory\n * is called per request with the collection's options). Money:\n * super-admin bypass does not apply, the grant must be explicit.\n */\nexport async function approveServiceTimeAction(\n entries: ServiceTimeEntryCollection,\n input: unknown,\n): Promise<ServiceTimeEntry> {\n const body = asRecord(input);\n const principal = await authorizeTimesheetAction(\n itemClass(entries),\n 'approve',\n entries.options,\n { allowSuperAdminBypass: false },\n );\n const source = actionCommercialResolver;\n if (!source) {\n throw new TimesheetActionError(\n // A 4xx so generated transports return the message; the host has not\n // installed the pricing the approval needs.\n 409,\n 'approval_not_configured',\n 'No ServiceCommercialResolver is installed for time entry approval.',\n );\n }\n return withActionPrincipal(principal, async () => {\n const entry = await loadEntry(entries, principal, body);\n // Built per request inside the principal's tenancy context, on the same\n // options (and so the same request-bound executor) as the entries.\n const commercial =\n typeof source === 'function'\n ? await source({ options: entries.options })\n : source;\n const service = await evidenceService(entries, commercial);\n return serviceCall(() =>\n service.approve(entry, {\n actorProfileId: principal.profileId,\n approvalPath: SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH,\n }),\n );\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"time-entry-actions-ClC8tMau.js","names":[],"sources":["../../src/models/service-snapshots.ts","../../src/services/action-principal.ts","../../src/services/service-evidence-service.ts","../../src/services/time-entry-actions.ts"],"sourcesContent":["import {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Refuse an amount that is not a JavaScript-safe integer number of minor\n * units. SQLite stores `19.99` in an INTEGER column without complaint and\n * PostgreSQL rejects it only at the driver, so the model boundary is the one\n * place both engines (and every writer: collections, `ServiceEvidenceService`,\n * resolvers) get the same answer. Sign is not constrained here.\n */\nexport function assertSnapshotMinorUnits(model: string, amount: unknown): void {\n if (typeof amount !== 'number' || !Number.isSafeInteger(amount)) {\n throw new ValidationError(\n `${model}.amount must be a safe integer number of minor units (cents) — ` +\n `got ${String(amount)}. $19.99 is 1999.`,\n 'VALIDATION_INVALID_VALUE',\n {\n fieldName: 'amount',\n value: amount,\n expectedType: 'safe integer minor units',\n },\n );\n }\n}\n\nconst chargeSnapshot = new WeakMap<ServiceChargeSnapshot, string>();\nconst compensationSnapshot = new WeakMap<ServiceCompensationSnapshot, string>();\n\nasync function immutableCommercialSnapshot(\n object: SmrtObject,\n fields: string[],\n): Promise<string | null> {\n if (!object.id) return null;\n try {\n const row = await object.db.get(object.tableName, { id: object.id });\n if (!row) return null;\n return JSON.stringify(\n Object.fromEntries(\n fields.map((key) => [\n key,\n row[key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)] ?? null,\n ]),\n ),\n );\n } catch {\n return null;\n }\n}\n\nfunction commercialSnapshot(\n object: Record<string, unknown>,\n fields: string[],\n): string {\n return JSON.stringify(\n Object.fromEntries(fields.map((key) => [key, object[key] ?? null])),\n );\n}\n\nconst CHARGE_FIELDS = [\n 'timeEntryId',\n 'amount',\n 'currency',\n 'pricingVersion',\n 'strategy',\n 'rateSnapshot',\n 'sourceChargeRef',\n];\nconst COMPENSATION_FIELDS = [\n 'timeEntryId',\n 'amount',\n 'currency',\n 'termsVersion',\n 'rateSnapshot',\n];\n\n/**\n * Immutable client-charge snapshot for one approved time entry. Money is\n * integer minor units, consistent with smrt-commerce (#2401).\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'service_charge_snapshots',\n // Moved from smrt-projects in #3288; stored references keep resolving.\n previousQualifiedNames: [\n '@happyvertical/smrt-projects:ServiceChargeSnapshot',\n ],\n conflictColumns: ['time_entry_id'],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class ServiceChargeSnapshot extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId({ nullable: true }) tenantId: string | null = null;\n @foreignKey('ServiceTimeEntry', { required: true }) timeEntryId: string = '';\n /**\n * Client charge in **integer minor units** of {@link currency} — `$19.99` is\n * `1999`. The `= 0` initializer is load-bearing: SMRT maps an integer literal\n * to INTEGER and a decimal literal to DECIMAL (#2401).\n *\n * Copied verbatim from the upstream `ClientCharge.amount`, so the two carry\n * the same unit by construction; a split would land cents in a major-units\n * column with no error anywhere.\n */\n amount: number = 0;\n @field({ type: 'text' }) currency: string = 'USD';\n @field({ type: 'text' }) pricingVersion: string = '';\n @field({ type: 'text' }) strategy: string = '';\n @field({ type: 'text' }) rateSnapshot: string = '{}';\n @field({ type: 'text' }) sourceChargeRef: string = '';\n createdAt: Date = new Date();\n\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved())\n chargeSnapshot.set(\n this,\n commercialSnapshot(\n this as unknown as Record<string, unknown>,\n CHARGE_FIELDS,\n ),\n );\n return this;\n }\n\n override async save(): Promise<this> {\n assertSnapshotMinorUnits('ServiceChargeSnapshot', this.amount);\n const current = commercialSnapshot(\n this as unknown as Record<string, unknown>,\n CHARGE_FIELDS,\n );\n const prior =\n (await immutableCommercialSnapshot(this, CHARGE_FIELDS)) ??\n chargeSnapshot.get(this) ??\n null;\n if (prior && prior !== current)\n throw new Error(\n 'ServiceChargeSnapshot is immutable; append an upstream adjustment instead.',\n );\n const result = (await super.save()) as this;\n chargeSnapshot.set(this, current);\n return result;\n }\n}\n\n/**\n * Immutable provider-compensation snapshot for one approved time entry,\n * kept separate from the client charge so the margin is `charge - compensation`.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'service_compensation_snapshots',\n // Moved from smrt-projects in #3288; stored references keep resolving.\n previousQualifiedNames: [\n '@happyvertical/smrt-projects:ServiceCompensationSnapshot',\n ],\n conflictColumns: ['time_entry_id'],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class ServiceCompensationSnapshot extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId({ nullable: true }) tenantId: string | null = null;\n @foreignKey('ServiceTimeEntry', { required: true }) timeEntryId: string = '';\n /**\n * Provider earning in **integer minor units** of {@link currency} (#2401).\n *\n * Converts with {@link ServiceChargeSnapshot.amount}: the delivery margin is\n * `charge - compensation`, so a mixed pair would make that subtraction\n * meaningless.\n */\n amount: number = 0;\n @field({ type: 'text' }) currency: string = 'USD';\n @field({ type: 'text' }) termsVersion: string = '';\n @field({ type: 'text' }) rateSnapshot: string = '{}';\n createdAt: Date = new Date();\n\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved())\n compensationSnapshot.set(\n this,\n commercialSnapshot(\n this as unknown as Record<string, unknown>,\n COMPENSATION_FIELDS,\n ),\n );\n return this;\n }\n\n override async save(): Promise<this> {\n assertSnapshotMinorUnits('ServiceCompensationSnapshot', this.amount);\n const current = commercialSnapshot(\n this as unknown as Record<string, unknown>,\n COMPENSATION_FIELDS,\n );\n const prior =\n (await immutableCommercialSnapshot(this, COMPENSATION_FIELDS)) ??\n compensationSnapshot.get(this) ??\n null;\n if (prior && prior !== current)\n throw new Error(\n 'ServiceCompensationSnapshot is immutable; create explicit settlement evidence instead.',\n );\n const result = (await super.save()) as this;\n compensationSnapshot.set(this, current);\n return result;\n }\n}\n\n/** Collection over `service_charge_snapshots`. */\n// Registered collection class: without its own surface it published\n// generated create/update/delete MCP tools, and a forged snapshot would make\n// approval skip pricing (#4001). Reads only, like the model.\n@smrt({ mcp: { include: ['list', 'get'] }, cli: false })\nexport class ServiceChargeSnapshotCollection extends SmrtCollection<ServiceChargeSnapshot> { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n static readonly _itemClass = ServiceChargeSnapshot;\n}\n/** Collection over `service_compensation_snapshots`. */\n// Registered collection class: without its own surface it published\n// generated create/update/delete MCP tools, and a forged snapshot would make\n// approval skip pricing (#4001). Reads only, like the model.\n@smrt({ mcp: { include: ['list', 'get'] }, cli: false })\nexport class ServiceCompensationSnapshotCollection extends SmrtCollection<ServiceCompensationSnapshot> { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n static readonly _itemClass = ServiceCompensationSnapshot;\n}\n","/**\n * The acting principal for the generated service-backed time entry actions\n * (#4001).\n *\n * Identity always comes from the ambient request context, never from the\n * request body: the tenancy ALS (`withTenant`, the SvelteKit/Express\n * adapters) first, then the authenticated smrt-users session-permission ALS\n * (`createSessionHandler()` without `enterTenantContext`). This is the\n * smrt-fields / smrt-preferences precedent. Authorization goes through\n * smrt-users' `assertOperationPermission()` against the principal's published\n * permission snapshot, so ceilings, super-admin and catalog presence behave\n * exactly as they do for every other guarded operation.\n */\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport {\n assertOperationPermission,\n getCurrentSessionPermissionContext,\n type OperationPermissionCollectionInput,\n OperationPermissionError,\n UserCollection,\n} from '@happyvertical/smrt-users';\n\n/** A refused action with a 4xx status the generated transports serialize. */\nexport class TimesheetActionError extends Error {\n readonly status: number;\n readonly code: string;\n readonly publicMessage: string;\n\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'TimesheetActionError';\n this.status = status;\n this.code = code;\n this.publicMessage = message;\n }\n}\n\n/** Who is acting, resolved from the ambient context. */\nexport interface TimesheetActionPrincipal {\n tenantId: string;\n userId: string;\n permissions: ReadonlySet<string>;\n /** smrt-profiles Profile linked to the user; stamped into audit fields. */\n profileId: string;\n /** True when the tenancy ALS already carries this principal. */\n fromTenancy: boolean;\n}\n\ninterface AmbientPrincipal {\n tenantId: string | null;\n userId: string | null;\n permissions: ReadonlySet<string>;\n profileId: string | null;\n fromTenancy: boolean;\n}\n\nfunction profileIdOf(user: unknown): string | null {\n if (!user || typeof user !== 'object') return null;\n const value = (user as { profileId?: unknown }).profileId;\n return typeof value === 'string' && value ? value : null;\n}\n\nfunction ambientPrincipal(): AmbientPrincipal {\n const tenant = getCurrentTenant();\n const session = getCurrentSessionPermissionContext();\n if (tenant?.tenantId) {\n // Generated SvelteKit routes enter `{ tenantId, userId, permissions }`\n // without the user object; the same person's session still carries it.\n const sessionProfile =\n session?.userId && session.userId === tenant.userId\n ? profileIdOf(session.user)\n : null;\n return {\n tenantId: tenant.tenantId,\n userId: tenant.userId ?? null,\n permissions: tenant.permissions,\n profileId: profileIdOf(tenant.user) ?? sessionProfile,\n fromTenancy: true,\n };\n }\n return {\n tenantId: session?.tenantId ?? null,\n userId: session?.userId ?? null,\n permissions: session?.permissionSet ?? new Set<string>(),\n profileId: profileIdOf(session?.user),\n fromTenancy: false,\n };\n}\n\nasync function loadProfileId(\n userId: string,\n options: SmrtClassOptions,\n): Promise<string | null> {\n try {\n const users = await UserCollection.create({\n db: options.db ?? options.persistence,\n } as SmrtClassOptions);\n return profileIdOf(await users.get({ id: userId }));\n } catch {\n return null;\n }\n}\n\n/** True when the ambient principal holds `<collection>.<action>`. */\nexport async function principalHolds(\n collection: OperationPermissionCollectionInput,\n action: string,\n options: SmrtClassOptions,\n): Promise<boolean> {\n const ambient = ambientPrincipal();\n try {\n await assertOperationPermission({\n collection,\n action,\n db: options.db ?? options.persistence,\n tenantId: ambient.tenantId,\n userId: ambient.userId,\n permissionSet: ambient.permissions,\n allowSystemContextBypass: false,\n allowSuperAdminBypass: false,\n });\n return true;\n } catch (error) {\n if (error instanceof OperationPermissionError) return false;\n throw error;\n }\n}\n\n/**\n * Authorize `<collection>.<action>` for the ambient principal and resolve the\n * profile it acts as. Fails closed (403) without an authenticated,\n * tenant-bound principal, without the permission, or without a linked\n * profile — an action always records who took it.\n *\n * System context never bypasses: trusted server code without a person calls\n * `ServiceEvidenceService` directly. `allowSuperAdminBypass: false` is for\n * money and separation-of-duties operations that need an explicit grant.\n */\nexport async function authorizeTimesheetAction(\n collection: OperationPermissionCollectionInput,\n action: string,\n options: SmrtClassOptions,\n guard: { allowSuperAdminBypass: boolean },\n): Promise<TimesheetActionPrincipal> {\n const ambient = ambientPrincipal();\n try {\n await assertOperationPermission({\n collection,\n action,\n db: options.db ?? options.persistence,\n tenantId: ambient.tenantId,\n userId: ambient.userId,\n permissionSet: ambient.permissions,\n allowSystemContextBypass: false,\n allowSuperAdminBypass: guard.allowSuperAdminBypass,\n });\n } catch (error) {\n if (error instanceof OperationPermissionError) {\n throw new TimesheetActionError(403, 'permission_denied', error.message);\n }\n throw error;\n }\n if (!ambient.tenantId || !ambient.userId) {\n throw new TimesheetActionError(\n 403,\n 'permission_denied',\n 'Time entry actions need an authenticated tenant context.',\n );\n }\n const profileId =\n ambient.profileId ?? (await loadProfileId(ambient.userId, options));\n if (!profileId) {\n throw new TimesheetActionError(\n 403,\n 'actor_profile_required',\n 'Time entry actions need a user with a linked profile.',\n );\n }\n return {\n tenantId: ambient.tenantId,\n userId: ambient.userId,\n permissions: ambient.permissions,\n profileId,\n fromTenancy: ambient.fromTenancy,\n };\n}\n\n/**\n * Run `fn` with the tenancy ALS carrying `principal`, so the tenancy\n * interceptor scopes every read and write. A session-only principal is\n * entered with the same tenant, user and permissions; nothing is widened.\n */\nexport async function withActionPrincipal<T>(\n principal: TimesheetActionPrincipal,\n fn: () => Promise<T>,\n): Promise<T> {\n if (principal.fromTenancy) return fn();\n return withTenant(\n {\n tenantId: principal.tenantId,\n userId: principal.userId,\n permissions: new Set(principal.permissions),\n },\n fn,\n );\n}\n","import type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n ServiceChargeSnapshotCollection,\n ServiceCompensationSnapshotCollection,\n} from '../models/service-snapshots.js';\nimport type { ServiceTimeEntry } from '../models/service-time-entry.js';\nimport {\n SERVICE_DURATION_HOURS_EVIDENCE,\n ServiceTimeEntryCollection,\n validateDurationHours,\n} from '../models/service-time-entry.js';\nimport type {\n ServiceEvidence,\n ServiceParticipantKind,\n ServiceTimeEntrySource,\n} from '../types.js';\n\n/**\n * Input for {@link ServiceEvidenceService.record}. The work reference is the\n * qualified class name and id of whatever the time was spent on.\n */\nexport interface RecordServiceTimeInput {\n tenantId?: string | null;\n workRefType?: string | null;\n workRefId?: string | null;\n participantKind: ServiceParticipantKind;\n participantProfileId?: string | null;\n agentRef?: string;\n source: ServiceTimeEntrySource;\n description: string;\n startedAt?: Date | null;\n endedAt?: Date | null;\n durationSeconds?: number | null;\n /** Exact positive decimal text; exclusive with seconds and timestamps. */\n durationHours?: string;\n evidence?: ServiceEvidence[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface CommercialSnapshot {\n /**\n * Integer minor units of `currency` — `$19.99` is `1999` (#2401).\n *\n * Written straight into `ServiceChargeSnapshot.amount` /\n * `ServiceCompensationSnapshot.amount`, INTEGER columns whose `save()`\n * refuses anything but a safe integer on every engine. A fractional\n * major-unit value (`19.99`) is rejected; a whole one (`150` for $150.00)\n * is not detectable and is stored 100× too small, so return minor units.\n */\n amount: number;\n currency?: string;\n version: string;\n strategy?: string;\n terms: Record<string, unknown>;\n sourceRef?: string;\n}\n\nexport interface ServiceCommercialResolver {\n /** Pure preflight: reject unsupported evidence before approval or snapshot writes. */\n validateEntry?(entry: ServiceTimeEntry): void | Promise<void>;\n priceClient(entry: ServiceTimeEntry): Promise<CommercialSnapshot>;\n compensateProvider(entry: ServiceTimeEntry): Promise<CommercialSnapshot>;\n}\n\n/**\n * Records, submits, approves, and corrects time entries. Approval writes one\n * immutable client-charge and one provider-compensation snapshot per entry;\n * a correction is a new entry pointing at the approved one, which flips to\n * `corrected` with its snapshots intact.\n */\nexport class ServiceEvidenceService {\n constructor(\n private readonly entries: ServiceTimeEntryCollection,\n private readonly charges: ServiceChargeSnapshotCollection,\n private readonly compensation: ServiceCompensationSnapshotCollection,\n private readonly commercial?: ServiceCommercialResolver,\n ) {}\n\n static async create(\n options: SmrtClassOptions = {},\n commercial?: ServiceCommercialResolver,\n ): Promise<ServiceEvidenceService> {\n const [entries, charges, compensation] = await Promise.all([\n ServiceTimeEntryCollection.create(options),\n ServiceChargeSnapshotCollection.create(options),\n ServiceCompensationSnapshotCollection.create(options),\n ]);\n return new ServiceEvidenceService(\n entries,\n charges,\n compensation,\n commercial,\n );\n }\n\n async record(input: RecordServiceTimeInput): Promise<ServiceTimeEntry> {\n if (!['timer', 'manual', 'import', 'agent'].includes(input.source))\n throw new Error('Unsupported service time source.');\n if (Boolean(input.workRefType) !== Boolean(input.workRefId))\n throw new Error('workRefType and workRefId must be provided together.');\n if (!(input.workRefType && input.workRefId))\n throw new Error('Service time requires a work reference.');\n if (input.participantKind === 'human' && !input.participantProfileId)\n throw new Error('Human service time requires participantProfileId.');\n if (input.participantKind === 'agent' && !input.agentRef)\n throw new Error('Agent service time requires agentRef.');\n if (input.source === 'timer' && (!input.startedAt || !input.endedAt))\n throw new Error('Timer service time requires startedAt and endedAt.');\n if (input.startedAt && input.endedAt && input.startedAt >= input.endedAt)\n throw new Error('Service time startedAt must be before endedAt.');\n const { durationHours, ...fields } = input;\n if (\n input.evidence?.some(\n (item) => item?.kind === SERVICE_DURATION_HOURS_EVIDENCE,\n )\n )\n throw new Error(\n 'Use durationHours to supply reserved decimal-hours evidence.',\n );\n const hours =\n durationHours === undefined ? null : validateDurationHours(durationHours);\n if (\n hours !== null &&\n (input.durationSeconds != null || input.startedAt || input.endedAt)\n )\n throw new Error(\n 'Decimal-hours-only service time cannot also supply seconds or timestamps.',\n );\n if (hours === null && input.durationSeconds === null)\n throw new Error('Null durationSeconds requires durationHours.');\n const duration =\n hours !== null\n ? null\n : (input.durationSeconds ??\n (input.startedAt && input.endedAt\n ? Math.round(\n (input.endedAt.getTime() - input.startedAt.getTime()) / 1000,\n )\n : 0));\n if (duration !== null && (!Number.isSafeInteger(duration) || duration <= 0))\n throw new Error(\n 'Service time durationSeconds must be a positive integer.',\n );\n const entry = await this.entries.create({\n ...fields,\n durationSeconds: duration,\n evidence: JSON.stringify([\n ...(input.evidence ?? []),\n ...(hours === null\n ? []\n : [{ kind: SERVICE_DURATION_HOURS_EVIDENCE, hours }]),\n ]),\n metadata: JSON.stringify(input.metadata ?? {}),\n status: 'draft',\n submittedAt: null,\n submittedByProfileId: null,\n approvedAt: null,\n approvedByProfileId: null,\n approvalPath: '',\n rejectedAt: null,\n rejectedByProfileId: null,\n rejectionReason: '',\n correctionOfId: null,\n });\n await entry.save();\n return entry;\n }\n\n async submit(\n entry: ServiceTimeEntry,\n profileId?: string,\n ): Promise<ServiceTimeEntry> {\n if (entry.status !== 'draft' && entry.status !== 'rejected')\n throw new Error('Only draft or rejected service time may be submitted.');\n entry.status = 'submitted';\n entry.submittedAt = new Date();\n entry.submittedByProfileId = profileId ?? null;\n return entry.save();\n }\n\n async approve(\n entry: ServiceTimeEntry,\n options: { actorProfileId?: string; approvalPath: string },\n ): Promise<ServiceTimeEntry> {\n if (entry.status !== 'submitted' && entry.status !== 'approved')\n throw new Error(\n 'Only submitted service time or an incomplete approval may be approved.',\n );\n if (!this.commercial)\n throw new Error(\n 'A ServiceCommercialResolver is required to approve service time.',\n );\n const timeEntryId = entry.id;\n if (!timeEntryId)\n throw new Error('ServiceTimeEntry must be saved before approval.');\n const existingCharge = (\n await this.charges.list({ where: { timeEntryId }, limit: 1 })\n )[0];\n const existingCompensation = (\n await this.compensation.list({ where: { timeEntryId }, limit: 1 })\n )[0];\n if (entry.status === 'approved' && existingCharge && existingCompensation)\n return entry;\n\n await this.commercial.validateEntry?.(entry);\n\n // Commit approval before either commercial resolver runs. The subscription\n // resolver creates an approved ClientCharge as part of priceClient(), so a\n // failed approval must never leave billable spend behind.\n if (entry.status === 'submitted') {\n entry.status = 'approved';\n entry.approvedAt = new Date();\n entry.approvedByProfileId = options.actorProfileId ?? null;\n entry.approvalPath = options.approvalPath;\n try {\n await entry.save();\n } catch (error) {\n const revisionConflict =\n error instanceof Error &&\n 'code' in error &&\n error.code === 'RUNTIME_REVISION_CONFLICT';\n if (!revisionConflict) throw error;\n const concurrent = await this.entries.get(timeEntryId);\n if (concurrent?.status !== 'approved') throw error;\n entry = concurrent;\n await this.commercial.validateEntry?.(entry);\n }\n }\n const provider = existingCompensation\n ? null\n : await this.commercial.compensateProvider(entry);\n if (provider) {\n const snapshotId = await deterministicSnapshotId(\n 'service-compensation-snapshot',\n timeEntryId,\n );\n try {\n await this.compensation.create({\n id: snapshotId,\n tenantId: entry.tenantId,\n timeEntryId,\n amount: provider.amount,\n currency: provider.currency ?? 'USD',\n termsVersion: provider.version,\n rateSnapshot: JSON.stringify(provider.terms),\n _insertOnly: true,\n });\n } catch (error) {\n const concurrent =\n (await this.compensation.get(snapshotId)) ??\n (\n await this.compensation.list({\n where: { timeEntryId },\n limit: 1,\n })\n )[0];\n if (!concurrent) throw error;\n }\n }\n const client = existingCharge\n ? null\n : await this.commercial.priceClient(entry);\n if (client) {\n const snapshotId = await deterministicSnapshotId(\n 'service-charge-snapshot',\n timeEntryId,\n );\n try {\n await this.charges.create({\n id: snapshotId,\n tenantId: entry.tenantId,\n timeEntryId,\n amount: client.amount,\n currency: client.currency ?? 'USD',\n pricingVersion: client.version,\n strategy: client.strategy ?? '',\n rateSnapshot: JSON.stringify(client.terms),\n sourceChargeRef: client.sourceRef ?? '',\n _insertOnly: true,\n });\n } catch (error) {\n const concurrent =\n (await this.charges.get(snapshotId)) ??\n (\n await this.charges.list({\n where: { timeEntryId },\n limit: 1,\n })\n )[0];\n if (!concurrent) throw error;\n }\n }\n return entry;\n }\n\n async correct(\n entry: ServiceTimeEntry,\n input: RecordServiceTimeInput,\n ): Promise<ServiceTimeEntry> {\n if (entry.status !== 'approved')\n throw new Error('Only approved service time may be corrected.');\n if (input.tenantId !== undefined && input.tenantId !== entry.tenantId) {\n throw new Error(\n 'Service time corrections must remain in the original tenant.',\n );\n }\n const correction = await this.record({\n ...input,\n tenantId: entry.tenantId,\n });\n correction.correctionOfId = entry.id ?? null;\n await correction.save();\n entry.status = 'corrected';\n await entry.save();\n return correction;\n }\n}\n\nasync function deterministicSnapshotId(\n kind: string,\n timeEntryId: string,\n): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify([kind, timeEntryId]));\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n const uuid = digest.slice(0, 16);\n uuid[6] = (uuid[6] & 0x0f) | 0x50;\n uuid[8] = (uuid[8] & 0x3f) | 0x80;\n const hex = Array.from(uuid, (byte) =>\n byte.toString(16).padStart(2, '0'),\n ).join('');\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20),\n ].join('-');\n}\n","/**\n * Service-backed write actions for `ServiceTimeEntry` (#4001).\n *\n * The generated CRUD writes stay closed: these are the only generated write\n * path, and each delegates to {@link ServiceEvidenceService}, so the status\n * machine, the submission/approval trail and the charge/compensation\n * snapshots are set in exactly one place. The thin collection methods in\n * `models/service-time-entry.ts` call in here.\n */\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n ServiceChargeSnapshotCollection,\n ServiceCompensationSnapshotCollection,\n} from '../models/service-snapshots.js';\nimport type {\n ServiceTimeEntry,\n ServiceTimeEntryCollection,\n} from '../models/service-time-entry.js';\nimport type { ServiceEvidence } from '../types.js';\nimport {\n authorizeTimesheetAction,\n principalHolds,\n TimesheetActionError,\n type TimesheetActionPrincipal,\n withActionPrincipal,\n} from './action-principal.js';\nimport {\n type ServiceCommercialResolver,\n ServiceEvidenceService,\n} from './service-evidence-service.js';\n\n/** Approval path stamped by the generated `approve` action. */\nexport const SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH = 'operator';\n\n/**\n * What `approve` prices with: a resolver, or a factory the action calls per\n * request with the collection's options. Use the factory whenever the\n * resolver writes (e.g. smrt-projects' `SubscriptionServiceCommercialResolver`\n * records client charges): `options.db` is the request-bound executor, so\n * those writes share the request's transaction and PostgreSQL RLS state\n * instead of a connection captured at startup.\n */\nexport type ServiceTimeEntryCommercialResolverSource =\n | ServiceCommercialResolver\n | ((context: {\n options: SmrtClassOptions;\n }) => ServiceCommercialResolver | Promise<ServiceCommercialResolver>);\n\nlet actionCommercialResolver: ServiceTimeEntryCommercialResolverSource | null =\n null;\n\n/**\n * Install what the generated `approve` action prices with. Pass `null` to\n * remove it. Without one, `approve` refuses with 409\n * (`approval_not_configured`) and writes nothing.\n *\n * @example\n * ```ts\n * setServiceTimeEntryCommercialResolver(({ options }) =>\n * SubscriptionServiceCommercialResolver.create(options, provider),\n * );\n * ```\n */\nexport function setServiceTimeEntryCommercialResolver(\n resolver: ServiceTimeEntryCommercialResolverSource | null,\n): void {\n actionCommercialResolver = resolver;\n}\n\n/** The source installed by {@link setServiceTimeEntryCommercialResolver}. */\nexport function getServiceTimeEntryCommercialResolver(): ServiceTimeEntryCommercialResolverSource | null {\n return actionCommercialResolver;\n}\n\n/** Body of the `record` action. Identity and tenant come from the session. */\nexport interface RecordServiceTimeActionInput {\n workRefType: string;\n workRefId: string;\n /** Defaults to the acting user's profile; another person needs `approve`. */\n participantProfileId?: string | null;\n /** `manual` (default) or `timer`; imports and agents use the service. */\n source?: 'manual' | 'timer';\n description?: string;\n /** ISO 8601 instant. */\n startedAt?: string | null;\n /** ISO 8601 instant. */\n endedAt?: string | null;\n durationSeconds?: number | null;\n /** Exact positive decimal text; exclusive with seconds and timestamps. */\n durationHours?: string;\n evidence?: ServiceEvidence[];\n metadata?: Record<string, unknown>;\n}\n\n/** Body of the `submit` and `approve` actions. */\nexport interface ServiceTimeEntryActionTarget {\n entryId: string;\n}\n\ntype ActionOptions = SmrtClassOptions;\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'The action body must be a JSON object.',\n );\n }\n return value as Record<string, unknown>;\n}\n\nfunction optionalString(\n body: Record<string, unknown>,\n key: string,\n): string | undefined {\n const value = body[key];\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'string') {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n `${key} must be a string.`,\n );\n }\n return value;\n}\n\nfunction requiredString(body: Record<string, unknown>, key: string): string {\n const value = optionalString(body, key)?.trim();\n if (!value) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n `${key} is required.`,\n );\n }\n return value;\n}\n\nfunction optionalInstant(\n body: Record<string, unknown>,\n key: string,\n): Date | null {\n const value = optionalString(body, key);\n if (value === undefined) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n `${key} must be an ISO 8601 instant.`,\n );\n }\n return date;\n}\n\n/**\n * Plain `Error`s from {@link ServiceEvidenceService} are its own input and\n * state preconditions; surface them as 400 instead of an opaque 500. Typed\n * errors (database, revision conflicts) pass through unchanged.\n */\nasync function serviceCall<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (error instanceof Error && error.constructor === Error) {\n throw new TimesheetActionError(400, 'invalid_request', error.message);\n }\n throw error;\n }\n}\n\nasync function evidenceService(\n entries: ServiceTimeEntryCollection,\n commercial?: ServiceCommercialResolver,\n): Promise<ServiceEvidenceService> {\n const options = entries.options;\n const [charges, compensation] = await Promise.all([\n ServiceChargeSnapshotCollection.create(options),\n ServiceCompensationSnapshotCollection.create(options),\n ]);\n return new ServiceEvidenceService(entries, charges, compensation, commercial);\n}\n\nfunction itemClass(entries: ServiceTimeEntryCollection) {\n return (entries.constructor as unknown as { _itemClass: unknown })\n ._itemClass as typeof ServiceTimeEntry;\n}\n\nasync function loadEntry(\n entries: ServiceTimeEntryCollection,\n principal: TimesheetActionPrincipal,\n body: Record<string, unknown>,\n): Promise<ServiceTimeEntry> {\n const entryId = requiredString(body, 'entryId');\n const entry = await entries.get({ id: entryId });\n // Optional tenancy also admits global (NULL-tenant) rows in a tenant\n // context; a write must land on the principal's own tenant only.\n if (!entry || (entry.tenantId ?? null) !== principal.tenantId) {\n throw new TimesheetActionError(\n 404,\n 'not_found',\n `Service time entry ${entryId} was not found.`,\n );\n }\n return entry;\n}\n\n/** `record`: create a draft entry through `ServiceEvidenceService.record()`. */\nexport async function recordServiceTimeAction(\n entries: ServiceTimeEntryCollection,\n input: unknown,\n): Promise<ServiceTimeEntry> {\n const body = asRecord(input);\n const principal = await authorizeTimesheetAction(\n itemClass(entries),\n 'record',\n entries.options as ActionOptions,\n { allowSuperAdminBypass: true },\n );\n const participantProfileId =\n optionalString(body, 'participantProfileId')?.trim() || principal.profileId;\n if (\n participantProfileId !== principal.profileId &&\n !(await principalHolds(itemClass(entries), 'approve', entries.options))\n ) {\n throw new TimesheetActionError(\n 403,\n 'permission_denied',\n \"Recording another person's time needs the approve permission.\",\n );\n }\n const source = optionalString(body, 'source') ?? 'manual';\n if (source !== 'manual' && source !== 'timer') {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n \"source must be 'manual' or 'timer'.\",\n );\n }\n const durationSeconds = body.durationSeconds;\n if (\n durationSeconds !== undefined &&\n durationSeconds !== null &&\n typeof durationSeconds !== 'number'\n ) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'durationSeconds must be a number.',\n );\n }\n const evidence = body.evidence;\n if (evidence !== undefined && !Array.isArray(evidence)) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'evidence must be an array.',\n );\n }\n const metadata = body.metadata;\n if (\n metadata !== undefined &&\n (metadata === null ||\n typeof metadata !== 'object' ||\n Array.isArray(metadata))\n ) {\n throw new TimesheetActionError(\n 400,\n 'invalid_request',\n 'metadata must be an object.',\n );\n }\n return withActionPrincipal(principal, async () => {\n const service = await evidenceService(entries);\n return serviceCall(() =>\n service.record({\n tenantId: principal.tenantId,\n workRefType: requiredString(body, 'workRefType'),\n workRefId: requiredString(body, 'workRefId'),\n participantKind: 'human',\n participantProfileId,\n source,\n description: optionalString(body, 'description') ?? '',\n startedAt: optionalInstant(body, 'startedAt'),\n endedAt: optionalInstant(body, 'endedAt'),\n ...(durationSeconds !== undefined\n ? { durationSeconds: durationSeconds as number | null }\n : {}),\n ...(body.durationHours !== undefined\n ? { durationHours: optionalString(body, 'durationHours') }\n : {}),\n ...(evidence ? { evidence: evidence as ServiceEvidence[] } : {}),\n ...(metadata ? { metadata: metadata as Record<string, unknown> } : {}),\n }),\n );\n });\n}\n\n/**\n * `submit`: `draft | rejected → submitted` through\n * `ServiceEvidenceService.submit()`, stamping `submittedAt` and the acting\n * profile. Only the entry's participant may submit it, unless the caller\n * holds `approve` (a supervisor submitting on someone's behalf).\n */\nexport async function submitServiceTimeAction(\n entries: ServiceTimeEntryCollection,\n input: unknown,\n): Promise<ServiceTimeEntry> {\n const body = asRecord(input);\n const principal = await authorizeTimesheetAction(\n itemClass(entries),\n 'submit',\n entries.options,\n { allowSuperAdminBypass: true },\n );\n return withActionPrincipal(principal, async () => {\n const entry = await loadEntry(entries, principal, body);\n if (\n entry.participantProfileId !== principal.profileId &&\n !(await principalHolds(itemClass(entries), 'approve', entries.options))\n ) {\n throw new TimesheetActionError(\n 403,\n 'permission_denied',\n \"Submitting another person's time needs the approve permission.\",\n );\n }\n const service = await evidenceService(entries);\n return serviceCall(() => service.submit(entry, principal.profileId));\n });\n}\n\n/**\n * `approve`: `submitted → approved` through `ServiceEvidenceService.approve()`,\n * which stamps `approvedAt`, the acting profile and the approval path, then\n * writes the immutable charge and compensation snapshots through the\n * installed {@link setServiceTimeEntryCommercialResolver resolver} (a factory\n * is called per request with the collection's options). Money:\n * super-admin bypass does not apply, the grant must be explicit.\n */\nexport async function approveServiceTimeAction(\n entries: ServiceTimeEntryCollection,\n input: unknown,\n): Promise<ServiceTimeEntry> {\n const body = asRecord(input);\n const principal = await authorizeTimesheetAction(\n itemClass(entries),\n 'approve',\n entries.options,\n { allowSuperAdminBypass: false },\n );\n const source = actionCommercialResolver;\n if (!source) {\n throw new TimesheetActionError(\n // A 4xx so generated transports return the message; the host has not\n // installed the pricing the approval needs.\n 409,\n 'approval_not_configured',\n 'No ServiceCommercialResolver is installed for time entry approval.',\n );\n }\n return withActionPrincipal(principal, async () => {\n const entry = await loadEntry(entries, principal, body);\n // Built per request inside the principal's tenancy context, on the same\n // options (and so the same request-bound executor) as the entries.\n const commercial =\n typeof source === 'function'\n ? await source({ options: entries.options })\n : source;\n const service = await evidenceService(entries, commercial);\n return serviceCall(() =>\n service.approve(entry, {\n actorProfileId: principal.profileId,\n approvalPath: SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH,\n }),\n );\n });\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as setServiceTimeEntryCommercialResolver, c as ServiceEvidenceService, d as ServiceChargeSnapshotCollection, f as ServiceCompensationSnapshot, i as recordServiceTimeAction, l as TimesheetActionError, n as approveServiceTimeAction, o as submitServiceTimeAction, p as ServiceCompensationSnapshotCollection, r as getServiceTimeEntryCommercialResolver, t as SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH, u as ServiceChargeSnapshot } from "./chunks/time-entry-actions-
|
|
2
|
-
import { i as ServiceTimeEntryCollection, n as SERVICE_TIME_ENTRY_FROZEN_FIELDS, r as ServiceTimeEntry, t as SERVICE_DURATION_HOURS_EVIDENCE } from "./chunks/service-time-entry-
|
|
3
|
-
import { a as AttendanceReplay, c as AttendanceSegmentCollection, i as AttendancePunchCollection, n as AttendanceBreakCollection, o as AttendanceReplayCollection, r as AttendancePunch, s as AttendanceSegment, t as AttendanceBreak } from "./chunks/models-
|
|
4
|
-
import { a as continueSegment, c as openSegment, d as validateAttendanceWork, f as ATTENDANCE_CORRECTION_KINDS, i as closeOpenSegment, l as replaceSegment, m as AttendanceCorrectionCollection, n as AttendanceService, o as endSegment, p as AttendanceCorrection, r as beginSegment, s as listSegments, t as AttendanceCorrectionService, u as AttendanceConflict } from "./chunks/attendance-
|
|
1
|
+
import { a as setServiceTimeEntryCommercialResolver, c as ServiceEvidenceService, d as ServiceChargeSnapshotCollection, f as ServiceCompensationSnapshot, i as recordServiceTimeAction, l as TimesheetActionError, n as approveServiceTimeAction, o as submitServiceTimeAction, p as ServiceCompensationSnapshotCollection, r as getServiceTimeEntryCommercialResolver, t as SERVICE_TIME_ENTRY_ACTION_APPROVAL_PATH, u as ServiceChargeSnapshot } from "./chunks/time-entry-actions-ClC8tMau.js";
|
|
2
|
+
import { i as ServiceTimeEntryCollection, n as SERVICE_TIME_ENTRY_FROZEN_FIELDS, r as ServiceTimeEntry, t as SERVICE_DURATION_HOURS_EVIDENCE } from "./chunks/service-time-entry-CVjZDt9I.js";
|
|
3
|
+
import { a as AttendanceReplay, c as AttendanceSegmentCollection, i as AttendancePunchCollection, n as AttendanceBreakCollection, o as AttendanceReplayCollection, r as AttendancePunch, s as AttendanceSegment, t as AttendanceBreak } from "./chunks/models-DERZIpZh.js";
|
|
4
|
+
import { a as continueSegment, c as openSegment, d as validateAttendanceWork, f as ATTENDANCE_CORRECTION_KINDS, i as closeOpenSegment, l as replaceSegment, m as AttendanceCorrectionCollection, n as AttendanceService, o as endSegment, p as AttendanceCorrection, r as beginSegment, s as listSegments, t as AttendanceCorrectionService, u as AttendanceConflict } from "./chunks/attendance-CtOOtfs1.js";
|
|
5
5
|
import { SERVICE_TIME_ENTRY_STATUS_TRANSITIONS } from "./types.js";
|
|
6
|
-
import { a as confirmedTimecardGuard, c as TimecardAdjustment, d as TimecardReopeningCollection, l as TimecardAdjustmentCollection, o as PeriodTimecard, s as PeriodTimecardCollection, t as PeriodRollupService, u as TimecardReopening } from "./chunks/rollup-
|
|
6
|
+
import { a as confirmedTimecardGuard, c as TimecardAdjustment, d as TimecardReopeningCollection, l as TimecardAdjustmentCollection, o as PeriodTimecard, s as PeriodTimecardCollection, t as PeriodRollupService, u as TimecardReopening } from "./chunks/rollup-CjtRNwp9.js";
|
|
7
7
|
import { TIMESHEETS_MODULE_META, TIMESHEETS_UI_SLOTS } from "./ui.js";
|
|
8
8
|
import { SmrtRecipe } from "@happyvertical/smrt-core";
|
|
9
9
|
//#region src/recipes.ts
|
package/dist/manifest.json
CHANGED
package/dist/rollup.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as confirmedTimecardGuard, c as TimecardAdjustment, d as TimecardReopeningCollection, l as TimecardAdjustmentCollection, o as PeriodTimecard, s as PeriodTimecardCollection, t as PeriodRollupService, u as TimecardReopening } from "./chunks/rollup-
|
|
1
|
+
import { a as confirmedTimecardGuard, c as TimecardAdjustment, d as TimecardReopeningCollection, l as TimecardAdjustmentCollection, o as PeriodTimecard, s as PeriodTimecardCollection, t as PeriodRollupService, u as TimecardReopening } from "./chunks/rollup-CjtRNwp9.js";
|
|
2
2
|
export { PeriodRollupService, PeriodTimecard, PeriodTimecardCollection, TimecardAdjustment, TimecardAdjustmentCollection, TimecardReopening, TimecardReopeningCollection, confirmedTimecardGuard };
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-timesheets",
|
|
6
|
-
"packageVersion": "0.57.
|
|
6
|
+
"packageVersion": "0.57.13",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
10
|
+
"manifest": "8c1d8d61dd76c37025abf2607e67eee3467d107e864140ea1ccbbf51e2f7abb4",
|
|
11
|
+
"packageJson": "652973e5e991ea82b329ac7047be1297f9de15846988d8f03ed38b17560d94e9",
|
|
12
12
|
"agents": "6771f7afb9dcff79c1d649519dd85538691c0f2753df321dbc3c1c9051c9a1bd",
|
|
13
13
|
"moduleDoc:agents/period-rollup.md": "68a7427e9d15815e04e8e8e77f52041224c96dd82f93712f741d471c81feada6"
|
|
14
14
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-timesheets",
|
|
3
|
-
"version": "0.57.
|
|
3
|
+
"version": "0.57.13",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Shared time entries for the SMRT framework: who worked how long on what, approval and correction, with immutable charge and compensation snapshots",
|
|
6
6
|
"type": "module",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@happyvertical/smrt-core": "0.57.
|
|
43
|
-
"@happyvertical/smrt-tenancy": "0.57.
|
|
44
|
-
"@happyvertical/smrt-types": "0.57.
|
|
45
|
-
"@happyvertical/smrt-ui": "0.57.
|
|
46
|
-
"@happyvertical/smrt-users": "0.57.
|
|
42
|
+
"@happyvertical/smrt-core": "0.57.13",
|
|
43
|
+
"@happyvertical/smrt-tenancy": "0.57.13",
|
|
44
|
+
"@happyvertical/smrt-types": "0.57.13",
|
|
45
|
+
"@happyvertical/smrt-ui": "0.57.13",
|
|
46
|
+
"@happyvertical/smrt-users": "0.57.13",
|
|
47
47
|
"@happyvertical/sql": "^0.105.5"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@happyvertical/smrt-vitest": "0.57.
|
|
58
|
+
"@happyvertical/smrt-vitest": "0.57.13",
|
|
59
59
|
"@sveltejs/package": "^2.5.8",
|
|
60
60
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
61
61
|
"@types/node": "24.13.6",
|