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

+14 -3
View File
@@ -28,7 +28,8 @@ import http from 'node:http'
* Rechnung muss nicht in st.customers stehen (dann bleiben Name und E-Mail leer). Traegt ein Aufruf die Kopfzeile
* Idempotency-Key, steht sie als idempotencyKey in calls; delayNext verzoegert eine Antwort (Zeitgrenzen testen).
* Kunden: GET /v1/customers/search (Metadaten), POST /v1/customers (anlegen) und POST /v1/customers/:id (aendern:
* email, name, address, metadata; unbekannte ID -> 404).
* email, name, address, metadata; unbekannte ID -> 404). Wie Stripe fuehrt ein Update die Anschrift feldweise zusammen
* (nur gesendete Felder aendern sich) und ein leerer Text loescht das Feld (gespeichert als null).
*/
// Parameter, die Stripe bei ausstehenden Aktualisierungen annimmt (docs.stripe.com/billing/subscriptions/
@@ -153,6 +154,16 @@ export async function startFakeStripe() {
return root
}
// Anschrift eines Kunden: ein Hash wird feldweise in die bestehende Anschrift geschrieben, '' loescht das Feld (null);
// ein leerer Text statt eines Hashes loescht die ganze Anschrift (wie Stripe)
const mergeAddress = (current, incoming) => {
if (incoming === undefined) return current ?? null
if (typeof incoming !== 'object' || incoming === null) return incoming === '' ? null : (current ?? null)
const merged = { ...(current || {}) }
for (const [field, value] of Object.entries(incoming)) merged[field] = value === '' ? null : value
return merged
}
const route = (method, path, query, body) => {
let m
if (method === 'GET' && (m = path.match(/^\/v1\/subscriptions\/([^/]+)$/))) {
@@ -513,7 +524,7 @@ export async function startFakeStripe() {
object: 'customer',
email: data.email ?? null,
name: data.name ?? null,
address: data.address ?? null,
address: mergeAddress(null, data.address),
invoice_settings: data.invoice_settings || {},
metadata: data.metadata || {},
}
@@ -531,7 +542,7 @@ export async function startFakeStripe() {
...customer,
...(data.email !== undefined ? { email: data.email || null } : {}),
...(data.name !== undefined ? { name: data.name || null } : {}),
...(data.address !== undefined ? { address: data.address } : {}),
...(data.address !== undefined ? { address: mergeAddress(customer.address, data.address) } : {}),
metadata,
}
customers.set(customer.id, updated)