1import { NextRequest, NextResponse } from 'next/server';
2import { fieldpineServerApi } from '@/lib/server/fieldpineApi';
3import { getRequestContext, validateApiAccess } from '@/lib/server/sessionUtils';
6 * Sales/Invoices Endpoint (ELINK API)
7 * GET /api/v1/openapi/sales - List sales/invoices
8 * POST /api/v1/openapi/sales - Create new sale
10 * Uses ELINK API (retailmax.elink.sale.list)
11 * Security: Retail stores can access this (ELINK only)
13export async function GET(request: NextRequest) {
15 // 1. Get session and store context
16 const context = await getRequestContext(request);
18 if (!context || !context.isAuthenticated) {
19 return NextResponse.json(
20 { error: 'Authentication required' },
25 // 2. SECURITY: Validate ELINK access
26 const apiAccessValidation = validateApiAccess(context, 'elink');
27 if (!apiAccessValidation.valid) {
28 console.warn(`[API Security] ELINK access denied: ${apiAccessValidation.error}`);
29 return NextResponse.json(
31 error: apiAccessValidation.error,
32 code: apiAccessValidation.errorCode
38 // 3. Parse query parameters
39 const { searchParams } = new URL(request.url);
40 const limit = searchParams.get('$top') || searchParams.get('limit') || '100';
41 const skip = searchParams.get('$skip') || searchParams.get('skip') || '0';
42 const customerId = searchParams.get('customerId');
43 const phase = searchParams.get('phase');
45 console.log('[Sales API] Fetching sales with:', { limit, skip, customerId, phase });
47 // 4. Build ELINK BUCK parameters
48 // Field reference: f102=sale ID, f131/f132=completed date, f210=total, f300=phase, f301=customer ID
49 const buckParams: Record<string, string> = {
50 '3': 'retailmax.elink.sale.list',
51 '10': '102,131,132,140,141,210,211,212,213,214,300,301,302,303,304,305',
52 '13': 'completeddt', // Sort by completion date (descending)
55 '99': Math.random().toString()
58 // Add filters if provided
60 buckParams['301'] = customerId;
64 buckParams['300'] = phase;
67 // 5. Make ELINK API call using singleton
68 // Use store-specific URL for API calls
69 const result = await fieldpineServerApi.buckApiCall(buckParams, context.session.apiKey, context.store.url);
71 // 6. Transform BUCK response
72 let sales: any[] = [];
73 if (result && result.DATS && Array.isArray(result.DATS)) {
74 sales = result.DATS.map((sale: any) => ({
75 Sid: sale.f102_E || 0,
76 ExternalId: sale.f140_s || sale.f141_s || null,
77 CompletedDt: sale.f131_DT || sale.f132_DT || null,
78 Total: parseFloat(sale.f210_$) || 0,
79 Phase: parseInt(sale.f300_E) || 0,
80 CustomerId: sale.f301_s || null,
81 CustomerName: sale.f302_s || null,
82 LocationId: parseInt(sale.f303_E) || null,
83 LocationName: sale.f304_s || null,
84 CreatedDt: sale.f305_DT || null
88 return NextResponse.json({
92 '@odata.count': sales.length
97 } catch (error: any) {
98 console.error('[Sales API] Error:', error);
99 return NextResponse.json(
100 { error: error.message || 'Failed to fetch sales' },
107 * Create new sale/invoice
109export async function POST(request: NextRequest) {
111 // 1. Get session and store context
112 const context = await getRequestContext(request);
114 if (!context || !context.isAuthenticated) {
115 return NextResponse.json(
116 { error: 'Authentication required' },
121 // 2. SECURITY: Validate ELINK access
122 const apiAccessValidation = validateApiAccess(context, 'elink');
123 if (!apiAccessValidation.valid) {
124 return NextResponse.json(
126 error: apiAccessValidation.error,
127 code: apiAccessValidation.errorCode
133 const saleData = await request.json();
134 console.log('[Sales API] Creating sale:', saleData);
136 const result = await fieldpineServerApi.createSale(saleData, context.session.apiKey);
138 return NextResponse.json({
144 } catch (error: any) {
145 console.error('[Sales API] Create error:', error);
146 return NextResponse.json(
147 { error: error.message || 'Failed to create sale' },