Finanzen: Nachtrag vor Livegang (Kundenabgleich beim Senden, Adress-Sperre, Land und Anschrift sauber an Stripe)

- sendInvoiceNow: Entwurf mit webklarCustomerId laedt den WOMS-Kunden und bringt den Stripe-Kunden vor dem
  Finalisieren auf die aktuellen Daten (Fehler: 502, nichts finalisiert); ohne Kennung kein Abgleich
- Adress-Sperre (§ 14 Abs. 4 Nr. 1 UStG): Senden ueber 250 € ohne Strasse/PLZ/Ort -> 409 vor jedem Stripe-Schreibzugriff
  (Anlegen mit senden=true und Entwurf senden; Text und Grenze wie im WOMS)
- stripeCustomerFields: Land aus deutschen Namen (unbekannt -> weglassen), PLZ/Ort mit Strasse immer mitsenden ('' loescht)
- Stripe-Nachbau: Kunden-Update fuehrt die Anschrift feldweise zusammen, '' loescht ein Feld

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-01 15:42:06 +00:00
1 parent 73811e0300
commit f0b57ebe41
5 files changed
+377 -26

No files matched your search

+48 -1
View File
@@ -987,13 +987,60 @@ test('POST /rechnungen/:id/senden: bezahlt -> 409, Abo-Rechnung -> 409, unbekann
})
test('POST /rechnungen/:id/senden: Kunde ohne E-Mail-Adresse -> 400', async () => {
putInvoice({ id: 'in_nomail', status: 'draft', number: null, customer_email: null, customer: 'cus_ohne' })
// Entwurf ohne webklarCustomerId (z. B. aus dem Stripe-Dashboard): es gibt keine WOMS-E-Mail, die Stripe nachgetragen werden koennte
putInvoice({ id: 'in_nomail', status: 'draft', number: null, customer_email: null, customer: 'cus_ohne', metadata: {} })
st.customers.set('cus_ohne', { id: 'cus_ohne', object: 'customer', name: 'Ohne', email: null, metadata: {} })
const res = await senden('in_nomail')
assert.equal(res.status, 400)
assert.equal(res.body.error, 'Für den Versand fehlt die E-Mail-Adresse des Kunden.')
})
const ADDRESS_INCOMPLETE = 'Adresse des Kunden unvollständig (Straße, PLZ, Ort). Für den Versand bitte zuerst ergänzen.'
const BIG_ITEMS = [{ description: 'Großauftrag', quantity: 1, unitAmountCents: 30_000 }]
test('POST /rechnungen: senden bei unvollstaendiger Anschrift und Summe ueber 250 € -> 409 mit fester Meldung, nichts bei Stripe; Entwurf geht', async () => {
// c1 hat in den Testdaten keine Anschrift
const blocked = await postInvoice({ customerId: 'c1', items: BIG_ITEMS, senden: true })
assert.equal(blocked.status, 409)
assert.deepEqual(blocked.body, { error: ADDRESS_INCOMPLETE })
assert.equal(st.calls.length, 0)
const draft = await postInvoice({ customerId: 'c1', items: BIG_ITEMS })
assert.equal(draft.status, 200, JSON.stringify(draft.body))
assert.equal(draft.body.rechnung.status, 'entwurf')
})
test('POST /rechnungen/:id/senden: Entwurf ueber 250 € ohne Anschrift -> 409; mit ergaenzter Anschrift wird der Stripe-Kunde vor dem Finalisieren abgeglichen', async () => {
const draft = (await postInvoice({ customerId: 'c1', items: BIG_ITEMS })).body.rechnung.id
st.calls.length = 0
const blocked = await senden(draft)
assert.equal(blocked.status, 409)
assert.deepEqual(blocked.body, { error: ADDRESS_INCOMPLETE })
assert.equal(st.calls.filter((c) => c.method !== 'GET').length, 0)
aw.seed('customers', { ...customers.c1, street: 'Hauptstr. 1', postalCode: '10115', city: 'Berlin' })
st.calls.length = 0
const sent = await senden(draft)
assert.equal(sent.status, 200, JSON.stringify(sent.body))
assert.equal(sent.body.rechnung.status, 'offen')
assert.deepEqual(
st.calls.filter((c) => c.method !== 'GET').map((c) => c.path.replace(draft, ':id')),
['/v1/customers/cus_c1', '/v1/invoices/:id/finalize', '/v1/invoices/:id/send']
)
assert.equal(st.customers.get('cus_c1').address.line1, 'Hauptstr. 1')
aw.seed('customers', customers.c1)
})
test('POST /rechnungen/:id/senden: scheitert der Abgleich des Stripe-Kunden -> 502 mit fester Meldung, nicht finalisiert', async () => {
const draft = (await postInvoice({ customerId: 'c1', items: ITEMS })).body.rechnung.id
st.calls.length = 0
st.failNext({ method: 'POST', path: '/v1/customers/cus_c1', status: 400, message: 'geheime Stripe-Meldung' })
const res = await senden(draft)
assert.equal(res.status, 502)
assert.deepEqual(res.body, { error: 'Kundendaten konnten nicht an Stripe übertragen werden.' })
assert.equal(callsTo('POST', /\/(finalize|send)$/).length, 0)
assert.equal(st.invoices.get(draft).status, 'draft')
})
test('POST /rechnungen/:id/stornieren: offene Rechnung -> storniert; Entwurf und bezahlte -> 409', async () => {
putInvoice({ id: 'in_open' })
putInvoice({ id: 'in_draft', status: 'draft', number: null })