feat: add hash and thumbnail columns to file, sort column and order method to manifestattachment

This commit is contained in:
seb
2026-09-18 01:21:54 +02:00
parent 980eda629b
commit cfb7f86f0e
11 changed files with 378 additions and 104 deletions

View File

@@ -18,10 +18,15 @@ Attaches a file to an existing manifest.
| `input.mimetype` | `string` | The MIME type of the file (e.g. `'application/pdf'`). |
| `input.name` | `string` | The file name. |
| `input.content` | `Buffer` | The file content. |
| `[input.sort]` | `number` | Optional display sort order. |
| `[input.hash]` | `string` | The file hash (auto-computed SHA-256 if omitted). |
| `[input.thumbavif]` | `Buffer` | Optional AVIF thumbnail buffer. |
| `[input.thumbx]` | `number` | Optional thumbnail width. |
| `[input.thumby]` | `number` | Optional thumbnail height. |
**Returns**
`Promise<{id: number, manifestId: number, fileId: number, mimetype: string, name: string}>` — The created attachment record.
`Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>` — The created attachment record.
**Throws**
@@ -35,7 +40,7 @@ const attachment = await store.manifestattachment.add(42, 1, {
name: 'invoice.pdf',
content: fileBuffer,
});
// => { id, manifestId, fileId, mimetype, name }
// => { id, manifestId, fileId, sort, hash, mimetype, name, thumbavif, thumbx, thumby }
```
## `manifestattachment.getById`
@@ -54,7 +59,7 @@ Gets an attachment for a manifest by its attachment ID or file ID, including ful
**Returns**
`Promise<{id: number, manifestId: number, fileId: number, mimetype: string, name: string, content: Buffer}>` — The attachment record with binary content.
`Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>` — The attachment record with binary content.
**Throws**
@@ -64,7 +69,7 @@ Gets an attachment for a manifest by its attachment ID or file ID, including ful
```js
const attachment = await store.manifestattachment.getById(42, 1, 5);
// => { id, manifestId, fileId, mimetype, name, content }
// => { id, manifestId, fileId, sort, hash, mimetype, name, content, thumbavif, thumbx, thumby }
```
## `manifestattachment.listByManifestId`
@@ -82,7 +87,7 @@ Lists all attachments for a given manifest, without loading their full binary co
**Returns**
`Promise<Array<{id: number, manifestId: number, fileId: number, mimetype: string, name: string, size: number}>>` — A list of attachment metadata objects.
`Promise<Array<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>>` — A list of attachment metadata objects.
**Throws**
@@ -92,7 +97,37 @@ Lists all attachments for a given manifest, without loading their full binary co
```js
const attachments = await store.manifestattachment.listByManifestId(42, 1);
// => [{ id, manifestId, fileId, mimetype, name, size }]
// => [{ id, manifestId, fileId, sort, hash, mimetype, name, size, thumbavif, thumbx, thumby }]
```
## `manifestattachment.order`
*Source: `src/store/manifestattachment/order.js`*
Reorders the attachments of a manifest.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `manifestId` | `number` | The manifest ID. |
| `input` | `object|Array<number>` | The reorder input or array of IDs. |
| `[input.ids]` | `Array<number>` | The attachment ids in their new order. |
**Returns**
`Promise<{ids: Array<number>}>` — The applied order.
**Throws**
`Error` — If the order is invalid.
**Example**
```js
const result = await store.manifestattachment.order(42, 1, { ids: [12, 10, 15] });
// => { ids: [12, 10, 15] }
```
## `manifestattachment.remove`

7
API.md
View File

@@ -123,9 +123,10 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `list(googleAuthId: number): Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- [manifestattachment](API.manifestattachment.md)
- `add(googleAuthId: number, manifestId: number, input: {mimetype: string, name: string, content: Buffer}): Promise<{id: number, manifestId: number, fileId: number, mimetype: string, name: string}>`
- `getById(googleAuthId: number, manifestId: number, id: number): Promise<{id: number, manifestId: number, fileId: number, mimetype: string, name: string, content: Buffer}>`
- `listByManifestId(googleAuthId: number, manifestId: number): Promise<Array<{id: number, manifestId: number, fileId: number, mimetype: string, name: string, size: number}>>`
- `add(googleAuthId: number, manifestId: number, input: {mimetype: string, name: string, content: Buffer, sort?: number, hash?: string, thumbavif?: Buffer, thumbx?: number, thumby?: number}): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>`
- `getById(googleAuthId: number, manifestId: number, id: number): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>`
- `listByManifestId(googleAuthId: number, manifestId: number): Promise<Array<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>>`
- `order(googleAuthId: number, manifestId: number, input: {ids?: Array<number>}): Promise<{ids: Array<number>}>`
- `remove(googleAuthId: number, manifestId: number, id: number): Promise<void>`
- [posterminal](API.posterminal.md)
- `delete(googleAuthId: number, id: number): Promise<void>`

View File

