import { http, clientIdHeaders } from './http.js'; /** * Company-scoped goods receipts (manifest type = delivery). * DeliverySummary = { id, number, externalNumber, type: 'delivery', supplierId, date } * - number: server-allocated, read-only manifest number (from nextNumber('delivery')). * - externalNumber: user-entered reference (nullable). * DeliveryLine = { id, articleId, sku, amount, net, transactedAmount, rebate, tax, comment } * Delivery = DeliverySummary & { lines: DeliveryLine[] } * DeliveryLineInput = { sku, amount, net, tax, rebate?, transactedAmount?, comment? } */ export const delivery = { /** * GET /api/delivery * @param {{ offset?: number, limit?: number, search?: string, onlyUnbooked?: boolean, supplierId?: number, seekMonths?: number }} [opts] * → { deliveries: DeliverySummary[], total?: number, unfilteredTotal?: number, offset?: number, limit?: number } */ // #region GET /api/delivery — list list(opts) { if (opts && typeof opts === 'object') { const params = new URLSearchParams(); if (opts.offset != null) params.set('offset', String(Math.max(0, Math.floor(Number(opts.offset) || 0)))); if (opts.limit != null) params.set('limit', String(Math.max(1, Math.floor(Number(opts.limit) || 50)))); if (opts.search && String(opts.search).trim()) params.set('search', String(opts.search).trim()); if (opts.onlyUnbooked === true || opts.onlyUnbooked === 'true') params.set('onlyUnbooked', 'true'); if (opts.supplierId != null) params.set('supplierId', String(opts.supplierId)); if (opts.seekMonths != null && Number(opts.seekMonths) < 0) { params.set('seekMonths', String(Math.floor(Number(opts.seekMonths)))); } const qs = params.toString(); return http(`/api/delivery${qs ? `?${qs}` : ''}`); } return http('/api/delivery'); }, // #endregion /** POST /api/delivery/rebuild-summaries → { ok: boolean, companyId: number } */ rebuildSummaries() { return http('/api/delivery/rebuild-summaries', { method: 'POST' }); }, /** GET /api/delivery/:id → { delivery: Delivery } */ // #region GET /api/delivery/:id — get get(id) { return http(`/api/delivery/${id}`); }, // #endregion /** * POST /api/delivery * Body: { externalNumber?, date, supplierId?, lines: DeliveryLineInput[] } * number is NOT sent — the server allocates it automatically. The * caller-supplied reference goes in externalNumber (nullable). * Line requires sku, amount, net, tax. rebate→0, transactedAmount→amount. * → 201 { delivery: Delivery } */ // #region POST /api/delivery — create create(body) { return http('/api/delivery', { method: 'POST', body }); }, // #endregion /** * PATCH /api/delivery/:id * Body: partial { externalNumber?, date?, supplierId?, lines? }. * number is read-only and cannot be edited. externalNumber is editable and * can be cleared by sending null or an empty string. * If lines present, replaces all lines; if omitted, lines unchanged. * → 200 { delivery: Delivery } */ // #region PATCH /api/delivery/:id — update update(id, body) { return http(`/api/delivery/${id}`, { method: 'PATCH', body }); }, // #endregion /** DELETE /api/delivery/:id → 204 */ // #region DELETE /api/delivery/:id — delete delete(id) { return http(`/api/delivery/${id}`, { method: 'DELETE' }); }, // #endregion /** * GET /api/delivery/:id/file/:fileId → Blob * Full binary content of attached file (PDF, text, etc.) */ // #region GET /api/delivery/:id/file/:fileId — getFile async getFile(id, fileId, { download = false } = {}) { const query = download ? '?download=true' : ''; const res = await fetch(`/api/delivery/${encodeURIComponent(id)}/file/${encodeURIComponent(fileId)}${query}`, { method: 'GET', credentials: 'include', }); if (!res.ok) { let message = res.statusText; try { const data = await res.json(); message = data.error || data.message || message; } catch { /* ignore */ } const err = new Error(message || `Request failed (${res.status})`); err.status = res.status; throw err; } return res.blob(); }, // #endregion /** * DELETE /api/delivery/:id/file/:fileId * Deletes delivery file attachment. * @param {number|string} id Delivery ID * @param {number|string} fileId File ID * → 204 void */ // #region DELETE /api/delivery/:id/file/:fileId — deleteFile deleteFile(id, fileId) { return http(`/api/delivery/${id}/file/${fileId}`, { method: 'DELETE' }); }, // #endregion };