import type { VercelRequest, VercelResponse } from "@vercel/node"; import { authenticateRequest, requireAdmin, logComplianceEvent } from "../_auth"; export default async function handler(req: VercelRequest, res: VercelResponse) { const auth = await authenticateRequest(req); if (!requireAdmin(auth, res)) return; const { adminClient, user } = auth; if (req.method === 'GET') { const { status, start_date, end_date, tax_year } = req.query; let query = adminClient .from('nexus_payouts') .select(` *, nexus_talent_profiles!inner( user_id, legal_first_name, legal_last_name, tax_classification, residency_state ) `) .order('created_at', { ascending: false }); if (status) query = query.eq('status', status); if (tax_year) query = query.eq('tax_year', tax_year); if (start_date) query = query.gte('scheduled_date', start_date); if (end_date) query = query.lte('scheduled_date', end_date); const { data, error } = await query; if (error) { return res.status(500).json({ error: error.message }); } const totalPending = data?.filter(p => p.status === 'pending').reduce((sum, p) => sum + Number(p.net_amount), 0) || 0; const totalProcessed = data?.filter(p => p.status === 'completed').reduce((sum, p) => sum + Number(p.net_amount), 0) || 0; return res.status(200).json({ data, summary: { total_payouts: data?.length || 0, pending_amount: totalPending, processed_amount: totalProcessed } }); } if (req.method === 'POST' && req.query.action === 'process') { const { payout_ids } = req.body; if (!payout_ids || !Array.isArray(payout_ids)) { return res.status(400).json({ error: 'payout_ids array required' }); } const { data: payouts } = await adminClient .from('nexus_payouts') .select('*') .in('id', payout_ids) .eq('status', 'pending'); if (!payouts || payouts.length === 0) { return res.status(400).json({ error: 'No pending payouts found' }); } const { data, error } = await adminClient .from('nexus_payouts') .update({ status: 'processing', updated_at: new Date().toISOString() }) .in('id', payout_ids) .select(); if (error) { return res.status(500).json({ error: error.message }); } await logComplianceEvent(adminClient, { entity_type: 'payroll', entity_id: user!.id, event_type: 'payroll_batch_processing', event_category: 'financial', actor_id: user!.id, actor_role: 'admin', realm_context: 'corp', description: `Processing ${data?.length} payouts`, payload: { payout_ids, total_amount: data?.reduce((sum, p) => sum + Number(p.net_amount), 0) }, legal_entity: 'for_profit' }, req); return res.status(200).json({ data, processed_count: data?.length || 0 }); } if (req.method === 'GET' && req.query.action === 'summary') { const currentYear = new Date().getFullYear(); const { data: yearPayouts } = await adminClient .from('nexus_payouts') .select('net_amount, status, tax_year') .eq('tax_year', currentYear); const { data: azHours } = await adminClient .from('nexus_time_logs') .select('az_eligible_hours') .eq('submission_status', 'approved') .gte('log_date', `${currentYear}-01-01`); return res.status(200).json({ tax_year: currentYear, total_payouts: yearPayouts?.filter(p => p.status === 'completed').reduce((sum, p) => sum + Number(p.net_amount), 0) || 0, pending_payouts: yearPayouts?.filter(p => p.status === 'pending').reduce((sum, p) => sum + Number(p.net_amount), 0) || 0, total_az_hours: azHours?.reduce((sum, h) => sum + Number(h.az_eligible_hours), 0) || 0, payout_count: yearPayouts?.length || 0 }); } return res.status(405).json({ error: 'Method not allowed' }); }