@kernhq/module-inventory 0.2.0 → 0.4.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.
- package/README.md +106 -25
- package/dist/contract/capabilities.d.ts +4 -4
- package/dist/contract/capabilities.d.ts.map +1 -1
- package/dist/contract/capabilities.js +51 -9
- package/dist/contract/capabilities.js.map +1 -1
- package/dist/contract/events.d.ts +43 -0
- package/dist/contract/events.d.ts.map +1 -1
- package/dist/contract/events.js +40 -0
- package/dist/contract/events.js.map +1 -1
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +1 -0
- package/dist/contract/index.js.map +1 -1
- package/dist/contract/models.d.ts +345 -0
- package/dist/contract/models.d.ts.map +1 -1
- package/dist/contract/models.js +245 -0
- package/dist/contract/models.js.map +1 -1
- package/dist/contract/notifications.d.ts +24 -0
- package/dist/contract/notifications.d.ts.map +1 -0
- package/dist/contract/notifications.js +72 -0
- package/dist/contract/notifications.js.map +1 -0
- package/dist/contract/permissions.d.ts +52 -2
- package/dist/contract/permissions.d.ts.map +1 -1
- package/dist/contract/permissions.js +55 -2
- package/dist/contract/permissions.js.map +1 -1
- package/dist/contract/router.d.ts +1108 -0
- package/dist/contract/router.d.ts.map +1 -1
- package/dist/contract/router.js +253 -1
- package/dist/contract/router.js.map +1 -1
- package/dist/contract/settings.d.ts +2 -0
- package/dist/contract/settings.d.ts.map +1 -1
- package/dist/contract/settings.js +24 -4
- package/dist/contract/settings.js.map +1 -1
- package/dist/server/index.d.ts +8 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +289 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs.d.ts +48 -0
- package/dist/server/jobs.d.ts.map +1 -0
- package/dist/server/jobs.js +358 -0
- package/dist/server/jobs.js.map +1 -0
- package/dist/server/router.d.ts +1392 -0
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/router.js +385 -7
- package/dist/server/router.js.map +1 -1
- package/dist/server/schema.d.ts +134 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +169 -4
- package/dist/server/schema.js.map +1 -1
- package/dist/server/services/assets.d.ts +69 -3
- package/dist/server/services/assets.d.ts.map +1 -1
- package/dist/server/services/assets.js +199 -31
- package/dist/server/services/assets.js.map +1 -1
- package/dist/server/services/attachments.d.ts +80 -0
- package/dist/server/services/attachments.d.ts.map +1 -0
- package/dist/server/services/attachments.js +182 -0
- package/dist/server/services/attachments.js.map +1 -0
- package/dist/server/services/audience.d.ts +15 -0
- package/dist/server/services/audience.d.ts.map +1 -0
- package/dist/server/services/audience.js +64 -0
- package/dist/server/services/audience.js.map +1 -0
- package/dist/server/services/categories.d.ts +157 -0
- package/dist/server/services/categories.d.ts.map +1 -0
- package/dist/server/services/categories.js +309 -0
- package/dist/server/services/categories.js.map +1 -0
- package/dist/server/services/cursor.d.ts +68 -0
- package/dist/server/services/cursor.d.ts.map +1 -0
- package/dist/server/services/cursor.js +39 -0
- package/dist/server/services/cursor.js.map +1 -0
- package/dist/server/services/custody.d.ts +175 -0
- package/dist/server/services/custody.d.ts.map +1 -0
- package/dist/server/services/custody.js +367 -0
- package/dist/server/services/custody.js.map +1 -0
- package/dist/server/services/db-errors.d.ts +7 -0
- package/dist/server/services/db-errors.d.ts.map +1 -0
- package/dist/server/services/db-errors.js +32 -0
- package/dist/server/services/db-errors.js.map +1 -0
- package/dist/server/services/index.d.ts +16 -0
- package/dist/server/services/index.d.ts.map +1 -1
- package/dist/server/services/index.js +25 -1
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/members.d.ts +27 -0
- package/dist/server/services/members.d.ts.map +1 -0
- package/dist/server/services/members.js +39 -0
- package/dist/server/services/members.js.map +1 -0
- package/dist/server/services/notify.d.ts +43 -1
- package/dist/server/services/notify.d.ts.map +1 -1
- package/dist/server/services/notify.js +47 -5
- package/dist/server/services/notify.js.map +1 -1
- package/dist/server/services/offboarding.d.ts +70 -0
- package/dist/server/services/offboarding.d.ts.map +1 -0
- package/dist/server/services/offboarding.js +116 -0
- package/dist/server/services/offboarding.js.map +1 -0
- package/dist/server/services/repairs.d.ts +204 -0
- package/dist/server/services/repairs.d.ts.map +1 -0
- package/dist/server/services/repairs.js +476 -0
- package/dist/server/services/repairs.js.map +1 -0
- package/dist/server/services/search.d.ts +85 -0
- package/dist/server/services/search.d.ts.map +1 -0
- package/dist/server/services/search.js +142 -0
- package/dist/server/services/search.js.map +1 -0
- package/dist/server/services/stats.d.ts +42 -0
- package/dist/server/services/stats.d.ts.map +1 -0
- package/dist/server/services/stats.js +80 -0
- package/dist/server/services/stats.js.map +1 -0
- package/dist/server/services/status.d.ts +102 -0
- package/dist/server/services/status.d.ts.map +1 -0
- package/dist/server/services/status.js +71 -0
- package/dist/server/services/status.js.map +1 -0
- package/migrations/0002_custody_and_categories.sql +23 -0
- package/migrations/0003_repairs.sql +23 -0
- package/migrations/0004_platform_surfaces.sql +51 -0
- package/migrations/0005_repair_dates.sql +35 -0
- package/migrations/0006_workspace_registry_read.sql +50 -0
- package/migrations/0007_history_sequence.sql +83 -0
- package/migrations/0008_category_order_unique.sql +71 -0
- package/migrations/meta/0002_snapshot.json +1054 -0
- package/migrations/meta/0003_snapshot.json +1070 -0
- package/migrations/meta/0004_snapshot.json +1130 -0
- package/migrations/meta/0005_snapshot.json +1135 -0
- package/migrations/meta/_journal.json +49 -0
- package/package.json +2 -1
- package/src/client/api-instance.ts +27 -2
- package/src/client/bidi.test.ts +148 -0
- package/src/client/bidi.ts +85 -0
- package/src/client/components/AssetDetailPanel.svelte +614 -0
- package/src/client/components/AssetFormDialog.svelte +71 -22
- package/src/client/components/AssetPhoto.svelte +178 -0
- package/src/client/components/AttachmentsSection.svelte +327 -0
- package/src/client/components/CustodyDialog.svelte +201 -0
- package/src/client/components/RepairDialog.svelte +271 -0
- package/src/client/components/RepairsSection.svelte +318 -0
- package/src/client/components/Timeline.svelte +347 -0
- package/src/client/components/TimelineText.svelte +124 -0
- package/src/client/core-api.ts +71 -0
- package/src/client/custody.test.ts +31 -0
- package/src/client/custody.ts +34 -0
- package/src/client/errors.test.ts +395 -0
- package/src/client/errors.ts +229 -0
- package/src/client/links.test.ts +74 -0
- package/src/client/links.ts +44 -0
- package/src/client/members.test.ts +132 -0
- package/src/client/members.ts +116 -0
- package/src/client/messages.test.ts +127 -0
- package/src/client/messages.ts +1106 -18
- package/src/client/mock.test.ts +465 -1
- package/src/client/mock.ts +1105 -79
- package/src/client/module.ts +73 -1
- package/src/client/pages/AssetsPage.svelte +317 -45
- package/src/client/permissions.ts +7 -0
- package/src/client/price.test.ts +151 -3
- package/src/client/price.ts +155 -11
- package/src/client/query.ts +36 -0
- package/src/client/reorder.test.ts +100 -0
- package/src/client/reorder.ts +79 -0
- package/src/client/repairs.test.ts +38 -0
- package/src/client/repairs.ts +38 -0
- package/src/client/sequence.test.ts +248 -0
- package/src/client/sequence.ts +185 -0
- package/src/client/settings/CategoriesSettings.svelte +746 -0
- package/src/client/settings/GeneralSettings.svelte +0 -0
- package/src/client/status.ts +29 -0
- package/src/client/timeline.test.ts +175 -0
- package/src/client/timeline.ts +206 -0
- package/src/client/widgets/OverviewWidget.svelte +127 -26
- package/src/client/widgets/RepairsWidget.svelte +124 -0
- package/src/contract/capabilities.ts +53 -9
- package/src/contract/events.ts +49 -0
- package/src/contract/index.ts +1 -0
- package/src/contract/models.ts +270 -0
- package/src/contract/notifications.ts +73 -0
- package/src/contract/permissions.ts +55 -2
- package/src/contract/router.ts +284 -1
- package/src/contract/settings.ts +24 -4
- package/src/module.test.ts +217 -4
- package/src/server/index.ts +311 -4
- package/src/server/inventory.int.test.ts +4109 -19
- package/src/server/jobs.ts +444 -0
- package/src/server/migrations.test.ts +253 -2
- package/src/server/router.ts +486 -6
- package/src/server/schema.ts +171 -3
- package/src/server/services/assets.ts +214 -54
- package/src/server/services/attachments.ts +215 -0
- package/src/server/services/audience.ts +77 -0
- package/src/server/services/categories.ts +337 -0
- package/src/server/services/cursor.ts +104 -0
- package/src/server/services/custody.ts +471 -0
- package/src/server/services/db-errors.ts +42 -0
- package/src/server/services/index.ts +34 -1
- package/src/server/services/members.ts +54 -0
- package/src/server/services/notify.ts +52 -7
- package/src/server/services/offboarding.ts +150 -0
- package/src/server/services/repairs.ts +567 -0
- package/src/server/services/search.ts +166 -0
- package/src/server/services/stats.ts +88 -0
- package/src/server/services/status.test.ts +34 -0
- package/src/server/services/status.ts +143 -0
- package/src/client/settings/core-api.ts +0 -32
package/README.md
CHANGED
|
@@ -13,47 +13,128 @@ deleting the package removes the feature completely. Enable it per workspace in
|
|
|
13
13
|
An **asset** carries an asset tag the server assigns (`INV-0042`), a name and description, a serial
|
|
14
14
|
number, a purchase date and vendor, a price, a warranty expiry, a location, a photo and a status.
|
|
15
15
|
|
|
16
|
+
A price is written the way the reader's own language writes one — `1.234,56` in German, `۱٬۲۳۴٫۵۶`
|
|
17
|
+
in Persian — and is stored in the currency's own minor units. How many decimal places that is comes
|
|
18
|
+
from the currency, so a yen amount takes none and a dinar takes three; anything the field cannot
|
|
19
|
+
read, including more decimal places than the currency has, is refused rather than rounded.
|
|
20
|
+
|
|
16
21
|
The status column holds one of `in_stock`, `assigned`, `reserved`, `under_repair`, `lost` and
|
|
17
|
-
`retired
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
`retired`. Three of them are derived from two facts and written in the same transaction as the fact
|
|
23
|
+
that moved: an open repair makes an item `under_repair`, a custodian makes it `assigned`, and
|
|
24
|
+
neither makes it `in_stock`. **A repair wins that column and does not touch custody** — a laptop at
|
|
25
|
+
the workshop is still the person's it was handed to, so completing a repair returns it to `assigned`
|
|
26
|
+
rather than to stock. `reserved`, `lost` and `retired` wait on the features that set them, so those
|
|
27
|
+
three filters correctly return nothing yet.
|
|
20
28
|
|
|
21
29
|
- The **Assets** screen lists them in a table, searches by name, tag or serial number, filters by
|
|
22
|
-
status, hides archived rows until the switch asks for them, and pages a workspace
|
|
23
|
-
assets than one screen holds. All
|
|
30
|
+
status and by category, hides archived rows until the switch asks for them, and pages a workspace
|
|
31
|
+
with more assets than one screen holds. All of it is one server query; nothing is filtered in the
|
|
32
|
+
browser.
|
|
24
33
|
- Adding an asset opens a dialog, and editing one opens the same dialog seeded from the row. The tag
|
|
25
34
|
is the server's job — people read tags off stickers rather than inventing them.
|
|
26
|
-
- Each row carries a menu: edit, archive, restore. Archiving asks first and names what it is
|
|
27
|
-
to archive; restoring does not, because it takes nothing away.
|
|
28
|
-
-
|
|
35
|
+
- Each row carries a menu: open, edit, archive, restore. Archiving asks first and names what it is
|
|
36
|
+
about to archive; restoring does not, because it takes nothing away.
|
|
37
|
+
- The line above the table says how many assets the workspace has — the real number, from
|
|
38
|
+
`stats.summary`, rather than how many rows happen to be loaded.
|
|
39
|
+
- Opening an asset puts `?asset=<id>` on the URL and slides a **detail panel** over the list, so the
|
|
40
|
+
list keeps its filters and its place and the panel can be linked to and reloaded. It has up to five
|
|
41
|
+
sections: **Details**, **Custody**, **Repairs**, **Files** and **History** — Repairs and Files are
|
|
42
|
+
capabilities, so a workspace that switches one off sees three.
|
|
43
|
+
- **Custody** answers who is holding an item and who held it before. Hand it over, hand it on to
|
|
44
|
+
somebody else, or take it back — each is one transaction that closes the open period, opens the
|
|
45
|
+
next, moves the custodian, the date and the status together, and writes a line of history. Two
|
|
46
|
+
people handing the same item over at the same instant is settled by a database exclusion
|
|
47
|
+
constraint, and the one who loses is told to reload rather than shown a driver error. The person
|
|
48
|
+
receiving an item is notified; the person doing the handing is not.
|
|
49
|
+
- **Repairs** record what went away to be fixed, to whom, what it cost and when it came back. One
|
|
50
|
+
repair can be open per item — a partial unique index decides that, not the service, so two people
|
|
51
|
+
sending the same laptop away at the same instant end with one repair and a sentence rather than
|
|
52
|
+
two. Sending an item away puts it in `under_repair` and leaves whoever holds it holding it;
|
|
53
|
+
logging it back returns it to them, or to stock if nobody has it. An item that is away cannot be
|
|
54
|
+
archived, for the same reason one somebody is holding cannot be.
|
|
55
|
+
- **Files** are receipts, warranty cards and manuals, kept against the asset or against one of its
|
|
56
|
+
repairs — a repair's invoice sits inside that repair rather than in with everything else. This
|
|
57
|
+
module records that an asset has a file and never holds a byte: the browser uploads to core, this
|
|
58
|
+
stores the id it is handed, and removing one detaches it rather than deleting core's copy. The
|
|
59
|
+
asset's **photo** is set on the Details tab and is a field of the asset rather than one of these,
|
|
60
|
+
so a workspace with Files switched off still has one.
|
|
61
|
+
- **History** is the asset's own timeline, read newest first and paged. It renders as sentences —
|
|
62
|
+
"Ada handed it to Bruno", "Warranty until changed from Mar 14, 2027 to Mar 14, 2028" — not as a
|
|
63
|
+
JSON diff, and somebody who has since left the workspace reads as a former member rather than as a
|
|
64
|
+
uuid. "It", not the tag: the timeline is inside that asset's own panel, whose header is already
|
|
65
|
+
showing `INV-0042`, so repeating it on every line would be the one word in the sentence a reader
|
|
66
|
+
does not need.
|
|
67
|
+
- **Categories** group what a workspace owns. They are managed in **Settings → Inventory →
|
|
68
|
+
Categories**, chosen on the asset form, shown as a chip on the row, and are the list's category
|
|
69
|
+
filter. The order they appear in everywhere else is set by **dragging them into it** — or, without
|
|
70
|
+
a pointer, with the move-up and move-down buttons on each row, which do exactly the same thing and
|
|
71
|
+
say where the row landed. A new category joins the end. They archive rather than delete:
|
|
72
|
+
`assets.category_id` carries no foreign key, so deleting one would leave every asset filed under
|
|
73
|
+
it pointing at nothing, and an archived category still names itself on the rows that carry it.
|
|
74
|
+
- Two **dashboard cards**: the most recently added assets, above a strip saying how many there are,
|
|
75
|
+
how many nobody is holding and how many are away; and — for a workspace that records repairs — what
|
|
76
|
+
is out for repair right now, with the tag and name of each item.
|
|
77
|
+
- Assets are in the **workspace-wide search index**, so a tag read off a sticker finds the item from
|
|
78
|
+
the command palette. Name, tag, serial number, location, description and category name are all
|
|
79
|
+
searched. Archiving an item takes it out of the index; restoring puts it back.
|
|
80
|
+
- `inventory:asset:<id>` **resolves anywhere in the product** — a chat message, a tracker issue, a
|
|
81
|
+
notification — into the tag, the name and a link to the item. Somebody without
|
|
82
|
+
`inventory.asset.view` gets plain text rather than a title they were not entitled to read, and an
|
|
83
|
+
archived item still resolves and says it is archived rather than turning into a dead link.
|
|
84
|
+
- Two **nightly notices**, each sent once per item and never again: a **warranty about to run out**,
|
|
85
|
+
to whoever is holding it or to whoever may replace it when nobody is; and a **repair that has been
|
|
86
|
+
away too long**, to the person who logged it and to whoever is still holding the item. Both windows
|
|
87
|
+
are settings.
|
|
88
|
+
- When somebody **leaves**, the workspace gets a **return list** of what is still recorded as theirs.
|
|
89
|
+
It is raised by core's own "member removed" event, and by the People module's status change if that
|
|
90
|
+
module is installed — Inventory works without it and declares no dependency on it. Nothing is moved:
|
|
91
|
+
custody changes because a person did something, and a hook that quietly returned an item would write
|
|
92
|
+
a handover nobody performed into the one record a company later argues from.
|
|
93
|
+
- Switching the module on for a workspace **seeds five categories** — Laptops, Phones, Monitors,
|
|
94
|
+
Furniture, Vehicles — so the asset form's picker is not empty on day one. All five are renamable and
|
|
95
|
+
archivable, and switching the module off and on again never seeds a second time or re-creates one
|
|
96
|
+
somebody renamed.
|
|
29
97
|
- **Settings → Inventory → General** sets the shape of an asset tag — the prefix and how many digits
|
|
30
98
|
it is padded to, so a workspace that already labels its laptops `LT-` keeps doing that. The number
|
|
31
99
|
itself is a counter in the database rather than a setting: a number an administrator can edit is a
|
|
32
|
-
number that produces a duplicate tag.
|
|
33
|
-
|
|
100
|
+
number that produces a duplicate tag. The same page sets the two notice windows: how far ahead of a
|
|
101
|
+
warranty expiring somebody is told, and how long an item may be at a repairer before somebody is
|
|
102
|
+
asked to chase it.
|
|
103
|
+
- Five permissions gate it: `inventory.asset.view`, `inventory.asset.manage`,
|
|
104
|
+
`inventory.custody.manage`, `inventory.repair.manage` and `inventory.category.manage`. Reading who
|
|
105
|
+
holds what and what has been repaired rides `asset.view` rather than a key of its own — "who has
|
|
106
|
+
the projector" and "where is it" are the questions an asset register exists to answer, and the
|
|
107
|
+
custodian is already a field of every row the list returns.
|
|
108
|
+
- Three **capabilities** decide how much of it a workspace has: `core` is required and always on,
|
|
109
|
+
and `repairs` and `attachments` are switches an administrator can turn off. A capability that is
|
|
110
|
+
off answers **404, not 403** — a workspace that does not record repairs has no such surface, so
|
|
111
|
+
the API says the same thing the hidden tab does. Switching one off destroys nothing: the rows stay
|
|
112
|
+
exactly where they were and come back when it is switched on again.
|
|
34
113
|
- Its strings ship in the five languages the platform speaks — English, Arabic, German, Persian,
|
|
35
114
|
Turkish.
|
|
36
115
|
|
|
37
|
-
The
|
|
38
|
-
|
|
39
|
-
|
|
116
|
+
The API is twenty-four procedures under `/api/inventory`:
|
|
117
|
+
`assets.{list,get,create,update,archive,history}`,
|
|
118
|
+
`custody.{assign,transfer,return,history,byUser}`,
|
|
119
|
+
`categories.{list,create,update,archive,reorder}`,
|
|
120
|
+
`repairs.{list,create,update,complete}`,
|
|
121
|
+
`attachments.{list,add,remove}` and
|
|
122
|
+
`stats.summary`.
|
|
123
|
+
An asset's custom values are settable by nothing, and wait on custom fields below.
|
|
124
|
+
|
|
125
|
+
Two more answers are reachable only from another service, over `kernel.call` —
|
|
126
|
+
`inventory.asset.byId` and `inventory.assets.byCustodian`. They run with elevated access and are
|
|
127
|
+
refused to anybody who is not a service, so that a module holding an id never has to learn the shape
|
|
128
|
+
of `mod_inventory`.
|
|
40
129
|
|
|
41
130
|
## Not built yet
|
|
42
131
|
|
|
43
|
-
Nothing here has a screen.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- **Custody** — who is holding an item now, who held it before, and handing it on.
|
|
49
|
-
- **History** — an append-only timeline of every change to an asset. The writes exist: create,
|
|
50
|
-
update, archive and restore each append an `asset_history` row, and the integration suite holds
|
|
51
|
-
them to it. Nothing reads them back — there is no procedure and no screen.
|
|
52
|
-
- **Repairs** — what went away for repair, to whom, what it cost and when it came back.
|
|
53
|
-
- **Categories** — the column and the list filter exist; nothing can create a category.
|
|
132
|
+
Nothing here has a screen. It is a table in `mod_inventory` and nothing more. A register is worth
|
|
133
|
+
designing whole, and a migration that adds a column later is cheaper than one that reshapes a table.
|
|
134
|
+
A table is not a feature, and this section stays until each one ships.
|
|
135
|
+
|
|
54
136
|
- **Custom fields** — a workspace's own fields on an asset. `assets.custom` holds the values;
|
|
55
137
|
nothing defines a field, so `create` and `update` refuse to write into it at all.
|
|
56
|
-
- **Attachments** — receipts, warranties and manuals against an asset or a repair.
|
|
57
138
|
|
|
58
139
|
Further off, and with nothing in the schema yet: locations and stock control (bins, counts, reorder
|
|
59
140
|
points), purchasing, depreciation, and reservations.
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
* the rows stay exactly where they are. Anything that would need data thrown away to reverse is
|
|
21
21
|
* not a capability, however much it looks like one.
|
|
22
22
|
*
|
|
23
|
-
* The rest arrive with the work that makes them mean something: `
|
|
24
|
-
* `
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
23
|
+
* The rest arrive with the work that makes them mean something: `labels` with the finished register;
|
|
24
|
+
* `custom_fields` and `locations` beside them; `stock` and `procurement` with stock control;
|
|
25
|
+
* `depreciation` and `reservations` last. Adding one here before its procedures exist is the mistake
|
|
26
|
+
* this comment is here to prevent.
|
|
27
27
|
*/
|
|
28
28
|
export declare const inventoryCapabilities: readonly {
|
|
29
29
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/contract/capabilities.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/contract/capabilities.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;GAiDhC,CAAA;AAEF,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAA;AAEhF;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAM3E,CAAA"}
|
|
@@ -21,22 +21,58 @@ import { defineCapabilities } from '@kernhq/contracts';
|
|
|
21
21
|
* the rows stay exactly where they are. Anything that would need data thrown away to reverse is
|
|
22
22
|
* not a capability, however much it looks like one.
|
|
23
23
|
*
|
|
24
|
-
* The rest arrive with the work that makes them mean something: `
|
|
25
|
-
* `
|
|
26
|
-
* `
|
|
27
|
-
*
|
|
24
|
+
* The rest arrive with the work that makes them mean something: `labels` with the finished register;
|
|
25
|
+
* `custom_fields` and `locations` beside them; `stock` and `procurement` with stock control;
|
|
26
|
+
* `depreciation` and `reservations` last. Adding one here before its procedures exist is the mistake
|
|
27
|
+
* this comment is here to prevent.
|
|
28
28
|
*/
|
|
29
29
|
export const inventoryCapabilities = defineCapabilities([
|
|
30
30
|
{
|
|
31
31
|
id: 'core',
|
|
32
32
|
label: 'Assets',
|
|
33
|
-
// Says only what the module answers today
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
description: 'The asset register: what the company owns
|
|
33
|
+
// Says only what the module answers today, which is the rule this line has been held to in both
|
|
34
|
+
// directions: custody and the change history were taken *out* of it while neither had a
|
|
35
|
+
// procedure, and put back in the change that gave them one.
|
|
36
|
+
description: 'The asset register: what the company owns item by item, who is holding each one, and how each one got there',
|
|
37
37
|
required: true,
|
|
38
38
|
level: 1,
|
|
39
39
|
},
|
|
40
|
+
{
|
|
41
|
+
/**
|
|
42
|
+
* The first capability anyone can actually switch, and the reason it is one is the shape of the
|
|
43
|
+
* customers: an office that hands out laptops wants a register and never records a repair,
|
|
44
|
+
* while a company with a workshop wants little else. One answer for the whole workspace, not
|
|
45
|
+
* per person — so a capability rather than a permission — and switching it off writes a boolean
|
|
46
|
+
* into module settings and leaves every repair row exactly where it was.
|
|
47
|
+
*
|
|
48
|
+
* `dependsOn: ['core']` says the obvious out loud: a repair is a trip *an asset* made, so there
|
|
49
|
+
* is nothing to record without the register. It also means the closure prunes this
|
|
50
|
+
* automatically if `core` could ever be off, rather than each screen remembering to check two
|
|
51
|
+
* switches.
|
|
52
|
+
*/
|
|
53
|
+
id: 'repairs',
|
|
54
|
+
label: 'Repairs',
|
|
55
|
+
description: 'What went away to be fixed, to whom, what it cost and when it came back — and the `under repair` status that follows an open one',
|
|
56
|
+
dependsOn: ['core'],
|
|
57
|
+
defaultEnabled: true,
|
|
58
|
+
level: 1,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
/**
|
|
62
|
+
* Files against an asset or a repair — a purchase receipt, a warranty card, a repair invoice.
|
|
63
|
+
*
|
|
64
|
+
* Separate from `repairs` because plenty of workspaces want one without the other: an office
|
|
65
|
+
* that keeps receipts and never records a repair, and a workshop that records repairs and keeps
|
|
66
|
+
* no paperwork. Switching it off hides the Files tab and answers 404; the rows and the files in
|
|
67
|
+
* core's storage are untouched, which is what makes it a capability rather than a migration.
|
|
68
|
+
*/
|
|
69
|
+
id: 'attachments',
|
|
70
|
+
label: 'Files',
|
|
71
|
+
description: 'Receipts, warranties and manuals kept against an asset or one of its repairs',
|
|
72
|
+
dependsOn: ['core'],
|
|
73
|
+
defaultEnabled: true,
|
|
74
|
+
level: 1,
|
|
75
|
+
},
|
|
40
76
|
]);
|
|
41
77
|
/**
|
|
42
78
|
* Which procedures sit behind which capability.
|
|
@@ -48,5 +84,11 @@ export const inventoryCapabilities = defineCapabilities([
|
|
|
48
84
|
* A procedure absent from this map belongs to the module as a whole and is reachable whenever
|
|
49
85
|
* Inventory is on — which for `core` is always, because it is `required`.
|
|
50
86
|
*/
|
|
51
|
-
export const inventoryCapabilityProcedures = {
|
|
87
|
+
export const inventoryCapabilityProcedures = {
|
|
88
|
+
repairs: ['repairs.list', 'repairs.create', 'repairs.update', 'repairs.complete'],
|
|
89
|
+
attachments: ['attachments.list', 'attachments.add', 'attachments.remove'],
|
|
90
|
+
// `stats.summary` is deliberately absent. It counts assets, which is `core`, and it answers
|
|
91
|
+
// `outForRepair: null` rather than a number when the workspace has `repairs` off — a count line
|
|
92
|
+
// and a dashboard card must not disappear because a *different* feature is switched off.
|
|
93
|
+
};
|
|
52
94
|
//# sourceMappingURL=capabilities.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/contract/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;IACtD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,QAAQ;QACf,
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/contract/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;IACtD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,QAAQ;QACf,gGAAgG;QAChG,wFAAwF;QACxF,4DAA4D;QAC5D,WAAW,EACT,6GAA6G;QAC/G,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC;KACT;IACD;QACE;;;;;;;;;;;WAWG;QACH,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,kIAAkI;QACpI,SAAS,EAAE,CAAC,MAAM,CAAC;QACnB,cAAc,EAAE,IAAI;QACpB,KAAK,EAAE,CAAC;KACT;IACD;QACE;;;;;;;WAOG;QACH,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,OAAO;QACd,WAAW,EAAE,8EAA8E;QAC3F,SAAS,EAAE,CAAC,MAAM,CAAC;QACnB,cAAc,EAAE,IAAI;QACpB,KAAK,EAAE,CAAC;KACT;CACF,CAAC,CAAA;AAIF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAsC;IAC9E,OAAO,EAAE,CAAC,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,CAAC;IACjF,WAAW,EAAE,CAAC,kBAAkB,EAAE,iBAAiB,EAAE,oBAAoB,CAAC;IAC1E,4FAA4F;IAC5F,gGAAgG;IAChG,yFAAyF;CAC1F,CAAA"}
|
|
@@ -29,5 +29,48 @@ export declare const inventoryEvents: {
|
|
|
29
29
|
assetId: z.ZodUUID;
|
|
30
30
|
workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
|
|
31
31
|
}, z.core.$strip>>;
|
|
32
|
+
/**
|
|
33
|
+
* One event for all three of assign, transfer and return, because a subscriber cares that the
|
|
34
|
+
* answer to "who has it" moved and not by which verb.
|
|
35
|
+
*
|
|
36
|
+
* `userId` is null when it came back to stock and `previousUserId` is null when it went out from
|
|
37
|
+
* stock, so the pair says which of the three happened without a fourth field claiming to. Both
|
|
38
|
+
* are ids, like everything else here: a subscriber that needs the person asks core with its own
|
|
39
|
+
* principal, so an event cannot become a way to read a name past a permission check.
|
|
40
|
+
*
|
|
41
|
+
* Nothing subscribes to it inside this module. It exists for what is outside — an offboarding
|
|
42
|
+
* automation that asks what somebody still holds, most obviously — which is the whole reason a
|
|
43
|
+
* module emits events rather than only writing its own history row.
|
|
44
|
+
*/
|
|
45
|
+
custodyChanged: import("@kernhq/contracts").EventDef<"inventory.custody.changed", z.ZodObject<{
|
|
46
|
+
assetId: z.ZodUUID;
|
|
47
|
+
workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
|
|
48
|
+
userId: z.ZodNullable<z.ZodUUID>;
|
|
49
|
+
previousUserId: z.ZodNullable<z.ZodUUID>;
|
|
50
|
+
}, z.core.$strip>>;
|
|
51
|
+
/**
|
|
52
|
+
* An item went to a repairer, and an item came back.
|
|
53
|
+
*
|
|
54
|
+
* Two events rather than one, where custody has one for three verbs — because a subscriber to
|
|
55
|
+
* custody cares only that "who has it" moved, while these two are opposite facts with opposite
|
|
56
|
+
* reactions: something outside this module wants to tell the holder their laptop has gone, and
|
|
57
|
+
* something else wants to tell them it is back. A single `repair.changed` would make every
|
|
58
|
+
* subscriber re-read the row to find out which happened.
|
|
59
|
+
*
|
|
60
|
+
* No `repair.updated`: correcting a vendor or filling in an invoice a week later is not a fact
|
|
61
|
+
* anything outside this module reacts to, and the screens that do care are refreshed by
|
|
62
|
+
* `kernel.realtime.change`, which needs no declaration. Declaring one so the set looks symmetrical
|
|
63
|
+
* is the same lie as a capability nothing checks.
|
|
64
|
+
*/
|
|
65
|
+
repairOpened: import("@kernhq/contracts").EventDef<"inventory.repair.opened", z.ZodObject<{
|
|
66
|
+
assetId: z.ZodUUID;
|
|
67
|
+
repairId: z.ZodUUID;
|
|
68
|
+
workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
|
|
69
|
+
}, z.core.$strip>>;
|
|
70
|
+
repairCompleted: import("@kernhq/contracts").EventDef<"inventory.repair.completed", z.ZodObject<{
|
|
71
|
+
assetId: z.ZodUUID;
|
|
72
|
+
repairId: z.ZodUUID;
|
|
73
|
+
workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
|
|
74
|
+
}, z.core.$strip>>;
|
|
32
75
|
};
|
|
33
76
|
//# sourceMappingURL=events.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;IAa1B;;;;;;OAMG;;;;;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;IAa1B;;;;;;OAMG;;;;;IAKH;;;;;;;;;;;;OAYG;;;;;;;IAUH;;;;;;;;;;;;;OAaG;;;;;;;;;;;CAcJ,CAAA"}
|
package/dist/contract/events.js
CHANGED
|
@@ -18,5 +18,45 @@ export const inventoryEvents = {
|
|
|
18
18
|
* better, recording `retired` and `restored` separately.
|
|
19
19
|
*/
|
|
20
20
|
assetRestored: defineEvent('inventory.asset.restored', z.object({ assetId: z.uuid(), workspaceId: WorkspaceId })),
|
|
21
|
+
/**
|
|
22
|
+
* One event for all three of assign, transfer and return, because a subscriber cares that the
|
|
23
|
+
* answer to "who has it" moved and not by which verb.
|
|
24
|
+
*
|
|
25
|
+
* `userId` is null when it came back to stock and `previousUserId` is null when it went out from
|
|
26
|
+
* stock, so the pair says which of the three happened without a fourth field claiming to. Both
|
|
27
|
+
* are ids, like everything else here: a subscriber that needs the person asks core with its own
|
|
28
|
+
* principal, so an event cannot become a way to read a name past a permission check.
|
|
29
|
+
*
|
|
30
|
+
* Nothing subscribes to it inside this module. It exists for what is outside — an offboarding
|
|
31
|
+
* automation that asks what somebody still holds, most obviously — which is the whole reason a
|
|
32
|
+
* module emits events rather than only writing its own history row.
|
|
33
|
+
*/
|
|
34
|
+
custodyChanged: defineEvent('inventory.custody.changed', z.object({
|
|
35
|
+
assetId: z.uuid(),
|
|
36
|
+
workspaceId: WorkspaceId,
|
|
37
|
+
userId: z.uuid().nullable(),
|
|
38
|
+
previousUserId: z.uuid().nullable(),
|
|
39
|
+
})),
|
|
40
|
+
/**
|
|
41
|
+
* An item went to a repairer, and an item came back.
|
|
42
|
+
*
|
|
43
|
+
* Two events rather than one, where custody has one for three verbs — because a subscriber to
|
|
44
|
+
* custody cares only that "who has it" moved, while these two are opposite facts with opposite
|
|
45
|
+
* reactions: something outside this module wants to tell the holder their laptop has gone, and
|
|
46
|
+
* something else wants to tell them it is back. A single `repair.changed` would make every
|
|
47
|
+
* subscriber re-read the row to find out which happened.
|
|
48
|
+
*
|
|
49
|
+
* No `repair.updated`: correcting a vendor or filling in an invoice a week later is not a fact
|
|
50
|
+
* anything outside this module reacts to, and the screens that do care are refreshed by
|
|
51
|
+
* `kernel.realtime.change`, which needs no declaration. Declaring one so the set looks symmetrical
|
|
52
|
+
* is the same lie as a capability nothing checks.
|
|
53
|
+
*/
|
|
54
|
+
repairOpened: defineEvent('inventory.repair.opened', z.object({ assetId: z.uuid(), repairId: z.uuid(), workspaceId: WorkspaceId })),
|
|
55
|
+
repairCompleted: defineEvent('inventory.repair.completed', z.object({ assetId: z.uuid(), repairId: z.uuid(), workspaceId: WorkspaceId })),
|
|
56
|
+
// No category and no attachment events, deliberately. An event is for something outside this
|
|
57
|
+
// module to react to, and nothing outside it has an opinion about a workspace renaming "Laptops"
|
|
58
|
+
// or filing a receipt; the screens that do care are refreshed by `kernel.realtime.change`, which
|
|
59
|
+
// needs no declaration. Declaring one here so the list looks complete is the same lie as a
|
|
60
|
+
// capability nothing checks.
|
|
21
61
|
};
|
|
22
62
|
//# sourceMappingURL=events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD,aAAa,EAAE,WAAW,CACxB,0BAA0B,EAC1B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD;;;;;;OAMG;IACH,aAAa,EAAE,WAAW,CACxB,0BAA0B,EAC1B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD,aAAa,EAAE,WAAW,CACxB,0BAA0B,EAC1B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD;;;;;;OAMG;IACH,aAAa,EAAE,WAAW,CACxB,0BAA0B,EAC1B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,WAAW,CACzB,2BAA2B,EAC3B,CAAC,CAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE;QACjB,WAAW,EAAE,WAAW;QACxB,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;QAC3B,cAAc,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;KACpC,CAAC,CACH;IACD;;;;;;;;;;;;;OAaG;IACH,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC9E;IACD,eAAe,EAAE,WAAW,CAC1B,4BAA4B,EAC5B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC9E;IACD,6FAA6F;IAC7F,iGAAiG;IACjG,iGAAiG;IACjG,2FAA2F;IAC3F,6BAA6B;CAC9B,CAAA"}
|
package/dist/contract/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
|
package/dist/contract/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
|