diff --git a/package-lock.json b/package-lock.json index c822103..f3bc8ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,7 @@ "@shopify/draggable": "^1.2.1", "dashgui": "file:../dashgui", "notistack": "^3.0.2", - "pdfjs-dist": "^6.3.289", - "pdftotext-wasm": "file:../pdftotext-wasm", + "pdfconvert-wasm": "^0.3.0", "react": "^19.3.0", "react-automat": "^0.1.0", "react-dom": "^19.3.0", @@ -57,13 +56,6 @@ "react-dom": "^19.0.0" } }, - "../pdftotext-wasm": { - "version": "0.1.0", - "license": "GPLv3", - "bin": { - "pdftotext-wasm": "bin.js" - } - }, "node_modules/@apideck/better-ajv-errors": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.7.tgz", @@ -2080,271 +2072,6 @@ } } }, - "node_modules/@napi-rs/canvas": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-1.0.9.tgz", - "integrity": "sha512-QviPdJImDi/jMAvBfqaw+19BndMd/sizXVW3NnpMd3VJGz++QXkOHcP9kWR/smHG0hNjHeyuHFyrx/5lD0oNcQ==", - "license": "MIT", - "optional": true, - "workspaces": [ - "e2e/*" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "1.0.9", - "@napi-rs/canvas-darwin-arm64": "1.0.9", - "@napi-rs/canvas-darwin-x64": "1.0.9", - "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.9", - "@napi-rs/canvas-linux-arm64-gnu": "1.0.9", - "@napi-rs/canvas-linux-arm64-musl": "1.0.9", - "@napi-rs/canvas-linux-riscv64-gnu": "1.0.9", - "@napi-rs/canvas-linux-x64-gnu": "1.0.9", - "@napi-rs/canvas-linux-x64-musl": "1.0.9", - "@napi-rs/canvas-win32-arm64-msvc": "1.0.9", - "@napi-rs/canvas-win32-x64-msvc": "1.0.9" - } - }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-1.0.9.tgz", - "integrity": "sha512-4LGXk2/0HVzE29K8SzML5WubgCp++B1FH3qgl35XmSZE+lLdr6P9VRQEnZ0MCLZMTSuJP41yyhtoiVNEuvrTIA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-1.0.9.tgz", - "integrity": "sha512-YNdfLBzY0W/Pep9fo2L6RmoNlNksnn05LRnX66W63R3ij58S25QOTcjdtEt2v8+PnCESzqZsYzUo+QPeIR44NA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-1.0.9.tgz", - "integrity": "sha512-ceZQSknTEcy3dOXoekv59LTCkXjvnLsq+VW5PeNNDHEPQbRS5Ervkm1EaDa7WLAjiYWMLuSQTRTHao1dEX4prg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-1.0.9.tgz", - "integrity": "sha512-XhfI0Wwv4llhd6nnWDtY3kQKjq0r+y1i91PlJlJI24ag2U9WrnwbG1qS3+fDLEyouwEVFchiKkTEHozK+5iUNA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-1.0.9.tgz", - "integrity": "sha512-012oiYtKaE7i9oxc8q7nraT7kDOpLcaCmFLzVe9Ty34RHDdoDzbWLrVh827CNxYh/EADX1eSikA3ymLjo/nNuw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-1.0.9.tgz", - "integrity": "sha512-Ls5UWYFFn63casTZEczbeyEg3vRDRkv9lscuGwfchtY5yLLQhgOB8SN4YGxmfJ5vTaBwZ2YUxBS3NtmjJmFXdA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-1.0.9.tgz", - "integrity": "sha512-hLKEGxV7ZiRHqndePTokgDMdBlo/rDfzg7P4p4QIv9pUhuYobnu3R2NIFLCRghG0nwfo+s2sw+c1xZFeCmEAsw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-1.0.9.tgz", - "integrity": "sha512-6kaz3w0QMy77PDWk6rJ1ksIihdad3qzEyX2o2oGT8GwCaypfT5mhjr8buOO5hstyLxcWXDScuz56RsINLtBPIQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-1.0.9.tgz", - "integrity": "sha512-xrGvmS3v55hmZ86ls/kBLVNMUTYio3f6Ik0DireemG994VfPAwiA3ZXA0Uf1bByctkB3NQ1Sfb+H5bkdUnnzfQ==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-arm64-msvc": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-1.0.9.tgz", - "integrity": "sha512-yjmVS3ArZeRVCP7jqbPq4rpZa/BhTeI7ELE2XqJg3snICQBDevLZyArxswHkiTnT34KRic33/4fLirrHI+SY8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-1.0.9.tgz", - "integrity": "sha512-QlSYQdMQslB81nlABo9wNfQ6npFhE7/O+saCZdqVGueGanyRk4jCogD5EwQenfP3kIq9e+mm6GreQBjX5MrA8g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, "node_modules/@napi-rs/lzma-linux-x64-gnu": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", @@ -5980,21 +5707,11 @@ "node": ">=8" } }, - "node_modules/pdfjs-dist": { - "version": "6.3.289", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-6.3.289.tgz", - "integrity": "sha512-ZHjSVpDa3D6izMq8/04lvkhkATUmL9px6ChPaXc1k6nU2Mrhlg1/7F0bdUqCwUjw3NsPTfPZsMDUU6ZIcRaeQw==", - "license": "Apache-2.0", - "engines": { - "node": ">=22.13.0 || >=24" - }, - "optionalDependencies": { - "@napi-rs/canvas": "^1.0.0" - } - }, - "node_modules/pdftotext-wasm": { - "resolved": "../pdftotext-wasm", - "link": true + "node_modules/pdfconvert-wasm": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pdfconvert-wasm/-/pdfconvert-wasm-0.3.0.tgz", + "integrity": "sha512-FbEk+Grg0UST4kOPuN7OFfVsut1FvDOJNPS3pt4rzCS8VlFsFzD7ptg7YtFZjEOKgJ4MgqlfCgjK4RYaRBpT0Q==", + "license": "GPLv3" }, "node_modules/picocolors": { "version": "1.1.1", diff --git a/package.json b/package.json index 7560ed4..e0cda11 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ "@shopify/draggable": "^1.2.1", "dashgui": "file:../dashgui", "notistack": "^3.0.2", - "pdfjs-dist": "^6.3.289", - "pdftotext-wasm": "file:../pdftotext-wasm", + "pdfconvert-wasm": "^0.3.0", "react": "^19.3.0", "react-automat": "^0.1.0", "react-dom": "^19.3.0", diff --git a/src/automats/deliveryEditorAutomat.js b/src/automats/deliveryEditorAutomat.js index 8fe4bb0..514ccb2 100644 --- a/src/automats/deliveryEditorAutomat.js +++ b/src/automats/deliveryEditorAutomat.js @@ -70,8 +70,10 @@ export const initialDelivery = { supplierArticlesLoading: false, loadingLineKeys: {}, supplierArticlesError: null, + unmatchedSupplierName: null, }; + export const initialState = { deliveryEntities: [], ...initialDelivery, @@ -137,6 +139,7 @@ export function createDeliveryEditorAutomat(options = {}) { automat.setState({ deliveryEntity: entityId, partyId: entityId, + unmatchedSupplierName: entityId != null ? null : automat.state.unmatchedSupplierName, ...(idChanged ? { supplierArticles: [], @@ -152,6 +155,11 @@ export function createDeliveryEditorAutomat(options = {}) { } }, + setUnmatchedSupplierName(name) { + automat.setState({ unmatchedSupplierName: name ?? null }); + }, + + setPartyId(partyId) { automat.actions.setDeliveryEntity(partyId); }, @@ -464,6 +472,7 @@ export const { setAttachments, updateAttachment, removeAttachment, + setUnmatchedSupplierName, resetDelivery, resetDeliveryEditor, } = deliveryEditorAutomat.actions; @@ -473,7 +482,9 @@ export const selectDeliveryId = (state = deliveryEditorAutomat.state) => state.d export const selectDeliveryEntities = (state = deliveryEditorAutomat.state) => state.deliveryEntities; export const selectDeliveryDate = (state = deliveryEditorAutomat.state) => state.date; export const selectDeliveryEntity = (state = deliveryEditorAutomat.state) => state.deliveryEntity ?? state.partyId; +export const selectUnmatchedSupplierName = (state = deliveryEditorAutomat.state) => state.unmatchedSupplierName; export const selectDeliveryExternalNumber = (state = deliveryEditorAutomat.state) => state.externalNumber ?? state.external_number; + export const selectDeliveryNumber = (state = deliveryEditorAutomat.state) => state.number; export const selectDeliveryType = (state = deliveryEditorAutomat.state) => state.type; export const selectDeliveryLineitems = (state = deliveryEditorAutomat.state) => state.lines ?? state.lineitems; diff --git a/src/components/manifest/ManifestPdfDialog.jsx b/src/components/manifest/ManifestPdfDialog.jsx index 6100e22..7a7630e 100644 --- a/src/components/manifest/ManifestPdfDialog.jsx +++ b/src/components/manifest/ManifestPdfDialog.jsx @@ -92,9 +92,18 @@ function EventLog({ messages, streaming }) { ); } -export default function ManifestPdfDialog({ open, filename, data, onClose, onAbort }) { +export default function ManifestPdfDialog({ + open, + filename, + data, + unmatchedSupplierName, + onCreateSupplier, + onClose, + onAbort, +}) { if (!open) return null; + const { streaming = false, messages = [], result = null, error = null, aborted = false } = data ?? {}; const hasResult = Boolean(result); @@ -177,6 +186,28 @@ export default function ManifestPdfDialog({ open, filename, data, onClose, onAbo Daten erfolgreich in das Formular übernommen. )} + + {/* Unmatched supplier alert */} + {hasResult && !streaming && !error && !aborted && unmatchedSupplierName && ( + onCreateSupplier(unmatchedSupplierName)} + > + Lieferant anlegen + + ) : null + } + > + Lieferant „{unmatchedSupplierName}“ wurde im Dokument erkannt, existiert aber noch nicht in den Stammdaten. + + )} @@ -191,11 +222,28 @@ export default function ManifestPdfDialog({ open, filename, data, onClose, onAbo Abbrechen ) : ( - + <> + {unmatchedSupplierName && onCreateSupplier ? ( + + ) : null} + + )} + ); } diff --git a/src/components/withNavigate.jsx b/src/components/withNavigate.jsx index 4b0354b..595d87f 100644 --- a/src/components/withNavigate.jsx +++ b/src/components/withNavigate.jsx @@ -1,9 +1,11 @@ -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; export default function withNavigate(Wrapped) { function NavigateWrapper(props) { const navigate = useNavigate(); - return ; + const location = useLocation(); + return ; } return NavigateWrapper; } + diff --git a/src/pages/lieferung/lieferanten.jsx b/src/pages/lieferung/lieferanten.jsx index 362b0a2..8fa4b8f 100644 --- a/src/pages/lieferung/lieferanten.jsx +++ b/src/pages/lieferung/lieferanten.jsx @@ -1,5 +1,7 @@ import { PureComponent } from 'react'; +import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; + import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; import IconButton from '@mui/material/IconButton'; @@ -13,11 +15,14 @@ import { enqueueSnackbar } from 'notistack'; import BackendError from '../../components/BackendError'; import SingleLineTextEdit from 'dashgui/SingleLineTextEdit'; import { PanelTile, PanelTileGrid } from 'dashgui/panels'; +import withNavigate from '../../components/withNavigate'; import { StorageManagerContext } from '../../api/storageManagerContext'; import { requireDefined, requireObject } from '../../utils/apiShape'; import { backendErrorFrom } from '../../utils/backendError'; import { notifyError } from '../../utils/notify'; import { setLastDeliveryPath } from '../../automats'; +import { deliveryDraftAutomat } from '../../automats/deliveryEditorAutomat'; + function sortSuppliers(suppliers) { return [...suppliers].sort((a, b) => @@ -29,17 +34,34 @@ function draftsFrom(suppliers) { return Object.fromEntries(suppliers.map((row) => [row.id, row.name])); } +function getInitialParams(props) { + const searchParams = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null; + const prefill = + props.location?.state?.name ?? + props.location?.state?.newName ?? + searchParams?.get('name') ?? + searchParams?.get('newName') ?? + deliveryDraftAutomat.state.unmatchedSupplierName ?? + ''; + const returnTo = + props.location?.state?.returnTo ?? + searchParams?.get('returnTo') ?? + null; + return { prefill: prefill.trim(), returnTo }; +} + class LieferungLieferantenPage extends PureComponent { static contextType = StorageManagerContext; constructor(props) { super(props); + const { prefill } = getInitialParams(props); this.state = { status: 'loading', error: null, suppliers: [], drafts: {}, - newName: '', + newName: prefill, busyKey: null, }; this._cancelled = false; @@ -49,11 +71,27 @@ class LieferungLieferantenPage extends PureComponent { } componentDidMount() { - setLastDeliveryPath(null); + const { returnTo, prefill } = getInitialParams(this.props); + if (!returnTo) { + setLastDeliveryPath(null); + } + if (prefill && !this.state.newName) { + this.setState({ newName: prefill }); + } this._cancelled = false; void this.loadSuppliers(); } + componentDidUpdate(prevProps) { + if (prevProps.location !== this.props.location) { + const { prefill } = getInitialParams(this.props); + if (prefill && prefill !== this.state.newName) { + this.setState({ newName: prefill }); + } + } + } + + componentWillUnmount() { this._cancelled = true; } @@ -94,16 +132,28 @@ class LieferungLieferantenPage extends PureComponent { const name = this.state.newName.trim(); if (!name || this.state.busyKey != null) return; + const { returnTo } = getInitialParams(this.props); + this.setState({ busyKey: 'new' }); try { const data = await this.context.supplier.create({ name }); requireObject(data, 'POST /api/supplier'); requireObject(data.supplier, 'supplier'); requireDefined(data.supplier.id, 'supplier.id'); + const newSupplier = data.supplier; + + // Assign the new supplier to the active delivery draft & clear unmatched name + deliveryDraftAutomat.actions.setDeliveryEntity(newSupplier.id); + deliveryDraftAutomat.actions.setUnmatchedSupplierName(null); + await this.refreshFromServer(); if (this._cancelled) return; this.setState({ newName: '', busyKey: null }); - enqueueSnackbar('Lieferant angelegt.', { variant: 'success' }); + enqueueSnackbar(`Lieferant „${newSupplier.name || name}“ angelegt.`, { variant: 'success' }); + + if (returnTo && this.props.navigate) { + this.props.navigate(returnTo); + } } catch (err) { if (this._cancelled) return; notifyError(err); @@ -111,6 +161,7 @@ class LieferungLieferantenPage extends PureComponent { } } + async handleRename(supplier) { const draft = String(this.state.drafts[supplier.id] ?? '').trim(); if (!draft) { @@ -165,6 +216,8 @@ class LieferungLieferantenPage extends PureComponent { const { status, error, suppliers, drafts, newName, busyKey } = this.state; const busy = busyKey != null; + const { returnTo } = getInitialParams(this.props); + if (status === 'loading') { return ( @@ -183,6 +236,26 @@ class LieferungLieferantenPage extends PureComponent { + {returnTo ? ( + this.props.navigate?.(returnTo)} + > + Zurück zur Lieferung + + } + > + {newName + ? `Lieferantenname „${newName}“ aus Dokument übernommen.` + : 'Neuen Lieferanten für die Lieferung anlegen.'} + + ) : null} + Lieferanten der ausgewählten Firma. @@ -271,4 +344,5 @@ class LieferungLieferantenPage extends PureComponent { } } -export default LieferungLieferantenPage; +export default withNavigate(LieferungLieferantenPage); + diff --git a/src/pages/lieferung/neu.jsx b/src/pages/lieferung/neu.jsx index aac80f2..977fe7c 100644 --- a/src/pages/lieferung/neu.jsx +++ b/src/pages/lieferung/neu.jsx @@ -1,7 +1,9 @@ import { PureComponent } from 'react'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; + import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import { enqueueSnackbar } from 'notistack'; @@ -54,11 +56,14 @@ class LieferungNeuPage extends PureComponent { textEditorAttachment: null, articleSearchDialogOpen: false, articleSearchLine: null, + unmatchedSupplierName: null, }; this._cancelled = false; this.handleRetry = this.handleRetry.bind(this); this.handlePartyChange = this.handlePartyChange.bind(this); + this.handleCreateSupplierFromPdf = this.handleCreateSupplierFromPdf.bind(this); this.handleDateChange = this.handleDateChange.bind(this); + this.handleNumberChange = this.handleNumberChange.bind(this); this.handleExternalNumberChange = this.handleExternalNumberChange.bind(this); this.handleLineChange = this.handleLineChange.bind(this); @@ -209,8 +214,20 @@ class LieferungNeuPage extends PureComponent { handlePartyChange(partyId) { this.automat.actions.setDeliveryEntity(partyId); + if (partyId != null) { + this.setState({ unmatchedSupplierName: null }); + this.automat.actions.setUnmatchedSupplierName(null); + } } + handleCreateSupplierFromPdf(supplierName) { + const name = (supplierName ?? this.state.unmatchedSupplierName ?? '').trim(); + this.handleClosePdfDialog(); + const target = `/lieferung/lieferanten?name=${encodeURIComponent(name)}&returnTo=/lieferung/neu`; + this.props.navigate(target, { state: { name, returnTo: '/lieferung/neu' } }); + } + + handleDateChange(date) { this.automat.actions.setDate(date); } @@ -424,7 +441,7 @@ class LieferungNeuPage extends PureComponent { }; this.automat.actions.addAttachment(pdfAttachment); - // 2. Extract text using pdftotext-wasm to create 2nd file (textfile) + // 2. Extract text using pdfconvert-wasm to create 2nd file (textfile) try { const text = await extractPdfText(file, { layout: true }); const txtName = file.name.replace(/\.pdf$/i, '') + '.txt'; @@ -441,7 +458,7 @@ class LieferungNeuPage extends PureComponent { }; this.automat.actions.addAttachment(txtAttachment); } catch (txtErr) { - console.error('Fehler beim Extrahieren von Text mit pdftotext-wasm:', txtErr); + console.error('Fehler beim Extrahieren von Text mit pdfconvert-wasm:', txtErr); } } else { // Direct text / CSV attachment @@ -612,9 +629,17 @@ class LieferungNeuPage extends PureComponent { const docType = result?.type ?? result?.basedata?.manifestType; const notes = Array.isArray(result?.notes) ? result.notes : []; + const parsedSupplierName = ( + result?.supplierName ?? + result?.basedata?.lieferantName ?? + result?.lieferantName ?? + result?.basedata?.supplierName ?? + '' + ).trim(); + // If supplierId is null, try matching supplier by name from loaded delivery entities - if (supplierId == null && result?.basedata?.lieferantName && Array.isArray(this.state.deliveryEntities)) { - const nameToMatch = result.basedata.lieferantName.trim().toLowerCase(); + if (supplierId == null && parsedSupplierName && Array.isArray(this.state.deliveryEntities)) { + const nameToMatch = parsedSupplierName.toLowerCase(); const matched = this.state.deliveryEntities.find( (e) => e.name && @@ -625,6 +650,11 @@ class LieferungNeuPage extends PureComponent { if (matched) supplierId = matched.id; } + const unmatchedSupplierName = supplierId == null && parsedSupplierName ? parsedSupplierName : null; + this.setState({ unmatchedSupplierName }); + this.automat.actions.setUnmatchedSupplierName(unmatchedSupplierName); + + if (docDate) this.automat.actions.setDate(docDate); if (supplierId != null) this.automat.actions.setDeliveryEntity(supplierId); if (extNum != null) { @@ -1018,16 +1048,50 @@ class LieferungNeuPage extends PureComponent { /> {partyId == null && (!lines || lines.length === 0) ? ( - - {cfg.partyGateMessage} - + {this.state.unmatchedSupplierName ? ( + + + Lieferant „{this.state.unmatchedSupplierName}“ wurde im Dokument erkannt, existiert aber noch nicht in den Stammdaten. + + + + + + ) : ( + + {cfg.partyGateMessage} + + )} ) : ( <> - {partyId == null && lines && lines.length > 0 && ( + {partyId == null && ( - - Bitte wählen Sie oben einen Lieferanten aus, um die erfassten Positionen speichern zu können. + this.handleCreateSupplierFromPdf(this.state.unmatchedSupplierName)} + > + Lieferant anlegen + + ) : null + } + sx={{ py: 0.5, alignItems: 'center' }} + > + {this.state.unmatchedSupplierName + ? `Lieferant „${this.state.unmatchedSupplierName}“ wurde im Dokument erkannt, existiert aber noch nicht in den Stammdaten.` + : 'Bitte wählen Sie oben einen Lieferanten aus, um die erfassten Positionen speichern zu können.'} )} @@ -1069,9 +1133,12 @@ class LieferungNeuPage extends PureComponent { open={pdfDialogOpen} filename={pdfFileName} data={pdfDialogData} + unmatchedSupplierName={this.state.unmatchedSupplierName} + onCreateSupplier={this.handleCreateSupplierFromPdf} onClose={this.handleClosePdfDialog} onAbort={this.handleAbortParse} /> + { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); } +