Files
dashfront/src/api/delivery.js
2026-10-16 20:46:26 +02:00

122 lines
4.6 KiB
JavaScript

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
};