@happyvertical/smrt-timesheets 0.57.0 → 0.57.2

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.
Files changed (43) hide show
  1. package/AGENTS.md +9 -3
  2. package/README.md +34 -5
  3. package/agents/period-rollup.md +13 -0
  4. package/dist/attendance/correction-service.d.ts +50 -9
  5. package/dist/attendance/correction-service.d.ts.map +1 -1
  6. package/dist/attendance/models.d.ts +6 -0
  7. package/dist/attendance/models.d.ts.map +1 -1
  8. package/dist/attendance/segments.d.ts.map +1 -1
  9. package/dist/attendance/service.d.ts +7 -1
  10. package/dist/attendance/service.d.ts.map +1 -1
  11. package/dist/attendance.js +2 -2
  12. package/dist/chunks/{attendance-Cmk-6dTV.js → attendance-CRhYpFij.js} +192 -38
  13. package/dist/chunks/attendance-CRhYpFij.js.map +1 -0
  14. package/dist/chunks/{attendance-CfIgcl26.js → attendance-NMy9n6Yz.js} +6 -6
  15. package/dist/chunks/attendance-NMy9n6Yz.js.map +1 -0
  16. package/dist/chunks/{models-BCrk0kZQ.js → models-CSNK96yj.js} +14 -3
  17. package/dist/chunks/models-CSNK96yj.js.map +1 -0
  18. package/dist/chunks/{rollup-CII9Hl1R.js → rollup-BfJ0Rx8A.js} +15 -7
  19. package/dist/chunks/rollup-BfJ0Rx8A.js.map +1 -0
  20. package/dist/chunks/service-time-entry-D-b03hMX.js +286 -0
  21. package/dist/chunks/service-time-entry-D-b03hMX.js.map +1 -0
  22. package/dist/chunks/{time-entry-actions-Lb8OYIS8.js → time-entry-actions-CFvxmB3J.js} +2 -2
  23. package/dist/chunks/{time-entry-actions-Lb8OYIS8.js.map → time-entry-actions-CFvxmB3J.js.map} +1 -1
  24. package/dist/index.js +5 -5
  25. package/dist/manifest.json +58 -5
  26. package/dist/rollup/attendance.d.ts +2 -2
  27. package/dist/rollup/attendance.d.ts.map +1 -1
  28. package/dist/rollup/calculation.d.ts +7 -1
  29. package/dist/rollup/calculation.d.ts.map +1 -1
  30. package/dist/rollup/service.d.ts.map +1 -1
  31. package/dist/rollup/types.d.ts +10 -0
  32. package/dist/rollup/types.d.ts.map +1 -1
  33. package/dist/rollup.d.ts +1 -1
  34. package/dist/rollup.d.ts.map +1 -1
  35. package/dist/rollup.js +1 -1
  36. package/dist/smrt-knowledge.json +25 -10
  37. package/package.json +7 -7
  38. package/dist/chunks/attendance-CfIgcl26.js.map +0 -1
  39. package/dist/chunks/attendance-Cmk-6dTV.js.map +0 -1
  40. package/dist/chunks/models-BCrk0kZQ.js.map +0 -1
  41. package/dist/chunks/rollup-CII9Hl1R.js.map +0 -1
  42. package/dist/chunks/service-time-entry-DzQsRZc_.js +0 -286
  43. package/dist/chunks/service-time-entry-DzQsRZc_.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"time-entry-actions-Lb8OYIS8.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-CFvxmB3J.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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBO,SAAS,yBAAyB,OAAe,QAAuB;CAC7E,IAAI,OAAO,WAAW,YAAY,CAAC,OAAO,cAAc,MAAM,GAC5D,MAAM,IAAI,gBACR,GAAG,MAAK,0EACC,OAAO,MAAM,EAAC,oBACvB,4BACA;EACE,WAAW;EACX,OAAO;EACP,cAAc;CAChB,CACF;AAEJ;AAEA,IAAM,iCAAiB,IAAI,QAAuC;AAClE,IAAM,uCAAuB,IAAI,QAA6C;AAE9E,eAAe,4BACb,QACA,QACwB;CACxB,IAAI,CAAC,OAAO,IAAI,OAAO;CACvB,IAAI;EACF,MAAM,MAAM,MAAM,OAAO,GAAG,IAAI,OAAO,WAAW,EAAE,IAAI,OAAO,GAAG,CAAC;EACnE,IAAI,CAAC,KAAK,OAAO;EACjB,OAAO,KAAK,UACV,OAAO,YACL,OAAO,KAAK,QAAQ,CAClB,KACA,IAAI,IAAI,QAAQ,WAAW,MAAM,IAAI,EAAE,YAAY,GAAG,MAAM,IAC9D,CAAC,CACH,CACF;CACF,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,mBACP,QACA,QACQ;CACR,OAAO,KAAK,UACV,OAAO,YAAY,OAAO,KAAK,QAAQ,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC,CAAC,CACpE;AACF;AAEA,IAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AACA,IAAM,sBAAsB;CAC1B;CACA;CACA;CACA;CACA;AACF;AAkBO,IAAM,wBAAN,cAAoC,WAAW;CACtB,WAA0B;CACJ,cAAsB;;;;;;;;;;CAU1E,SAAiB;CACQ,WAAmB;CACnB,iBAAyB;CACzB,WAAmB;CACnB,eAAuB;CACvB,kBAA0B;CACnD,4BAAkB,IAAI,KAAK;CAE3B,MAAe,aAA4B;EACzC,MAAM,MAAM,WAAW;EACvB,IAAI,MAAM,KAAK,QAAQ,GACrB,eAAe,IACb,MACA,mBACE,MACA,aACF,CACF;EACF,OAAO;CACT;CAEA,MAAe,OAAsB;EACnC,yBAAyB,yBAAyB,KAAK,MAAM;EAC7D,MAAM,UAAU,mBACd,MACA,aACF;EACA,MAAM,QACH,MAAM,4BAA4B,MAAM,aAAa,KACtD,eAAe,IAAI,IAAI,KACvB;EACF,IAAI,SAAS,UAAU,SACrB,MAAM,IAAI,MACR,4EACF;EACF,MAAM,SAAU,MAAM,MAAM,KAAK;EACjC,eAAe,IAAI,MAAM,OAAO;EAChC,OAAO;CACT;AACF;AAnDwD,cAA3C,uBAAkD,mBAAkB,gCAAA;AACjD,gBAAA,CAA7B,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GADjB,sBACmB,WAAA,YAAA,CAAA;AACsB,gBAAA,CAAnD,WAAW,oBAAoB,EAAE,UAAU,KAAK,CAAC,CAAA,GAFvC,sBAEyC,WAAA,eAAA,CAAA;AAW3B,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAbZ,sBAac,WAAA,YAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAdZ,sBAcc,WAAA,kBAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAfZ,sBAec,WAAA,YAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAhBZ,sBAgBc,WAAA,gBAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAjBZ,sBAiBc,WAAA,mBAAA,CAAA;AAjBd,wBAAN,gBAAA,CAZN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAEX,wBAAwB,CACtB,oDACF;CACA,iBAAiB,CAAC,eAAe;CACjC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;AAClC,CAAC,CAAA,GACY,qBAAA;AAqEN,IAAM,8BAAN,cAA0C,WAAW;CAC5B,WAA0B;CACJ,cAAsB;;;;;;;;CAQ1E,SAAiB;CACQ,WAAmB;CACnB,eAAuB;CACvB,eAAuB;CAChD,4BAAkB,IAAI,KAAK;CAE3B,MAAe,aAA4B;EACzC,MAAM,MAAM,WAAW;EACvB,IAAI,MAAM,KAAK,QAAQ,GACrB,qBAAqB,IACnB,MACA,mBACE,MACA,mBACF,CACF;EACF,OAAO;CACT;CAEA,MAAe,OAAsB;EACnC,yBAAyB,+BAA+B,KAAK,MAAM;EACnE,MAAM,UAAU,mBACd,MACA,mBACF;EACA,MAAM,QACH,MAAM,4BAA4B,MAAM,mBAAmB,KAC5D,qBAAqB,IAAI,IAAI,KAC7B;EACF,IAAI,SAAS,UAAU,SACrB,MAAM,IAAI,MACR,wFACF;EACF,MAAM,SAAU,MAAM,MAAM,KAAK;EACjC,qBAAqB,IAAI,MAAM,OAAO;EACtC,OAAO;CACT;AACF;AA/C8D,cAAjD,6BAAwD,mBAAkB,gCAAA;AACvD,gBAAA,CAA7B,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GADjB,4BACmB,WAAA,YAAA,CAAA;AACsB,gBAAA,CAAnD,WAAW,oBAAoB,EAAE,UAAU,KAAK,CAAC,CAAA,GAFvC,4BAEyC,WAAA,eAAA,CAAA;AAS3B,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAXZ,4BAWc,WAAA,YAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAZZ,4BAYc,WAAA,gBAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAbZ,4BAac,WAAA,gBAAA,CAAA;AAbd,8BAAN,gBAAA,CAZN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAEX,wBAAwB,CACtB,0DACF;CACA,iBAAiB,CAAC,eAAe;CACjC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;AAClC,CAAC,CAAA,GACY,2BAAA;AAsDN,IAAM,kCAAN,cAA8C,eAAsC,CAE3F;AAF6F,cAAhF,iCAAuF,mBAAkB,gCAAA;AACpH,cADW,iCACK,cAAa,qBAAA;AADlB,kCAAN,gBAAA,CADN,KAAK;CAAE,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAAG,KAAK;AAAM,CAAC,CAAA,GAC1C,+BAAA;AAQN,IAAM,wCAAN,cAAoD,eAA4C,CAEvG;AAFyG,cAA5F,uCAAmG,mBAAkB,gCAAA;AAChI,cADW,uCACK,cAAa,2BAAA;AADlB,wCAAN,gBAAA,CADN,KAAK;CAAE,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAAG,KAAK;AAAM,CAAC,CAAA,GAC1C,qCAAA;;;AC7MN,IAAM,uBAAN,cAAmC,MAAM;CACrC;CACA;CACA;CAET,YAAY,QAAgB,MAAc,SAAiB;EACzD,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,OAAO;EACZ,KAAK,gBAAgB;CACvB;AACF;AAqBA,SAAS,YAAY,MAA8B;CACjD,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,QAAS,KAAiC;CAChD,OAAO,OAAO,UAAU,YAAY,QAAQ,QAAQ;AACtD;AAEA,SAAS,mBAAqC;CAC5C,MAAM,SAAS,iBAAiB;CAChC,MAAM,UAAU,mCAAmC;CACnD,IAAI,QAAQ,UAAU;EAGpB,MAAM,iBACJ,SAAS,UAAU,QAAQ,WAAW,OAAO,SACzC,YAAY,QAAQ,IAAI,IACxB;EACN,OAAO;GACL,UAAU,OAAO;GACjB,QAAQ,OAAO,UAAU;GACzB,aAAa,OAAO;GACpB,WAAW,YAAY,OAAO,IAAI,KAAK;GACvC,aAAa;EACf;CACF;CACA,OAAO;EACL,UAAU,SAAS,YAAY;EAC/B,QAAQ,SAAS,UAAU;EAC3B,aAAa,SAAS,iCAAiB,IAAI,IAAY;EACvD,WAAW,YAAY,SAAS,IAAI;EACpC,aAAa;CACf;AACF;AAEA,eAAe,cACb,QACA,SACwB;CACxB,IAAI;EAIF,OAAO,YAAY,OAAM,MAHL,eAAe,OAAO,EACxC,IAAI,QAAQ,MAAM,QAAQ,YAC5B,CAAqB,EAAA,CACU,IAAI,EAAE,IAAI,OAAO,CAAC,CAAC;CACpD,QAAQ;EACN,OAAO;CACT;AACF;AAGA,eAAsB,eACpB,YACA,QACA,SACkB;CAClB,MAAM,UAAU,iBAAiB;CACjC,IAAI;EACF,MAAM,0BAA0B;GAC9B;GACA;GACA,IAAI,QAAQ,MAAM,QAAQ;GAC1B,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB,eAAe,QAAQ;GACvB,0BAA0B;GAC1B,uBAAuB;EACzB,CAAC;EACD,OAAO;CACT,SAAS,OAAO;EACd,IAAI,iBAAiB,0BAA0B,OAAO;EACtD,MAAM;CACR;AACF;AAYA,eAAsB,yBACpB,YACA,QACA,SACA,OACmC;CACnC,MAAM,UAAU,iBAAiB;CACjC,IAAI;EACF,MAAM,0BAA0B;GAC9B;GACA;GACA,IAAI,QAAQ,MAAM,QAAQ;GAC1B,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB,eAAe,QAAQ;GACvB,0BAA0B;GAC1B,uBAAuB,MAAM;EAC/B,CAAC;CACH,SAAS,OAAO;EACd,IAAI,iBAAiB,0BACnB,MAAM,IAAI,qBAAqB,KAAK,qBAAqB,MAAM,OAAO;EAExE,MAAM;CACR;CACA,IAAI,CAAC,QAAQ,YAAY,CAAC,QAAQ,QAChC,MAAM,IAAI,qBACR,KACA,qBACA,0DACF;CAEF,MAAM,YACJ,QAAQ,aAAc,MAAM,cAAc,QAAQ,QAAQ,OAAO;CACnE,IAAI,CAAC,WACH,MAAM,IAAI,qBACR,KACA,0BACA,uDACF;CAEF,OAAO;EACL,UAAU,QAAQ;EAClB,QAAQ,QAAQ;EAChB,aAAa,QAAQ;EACrB;EACA,aAAa,QAAQ;CACvB;AACF;AAOA,eAAsB,oBACpB,WACA,IACY;CACZ,IAAI,UAAU,aAAa,OAAO,GAAG;CACrC,OAAO,WACL;EACE,UAAU,UAAU;EACpB,QAAQ,UAAU;EAClB,aAAa,IAAI,IAAI,UAAU,WAAW;CAC5C,GACA,EACF;AACF;;;ACxIO,IAAM,yBAAN,MAAM,uBAAuB;CAClC,YACmB,SACA,SACA,cACA,YACjB;EAJiB,KAAA,UAAA;EACA,KAAA,UAAA;EACA,KAAA,eAAA;EACA,KAAA,aAAA;CAChB;CAJgB;CACA;CACA;CACA;CAGnB,aAAa,OACX,UAA4B,CAAC,GAC7B,YACiC;EACjC,MAAM,CAAC,SAAS,SAAS,gBAAgB,MAAM,QAAQ,IAAI;GACzD,2BAA2B,OAAO,OAAO;GACzC,gCAAgC,OAAO,OAAO;GAC9C,sCAAsC,OAAO,OAAO;EACtD,CAAC;EACD,OAAO,IAAI,uBACT,SACA,SACA,cACA,UACF;CACF;CAEA,MAAM,OAAO,OAA0D;EACrE,IAAI,CAAC;GAAC;GAAS;GAAU;GAAU;EAAO,CAAA,CAAE,SAAS,MAAM,MAAM,GAC/D,MAAM,IAAI,MAAM,kCAAkC;EACpD,IAAI,QAAQ,MAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,GACxD,MAAM,IAAI,MAAM,sDAAsD;EACxE,IAAI,EAAE,MAAM,eAAe,MAAM,YAC/B,MAAM,IAAI,MAAM,yCAAyC;EAC3D,IAAI,MAAM,oBAAoB,WAAW,CAAC,MAAM,sBAC9C,MAAM,IAAI,MAAM,mDAAmD;EACrE,IAAI,MAAM,oBAAoB,WAAW,CAAC,MAAM,UAC9C,MAAM,IAAI,MAAM,uCAAuC;EACzD,IAAI,MAAM,WAAW,YAAY,CAAC,MAAM,aAAa,CAAC,MAAM,UAC1D,MAAM,IAAI,MAAM,oDAAoD;EACtE,IAAI,MAAM,aAAa,MAAM,WAAW,MAAM,aAAa,MAAM,SAC/D,MAAM,IAAI,MAAM,gDAAgD;EAClE,MAAM,EAAE,eAAe,GAAG,WAAW;EACrC,IACE,MAAM,UAAU,MACb,SAAS,MAAM,SAAA,+CAClB,GAEA,MAAM,IAAI,MACR,8DACF;EACF,MAAM,QACJ,kBAAkB,KAAA,IAAY,OAAO,sBAAsB,aAAa;EAC1E,IACE,UAAU,SACT,MAAM,mBAAmB,QAAQ,MAAM,aAAa,MAAM,UAE3D,MAAM,IAAI,MACR,2EACF;EACF,IAAI,UAAU,QAAQ,MAAM,oBAAoB,MAC9C,MAAM,IAAI,MAAM,8CAA8C;EAChE,MAAM,WACJ,UAAU,OACN,OACC,MAAM,oBACN,MAAM,aAAa,MAAM,UACtB,KAAK,OACF,MAAM,QAAQ,QAAQ,IAAI,MAAM,UAAU,QAAQ,KAAK,GAC1D,IACA;EACV,IAAI,aAAa,SAAS,CAAC,OAAO,cAAc,QAAQ,KAAK,YAAY,IACvE,MAAM,IAAI,MACR,0DACF;EACF,MAAM,QAAQ,MAAM,KAAK,QAAQ,OAAO;GACtC,GAAG;GACH,iBAAiB;GACjB,UAAU,KAAK,UAAU,CACvB,GAAI,MAAM,YAAY,CAAC,GACvB,GAAI,UAAU,OACV,CAAC,IACD,CAAC;IAAE,MAAM;IAAiC;GAAM,CAAC,CACvD,CAAC;GACD,UAAU,KAAK,UAAU,MAAM,YAAY,CAAC,CAAC;GAC7C,QAAQ;GACR,aAAa;GACb,sBAAsB;GACtB,YAAY;GACZ,qBAAqB;GACrB,cAAc;GACd,YAAY;GACZ,qBAAqB;GACrB,iBAAiB;GACjB,gBAAgB;EAClB,CAAC;EACD,MAAM,MAAM,KAAK;EACjB,OAAO;CACT;CAEA,MAAM,OACJ,OACA,WAC2B;EAC3B,IAAI,MAAM,WAAW,WAAW,MAAM,WAAW,YAC/C,MAAM,IAAI,MAAM,uDAAuD;EACzE,MAAM,SAAS;EACf,MAAM,8BAAc,IAAI,KAAK;EAC7B,MAAM,uBAAuB,aAAa;EAC1C,OAAO,MAAM,KAAK;CACpB;CAEA,MAAM,QACJ,OACA,SAC2B;EAC3B,IAAI,MAAM,WAAW,eAAe,MAAM,WAAW,YACnD,MAAM,IAAI,MACR,wEACF;EACF,IAAI,CAAC,KAAK,YACR,MAAM,IAAI,MACR,kEACF;EACF,MAAM,cAAc,MAAM;EAC1B,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,iDAAiD;EACnE,MAAM,kBACJ,MAAM,KAAK,QAAQ,KAAK;GAAE,OAAO,EAAE,YAAY;GAAG,OAAO;EAAE,CAAC,EAAA,CAC5D;EACF,MAAM,wBACJ,MAAM,KAAK,aAAa,KAAK;GAAE,OAAO,EAAE,YAAY;GAAG,OAAO;EAAE,CAAC,EAAA,CACjE;EACF,IAAI,MAAM,WAAW,cAAc,kBAAkB,sBACnD,OAAO;EAET,MAAM,KAAK,WAAW,gBAAgB,KAAK;EAK3C,IAAI,MAAM,WAAW,aAAa;GAChC,MAAM,SAAS;GACf,MAAM,6BAAa,IAAI,KAAK;GAC5B,MAAM,sBAAsB,QAAQ,kBAAkB;GACtD,MAAM,eAAe,QAAQ;GAC7B,IAAI;IACF,MAAM,MAAM,KAAK;GACnB,SAAS,OAAO;IAKd,IAAI,EAHF,iBAAiB,SACjB,UAAU,SACV,MAAM,SAAS,8BACM,MAAM;IAC7B,MAAM,aAAa,MAAM,KAAK,QAAQ,IAAI,WAAW;IACrD,IAAI,YAAY,WAAW,YAAY,MAAM;IAC7C,QAAQ;IACR,MAAM,KAAK,WAAW,gBAAgB,KAAK;GAC7C;EACF;EACA,MAAM,WAAW,uBACb,OACA,MAAM,KAAK,WAAW,mBAAmB,KAAK;EAClD,IAAI,UAAU;GACZ,MAAM,aAAa,MAAM,wBACvB,iCACA,WACF;GACA,IAAI;IACF,MAAM,KAAK,aAAa,OAAO;KAC7B,IAAI;KACJ,UAAU,MAAM;KAChB;KACA,QAAQ,SAAS;KACjB,UAAU,SAAS,YAAY;KAC/B,cAAc,SAAS;KACvB,cAAc,KAAK,UAAU,SAAS,KAAK;KAC3C,aAAa;IACf,CAAC;GACH,SAAS,OAAO;IASd,IAAI,EAPD,MAAM,KAAK,aAAa,IAAI,UAAU,MAErC,MAAM,KAAK,aAAa,KAAK;KAC3B,OAAO,EAAE,YAAY;KACrB,OAAO;IACT,CAAC,EAAA,CACD,KACa,MAAM;GACzB;EACF;EACA,MAAM,SAAS,iBACX,OACA,MAAM,KAAK,WAAW,YAAY,KAAK;EAC3C,IAAI,QAAQ;GACV,MAAM,aAAa,MAAM,wBACvB,2BACA,WACF;GACA,IAAI;IACF,MAAM,KAAK,QAAQ,OAAO;KACxB,IAAI;KACJ,UAAU,MAAM;KAChB;KACA,QAAQ,OAAO;KACf,UAAU,OAAO,YAAY;KAC7B,gBAAgB,OAAO;KACvB,UAAU,OAAO,YAAY;KAC7B,cAAc,KAAK,UAAU,OAAO,KAAK;KACzC,iBAAiB,OAAO,aAAa;KACrC,aAAa;IACf,CAAC;GACH,SAAS,OAAO;IASd,IAAI,EAPD,MAAM,KAAK,QAAQ,IAAI,UAAU,MAEhC,MAAM,KAAK,QAAQ,KAAK;KACtB,OAAO,EAAE,YAAY;KACrB,OAAO;IACT,CAAC,EAAA,CACD,KACa,MAAM;GACzB;EACF;EACA,OAAO;CACT;CAEA,MAAM,QACJ,OACA,OAC2B;EAC3B,IAAI,MAAM,WAAW,YACnB,MAAM,IAAI,MAAM,8CAA8C;EAChE,IAAI,MAAM,aAAa,KAAA,KAAa,MAAM,aAAa,MAAM,UAC3D,MAAM,IAAI,MACR,8DACF;EAEF,MAAM,aAAa,MAAM,KAAK,OAAO;GACnC,GAAG;GACH,UAAU,MAAM;EAClB,CAAC;EACD,WAAW,iBAAiB,MAAM,MAAM;EACxC,MAAM,WAAW,KAAK;EACtB,MAAM,SAAS;EACf,MAAM,MAAM,KAAK;EACjB,OAAO;CACT;AACF;AAEA,eAAe,wBACb,MACA,aACiB;CACjB,MAAM,QAAQ,IAAI,YAAY,CAAA,CAAE,OAAO,KAAK,UAAU,CAAC,MAAM,WAAW,CAAC,CAAC;CAE1E,MAAM,OAAO,IADM,WAAW,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,CAC5D,CAAA,CAAO,MAAM,GAAG,EAAE;CAC/B,KAAK,KAAM,KAAK,KAAK,KAAQ;CAC7B,KAAK,KAAM,KAAK,KAAK,KAAQ;CAC7B,MAAM,MAAM,MAAM,KAAK,OAAO,SAC5B,KAAK,SAAS,EAAE,CAAA,CAAE,SAAS,GAAG,GAAG,CACnC,CAAA,CAAE,KAAK,EAAE;CACT,OAAO;EACL,IAAI,MAAM,GAAG,CAAC;EACd,IAAI,MAAM,GAAG,EAAE;EACf,IAAI,MAAM,IAAI,EAAE;EAChB,IAAI,MAAM,IAAI,EAAE;EAChB,IAAI,MAAM,EAAE;CACd,CAAA,CAAE,KAAK,GAAG;AACZ;;;;;;;;;;;ACjTO,IAAM,0CAA0C;AAgBvD,IAAI,2BACF;AAcK,SAAS,sCACd,UACM;CACN,2BAA2B;AAC7B;AAGO,SAAS,wCAAyF;CACvG,OAAO;AACT;AA6BA,SAAS,SAAS,OAAyC;CACzD,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAC5D,MAAM,IAAI,qBACR,KACA,mBACA,wCACF;CAEF,OAAO;AACT;AAEA,SAAS,eACP,MACA,KACoB;CACpB,MAAM,QAAQ,KAAK;CACnB,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO,KAAA;CAClD,IAAI,OAAO,UAAU,UACnB,MAAM,IAAI,qBACR,KACA,mBACA,GAAG,IAAG,mBACR;CAEF,OAAO;AACT;AAEA,SAAS,eAAe,MAA+B,KAAqB;CAC1E,MAAM,QAAQ,eAAe,MAAM,GAAG,CAAA,EAAG,KAAK;CAC9C,IAAI,CAAC,OACH,MAAM,IAAI,qBACR,KACA,mBACA,GAAG,IAAG,cACR;CAEF,OAAO;AACT;AAEA,SAAS,gBACP,MACA,KACa;CACb,MAAM,QAAQ,eAAe,MAAM,GAAG;CACtC,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,MAAM,OAAO,IAAI,KAAK,KAAK;CAC3B,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,MAAM,IAAI,qBACR,KACA,mBACA,GAAG,IAAG,8BACR;CAEF,OAAO;AACT;AAOA,eAAe,YAAe,IAAkC;CAC9D,IAAI;EACF,OAAO,MAAM,GAAG;CAClB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,MAAM,gBAAgB,OAClD,MAAM,IAAI,qBAAqB,KAAK,mBAAmB,MAAM,OAAO;EAEtE,MAAM;CACR;AACF;AAEA,eAAe,gBACb,SACA,YACiC;CACjC,MAAM,UAAU,QAAQ;CACxB,MAAM,CAAC,SAAS,gBAAgB,MAAM,QAAQ,IAAI,CAChD,gCAAgC,OAAO,OAAO,GAC9C,sCAAsC,OAAO,OAAO,CACtD,CAAC;CACD,OAAO,IAAI,uBAAuB,SAAS,SAAS,cAAc,UAAU;AAC9E;AAEA,SAAS,UAAU,SAAqC;CACtD,OAAQ,QAAQ,YACb;AACL;AAEA,eAAe,UACb,SACA,WACA,MAC2B;CAC3B,MAAM,UAAU,eAAe,MAAM,SAAS;CAC9C,MAAM,QAAQ,MAAM,QAAQ,IAAI,EAAE,IAAI,QAAQ,CAAC;CAG/C,IAAI,CAAC,UAAU,MAAM,YAAY,UAAU,UAAU,UACnD,MAAM,IAAI,qBACR,KACA,aACA,sBAAsB,QAAO,gBAC/B;CAEF,OAAO;AACT;AAGA,eAAsB,wBACpB,SACA,OAC2B;CAC3B,MAAM,OAAO,SAAS,KAAK;CAC3B,MAAM,YAAY,MAAM,yBACtB,UAAU,OAAO,GACjB,UACA,QAAQ,SACR,EAAE,uBAAuB,KAAK,CAChC;CACA,MAAM,uBACJ,eAAe,MAAM,sBAAsB,CAAA,EAAG,KAAK,KAAK,UAAU;CACpE,IACE,yBAAyB,UAAU,aACnC,CAAE,MAAM,eAAe,UAAU,OAAO,GAAG,WAAW,QAAQ,OAAO,GAErE,MAAM,IAAI,qBACR,KACA,qBACA,+DACF;CAEF,MAAM,SAAS,eAAe,MAAM,QAAQ,KAAK;CACjD,IAAI,WAAW,YAAY,WAAW,SACpC,MAAM,IAAI,qBACR,KACA,mBACA,qCACF;CAEF,MAAM,kBAAkB,KAAK;CAC7B,IACE,oBAAoB,KAAA,KACpB,oBAAoB,QACpB,OAAO,oBAAoB,UAE3B,MAAM,IAAI,qBACR,KACA,mBACA,mCACF;CAEF,MAAM,WAAW,KAAK;CACtB,IAAI,aAAa,KAAA,KAAa,CAAC,MAAM,QAAQ,QAAQ,GACnD,MAAM,IAAI,qBACR,KACA,mBACA,4BACF;CAEF,MAAM,WAAW,KAAK;CACtB,IACE,aAAa,KAAA,MACZ,aAAa,QACZ,OAAO,aAAa,YACpB,MAAM,QAAQ,QAAQ,IAExB,MAAM,IAAI,qBACR,KACA,mBACA,6BACF;CAEF,OAAO,oBAAoB,WAAW,YAAY;EAChD,MAAM,UAAU,MAAM,gBAAgB,OAAO;EAC7C,OAAO,kBACL,QAAQ,OAAO;GACb,UAAU,UAAU;GACpB,aAAa,eAAe,MAAM,aAAa;GAC/C,WAAW,eAAe,MAAM,WAAW;GAC3C,iBAAiB;GACjB;GACA;GACA,aAAa,eAAe,MAAM,aAAa,KAAK;GACpD,WAAW,gBAAgB,MAAM,WAAW;GAC5C,SAAS,gBAAgB,MAAM,SAAS;GACxC,GAAI,oBAAoB,KAAA,IACpB,EAAE,gBAAkD,IACpD,CAAC;GACL,GAAI,KAAK,kBAAkB,KAAA,IACvB,EAAE,eAAe,eAAe,MAAM,eAAe,EAAE,IACvD,CAAC;GACL,GAAI,WAAW,EAAE,SAAwC,IAAI,CAAC;GAC9D,GAAI,WAAW,EAAE,SAA8C,IAAI,CAAC;EACtE,CAAC,CACH;CACF,CAAC;AACH;AAQA,eAAsB,wBACpB,SACA,OAC2B;CAC3B,MAAM,OAAO,SAAS,KAAK;CAC3B,MAAM,YAAY,MAAM,yBACtB,UAAU,OAAO,GACjB,UACA,QAAQ,SACR,EAAE,uBAAuB,KAAK,CAChC;CACA,OAAO,oBAAoB,WAAW,YAAY;EAChD,MAAM,QAAQ,MAAM,UAAU,SAAS,WAAW,IAAI;EACtD,IACE,MAAM,yBAAyB,UAAU,aACzC,CAAE,MAAM,eAAe,UAAU,OAAO,GAAG,WAAW,QAAQ,OAAO,GAErE,MAAM,IAAI,qBACR,KACA,qBACA,gEACF;EAEF,MAAM,UAAU,MAAM,gBAAgB,OAAO;EAC7C,OAAO,kBAAkB,QAAQ,OAAO,OAAO,UAAU,SAAS,CAAC;CACrE,CAAC;AACH;AAUA,eAAsB,yBACpB,SACA,OAC2B;CAC3B,MAAM,OAAO,SAAS,KAAK;CAC3B,MAAM,YAAY,MAAM,yBACtB,UAAU,OAAO,GACjB,WACA,QAAQ,SACR,EAAE,uBAAuB,MAAM,CACjC;CACA,MAAM,SAAS;CACf,IAAI,CAAC,QACH,MAAM,IAAI,qBAGR,KACA,2BACA,oEACF;CAEF,OAAO,oBAAoB,WAAW,YAAY;EAChD,MAAM,QAAQ,MAAM,UAAU,SAAS,WAAW,IAAI;EAOtD,MAAM,UAAU,MAAM,gBAAgB,SAHpC,OAAO,WAAW,aACd,MAAM,OAAO,EAAE,SAAS,QAAQ,QAAQ,CAAC,IACzC,MACmD;EACzD,OAAO,kBACL,QAAQ,QAAQ,OAAO;GACrB,gBAAgB,UAAU;GAC1B,cAAc;EAChB,CAAC,CACH;CACF,CAAC;AACH"}
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-Lb8OYIS8.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-DzQsRZc_.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-BCrk0kZQ.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-Cmk-6dTV.js";
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-CFvxmB3J.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-D-b03hMX.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-CSNK96yj.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-CRhYpFij.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-CII9Hl1R.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-BfJ0Rx8A.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
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-timesheets",
5
- "packageVersion": "0.57.0",
5
+ "packageVersion": "0.57.2",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-timesheets:AttendanceCorrection": {
8
8
  "name": "attendancecorrection",
