Files
paperlessmanager/paperless-frontend/src/api/settings.ts
T
bjoernpoettkerandClaude Opus 5.5 3d4125b29a
Build and Push Multi-Platform Images / build-and-push (push) Successful in 1m1s
feat(agrarmonitor): fehlende Lieferantennummern vergeben
Neuer Bereich „Lieferantennummern überprüfen“ in den Agrarmonitor-
Einstellungen. Er vergibt allen aktiven Lieferanten ohne
Lieferantennummer eine fortlaufende Nummer im Schema 1555xxxx, ab der
höchsten vergebenen Nummer (Inaktive zählen mit, Altlasten werden
ignoriert).

- Vorschau „Lieferant → neue Nummer“, geschrieben wird erst nach
  Bestätigung; Einzelmodus zum Testen an einem Lieferanten
- formData wird wie in der Weboberfläche aus #form_detail aufgebaut,
  nur die Lieferantennummer ändert sich
- Dublettencheck über vorhanden.php, Erfolgskontrolle durch Neuladen
  des Details, Abbruch beim ersten Fehler
- Lauf im Hintergrund mit Status-Abfrage (wegen Proxy-Timeouts),
  Listenfilter werden danach zurückgesetzt
- HTML-Parsing mit cheerio, Unit-Tests für Parser und Vergabe-Ablauf

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:51:13 +02:00

335 lines
12 KiB
TypeScript

