From c1efc7916fbd4dbe8c77829c6d646dce21f08482 Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 11 Aug 2026 23:40:37 +0200 Subject: [PATCH] feat(email/pdf): include partner company name, partner user name and email in emails and PDF confirmations --- shop/components/invoice-pdf.tsx | 51 +++++++++++++++++++++++---------- shop/lib/actions/orders.ts | 37 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/shop/components/invoice-pdf.tsx b/shop/components/invoice-pdf.tsx index 49cd9d2..0a5d33e 100644 --- a/shop/components/invoice-pdf.tsx +++ b/shop/components/invoice-pdf.tsx @@ -94,7 +94,14 @@ const styles = StyleSheet.create({ } }); -export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => { +export const InvoicePDF = ({ + order, + orderSnapshot, + customer, + partnerCompanyName, + partnerUserName, + partnerUserEmail, +}: any) => { const items = orderSnapshot?.items ?? []; const taxRate = orderSnapshot?.tax_rate ?? 19; const isSubscription = order?.type === 'subscription' || orderSnapshot?.billing_cycle === 'monthly'; @@ -112,7 +119,6 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => { item.selected_modules?.forEach((mod: any) => { const qty = mod.quantity || 1; const price = mod.total_price ?? (mod.price * qty); - // Module sind monatliche wiederkehrende Beträge (Abo/Miete) monthlyNet += price; }); }); @@ -135,6 +141,10 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => { groupedItems[devName].push(item); }); + const displayPartnerName = partnerCompanyName || order?.partner_company_name; + const displayUserName = partnerUserName || order?.partner_user_name; + const displayUserEmail = partnerUserEmail || order?.partner_user_email; + return ( @@ -155,20 +165,31 @@ export const InvoicePDF = ({ order, orderSnapshot, customer }: any) => { Anfragebestätigung: {order.order_number || order.id} - - Kunde - {customer.company_name} - {customer.first_name} {customer.last_name} - {customer.address} - {customer.zip_code} {customer.city} - USt-IdNr: {customer.vat_id} - + + + Endkunde + {customer.company_name} + {customer.first_name || customer.last_name ? {customer.first_name} {customer.last_name} : null} + {customer.address ? {customer.address} : null} + {customer.zip_code || customer.city ? {customer.zip_code} {customer.city} : null} + {customer.vat_id ? USt-IdNr: {customer.vat_id} : null} + - - Bestelldetails - Bestellnummer: {order.order_number || order.id} - Zahlungsart: {isSubscription ? 'SEPA-Lastschrift' : 'Rechnung'} - Datum: {new Date(order.created_at || Date.now()).toLocaleDateString('de-DE')} + + Bestelldetails + Bestellnummer: {order.order_number || order.id} + Zahlungsart: {isSubscription ? 'SEPA-Lastschrift' : 'Rechnung'} + Datum: {new Date(order.created_at || Date.now()).toLocaleDateString('de-DE')} + + {(displayPartnerName || displayUserName || displayUserEmail) && ( + + Erstellt durch Partner / User + {displayPartnerName ? Partner: {displayPartnerName} : null} + {displayUserName ? Erstellt von: {displayUserName} : null} + {displayUserEmail ? E-Mail: {displayUserEmail} : null} + + )} + diff --git a/shop/lib/actions/orders.ts b/shop/lib/actions/orders.ts index a04d9cb..42d7433 100644 --- a/shop/lib/actions/orders.ts +++ b/shop/lib/actions/orders.ts @@ -16,6 +16,30 @@ import { getOrderEmailTemplate, getStatusEmailTemplate, buildEmailItemsSection } // ─── Hilfsfunktionen ───────────────────────────────────────────────────────── +async function fetchOrderPartnerInfo(adminClient: any, companyId: string | null, userId: string | null) { + let partnerCompanyName = '' + let partnerUserName = '' + let partnerUserEmail = '' + + if (companyId) { + const { data: comp } = await adminClient.from('companies').select('name').eq('id', companyId).single() + if (comp) partnerCompanyName = comp.name + } + + if (userId) { + const { data: dbUser } = await adminClient.from('users').select('first_name, last_name, email, role').eq('id', userId).single() + if (dbUser) { + partnerUserName = [dbUser.first_name, dbUser.last_name].filter(Boolean).join(' ') + partnerUserEmail = dbUser.email || '' + if (!partnerCompanyName && (dbUser.role === 'admin' || dbUser.role === 'verwaltung')) { + partnerCompanyName = 'Administrator / Direktbestellung' + } + } + } + + return { partnerCompanyName, partnerUserName, partnerUserEmail } +} + /** @@ -164,6 +188,9 @@ export async function submitOrder(params: { order, customer: customerSnapshot, orderSnapshot, + partnerCompanyName, + partnerUserName, + partnerUserEmail, }) ) @@ -382,6 +409,8 @@ export async function updateOrderStatus( throw new Error(`Fehler beim Aktualisieren des Status: ${updateError?.message || 'Unbekannt'}`) } + const partnerInfo = await fetchOrderPartnerInfo(admin, updatedOrder.company_id, updatedOrder.user_id) + // 3. Wenn Status auf 'active' oder 'approved' wechselt, generiere PDF und lade es hoch (pdf-invoice-generator) let attachmentBuffer: Buffer | null = null if (newStatus === 'active' || newStatus === 'approved') { @@ -394,6 +423,9 @@ export async function updateOrderStatus( order: updatedOrder, customer: customerSnapshot, orderSnapshot, + partnerCompanyName: partnerInfo.partnerCompanyName, + partnerUserName: partnerInfo.partnerUserName, + partnerUserEmail: partnerInfo.partnerUserEmail, }) ) attachmentBuffer = buffer @@ -751,6 +783,8 @@ export async function updateOrder( if (orderError || !order) throw orderError || new Error('Fehler beim Aktualisieren der Bestellung.') + const partnerInfo = await fetchOrderPartnerInfo(admin, order.company_id, order.user_id) + // 3. PDF generieren und überschreiben try { const buffer = await renderToBuffer( @@ -758,6 +792,9 @@ export async function updateOrder( order, customer: customerSnapshot, orderSnapshot, + partnerCompanyName: partnerInfo.partnerCompanyName, + partnerUserName: partnerInfo.partnerUserName, + partnerUserEmail: partnerInfo.partnerUserEmail, }) )