@@ -1146,6 +1146,11 @@
1146
1146
  "required": false,
1147
1147
  "related": "AttendancePunch"
1148
1148
  },
1149
+ "profileId": {
1150
+ "type": "crossPackageRef",
1151
+ "required": false,
1152
+ "related": "@happyvertical/smrt-profiles:Profile"
1153
+ },
1149
1154
  "workRefType": {
1150
1155
  "type": "text",
1151
1156
  "required": false
@@ -1246,6 +1251,14 @@
1246
1251
  "taskRefType",
1247
1252
  "taskRefId"
1248
1253
  ]
1254
+ },
1255
+ {
1256
+ "name": "attendance_segments_profile_idx",
1257
+ "columns": [
1258
+ "tenantId",
1259
+ "profileId",
1260
+ "startedAt"
1261
+ ]
1249
1262
  }
1250
1263
  ],
1251
1264
  "api": {
@@ -1297,7 +1310,7 @@
1297
1310
  ],
1298
1311
  "schema": {
1299
1312
  "tableName": "attendance_segments",
1300
- "ddl": "CREATE TABLE IF NOT EXISTS \"attendance_segments\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"punch_id\" UUID,\n \"work_ref_type\" TEXT,\n \"work_ref_id\" TEXT,\n \"task_ref_type\" TEXT,\n \"task_ref_id\" TEXT,\n \"kind\" TEXT,\n \"description\" TEXT,\n \"started_at\" TIMESTAMP,\n \"ended_at\" TIMESTAMP,\n \"duration_seconds\" INTEGER NOT NULL DEFAULT 0,\n \"closed_reason\" TEXT DEFAULT '',\n \"review_required\" BOOLEAN NOT NULL DEFAULT FALSE,\n \"open_slot\" TEXT NOT NULL\n);",
1313
+ "ddl": "CREATE TABLE IF NOT EXISTS \"attendance_segments\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"punch_id\" UUID,\n \"profile_id\" UUID,\n \"work_ref_type\" TEXT,\n \"work_ref_id\" TEXT,\n \"task_ref_type\" TEXT,\n \"task_ref_id\" TEXT,\n \"kind\" TEXT,\n \"description\" TEXT,\n \"started_at\" TIMESTAMP,\n \"ended_at\" TIMESTAMP,\n \"duration_seconds\" INTEGER NOT NULL DEFAULT 0,\n \"closed_reason\" TEXT DEFAULT '',\n \"review_required\" BOOLEAN NOT NULL DEFAULT FALSE,\n \"open_slot\" TEXT NOT NULL\n);",
1301
1314
  "columns": {
1302
1315
  "id": {
1303
1316
  "type": "UUID",
@@ -1342,6 +1355,12 @@
1342
1355
  "onUpdate": "CASCADE"
1343
1356
  }
1344
1357
  },
1358
+ "profile_id": {
1359
+ "type": "UUID",
1360
+ "referenceKind": "crossPackageRef",
1361
+ "notNull": false,
1362
+ "unique": false
1363
+ },
1345
1364
  "work_ref_type": {
1346
1365
  "type": "TEXT",
1347
1366
  "notNull": false,
@@ -1441,6 +1460,14 @@
1441
1460
  "task_ref_id"
1442
1461
  ]
1443
1462
  },
