@kernhq/module-inventory 0.1.2 → 0.3.0

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 (213) hide show
  1. package/README.md +134 -9
  2. package/dist/contract/capabilities.d.ts +49 -0
  3. package/dist/contract/capabilities.d.ts.map +1 -0
  4. package/dist/contract/capabilities.js +94 -0
  5. package/dist/contract/capabilities.js.map +1 -0
  6. package/dist/contract/events.d.ts +76 -0
  7. package/dist/contract/events.d.ts.map +1 -0
  8. package/dist/contract/events.js +62 -0
  9. package/dist/contract/events.js.map +1 -0
  10. package/dist/contract/index.d.ts +16 -0
  11. package/dist/contract/index.d.ts.map +1 -0
  12. package/dist/contract/index.js +16 -0
  13. package/dist/contract/index.js.map +1 -0
  14. package/dist/contract/models.d.ts +468 -0
  15. package/dist/contract/models.d.ts.map +1 -0
  16. package/dist/contract/models.js +320 -0
  17. package/dist/contract/models.js.map +1 -0
  18. package/dist/contract/notifications.d.ts +24 -0
  19. package/dist/contract/notifications.d.ts.map +1 -0
  20. package/dist/contract/notifications.js +72 -0
  21. package/dist/contract/notifications.js.map +1 -0
  22. package/dist/contract/permissions.d.ts +72 -0
  23. package/dist/contract/permissions.d.ts.map +1 -0
  24. package/dist/contract/permissions.js +79 -0
  25. package/dist/contract/permissions.js.map +1 -0
  26. package/dist/contract/router.d.ts +1378 -0
  27. package/dist/contract/router.d.ts.map +1 -0
  28. package/dist/contract/router.js +266 -0
  29. package/dist/contract/router.js.map +1 -0
  30. package/dist/contract/settings.d.ts +20 -0
  31. package/dist/contract/settings.d.ts.map +1 -0
  32. package/dist/contract/settings.js +49 -0
  33. package/dist/contract/settings.js.map +1 -0
  34. package/dist/server/index.d.ts +8 -1
  35. package/dist/server/index.d.ts.map +1 -1
  36. package/dist/server/index.js +288 -8
  37. package/dist/server/index.js.map +1 -1
  38. package/dist/server/jobs.d.ts +48 -0
  39. package/dist/server/jobs.d.ts.map +1 -0
  40. package/dist/server/jobs.js +358 -0
  41. package/dist/server/jobs.js.map +1 -0
  42. package/dist/server/router.d.ts +1789 -0
  43. package/dist/server/router.d.ts.map +1 -0
  44. package/dist/server/router.js +439 -0
  45. package/dist/server/router.js.map +1 -0
  46. package/dist/server/schema.d.ts +156 -9
  47. package/dist/server/schema.d.ts.map +1 -1
  48. package/dist/server/schema.js +183 -11
  49. package/dist/server/schema.js.map +1 -1
  50. package/dist/server/services/assets.d.ts +139 -0
  51. package/dist/server/services/assets.d.ts.map +1 -0
  52. package/dist/server/services/assets.js +429 -0
  53. package/dist/server/services/assets.js.map +1 -0
  54. package/dist/server/services/attachments.d.ts +80 -0
  55. package/dist/server/services/attachments.d.ts.map +1 -0
  56. package/dist/server/services/attachments.js +182 -0
  57. package/dist/server/services/attachments.js.map +1 -0
  58. package/dist/server/services/audience.d.ts +15 -0
  59. package/dist/server/services/audience.d.ts.map +1 -0
  60. package/dist/server/services/audience.js +64 -0
  61. package/dist/server/services/audience.js.map +1 -0
  62. package/dist/server/services/categories.d.ts +57 -0
  63. package/dist/server/services/categories.d.ts.map +1 -0
  64. package/dist/server/services/categories.js +124 -0
  65. package/dist/server/services/categories.js.map +1 -0
  66. package/dist/server/services/cursor.d.ts +68 -0
  67. package/dist/server/services/cursor.d.ts.map +1 -0
  68. package/dist/server/services/cursor.js +39 -0
  69. package/dist/server/services/cursor.js.map +1 -0
  70. package/dist/server/services/custody.d.ts +175 -0
  71. package/dist/server/services/custody.d.ts.map +1 -0
  72. package/dist/server/services/custody.js +367 -0
  73. package/dist/server/services/custody.js.map +1 -0
  74. package/dist/server/services/db-errors.d.ts +7 -0
  75. package/dist/server/services/db-errors.d.ts.map +1 -0
  76. package/dist/server/services/db-errors.js +32 -0
  77. package/dist/server/services/db-errors.js.map +1 -0
  78. package/dist/server/services/index.d.ts +26 -0
  79. package/dist/server/services/index.d.ts.map +1 -0
  80. package/dist/server/services/index.js +39 -0
  81. package/dist/server/services/index.js.map +1 -0
  82. package/dist/server/services/members.d.ts +27 -0
  83. package/dist/server/services/members.d.ts.map +1 -0
  84. package/dist/server/services/members.js +39 -0
  85. package/dist/server/services/members.js.map +1 -0
  86. package/dist/server/services/notify.d.ts +105 -0
  87. package/dist/server/services/notify.d.ts.map +1 -0
  88. package/dist/server/services/notify.js +147 -0
  89. package/dist/server/services/notify.js.map +1 -0
  90. package/dist/server/services/offboarding.d.ts +70 -0
  91. package/dist/server/services/offboarding.d.ts.map +1 -0
  92. package/dist/server/services/offboarding.js +116 -0
  93. package/dist/server/services/offboarding.js.map +1 -0
  94. package/dist/server/services/repairs.d.ts +204 -0
  95. package/dist/server/services/repairs.d.ts.map +1 -0
  96. package/dist/server/services/repairs.js +476 -0
  97. package/dist/server/services/repairs.js.map +1 -0
  98. package/dist/server/services/search.d.ts +85 -0
  99. package/dist/server/services/search.d.ts.map +1 -0
  100. package/dist/server/services/search.js +142 -0
  101. package/dist/server/services/search.js.map +1 -0
  102. package/dist/server/services/stats.d.ts +42 -0
  103. package/dist/server/services/stats.d.ts.map +1 -0
  104. package/dist/server/services/stats.js +80 -0
  105. package/dist/server/services/stats.js.map +1 -0
  106. package/dist/server/services/status.d.ts +102 -0
  107. package/dist/server/services/status.d.ts.map +1 -0
  108. package/dist/server/services/status.js +71 -0
  109. package/dist/server/services/status.js.map +1 -0
  110. package/migrations/0000_init.sql +12 -3
  111. package/migrations/0001_rls.sql +24 -0
  112. package/migrations/0002_custody_and_categories.sql +23 -0
  113. package/migrations/0003_repairs.sql +23 -0
  114. package/migrations/0004_platform_surfaces.sql +51 -0
  115. package/migrations/0005_repair_dates.sql +35 -0
  116. package/migrations/0006_workspace_registry_read.sql +50 -0
  117. package/migrations/0007_history_sequence.sql +83 -0
  118. package/migrations/meta/0000_snapshot.json +40 -13
  119. package/migrations/meta/0002_snapshot.json +1054 -0
  120. package/migrations/meta/0003_snapshot.json +1070 -0
  121. package/migrations/meta/0004_snapshot.json +1130 -0
  122. package/migrations/meta/0005_snapshot.json +1135 -0
  123. package/migrations/meta/_journal.json +44 -2
  124. package/package.json +5 -4
  125. package/src/client/api-instance.ts +27 -2
  126. package/src/client/api.ts +1 -1
  127. package/src/client/bidi.test.ts +148 -0
  128. package/src/client/bidi.ts +85 -0
  129. package/src/client/components/AssetDetailPanel.svelte +614 -0
  130. package/src/client/components/AssetFormDialog.svelte +191 -59
  131. package/src/client/components/AssetPhoto.svelte +178 -0
  132. package/src/client/components/AttachmentsSection.svelte +327 -0
  133. package/src/client/components/CustodyDialog.svelte +201 -0
  134. package/src/client/components/RepairDialog.svelte +271 -0
  135. package/src/client/components/RepairsSection.svelte +318 -0
  136. package/src/client/components/Timeline.svelte +347 -0
  137. package/src/client/components/TimelineText.svelte +124 -0
  138. package/src/client/core-api.ts +71 -0
  139. package/src/client/custody.test.ts +31 -0
  140. package/src/client/custody.ts +34 -0
  141. package/src/client/errors.test.ts +365 -0
  142. package/src/client/errors.ts +201 -0
  143. package/src/client/i18n.ts +11 -166
  144. package/src/client/index.ts +8 -1
  145. package/src/client/links.test.ts +74 -0
  146. package/src/client/links.ts +44 -0
  147. package/src/client/members.test.ts +132 -0
  148. package/src/client/members.ts +116 -0
  149. package/src/client/messages.test.ts +296 -0
  150. package/src/client/messages.ts +1424 -0
  151. package/src/client/mock.test.ts +555 -0
  152. package/src/client/mock.ts +1261 -52
  153. package/src/client/module.ts +76 -2
  154. package/src/client/pages/AssetsPage.svelte +638 -145
  155. package/src/client/permissions.ts +8 -1
  156. package/src/client/price.test.ts +254 -0
  157. package/src/client/price.ts +279 -0
  158. package/src/client/query.test.ts +58 -0
  159. package/src/client/query.ts +51 -2
  160. package/src/client/repairs.test.ts +38 -0
  161. package/src/client/repairs.ts +38 -0
  162. package/src/client/settings/CategoriesSettings.svelte +421 -0
  163. package/src/client/settings/GeneralSettings.svelte +403 -0
  164. package/src/client/status.ts +29 -0
  165. package/src/client/timeline.test.ts +175 -0
  166. package/src/client/timeline.ts +206 -0
  167. package/src/client/widgets/OverviewWidget.svelte +140 -26
  168. package/src/client/widgets/RepairsWidget.svelte +124 -0
  169. package/src/contract/capabilities.ts +99 -0
  170. package/src/contract/events.ts +83 -0
  171. package/src/contract/index.ts +16 -0
  172. package/src/contract/models.ts +360 -0
  173. package/src/contract/notifications.ts +73 -0
  174. package/src/contract/permissions.ts +79 -0
  175. package/src/contract/router.ts +300 -0
  176. package/src/contract/settings.ts +50 -0
  177. package/src/module.test.ts +330 -7
  178. package/src/server/index.ts +318 -8
  179. package/src/server/inventory.int.test.ts +4374 -0
  180. package/src/server/jobs.ts +444 -0
  181. package/src/server/migrations.test.ts +251 -0
  182. package/src/server/router.ts +574 -0
  183. package/src/server/schema.ts +184 -10
  184. package/src/server/services/assets.ts +528 -0
  185. package/src/server/services/attachments.ts +215 -0
  186. package/src/server/services/audience.ts +77 -0
  187. package/src/server/services/categories.ts +136 -0
  188. package/src/server/services/cursor.ts +104 -0
  189. package/src/server/services/custody.ts +471 -0
  190. package/src/server/services/db-errors.ts +42 -0
  191. package/src/server/services/index.ts +56 -0
  192. package/src/server/services/members.ts +54 -0
  193. package/src/server/services/notify.ts +196 -0
  194. package/src/server/services/offboarding.ts +150 -0
  195. package/src/server/services/repairs.ts +567 -0
  196. package/src/server/services/search.ts +166 -0
  197. package/src/server/services/stats.ts +88 -0
  198. package/src/server/services/status.test.ts +34 -0
  199. package/src/server/services/status.ts +143 -0
  200. package/tsconfig.base.json +22 -0
  201. package/tsconfig.client.json +1 -1
  202. package/tsconfig.json +1 -1
  203. package/vitest.config.ts +18 -3
  204. package/dist/contract.d.ts +0 -387
  205. package/dist/contract.d.ts.map +0 -1
  206. package/dist/contract.js +0 -119
  207. package/dist/contract.js.map +0 -1
  208. package/dist/server/_impl.d.ts +0 -427
  209. package/dist/server/_impl.d.ts.map +0 -1
  210. package/dist/server/_impl.js +0 -204
  211. package/dist/server/_impl.js.map +0 -1
  212. package/src/contract.ts +0 -143
  213. package/src/server/_impl.ts +0 -275
