'use server' import { createClient } from '@/lib/supabase/server' import { createAdminClient } from '@/lib/supabase/admin' import type { Order, EndCustomer, EndCustomerWithOrders, OrderWithRegisterName, FlattenedDevice, EndCustomerWithDevices, OrderItem } from '@/lib/types' /** * Holt alle Bestellanfragen. * RLS filtert automatisch auf die eigene Firma (bzw. lässt Admins alle sehen). */ export async function getCompanyOrders(): Promise { const supabase = await createClient() const { data, error } = await supabase .from('orders') .select('*') .order('created_at', { ascending: false }) if (error) throw error return data as Order[] } /** * Holt alle Endkunden. * RLS filtert automatisch auf die eigene Firma (bzw. lässt Admins alle sehen). */ export async function getCompanyEndCustomers(): Promise { const supabase = await createClient() const { data, error } = await supabase .from('end_customers') .select('*') .order('company_name', { ascending: true }) if (error) throw error return data as EndCustomer[] } /** * Holt alle Endkunden inkl. ihrer Bestellungen/Kassen verschachtelt. * Extrahiert den Kassen-Namen aus order_data.items[0].device_name (Fallback "Kasse 1"). * RLS filtert automatisch über get_auth_company_id(). */ export async function getPartnerCustomersWithOrders(): Promise { const supabase = await createClient() const { data, error } = await supabase .from('end_customers') .select('*, orders(*)') .order('company_name', { ascending: true }) if (error) throw error return (data || []).map((customer: any) => { const rawOrders = customer.orders || [] // Sort orders by created_at desc const sortedOrders = [...rawOrders].sort( (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() ) const orders: OrderWithRegisterName[] = sortedOrders.map((order: any) => { const items = order.order_data?.items || [] const deviceName = items[0]?.device_name || 'Kasse 1' return { ...order, register_name: deviceName, } }) return { ...customer, orders, } }) } /** * Bereinigt Kassen-Präfixe aus Produkt- und Modulnamen. * Entfernt Muster wie "Kasse1:", "kass2:", "kasse 1:" etc. */ function stripDevicePrefix(name: string): string { return name.replace(/^kasse?\s*\d*\s*:\s*/i, '').trim() } /** * Holt alle Endkunden mit geflatteteten Kassen (eine Kasse = ein FlattenedDevice). * Jede Order wird nach device_name in einzelne Kassen aufgespalten. * RLS filtert automatisch über get_auth_company_id(). */ export async function getPartnerCustomersWithDevices(): Promise { const supabase = await createClient() const { data, error } = await supabase .from('end_customers') .select('*, orders(*)') .order('company_name', { ascending: true }) if (error) throw error return (data || []).map((customer: any) => { const rawOrders = customer.orders || [] const sortedOrders = [...rawOrders].sort( (a: any, b: any) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() ) const devices: FlattenedDevice[] = [] for (const order of sortedOrders) { const allItems: OrderItem[] = order.order_data?.items || [] const totalOrderPrice: number = order.total_price || 0 // Gruppiere Items nach device_name const deviceGroups: Record = {} for (const item of allItems) { const key = item.device_name || 'Kasse 1' if (!deviceGroups[key]) deviceGroups[key] = [] deviceGroups[key].push(item) } const deviceKeys = Object.keys(deviceGroups) for (const deviceKey of deviceKeys) { const devItems = deviceGroups[deviceKey] // Bereinige Produkt- und Modulnamen const cleanedItems: OrderItem[] = devItems.map(item => ({ ...item, product_name: stripDevicePrefix(item.product_name), selected_modules: (item.selected_modules || []).map(mod => ({ ...mod, module_name: stripDevicePrefix(mod.module_name), })), })) // Anteiliger Preis dieser Kasse (gleichmäßige Aufteilung) const devicePrice = deviceKeys.length > 1 ? totalOrderPrice / deviceKeys.length : totalOrderPrice devices.push({ orderId: order.id, orderNumber: order.order_number, orderStatus: order.status, pdfUrl: order.pdf_url, createdAt: order.created_at, deviceId: deviceKey, deviceName: deviceKey, items: cleanedItems, totalPrice: Math.round(devicePrice * 100) / 100, }) } } const { orders: _orders, ...customerWithoutOrders } = customer return { ...customerWithoutOrders, devices, } as EndCustomerWithDevices }) } export interface GetCustomersForWizardParams { partnerCompanyId?: string page?: number pageSize?: number search?: string } export interface GetCustomersForWizardResult { customers: EndCustomer[] totalCount: number totalPages: number page: number pageSize: number isAdmin: boolean } /** * Server Action für den Neubestellung-Wizard: * Holt Endkunden rollenbasiert mit Partner-Filter (für Admins) und Pagination. */ export async function getCustomersForWizard( params: GetCustomersForWizardParams = {} ): Promise { const { partnerCompanyId, page = 1, pageSize = 10, search = '' } = params const supabase = await createClient() const { data: { user }, } = await supabase.auth.getUser() if (!user) { throw new Error('Nicht authentifiziert') } const { data: dbUser } = await supabase .from('users') .select('role, company_id') .eq('id', user.id) .single() const isAdmin = dbUser?.role === 'admin' let client = supabase if (isAdmin) { try { client = createAdminClient() as any } catch { client = supabase } } let query = client .from('end_customers') .select('*', { count: 'exact' }) .eq('is_anonymized', false) if (isAdmin) { if (partnerCompanyId && partnerCompanyId !== 'all' && partnerCompanyId !== '') { query = query.eq('partner_id', partnerCompanyId) } } else { if (dbUser?.company_id) { query = query.eq('partner_id', dbUser.company_id) } } if (search && search.trim() !== '') { const term = `%${search.trim()}%` query = query.or( `company_name.ilike.${term},first_name.ilike.${term},last_name.ilike.${term},city.ilike.${term},zip.ilike.${term}` ) } query = query.order('company_name', { ascending: true }) const validPageSize = [10, 20, 50].includes(pageSize) ? pageSize : 10 const validPage = Math.max(1, page) const from = (validPage - 1) * validPageSize const to = from + validPageSize - 1 query = query.range(from, to) const { data, error, count } = await query if (error) { throw error } const totalCount = count ?? 0 const totalPages = Math.ceil(totalCount / validPageSize) || 1 return { customers: (data as EndCustomer[]) || [], totalCount, totalPages, page: validPage, pageSize: validPageSize, isAdmin, } }