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 } from '@/lib/server/sessionUtils';
4
5/**
6 * Helper function to escape XML special characters
7 */
8function escapeXml(unsafe: string): string {
9 if (typeof unsafe !== 'string') return String(unsafe || '');
10 return unsafe
11 .replace(/&/g, '&')
12 .replace(/</g, '&lt;')
13 .replace(/>/g, '&gt;')
14 .replace(/"/g, '&quot;')
15 .replace(/'/g, '&apos;');
16}
17
18/**
19 * OpenAPI Products Endpoint
20 * GET /api/v1/openapi/products/[id] - Get single product
21 */
22export async function GET(
23 request: NextRequest,
24 { params }: { params: Promise<{ id: string }> }
25) {
26 console.log('[Product API] ===== HANDLER STARTING v2 =====');
27 try {
28 // Await params in Next.js 15
29 const { id } = await params;
30
31 console.log('[Product API] Requested ID:', id);
32
33 // 1. Get and validate session
34 console.log('[Product API] Getting request context...');
35 const context = await getRequestContext(request);
36 console.log('[Product API] Got context, validating API access...');
37 const validation = validateApiAccess(context, 'elink');
38 console.log('[Product API] Validation result:', validation);
39
40 // Handle both { valid: true } and true responses
41 const isValid = typeof validation === 'boolean' ? validation : validation.valid;
42 if (!isValid || !context) {
43 console.log('[Product API] Validation failed, returning error response');
44 const errorMsg = typeof validation === 'object' ? validation.error : 'Access denied';
45 const errorCode = typeof validation === 'object' ? validation.errorCode : undefined;
46 return NextResponse.json(
47 { error: errorMsg || 'Access denied', errorCode },
48 { status: 403 }
49 );
50 }
51 console.log('[Product API] Validation passed, proceeding...');
52
53 // Use GNAP/BUCK API to get full product details
54 // OpenAPI doesn't return barcode and many other fields, so we use the legacy eLink API
55 // Field mapping reference from Fieldpine productedit.htm:
56 // 100-262: Core product fields (Pid, Description, Price, Cost, PLU, Depid, Spid, etc.)
57 // 300-499: Extended product fields (pricing, warehouse, etc.)
58 // 350-352, 371-374: Department hierarchy (Depid2-8)
59 // 512-514, 526: Barcodes and related
60 // 4000-4399: Industry-specific fields (optical/eyewear, food attributes, etc.)
61 // 8390617,8390618: Web descriptions
62 // 8391608-8391618: Website publishing controls
63 // 8390713-8390730: Website indicators and flags
64 // 8391117,8392117: SEO fields (blurb, keywords)
65 // 8392608,8392609,8392708,8392772,8394608: Marketing and display controls
66 // 8388814,8388817,8388818,8388908,8389009: Attributes (weight, size, color, brand)
67 // 8393608,8393609,8393610: Additional product attributes
68 // 8389208,8389308: Product flags and controls
69 // 8408610: Shopify/ecommerce integration
70 // 8480001-8480010: Website feature flags
71
72 // Comprehensive field list matching Fieldpine's productedit.htm
73 const fieldList = '100-262,300-499,350-352,371-374,512-514,526,4000-4399,8390617,8390618,8391608-8391618,8390713-8390730,8391117,8392117,8392608,8392609,8392708,8392772,8394608,8388814,8388817,8388818,8388908,8389009,8393608,8393609,8393610,8389208,8389308,8408610,8480001-8480010';
74
75 console.log('[Product API] Fetching product via BUCK API, ID:', id);
76
77 const buckParams: Record<string, string | string[]> = {
78 '3': 'retailmax.elink.products',
79 '100': '1', // Detail level
80 '7': fieldList,
81 '9': `f100,0,${id};f503,0,a6`, // Filter by Pid AND include all product statuses (a6)
82 '99': Math.random().toString() // Cache buster
83 };
84
85 // Use store-specific URL for API calls
86 const response = await fieldpineServerApi.buckApiCall(
87 buckParams,
88 context.session.apiKey,
89 context.store.url
90 );
91
92 console.log('[Product API] BUCK response:', { hasResponse: !!response, responseType: typeof response });
93
94 // Extract products array from BUCK API response
95 let productsArray: any[] = [];
96 if (response?.DATS && Array.isArray(response.DATS)) {
97 productsArray = response.DATS;
98 } else if (response?.data?.DATS && Array.isArray(response.data.DATS)) {
99 productsArray = response.data.DATS;
100 }
101
102 console.log('[Product API] BUCK API returned products:', productsArray?.length || 0);
103
104 // BUCK API returns a single product since we filtered by Pid
105 const product = productsArray?.[0];
106
107 console.log('[Product API] Found product via BUCK:', product ? `f100: ${product?.f100}, f101: ${product?.f101}` : 'Not found');
108
109 if (product) {
110 console.log('[Product API] Product fields from BUCK API:', Object.keys(product || {}).sort());
111 console.log('[Product API] Barcode field (f513):', product?.f513);
112 }
113
114 console.log('[Product API] Extracted product:', product ? 'Found' : 'Not found');
115
116 if (!product) {
117 return NextResponse.json(
118 { error: 'Product not found' },
119 { status: 404 }
120 );
121 }
122
123 // Map BUCK API field codes to standard names
124 // Reference: https://www.fieldpine.com/docs/fieldpine-field-reference.html
125
126 // Debug: Log department field values
127 console.log('[Product API] Department fields for product:', {
128 f106: product?.f106,
129 f350: product?.f350,
130 f351: product?.f351,
131 f352: product?.f352,
132 f371: product?.f371,
133 f372: product?.f372,
134 f373: product?.f373,
135 f374: product?.f374
136 });
137
138 const mappedProduct = {
139 Pid: product?.f100,
140 Description: product?.f101 || product?.f102, // f101=Description, f102=Name
141 Name: product?.f102 || product?.f101,
142 Price: product?.f103,
143 SellPrice: product?.f103,
144 Cost: product?.f108,
145 CostPrice: product?.f108,
146 Plu: product?.f105,
147 PLU: product?.f105,
148 Depid: product?.f106,
149 Department: product?.f106,
150 Depid2: product?.f350,
151 Depid3: product?.f351,
152 Depid4: product?.f352,
153 Depid5: product?.f371,
154 Depid6: product?.f372,
155 Depid7: product?.f373,
156 Depid8: product?.f374,
157 Spid: product?.f104,
158 LastSupplierSpid: product?.f104,
159 Barcode: product?.f513, // f513 contains barcodes (can be array or string)
160 Barcodes: product?.f513,
161 WebGroup: product?.f182,
162 Comments: product?.f256,
163 Brand: product?.f8389009,
164 ShortName: product?.f102,
165 WeightGm: product?.f8388814,
166 Size: product?.f8388818,
167 MainColour: product?.f8388908,
168 Hidden: product?.f155,
169 NoSell: product?.f151,
170 TaxFlag: product?.f112,
171 SellFlags: product?.f150,
172 StockLevel: product?.f107,
173 StockMin: product?.f8390726,
174 StockMax: product?.f8390727,
175 ReorderLevel: product?.f320,
176 ReorderMax: product?.f321,
177 SupplierName: product?.f1041, // May not be in response
178 SupplierPartCode: product?.f1042, // May not be in response
179 Measurement: product?.f111,
180 DepartmentName: product?.f1061, // May not be in response
181 // Pricing extras
182 Freight: product?.f147,
183 Packaging: product?.f149,
184 DisableDiscounts: product?.f170,
185 DecimalPlaces: product?.f346,
186 // Warehouse
187 PartCount: product?.f211,
188 WarehouseLabelName: product?.f212,
189 // Dates
190 ReleaseDate: product?.f363,
191 AvailableDate: product?.f364,
192 // Shelf Labels
193 ShelfLabelFormat: product?.f349,
194 ShelfLabelLine1: product?.f228,
195 ShelfLabelLine2: product?.f229,
196 ShelfLabelLine3: product?.f230,
197 // Advanced Pricing
198 ConfirmPrice: product?.f323,
199 FranchisePrice: product?.f325,
200 PriceBand1: product?.f148,
201 PriceBand2: product?.f302,
202 PriceBand3: product?.f303,
203 // Discount Limits
204 LowestPrice: product?.f8389618,
205 DiscountLimit1: product?.f8389619,
206 DiscountLimit2: product?.f8389620,
207 DiscountLimit3: product?.f8389621,
208 DiscountLimit4: product?.f8389622,
209 DiscountLimit5: product?.f8389623,
210 // Stock Control
211 SupplySource: product?.f196,
212 IgnoreStockMovements: product?.f180,
213 RecordStockMovements: product?.f258,
214 Placement: product?.f238,
215 // Optical/Eyewear
216 AlternateFit: product?.f4002 === 'Y' || product?.f4002 === true,
217 LensSize: product?.f4003 || '',
218 BridgeSize: product?.f4004 || '',
219 TempleLength: product?.f4005 || '',
220 LensBaseCurve: product?.f4006 || '',
221 LensMaterial: product?.f4007 || 0,
222 FrameMaterial: product?.f4008 || 0,
223 // Website/Marketing
224 PublishToWebsite: product?.f198 === 'Y' || product?.f198 === true,
225 WebsiteHandling: product?.f315 || 0,
226 WebDescription: product?.f8390617 || '',
227 WebKeywords: product?.f8392117 || '',
228 WebBlurb: product?.f8391117 || '',
229 SearchRank: product?.f316 || 0,
230 DisplayRank: product?.f319 || 0,
231 WebsiteControl: product?.f318 || '',
232 OnSpecial: product?.f8390716 === 'Y' || product?.f8390716 === true,
233 OutOfStock: product?.f8390717 === 'Y' || product?.f8390717 === true,
234 OutOfSeason: product?.f8392609 === 'Y' || product?.f8392609 === true,
235 InstoreOnly: product?.f8390718 === 'Y' || product?.f8390718 === true,
236 NewProduct: product?.f8390720 === 'Y' || product?.f8390720 === true,
237 FreeLocalDelivery: product?.f8390719 === 'Y' || product?.f8390719 === true,
238 HighlightProduct: product?.f8390721 === 'Y' || product?.f8390721 === true,
239 FeaturedProduct: product?.f8480001 === 'Y' || product?.f8480001 === true,
240 PromoteProduct: product?.f8480002 === 'Y' || product?.f8480002 === true,
241 MarketingColor1: product?.f234 || '',
242 MarketingColor2: product?.f235 || '',
243 MarketingColor3: product?.f236 || '',
244 MarketingColor4: product?.f237 || '',
245 InstallationUrl: product?.f240 || '',
246 WarrantyUrl: product?.f241 || '',
247 UpsellUrl: product?.f242 || '',
248 MarketingUrl: product?.f243 || '',
249 // Measurements
250 MeasurementId: product?.f111 || 1,
251 StockMeasurementId: product?.f114 || 1,
252 DisplayMeasurementId: product?.f115 || 1,
253 PackagingWeight: product?.f213 || 0,
254 LengthMm: product?.f353 || 0,
255 WidthMm: product?.f354 || 0,
256 HeightMm: product?.f355 || 0,
257 ShippingCubic: product?.f356 || 0,
258 // Supply/Ordering
259 ManufacturerPartCode: product?.f314 || '',
260 OrderCategory: product?.f328 || 0,
261 MessageWhenOrdering: product?.f221 || '',
262 DisableOrdering: product?.f222 === 'Y' || product?.f222 === true,
263 MinUnitsPerOrder: product?.f223 || 0,
264 MaxUnitsPerOrder: product?.f224 || 0,
265 PriorityColorCode: product?.f220 || 0,
266 ImportanceLevel: product?.f178 || 0,
267 // Debug/System (read-only)
268 Physkey: product?.f210 || 0,
269 GlobalKey: product?.f329 || 0,
270 Rve: product?.f249 || 0,
271 CreateDateTime: product?.f219 || '',
272 ProcessFlags: product?.f253 || 0,
273 ShopifyId: product?.f4208 || 0,
274 BigcommerceId: product?.f4209 || 0,
275 RmSystem: product?.f251 || 0,
276 ExtractFlags: product?.f342 || 0,
277 // Additional fields discovered from HAR file
278 // Barcodes
279 Barcode513: product?.f513 || '',
280 Barcode512: product?.f512 || '',
281 Barcode514: product?.f514 || '',
282 // Food Attributes (8391608-8391618)
283 IsGlutenFree: product?.f8391608 === 'Y' || product?.f8391608 === true || product?.f8391608 === '1',
284 IsOrganic: product?.f8391609 === 'Y' || product?.f8391609 === true || product?.f8391609 === '1',
285 IsDairyFree: product?.f8391610 === 'Y' || product?.f8391610 === true || product?.f8391610 === '1',
286 IsLowSugar: product?.f8391611 === 'Y' || product?.f8391611 === true || product?.f8391611 === '1',
287 IsVegan: product?.f8391612 === 'Y' || product?.f8391612 === true || product?.f8391612 === '1',
288 IsLowSalt: product?.f8391613 === 'Y' || product?.f8391613 === true || product?.f8391613 === '1',
289 IsYeastFree: product?.f8391614 === 'Y' || product?.f8391614 === true || product?.f8391614 === '1',
290 IsLowFat: product?.f8391615 === 'Y' || product?.f8391615 === true || product?.f8391615 === '1',
291 IsRaw: product?.f8391616 === 'Y' || product?.f8391616 === true || product?.f8391616 === '1',
292 IsKosher: product?.f8391617 === 'Y' || product?.f8391617 === true || product?.f8391617 === '1',
293 IsEdible: product?.f8391618 === 'Y' || product?.f8391618 === true || product?.f8391618 === '1',
294 // Additional product attributes
295 LensType: product?.f8393608 || '',
296 MadeFrom: product?.f8389208 || '',
297 DesignShape: product?.f8389308 || '',
298 Polarised: product?.f232 === 'Y' || product?.f232 === true,
299 Mirrored: product?.f233 === 'Y' || product?.f233 === true,
300 Perishable: product?.f231 === 'Y' || product?.f231 === true,
301 BlueLight: product?.f362 === 'Y' || product?.f362 === true,
302 // Supply chain
303 FairTrade: product?.f8392608 === 'Y' || product?.f8392608 === true,
304 CountryMade: product?.f8392708 || '',
305 NZMade: product?.f8392772 === 'Y' || product?.f8392772 === true,
306 // eCommerce additional flags
307 Gender: product?.f8408610 || '',
308 ASNZSCompliance: product?.f8394608 || '',
309 // Additional website flags (8480003-8480010)
310 WebsiteBackgroundItem: product?.f8480003 === 'Y' || product?.f8480003 === true,
311 WebsitePositionTop: product?.f8480004 === 'Y' || product?.f8480004 === true,
312 WebsitePositionLeft: product?.f8480005 === 'Y' || product?.f8480005 === true,
313 WebsitePositionBottom: product?.f8480006 === 'Y' || product?.f8480006 === true,
314 WebsitePositionRight: product?.f8480007 === 'Y' || product?.f8480007 === true,
315 WebsiteTag: product?.f8481000 || '',
316 // Extended product properties
317 Model: product?.f348 || '',
318 ItemColor: product?.f357 || '',
319 Cartridge: product?.f358 || '',
320 Refillable: product?.f360 === 'Y' || product?.f360 === true,
321 InternalSupportUrl: product?.f361 || '',
322 RxAble: product?.f380 || 0,
323 ProductCase: product?.f381 || '',
324 Warranty: product?.f382 || '',
325 ProductManufacturer: product?.f383 || '',
326 PaperSize: product?.f394 || '',
327 PrinterConnectivity: product?.f395 || '',
328 PrintingTechnology: product?.f396 || '',
329 PrinterAdfCapacity: product?.f397 || '',
330 PrintSpeed: product?.f398 || '',
331 ScanResolution: product?.f399 || '',
332 PrinterPaperCapacity: product?.f400 || '',
333 NonTangibleItem: product?.f8393609 === 'Y' || product?.f8393609 === true,
334 OneOffItem: product?.f8393610 === 'Y' || product?.f8393610 === true,
335 // Prompting fields
336 PromptRegno: product?.f8394609 === 'Y' || product?.f8394609 === true,
337 PromptLTNZNo: product?.f8394610 === 'Y' || product?.f8394610 === true,
338 PromptDocument: product?.f8394611 === 'Y' || product?.f8394611 === true,
339 PromptCustomer: product?.f8394612 === 'Y' || product?.f8394612 === true
340 };
341
342 return NextResponse.json({
343 success: true,
344 data: mappedProduct,
345 source: 'buck-api'
346 });
347
348 } catch (error) {
349 console.error('Product API error:', error);
350 return NextResponse.json(
351 { error: 'Failed to fetch product' },
352 { status: 500 }
353 );
354 }
355}
356
357/**
358 * PUT /api/v1/openapi/products/[id] - Update product
359 */
360export async function PUT(
361 request: NextRequest,
362 { params }: { params: Promise<{ id: string }> }
363) {
364 console.log('[Product API PUT] ===== HANDLER STARTING =====');
365 try {
366 const { id } = await params;
367 const body = await request.json();
368
369 console.log('[Product API PUT] Updating product:', id, body);
370
371 // Get and validate session
372 console.log('[Product API PUT] Getting request context...');
373 const context = await getRequestContext(request);
374 console.log('[Product API PUT] Got context, validating API access...');
375 const validation = validateApiAccess(context, 'elink');
376 console.log('[Product API PUT] Validation result:', validation);
377
378 // Handle both { valid: true } and true responses
379 const isValid = typeof validation === 'boolean' ? validation : validation.valid;
380 if (!isValid || !context) {
381 console.log('[Product API PUT] Validation failed, returning error response');
382 const errorMsg = typeof validation === 'object' ? validation.error : 'Access denied';
383 const errorCode = typeof validation === 'object' ? validation.errorCode : undefined;
384 return NextResponse.json(
385 { success: false, error: errorMsg || 'Access denied', errorCode },
386 { status: 403 }
387 );
388 }
389 console.log('[Product API PUT] Validation passed, proceeding...');
390
391 // Map frontend fields to Fieldpine API format
392 const updateData: any = {};
393
394 // Basic fields
395 if (body.name !== undefined) updateData.Description = body.name;
396 if (body.shortName !== undefined) updateData.ShortName = body.shortName;
397 if (body.plu !== undefined) updateData.Plu = body.plu;
398 if (body.barcode !== undefined) updateData.Barcode = body.barcode;
399
400 // Pricing
401 if (body.sellPrice !== undefined) updateData.Price = parseFloat(body.sellPrice);
402 if (body.costPrice !== undefined) updateData.Cost = parseFloat(body.costPrice);
403
404 // Departments (all 8 levels)
405 if (body.department !== undefined) updateData.Depid = parseInt(body.department) || null;
406 if (body.department2 !== undefined) updateData.Depid2 = body.department2 ? parseInt(body.department2) : null;
407 if (body.department3 !== undefined) updateData.Depid3 = body.department3 ? parseInt(body.department3) : null;
408 if (body.department4 !== undefined) updateData.Depid4 = body.department4 ? parseInt(body.department4) : null;
409 if (body.department5 !== undefined) updateData.Depid5 = body.department5 ? parseInt(body.department5) : null;
410 if (body.department6 !== undefined) updateData.Depid6 = body.department6 ? parseInt(body.department6) : null;
411 if (body.department7 !== undefined) updateData.Depid7 = body.department7 ? parseInt(body.department7) : null;
412 if (body.department8 !== undefined) updateData.Depid8 = body.department8 ? parseInt(body.department8) : null;
413
414 // Pricing extras
415 if (body.freight !== undefined) updateData.Freight = body.freight ? parseFloat(body.freight) : null;
416 if (body.packaging !== undefined) updateData.Packaging = body.packaging ? parseFloat(body.packaging) : null;
417 if (body.disableDiscounts !== undefined) updateData.DisableDiscounts = body.disableDiscounts ? 'Y' : 'N';
418
419 // Warehouse
420 if (body.partCount !== undefined) updateData.PartCount = body.partCount ? parseInt(body.partCount) : null;
421 if (body.warehouseLabelName !== undefined) updateData.WarehouseLabelName = body.warehouseLabelName;
422
423 // Dates
424 if (body.releaseDate !== undefined) updateData.ReleaseDate = body.releaseDate;
425 if (body.availableDate !== undefined) updateData.AvailableDate = body.availableDate;
426
427 // Shelf Labels
428 if (body.shelfLabelFormat !== undefined) updateData.ShelfLabelFormat = body.shelfLabelFormat;
429 if (body.shelfLabelLine1 !== undefined) updateData.ShelfLabelLine1 = body.shelfLabelLine1;
430 if (body.shelfLabelLine2 !== undefined) updateData.ShelfLabelLine2 = body.shelfLabelLine2;
431 if (body.shelfLabelLine3 !== undefined) updateData.ShelfLabelLine3 = body.shelfLabelLine3;
432
433 // Supplier information
434 if (body.supplierId !== undefined) updateData.LastSupplierSpid = parseInt(body.supplierId) || null;
435 if (body.supplierName !== undefined) updateData.SupplierName = body.supplierName;
436 if (body.supplierPartCode !== undefined) updateData.SupplierPartCode = body.supplierPartCode;
437
438 // Stock levels
439 if (body.stockMin !== undefined) updateData.ReorderLevel = parseFloat(body.stockMin);
440 if (body.stockMax !== undefined) updateData.ReorderMax = parseFloat(body.stockMax);
441
442 // Flags
443 if (body.taxFlag !== undefined) updateData.TaxFlag = body.taxFlag ? 1 : 0;
444 if (body.noSell !== undefined) updateData.NoSell = body.noSell ? 'Y' : 'N';
445 if (body.hidden !== undefined) updateData.Hidden = body.hidden ? 'Y' : 'N';
446
447 // Product attributes
448 if (body.brand !== undefined) updateData.Brand = body.brand;
449 if (body.weightGm !== undefined) updateData.WeightGm = body.weightGm ? parseFloat(body.weightGm) : null;
450 if (body.size !== undefined) updateData.Size = body.size;
451 if (body.webGroup !== undefined) updateData.WebGroup = body.webGroup;
452 if (body.comments !== undefined) updateData.Comments = body.comments;
453
454 // Advanced Pricing
455 if (body.confirmPrice !== undefined) updateData.ConfirmPrice = body.confirmPrice ? 'Y' : 'N';
456 if (body.franchisePrice !== undefined) updateData.FranchisePrice = body.franchisePrice ? parseFloat(body.franchisePrice) : null;
457 if (body.priceBand1 !== undefined) updateData.PriceBand1 = body.priceBand1 ? parseFloat(body.priceBand1) : null;
458 if (body.priceBand2 !== undefined) updateData.PriceBand2 = body.priceBand2 ? parseFloat(body.priceBand2) : null;
459 if (body.priceBand3 !== undefined) updateData.PriceBand3 = body.priceBand3 ? parseFloat(body.priceBand3) : null;
460
461 // Discount Limits
462 if (body.lowestPrice !== undefined) updateData.LowestPrice = body.lowestPrice ? parseFloat(body.lowestPrice) : null;
463 if (body.discountLimit1 !== undefined) updateData.DiscountLimit1 = body.discountLimit1 ? parseFloat(body.discountLimit1) : null;
464 if (body.discountLimit2 !== undefined) updateData.DiscountLimit2 = body.discountLimit2 ? parseFloat(body.discountLimit2) : null;
465 if (body.discountLimit3 !== undefined) updateData.DiscountLimit3 = body.discountLimit3 ? parseFloat(body.discountLimit3) : null;
466 if (body.discountLimit4 !== undefined) updateData.DiscountLimit4 = body.discountLimit4 ? parseFloat(body.discountLimit4) : null;
467 if (body.discountLimit5 !== undefined) updateData.DiscountLimit5 = body.discountLimit5 ? parseFloat(body.discountLimit5) : null;
468
469 // Stock Control
470 if (body.supplySource !== undefined) updateData.SupplySource = parseInt(body.supplySource) || 0;
471 if (body.ignoreStockMovements !== undefined) updateData.IgnoreStockMovements = body.ignoreStockMovements ? 'Y' : 'N';
472 if (body.recordStockMovements !== undefined) updateData.RecordStockMovements = body.recordStockMovements ? 'Y' : 'N';
473 if (body.placement !== undefined) updateData.Placement = parseInt(body.placement) || 0;
474
475 // Optical/Eyewear fields
476 if (body.alternateFit !== undefined) updateData.AlternateFit = body.alternateFit ? 'Y' : 'N';
477 if (body.lensSize !== undefined) updateData.LensSize = body.lensSize;
478 if (body.bridgeSize !== undefined) updateData.BridgeSize = body.bridgeSize;
479 if (body.templeLength !== undefined) updateData.TempleLength = body.templeLength;
480 if (body.lensBaseCurve !== undefined) updateData.LensBaseCurve = body.lensBaseCurve;
481 if (body.lensMaterial !== undefined) updateData.LensMaterial = parseInt(body.lensMaterial) || 0;
482 if (body.frameMaterial !== undefined) updateData.FrameMaterial = parseInt(body.frameMaterial) || 0;
483
484 // Website/Marketing fields
485 if (body.publishToWebsite !== undefined) updateData.PublishToWebsite = body.publishToWebsite ? 'Y' : 'N';
486 if (body.websiteHandling !== undefined) updateData.WebsiteHandling = parseInt(body.websiteHandling) || 0;
487 if (body.webDescription !== undefined) updateData.WebDescription = body.webDescription;
488 if (body.webKeywords !== undefined) updateData.WebKeywords = body.webKeywords;
489 if (body.webBlurb !== undefined) updateData.WebBlurb = body.webBlurb;
490 if (body.searchRank !== undefined) updateData.SearchRank = parseInt(body.searchRank) || 0;
491 if (body.displayRank !== undefined) updateData.DisplayRank = parseInt(body.displayRank) || 0;
492 if (body.websiteControl !== undefined) updateData.WebsiteControl = body.websiteControl;
493 if (body.onSpecial !== undefined) updateData.OnSpecial = body.onSpecial ? 'Y' : 'N';
494 if (body.outOfStock !== undefined) updateData.OutOfStock = body.outOfStock ? 'Y' : 'N';
495 if (body.outOfSeason !== undefined) updateData.OutOfSeason = body.outOfSeason ? 'Y' : 'N';
496 if (body.instoreOnly !== undefined) updateData.InstoreOnly = body.instoreOnly ? 'Y' : 'N';
497 if (body.newProduct !== undefined) updateData.NewProduct = body.newProduct ? 'Y' : 'N';
498 if (body.freeLocalDelivery !== undefined) updateData.FreeLocalDelivery = body.freeLocalDelivery ? 'Y' : 'N';
499 if (body.highlightProduct !== undefined) updateData.HighlightProduct = body.highlightProduct ? 'Y' : 'N';
500 if (body.featuredProduct !== undefined) updateData.FeaturedProduct = body.featuredProduct ? 'Y' : 'N';
501 if (body.promoteProduct !== undefined) updateData.PromoteProduct = body.promoteProduct ? 'Y' : 'N';
502 if (body.marketingColor1 !== undefined) updateData.MarketingColor1 = body.marketingColor1;
503 if (body.marketingColor2 !== undefined) updateData.MarketingColor2 = body.marketingColor2;
504 if (body.marketingColor3 !== undefined) updateData.MarketingColor3 = body.marketingColor3;
505 if (body.marketingColor4 !== undefined) updateData.MarketingColor4 = body.marketingColor4;
506 if (body.installationUrl !== undefined) updateData.InstallationUrl = body.installationUrl;
507 if (body.warrantyUrl !== undefined) updateData.WarrantyUrl = body.warrantyUrl;
508 if (body.upsellUrl !== undefined) updateData.UpsellUrl = body.upsellUrl;
509 if (body.marketingUrl !== undefined) updateData.MarketingUrl = body.marketingUrl;
510
511 // Measurement fields
512 if (body.measurementId !== undefined) updateData.MeasurementId = parseInt(body.measurementId) || 1;
513 if (body.stockMeasurementId !== undefined) updateData.StockMeasurementId = parseInt(body.stockMeasurementId) || 1;
514 if (body.displayMeasurementId !== undefined) updateData.DisplayMeasurementId = parseInt(body.displayMeasurementId) || 1;
515 if (body.packagingWeight !== undefined) updateData.PackagingWeight = parseFloat(body.packagingWeight) || 0;
516 if (body.lengthMm !== undefined) updateData.LengthMm = parseFloat(body.lengthMm) || 0;
517 if (body.widthMm !== undefined) updateData.WidthMm = parseFloat(body.widthMm) || 0;
518 if (body.heightMm !== undefined) updateData.HeightMm = parseFloat(body.heightMm) || 0;
519 if (body.shippingCubic !== undefined) updateData.ShippingCubic = parseFloat(body.shippingCubic) || 0;
520
521 // Supply/Ordering fields
522 if (body.manufacturerPartCode !== undefined) updateData.ManufacturerPartCode = body.manufacturerPartCode;
523 if (body.orderCategory !== undefined) updateData.OrderCategory = parseInt(body.orderCategory) || 0;
524 if (body.messageWhenOrdering !== undefined) updateData.MessageWhenOrdering = body.messageWhenOrdering;
525 if (body.disableOrdering !== undefined) updateData.DisableOrdering = body.disableOrdering ? 'Y' : 'N';
526 if (body.minUnitsPerOrder !== undefined) updateData.MinUnitsPerOrder = parseInt(body.minUnitsPerOrder) || 0;
527 if (body.maxUnitsPerOrder !== undefined) updateData.MaxUnitsPerOrder = parseInt(body.maxUnitsPerOrder) || 0;
528 if (body.priorityColorCode !== undefined) updateData.PriorityColorCode = parseInt(body.priorityColorCode) || 0;
529 if (body.importanceLevel !== undefined) updateData.ImportanceLevel = parseInt(body.importanceLevel) || 0;
530
531 console.log('[Product API PUT] Mapped update data:', updateData);
532
533 // Use BUCK API for updates - same method as reads but with parameter '4': '1' for update
534 const buckUpdateParams: Record<string, string> = {
535 '3': 'retailmax.elink.products',
536 '4': '1', // Update operation
537 '9': `f100,0,${id}`, // Filter by product ID
538 '99': Math.random().toString() // Cache buster
539 };
540
541 // Add update fields using Fieldpine field codes
542 if (updateData.Description !== undefined) buckUpdateParams['101'] = updateData.Description;
543 if (updateData.ShortName !== undefined) buckUpdateParams['102'] = updateData.ShortName;
544 if (updateData.Plu !== undefined) buckUpdateParams['105'] = updateData.Plu;
545 if (updateData.Barcode !== undefined) buckUpdateParams['513'] = updateData.Barcode;
546 if (updateData.Price !== undefined) buckUpdateParams['103'] = updateData.Price.toString();
547 if (updateData.Cost !== undefined) buckUpdateParams['108'] = updateData.Cost.toString();
548 if (updateData.Depid !== undefined) buckUpdateParams['106'] = updateData.Depid?.toString() || '';
549 if (updateData.Depid2 !== undefined) buckUpdateParams['350'] = updateData.Depid2?.toString() || '';
550 if (updateData.Depid3 !== undefined) buckUpdateParams['351'] = updateData.Depid3?.toString() || '';
551 if (updateData.Depid4 !== undefined) buckUpdateParams['352'] = updateData.Depid4?.toString() || '';
552 if (updateData.Depid5 !== undefined) buckUpdateParams['371'] = updateData.Depid5?.toString() || '';
553 if (updateData.Depid6 !== undefined) buckUpdateParams['372'] = updateData.Depid6?.toString() || '';
554 if (updateData.Depid7 !== undefined) buckUpdateParams['373'] = updateData.Depid7?.toString() || '';
555 if (updateData.Depid8 !== undefined) buckUpdateParams['374'] = updateData.Depid8?.toString() || '';
556 if (updateData.LastSupplierSpid !== undefined) buckUpdateParams['104'] = updateData.LastSupplierSpid?.toString() || '';
557 if (updateData.Freight !== undefined) buckUpdateParams['147'] = updateData.Freight?.toString() || '';
558 if (updateData.Packaging !== undefined) buckUpdateParams['149'] = updateData.Packaging?.toString() || '';
559 if (updateData.DisableDiscounts !== undefined) buckUpdateParams['170'] = updateData.DisableDiscounts;
560 if (updateData.PartCount !== undefined) buckUpdateParams['211'] = updateData.PartCount?.toString() || '';
561 if (updateData.WarehouseLabelName !== undefined) buckUpdateParams['212'] = updateData.WarehouseLabelName;
562 if (updateData.ShelfLabelLine1 !== undefined) buckUpdateParams['228'] = updateData.ShelfLabelLine1;
563 if (updateData.ShelfLabelLine2 !== undefined) buckUpdateParams['229'] = updateData.ShelfLabelLine2;
564 if (updateData.ShelfLabelLine3 !== undefined) buckUpdateParams['230'] = updateData.ShelfLabelLine3;
565 if (updateData.ShelfLabelFormat !== undefined) buckUpdateParams['349'] = updateData.ShelfLabelFormat;
566 if (updateData.ReleaseDate !== undefined) buckUpdateParams['363'] = updateData.ReleaseDate;
567 if (updateData.AvailableDate !== undefined) buckUpdateParams['364'] = updateData.AvailableDate;
568 if (updateData.ReorderLevel !== undefined) buckUpdateParams['8390726'] = updateData.ReorderLevel.toString();
569 if (updateData.ReorderMax !== undefined) buckUpdateParams['8390727'] = updateData.ReorderMax.toString();
570 if (updateData.TaxFlag !== undefined) buckUpdateParams['112'] = updateData.TaxFlag.toString();
571 if (updateData.NoSell !== undefined) buckUpdateParams['151'] = updateData.NoSell;
572 if (updateData.Hidden !== undefined) buckUpdateParams['155'] = updateData.Hidden;
573 if (updateData.Brand !== undefined) buckUpdateParams['8389009'] = updateData.Brand;
574 if (updateData.WeightGm !== undefined) buckUpdateParams['8388814'] = updateData.WeightGm?.toString() || '';
575 if (updateData.Size !== undefined) buckUpdateParams['8388818'] = updateData.Size;
576 if (updateData.WebGroup !== undefined) buckUpdateParams['182'] = updateData.WebGroup.toString();
577 if (updateData.Comments !== undefined) buckUpdateParams['256'] = updateData.Comments;
578 if (updateData.ConfirmPrice !== undefined) buckUpdateParams['323'] = updateData.ConfirmPrice;
579 if (updateData.FranchisePrice !== undefined) buckUpdateParams['325'] = updateData.FranchisePrice?.toString() || '';
580 if (updateData.PriceBand1 !== undefined) buckUpdateParams['148'] = updateData.PriceBand1?.toString() || '';
581 if (updateData.PriceBand2 !== undefined) buckUpdateParams['302'] = updateData.PriceBand2?.toString() || '';
582 if (updateData.PriceBand3 !== undefined) buckUpdateParams['303'] = updateData.PriceBand3?.toString() || '';
583 if (updateData.LowestPrice !== undefined) buckUpdateParams['8389618'] = updateData.LowestPrice?.toString() || '';
584 if (updateData.DiscountLimit1 !== undefined) buckUpdateParams['8389619'] = updateData.DiscountLimit1?.toString() || '';
585 if (updateData.DiscountLimit2 !== undefined) buckUpdateParams['8389620'] = updateData.DiscountLimit2?.toString() || '';
586 if (updateData.DiscountLimit3 !== undefined) buckUpdateParams['8389621'] = updateData.DiscountLimit3?.toString() || '';
587 if (updateData.DiscountLimit4 !== undefined) buckUpdateParams['8389622'] = updateData.DiscountLimit4?.toString() || '';
588 if (updateData.DiscountLimit5 !== undefined) buckUpdateParams['8389623'] = updateData.DiscountLimit5?.toString() || '';
589 if (updateData.SupplySource !== undefined) buckUpdateParams['196'] = updateData.SupplySource.toString();
590 if (updateData.IgnoreStockMovements !== undefined) buckUpdateParams['180'] = updateData.IgnoreStockMovements;
591 if (updateData.RecordStockMovements !== undefined) buckUpdateParams['258'] = updateData.RecordStockMovements;
592 if (updateData.Placement !== undefined) buckUpdateParams['238'] = updateData.Placement.toString();
593 // Optical/Eyewear
594 if (updateData.AlternateFit !== undefined) buckUpdateParams['4002'] = updateData.AlternateFit;
595 if (updateData.LensSize !== undefined) buckUpdateParams['4003'] = updateData.LensSize;
596 if (updateData.BridgeSize !== undefined) buckUpdateParams['4004'] = updateData.BridgeSize;
597 if (updateData.TempleLength !== undefined) buckUpdateParams['4005'] = updateData.TempleLength;
598 if (updateData.LensBaseCurve !== undefined) buckUpdateParams['4006'] = updateData.LensBaseCurve;
599 if (updateData.LensMaterial !== undefined) buckUpdateParams['4007'] = updateData.LensMaterial.toString();
600 if (updateData.FrameMaterial !== undefined) buckUpdateParams['4008'] = updateData.FrameMaterial.toString();
601 // Website/Marketing
602 if (updateData.PublishToWebsite !== undefined) buckUpdateParams['198'] = updateData.PublishToWebsite;
603 if (updateData.WebsiteHandling !== undefined) buckUpdateParams['315'] = updateData.WebsiteHandling.toString();
604 if (updateData.WebDescription !== undefined) buckUpdateParams['8390617'] = updateData.WebDescription;
605 if (updateData.WebKeywords !== undefined) buckUpdateParams['8392117'] = updateData.WebKeywords;
606 if (updateData.WebBlurb !== undefined) buckUpdateParams['8391117'] = updateData.WebBlurb;
607 if (updateData.SearchRank !== undefined) buckUpdateParams['316'] = updateData.SearchRank.toString();
608 if (updateData.DisplayRank !== undefined) buckUpdateParams['319'] = updateData.DisplayRank.toString();
609 if (updateData.WebsiteControl !== undefined) buckUpdateParams['318'] = updateData.WebsiteControl;
610 if (updateData.OnSpecial !== undefined) buckUpdateParams['8390716'] = updateData.OnSpecial;
611 if (updateData.OutOfStock !== undefined) buckUpdateParams['8390717'] = updateData.OutOfStock;
612 if (updateData.OutOfSeason !== undefined) buckUpdateParams['8392609'] = updateData.OutOfSeason;
613 if (updateData.InstoreOnly !== undefined) buckUpdateParams['8390718'] = updateData.InstoreOnly;
614 if (updateData.NewProduct !== undefined) buckUpdateParams['8390720'] = updateData.NewProduct;
615 if (updateData.FreeLocalDelivery !== undefined) buckUpdateParams['8390719'] = updateData.FreeLocalDelivery;
616 if (updateData.HighlightProduct !== undefined) buckUpdateParams['8390721'] = updateData.HighlightProduct;
617 if (updateData.FeaturedProduct !== undefined) buckUpdateParams['8480001'] = updateData.FeaturedProduct;
618 if (updateData.PromoteProduct !== undefined) buckUpdateParams['8480002'] = updateData.PromoteProduct;
619 if (updateData.MarketingColor1 !== undefined) buckUpdateParams['234'] = updateData.MarketingColor1;
620 if (updateData.MarketingColor2 !== undefined) buckUpdateParams['235'] = updateData.MarketingColor2;
621 if (updateData.MarketingColor3 !== undefined) buckUpdateParams['236'] = updateData.MarketingColor3;
622 if (updateData.MarketingColor4 !== undefined) buckUpdateParams['237'] = updateData.MarketingColor4;
623 if (updateData.InstallationUrl !== undefined) buckUpdateParams['240'] = updateData.InstallationUrl;
624 if (updateData.WarrantyUrl !== undefined) buckUpdateParams['241'] = updateData.WarrantyUrl;
625 if (updateData.UpsellUrl !== undefined) buckUpdateParams['242'] = updateData.UpsellUrl;
626 if (updateData.MarketingUrl !== undefined) buckUpdateParams['243'] = updateData.MarketingUrl;
627 // Measurements
628 if (updateData.MeasurementId !== undefined) buckUpdateParams['111'] = updateData.MeasurementId.toString();
629 if (updateData.StockMeasurementId !== undefined) buckUpdateParams['114'] = updateData.StockMeasurementId.toString();
630 if (updateData.DisplayMeasurementId !== undefined) buckUpdateParams['115'] = updateData.DisplayMeasurementId.toString();
631 if (updateData.PackagingWeight !== undefined) buckUpdateParams['213'] = updateData.PackagingWeight.toString();
632 if (updateData.LengthMm !== undefined) buckUpdateParams['353'] = updateData.LengthMm.toString();
633 if (updateData.WidthMm !== undefined) buckUpdateParams['354'] = updateData.WidthMm.toString();
634 if (updateData.HeightMm !== undefined) buckUpdateParams['355'] = updateData.HeightMm.toString();
635 if (updateData.ShippingCubic !== undefined) buckUpdateParams['356'] = updateData.ShippingCubic.toString();
636 // Supply/Ordering
637 if (updateData.ManufacturerPartCode !== undefined) buckUpdateParams['314'] = updateData.ManufacturerPartCode;
638 if (updateData.OrderCategory !== undefined) buckUpdateParams['328'] = updateData.OrderCategory.toString();
639 if (updateData.MessageWhenOrdering !== undefined) buckUpdateParams['221'] = updateData.MessageWhenOrdering;
640 if (updateData.DisableOrdering !== undefined) buckUpdateParams['222'] = updateData.DisableOrdering;
641 if (updateData.MinUnitsPerOrder !== undefined) buckUpdateParams['223'] = updateData.MinUnitsPerOrder.toString();
642 if (updateData.MaxUnitsPerOrder !== undefined) buckUpdateParams['224'] = updateData.MaxUnitsPerOrder.toString();
643 if (updateData.PriorityColorCode !== undefined) buckUpdateParams['220'] = updateData.PriorityColorCode.toString();
644 if (updateData.ImportanceLevel !== undefined) buckUpdateParams['178'] = updateData.ImportanceLevel.toString();
645
646 console.log('[Product API PUT] BUCK update params:', buckUpdateParams);
647
648 const result = await fieldpineServerApi.buckApiCall(
649 buckUpdateParams,
650 context.session.apiKey,
651 context.store.url
652 );
653
654 console.log('[Product API PUT] Update response:', result);
655
656 return NextResponse.json({
657 success: true,
658 data: result,
659 message: 'Product updated successfully'
660 });
661
662 } catch (error) {
663 console.error('[Product API PUT] Update error:', error);
664 return NextResponse.json(
665 { success: false, error: 'Failed to update product' },
666 { status: 500 }
667 );
668 }
669}