Plain-text paid badge, uniform item rows, show B2B recipient fields
Validate e-invoices / mustang (push) Successful in 22s

- The "Bereits beglichen" confirmation is now plain green Text, not a
  tinted pill/box — read as too heavy for a status note.
- Item table rows no longer alternate white/tinted (zebra striping);
  every row now shares the same tinted background for a more uniform
  table, on both the invoice and its Storno/Gutschrift corrections.
- InvoiceOrder/CorrectionInvoiceOrder gained optional companyName/
  vatId, rendered in the "An" recipient block when present — the
  buyer-side counterpart to the sellers' own fields already shown in
  the footer. Both consuming repos need their own commit to actually
  pass these through from Orders.companyName/vatId.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-23 18:16:27 +00:00
parent ceedb4be05
commit 48da8c069a
2 changed files with 31 additions and 16 deletions
+20 -13
View File
@@ -19,7 +19,6 @@ const TEXT_MUTED = "#6b6b69";
const BORDER = "#e5e0d8";
const BG_MUTED = "#f8f5f1";
const SUCCESS = "#2f8f4e";
const SUCCESS_TINT = "#e7f5eb";
const styles = StyleSheet.create({
page: { padding: 0, fontSize: 10, fontFamily: "Helvetica", color: "#1a1a18" },
@@ -50,12 +49,13 @@ const styles = StyleSheet.create({
metaBox: { borderWidth: 1, borderColor: BORDER, borderRadius: 6, paddingVertical: 8, paddingHorizontal: 12 },
metaLabel: { fontSize: 7, color: TEXT_MUTED, textTransform: "uppercase", marginBottom: 2 },
metaValue: { fontSize: 10, fontFamily: "Helvetica-Bold" },
paidBadge: { backgroundColor: SUCCESS_TINT, borderRadius: 6, paddingVertical: 8, paddingHorizontal: 12, justifyContent: "center" },
paidBadgeText: { fontSize: 10, fontFamily: "Helvetica-Bold", color: SUCCESS },
table: { borderRadius: 6, overflow: "hidden", borderWidth: 1, borderColor: BORDER, marginTop: 8, marginBottom: 16 },
tableHeader: { flexDirection: "row", backgroundColor: BG_MUTED, paddingVertical: 8, paddingHorizontal: 10 },
tableRow: { flexDirection: "row", paddingVertical: 8, paddingHorizontal: 10, borderTopWidth: 1, borderTopColor: BORDER },
tableRowAlt: { backgroundColor: BG_MUTED },
// Every item row shares this same tinted background now — no more
// odd/even zebra striping that left alternating rows plain white; the
// borderTop is what separates rows visually instead.
tableRow: { flexDirection: "row", paddingVertical: 8, paddingHorizontal: 10, borderTopWidth: 1, borderTopColor: BORDER, backgroundColor: BG_MUTED },
// marginRight (not a tableRow `gap`) — explicit and unambiguous across
// react-pdf's flexbox implementation for a mixed Image/Text row, rather
// than relying on a row-level `gap` to apply consistently between an
@@ -85,11 +85,12 @@ const styles = StyleSheet.create({
// Moved here (below the summary card) from the top meta row — reads more
// naturally right next to the amount it's confirming was paid, and keeps
// the meta row itself to just the three reference numbers. Deliberately
// a *sibling* of summaryBox, not a row inside it — the badge confirms
// the whole card's total, so it sits visually outside/below the card
// rather than being absorbed as one more line inside it (a correction
// made after initially placing it inside the card — see this package's
// README/the e-invoicing memory notes if this needs revisiting again).
// a *sibling* of summaryBox, not a row inside it — the confirmation
// applies to the whole card's total, so it sits visually outside/below
// the card rather than being absorbed as one more line inside it (a
// correction made after initially placing it inside the card). Plain
// green text, not a pill/badge box — a colored background here read as
// too heavy/attention-grabbing for what's just a status note.
paidBadgeRow: { alignItems: "flex-end", marginTop: 10 },
// `fixed` (below, on the element) + absolute positioning — always pinned
// to the bottom of the page regardless of how much content is above it,
@@ -128,6 +129,12 @@ export type InvoiceOrder = {
invoiceIssuedAt: string;
customerFirstName: string;
customerLastName: string;
// Optional B2B recipient fields (Orders.companyName/vatId in the Payload
// backend) — when present, shown in the "An" block: companyName as its
// own line above the contact person's name, vatId as its own line below
// the address. Neither implies the other (see Orders.ts's own comment).
companyName?: string | null;
vatId?: string | null;
deliveryMethod: "address" | "packstation";
street?: string | null;
packstationNumber?: string | null;
@@ -258,6 +265,7 @@ export function InvoiceDocument({ order, seller }: { order: InvoiceOrder; seller
</View>
<View style={addressBlockStyle}>
<Text style={styles.addressLabel}>An</Text>
{order.companyName && <Text style={styles.addressLine}>{order.companyName}</Text>}
<Text style={styles.addressLine}>
{order.customerFirstName} {order.customerLastName}
</Text>
@@ -266,6 +274,7 @@ export function InvoiceDocument({ order, seller }: { order: InvoiceOrder; seller
{order.zip} {order.city}
</Text>
<Text style={styles.addressLine}>{order.country}</Text>
{order.vatId && <Text style={styles.addressLine}>USt-IdNr. {order.vatId}</Text>}
</View>
{order.hasDifferentShippingAddress && (
<View style={addressBlockStyle}>
@@ -311,7 +320,7 @@ export function InvoiceDocument({ order, seller }: { order: InvoiceOrder; seller
<Text style={[styles.colTotal, styles.headerCell]}>Betrag</Text>
</View>
{order.items.map((item, i) => (
<View style={[styles.tableRow, i % 2 === 1 ? styles.tableRowAlt : {}]} key={i}>
<View style={styles.tableRow} key={i}>
<View style={styles.colImage}>
{item.imageUrl && <Image src={item.imageUrl} style={styles.itemImage} />}
</View>
@@ -362,9 +371,7 @@ export function InvoiceDocument({ order, seller }: { order: InvoiceOrder; seller
</View>
{paid && (
<View style={styles.paidBadgeRow}>
<View style={styles.paidBadge}>
<Text style={styles.paidBadgeText}> Bereits beglichen ({order.paymentMethodTitle})</Text>
</View>
<Text style={styles.paidBadgeText}> Bereits beglichen ({order.paymentMethodTitle})</Text>
</View>
)}
</View>