1import { NextRequest, NextResponse } from 'next/server';
2import { fieldpineServerApi } from '@/lib/server/fieldpineApi';
3import { getStoredAuth } from '@/lib/server/auth';
5// Helper function to calculate string similarity (Levenshtein distance)
6function similarity(s1: string, s2: string): number {
7 if (!s1 || !s2) return 0;
9 const longer = s1.length > s2.length ? s1 : s2;
10 const shorter = s1.length > s2.length ? s2 : s1;
12 if (longer.length === 0) return 100;
14 const editDistance = levenshteinDistance(longer.toLowerCase(), shorter.toLowerCase());
15 return Math.round(((longer.length - editDistance) / longer.length) * 100);
18function levenshteinDistance(s1: string, s2: string): number {
19 const costs: number[] = [];
20 for (let i = 0; i <= s1.length; i++) {
22 for (let j = 0; j <= s2.length; j++) {
26 let newValue = costs[j - 1];
27 if (s1.charAt(i - 1) !== s2.charAt(j - 1)) {
28 newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
30 costs[j - 1] = lastValue;
34 if (i > 0) costs[s2.length] = lastValue;
36 return costs[s2.length];
40 f100: number; // Customer ID
41 f101?: string; // Name
42 f102?: string; // Last Name
43 f103?: string; // First Name
44 f112?: string; // Phone
45 f113?: string; // Mobile
46 f115?: string; // Email
47 f254?: string; // Physkey
50interface DuplicateMatch {
65export async function GET(request: NextRequest) {
67 // Verify authentication
68 const authData = await getStoredAuth();
69 if (!authData || !authData.authenticated) {
70 return NextResponse.json(
71 { error: 'Authentication required' },
76 console.log('[Duplicate Customers] Fetching customer data...');
78 // Fetch all customers using BUCK API
79 const result = await fieldpineServerApi.buckApiCall({
80 "3": "retailmax.elink.customers",
81 "10": "1000", // Limit to 1000 customers for performance
82 "17": "5" // Return fields: f100 (id), f101 (name), f102 (lastname), f103 (firstname), f112 (phone), f113 (mobile), f115 (email), f254 (physkey)
85 console.log('[Duplicate Customers] BUCK result:', {
86 hasRootType: !!result?.RootType,
87 hasDats: !!result?.DATS,
88 datsLength: result?.DATS?.length || 0
91 // Extract customer data
92 const customers: Customer[] = result?.DATS || [];
94 if (!customers || customers.length === 0) {
95 console.log('[Duplicate Customers] No customers found');
96 return NextResponse.json({
99 message: 'No customers found'
103 console.log('[Duplicate Customers] Processing', customers.length, 'customers');
105 // Find potential duplicates
106 const duplicates: DuplicateMatch[] = [];
107 const processedPairs = new Set<string>();
109 for (let i = 0; i < customers.length; i++) {
110 for (let j = i + 1; j < customers.length; j++) {
111 const c1 = customers[i];
112 const c2 = customers[j];
114 // Create unique pair key to avoid duplicates
115 const pairKey = `${Math.min(c1.f100, c2.f100)}-${Math.max(c1.f100, c2.f100)}`;
116 if (processedPairs.has(pairKey)) continue;
118 const name1 = c1.f101 || c1.f102 || c1.f103 || '';
119 const name2 = c2.f101 || c2.f102 || c2.f103 || '';
121 const phone1 = c1.f112 || c1.f113 || '';
122 const phone2 = c2.f112 || c2.f113 || '';
124 const email1 = c1.f115 || '';
125 const email2 = c2.f115 || '';
128 let matchReason = '';
130 // Check name similarity
131 if (name1 && name2) {
132 const nameSimilarity = similarity(name1, name2);
133 if (nameSimilarity >= 80) {
134 matchScore = nameSimilarity;
135 matchReason = 'Similar names';
139 // Check exact phone match
140 if (phone1 && phone2 && phone1 === phone2) {
141 matchScore = Math.max(matchScore, 95);
142 matchReason = matchReason ? `${matchReason}, Same phone` : 'Same phone number';
145 // Check exact email match
146 if (email1 && email2 && email1.toLowerCase() === email2.toLowerCase()) {
147 matchScore = Math.max(matchScore, 98);
148 matchReason = matchReason ? `${matchReason}, Same email` : 'Same email address';
151 // If match score is high enough, add to duplicates
152 if (matchScore >= 75) {
153 processedPairs.add(pairKey);
157 physkey1: c1.f254 || '',
158 physkey2: c2.f254 || '',
165 similarity: matchScore,
166 matchReason: matchReason
172 console.log('[Duplicate Customers] Found', duplicates.length, 'potential duplicates');
174 // Sort by similarity (highest first)
175 duplicates.sort((a, b) => b.similarity - a.similarity);
177 return NextResponse.json({
179 duplicates: duplicates,
180 totalCustomers: customers.length,
183 } catch (error: any) {
184 console.error('Duplicate Customers API error:', error);
185 return NextResponse.json(
186 { success: false, error: error.message || 'Failed to fetch duplicate customers' },