@@ -55,7 +55,7 @@ digraph schema {
pic [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in1" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in2" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="3" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>pic</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="w" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">w</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="h" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">h</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="size" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">size</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">BIGINT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="hash" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">hash</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(64)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumbw" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumbw</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumbh" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumbh</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumbhash" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumbhash</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(64)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="source" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">source</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="fullbinary" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">fullbinary</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">VARBINARY(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumbbinary" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumbbinary</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">VARBINARY(MAX)</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
brandpic [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>brandpic</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="brand_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>brand_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="pic_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>pic_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sort" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">sort</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
articlepic [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>articlepic</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="article_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>article_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="pic_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>pic_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sort" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">sort</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
file [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="1" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>file</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="mimetype" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">mimetype</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="name" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">name</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="content" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">content</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">VARBINARY(MAX)</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
file [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="1" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>file</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="hash" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">hash</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(64)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="mimetype" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">mimetype</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="name" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">name</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="content" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">content</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">VARBINARY(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumbavif" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumbavif?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">VARBINARY(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumbx" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumbx?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="thumby" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">thumby?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
}
subgraph cluster_manifests {
label="Documents"; labeljust="l"; fontsize=15;
@@ -64,7 +64,7 @@ digraph schema {
manifestarticle [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="1" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifestarticle</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifest_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifest_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sku" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">sku?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="name" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">name?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="pu" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">pu</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="amount" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">amount</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="net" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">net</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="comment" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">comment?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="rebate" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">rebate</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="taxrate" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">taxrate</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
ledger [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>ledger</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifestarticle_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifestarticle_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="transaction_type" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">transaction_type</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="amount" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">amount</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">FLOAT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="transaction_text" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">transaction_text</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="hash_version" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">hash_version</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="prev_hash" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">prev_hash?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(128)</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
manifest [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in1" BGCOLOR="#33415c" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifest</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="entity_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>entity_id</B>?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="company_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>company_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="number" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">number</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="type" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">type</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="external_number" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">external_number?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">NVARCHAR(100)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="date" COLSPAN="2" BGCOLOR="#2a3242" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">date</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">DATETIME2</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
manifestattachment [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifestattachment</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifest_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifest_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="file_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>file_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR></TABLE>>];
manifestattachment [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#3f4c5f"><TR><TD COLSPAN="2" BGCOLOR="#33415c" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#e2e8f0" POINT-SIZE="12"><B>manifestattachment</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="manifest_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>manifest_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="file_id" COLSPAN="2" BGCOLOR="#2b3a52" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9"><B>file_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sort" COLSPAN="2" BGCOLOR="#272e3d" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#d8dee9">sort</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#8b95a7" POINT-SIZE="9">INT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
}
subgraph cluster_commerce {
label="Pricing"; labeljust="l"; fontsize=15;
@@ -139,7 +139,7 @@ digraph schema {
articlesupplier:price_id:e -> price:in0:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_articlesupplier_price", constraint=false, weight=1];
manifest:company_id:e -> company:in1:n [color="#ff6b6b", fontcolor="#ff6b6b", tooltip="FK_manifest_company", weight=3];
manifest:entity_id:e -> entity:in0:n [color="#6bdf9a", fontcolor="#6bdf9a", tooltip="FK_manifest_entity", constraint=false, weight=1];
manifestarticle:manifest_id:ne -> manifest:in0:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_manifestarticle_manifest", weight=3];
manifestarticle:manifest_id:se -> manifest:in0:n [color="#5aa7ff", fontcolor="#5aa7ff", tooltip="FK_manifestarticle_manifest", weight=3];
ledger:manifestarticle_id:sw -> manifestarticle:in0:n [color="#f5c15c", fontcolor="#f5c15c", tooltip="FK_ledger_manifestarticle", weight=3];
numbers:company_id:e -> company:in3:n [color="#c79bff", fontcolor="#c79bff", tooltip="FK_numbers_company", weight=3];
companyuser:google_auth_id:e -> google_auth:in0:n [color="#ff9f5a", fontcolor="#ff9f5a", tooltip="FK_companyuser_google_auth", constraint=false, weight=1];

View File

@@ -4,11 +4,11 @@
<!-- Generated by graphviz version 16.1.0 (0)
-->
<!-- Title: schema Pages: 1 -->
<svg width="4949pt" height="1786pt"
viewBox="0.00 0.00 4949.00 1786.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color:#1e2430;">
<svg width="4963pt" height="1786pt"
viewBox="0.00 0.00 4963.00 1786.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color:#1e2430;">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(36 1749.6)">
<title>schema</title>
<polygon fill="#1e2430" stroke="none" points="-36,36 -36,-1749.6 4913.27,-1749.6 4913.27,36 -36,36"/>
<polygon fill="#1e2430" stroke="none" points="-36,36 -36,-1749.6 4927.27,-1749.6 4927.27,36 -36,36"/>
<g id="clust1" class="cluster">
<title>cluster_identity</title>
<path fill="#202a3a" stroke="#3f4c5f" d="M670,-196.8C670,-196.8 1976,-196.8 1976,-196.8 1982,-196.8 1988,-202.8 1988,-208.8 1988,-208.8 1988,-1643.6 1988,-1643.6 1988,-1649.6 1982,-1655.6 1976,-1655.6 1976,-1655.6 670,-1655.6 670,-1655.6 664,-1655.6 658,-1649.6 658,-1643.6 658,-1643.6 658,-208.8 658,-208.8 658,-202.8 664,-196.8 670,-196.8"/>
@@ -26,7 +26,7 @@
</g>
<g id="clust4" class="cluster">
<title>cluster_media</title>
<path fill="#202a3a" stroke="#3f4c5f" d="M3986,-252.4C3986,-252.4 4605,-252.4 4605,-252.4 4611,-252.4 4617,-258.4 4617,-264.4 4617,-264.4 4617,-1645.6 4617,-1645.6 4617,-1651.6 4611,-1657.6 4605,-1657.6 4605,-1657.6 3986,-1657.6 3986,-1657.6 3980,-1657.6 3974,-1651.6 3974,-1645.6 3974,-1645.6 3974,-264.4 3974,-264.4 3974,-258.4 3980,-252.4 3986,-252.4"/>
<path fill="#202a3a" stroke="#3f4c5f" d="M3986,-252.4C3986,-252.4 4619,-252.4 4619,-252.4 4625,-252.4 4631,-258.4 4631,-264.4 4631,-264.4 4631,-1645.6 4631,-1645.6 4631,-1651.6 4625,-1657.6 4619,-1657.6 4619,-1657.6 3986,-1657.6 3986,-1657.6 3980,-1657.6 3974,-1651.6 3974,-1645.6 3974,-1645.6 3974,-264.4 3974,-264.4 3974,-258.4 3980,-252.4 3986,-252.4"/>
<text xml:space="preserve" text-anchor="middle" x="4002.43" y="-1640.1" font-family="Helvetica,sans-Serif" font-size="15.00" fill="#e2e8f0">Media</text>
</g>
<g id="clust5" class="cluster">
@@ -1597,27 +1597,43 @@
<!-- file -->
<g id="node32" class="node">
<title>file</title>
<polygon fill="#33415c" stroke="none" points="4444.9,-1491.4 4444.9,-1519.8 4595.1,-1519.8 4595.1,-1491.4 4444.9,-1491.4"/>
<polygon fill="none" stroke="#3f4c5f" points="4444.9,-1491.4 4444.9,-1519.8 4595.1,-1519.8 4595.1,-1491.4 4444.9,-1491.4"/>
<polygon fill="#33415c" stroke="none" points="4446.9,-1513.8 4446.9,-1517.8 4593.1,-1517.8 4593.1,-1513.8 4446.9,-1513.8"/>
<text xml:space="preserve" text-anchor="start" x="4511.33" y="-1501" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">file</text>
<polygon fill="#2b3a52" stroke="none" points="4444.9,-1464.6 4444.9,-1491.4 4595.1,-1491.4 4595.1,-1464.6 4444.9,-1464.6"/>
<polygon fill="none" stroke="#3f4c5f" points="4444.9,-1464.6 4444.9,-1491.4 4595.1,-1491.4 4595.1,-1464.6 4444.9,-1464.6"/>
<text xml:space="preserve" text-anchor="start" x="4449.9" y="-1474.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">id</text>
<text xml:space="preserve" text-anchor="start" x="4560.6" y="-1476.3" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="4577.6" y="-1476.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">PK</text>
<polygon fill="#272e3d" stroke="none" points="4444.9,-1437.8 4444.9,-1464.6 4595.1,-1464.6 4595.1,-1437.8 4444.9,-1437.8"/>
<polygon fill="none" stroke="#3f4c5f" points="4444.9,-1437.8 4444.9,-1464.6 4595.1,-1464.6 4595.1,-1437.8 4444.9,-1437.8"/>
<text xml:space="preserve" text-anchor="start" x="4449.9" y="-1445.6" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">mimetype</text>
<text xml:space="preserve" text-anchor="start" x="4518.58" y="-1447.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(255)</text>
<polygon fill="#2a3242" stroke="none" points="4444.9,-1411 4444.9,-1437.8 4595.1,-1437.8 4595.1,-1411 4444.9,-1411"/>
<polygon fill="none" stroke="#3f4c5f" points="4444.9,-1411 4444.9,-1437.8 4595.1,-1437.8 4595.1,-1411 4444.9,-1411"/>
<text xml:space="preserve" text-anchor="start" x="4449.9" y="-1418.8" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">name</text>
<text xml:space="preserve" text-anchor="start" x="4518.58" y="-1420.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(255)</text>
<polygon fill="#272e3d" stroke="none" points="4444.9,-1384.2 4444.9,-1411 4595.1,-1411 4595.1,-1384.2 4444.9,-1384.2"/>
<polygon fill="none" stroke="#3f4c5f" points="4444.9,-1384.2 4444.9,-1411 4595.1,-1411 4595.1,-1384.2 4444.9,-1384.2"/>
<text xml:space="preserve" text-anchor="start" x="4449.9" y="-1392" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">content</text>
<text xml:space="preserve" text-anchor="start" x="4512.59" y="-1394" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">VARBINARY(MAX)</text>
<polygon fill="#33415c" stroke="none" points="4445,-1545 4445,-1573.4 4609,-1573.4 4609,-1545 4445,-1545"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1545 4445,-1573.4 4609,-1573.4 4609,-1545 4445,-1545"/>
<polygon fill="#33415c" stroke="none" points="4447,-1567.4 4447,-1571.4 4607,-1571.4 4607,-1567.4 4447,-1567.4"/>
<text xml:space="preserve" text-anchor="start" x="4518.33" y="-1554.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">file</text>
<polygon fill="#2b3a52" stroke="none" points="4445,-1518.2 4445,-1545 4609,-1545 4609,-1518.2 4445,-1518.2"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1518.2 4445,-1545 4609,-1545 4609,-1518.2 4445,-1518.2"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1528.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">id</text>
<text xml:space="preserve" text-anchor="start" x="4574.5" y="-1529.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="4591.49" y="-1529.9" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">PK</text>
<polygon fill="#272e3d" stroke="none" points="4445,-1491.4 4445,-1518.2 4609,-1518.2 4609,-1491.4 4445,-1491.4"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1491.4 4445,-1518.2 4609,-1518.2 4609,-1491.4 4445,-1491.4"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1499.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">hash</text>
<text xml:space="preserve" text-anchor="start" x="4537.49" y="-1501.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(64)</text>
<polygon fill="#2a3242" stroke="none" points="4445,-1464.6 4445,-1491.4 4609,-1491.4 4609,-1464.6 4445,-1464.6"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1464.6 4445,-1491.4 4609,-1491.4 4609,-1464.6 4445,-1464.6"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1472.4" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">mimetype</text>
<text xml:space="preserve" text-anchor="start" x="4532.48" y="-1474.4" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(255)</text>
<polygon fill="#272e3d" stroke="none" points="4445,-1437.8 4445,-1464.6 4609,-1464.6 4609,-1437.8 4445,-1437.8"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1437.8 4445,-1464.6 4609,-1464.6 4609,-1437.8 4445,-1437.8"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1445.6" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">name</text>
<text xml:space="preserve" text-anchor="start" x="4532.48" y="-1447.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">NVARCHAR(255)</text>
<polygon fill="#2a3242" stroke="none" points="4445,-1411 4445,-1437.8 4609,-1437.8 4609,-1411 4445,-1411"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1411 4445,-1437.8 4609,-1437.8 4609,-1411 4445,-1411"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1418.8" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">content</text>
<text xml:space="preserve" text-anchor="start" x="4526.49" y="-1420.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">VARBINARY(MAX)</text>
<polygon fill="#272e3d" stroke="none" points="4445,-1384.2 4445,-1411 4609,-1411 4609,-1384.2 4445,-1384.2"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1384.2 4445,-1411 4609,-1411 4609,-1384.2 4445,-1384.2"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1392" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">thumbavif?</text>
<text xml:space="preserve" text-anchor="start" x="4526.49" y="-1394" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">VARBINARY(MAX)</text>
<polygon fill="#2a3242" stroke="none" points="4445,-1357.4 4445,-1384.2 4609,-1384.2 4609,-1357.4 4445,-1357.4"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1357.4 4445,-1384.2 4609,-1384.2 4609,-1357.4 4445,-1357.4"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1365.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">thumbx?</text>
<text xml:space="preserve" text-anchor="start" x="4589.5" y="-1367.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT</text>
<polygon fill="#272e3d" stroke="none" points="4445,-1330.6 4445,-1357.4 4609,-1357.4 4609,-1330.6 4445,-1330.6"/>
<polygon fill="none" stroke="#3f4c5f" points="4445,-1330.6 4445,-1357.4 4609,-1357.4 4609,-1330.6 4445,-1330.6"/>
<text xml:space="preserve" text-anchor="start" x="4450" y="-1338.4" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">thumby?</text>
<text xml:space="preserve" text-anchor="start" x="4589.5" y="-1340.4" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT</text>
</g>
<!-- manifestarticle -->
<g id="node33" class="node">
@@ -1712,10 +1728,10 @@
</g>
<!-- manifestarticle&#45;&gt;manifest -->
<g id="edge51" class="edge">
<title>manifestarticle:ne&#45;&gt;manifest:n</title>
<title>manifestarticle:se&#45;&gt;manifest:n</title>
<g id="a_edge51"><a xlink:title="FK_manifestarticle_manifest">
<path fill="none" stroke="#5aa7ff" stroke-width="1.4" d="M420.68,-1115C420.68,-1115 419.64,-817.36 419.44,-761.56"/>
<polygon fill="#5aa7ff" stroke="#5aa7ff" stroke-width="1.4" points="421.89,-761.71 419.42,-754.72 416.99,-761.73 421.89,-761.71"/>
<path fill="none" stroke="#5aa7ff" stroke-width="1.4" d="M420.68,-1088.2C471.46,-1037.42 423.27,-842.42 419.62,-761.44"/>
<polygon fill="#5aa7ff" stroke="#5aa7ff" stroke-width="1.4" points="422.08,-761.66 419.46,-754.72 417.18,-761.78 422.08,-761.66"/>
</a>
</g>
</g>
@@ -1786,31 +1802,35 @@
<!-- manifestattachment -->
<g id="node36" class="node">
<title>manifestattachment</title>
<polygon fill="#33415c" stroke="none" points="503.6,-1050 503.6,-1074.4 628.4,-1074.4 628.4,-1050 503.6,-1050"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-1050 503.6,-1074.4 628.4,-1074.4 628.4,-1050 503.6,-1050"/>
<text xml:space="preserve" text-anchor="start" x="509.32" y="-1059.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">manifestattachment</text>
<polygon fill="#2b3a52" stroke="none" points="503.6,-1023.2 503.6,-1050 628.4,-1050 628.4,-1023.2 503.6,-1023.2"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-1023.2 503.6,-1050 628.4,-1050 628.4,-1023.2 503.6,-1023.2"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-1033.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">id</text>
<text xml:space="preserve" text-anchor="start" x="593.9" y="-1034.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="610.9" y="-1034.9" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">PK</text>
<polygon fill="#2b3a52" stroke="none" points="503.6,-996.4 503.6,-1023.2 628.4,-1023.2 628.4,-996.4 503.6,-996.4"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-996.4 503.6,-1023.2 628.4,-1023.2 628.4,-996.4 503.6,-996.4"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-1006.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">manifest_id</text>
<text xml:space="preserve" text-anchor="start" x="594.4" y="-1008.1" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="611.4" y="-1008.1" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">FK</text>
<polygon fill="#2b3a52" stroke="none" points="503.6,-969.6 503.6,-996.4 628.4,-996.4 628.4,-969.6 503.6,-969.6"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-969.6 503.6,-996.4 628.4,-996.4 628.4,-969.6 503.6,-969.6"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-979.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">file_id</text>
<text xml:space="preserve" text-anchor="start" x="594.4" y="-981.3" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="611.4" y="-981.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">FK</text>
<polygon fill="#33415c" stroke="none" points="503.6,-1063.4 503.6,-1087.8 628.4,-1087.8 628.4,-1063.4 503.6,-1063.4"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-1063.4 503.6,-1087.8 628.4,-1087.8 628.4,-1063.4 503.6,-1063.4"/>
<text xml:space="preserve" text-anchor="start" x="509.32" y="-1073" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">manifestattachment</text>
<polygon fill="#2b3a52" stroke="none" points="503.6,-1036.6 503.6,-1063.4 628.4,-1063.4 628.4,-1036.6 503.6,-1036.6"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-1036.6 503.6,-1063.4 628.4,-1063.4 628.4,-1036.6 503.6,-1036.6"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-1046.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">id</text>
<text xml:space="preserve" text-anchor="start" x="593.9" y="-1048.3" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="610.9" y="-1048.3" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">PK</text>
<polygon fill="#2b3a52" stroke="none" points="503.6,-1009.8 503.6,-1036.6 628.4,-1036.6 628.4,-1009.8 503.6,-1009.8"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-1009.8 503.6,-1036.6 628.4,-1036.6 628.4,-1009.8 503.6,-1009.8"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-1020" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">manifest_id</text>
<text xml:space="preserve" text-anchor="start" x="594.4" y="-1021.5" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="611.4" y="-1021.5" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">FK</text>
<polygon fill="#2b3a52" stroke="none" points="503.6,-983 503.6,-1009.8 628.4,-1009.8 628.4,-983 503.6,-983"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-983 503.6,-1009.8 628.4,-1009.8 628.4,-983 503.6,-983"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-993.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">file_id</text>
<text xml:space="preserve" text-anchor="start" x="594.4" y="-994.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT </text>
<text xml:space="preserve" text-anchor="start" x="611.4" y="-994.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="9.00" fill="#8b95a7">FK</text>
<polygon fill="#272e3d" stroke="none" points="503.6,-956.2 503.6,-983 628.4,-983 628.4,-956.2 503.6,-956.2"/>
<polygon fill="none" stroke="#3f4c5f" points="503.6,-956.2 503.6,-983 628.4,-983 628.4,-956.2 503.6,-956.2"/>
<text xml:space="preserve" text-anchor="start" x="508.6" y="-964" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d8dee9">sort</text>
<text xml:space="preserve" text-anchor="start" x="608.9" y="-966" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">INT</text>
</g>
<!-- manifestattachment&#45;&gt;file -->
<g id="edge62" class="edge">
<title>manifestattachment:e&#45;&gt;file:n</title>
<g id="a_edge62"><a xlink:title="FK_manifestattachment_file">
<path fill="none" stroke="#ff6f6f" stroke-width="1.4" d="M629.4,-983C655.14,-983 622.52,-1196.48 641,-1214.4 716.04,-1287.15 4301.2,-1262.7 4403,-1286.4 4498.66,-1308.67 4550.41,-1297.31 4603.1,-1380.2 4635.91,-1431.81 4646.08,-1474.3 4603.1,-1517.8 4579.47,-1541.71 4530.02,-1553.52 4521.32,-1527.6"/>
<polygon fill="#ff6f6f" stroke="#ff6f6f" stroke-width="1.4" points="4523.77,-1527.45 4520.31,-1520.9 4518.93,-1528.18 4523.77,-1527.45"/>
<path fill="none" stroke="#ff6f6f" stroke-width="1.4" d="M629.4,-996.4C653.65,-996.4 623.56,-1197.54 641,-1214.4 716.15,-1287.04 4299.03,-1275.71 4403,-1286.4 4499.27,-1296.29 4555.21,-1252.12 4617,-1326.6 4651.73,-1368.47 4655.25,-1532.72 4617,-1571.4 4591.18,-1597.51 4536.92,-1610.23 4528.2,-1581.2"/>
<polygon fill="#ff6f6f" stroke="#ff6f6f" stroke-width="1.4" points="4530.66,-1581.11 4527.29,-1574.5 4525.8,-1581.77 4530.66,-1581.11"/>
</a>
</g>
</g>
@@ -1818,8 +1838,8 @@
<g id="edge61" class="edge">
<title>manifestattachment:sw&#45;&gt;manifest:n</title>
<g id="a_edge61"><a xlink:title="FK_manifestattachment_manifest">
<path fill="none" stroke="#a98cff" stroke-width="1.4" d="M502.6,-996.4C427.99,-921.79 513.47,-864.51 518.37,-761.71"/>
<polygon fill="#a98cff" stroke="#a98cff" stroke-width="1.4" points="520.82,-761.77 518.54,-754.72 515.92,-761.66 520.82,-761.77"/>
<path fill="none" stroke="#a98cff" stroke-width="1.4" d="M502.6,-1009.8C484.5,-991.7 502.42,-977.78 503.6,-952.2 507.57,-866.28 517.88,-843.08 518.56,-761.24"/>
<polygon fill="#a98cff" stroke="#a98cff" stroke-width="1.4" points="521,-761.73 518.58,-754.72 516.1,-761.71 521,-761.73"/>
</a>
</g>
</g>
@@ -1924,7 +1944,7 @@
<g id="edge60" class="edge">
<title>posterminalcategory:e&#45;&gt;category:n</title>
<g id="a_edge60"><a xlink:title="FK_posterminalcategory_category">
<path fill="none" stroke="#4fc4e8" stroke-width="1.4" d="M177.29,-983C248.18,-983 166.25,-874.63 221,-829.6 979.85,-205.51 3961.79,-1184.68 4627,-461.6 4706.68,-374.99 4708.85,-281.36 4627,-196.8 4554.3,-121.69 2792.26,-234.89 2699.39,-141.82"/>
<path fill="none" stroke="#4fc4e8" stroke-width="1.4" d="M177.29,-983C248.18,-983 166.24,-874.62 221,-829.6 982.33,-203.64 3973.6,-1186.88 4641,-461.6 4720.69,-375 4722.86,-281.35 4641,-196.8 4567.77,-121.16 2792.96,-235.6 2699.42,-141.88"/>
<polygon fill="#4fc4e8" stroke="#4fc4e8" stroke-width="1.4" points="2701.71,-140.99 2696.49,-135.71 2697.28,-143.09 2701.71,-140.99"/>
</a>
</g>
@@ -1941,30 +1961,30 @@
<!-- __legend -->
<g id="node42" class="node">
<title>__legend</title>
<polygon fill="#2a3242" stroke="none" points="4678.73,-1373.4 4678.73,-1530.6 4869.27,-1530.6 4869.27,-1373.4 4678.73,-1373.4"/>
<polygon fill="#33415c" stroke="none" points="4678.73,-1507.4 4678.73,-1530.6 4869.27,-1530.6 4869.27,-1507.4 4678.73,-1507.4"/>
<polygon fill="none" stroke="#3f4c5f" points="4678.73,-1507.4 4678.73,-1530.6 4869.27,-1530.6 4869.27,-1507.4 4678.73,-1507.4"/>
<text xml:space="preserve" text-anchor="start" x="4754.44" y="-1516.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="11.00" fill="#e2e8f0">Legend</text>
<polygon fill="none" stroke="#3f4c5f" points="4678.73,-1480.6 4678.73,-1507.4 4708.18,-1507.4 4708.18,-1480.6 4678.73,-1480.6"/>
<text xml:space="preserve" text-anchor="start" x="4683.73" y="-1490.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">PK</text>
<polygon fill="none" stroke="#3f4c5f" points="4708.18,-1480.6 4708.18,-1507.4 4869.27,-1507.4 4869.27,-1480.6 4708.18,-1480.6"/>
<text xml:space="preserve" text-anchor="start" x="4713.18" y="-1490.4" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">primary key</text>
<polygon fill="none" stroke="#3f4c5f" points="4678.73,-1453.8 4678.73,-1480.6 4708.18,-1480.6 4708.18,-1453.8 4678.73,-1453.8"/>
<text xml:space="preserve" text-anchor="start" x="4683.73" y="-1464" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">FK</text>
<polygon fill="none" stroke="#3f4c5f" points="4708.18,-1453.8 4708.18,-1480.6 4869.27,-1480.6 4869.27,-1453.8 4708.18,-1453.8"/>
<text xml:space="preserve" text-anchor="start" x="4713.18" y="-1463.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">foreign key</text>
<polygon fill="none" stroke="#3f4c5f" points="4678.73,-1427 4678.73,-1453.8 4708.18,-1453.8 4708.18,-1427 4678.73,-1427"/>
<text xml:space="preserve" text-anchor="start" x="4683.73" y="-1437.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">?</text>
<polygon fill="none" stroke="#3f4c5f" points="4708.18,-1427 4708.18,-1453.8 4869.27,-1453.8 4869.27,-1427 4708.18,-1427"/>
<text xml:space="preserve" text-anchor="start" x="4713.18" y="-1436.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">nullable column</text>
<polygon fill="none" stroke="#3f4c5f" points="4678.73,-1400.2 4678.73,-1427 4708.18,-1427 4708.18,-1400.2 4678.73,-1400.2"/>
<text xml:space="preserve" text-anchor="start" x="4683.73" y="-1408" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#8b95a7">—</text>
<polygon fill="none" stroke="#3f4c5f" points="4708.18,-1400.2 4708.18,-1427 4869.27,-1427 4869.27,-1400.2 4708.18,-1400.2"/>
<text xml:space="preserve" text-anchor="start" x="4713.18" y="-1410" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">FOREIGN KEY constraint</text>
<polygon fill="none" stroke="#3f4c5f" points="4678.73,-1373.4 4678.73,-1400.2 4708.18,-1400.2 4708.18,-1373.4 4678.73,-1373.4"/>
<text xml:space="preserve" text-anchor="start" x="4683.73" y="-1381.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#8b95a7">&#45; &#45;</text>
<polygon fill="none" stroke="#3f4c5f" points="4708.18,-1373.4 4708.18,-1400.2 4869.27,-1400.2 4869.27,-1373.4 4708.18,-1373.4"/>
<text xml:space="preserve" text-anchor="start" x="4713.18" y="-1383.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">naming convention only, no constraint</text>
<polygon fill="#2a3242" stroke="none" points="4692.73,-1373.4 4692.73,-1530.6 4883.27,-1530.6 4883.27,-1373.4 4692.73,-1373.4"/>
<polygon fill="#33415c" stroke="none" points="4692.73,-1507.4 4692.73,-1530.6 4883.27,-1530.6 4883.27,-1507.4 4692.73,-1507.4"/>
<polygon fill="none" stroke="#3f4c5f" points="4692.73,-1507.4 4692.73,-1530.6 4883.27,-1530.6 4883.27,-1507.4 4692.73,-1507.4"/>
<text xml:space="preserve" text-anchor="start" x="4768.44" y="-1516.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="11.00" fill="#e2e8f0">Legend</text>
<polygon fill="none" stroke="#3f4c5f" points="4692.73,-1480.6 4692.73,-1507.4 4722.18,-1507.4 4722.18,-1480.6 4692.73,-1480.6"/>
<text xml:space="preserve" text-anchor="start" x="4697.73" y="-1490.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">PK</text>
<polygon fill="none" stroke="#3f4c5f" points="4722.18,-1480.6 4722.18,-1507.4 4883.27,-1507.4 4883.27,-1480.6 4722.18,-1480.6"/>
<text xml:space="preserve" text-anchor="start" x="4727.18" y="-1490.4" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">primary key</text>
<polygon fill="none" stroke="#3f4c5f" points="4692.73,-1453.8 4692.73,-1480.6 4722.18,-1480.6 4722.18,-1453.8 4692.73,-1453.8"/>
<text xml:space="preserve" text-anchor="start" x="4697.73" y="-1464" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">FK</text>
<polygon fill="none" stroke="#3f4c5f" points="4722.18,-1453.8 4722.18,-1480.6 4883.27,-1480.6 4883.27,-1453.8 4722.18,-1453.8"/>
<text xml:space="preserve" text-anchor="start" x="4727.18" y="-1463.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">foreign key</text>
<polygon fill="none" stroke="#3f4c5f" points="4692.73,-1427 4692.73,-1453.8 4722.18,-1453.8 4722.18,-1427 4692.73,-1427"/>
<text xml:space="preserve" text-anchor="start" x="4697.73" y="-1437.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00" fill="#d8dee9">?</text>
<polygon fill="none" stroke="#3f4c5f" points="4722.18,-1427 4722.18,-1453.8 4883.27,-1453.8 4883.27,-1427 4722.18,-1427"/>
<text xml:space="preserve" text-anchor="start" x="4727.18" y="-1436.8" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">nullable column</text>
<polygon fill="none" stroke="#3f4c5f" points="4692.73,-1400.2 4692.73,-1427 4722.18,-1427 4722.18,-1400.2 4692.73,-1400.2"/>
<text xml:space="preserve" text-anchor="start" x="4697.73" y="-1408" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#8b95a7">—</text>
<polygon fill="none" stroke="#3f4c5f" points="4722.18,-1400.2 4722.18,-1427 4883.27,-1427 4883.27,-1400.2 4722.18,-1400.2"/>
<text xml:space="preserve" text-anchor="start" x="4727.18" y="-1410" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">FOREIGN KEY constraint</text>
<polygon fill="none" stroke="#3f4c5f" points="4692.73,-1373.4 4692.73,-1400.2 4722.18,-1400.2 4722.18,-1373.4 4692.73,-1373.4"/>
<text xml:space="preserve" text-anchor="start" x="4697.73" y="-1381.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#8b95a7">&#45; &#45;</text>
<polygon fill="none" stroke="#3f4c5f" points="4722.18,-1373.4 4722.18,-1400.2 4883.27,-1400.2 4883.27,-1373.4 4722.18,-1373.4"/>
<text xml:space="preserve" text-anchor="start" x="4727.18" y="-1383.2" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d8dee9">naming convention only, no constraint</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

@@ -119,6 +119,7 @@ import manifestattachmentAdd from './store/manifestattachment/add.js';
import manifestattachmentRemove from './store/manifestattachment/remove.js';
import manifestattachmentListByManifestId from './store/manifestattachment/listByManifestId.js';
import manifestattachmentGetById from './store/manifestattachment/getById.js';
import manifestattachmentOrder from './store/manifestattachment/order.js';
export default class LibDashStore {
constructor(pool) {
@@ -310,6 +311,7 @@ export default class LibDashStore {
remove: manifestattachmentRemove(pool),
listByManifestId: manifestattachmentListByManifestId(pool),
getById: manifestattachmentGetById(pool),
order: manifestattachmentOrder(pool),
};
this.purge = storePurge(pool);

View File

@@ -773,9 +773,13 @@ IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'file')
BEGIN
CREATE TABLE [file] (
id INT IDENTITY(1,1) PRIMARY KEY,
hash NVARCHAR(64) NOT NULL,
mimetype NVARCHAR(255) NOT NULL,
name NVARCHAR(255) NOT NULL,
content VARBINARY(MAX) NOT NULL
content VARBINARY(MAX) NOT NULL,
thumbavif VARBINARY(MAX) NULL,
thumbx INT NULL,
thumby INT NULL
);
END
`;
@@ -787,8 +791,10 @@ BEGIN
id INT IDENTITY(1,1) PRIMARY KEY,
manifest_id INT NOT NULL,
file_id INT NOT NULL,
sort INT NOT NULL CONSTRAINT DF_manifestattachment_sort DEFAULT 0,
CONSTRAINT FK_manifestattachment_manifest FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE,
CONSTRAINT FK_manifestattachment_file FOREIGN KEY (file_id) REFERENCES [file](id) ON DELETE CASCADE
CONSTRAINT FK_manifestattachment_file FOREIGN KEY (file_id) REFERENCES [file](id) ON DELETE CASCADE,
CONSTRAINT UQ_manifestattachment_manifest_sort UNIQUE (manifest_id, sort)
);
END
`;

View File

@@ -5,15 +5,21 @@ WHERE id = @manifestId AND company_id = @companyId
`;
export const CREATE_FILE = `
INSERT INTO [file] (mimetype, name, content)
INSERT INTO [file] (hash, mimetype, name, content, thumbavif, thumbx, thumby)
OUTPUT INSERTED.id
VALUES (@mimetype, @name, @content)
VALUES (@hash, @mimetype, @name, @content, CONVERT(VARBINARY(MAX), @thumbavif), @thumbx, @thumby)
`;
export const GET_NEXT_SORT = `
SELECT ISNULL(MAX(sort), -1) + 1 AS sort
FROM manifestattachment
WHERE manifest_id = @manifestId;
`;
export const CREATE_ATTACHMENT = `
INSERT INTO manifestattachment (manifest_id, file_id)
INSERT INTO manifestattachment (manifest_id, file_id, sort)
OUTPUT INSERTED.id
VALUES (@manifestId, @fileId)
VALUES (@manifestId, @fileId, @sort)
`;
export const LIST_BY_MANIFEST = `
@@ -21,13 +27,18 @@ SELECT
ma.id,
ma.manifest_id AS manifestId,
ma.file_id AS fileId,
ma.sort,
f.hash,
f.mimetype,
f.name,
DATALENGTH(f.content) AS size
DATALENGTH(f.content) AS size,
f.thumbavif,
f.thumbx,
f.thumby
FROM manifestattachment ma
INNER JOIN [file] f ON f.id = ma.file_id
WHERE ma.manifest_id = @manifestId
ORDER BY ma.id ASC
ORDER BY ma.sort ASC, ma.id ASC
`;
export const GET_BY_ID = `
@@ -35,9 +46,14 @@ SELECT
ma.id,
ma.manifest_id AS manifestId,
ma.file_id AS fileId,
ma.sort,
f.hash,
f.mimetype,
f.name,
f.content
f.content,
f.thumbavif,
f.thumbx,
f.thumby
FROM manifestattachment ma
INNER JOIN [file] f ON f.id = ma.file_id
WHERE ma.manifest_id = @manifestId
@@ -60,3 +76,22 @@ export const DELETE_FILE = `
DELETE FROM [file]
WHERE id = @fileId
`;
export const LIST_IDS_BY_MANIFEST = `
SELECT id
FROM manifestattachment
WHERE manifest_id = @manifestId
ORDER BY sort ASC, id ASC;
`;
export const SET_TEMP_SORTS = `
UPDATE manifestattachment
SET sort = -id
WHERE manifest_id = @manifestId;
`;
export const UPDATE_SORT = `
UPDATE manifestattachment
SET sort = @sort
WHERE id = @id AND manifest_id = @manifestId;
`;

View File

@@ -1,4 +1,10 @@
import { CREATE_FILE, CREATE_ATTACHMENT } from '../../sql/manifestattachment.js';
import { createHash } from 'node:crypto';
import sharp from 'sharp';
import {
CREATE_FILE,
CREATE_ATTACHMENT,
GET_NEXT_SORT,
} from '../../sql/manifestattachment.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import resolveManifestAccess from './resolveManifestAccess.js';
import request from '../request.js';
@@ -16,7 +22,12 @@ export default function add(pool) {
* @param {string} input.mimetype - The MIME type of the file (e.g. `'application/pdf'`).
* @param {string} input.name - The file name.
* @param {Buffer} input.content - The file content.
* @returns {Promise<{id: number, manifestId: number, fileId: number, mimetype: string, name: string}>} The created attachment record.
* @param {number} [input.sort] - Optional display sort order.
* @param {string} [input.hash] - The file hash (auto-computed SHA-256 if omitted).
* @param {Buffer} [input.thumbavif] - Optional AVIF thumbnail buffer.
* @param {number} [input.thumbx] - Optional thumbnail width.
* @param {number} [input.thumby] - Optional thumbnail height.
* @returns {Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>} The created attachment record.
* @throws {Error} If the manifest is not found or file input is invalid.
*
* @example
@@ -25,14 +36,19 @@ export default function add(pool) {
* name: 'invoice.pdf',
* content: fileBuffer,
* });
* // => { id, manifestId, fileId, mimetype, name }
* // => { id, manifestId, fileId, sort, hash, mimetype, name, thumbavif, thumbx, thumby }
*/
return async (googleAuthId, manifestId, input, ...rest) => {
let rawMimetype, rawName, rawContent;
let rawMimetype, rawName, rawContent, rawHash, rawThumbavif, rawThumbx, rawThumby, rawSort;
if (typeof input === 'object' && input !== null && !Buffer.isBuffer(input)) {
rawMimetype = input.mimetype;
rawName = input.name;
rawContent = input.content;
rawHash = input.hash;
rawThumbavif = input.thumbavif;
rawThumbx = input.thumbx ?? input.thumbw;
rawThumby = input.thumby ?? input.thumbh;
rawSort = input.sort;
} else {
rawMimetype = input;
rawName = rest[0];
@@ -56,6 +72,49 @@ export default function add(pool) {
? rawContent
: Buffer.from(rawContent);
if (rawSort !== undefined && rawSort !== null) {
if (!Number.isFinite(rawSort)) {
throw new Error('Invalid sort');
}
}
const hash =
typeof rawHash === 'string' && rawHash.trim()
? rawHash.trim()
: createHash('sha256').update(content).digest('hex');
let thumbavif = rawThumbavif
? (Buffer.isBuffer(rawThumbavif) ? rawThumbavif : Buffer.from(rawThumbavif))
: null;
let thumbx = Number.isFinite(rawThumbx) ? Number(rawThumbx) : null;
let thumby = Number.isFinite(rawThumby) ? Number(rawThumby) : null;
if (thumbavif && (!thumbx || !thumby)) {
try {
const meta = await sharp(thumbavif).metadata();
thumbx = thumbx ?? meta.width ?? null;
thumby = thumby ?? meta.height ?? null;
} catch {
// ignore
}
}
if (!thumbavif) {
try {
const thumbResult = await sharp(content)
.resize(200, 200, { fit: 'inside', withoutEnlargement: true })
.avif({ effort: 4 })
.toBuffer({ resolveWithObject: true });
thumbavif = thumbResult.data;
thumbx = thumbResult.info.width;
thumby = thumbResult.info.height;
} catch {
thumbavif = null;
thumbx = null;
thumby = null;
}
}
const { companyId } = await access(googleAuthId);
await manifestAccess(companyId, manifestId);
@@ -63,10 +122,22 @@ export default function add(pool) {
await tx.begin();
try {
let sort = rawSort;
if (sort === undefined || sort === null) {
const sortRes = await request(pool, tx)
.input('manifestId', manifestId)
.query(GET_NEXT_SORT);
sort = sortRes.recordset[0].sort;
}
const fileRes = await request(pool, tx)
.input('hash', hash)
.input('mimetype', mimetype)
.input('name', name)
.input('content', content)
.input('thumbavif', thumbavif)
.input('thumbx', thumbx)
.input('thumby', thumby)
.query(CREATE_FILE);
const fileId = fileRes.recordset[0].id;
@@ -74,6 +145,7 @@ export default function add(pool) {
const attachmentRes = await request(pool, tx)
.input('manifestId', manifestId)
.input('fileId', fileId)
.input('sort', sort)
.query(CREATE_ATTACHMENT);
const attachmentId = attachmentRes.recordset[0].id;
@@ -84,8 +156,13 @@ export default function add(pool) {
id: attachmentId,
manifestId,
fileId,
sort,
hash,
mimetype,
name,
thumbavif,
thumbx,
thumby,
};
} catch (err) {
try {

View File

@@ -13,12 +13,12 @@ export default function getById(pool) {
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} manifestId - The manifest ID.
* @param {number} id - The attachment ID or file ID.
* @returns {Promise<{id: number, manifestId: number, fileId: number, mimetype: string, name: string, content: Buffer}>} The attachment record with binary content.
* @returns {Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>} The attachment record with binary content.
* @throws {Error} If the manifest or attachment is not found, or user is not authorized.
*
* @example
* const attachment = await store.manifestattachment.getById(42, 1, 5);
* // => { id, manifestId, fileId, mimetype, name, content }
* // => { id, manifestId, fileId, sort, hash, mimetype, name, content, thumbavif, thumbx, thumby }
*/
return async (googleAuthId, manifestId, id) => {
if (!Number.isFinite(id)) {
@@ -42,9 +42,14 @@ export default function getById(pool) {
id: row.id,
manifestId: row.manifestId,
fileId: row.fileId,
sort: row.sort,
hash: row.hash,
mimetype: row.mimetype,
name: row.name,
content: row.content,
thumbavif: row.thumbavif ?? null,
thumbx: row.thumbx ?? null,
thumby: row.thumby ?? null,
};
};
}

View File

@@ -8,9 +8,14 @@ function shape(row) {
id: row.id,
manifestId: row.manifestId,
fileId: row.fileId,
sort: row.sort,
hash: row.hash,
mimetype: row.mimetype,
name: row.name,
size: row.size,
thumbavif: row.thumbavif ?? null,
thumbx: row.thumbx ?? null,
thumby: row.thumby ?? null,
};
}
@@ -23,12 +28,12 @@ export default function listByManifestId(pool) {
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} manifestId - The manifest ID.
* @returns {Promise<Array<{id: number, manifestId: number, fileId: number, mimetype: string, name: string, size: number}>>} A list of attachment metadata objects.
* @returns {Promise<Array<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>>} A list of attachment metadata objects.
* @throws {Error} If the manifest is not found or the user is not authorized.
*
* @example
* const attachments = await store.manifestattachment.listByManifestId(42, 1);
* // => [{ id, manifestId, fileId, mimetype, name, size }]
* // => [{ id, manifestId, fileId, sort, hash, mimetype, name, size, thumbavif, thumbx, thumby }]
*/
return async (googleAuthId, manifestId) => {
const { companyId } = await access(googleAuthId);

View File

@@ -0,0 +1,88 @@
import {
LIST_IDS_BY_MANIFEST,
SET_TEMP_SORTS,
UPDATE_SORT,
} from '../../sql/manifestattachment.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import resolveManifestAccess from './resolveManifestAccess.js';
import request from '../request.js';
export default function order(pool) {
const access = resolveSelectedCompanyAccess(pool);
const manifestAccess = resolveManifestAccess(pool);
/**
* Reorders the attachments of a manifest.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} manifestId - The manifest ID.
* @param {object|Array<number>} input - The reorder input or array of IDs.
* @param {Array<number>} [input.ids] - The attachment ids in their new order.
* @returns {Promise<{ids: Array<number>}>} The applied order.
* @throws {Error} If the order is invalid.
*
* @example
* const result = await store.manifestattachment.order(42, 1, { ids: [12, 10, 15] });
* // => { ids: [12, 10, 15] }
*/
return async (googleAuthId, manifestId, input) => {
const rawIds = Array.isArray(input) ? input : input?.ids;
if (!Array.isArray(rawIds)) {
throw new Error('Invalid attachment order');
}
const { companyId } = await access(googleAuthId);
await manifestAccess(companyId, manifestId);
const current = await pool
.request()
.input('manifestId', manifestId)
.query(LIST_IDS_BY_MANIFEST);
const existing = new Set(current.recordset.map((r) => r.id));
if (rawIds.length !== existing.size) {
throw new Error('Invalid attachment order');
}
const seen = new Set();
for (const id of rawIds) {
if (!Number.isFinite(id) || !existing.has(id) || seen.has(id)) {
throw new Error('Invalid attachment order');
}
seen.add(id);
}
const tx = pool.transaction();
await tx.begin();
try {
await request(pool, tx)
.input('manifestId', manifestId)
.query(SET_TEMP_SORTS);
for (let sort = 0; sort < rawIds.length; sort++) {
const updateRes = await request(pool, tx)
.input('id', rawIds[sort])
.input('manifestId', manifestId)
.input('sort', sort)
.query(UPDATE_SORT);
if (!updateRes.rowsAffected[0]) {
throw new Error('Invalid attachment order');
}
}
await tx.commit();
return { ids: rawIds };
} catch (err) {
try {
await tx.rollback();
} catch {
// ignore
}
throw err;
}
};
}