1463
+ {
1464
+ "name": "attendance_segments_profile_idx",
1465
+ "columns": [
1466
+ "tenant_id",
1467
+ "profile_id",
1468
+ "started_at"
1469
+ ]
1470
+ },
1444
1471
  {
1445
1472
  "name": "attendance_segments_tenant_id_created_at_idx",
1446
1473
  "columns": [
@@ -1453,9 +1480,15 @@
1453
1480
  "columns": [
1454
1481
  "punch_id"
1455
1482
  ]
1483
+ },
1484
+ {
1485
+ "name": "attendance_segments_profile_id_idx",
1486
+ "columns": [
1487
+ "profile_id"
1488
+ ]
1456
1489
  }
1457
1490
  ],
1458
- "version": "6e1c6c80"
1491
+ "version": "fb419025"
1459
1492
  },
1460
1493
  "importPath": "@happyvertical/smrt-timesheets/attendance"
1461
1494
  },
@@ -2029,7 +2062,7 @@
2029
2062
  "collectionExportName": "AttendanceSegmentCollectionCollection",
2030
2063
  "schema": {
2031
2064
  "tableName": "attendance_segments",
2032
- "ddl": "CREATE TABLE IF NOT EXISTS \"attendance_segments\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"punch_id\" UUID,\n \"work_ref_type\" TEXT,\n \"work_ref_id\" TEXT,\n \"task_ref_type\" TEXT,\n \"task_ref_id\" TEXT,\n \"kind\" TEXT,\n \"description\" TEXT,\n \"started_at\" TIMESTAMP,\n \"ended_at\" TIMESTAMP,\n \"duration_seconds\" INTEGER NOT NULL DEFAULT 0,\n \"closed_reason\" TEXT DEFAULT '',\n \"review_required\" BOOLEAN NOT NULL DEFAULT FALSE,\n \"open_slot\" TEXT NOT NULL\n);",
2065
+ "ddl": "CREATE TABLE IF NOT EXISTS \"attendance_segments\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"punch_id\" UUID,\n \"profile_id\" UUID,\n \"work_ref_type\" TEXT,\n \"work_ref_id\" TEXT,\n \"task_ref_type\" TEXT,\n \"task_ref_id\" TEXT,\n \"kind\" TEXT,\n \"description\" TEXT,\n \"started_at\" TIMESTAMP,\n \"ended_at\" TIMESTAMP,\n \"duration_seconds\" INTEGER NOT NULL DEFAULT 0,\n \"closed_reason\" TEXT DEFAULT '',\n \"review_required\" BOOLEAN NOT NULL DEFAULT FALSE,\n \"open_slot\" TEXT NOT NULL\n);",
2033
2066
  "columns": {
2034
2067
  "id": {
2035
2068
  "type": "UUID",
@@ -2074,6 +2107,12 @@
2074
2107
  "onUpdate": "CASCADE"
2075
2108
  }
2076
2109
  },
2110
+ "profile_id": {
2111
+ "type": "UUID",
2112
+ "referenceKind": "crossPackageRef",
2113
+ "notNull": false,
2114
+ "unique": false
2115
+ },
2077
2116
  "work_ref_type": {
2078
2117
  "type": "TEXT",
2079
2118
  "notNull": false,
@@ -2173,6 +2212,14 @@
2173
2212
  "task_ref_id"
2174
2213
  ]
2175
2214
  },
