feat(handelsware-datev): API, Kontenliste mit Zeilenschutz und DATEV-Export
- Prisma-Modelle HandelswareDatevConfig und HandelswareKonto mit Zeilenschutz (Migration 20261002130000) - XLSX lesen (B1 Kopf, A/B ab Zeile 2, Zahl oder deutscher Text), Konten zuordnen, TXT erzeugen - neue Konten werden nur beim Export in einer mandantengebundenen Transaktion gespeichert (409 bei geaenderter Liste) - Konten-CSV Import (alles ersetzen) und Export mit Schutz vor Formeleinschleusung - Einstellungen nur fuer Administratoren, statische Routen vor accounts/:id Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { decodeUploadFilename } from './decode-upload-filename';
|
||||
|
||||
describe('decodeUploadFilename', () => {
|
||||
it('laesst ASCII-Namen unveraendert', () => {
|
||||
expect(decodeUploadFilename('HWA 0326 Test.xlsx')).toBe('HWA 0326 Test.xlsx');
|
||||
});
|
||||
|
||||
it('kehrt latin1-gelesenes UTF-8 um', () => {
|
||||
const mojibake = Buffer.from('Käse 0326.xlsx', 'utf8').toString('latin1');
|
||||
expect(decodeUploadFilename(mojibake)).toBe('Käse 0326.xlsx');
|
||||
});
|
||||
|
||||
it('laesst einen schon richtigen Namen mit Umlaut stehen', () => {
|
||||
expect(decodeUploadFilename('Käse.xlsx')).toBe('Käse.xlsx');
|
||||
});
|
||||
|
||||
it('laesst Namen mit Zeichen ueber 255 stehen', () => {
|
||||
expect(decodeUploadFilename('Preis €.xlsx')).toBe('Preis €.xlsx');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Multer liefert `originalname` je nach Version als latin1-gelesene Bytes: ein
|
||||
* UTF-8-Dateiname wie "Käse.xlsx" kommt als "Käse.xlsx" an. Diese Funktion
|
||||
* kehrt das um, ohne einen schon richtigen Namen zu zerstoeren: ist der Name
|
||||
* nicht aus latin1-Zeichen zusammengesetzt (Zeichen > 255) oder ergibt die
|
||||
* Umkehrung kein gueltiges UTF-8, bleibt er unveraendert.
|
||||
*/
|
||||
export function decodeUploadFilename(name: string): string {
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
if (name.charCodeAt(i) > 255) return name;
|
||||
}
|
||||
const converted = Buffer.from(name, 'latin1').toString('utf8');
|
||||
return converted.includes('�') ? name : converted;
|
||||
}
|
||||
Reference in New Issue
Block a user