EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
route.ts
Go to the documentation of this file.
1import { NextRequest, NextResponse } from 'next/server';
2import { FieldpineServerApi } from '@/lib/server/fieldpineApi';
3import { getRequestContext, validateApiAccess, getStoreApiUrl } from '@/lib/server/sessionUtils';
4
5/**
6 * Sales Totals Report
7 * ELINK: retailmax.elink.sale.totals
8 * Core sales reporting endpoint (46 uses)
9 */
10export async function GET(request: NextRequest) {
11 try {
12 // Get session context
13 const context = await getRequestContext(request);
14 if (!context || !context.isAuthenticated) {
15 return NextResponse.json(
16 { error: 'Authentication required' },
17 { status: 401 }
18 );
19 }
20
21 // Validate ELINK access
22 if (!validateApiAccess(context, 'elink')) {
23 return NextResponse.json(
24 { error: 'ELINK access not permitted for this store type' },
25 { status: 403 }
26 );
27 }
28
29 const { searchParams } = new URL(request.url);
30
31 // Date filters
32 const startDate = searchParams.get('startDate');
33 const endDate = searchParams.get('endDate');
34
35 // Grouping options
36 const groupBy = searchParams.get('groupBy'); // day, week, month, location, teller
37
38 // Location and teller filters
39 const locationId = searchParams.get('locationId');
40 const tellerId = searchParams.get('tellerId');
41
42 // Sort field
43 const sortBy = searchParams.get('sortBy') || 'date';
44
45 // Create store-specific API instance
46 const storeApiUrl = getStoreApiUrl(context, 'elink');
47 const api = new FieldpineServerApi(storeApiUrl, context.session.apiKey);
48
49 console.log('[Sales Totals] Calling ELINK: retailmax.elink.sale.totals');
50
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
55 };
56
57 // Add date filters
58 const filters: string[] = [];
59 if (startDate) {
60 filters.push(`f102,4,${startDate}`); // f102 >= startDate
61 }
62 if (endDate) {
63 filters.push(`f102,1,${endDate}`); // f102 <= endDate
64 }
65 if (locationId) {
66 filters.push(`f131,0,${locationId}`); // f131 = locationId
67 }
68 if (tellerId) {
69 filters.push(`f140,0,${tellerId}`); // f140 = tellerId
70 }
71 if (filters.length > 0) {
72 buckParams["9"] = filters;
73 }
74
75 // Add grouping
76 if (groupBy) {
77 switch (groupBy) {
78 case 'day':
79 buckParams["15"] = "102"; // Group by date
80 break;
81 case 'location':
82 buckParams["15"] = "131"; // Group by location
83 break;
84 case 'teller':
85 buckParams["15"] = "140"; // Group by teller
86 break;
87 case 'department':
88 buckParams["15"] = "133"; // Group by department
89 break;
90 }
91 }
92
93 // Add sort
94 switch (sortBy) {
95 case 'date':
96 buckParams["13"] = "completeddt";
97 break;
98 case 'revenue':
99 buckParams["13"] = "211"; // f211 = revenue
100 break;
101 case 'units':
102 buckParams["13"] = "210"; // f210 = units
103 break;
104 default:
105 buckParams["13"] = "completeddt";
106 }
107
108 // Use store-specific URL for API calls
109 const result = await api.buckApiCall(buckParams, context.session.apiKey, context.store.url);
110
111 // Process ELINK response
112 if (result && result.DATS && Array.isArray(result.DATS)) {
113 const totals = result.DATS.map((item: any) => ({
114 date: item.f102,
115 locationId: item.f131,
116 locationName: item.f132,
117 tellerId: item.f140,
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
130 }));
131
132 // Calculate overall summary
133 const 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),
139 averageMargin: 0,
140 averageTransaction: 0
141 };
142
143 if (summary.totalRevenue > 0) {
144 summary.averageMargin = (summary.totalProfit / summary.totalRevenue) * 100;
145 }
146 if (summary.totalTransactions > 0) {
147 summary.averageTransaction = summary.totalRevenue / summary.totalTransactions;
148 }
149
150 return NextResponse.json({
151 success: true,
152 data: totals,
153 summary,
154 count: totals.length,
155 groupBy,
156 source: 'elink'
157 });
158 }
159
160 return NextResponse.json({
161 success: true,
162 data: [],
163 summary: {},
164 count: 0,
165 groupBy,
166 source: 'elink'
167 });
168
169 } catch (error) {
170 console.error('[Sales Totals] Error:', error);
171 return NextResponse.json(
172 { error: 'Failed to fetch sales totals' },
173 { status: 500 }
174 );
175 }
176}