This commit is contained in:
seb
2026-10-15 00:30:23 +02:00
parent bdd7ebf5b8
commit 8c55f899b5
27 changed files with 136 additions and 46 deletions

View File

@@ -23,7 +23,7 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
- `T12` = `{ labeltemplate: T14 }` - `T12` = `{ labeltemplate: T14 }`
- `T13` = `{ attachment: T15, fileUrl: str }` - `T13` = `{ attachment: T15, fileUrl: str }`
- `T14` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T25, thumb?: str|null }` - `T14` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T25, thumb?: str|null }`
- `T15` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` - `T15` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }`
- `T16` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }` - `T16` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }`
- `T17` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T54|null }` - `T17` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T54|null }`
- `T18` = `{ vfs: T52, storage: T52, database: T45 }` - `T18` = `{ vfs: T52, storage: T52, database: T45 }`

View File

@@ -21,7 +21,7 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
- `T10` = `{ labeltemplate: T12 }` - `T10` = `{ labeltemplate: T12 }`
- `T11` = `{ attachment: T13, fileUrl: str }` - `T11` = `{ attachment: T13, fileUrl: str }`
- `T12` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T19, thumb?: str|null }` - `T12` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T19, thumb?: str|null }`
- `T13` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` - `T13` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }`
- `T14` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T45|null }` - `T14` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T45|null }`
- `T15` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` - `T15` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }`
- `T16` = `{ ledger: T17[] }` - `T16` = `{ ledger: T17[] }`

View File

@@ -35,7 +35,7 @@ PDF generated and attached to delivery
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -97,7 +97,7 @@ Created delivery
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -55,7 +55,7 @@ Delivery
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -84,7 +84,7 @@ Updated delivery
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -35,7 +35,7 @@ PDF generated and attached to order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -65,7 +65,7 @@ List of shipments
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -102,7 +102,7 @@ Created shipment
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -153,7 +153,7 @@ Created order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}
@@ -173,7 +173,7 @@ Created order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -128,7 +128,7 @@ Order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}
@@ -148,7 +148,7 @@ Order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -137,7 +137,7 @@ Order list
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}
@@ -157,7 +157,7 @@ Order list
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -147,7 +147,7 @@ Updated order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}
@@ -167,7 +167,7 @@ Updated order
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -35,7 +35,7 @@ PDF generated and attached to quote
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -79,7 +79,7 @@ Created quote
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -54,7 +54,7 @@ Quote
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -60,7 +60,7 @@ Quote list
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -73,7 +73,7 @@ Updated quote
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -64,7 +64,7 @@ Shipment details
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -36,7 +36,7 @@ PDF generated and attached to shipment
"size": 1, "size": 1,
"created": "2026-08-10T00:00:00.000Z", "created": "2026-08-10T00:00:00.000Z",
"modified": "2026-08-10T00:00:00.000Z", "modified": "2026-08-10T00:00:00.000Z",
"thumbavif": "…", "skeledash": "…",
"thumbx": 1, "thumbx": 1,
"thumby": 1, "thumby": 1,
"meta": {} "meta": {}

View File

@@ -834,11 +834,10 @@
* modified: * modified:
* type: string * type: string
* format: date-time * format: date-time
* thumbavif: * skeledash:
* type: string * type: string
* format: byte
* nullable: true * nullable: true
* description: Base64-encoded AVIF thumbnail generated server-side (null for text files) * description: Base62 skelehash string of the document thumbnail
* thumbx: * thumbx:
* type: integer * type: integer
* nullable: true * nullable: true

View File

