@happyvertical/smrt-timesheets 0.56.2 → 0.57.1

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-mk6nqJhX.js → attendance-DAitZkht.js} +6 -6
  13. package/dist/chunks/attendance-DAitZkht.js.map +1 -0
  14. package/dist/chunks/{attendance-CDfhph1R.js → attendance-UY7sTXFx.js} +192 -38
  15. package/dist/chunks/attendance-UY7sTXFx.js.map +1 -0
  16. package/dist/chunks/{models-C1krjn40.js → models-BJcnFMOL.js} +14 -3
  17. package/dist/chunks/models-BJcnFMOL.js.map +1 -0
  18. package/dist/chunks/{rollup-C8p1PAXX.js → rollup-B-qdcGDs.js} +15 -7
  19. package/dist/chunks/rollup-B-qdcGDs.js.map +1 -0
  20. package/dist/chunks/service-time-entry-CWAVYIID.js +286 -0
  21. package/dist/chunks/service-time-entry-CWAVYIID.js.map +1 -0
  22. package/dist/chunks/{time-entry-actions-Bp--TxUD.js → time-entry-actions-BgaZCLL4.js} +2 -2
  23. package/dist/chunks/{time-entry-actions-Bp--TxUD.js.map → time-entry-actions-BgaZCLL4.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-CDfhph1R.js.map +0 -1
  39. package/dist/chunks/attendance-mk6nqJhX.js.map +0 -1
  40. package/dist/chunks/models-C1krjn40.js.map +0 -1
  41. package/dist/chunks/rollup-C8p1PAXX.js.map +0 -1
  42. package/dist/chunks/service-time-entry-Cxh0pt0J.js +0 -286
  43. package/dist/chunks/service-time-entry-Cxh0pt0J.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"rollup-C8p1PAXX.js","names":["id"],"sources":["../../src/rollup/write.ts","../../src/rollup/models.ts","../../src/rollup/correction-guard.ts","../../src/rollup/calculation.ts","../../src/rollup/service.ts"],"sourcesContent":["import type { SmrtObject } from '@happyvertical/smrt-core';\n\n// Per-instance capability, deliberately absent from the package export surface.\nconst permitted = new WeakSet<SmrtObject>();\nexport function assertRollupWrite(object: SmrtObject): void {\n if (!permitted.has(object))\n throw new Error(\n 'Timecards and adjustments are writable only through PeriodRollupService.',\n );\n}\nexport async function persistRollup<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n permitted.add(object);\n try {\n await object.save();\n return object;\n } finally {\n permitted.delete(object);\n }\n}\n\n// Second capability: only reopen() may move a confirmed/submitted card back.\nconst reopening = new WeakSet<SmrtObject>();\nexport function isReopenWrite(object: SmrtObject): boolean {\n return reopening.has(object);\n}\nexport async function persistReopen<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n reopening.add(object);\n try {\n return await persistRollup(object);\n } finally {\n reopening.delete(object);\n }\n}\n","import {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { TimecardSource } from './types.js';\nimport { assertRollupWrite, isReopenWrite } from './write.js';\n\n/** `open` is the draft state; `submitted` is optional (see `PeriodRollupService`). */\nexport type TimecardStatus = 'open' | 'submitted' | 'confirmed';\n\n/** One person's immutable-on-confirmation period snapshot, in integer seconds. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'period_timecards',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class PeriodTimecard extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n startsAt: Date = new Date(0);\n endsAt: Date = new Date(0);\n timezone = '';\n rulesVersion = '';\n @field({ type: 'text' }) rulesSnapshot = '{}';\n @field({ type: 'text' }) sourceSnapshot = '[]';\n regularSeconds = 0;\n overtimeSeconds = 0;\n totalSeconds = 0;\n @field({ type: 'text' }) status: TimecardStatus = 'open';\n /** Set by `submit()`; cleared when the card is reopened. */\n submittedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n nullable: true,\n onDelete: 'RESTRICT',\n })\n submittedByProfileId: string | null = null;\n confirmedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n nullable: true,\n onDelete: 'RESTRICT',\n })\n confirmedByProfileId: string | null = null;\n\n /** Return a detached source snapshot; corrupt persisted JSON fails closed. */\n getSources(): TimecardSource[] {\n const result: unknown = JSON.parse(this.sourceSnapshot);\n if (!Array.isArray(result))\n throw new Error('Invalid timecard source snapshot.');\n return result as TimecardSource[];\n }\n /** Return the consumer policy frozen with this rollup. */\n getRules(): Record<string, unknown> {\n const result: unknown = JSON.parse(this.rulesSnapshot);\n if (!result || typeof result !== 'object' || Array.isArray(result))\n throw new Error('Invalid timecard rules snapshot.');\n return result as Record<string, unknown>;\n }\n override async save(): Promise<this> {\n assertRollupWrite(this);\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if (prior?.status === 'confirmed' && !isReopenWrite(this))\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\n );\n if (prior?.status === 'submitted' && !isReopenWrite(this)) {\n // A submitted card only moves forward to confirmed (or back via reopen).\n if (this.status !== 'confirmed' && this.status !== 'submitted')\n throw new Error(\n 'Submitted timecards can only be confirmed or reopened.',\n );\n }\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.profile_id !== this.profileId ||\n new Date(prior.starts_at as string).getTime() !==\n this.startsAt.getTime())\n )\n throw new Error('Timecard identity is immutable.');\n return (await super.save()) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard history cannot be deleted.');\n }\n}\n\n/** Attributed append-only signed seconds; the confirmed base card never changes. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'timecard_adjustments',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class TimecardAdjustment extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(PeriodTimecard, { onDelete: 'RESTRICT' }) timecardId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n actorProfileId = '';\n operationId = '';\n regularSeconds = 0;\n overtimeSeconds = 0;\n reason = '';\n override async save(): Promise<this> {\n assertRollupWrite(this);\n if (this.id && (await this.db.get(this.tableName, { id: this.id })))\n throw new Error('Timecard adjustments are append-only.');\n this.requireInsertOnSave();\n return (await super.save()) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard adjustments cannot be deleted.');\n }\n}\n/**\n * Append-only audit row for one reopening of a submitted or confirmed card: who\n * reopened whose period, why, and the totals it had at that moment (base,\n * adjustments and effective, in integer seconds). Written only by\n * `PeriodRollupService.reopen()` in the same transaction as the status change.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'timecard_reopenings',\n sensitive: true,\n indexes: [\n {\n name: 'timecard_reopenings_card_idx',\n columns: ['tenantId', 'timecardId'],\n },\n ],\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class TimecardReopening extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(PeriodTimecard, { onDelete: 'RESTRICT' }) timecardId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n actorProfileId = '';\n operationId = '';\n @field({ type: 'text' }) fromStatus: TimecardStatus = 'confirmed';\n @field({ type: 'text' }) reason = '';\n /** JSON: base/adjustment/effective seconds, confirmation and submission attribution. */\n @field({ type: 'text' }) previousState = '{}';\n /** The totals and attribution the card had when it was reopened. */\n getPrevious<T = Record<string, unknown>>(): T {\n const result: unknown = JSON.parse(this.previousState);\n if (!result || typeof result !== 'object' || Array.isArray(result))\n throw new Error('Invalid timecard reopening snapshot.');\n return result as T;\n }\n override async save(): Promise<this> {\n assertRollupWrite(this);\n if (this.id && (await this.db.get(this.tableName, { id: this.id })))\n throw new Error('Timecard reopenings are append-only.');\n if (!this.reason?.trim() || !this.operationId?.trim())\n throw new Error('Reopening requires an operationId and reason.');\n this.requireInsertOnSave();\n return (await super.save()) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard reopenings cannot be deleted.');\n }\n}\n/** Scoped persistence collection; mutations must use PeriodRollupService. */\nexport class PeriodTimecardCollection extends SmrtCollection<PeriodTimecard> {\n static readonly _itemClass = PeriodTimecard;\n}\n/** Scoped append-only adjustment collection. */\nexport class TimecardAdjustmentCollection extends SmrtCollection<TimecardAdjustment> {\n static readonly _itemClass = TimecardAdjustment;\n}\n/** Scoped append-only reopening ledger. */\nexport class TimecardReopeningCollection extends SmrtCollection<TimecardReopening> {\n static readonly _itemClass = TimecardReopening;\n}\n","import type { AttendanceCorrectionGuard } from '../attendance/correction-service.js';\nimport { PeriodTimecardCollection } from './models.js';\n\n/**\n * An `AttendanceCorrectionService` guard that refuses to change attendance\n * inside a period a `PeriodTimecard` has confirmed. A confirmed card never\n * changes, so correcting its sources would leave card and attendance\n * disagreeing; reopen the card first (`PeriodRollupService.reopen`), correct,\n * then roll up again. A submitted card is frozen too. A reopened card is `open`\n * again, so corrections inside it are allowed.\n * Needs the `period_timecards` table, which is why it is opt-in.\n */\nexport const confirmedTimecardGuard: AttendanceCorrectionGuard = async (\n tx,\n subject,\n intervals,\n) => {\n const cards = await PeriodTimecardCollection.create({ db: tx.db });\n const locked = (\n await cards.list({\n where: { tenantId: subject.tenantId, profileId: subject.profileId },\n })\n ).filter(\n (card) => card.status === 'confirmed' || card.status === 'submitted',\n );\n const hit = locked.find((card) =>\n intervals.some(\n (i) =>\n i.startsAt.getTime() < card.endsAt.getTime() &&\n card.startsAt.getTime() < i.endsAt.getTime(),\n ),\n );\n return hit\n ? `Attendance overlaps ${hit.status} timecard ${hit.id}; reopen it before correcting.`\n : null;\n};\n","import type { TimecardPeriod, TimecardSource } from './types.js';\n\nexport function integer(value: number, signed = false): number {\n if (!Number.isSafeInteger(value) || (!signed && value < 0))\n throw new Error('Timecard seconds must be safe integers and nonnegative.');\n return value;\n}\nexport function instant(value: Date): number {\n if (!(value instanceof Date) || !Number.isFinite(value.getTime()))\n throw new Error('Timecard timestamps must be valid dates.');\n return value.getTime();\n}\nexport function validatePeriod(\n value: TimecardPeriod,\n at: Date,\n): TimecardPeriod {\n if (\n !value ||\n instant(value.startsAt) > instant(at) ||\n instant(value.endsAt) <= instant(at) ||\n instant(value.startsAt) >= instant(value.endsAt)\n )\n throw new Error('Resolver period must contain the requested instant.');\n if (!value.timezone || !value.version?.trim())\n throw new Error('Resolver must supply a timezone and policy version.');\n new Intl.DateTimeFormat('en', { timeZone: value.timezone });\n if (\n !value.rules ||\n typeof value.rules !== 'object' ||\n Array.isArray(value.rules)\n )\n throw new Error('Resolver rules must be a JSON object.');\n // JSON round-trip detaches consumer-owned state; fail rather than silently lose non-JSON values.\n const json = JSON.stringify(value.rules, (_key, item) => {\n if (\n typeof item === 'function' ||\n typeof item === 'undefined' ||\n typeof item === 'symbol' ||\n (typeof item === 'number' && !Number.isFinite(item))\n )\n throw new Error('Resolver rules must be JSON serializable.');\n return item;\n });\n const rules: unknown = JSON.parse(json);\n if (!rules || typeof rules !== 'object' || Array.isArray(rules))\n throw new Error('Resolver rules must serialize to a JSON object.');\n return {\n startsAt: new Date(value.startsAt),\n endsAt: new Date(value.endsAt),\n timezone: value.timezone,\n version: value.version,\n rules: rules as Record<string, unknown>,\n };\n}\n/** Prefix allocation preserves integer seconds across adjacent half-open periods. */\nexport function clipSource(\n source: TimecardSource,\n period: TimecardPeriod,\n): TimecardSource | null {\n const start = instant(new Date(source.startsAt));\n const end = instant(new Date(source.endsAt));\n integer(source.seconds);\n if (end < start) throw new Error('Invalid source interval.');\n const left = Math.max(start, period.startsAt.getTime());\n const right = Math.min(end, period.endsAt.getTime());\n if (start === end)\n return start >= period.startsAt.getTime() && start < period.endsAt.getTime()\n ? { ...source }\n : null;\n if (left >= right) return null;\n const span = integer(end - start);\n const prefix = (elapsed: number) =>\n Number((BigInt(source.seconds) * BigInt(integer(elapsed))) / BigInt(span));\n const seconds = prefix(right - start) - prefix(left - start);\n return {\n ...source,\n startsAt: new Date(left).toISOString(),\n endsAt: new Date(right).toISOString(),\n seconds: integer(seconds),\n };\n}\n","import { createHash } from 'node:crypto';\nimport {\n isEmbeddedDatabase,\n isPostgresDatabase,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport { getTenantId, withTenant } from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { ServiceTimeEntryCollection } from '../models/service-time-entry.js';\nimport { clipSource, instant, integer, validatePeriod } from './calculation.js';\nimport {\n PeriodTimecard,\n PeriodTimecardCollection,\n TimecardAdjustment,\n TimecardAdjustmentCollection,\n TimecardReopening,\n TimecardReopeningCollection,\n} from './models.js';\nimport type {\n PeriodRollupOptions,\n PeriodRulesResolver,\n TimecardActor,\n TimecardAdjustmentInput,\n TimecardReopenInput,\n TimecardSource,\n} from './types.js';\nimport { persistReopen, persistRollup } from './write.js';\n\nfunction identity(parts: string[]): string {\n const bytes = createHash('sha256').update(JSON.stringify(parts)).digest();\n bytes[6] = (bytes[6] & 15) | 80;\n bytes[8] = (bytes[8] & 63) | 128;\n const hex = bytes.subarray(0, 16).toString('hex');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nfunction adjustedSeconds(\n base: number,\n rows: TimecardAdjustment[],\n field: 'regularSeconds' | 'overtimeSeconds',\n): number {\n const total = rows.reduce(\n (sum, row) => sum + BigInt(integer(row[field], true)),\n BigInt(integer(base)),\n );\n return integer(Number(total));\n}\n\n/**\n * Server-side, trusted tenant/person capability. Consumers authorize this scope\n * before construction. Reads and writes reject conflicting ambient tenancy.\n * SQLite and PostgreSQL are supported; policy and payroll remain consumer-owned.\n */\nexport class PeriodRollupService {\n constructor(\n private readonly db: DatabaseInterface,\n private readonly actor: TimecardActor,\n private readonly resolver: PeriodRulesResolver,\n private readonly options: PeriodRollupOptions = {},\n ) {\n if (!actor.tenantId?.trim() || !actor.profileId?.trim())\n throw new Error('Timecards require a trusted tenant/profile actor.');\n this.actor = Object.freeze({\n tenantId: this.identity(actor.tenantId),\n profileId: this.identity(actor.profileId),\n });\n if (options.actorProfileId !== undefined && !options.actorProfileId.trim())\n throw new Error('Actor attribution must be a nonempty profile id.');\n this.options = {\n attendance: options.attendance,\n requireSubmission: options.requireSubmission === true,\n actorProfileId: this.identity(\n options.actorProfileId ?? this.actor.profileId,\n ),\n };\n }\n\n private identity(value: string): string {\n if (!isPostgresDatabase(this.db)) return value;\n const hex = value.replace(/[{}-]/g, '').toLowerCase();\n if (!/^[0-9a-f]{32}$/.test(hex))\n throw new Error('PostgreSQL timecard identities must be UUIDs.');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n private scope<T>(run: () => Promise<T>): Promise<T> {\n const active = getTenantId();\n if (active && this.identity(active) !== this.actor.tenantId)\n throw new Error('Timecard actor differs from active tenant.');\n return withTenant({ tenantId: this.actor.tenantId }, run);\n }\n private transact<T>(run: (db: DatabaseInterface) => Promise<T>): Promise<T> {\n return this.scope(() =>\n withEmbeddedWriteTransaction(\n this.db,\n isEmbeddedDatabase(this.db),\n async (db) => {\n if (isPostgresDatabase(db))\n await db.query(\n 'SELECT pg_advisory_xact_lock(hashtextextended($1, 0))',\n JSON.stringify([\n 'smrt.rollup',\n this.actor.tenantId,\n this.actor.profileId,\n ]),\n );\n if (this.options.attendance && isPostgresDatabase(db))\n await db.query(\n 'SELECT pg_advisory_xact_lock(hashtextextended($1, 0))',\n JSON.stringify([\n 'smrt.attendance',\n this.actor.tenantId,\n this.actor.profileId,\n ]),\n );\n return run(db);\n },\n true,\n ),\n );\n }\n private async card(\n db: DatabaseInterface,\n id: string,\n ): Promise<PeriodTimecard> {\n const collection = await PeriodTimecardCollection.create({ db });\n const [card] = await collection.list({\n where: { ...this.actor, id },\n limit: 1,\n });\n if (!card) throw new Error('Timecard not found in actor scope.');\n return card;\n }\n /** Scoped read on the root executor, including after transaction completion. */\n async get(id: string): Promise<PeriodTimecard> {\n return this.scope(() => this.card(this.db, id));\n }\n /** Rebuild one open card from a consistent transaction-bound source snapshot. */\n async rollup(at: Date): Promise<PeriodTimecard> {\n at = new Date(instant(at));\n const id = await this.transact(async (db) => {\n const period = validatePeriod(\n await this.resolver.periodFor(new Date(at), this.actor),\n at,\n );\n const id = identity([\n 'smrt.period',\n this.actor.tenantId,\n this.actor.profileId,\n period.startsAt.toISOString(),\n ]);\n const collection = await PeriodTimecardCollection.create({ db });\n const [prior] = await collection.list({\n where: { ...this.actor, id },\n limit: 1,\n });\n if (prior?.status === 'confirmed')\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\n );\n // A policy change must not create overlapping cards for the same person.\n for (const other of await collection.list({ where: { ...this.actor } })) {\n if (\n other.id !== id &&\n other.startsAt < period.endsAt &&\n other.endsAt > period.startsAt\n )\n throw new Error('Resolver period overlaps an existing timecard.');\n }\n if (prior?.status === 'submitted')\n throw new Error('Submitted timecards are frozen; reopen first.');\n let sources: TimecardSource[] = [];\n let linkedEntryIds = new Set<string>();\n if (this.options.attendance) {\n const { attendanceSources } = await import('./attendance.js');\n ({ sources, linkedEntryIds } = await attendanceSources(\n db,\n this.actor,\n period,\n ));\n }\n const entries = await ServiceTimeEntryCollection.create({ db });\n const evidence = await entries.list({\n where: {\n tenantId: this.actor.tenantId,\n participantProfileId: this.actor.profileId,\n participantKind: 'human',\n },\n });\n const byId = new Map(evidence.map((entry) => [String(entry.id), entry]));\n const approvedRoots = new Set<string>();\n for (const entry of evidence) {\n if (entry.status !== 'approved') continue;\n let linked = linkedEntryIds.has(String(entry.id));\n let ancestor = entry;\n const seen = new Set([String(entry.id)]);\n while (ancestor.correctionOfId) {\n const parentId = ancestor.correctionOfId;\n if (seen.has(parentId))\n throw new Error('Cyclic evidence correction chain.');\n seen.add(parentId);\n const parent = byId.get(parentId);\n if (!parent || parent.status !== 'corrected')\n throw new Error(\n 'Correction requires corrected evidence in the same actor scope.',\n );\n linked ||= linkedEntryIds.has(parentId);\n ancestor = parent;\n }\n const rootId = String(ancestor.id);\n if (approvedRoots.has(rootId))\n throw new Error('Ambiguous approved evidence correction chain.');\n approvedRoots.add(rootId);\n if (linked) continue;\n // Duration-only manual evidence belongs to its approval instant. A\n // bounded interval allocates its actual duration by elapsed fraction.\n const start = entry.startedAt ?? entry.approvedAt;\n if (!start)\n throw new Error(\n 'Approved evidence needs a start or approval timestamp.',\n );\n // Hours-only sources are unbounded and attributed at approval. An\n // unsupported quantity in another period must not block this one.\n if (\n entry.durationSeconds === null &&\n (instant(start) < period.startsAt.getTime() ||\n instant(start) >= period.endsAt.getTime())\n )\n continue;\n const end = entry.startedAt && entry.endedAt ? entry.endedAt : start;\n const source = clipSource(\n {\n kind: 'entry',\n id: String(entry.id),\n startsAt: new Date(instant(start)).toISOString(),\n endsAt: new Date(instant(end)).toISOString(),\n seconds: integer(entry.requireDurationSeconds()),\n },\n period,\n );\n if (source?.seconds) sources.push(source);\n }\n sources.sort(\n (a, b) =>\n a.startsAt.localeCompare(b.startsAt) ||\n a.kind.localeCompare(b.kind) ||\n a.id.localeCompare(b.id),\n );\n const totalSeconds = sources.reduce(\n (sum, source) => integer(sum + source.seconds),\n 0,\n );\n // Resolver receives detached snapshots so it cannot alter persisted evidence.\n const classified = await this.resolver.classify(\n structuredClone(period),\n structuredClone(sources),\n this.actor,\n );\n const regularSeconds = integer(classified?.regularSeconds);\n const overtimeSeconds = integer(classified?.overtimeSeconds);\n if (integer(regularSeconds + overtimeSeconds) !== totalSeconds)\n throw new Error('Resolver classification must conserve total seconds.');\n const card =\n prior ??\n (await new PeriodTimecard({ db, id, _skipLoad: true }).initialize());\n if (!prior) card.requireInsertOnSave();\n Object.assign(card, this.actor, {\n startsAt: period.startsAt,\n endsAt: period.endsAt,\n timezone: period.timezone,\n rulesVersion: period.version,\n rulesSnapshot: JSON.stringify(period.rules),\n sourceSnapshot: JSON.stringify(sources),\n regularSeconds,\n overtimeSeconds,\n totalSeconds,\n });\n await persistRollup(card);\n return id;\n });\n return this.get(id);\n }\n /**\n * Optional step: freeze an open card for review (open -> submitted). The\n * actor is the submitter; repeating it on a submitted card is idempotent.\n */\n async submit(id: string): Promise<PeriodTimecard> {\n id = this.identity(id);\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status === 'submitted') return;\n if (card.status !== 'open')\n throw new Error('Only an open timecard can be submitted.');\n card.status = 'submitted';\n card.submittedAt = new Date();\n card.submittedByProfileId = this.options.actorProfileId!;\n await persistRollup(card);\n });\n return this.get(id);\n }\n /**\n * Confirm the current snapshot; repeated confirmation is idempotent. With\n * `requireSubmission` the card must be submitted first.\n */\n async confirm(id: string): Promise<PeriodTimecard> {\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status === 'confirmed') return;\n if (card.status === 'open' && this.options.requireSubmission)\n throw new Error('Timecards must be submitted before confirmation.');\n card.status = 'confirmed';\n card.confirmedAt = new Date();\n card.confirmedByProfileId = this.options.actorProfileId!;\n await persistRollup(card);\n });\n return this.get(id);\n }\n /**\n * Return a submitted or confirmed card to `open` so its sources can be\n * corrected and `rollup()` run again, then confirmed anew. Needs a reason and\n * an operation key; appends a `TimecardReopening` with the totals the card had\n * (base, adjustments, effective). Adjustments stay on the ledger and keep\n * counting in `totals()`: after a re-roll, offset them with a new adjustment\n * if the correction made them redundant. The consumer authorizes the actor\n * (give reopening its own permission); a retry with the same key and reason\n * is a no-op.\n */\n async reopen(\n id: string,\n input: TimecardReopenInput,\n ): Promise<PeriodTimecard> {\n id = this.identity(id);\n input = { operationId: input.operationId, reason: input.reason };\n if (!input.operationId?.trim() || !input.reason?.trim())\n throw new Error('Reopening requires an operationId and reason.');\n const reopeningId = identity([\n 'smrt.reopening',\n this.actor.tenantId,\n this.actor.profileId,\n id,\n input.operationId,\n ]);\n await this.transact(async (db) => {\n const ledger = await TimecardReopeningCollection.create({ db });\n const [done] = await ledger.list({\n where: { ...this.actor, timecardId: id, id: reopeningId },\n limit: 1,\n });\n if (done) {\n if (\n done.reason !== input.reason ||\n done.actorProfileId !== this.options.actorProfileId\n )\n throw new Error(\n 'Reopening operationId reused with a different payload.',\n );\n return;\n }\n const card = await this.card(db, id);\n if (card.status === 'open')\n throw new Error(\n 'Only a submitted or confirmed timecard can be reopened.',\n );\n const adjustments = await (\n await TimecardAdjustmentCollection.create({ db })\n ).list({ where: { ...this.actor, timecardId: id } });\n const regular = adjustedSeconds(\n card.regularSeconds,\n adjustments,\n 'regularSeconds',\n );\n const overtime = adjustedSeconds(\n card.overtimeSeconds,\n adjustments,\n 'overtimeSeconds',\n );\n const row = await new TimecardReopening({\n db,\n id: reopeningId,\n _skipLoad: true,\n }).initialize();\n Object.assign(row, this.actor, {\n timecardId: id,\n actorProfileId: this.options.actorProfileId!,\n operationId: input.operationId,\n fromStatus: card.status,\n reason: input.reason,\n previousState: JSON.stringify({\n rulesVersion: card.rulesVersion,\n base: {\n regularSeconds: card.regularSeconds,\n overtimeSeconds: card.overtimeSeconds,\n totalSeconds: card.totalSeconds,\n },\n adjustments: {\n count: adjustments.length,\n regularSeconds: integer(regular - card.regularSeconds, true),\n overtimeSeconds: integer(overtime - card.overtimeSeconds, true),\n },\n effective: {\n regularSeconds: regular,\n overtimeSeconds: overtime,\n totalSeconds: integer(regular + overtime),\n },\n submittedAt: card.submittedAt?.toISOString() ?? null,\n submittedByProfileId: card.submittedByProfileId,\n confirmedAt: card.confirmedAt?.toISOString() ?? null,\n confirmedByProfileId: card.confirmedByProfileId,\n }),\n });\n await persistRollup(row);\n card.status = 'open';\n card.submittedAt = null;\n card.submittedByProfileId = null;\n card.confirmedAt = null;\n card.confirmedByProfileId = null;\n await persistReopen(card);\n });\n return this.get(id);\n }\n /** The append-only reopening history of one card, oldest first. */\n async reopenings(id: string): Promise<TimecardReopening[]> {\n id = this.identity(id);\n return this.scope(async () => {\n await this.card(this.db, id);\n return (await TimecardReopeningCollection.create({ db: this.db })).list({\n where: { ...this.actor, timecardId: id },\n orderBy: 'created_at ASC',\n });\n });\n }\n /** Append a correction. An operation key retry must have exactly the same payload. */\n async adjust(\n id: string,\n input: TimecardAdjustmentInput,\n ): Promise<TimecardAdjustment> {\n id = this.identity(id);\n input = {\n operationId: input.operationId,\n reason: input.reason,\n regularSeconds: input.regularSeconds,\n overtimeSeconds: input.overtimeSeconds,\n };\n integer(input.regularSeconds, true);\n integer(input.overtimeSeconds, true);\n if (!input.operationId?.trim() || !input.reason?.trim())\n throw new Error('Adjustment requires an operationId and reason.');\n const adjustmentId = identity([\n 'smrt.adjustment',\n this.actor.tenantId,\n this.actor.profileId,\n id,\n input.operationId,\n ]);\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status !== 'confirmed')\n throw new Error('Adjustments require a confirmed timecard.');\n const collection = await TimecardAdjustmentCollection.create({ db });\n const rows = await collection.list({\n where: { ...this.actor, timecardId: id },\n });\n const prior = rows.find((row) => row.id === adjustmentId);\n if (prior) {\n if (\n prior.regularSeconds !== input.regularSeconds ||\n prior.overtimeSeconds !== input.overtimeSeconds ||\n prior.reason !== input.reason ||\n prior.actorProfileId !== this.options.actorProfileId\n )\n throw new Error(\n 'Adjustment operationId reused with a different payload.',\n );\n return;\n }\n const regular = adjustedSeconds(\n card.regularSeconds,\n rows,\n 'regularSeconds',\n );\n const overtime = adjustedSeconds(\n card.overtimeSeconds,\n rows,\n 'overtimeSeconds',\n );\n integer(\n integer(regular + input.regularSeconds) +\n integer(overtime + input.overtimeSeconds),\n );\n const adjustment = await new TimecardAdjustment({\n db,\n id: adjustmentId,\n _skipLoad: true,\n }).initialize();\n Object.assign(adjustment, this.actor, {\n timecardId: id,\n actorProfileId: this.options.actorProfileId!,\n ...input,\n });\n await persistRollup(adjustment);\n });\n return this.scope(async () => {\n const collection = await TimecardAdjustmentCollection.create({\n db: this.db,\n });\n const [row] = await collection.list({\n where: { ...this.actor, id: adjustmentId },\n limit: 1,\n });\n if (!row) throw new Error('Committed adjustment disappeared.');\n return row;\n });\n }\n /** Effective totals combine the frozen base with append-only corrections. */\n async totals(id: string): Promise<{\n regularSeconds: number;\n overtimeSeconds: number;\n totalSeconds: number;\n }> {\n return this.transact(async (db) => {\n const card = await this.card(db, id);\n const collection = await TimecardAdjustmentCollection.create({ db });\n const rows = await collection.list({\n where: { ...this.actor, timecardId: id },\n });\n const regularSeconds = adjustedSeconds(\n card.regularSeconds,\n rows,\n 'regularSeconds',\n );\n const overtimeSeconds = adjustedSeconds(\n card.overtimeSeconds,\n rows,\n 'overtimeSeconds',\n );\n return {\n regularSeconds,\n overtimeSeconds,\n totalSeconds: integer(regularSeconds + overtimeSeconds),\n };\n });\n }\n}\n"],"mappings":"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"}