2215
+ {
2216
+ "name": "attendance_segments_profile_idx",
2217
+ "columns": [
2218
+ "tenant_id",
2219
+ "profile_id",
2220
+ "started_at"
2221
+ ]
2222
+ },
2176
2223
  {
2177
2224
  "name": "attendance_segments_tenant_id_created_at_idx",
2178
2225
  "columns": [
@@ -2185,9 +2232,15 @@
2185
2232
  "columns": [
2186
2233
  "punch_id"
2187
2234
  ]
2235
+ },
2236
+ {
2237
+ "name": "attendance_segments_profile_id_idx",
2238
+ "columns": [
2239
+ "profile_id"
2240
+ ]
2188
2241
  }
2189
2242
  ],
2190
- "version": "6e1c6c80"
2243
+ "version": "fb419025"
2191
2244
  },
2192
2245
  "importPath": "@happyvertical/smrt-timesheets/attendance"
2193
2246
  },
@@ -1,7 +1,7 @@
1
1
  import type { DatabaseInterface } from '@happyvertical/sql';
2
- import type { TimecardActor, TimecardPeriod, TimecardSource } from './types.js';
2
+ import type { RollupAttribution, TimecardActor, TimecardPeriod, TimecardSource } from './types.js';
3
3
  /** Optional attendance reader. Called on the rollup's transaction executor. */