@@ -1,23 +1,40 @@
import { getStore } from './store.js'; import { getStore } from './store.js';
import { formatAttachments } from '../util/manifestAttachment.js';
async function formatDelivery(delivery) {
if (!delivery) return delivery;
if (Array.isArray(delivery.files) && delivery.files.length > 0) {
const files = await formatAttachments(delivery.files);
return { ...delivery, files };
}
return delivery;
}
export async function list(googleAuthId) { export async function list(googleAuthId) {
const store = await getStore(); const store = await getStore();
return store.delivery.list(googleAuthId); const result = await store.delivery.list(googleAuthId);
if (Array.isArray(result)) {
return Promise.all(result.map(formatDelivery));
}
return result;
} }
export async function getById(googleAuthId, id) { export async function getById(googleAuthId, id) {
const store = await getStore(); const store = await getStore();
return store.delivery.getById(googleAuthId, id); const delivery = await store.delivery.getById(googleAuthId, id);
return formatDelivery(delivery);
} }
export async function create(googleAuthId, data) { export async function create(googleAuthId, data) {
const store = await getStore(); const store = await getStore();
return store.delivery.create(googleAuthId, data); const delivery = await store.delivery.create(googleAuthId, data);
return formatDelivery(delivery);
} }
export async function update(googleAuthId, id, data) { export async function update(googleAuthId, id, data) {
const store = await getStore(); const store = await getStore();
return store.delivery.update(googleAuthId, id, data); const delivery = await store.delivery.update(googleAuthId, id, data);
return formatDelivery(delivery);
} }
export async function remove(googleAuthId, id) { export async function remove(googleAuthId, id) {
@@ -32,12 +49,14 @@ export async function getFile(googleAuthId, deliveryId, fileId) {
export async function addAttachment(googleAuthId, deliveryId, data) { export async function addAttachment(googleAuthId, deliveryId, data) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.add(googleAuthId, deliveryId, data); const raw = await store.manifestattachment.add(googleAuthId, deliveryId, data);
return formatAttachments([raw]).then((r) => r[0]);
} }
export async function listAttachments(googleAuthId, deliveryId) { export async function listAttachments(googleAuthId, deliveryId) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.listByManifestId(googleAuthId, deliveryId); const raw = await store.manifestattachment.listByManifestId(googleAuthId, deliveryId);
return formatAttachments(raw);
} }
export async function removeAttachment(googleAuthId, deliveryId, fileId) { export async function removeAttachment(googleAuthId, deliveryId, fileId) {

View File

@@ -1,4 +1,5 @@
import { getStore } from './store.js'; import { getStore } from './store.js';
import { formatAttachments } from '../util/manifestAttachment.js';
function round2(num) { function round2(num) {
return Math.round((Number(num) || 0) * 100) / 100; return Math.round((Number(num) || 0) * 100) / 100;
@@ -46,13 +47,14 @@ function calculateShippedQuantity(shipments = []) {
async function enrichSingleOrder(store, googleAuthId, order) { async function enrichSingleOrder(store, googleAuthId, order) {
if (!order) return order; if (!order) return order;
const [shippings, payments, shipments, files] = await Promise.all([ const [shippings, payments, shipments, rawFiles] = await Promise.all([
store.shipping.listByManifestId(googleAuthId, order.id).catch(() => []), store.shipping.listByManifestId(googleAuthId, order.id).catch(() => []),
store.payment.listByManifestId(googleAuthId, order.id).catch(() => []), store.payment.listByManifestId(googleAuthId, order.id).catch(() => []),
store.shipment.listByOrderId(googleAuthId, order.id).catch(() => []), store.shipment.listByOrderId(googleAuthId, order.id).catch(() => []),
store.manifestattachment.listByManifestId(googleAuthId, order.id).catch(() => []), store.manifestattachment.listByManifestId(googleAuthId, order.id).catch(() => []),
]); ]);
const files = await formatAttachments(rawFiles);
const orderLines = Array.isArray(order.lines) ? order.lines : []; const orderLines = Array.isArray(order.lines) ? order.lines : [];
const paymentsList = Array.isArray(payments) ? payments : []; const paymentsList = Array.isArray(payments) ? payments : [];
const shipmentsList = Array.isArray(shipments) ? shipments : []; const shipmentsList = Array.isArray(shipments) ? shipments : [];
@@ -165,12 +167,14 @@ export async function getFile(googleAuthId, orderId, fileId) {
export async function addAttachment(googleAuthId, orderId, data) { export async function addAttachment(googleAuthId, orderId, data) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.add(googleAuthId, orderId, data); const raw = await store.manifestattachment.add(googleAuthId, orderId, data);
return formatAttachments([raw]).then((r) => r[0]);
} }
export async function listAttachments(googleAuthId, orderId) { export async function listAttachments(googleAuthId, orderId) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.listByManifestId(googleAuthId, orderId); const raw = await store.manifestattachment.listByManifestId(googleAuthId, orderId);
return formatAttachments(raw);
} }
export async function removeAttachment(googleAuthId, orderId, fileId) { export async function removeAttachment(googleAuthId, orderId, fileId) {

View File

@@ -1,4 +1,5 @@
import { getStore } from './store.js'; import { getStore } from './store.js';
import { formatAttachments } from '../util/manifestAttachment.js';
function round2(num) { function round2(num) {
return Math.round((Number(num) || 0) * 100) / 100; return Math.round((Number(num) || 0) * 100) / 100;
@@ -47,13 +48,19 @@ export async function list(googleAuthId, opts) {
const result = await store.quote.list(googleAuthId, opts); const result = await store.quote.list(googleAuthId, opts);
if (Array.isArray(result)) { if (Array.isArray(result)) {
const filesList = await Promise.all( const filesList = await Promise.all(
result.map((q) => store.manifestattachment.listByManifestId(googleAuthId, q.id).catch(() => [])) result.map(async (q) => {
const raw = await store.manifestattachment.listByManifestId(googleAuthId, q.id).catch(() => []);
return formatAttachments(raw);
})
); );
return result.map((q, i) => enrichQuote(q, filesList[i])); return result.map((q, i) => enrichQuote(q, filesList[i]));
} }
if (result && Array.isArray(result.quotes)) { if (result && Array.isArray(result.quotes)) {
const filesList = await Promise.all( const filesList = await Promise.all(
result.quotes.map((q) => store.manifestattachment.listByManifestId(googleAuthId, q.id).catch(() => [])) result.quotes.map(async (q) => {
const raw = await store.manifestattachment.listByManifestId(googleAuthId, q.id).catch(() => []);
return formatAttachments(raw);
})
); );
return { return {
...result, ...result,
@@ -65,10 +72,11 @@ export async function list(googleAuthId, opts) {
export async function getById(googleAuthId, id) { export async function getById(googleAuthId, id) {
const store = await getStore(); const store = await getStore();
const [quote, files] = await Promise.all([ const [quote, rawFiles] = await Promise.all([
store.quote.getById(googleAuthId, id), store.quote.getById(googleAuthId, id),
store.manifestattachment.listByManifestId(googleAuthId, id).catch(() => []), store.manifestattachment.listByManifestId(googleAuthId, id).catch(() => []),
]); ]);
const files = await formatAttachments(rawFiles);
return enrichQuote(quote, files); return enrichQuote(quote, files);
} }
@@ -80,10 +88,11 @@ export async function create(googleAuthId, data) {
export async function update(googleAuthId, id, data) { export async function update(googleAuthId, id, data) {
const store = await getStore(); const store = await getStore();
const [quote, files] = await Promise.all([ const [quote, rawFiles] = await Promise.all([
store.quote.update(googleAuthId, id, data), store.quote.update(googleAuthId, id, data),
store.manifestattachment.listByManifestId(googleAuthId, id).catch(() => []), store.manifestattachment.listByManifestId(googleAuthId, id).catch(() => []),
]); ]);
const files = await formatAttachments(rawFiles);
return enrichQuote(quote, files); return enrichQuote(quote, files);
} }
@@ -104,12 +113,14 @@ export async function getFile(googleAuthId, quoteId, fileId) {
export async function addAttachment(googleAuthId, quoteId, data) { export async function addAttachment(googleAuthId, quoteId, data) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.add(googleAuthId, quoteId, data); const raw = await store.manifestattachment.add(googleAuthId, quoteId, data);
return formatAttachments([raw]).then((r) => r[0]);
} }
export async function listAttachments(googleAuthId, quoteId) { export async function listAttachments(googleAuthId, quoteId) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.listByManifestId(googleAuthId, quoteId); const raw = await store.manifestattachment.listByManifestId(googleAuthId, quoteId);
return formatAttachments(raw);
} }
export async function removeAttachment(googleAuthId, quoteId, fileId) { export async function removeAttachment(googleAuthId, quoteId, fileId) {

View File

@@ -1,4 +1,5 @@
import { getStore } from './store.js'; import { getStore } from './store.js';
import { formatAttachments } from '../util/manifestAttachment.js';
export async function create(googleAuthId, data) { export async function create(googleAuthId, data) {
const store = await getStore(); const store = await getStore();
@@ -7,11 +8,12 @@ export async function create(googleAuthId, data) {
export async function getById(googleAuthId, id) { export async function getById(googleAuthId, id) {
const store = await getStore(); const store = await getStore();
const [shipment, files] = await Promise.all([ const [shipment, rawFiles] = await Promise.all([
store.shipment.getById(googleAuthId, id), store.shipment.getById(googleAuthId, id),
store.manifestattachment.listByManifestId(googleAuthId, id).catch(() => []), store.manifestattachment.listByManifestId(googleAuthId, id).catch(() => []),
]); ]);
if (!shipment) return shipment; if (!shipment) return shipment;
const files = await formatAttachments(rawFiles);
return { return {
...shipment, ...shipment,
files: Array.isArray(files) ? files : [], files: Array.isArray(files) ? files : [],
@@ -35,12 +37,14 @@ export async function getFile(googleAuthId, shipmentId, fileId) {
export async function addAttachment(googleAuthId, shipmentId, data) { export async function addAttachment(googleAuthId, shipmentId, data) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.add(googleAuthId, shipmentId, data); const raw = await store.manifestattachment.add(googleAuthId, shipmentId, data);
return formatAttachments([raw]).then((r) => r[0]);
} }
export async function listAttachments(googleAuthId, shipmentId) { export async function listAttachments(googleAuthId, shipmentId) {
const store = await getStore(); const store = await getStore();
return store.manifestattachment.listByManifestId(googleAuthId, shipmentId); const raw = await store.manifestattachment.listByManifestId(googleAuthId, shipmentId);
return formatAttachments(raw);
} }
export async function removeAttachment(googleAuthId, shipmentId, fileId) { export async function removeAttachment(googleAuthId, shipmentId, fileId) {

View File

@@ -2,6 +2,7 @@ import PDFDocument from 'pdfkit';
import { getStore } from '../db/store.js'; import { getStore } from '../db/store.js';
import * as dbOrder from '../db/order.js'; import * as dbOrder from '../db/order.js';
import { emit } from '../api/sse.js'; import { emit } from '../api/sse.js';
import { formatAttachment } from '../util/manifestAttachment.js';
/** /**
* Formats a Date or date string to German format DD.MM.YYYY. * Formats a Date or date string to German format DD.MM.YYYY.
@@ -786,13 +787,15 @@ export async function createAndAttachManifestPdf(googleAuthId, companyId, manife
const safeNumber = String(manifest.number || manifest.id).replace(/[^a-zA-Z0-9._-]/g, '_'); const safeNumber = String(manifest.number || manifest.id).replace(/[^a-zA-Z0-9._-]/g, '_');
const filename = `${config.filenamePrefix}-${safeNumber}.pdf`; const filename = `${config.filenamePrefix}-${safeNumber}.pdf`;
const attachment = await store.manifestattachment.add(googleAuthId, manifest.id, { const rawAttachment = await store.manifestattachment.add(googleAuthId, manifest.id, {
mimetype: 'application/pdf', mimetype: 'application/pdf',
name: filename, name: filename,
content: pdfBuffer, content: pdfBuffer,
meta: { type: `${type}_pdf` }, meta: { type: `${type}_pdf` },
}); });
const attachment = await formatAttachment(rawAttachment);
emit(type, { companyId }); emit(type, { companyId });
return { manifest, attachment, pdfBuffer, filename }; return { manifest, attachment, pdfBuffer, filename };

View File

@@ -0,0 +1,50 @@
import { computeSkeleHash } from '../services/companyImport/imageHasher.js';
const skeleCache = new Map();
/**
* Strips raw binary `thumbavif` buffer and attaches `skeledash` (base62 skelehash string).
*
* @param {object} file - Raw attachment file object from libdashstore.
* @returns {Promise<object>} Formatted attachment with `skeledash` and without `thumbavif`.
*/
export async function formatAttachment(file) {
if (!file || typeof file !== 'object') return file;
let skeledash = file.skeledash || file.skelehash || file.meta?.skeledash || file.meta?.skelehash || null;
if (!skeledash && file.hash && skeleCache.has(file.hash)) {
skeledash = skeleCache.get(file.hash);
}
if (!skeledash && file.thumbavif) {
try {
const result = await computeSkeleHash(file.thumbavif);
skeledash = result?.base62 || null;
if (file.hash && skeledash) {
skeleCache.set(file.hash, skeledash);
}
} catch {
skeledash = null;
}
}
const { thumbavif: _thumbavif, ...rest } = file;
return {
...rest,
skeledash,
skelehash: skeledash,
};
}
/**
* Formats a list of attachments in parallel.
*
* @param {Array<object>} files - Array of raw file attachment objects.
* @returns {Promise<Array<object>>}
*/
export async function formatAttachments(files = []) {
if (!Array.isArray(files) || files.length === 0) return [];
return Promise.all(files.map(formatAttachment));
}