import api from './client';
export interface SettingDocType {
Id: number;
DocumentTypeId: number;
TitelTemplate: string;
TagNotReady: number | null;
TagReady: number | null;
FreigabeErforderlich?: boolean | null;
/** Rechnungstyp-Codes (BT-3) einer E-Rechnung, kommagetrennt (z. B. "380,384") */
ERechnungTypeCodes?: string | null;
}
export interface SettingDocField {
Id: number;
DocumentType: number;
Type: number;
TypeIndex: number | null;
IsRequired: boolean;
IsRequiredPosteingang: boolean;
Hinweis: string | null;
VisiblePosteingang: boolean;
}
// Filter types
export interface FilterCondition {
field: string;
operator: string;
value: any;
}
export interface FilterGroup {
combinator: 'AND' | 'OR';
rules: (FilterCondition | FilterGroup)[];
}
export interface SettingPostprocessing {
Id: number;
Name: string;
FilterJson: FilterGroup;
Order: number;
IsActive: boolean;
NoFurther: boolean;
}
export interface SettingPostprocessingAction {
Id: number;
PostprocessingId: number;
ActionType: number; // 1=Export, 2=Mail, 3=Tags, 4=CustomField, 5=Webhook
Content: Record<string, any>;
Order: number;
IsActive: boolean;
}
export interface SettingExportTarget {
Id: number;
Name: string;
Protocol: string; // 'ftp'
Host: string;
Port: number | null;
Username: string | null;
Password: string | null;
RemotePath: string | null;
IsActive: boolean;
}
export interface SettingPostprocessingLog {
Id: number;
PostprocessingId: number;
ActionId: number | null;
DocumentId: number;
Status: string;
Message: string | null;
CreatedAt: string;
}
export interface SettingUserClient {
Id: number;
UserId: string;
ClientId: number;
Role: 'viewer' | 'editor' | 'admin';
}
export interface SettingClient {
Id: number;
Name: string;
PaperlessUserId: number;
AgrarmonitorBetriebId: number | null;
}
/** Kennungsarten, über die ein Mandant in einer E-Rechnung erkannt wird. */
export type ClientIdentifierTyp = 'ustid' | 'leitwegid' | 'kaeuferkennung';
export interface SettingClientIdentifier {
Id: number;
ClientId: number;
Typ: ClientIdentifierTyp;
Wert: string;
}
export const CLIENT_IDENTIFIER_LABELS: Record<ClientIdentifierTyp, string> = {
ustid: 'USt-IdNr.',
leitwegid: 'Leitweg-ID',
kaeuferkennung: 'Käufer-Kennung',
};
export const settingsApi = {
// Dokumenttypen
getDocTypes: () => api.get<SettingDocType[]>('/api/settings/document-types').then(r => r.data),
updateDocType: (id: number, data: Partial<SettingDocType>) =>
api.put<SettingDocType>(`/api/settings/document-types/${id}`, data).then(r => r.data),
// Document Fields
getDocFields: (docTypeId: number) =>
api.get<SettingDocField[]>(`/api/settings/document-types/${docTypeId}/fields`).then(r => r.data),
createDocField: (docTypeId: number, data: Partial<SettingDocField>) =>
api.post<SettingDocField>(`/api/settings/document-types/${docTypeId}/fields`, data).then(r => r.data),
updateDocField: (id: number, data: Partial<SettingDocField>) =>
api.put<SettingDocField>(`/api/settings/document-fields/${id}`, data).then(r => r.data),
deleteDocField: (id: number) =>
api.delete(`/api/settings/document-fields/${id}`).then(r => r.data),
// Postprocessing
getPostprocessing: () => api.get<SettingPostprocessing[]>('/api/settings/postprocessing').then(r => r.data),
createPostprocessing: (data: Partial<SettingPostprocessing>) =>
api.post<SettingPostprocessing>('/api/settings/postprocessing', data).then(r => r.data),
updatePostprocessing: (id: number, data: Partial<SettingPostprocessing>) =>
api.put<SettingPostprocessing>(`/api/settings/postprocessing/${id}`, data).then(r => r.data),
deletePostprocessing: (id: number) =>
api.delete(`/api/settings/postprocessing/${id}`).then(r => r.data),
duplicatePostprocessing: (id: number) =>
api.post<SettingPostprocessing>(`/api/settings/postprocessing/${id}/duplicate`).then(r => r.data),
// Postprocessing Actions
getActions: (ppId: number) =>
api.get<SettingPostprocessingAction[]>(`/api/settings/postprocessing/${ppId}/actions`).then(r => r.data),
createAction: (ppId: number, data: Partial<SettingPostprocessingAction>) =>
api.post<SettingPostprocessingAction>(`/api/settings/postprocessing/${ppId}/actions`, data).then(r => r.data),
updateAction: (actionId: number, data: Partial<SettingPostprocessingAction>) =>
api.put<SettingPostprocessingAction>(`/api/settings/postprocessing-actions/${actionId}`, data).then(r => r.data),
deleteAction: (actionId: number) =>
api.delete(`/api/settings/postprocessing-actions/${actionId}`).then(r => r.data),
// Export-Ziele
getExportTargets: () => api.get<SettingExportTarget[]>('/api/settings/export-targets').then(r => r.data),
createExportTarget: (data: Partial<SettingExportTarget>) =>
api.post<SettingExportTarget>('/api/settings/export-targets', data).then(r => r.data),
updateExportTarget: (id: number, data: Partial<SettingExportTarget>) =>
api.put<SettingExportTarget>(`/api/settings/export-targets/${id}`, data).then(r => r.data),
deleteExportTarget: (id: number) =>
api.delete(`/api/settings/export-targets/${id}`).then(r => r.data),
testExportTarget: (id: number) =>
api.post<{ success: boolean; message: string }>(`/api/settings/export-targets/${id}/test`).then(r => r.data),
// Postprocessing Logs
getPostprocessingLogs: (limit = 50, offset = 0) =>
api.get<{ data: SettingPostprocessingLog[]; total: number }>('/api/settings/postprocessing-logs', { params: { limit, offset } }).then(r => r.data),
// Benutzer-Betrieb
getUserClients: () => api.get<SettingUserClient[]>('/api/settings/user-clients').then(r => r.data),
createUserClient: (data: Partial<SettingUserClient>) =>
api.post<SettingUserClient>('/api/settings/user-clients', data).then(r => r.data),
deleteUserClient: (id: number) =>
api.delete(`/api/settings/user-clients/${id}`).then(r => r.data),
// Steuertags
getSteuertagIds: () => api.get<{ ids: number[] }>('/api/settings/steuertags').then(r => r.data.ids),
updateSteuertagIds: (ids: number[]) =>
api.put<{ ids: number[] }>('/api/settings/steuertags', { ids }).then(r => r.data.ids),
// Korrespondenten
getCorrespondents: (page = 1, pageSize = 50, search?: string) =>
api.get<{ data: any[], total: number }>('/api/settings/correspondents', { params: { page, pageSize, search } }).then(r => r.data),
createCorrespondent: (data: { name: string }) => api.post<any>('/api/settings/correspondents', data).then(r => r.data),
updateCorrespondentSetting: (id: number, agrarmonitorId: number | null) =>
api.put<any>(`/api/settings/correspondents/${id}`, { agrarmonitorId }).then(r => r.data),
// Betriebe
getClients: () => api.get<SettingClient[]>('/api/settings/clients').then(r => r.data),
updateClient: (id: number, AgrarmonitorBetriebId: number | null) =>
api.put<SettingClient>(`/api/settings/clients/${id}`, { AgrarmonitorBetriebId }).then(r => r.data),
// E-Rechnungs-Kennungen der Mandanten
getClientIdentifiers: () =>
api.get<SettingClientIdentifier[]>('/api/settings/client-identifiers').then(r => r.data),
createClientIdentifier: (data: { ClientId: number; Typ: ClientIdentifierTyp; Wert: string }) =>
api.post<SettingClientIdentifier>('/api/settings/client-identifiers', data).then(r => r.data),
deleteClientIdentifier: (id: number) =>
api.delete(`/api/settings/client-identifiers/${id}`).then(r => r.data),
// Inbox-Postprozessor (global, deprecated)
listInboxActions: () =>
api.get<InboxAction[]>('/api/settings/inbox-actions').then((r) => r.data),
createInboxAction: (data: Partial<InboxAction>) =>
api.post<InboxAction>('/api/settings/inbox-actions', data).then((r) => r.data),
updateInboxAction: (id: number, data: Partial<InboxAction>) =>
api.put<InboxAction>(`/api/settings/inbox-actions/${id}`, data).then((r) => r.data),
deleteInboxAction: (id: number) =>
api.delete(`/api/settings/inbox-actions/${id}`).then((r) => r.data),
// Inbox-Aktionen pro Barcode-Vorlage
listInboxActionsForTemplate: (templateId: number) =>
api.get<InboxAction[]>(`/api/settings/barcode-templates/${templateId}/inbox-actions`).then((r) => r.data),
createInboxActionForTemplate: (templateId: number, data: Partial<InboxAction>) =>
api.post<InboxAction>(`/api/settings/barcode-templates/${templateId}/inbox-actions`, data).then((r) => r.data),
};
export type InboxActionType = 'MAIL' | 'EXPORT' | 'PAPERLESS';
export interface InboxAction {
Id: number;
ActionType: InboxActionType;
Content: Record<string, any>;
Order: number;
IsActive: boolean;
}
export const INBOX_ACTION_LABELS: Record<InboxActionType, string> = {
MAIL: 'Per E-Mail senden',
EXPORT: 'Export (FTP)',
PAPERLESS: 'In Paperless importieren',
};
export interface AgrarmonitorStatusData {
connected: boolean;
registriert: boolean | null;
freigeschaltet: boolean | null;
error?: string;
}
export interface AgrarmonitorPollingConfig {
tagFertig: string;
tagVerbucht: string;
tagHochgeladen: string;
linkField: string;
tagManuell: string;
importWartezeitMinuten: string;
notizMarker: string;
}
export interface AgrarmonitorPollingResult {
processed: number;
updated: number;
skipped: number;
errors: string[];
}
export interface SyncConflict {
agrarmonitorId: number;
correspondents: Array<{ id: number; name: string; documentCount: number }>;
}
export interface SyncCorrespondentsResult {
total: number;
matched: number;
unmatched: number;
autoMerged: number;
conflicts: SyncConflict[];
}
export interface LieferantennummerKandidat {
kundeId: number;
name: string;
neueNummer: string;
}
export interface LieferantennummerVorschau {
maximum: number;
naechsteNummer: number;
kandidaten: LieferantennummerKandidat[];
}
export type LieferantennummerEintragStatus = 'vergeben' | 'uebersprungen' | 'pruefen' | 'fehler';
export interface LieferantennummerEintrag {
kundeId: number;
name: string;
status: LieferantennummerEintragStatus;
nummer?: string;
hinweis?: string;
}
export interface LieferantennummerLaufErgebnis {
modus: 'alle' | 'einzeln';
gestartetAm: string;
beendetAm: string | null;
gesamt: number;
vergeben: number;
uebersprungen: number;
zuPruefen: number;
fehler: number;
abgebrochen: boolean;
eintraege: LieferantennummerEintrag[];
}
export interface LieferantennummerStatus {
aktiv: boolean;
lauf: LieferantennummerLaufErgebnis | null;
}
export const agrarmonitorApi = {
getStatus: () =>
api.get<AgrarmonitorStatusData>('/api/agrarmonitor/status').then((r) => r.data),
registerDevice: (pcName: string, agrarmonitorId: string) =>
api
.post<{ success: boolean; message: string }>('/api/agrarmonitor/register', { pcName, agrarmonitorId })
.then((r) => r.data),
getPollingConfig: () =>
api.get<AgrarmonitorPollingConfig>('/api/agrarmonitor/polling-config').then((r) => r.data),
updatePollingConfig: (config: AgrarmonitorPollingConfig) =>
api.put<AgrarmonitorPollingConfig>('/api/agrarmonitor/polling-config', config).then((r) => r.data),
runPolling: () =>
api.post<AgrarmonitorPollingResult>('/api/agrarmonitor/run-polling').then((r) => r.data),
processUploads: () =>
api.post<AgrarmonitorPollingResult>('/api/agrarmonitor/process-uploads').then((r) => r.data),
syncCorrespondents: () =>
api.post<SyncCorrespondentsResult>('/api/agrarmonitor/sync-correspondents').then((r) => r.data),
mergeCorrespondents: (keepId: number, deleteId: number) =>
api.post<{ mergedDocuments: number }>('/api/agrarmonitor/merge-correspondents', { keepId, deleteId }).then((r) => r.data),
getLieferantennummernStatus: () =>
api.get<LieferantennummerStatus>('/api/agrarmonitor/lieferantennummern/status').then((r) => r.data),
lieferantennummernVorschau: () =>
api
.post<LieferantennummerVorschau>('/api/agrarmonitor/lieferantennummern/vorschau', {}, { timeout: 120_000 })
.then((r) => r.data),
lieferantennummernVergeben: (startNummer: number, nurKundeId?: number) =>
api
.post<LieferantennummerStatus>(
'/api/agrarmonitor/lieferantennummern/vergeben',
{ startNummer, nurKundeId },
{ timeout: 120_000 },
)
.then((r) => r.data),
};