This commit is contained in:
seb
2026-09-18 07:37:25 +02:00
parent a537c2f72d
commit dd13f736c0
5 changed files with 75 additions and 6 deletions

View File

@@ -406,8 +406,10 @@ export class ApiClient {
const headers = { ...extraHeaders }; const headers = { ...extraHeaders };
let payload; let payload;
if (body !== undefined) { if (body !== undefined) {
if (raw) { if (typeof FormData !== 'undefined' && body instanceof FormData) {
headers['Content-Type'] = contentType; payload = body;
} else if (raw) {
if (contentType) headers['Content-Type'] = contentType;
payload = body; payload = body;
} else { } else {
headers['Content-Type'] = contentType || 'application/json'; headers['Content-Type'] = contentType || 'application/json';

View File

@@ -20,7 +20,11 @@ function readableHeaders(headers) {
} }
async function http(path, { method = 'GET', body } = {}) { async function http(path, { method = 'GET', body } = {}) {
const headers = { 'Content-Type': 'application/json' }; const isFormData = typeof FormData !== 'undefined' && body instanceof FormData;
const headers = {};
if (!isFormData) {
headers['Content-Type'] = 'application/json';
}
const url = new URL(path, window.location.origin).href; const url = new URL(path, window.location.origin).href;
logApi('request', { logApi('request', {
@@ -38,7 +42,7 @@ async function http(path, { method = 'GET', body } = {}) {
method, method,
headers, headers,
credentials: 'include', credentials: 'include',
body: body ? JSON.stringify(body) : undefined, body: isFormData ? body : (body ? JSON.stringify(body) : undefined),
}); });
const responseHeaders = readableHeaders(res.headers); const responseHeaders = readableHeaders(res.headers);

View File

@@ -15,6 +15,7 @@ export {
lineFromApi, lineFromApi,
buildManifestBody, buildManifestBody,
buildFilesForUpload, buildFilesForUpload,
collectFilesForUpload,
restoreAttachmentsFromApi, restoreAttachmentsFromApi,
dateInputValue, dateInputValue,
} from './manifestConfig'; } from './manifestConfig';

View File

@@ -344,6 +344,53 @@ export async function restoreAttachmentsFromApi(apiFiles = [], existingAttachmen
return attachments; return attachments;
} }
/**
* Collect new or modified attachments for multipart upload
* @param {Array<{file: File, name: string, type: string, thumbnail?: string, text?: string, parentPdfId?: string|number, isPersisted?: boolean, isDirty?: boolean}>} attachments
* @param {{ onlyNew?: boolean }} [options]
* @returns {Array<{file: File, name: string, mimetype: string, meta?: object}>}
*/
export function collectFilesForUpload(attachments = [], { onlyNew = false } = {}) {
const files = [];
for (const attachment of attachments) {
if (!attachment) continue;
// Skip already persisted attachments in update mode unless modified
if (onlyNew && attachment.isPersisted && !attachment.isDirty) {
continue;
}
if (attachment.isPersisted && !attachment.isDirty && (!attachment.file || attachment.file.size === 0)) {
continue;
}
let fileToUpload = attachment.file;
if (!fileToUpload || fileToUpload.size === 0) {
if (attachment.text) {
fileToUpload = new File([attachment.text], attachment.name || 'file.txt', {
type: 'text/plain;charset=utf-8',
});
} else {
continue;
}
}
const meta = {};
if (attachment.parentPdfId) {
meta.parentPdfId = String(attachment.parentPdfId);
}
if (attachment.text) {
meta.text = String(attachment.text);
}
files.push({
file: fileToUpload,
name: attachment.name || fileToUpload.name,
mimetype: attachment.type || fileToUpload.type,
meta: Object.keys(meta).length > 0 ? meta : undefined,
});
}
return files;
}
/** /**
* @param {{ number: string, externalNumber?: string, date: string, partyId: number|null, lines: object[], attachments?: object[] }} form * @param {{ number: string, externalNumber?: string, date: string, partyId: number|null, lines: object[], attachments?: object[] }} form
* @param {{ partyField: 'supplierId'|'entityId', requireAtLeastOne: boolean, externalNumber?: boolean }} opts * @param {{ partyField: 'supplierId'|'entityId', requireAtLeastOne: boolean, externalNumber?: boolean }} opts
@@ -380,9 +427,22 @@ export async function buildManifestBody(
// Add files if attachments exist // Add files if attachments exist
if (form.attachments && form.attachments.length > 0) { if (form.attachments && form.attachments.length > 0) {
const isUpdate = !requireAtLeastOne; const isUpdate = !requireAtLeastOne;
const filesToUpload = await buildFilesForUpload(form.attachments, { onlyNew: isUpdate }); const filesToUpload = collectFilesForUpload(form.attachments, { onlyNew: isUpdate });
if (filesToUpload.length > 0) { if (filesToUpload.length > 0) {
body.files = filesToUpload; const filesMeta = filesToUpload
.filter((f) => f.meta)
.map((f) => ({ name: f.name, meta: f.meta }));
if (filesMeta.length > 0) {
body.filesMeta = filesMeta;
}
const formData = new FormData();
formData.append('data', JSON.stringify(body));
for (const item of filesToUpload) {
formData.append('files', item.file, item.name);
}
return formData;
} }
} }

View File

@@ -379,6 +379,7 @@ class LieferungNeuPage extends PureComponent {
file: txtFile, file: txtFile,
size: txtFile.size, size: txtFile.size,
text, text,
isDirty: true,
}); });
} else { } else {
deliveryEditorAutomat.actions.addAttachment({ deliveryEditorAutomat.actions.addAttachment({
@@ -606,6 +607,7 @@ class LieferungNeuPage extends PureComponent {
file: newFile, file: newFile,
size: newFile.size, size: newFile.size,
text: newText, text: newText,
isDirty: true,
}); });
this.setState({ this.setState({