42b89f110b
- Reiter Import: Vorschau mit neu-Markierung, Buchungsdatum (TTMM) aus dem Dateinamen, Download speichert neue Konten - Reiter Konten: anlegen, bearbeiten, loeschen mit Rueckfrage, CSV-Import (ersetzt alles, nach Rueckfrage) und -Export - Reiter Einstellungen nur fuer Administratoren; Texte de/en Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
/**
|
|
* Handelsware — API-Client (quick-261002-fm5). Konsumiert
|
|
* `/modules/handelsware-datev`. Die Excel-Datei geht als Multipart-Feld `file`;
|
|
* beim Export zusaetzlich `buchungsdatum` und `newAccounts` (JSON-Text der von
|
|
* der Vorschau gemeldeten neuen Konten) — die API liest alle Zeilen erneut aus
|
|
* derselben Datei, damit die TXT nicht von der Arbeitsmappe abweichen kann.
|
|
*/
|
|
|
|
import { accountingRequest, type FileResponse } from '@/lib/accounting-request';
|
|
|
|
export type { FileResponse } from '@/lib/accounting-request';
|
|
export { AccountingRequestError as HandelswareRequestError } from '@/lib/accounting-request';
|
|
|
|
const BASE = '/modules/handelsware-datev';
|
|
|
|
export interface HandelswareSettings {
|
|
erloeskonto: number | null;
|
|
startGegenkonto: number | null;
|
|
configured: boolean;
|
|
}
|
|
|
|
export interface HandelswareSettingsInput {
|
|
erloeskonto: number;
|
|
startGegenkonto: number;
|
|
}
|
|
|
|
export interface HandelswareAccount {
|
|
id: string;
|
|
name: string;
|
|
gegenkonto: number;
|
|
erloeskonto: number;
|
|
}
|
|
|
|
export interface HandelswareAccountInput {
|
|
name: string;
|
|
gegenkonto: number;
|
|
erloeskonto: number;
|
|
}
|
|
|
|
export interface NewAccount {
|
|
name: string;
|
|
gegenkonto: number;
|
|
erloeskonto: number;
|
|
}
|
|
|
|
export interface PreviewRow {
|
|
line: number;
|
|
buchungstext: string;
|
|
umsatz: string;
|
|
sollHaben: 'S' | 'H';
|
|
gegenkonto: number;
|
|
erloeskonto: number;
|
|
isNew: boolean;
|
|
}
|
|
|
|
export interface RowError {
|
|
line: number;
|
|
code: string;
|
|
message: string;
|
|
}
|
|
|
|
export interface HandelswarePreview {
|
|
headerText: string;
|
|
suggestedBuchungsdatum: string;
|
|
exportFilename: string;
|
|
rows: PreviewRow[];
|
|
newAccounts: NewAccount[];
|
|
rowErrors: RowError[];
|
|
}
|
|
|
|
export type HandelswareExportResult = FileResponse & { createdCount: number };
|
|
|
|
const DAYS_PER_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
|
|
/** TTMM: vier Ziffern, Monat 1-12, Tag passend zum Monat (Februar bis 29) — wie die API. */
|
|
export function isValidBuchungsdatum(ttmm: string): boolean {
|
|
if (!/^\d{4}$/.test(ttmm)) return false;
|
|
const day = Number.parseInt(ttmm.slice(0, 2), 10);
|
|
const month = Number.parseInt(ttmm.slice(2, 4), 10);
|
|
if (month < 1 || month > 12) return false;
|
|
return day >= 1 && day <= DAYS_PER_MONTH[month - 1];
|
|
}
|
|
|
|
function fileForm(file: File): FormData {
|
|
const form = new FormData();
|
|
form.append('file', file);
|
|
return form;
|
|
}
|
|
|
|
export function getHandelswareSettings(): Promise<HandelswareSettings> {
|
|
return accountingRequest(`${BASE}/settings`);
|
|
}
|
|
|
|
export function saveHandelswareSettings(
|
|
input: HandelswareSettingsInput,
|
|
): Promise<HandelswareSettings> {
|
|
return accountingRequest(`${BASE}/settings`, { method: 'PUT', json: input });
|
|
}
|
|
|
|
export function previewHandelsware(file: File): Promise<HandelswarePreview> {
|
|
return accountingRequest(`${BASE}/preview`, { method: 'POST', form: fileForm(file) });
|
|
}
|
|
|
|
export function exportHandelsware(
|
|
file: File,
|
|
buchungsdatum: string,
|
|
newAccounts: NewAccount[],
|
|
): Promise<HandelswareExportResult> {
|
|
const form = fileForm(file);
|
|
form.append('buchungsdatum', buchungsdatum);
|
|
form.append('newAccounts', JSON.stringify(newAccounts));
|
|
return accountingRequest(`${BASE}/export`, { method: 'POST', form });
|
|
}
|
|
|
|
export function listAccounts(): Promise<HandelswareAccount[]> {
|
|
return accountingRequest(`${BASE}/accounts`);
|
|
}
|
|
|
|
export function createAccount(input: HandelswareAccountInput): Promise<HandelswareAccount> {
|
|
return accountingRequest(`${BASE}/accounts`, { method: 'POST', json: input });
|
|
}
|
|
|
|
export function updateAccount(
|
|
id: string,
|
|
input: HandelswareAccountInput,
|
|
): Promise<HandelswareAccount> {
|
|
return accountingRequest(`${BASE}/accounts/${encodeURIComponent(id)}`, {
|
|
method: 'PUT',
|
|
json: input,
|
|
});
|
|
}
|
|
|
|
export function deleteAccount(id: string): Promise<{ deleted: true }> {
|
|
return accountingRequest(`${BASE}/accounts/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
}
|
|
|
|
export function importAccountsCsv(file: File): Promise<{ count: number }> {
|
|
return accountingRequest(`${BASE}/accounts/import-csv`, { method: 'POST', form: fileForm(file) });
|
|
}
|
|
|
|
export function exportAccountsCsv(): Promise<FileResponse> {
|
|
return accountingRequest(`${BASE}/accounts/export-csv`);
|
|
}
|