4
- export declare function attendanceSources(db: DatabaseInterface, actor: TimecardActor, period: TimecardPeriod): Promise<{
4
+ export declare function attendanceSources(db: DatabaseInterface, actor: TimecardActor, period: TimecardPeriod, attribution?: RollupAttribution): Promise<{
5
5
  sources: TimecardSource[];
6
6
  linkedEntryIds: Set<string>;
7
7
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"attendance.d.ts","sourceRoot":"","sources":["../../src/rollup/attendance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAM5D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEhF,+EAA+E;AAC/E,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,iBAAiB,EACrB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CA0DrE"}
1
+ {"version":3,"file":"attendance.d.ts","sourceRoot":"","sources":["../../src/rollup/attendance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAM5D,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,+EAA+E;AAC/E,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,iBAAiB,EACrB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,cAAc,EACtB,WAAW,GAAE,iBAA0B,GACtC,OAAO,CAAC;IAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CA4DrE"}
@@ -1,7 +1,13 @@
1
- import type { TimecardPeriod, TimecardSource } from './types.js';
1
+ import type { RollupAttribution, TimecardPeriod, TimecardSource } from './types.js';
2
2
  export declare function integer(value: number, signed?: boolean): number;
3
3
  export declare function instant(value: Date): number;
4
4
  export declare function validatePeriod(value: TimecardPeriod, at: Date): TimecardPeriod;
5
5
  /** Prefix allocation preserves integer seconds across adjacent half-open periods. */
6
6
  export declare function clipSource(source: TimecardSource, period: TimecardPeriod): TimecardSource | null;
7
+ /**
8
+ * Attribute a source to a period. `'clip'` prorates it to the period; `'start'`
9
+ * keeps it whole when `ownerStart` (the start of the punch or entry it belongs
10
+ * to, in epoch ms) falls inside the period, and drops it otherwise.
11
+ */
12
+ export declare function attributeSource(source: TimecardSource, period: TimecardPeriod, attribution: RollupAttribution, ownerStart: number): TimecardSource | null;
7
13
  //# sourceMappingURL=calculation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"calculation.d.ts","sourceRoot":"","sources":["../../src/rollup/calculation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,MAAM,CAI7D;AACD,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAI3C;AACD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,IAAI,GACP,cAAc,CAsChB;AACD,qFAAqF;AACrF,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,GACrB,cAAc,GAAG,IAAI,CAsBvB"}
1
+ {"version":3,"file":"calculation.d.ts","sourceRoot":"","sources":["../../src/rollup/calculation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,MAAM,CAI7D;AACD,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAI3C;AACD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,IAAI,GACP,cAAc,CAsChB;AACD,qFAAqF;AACrF,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,GACrB,cAAc,GAAG,IAAI,CAsBvB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,iBAAiB,EAC9B,UAAU,EAAE,MAAM,GACjB,cAAc,GAAG,IAAI,CASvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/rollup/service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,OAAO,EACL,cAAc,EAEd,kBAAkB,EAElB,iBAAiB,EAElB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AAuBpB;;;;GAIG;AACH,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAHP,EAAE,EAAE,iBAAiB,EACrB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,GAAE,mBAAwB;IAmBpD,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,QAAQ;YA8BF,IAAI;IAYlB,gFAAgF;IAC1E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAG9C,iFAAiF;IAC3E,MAAM,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAgJ/C;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAcjD;;;OAGG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAalD;;;;;;;;;OASG;IACG,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,cAAc,CAAC;IA0F1B,mEAAmE;IAC7D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAU1D,sFAAsF;IAChF,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,kBAAkB,CAAC;IA8E9B,6EAA6E;IACvE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CAwBH"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/rollup/service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAQ5D,OAAO,EACL,cAAc,EAEd,kBAAkB,EAElB,iBAAiB,EAElB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AAuBpB;;;;GAIG;AACH,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAHP,EAAE,EAAE,iBAAiB,EACrB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,GAAE,mBAAwB;IA0BpD,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,QAAQ;YA8BF,IAAI;IAYlB,gFAAgF;IAC1E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAG9C,iFAAiF;IAC3E,MAAM,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAmJ/C;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAcjD;;;OAGG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAalD;;;;;;;;;OASG;IACG,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,cAAc,CAAC;IA0F1B,mEAAmE;IAC7D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAU1D,sFAAsF;IAChF,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,kBAAkB,CAAC;IA8E9B,6EAA6E;IACvE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CAwBH"}
@@ -42,9 +42,19 @@ export interface TimecardReopenInput {
42
42
  /** Required, non-blank; stored on the ledger row. */
43
43
  reason: string;
44
44
  }
45
+ /**
46
+ * How a source that crosses a period boundary is attributed.
47
+ * - `'clip'` (default): prorate its seconds across every period it overlaps.
48
+ * - `'start'`: it belongs wholly, unclipped, to the period containing its
49
+ * start and is excluded from the period it spills into. An attendance
50
+ * punch's breaks and segments follow the punch.
51
+ */
52
+ export type RollupAttribution = 'clip' | 'start';
45
53
  /** Service options for the card lifecycle. */
46
54
  export interface PeriodRollupOptions {
47
55
  attendance?: boolean;
56
+ /** Boundary attribution of crossing sources. Default `'clip'`. */
57
+ attribution?: RollupAttribution;
48
58
  actorProfileId?: string;
49
59
  /**
50
60
  * When true, `confirm()` accepts only a `submitted` card (open -> submitted
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rollup/types.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7E,QAAQ,CACN,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,EAChC,OAAO,EAAE,SAAS,QAAQ,CAAC,cAAc,CAAC,EAAE,EAC5C,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,GAC7B,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED,qFAAqF;AACrF,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rollup/types.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7E,QAAQ,CACN,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,EAChC,OAAO,EAAE,SAAS,QAAQ,CAAC,cAAc,CAAC,EAAE,EAC5C,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,GAC7B,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED,qFAAqF;AACrF,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEjD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kEAAkE;IAClE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B"}
package/dist/rollup.d.ts CHANGED
@@ -2,5 +2,5 @@ import './__smrt-register__.js';
2
2
  export { confirmedTimecardGuard } from './rollup/correction-guard.js';
3
3
  export { PeriodTimecard, PeriodTimecardCollection, TimecardAdjustment, TimecardAdjustmentCollection, TimecardReopening, TimecardReopeningCollection, type TimecardStatus, } from './rollup/models.js';
4
4
  export { PeriodRollupService } from './rollup/service.js';
5
- export type { PeriodRollupOptions, PeriodRulesResolver, TimecardActor, TimecardAdjustmentInput, TimecardPeriod, TimecardReopenInput, TimecardSource, } from './rollup/types.js';
5
+ export type { PeriodRollupOptions, PeriodRulesResolver, RollupAttribution, TimecardActor, TimecardAdjustmentInput, TimecardPeriod, TimecardReopenInput, TimecardSource, } from './rollup/types.js';
6
6
  //# sourceMappingURL=rollup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAAA,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,kBAAkB,EAClB,4BAA4B,EAC5B,iBAAiB,EACjB,2BAA2B,EAC3B,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAAA,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,kBAAkB,EAClB,4BAA4B,EAC5B,iBAAiB,EACjB,2BAA2B,EAC3B,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
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-CII9Hl1R.js";
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-BfJ0Rx8A.js";
2
2
  export { PeriodRollupService, PeriodTimecard, PeriodTimecardCollection, TimecardAdjustment, TimecardAdjustmentCollection, TimecardReopening, TimecardReopeningCollection, confirmedTimecardGuard };