import { NextRequest, NextResponse } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { findColIndex } from '@/lib/creationSheets' import { getConfig } from '@/lib/shopifySync' export async function POST(req: NextRequest) { const session = await getServerSession(authOptions) if (!session) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) const { tab } = await req.json().catch(() => ({})) as { tab?: string } const stream = new ReadableStream({ async start(controller) { const send = (obj: object) => controller.enqueue(new TextEncoder().encode(JSON.stringify(obj) + '\n')) try { const { baseUrl, headers } = getConfig() // Récupérer le location_id une seule fois const locRes = await fetch(`${baseUrl}/locations.json`, { headers }) if (!locRes.ok) throw new Error(`Shopify locations error: ${locRes.status}`) const locData = await locRes.json() as { locations: Array<{ id: number }> } const locationId = locData.locations[0]?.id if (!locationId) throw new Error('Aucune location Shopify trouvée') // Lire tous les onglets avec un ID Shopify en colonne A const apiKey = process.env.GOOGLE_API_KEY const sheetId = process.env.GOOGLE_SHEETS_ID if (!apiKey || !sheetId) throw new Error('GOOGLE_API_KEY et GOOGLE_SHEETS_ID requis') let tabs: string[] if (tab) { tabs = [tab] } else { const metaRes = await fetch( `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}?key=${apiKey}&fields=sheets.properties.title`, ) if (!metaRes.ok) throw new Error(`Sheets metadata error: ${metaRes.status}`) const meta = await metaRes.json() tabs = (meta.sheets as Array<{ properties: { title: string } }>).map(s => s.properties.title) } type StockEntry = { shopifyId: string; stock: number; title: string; tab: string } const entries: StockEntry[] = [] for (const t of tabs) { const range = encodeURIComponent(`${t}!A1:BZ`) const res = await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${range}?key=${apiKey}`) if (!res.ok) continue const data = await res.json() const rows = (data.values ?? []) as string[][] if (rows.length < 2) continue const headerRowIdx = rows.findIndex(r => (r[0] ?? '').trim() === 'ID') if (headerRowIdx === -1) continue const hdrs = rows[headerRowIdx] if (findColIndex(hdrs, 'Publié') === -1) continue const idIdx = 0 const nomIdx = findColIndex(hdrs, 'Nom') const stockIdx = findColIndex(hdrs, 'Stock (en pièce)') !== -1 ? findColIndex(hdrs, 'Stock (en pièce)') : findColIndex(hdrs, 'Stock (pièce)') const stockCdtIdx = findColIndex(hdrs, 'Stock conditionné') const get = (row: string[], i: number) => (i >= 0 ? (row[i] ?? '').trim() : '') for (const row of rows.slice(headerRowIdx + 1)) { const shopifyId = get(row, idIdx) if (!shopifyId || !/^\d+$/.test(shopifyId)) continue const rawStock = get(row, stockCdtIdx) || get(row, stockIdx) const stock = parseInt(rawStock || '0', 10) || 0 entries.push({ shopifyId, stock, title: get(row, nomIdx), tab: t }) } } send({ type: 'total', total: entries.length }) let updated = 0 let errors = 0 for (let i = 0; i < entries.length; i++) { const { shopifyId, stock, title } = entries[i] send({ type: 'progress', current: i + 1, total: entries.length, title }) try { // Récupérer le variant pour obtenir inventory_item_id const pRes = await fetch(`${baseUrl}/products/${shopifyId}.json?fields=variants`, { headers }) if (pRes.status === 404) { // Produit supprimé de Shopify, on ignore silencieusement continue } if (!pRes.ok) { const msg = `Produit ${shopifyId} erreur (${pRes.status})` send({ type: 'error', title, message: msg }) errors++ continue } const pData = await pRes.json() as { product: { variants: Array<{ inventory_item_id: number }> } } const inventoryItemId = pData.product.variants[0]?.inventory_item_id if (!inventoryItemId) { errors++; continue } // Connect (idempotent) await fetch(`${baseUrl}/inventory_levels/connect.json`, { method: 'POST', headers, body: JSON.stringify({ location_id: locationId, inventory_item_id: inventoryItemId }), }) const setRes = await fetch(`${baseUrl}/inventory_levels/set.json`, { method: 'POST', headers, body: JSON.stringify({ location_id: locationId, inventory_item_id: inventoryItemId, available: stock }), }) if (!setRes.ok) { const txt = await setRes.text() send({ type: 'error', title, message: `stock set failed: ${setRes.status} — ${txt}` }) errors++ } else { updated++ } } catch (err) { send({ type: 'error', title, message: err instanceof Error ? err.message : 'Erreur inconnue' }) errors++ } } send({ type: 'done', updated, errors }) } catch (err) { const message = err instanceof Error ? err.message : 'Erreur inconnue' console.error('[sync-stock] fatal:', message) send({ type: 'done', updated: 0, errors: 1, fatalError: message }) } finally { controller.close() } }, }) return new Response(stream, { headers: { 'Content-Type': 'application/x-ndjson', 'Transfer-Encoding': 'chunked' }, }) }