1import { NextRequest, NextResponse } from 'next/server';
2import { FieldpineServerApi } from '@/lib/server/fieldpineApi';
3import { getRequestContext, validateApiAccess, getStoreApiUrl } from '@/lib/server/sessionUtils';
7 * ELINK: retailmax.elink.sale.totals
8 * Core sales reporting endpoint (46 uses)
10export async function GET(request: NextRequest) {
12 // Get session context
13 const context = await getRequestContext(request);
14 if (!context || !context.isAuthenticated) {
15 return NextResponse.json(
16 { error: 'Authentication required' },
21 // Validate ELINK access
22 if (!validateApiAccess(context, 'elink')) {
23 return NextResponse.json(
24 { error: 'ELINK access not permitted for this store type' },
29 const { searchParams } = new URL(request.url);
32 const startDate = searchParams.get('startDate');
33 const endDate = searchParams.get('endDate');
36 const groupBy = searchParams.get('groupBy'); // day, week, month, location, teller
38 // Location and teller filters
39 const locationId = searchParams.get('locationId');
40 const tellerId = searchParams.get('tellerId');
43 const sortBy = searchParams.get('sortBy') || 'date';
45 // Create store-specific API instance
46 const storeApiUrl = getStoreApiUrl(context, 'elink');
47 const api = new FieldpineServerApi(storeApiUrl, context.session.apiKey);
49 console.log('[Sales Totals] Calling ELINK: retailmax.elink.sale.totals');
51 // Build BUCK parameters
52 const buckParams: Record<string, string | string[]> = {
53 "3": "retailmax.elink.sale.totals",
54 "10": "102,131,132,140,141,210,211,212,213,214" // Field selection
58 const filters: string[] = [];
60 filters.push(`f102,4,${startDate}`); // f102 >= startDate
63 filters.push(`f102,1,${endDate}`); // f102 <= endDate
66 filters.push(`f131,0,${locationId}`); // f131 = locationId
69 filters.push(`f140,0,${tellerId}`); // f140 = tellerId
71 if (filters.length > 0) {
72 buckParams["9"] = filters;
79 buckParams["15"] = "102"; // Group by date
82 buckParams["15"] = "131"; // Group by location
85 buckParams["15"] = "140"; // Group by teller
88 buckParams["15"] = "133"; // Group by department
96 buckParams["13"] = "completeddt";
99 buckParams["13"] = "211"; // f211 = revenue
102 buckParams["13"] = "210"; // f210 = units
105 buckParams["13"] = "completeddt";
108 // Use store-specific URL for API calls
109 const result = await api.buckApiCall(buckParams, context.session.apiKey, context.store.url);
111 // Process ELINK response
112 if (result && result.DATS && Array.isArray(result.DATS)) {
113 const totals = result.DATS.map((item: any) => ({
115 locationId: item.f131,
116 locationName: item.f132,
118 tellerName: item.f141,
119 departmentId: item.f133,
120 departmentName: item.f134,
121 totalUnits: item.f210,
122 totalRevenue: item.f211,
123 totalCost: item.f212,
124 grossProfit: item.f213,
125 grossMargin: item.f214,
126 transactionCount: item.f220,
127 averageTransaction: item.f221,
128 taxCollected: item.f230,
129 discountGiven: item.f231
132 // Calculate overall summary
134 totalRevenue: totals.reduce((sum: number, t: any) => sum + (t.totalRevenue || 0), 0),
135 totalUnits: totals.reduce((sum: number, t: any) => sum + (t.totalUnits || 0), 0),
136 totalCost: totals.reduce((sum: number, t: any) => sum + (t.totalCost || 0), 0),
137 totalProfit: totals.reduce((sum: number, t: any) => sum + (t.grossProfit || 0), 0),
138 totalTransactions: totals.reduce((sum: number, t: any) => sum + (t.transactionCount || 0), 0),
140 averageTransaction: 0
143 if (summary.totalRevenue > 0) {
144 summary.averageMargin = (summary.totalProfit / summary.totalRevenue) * 100;
146 if (summary.totalTransactions > 0) {
147 summary.averageTransaction = summary.totalRevenue / summary.totalTransactions;
150 return NextResponse.json({
154 count: totals.length,
160 return NextResponse.json({
170 console.error('[Sales Totals] Error:', error);
171 return NextResponse.json(
172 { error: 'Failed to fetch sales totals' },