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:
1 parent
73811e0300
commit
f0b57ebe41
5 files changed
+377
-26
No files matched your search
@@ -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 })
|
||||
|
||||
Reference in new issue
Block a user