@@ -1 +0,0 @@
1
- {"version":3,"file":"_impl.js","sourceRoot":"","sources":["../../src/server/_impl.ts"],"names":[],"mappings":"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"}
package/src/contract.ts DELETED
@@ -1,143 +0,0 @@
1
- import { baseContract, defineEvent, definePermissions, PageInput, page, WorkspaceId } from '@kernhq/contracts'
2
- import { z } from 'zod'
3
-
4
- /**
5
- * What this module offers, as data.
6
- *
7
- * Imported by **both** halves — the server implements it, the client calls it — so nothing here may
8
- * touch Node. The contract is the only thing that crosses that line, which is why a procedure that
9
- * exists here and not in the router is a lie that compiles. `module.test.ts` checks exactly that.
10
- */
11
-
12
- /** Lowercase, 2-32 characters. Names the API prefix, the Postgres schema `mod_<id>` and every event. */
13
- export const MODULE_ID = 'inventory'
14
-
15
- /**
16
- * An asset's lifecycle. `in_stock` and `assigned` follow custody (an open row in
17
- * `custody_periods` means assigned); `under_repair` follows an open repair; `retired` is set by
18
- * hand when an item leaves the company. Stored, because every list filter needs it, and kept in
19
- * step inside the same transaction that writes the row it derives from.
20
- */
21
- export const AssetStatus = z.enum(['in_stock', 'assigned', 'under_repair', 'retired'])
22
- export type AssetStatus = z.infer<typeof AssetStatus>
23
-
24
- export const Asset = z.object({
25
- id: z.uuid(),
26
- workspaceId: WorkspaceId,
27
- /** Human-facing asset tag (`INV-0001`), assigned by the server, unique per workspace. */
28
- code: z.string().min(1).max(40),
29
- name: z.string().min(1).max(200),
30
- description: z.string().max(4000),
31
- categoryId: z.uuid().nullable(),
32
- status: AssetStatus,
33
- /**
34
- * The member currently holding the item. A plain uuid — cross-schema foreign keys are what the
35
- * module boundary exists to prevent — resolved against core membership at read time.
36
- */
37
- custodianUserId: z.uuid().nullable(),
38
- custodySince: z.string().nullable(),
39
- serialNumber: z.string().max(200).nullable(),
40
- location: z.string().max(200).nullable(),
41
- purchasedOn: z.string().nullable(),
42
- purchasedFrom: z.string().max(200).nullable(),
43
- /** Minor units (cents), the convention billing established; formatted on the client. */
44
- priceMinor: z.number().int().nullable(),
45
- currency: z.string().length(3).nullable(),
46
- warrantyUntil: z.string().nullable(),
47
- photoFileId: z.uuid().nullable(),
48
- createdAt: z.string(),
49
- updatedAt: z.string(),
50
- archivedAt: z.string().nullable(),
51
- })
52
- export type Asset = z.infer<typeof Asset>
53
-
54
- const ws = z.object({ workspaceId: WorkspaceId })
55
-
56
- const AssetCreateInput = z.object({
57
- name: z.string().min(1).max(200),
58
- description: z.string().max(4000).default(''),
59
- categoryId: z.uuid().nullish(),
60
- serialNumber: z.string().max(200).nullish(),
61
- location: z.string().max(200).nullish(),
62
- purchasedFrom: z.string().max(200).nullish(),
63
- purchasedOn: z.iso.date().nullish(),
64
- warrantyUntil: z.iso.date().nullish(),
65
- priceMinor: z.number().int().min(0).nullish(),
66
- currency: z.string().length(3).nullish(),
67
- })
68
-
69
- export const inventoryContract = {
70
- assets: {
71
- list: baseContract
72
- .route({ method: 'GET', path: '/assets', tags: ['inventory'] })
73
- .input(
74
- ws.extend({
75
- ...PageInput.shape,
76
- q: z.string().max(200).optional(),
77
- categoryId: z.uuid().optional(),
78
- status: AssetStatus.optional(),
79
- sort: z.enum(['recent', 'name', 'code']).default('recent'),
80
- }),
81
- )
82
- .output(page(Asset)),
83
- get: baseContract
84
- .route({ method: 'GET', path: '/assets/{assetId}', tags: ['inventory'] })
85
- .input(ws.extend({ assetId: z.uuid() }))
86
- .output(Asset),
87
- create: baseContract
88
- .route({ method: 'POST', path: '/assets', tags: ['inventory'] })
89
- .input(ws.extend(AssetCreateInput.shape))
90
- .output(Asset),
91
- update: baseContract
92
- .route({ method: 'PATCH', path: '/assets/{assetId}', tags: ['inventory'] })
93
- .input(
94
- ws.extend({
95
- assetId: z.uuid(),
96
- ...AssetCreateInput.partial().shape,
97
- }),
98
- )
99
- .output(Asset),
100
- archive: baseContract
101
- .route({ method: 'POST', path: '/assets/{assetId}/archive', tags: ['inventory'] })
102
- .input(ws.extend({ assetId: z.uuid(), archived: z.boolean().default(true) }))
103
- .output(Asset),
104
- },
105
- }
106
- export type InventoryContract = typeof inventoryContract
107
-
108
- /** `<module>.<entity>.<action>`. Anything that emits one declares it here. Payloads carry ids, never rows. */
109
- export const inventoryEvents = {
110
- assetCreated: defineEvent(
111
- 'inventory.asset.created',
112
- z.object({ assetId: z.uuid(), workspaceId: WorkspaceId }),
113
- ),
114
- assetUpdated: defineEvent(
115
- 'inventory.asset.updated',
116
- z.object({ assetId: z.uuid(), workspaceId: WorkspaceId }),
117
- ),
118
- assetArchived: defineEvent(
119
- 'inventory.asset.archived',
120
- z.object({ assetId: z.uuid(), workspaceId: WorkspaceId }),
121
- ),
122
- }
123
-
124
- /**
125
- * `<module>.<resource>.<action>`, each with the narrowest scope that works and the roles that hold it
126
- * by default. A workspace can add or remove any of them afterwards with a custom role.
127
- */
128
- export const inventoryPermissions = definePermissions([
129
- {
130
- key: 'inventory.asset.view',
131
- label: 'View assets',
132
- scope: 'workspace',
133
- defaultRoles: ['owner', 'admin', 'member', 'guest'],
134
- dangerous: false,
135
- },
136
- {
137
- key: 'inventory.asset.manage',
138
- label: 'Create and edit assets',
139
- scope: 'workspace',
140
- defaultRoles: ['owner', 'admin', 'member'],
141
- dangerous: false,
142
- },
143
- ])
@@ -1,275 +0,0 @@
1
- import {
2
- defineModule,
3
- defineServerModule,
4
- KernError,
5
- type Kernel,
6
- packageVersion,
7
- type RequestContext,
8
- requires,
9
- type Tx,
10
- uuidv7,
11
- workspaceScoped,
12
- } from '@kernhq/kernel'
13
- import { implement } from '@orpc/server'
14
- import { and, desc, eq, ilike, or, sql } from 'drizzle-orm'
15
- import { type Asset as AssetModel, inventoryContract, inventoryEvents, MODULE_ID } from '../contract.js'
16
- import { assetHistory, assets, counters } from './schema.js'
17
-
18
- /**
19
- * The router, kept apart from `index.ts` so `module.test.ts` can walk it without booting a kernel.
20
- *
21
- * Two middlewares on every procedure, and the test fails if either is missing:
22
- * `workspaceScoped` (a real membership, and the module switched on for that workspace) and
23
- * `requires` (the permission this particular call needs).
24
- */
25
- export { defineModule, defineServerModule, packageVersion }
26
-
27
- const os = implement(inventoryContract).$context<RequestContext>()
28
-
29
- /** The wire shape: drizzle gives Date objects for timestamps, the contract promises ISO strings. */
30
- function toAsset(row: typeof assets.$inferSelect): AssetModel {
31
- return {
32
- id: row.id,
33
- workspaceId: row.workspaceId as AssetModel['workspaceId'],
34
- code: row.code,
35
- name: row.name,
36
- description: row.description,
37
- categoryId: row.categoryId,
38
- status: row.status,
39
- custodianUserId: row.custodianUserId,
40
- custodySince: row.custodySince?.toISOString() ?? null,
41
- serialNumber: row.serialNumber,
42
- location: row.location,
43
- purchasedOn: row.purchasedOn ?? null,
44
- purchasedFrom: row.purchasedFrom,
45
- priceMinor: row.priceMinor,
46
- currency: row.currency,
47
- warrantyUntil: row.warrantyUntil ?? null,
48
- photoFileId: row.photoFileId,
49
- createdAt: row.createdAt.toISOString(),
50
- updatedAt: row.updatedAt.toISOString(),
51
- archivedAt: row.archivedAt?.toISOString() ?? null,
52
- }
53
- }
54
-
55
- /**
56
- * The next asset tag for a workspace. One narrow row per workspace and key, incremented under the
57
- * insert's conflict lock — two concurrent creates each read the value their own update returned, so
58
- * codes stay unique without a retry loop. `INV-0042`, because people say asset tags out loud.
59
- */
60
- async function nextCode(
61
- tx: Parameters<Parameters<Kernel['database']['withWorkspace']>[1]>[0],
62
- workspaceId: string,
63
- ) {
64
- const [row] = await tx
65
- .insert(counters)
66
- .values({ workspaceId, key: 'asset_code', value: 1 })
67
- .onConflictDoUpdate({
68
- target: [counters.workspaceId, counters.key],
69
- set: { value: sql`${counters.value} + 1` },
70
- })
71
- .returning()
72
- return `INV-${String(row!.value).padStart(4, '0')}`
73
- }
74
-
75
- export function implement_(kernel: Kernel) {
76
- const scoped = os.use(workspaceScoped(MODULE_ID))
77
-
78
- const changed = (workspaceId: string, id: string, op: 'created' | 'updated' | 'deleted') =>
79
- kernel.realtime.change(workspaceId, { module: MODULE_ID, entity: 'asset', id, op })
80
-
81
- async function record(
82
- tx: Tx,
83
- input: { workspaceId: string },
84
- assetId: string,
85
- actorId: string | null | undefined,
86
- action: string,
87
- changes: { field: string; from: unknown; to: unknown }[] = [],
88
- data?: Record<string, unknown>,
89
- ) {
90
- await tx.insert(assetHistory).values({
91
- id: uuidv7(),
92
- workspaceId: input.workspaceId,
93
- assetId,
94
- actorId: actorId ?? null,
95
- action,
96
- changes,
97
- data: data ?? null,
98
- })
99
- }
100
-
101
- return os.router({
102
- assets: {
103
- list: scoped.assets.list.use(requires('inventory.asset.view')).handler(({ input }) =>
104
- kernel.database.withWorkspace(input.workspaceId, async (tx) => {
105
- const filters = [eq(assets.workspaceId, input.workspaceId)]
106
- if (input.q) {
107
- // A code is something somebody reads off a sticker; matching name, code and serial
108
- // loosely matters more than word-splitting here. Full-text arrives with the core indexer.
109
- filters.push(
110
- or(
111
- ilike(assets.name, `%${input.q}%`),
112
- ilike(assets.code, `%${input.q}%`),
113
- ilike(sql`coalesce(${assets.serialNumber}, '')`, `%${input.q}%`),
114
- )!,
115
- )
116
- }
117
- if (input.categoryId) filters.push(eq(assets.categoryId, input.categoryId))
118
- if (input.status) filters.push(eq(assets.status, input.status))
119
-
120
- const order =
121
- input.sort === 'name' ? assets.name : input.sort === 'code' ? assets.code : desc(assets.createdAt)
122
-
123
- const rows = await tx
124
- .select()
125
- .from(assets)
126
- .where(and(...filters))
127
- .orderBy(order)
128
- .limit(input.limit)
129
- return { items: rows.map(toAsset), nextCursor: null }
130
- }),
131
- ),
132
-
133
- get: scoped.assets.get.use(requires('inventory.asset.view')).handler(({ input }) =>
134
- kernel.database.withWorkspace(input.workspaceId, async (tx) => {
135
- const [row] = await tx
136
- .select()
137
- .from(assets)
138
- .where(and(eq(assets.workspaceId, input.workspaceId), eq(assets.id, input.assetId)))
139
- if (!row) throw KernError.notFound('Asset')
140
- return toAsset(row)
141
- }),
142
- ),
143
-
144
- create: scoped.assets.create
145
- .use(requires('inventory.asset.manage'))
146
- .handler(async ({ input, context }) => {
147
- const row = await kernel.database.withWorkspace(input.workspaceId, async (tx) => {
148
- const [r] = await tx
149
- .insert(assets)
150
- .values({
151
- id: uuidv7(),
152
- workspaceId: input.workspaceId,
153
- code: await nextCode(tx, input.workspaceId),
154
- name: input.name,
155
- description: input.description,
156
- categoryId: input.categoryId ?? null,
157
- serialNumber: input.serialNumber ?? null,
158
- location: input.location ?? null,
159
- purchasedFrom: input.purchasedFrom ?? null,
160
- purchasedOn: input.purchasedOn ?? null,
161
- warrantyUntil: input.warrantyUntil ?? null,
162
- priceMinor: input.priceMinor ?? null,
163
- currency: input.currency ?? null,
164
- })
165
- .returning()
166
- await record(tx, input, r!.id, context.principal.userId, 'created')
167
- return r!
168
- })
169
-
170
- // Both, every time. The event is for anything that reacts later; the realtime change is
171
- // what redraws a screen somebody is looking at now. A mutation that does neither leaves
172
- // the rest of the product believing the old answer.
173
- await kernel.emit(
174
- inventoryEvents.assetCreated,
175
- { assetId: row.id, workspaceId: input.workspaceId },
176
- { workspaceId: input.workspaceId, actorId: context.principal.userId },
177
- )
178
- await changed(input.workspaceId, row.id, 'created')
179
-
180
- return toAsset(row)
181
- }),
182
-
183
- update: scoped.assets.update
184
- .use(requires('inventory.asset.manage'))
185
- .handler(async ({ input, context }) => {
186
- const row = await kernel.database.withWorkspace(input.workspaceId, async (tx) => {
187
- const [prev] = await tx
188
- .select()
189
- .from(assets)
190
- .where(and(eq(assets.workspaceId, input.workspaceId), eq(assets.id, input.assetId)))
191
- .for('update')
192
- if (!prev) throw KernError.notFound('Asset')
193
-
194
- const patch = {
195
- name: input.name ?? prev.name,
196
- description: input.description ?? prev.description,
197
- categoryId: input.categoryId !== undefined ? (input.categoryId ?? null) : prev.categoryId,
198
- serialNumber:
199
- input.serialNumber !== undefined ? (input.serialNumber ?? null) : prev.serialNumber,
200
- location: input.location !== undefined ? (input.location ?? null) : prev.location,
201
- purchasedFrom:
202
- input.purchasedFrom !== undefined ? (input.purchasedFrom ?? null) : prev.purchasedFrom,
203
- purchasedOn: input.purchasedOn !== undefined ? (input.purchasedOn ?? null) : prev.purchasedOn,
204
- warrantyUntil:
205
- input.warrantyUntil !== undefined ? (input.warrantyUntil ?? null) : prev.warrantyUntil,
206
- priceMinor: input.priceMinor !== undefined ? (input.priceMinor ?? null) : prev.priceMinor,
207
- currency: input.currency !== undefined ? (input.currency ?? null) : prev.currency,
208
- updatedAt: new Date(),
209
- }
210
-
211
- const [r] = await tx.update(assets).set(patch).where(eq(assets.id, input.assetId)).returning()
212
-
213
- // Field-level diffs are the timeline — write only what actually moved.
214
- const fields = [
215
- 'name',
216
- 'description',
217
- 'categoryId',
218
- 'serialNumber',
219
- 'location',
220
- 'purchasedFrom',
221
- 'purchasedOn',
222
- 'warrantyUntil',
223
- 'priceMinor',
224
- 'currency',
225
- ] as const
226
- const iso = (v: unknown) => (v instanceof Date ? v.toISOString() : v) ?? null
227
- const changes = fields
228
- .filter((f) => iso(patch[f]) !== iso(prev[f]))
229
- .map((f) => ({ field: f, from: iso(prev[f]), to: iso(patch[f]) }))
230
- if (changes.length > 0)
231
- await record(tx, input, input.assetId, context.principal.userId, 'updated', changes)
232
-
233
- return r!
234
- })
235
-
236
- await kernel.emit(
237
- inventoryEvents.assetUpdated,
238
- { assetId: row.id, workspaceId: input.workspaceId },
239
- { workspaceId: input.workspaceId, actorId: context.principal.userId },
240
- )
241
- await changed(input.workspaceId, row.id, 'updated')
242
- return toAsset(row)
243
- }),
244
-
245
- archive: scoped.assets.archive
246
- .use(requires('inventory.asset.manage'))
247
- .handler(async ({ input, context }) => {
248
- const row = await kernel.database.withWorkspace(input.workspaceId, async (tx) => {
249
- const [r] = await tx
250
- .update(assets)
251
- .set({ archivedAt: input.archived ? new Date() : null, updatedAt: new Date() })
252
- .where(and(eq(assets.workspaceId, input.workspaceId), eq(assets.id, input.assetId)))
253
- .returning()
254
- if (!r) throw KernError.notFound('Asset')
255
- await record(
256
- tx,
257
- input,
258
- input.assetId,
259
- context.principal.userId,
260
- input.archived ? 'retired' : 'restored',
261
- )
262
- return r
263
- })
264
-
265
- await kernel.emit(
266
- inventoryEvents.assetArchived,
267
- { assetId: row.id, workspaceId: input.workspaceId },
268
- { workspaceId: input.workspaceId, actorId: context.principal.userId },
269
- )
270
- await changed(input.workspaceId, row.id, 'updated')
271
- return toAsset(row)
272
- }),
273
- },
274
- })
275
- }