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 Stock Adjustment API
7 * POST /api/v1/products/stock-adjustment
8 * Adjust product stock levels with reason tracking
9 */
10export async function POST(request: NextRequest) {
11 try {
12 const authData = await getStoredAuth();
13 if (!authData || !authData.authenticated) {
14 return NextResponse.json(
15 { success: false, error: 'Authentication required' },
16 { status: 401 }
17 );
18 }
19
20 const body = await request.json();
21 const { productId, adjustment, reason, locationId = 1 } = body;
22
23 if (!productId) {
24 return NextResponse.json(
25 { success: false, error: 'Product ID is required' },
26 { status: 400 }
27 );
28 }
29
30 if (adjustment === undefined || adjustment === null) {
31 return NextResponse.json(
32 { success: false, error: 'Adjustment amount is required' },
33 { status: 400 }
34 );
35 }
36
37 const adjustmentNum = parseFloat(adjustment);
38 if (isNaN(adjustmentNum)) {
39 return NextResponse.json(
40 { success: false, error: 'Invalid adjustment amount' },
41 { status: 400 }
42 );
43 }
44
45 console.log('[Stock Adjustment] Request:', { productId, adjustment: adjustmentNum, reason, locationId });
46
47 // Escape XML content
48 const escapeXml = (v: string | number) =>
49 String(v).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
50
51 // Determine transaction type based on reason
52 let transactionType = 204; // Default: Manual Adjustment
53 let comments = reason || 'Manual adjustment';
54
55 switch (reason) {
56 case 'received':
57 transactionType = 100; // Received
58 comments = 'Stock Received';
59 break;
60 case 'sale':
61 transactionType = 210; // Sales
62 comments = 'Sale Adjustment';
63 break;
64 case 'damage':
65 case 'writeoff':
66 transactionType = 201; // Write Off
67 comments = 'Damage/Loss';
68 break;
69 case 'return':
70 transactionType = 200; // Supplier Return
71 comments = 'Stock Return';
72 break;
73 case 'adjustment':
74 default:
75 transactionType = 204; // Stocktake Adjustment
76 comments = 'Manual Adjustment';
77 }
78
79 // Build DATI XML for goods in history (stock movement)
80 const xml = [
81 '<DATI>',
82 '<f8_s>retailmax.elink.goodsin.edit</f8_s>',
83 '<f11_B>I</f11_B>',
84 `<f100_E>${escapeXml(productId)}</f100_E>`,
85 `<f110_E>${transactionType}</f110_E>`,
86 `<f111>${Math.abs(adjustmentNum)}</f111>`,
87 `<f120_E>${escapeXml(locationId)}</f120_E>`,
88 `<f140_s>${escapeXml(comments)}</f140_s>`,
89 '</DATI>'
90 ].join('');
91
92 console.log('[Stock Adjustment] DATI XML:', xml);
93
94 // Send DATI request
95 const url = `${process.env.FIELDPINE_BASE_URL || 'https://iig.cwanz.online'}/GNAP/j/buck`;
96 const headers: Record<string, string> = {
97 'Accept': 'application/json',
98 'Content-Type': 'text/xml',
99 'Cookie': `FieldpineApiKey=${authData.apiKey}`
100 };
101
102 const response = await fetch(url, {
103 method: 'POST',
104 headers,
105 body: xml,
106 });
107
108 if (!response.ok) {
109 const errorText = await response.text();
110 console.error('[Stock Adjustment] Error response:', errorText);
111 throw new Error(`Stock adjustment failed: ${response.status} ${response.statusText}`);
112 }
113
114 const result = await response.json();
115 console.log('[Stock Adjustment] Success:', result);
116
117 return NextResponse.json({
118 success: true,
119 data: result,
120 message: `Stock adjusted by ${adjustmentNum > 0 ? '+' : ''}${adjustmentNum}`,
121 source: 'dati'
122 });
123
124 } catch (error: any) {
125 console.error('[Stock Adjustment] Error:', error);
126 return NextResponse.json(
127 { success: false, error: error.message || 'Failed to adjust stock' },
128 { status: 500 }
129 );
130 }
131}