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 { getStoredAuth } from '@/lib/server/auth';
4
5/**
6 * Product Performance Analytics
7 * BUCK: retailmax.elink.stats.product.performance
8 * Most heavily used query (432 uses!) - Critical for business intelligence
9 */
10export async function GET(request: NextRequest) {
11 try {
12 const authData = await getStoredAuth();
13 if (!authData || !authData.authenticated) {
14 return NextResponse.json(
15 { error: 'Authentication required' },
16 { status: 401 }
17 );
18 }
19
20 const { searchParams } = new URL(request.url);
21
22 // Customer/location filter
23 const customerId = searchParams.get('customerId');
24 const locationId = searchParams.get('locationId');
25
26 // Grouping: product (pid), supplier (spid), department (dept)
27 const groupBy = searchParams.get('groupBy') || 'pid';
28
29 // Limit results
30 const limit = searchParams.get('limit') || '30';
31
32 // Sort mode (100 parameter)
33 // 1 = by units sold (volume)
34 // 2 = by revenue
35 // 3 = by gross profit
36 const sortBy = searchParams.get('sortBy') || '2'; // Default to revenue
37
38 // Build BUCK parameters
39 const params: string[] = [
40 '3=retailmax.elink.stats.product.performance',
41 `8=${limit}`,
42 `100=${sortBy}`
43 ];
44
45 // Add grouping
46 switch (groupBy) {
47 case 'pid':
48 case 'product':
49 params.push('15=2,pid'); // Group by product
50 break;
51 case 'spid':
52 case 'supplier':
53 params.push('15=2,spid'); // Group by supplier
54 break;
55 case 'dept':
56 case 'department':
57 params.push('15=2,dept'); // Group by department
58 break;
59 }
60
61 // Add customer filter (f107 = customer)
62 if (customerId) {
63 params.push(`9=f107,0,${customerId}`);
64 }
65
66 // Add location filter (f131 = location)
67 if (locationId) {
68 params.push(`9=f131,0,${locationId}`);
69 }
70
71 const query = params.join('&');
72 const url = `/GNAP/j/buck?${query}`;
73
74 console.log('[Product Performance] BUCK query:', url);
75
76 const result = await fieldpineServerApi.apiCall(url, {
77 cookie: authData.apiKey,
78 useOpenApi: false
79 });
80
81 // Process BUCK response
82 if (result && result.DATS && Array.isArray(result.DATS)) {
83 const performance = result.DATS.map((item: any) => ({
84 productId: item.f200,
85 productName: item.f201,
86 barcode: item.f202,
87 supplierId: item.f220,
88 supplierName: item.f221,
89 departmentId: item.f240,
90 departmentName: item.f241,
91 unitsSold: item.f210,
92 revenue: item.f211,
93 cost: item.f212,
94 grossProfit: item.f213,
95 grossMargin: item.f214,
96 averagePrice: item.f215,
97 transactionCount: item.f216,
98 averageUnitsPerTransaction: item.f217,
99 lastSaleDate: item.f250,
100 customerId: item.f300,
101 customerName: item.f301
102 }));
103
104 // Calculate totals
105 const totals = {
106 totalUnits: performance.reduce((sum: number, p: any) => sum + (p.unitsSold || 0), 0),
107 totalRevenue: performance.reduce((sum: number, p: any) => sum + (p.revenue || 0), 0),
108 totalCost: performance.reduce((sum: number, p: any) => sum + (p.cost || 0), 0),
109 totalProfit: performance.reduce((sum: number, p: any) => sum + (p.grossProfit || 0), 0),
110 averageMargin: 0
111 };
112
113 if (totals.totalRevenue > 0) {
114 totals.averageMargin = (totals.totalProfit / totals.totalRevenue) * 100;
115 }
116
117 return NextResponse.json({
118 success: true,
119 data: performance,
120 count: performance.length,
121 totals,
122 groupBy,
123 sortBy,
124 source: 'buck'
125 });
126 }
127
128 return NextResponse.json({
129 success: true,
130 data: [],
131 count: 0,
132 groupBy,
133 sortBy,
134 source: 'buck'
135 });
136
137 } catch (error) {
138 console.error('[Product Performance] Error:', error);
139 return NextResponse.json(
140 { error: 'Failed to fetch product performance' },
141 { status: 500 }
142 );
143 }
144}