feat(plan5): creationSheets — lecture lignes en attente (TDD)
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import type { PendingProduct } from '@/types/creation'
|
||||
|
||||
const COMMON_HEADERS = new Set([
|
||||
'ID', 'Type', 'UGS', 'Nom', 'Publié', 'Mis en avant ?',
|
||||
'Description courte', 'Description longue',
|
||||
'Date de début de promo', 'Date de fin de promo',
|
||||
'État de la TVA', 'Classe de TVA', 'En stock ?',
|
||||
'Stock (en pièce)', 'Vendre individuellement ?',
|
||||
'Prix marché', 'Prix remisé', 'Code ecopart', 'Ecopart montant',
|
||||
'Famille', 'Sous famille',
|
||||
"Classe d'expédition (en retrait, livraison)",
|
||||
'Conditionnement', 'Surface (m2)', 'Poids (kg) fonction unité',
|
||||
'Images', 'Unité', 'Marque', 'Neuf ?', 'PHOTO ?',
|
||||
'ID FAMILLE', 'ID SOUS FAMILLE',
|
||||
])
|
||||
|
||||
export function findColIndex(headers: string[], search: string): number {
|
||||
const exact = headers.findIndex(h => h === search)
|
||||
if (exact !== -1) return exact
|
||||
return headers.findIndex(h => h.startsWith(search))
|
||||
}
|
||||
|
||||
export function extractSpecificFields(headers: string[], row: string[]): Record<string, string> {
|
||||
const result: Record<string, string> = {}
|
||||
headers.forEach((header, i) => {
|
||||
if (!header) return
|
||||
const isCommon = COMMON_HEADERS.has(header) || header.startsWith('Commentaire')
|
||||
if (isCommon) return
|
||||
const val = (row[i] ?? '').trim()
|
||||
if (val) result[header] = val
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export function parseTabRows(tab: string, headers: string[], rows: string[][]): PendingProduct[] {
|
||||
const idx = {
|
||||
id: findColIndex(headers, 'ID'),
|
||||
sku: findColIndex(headers, 'UGS'),
|
||||
nom: findColIndex(headers, 'Nom'),
|
||||
publie: findColIndex(headers, 'Publié'),
|
||||
stock: findColIndex(headers, 'Stock (en pièce)'),
|
||||
prixMarche: findColIndex(headers, 'Prix marché'),
|
||||
prixRemise: findColIndex(headers, 'Prix remisé'),
|
||||
poids: findColIndex(headers, 'Poids (kg) fonction unité'),
|
||||
marque: findColIndex(headers, 'Marque'),
|
||||
famille: findColIndex(headers, 'Famille'),
|
||||
sousFamille: findColIndex(headers, 'Sous famille'),
|
||||
commentaire: findColIndex(headers, 'Commentaire'),
|
||||
photo: findColIndex(headers, 'PHOTO ?'),
|
||||
collectionId: findColIndex(headers, 'ID FAMILLE'),
|
||||
subCollectionId: findColIndex(headers, 'ID SOUS FAMILLE'),
|
||||
}
|
||||
|
||||
const get = (row: string[], i: number) => (i >= 0 ? (row[i] ?? '').trim() : '')
|
||||
|
||||
return rows
|
||||
.map((row, rowIdx): PendingProduct | null => {
|
||||
const id = get(row, idx.id)
|
||||
const publie = get(row, idx.publie)
|
||||
if (id !== '' || publie !== '1') return null
|
||||
return {
|
||||
tab,
|
||||
rowNumber: rowIdx + 2,
|
||||
sku: get(row, idx.sku),
|
||||
title: get(row, idx.nom),
|
||||
priceMarket: get(row, idx.prixMarche).replace(',', '.'),
|
||||
priceActual: get(row, idx.prixRemise).replace(',', '.'),
|
||||
stock: parseInt(get(row, idx.stock) || '0', 10) || 0,
|
||||
weightKg: parseFloat(get(row, idx.poids).replace(',', '.') || '0') || 0,
|
||||
vendor: get(row, idx.marque),
|
||||
famille: get(row, idx.famille),
|
||||
sousFamille: get(row, idx.sousFamille),
|
||||
comment: get(row, idx.commentaire),
|
||||
collectionId: get(row, idx.collectionId),
|
||||
subCollectionId: get(row, idx.subCollectionId),
|
||||
hasPhoto: get(row, idx.photo) !== '',
|
||||
specificFields: extractSpecificFields(headers, row),
|
||||
}
|
||||
})
|
||||
.filter((p): p is PendingProduct => p !== null)
|
||||
}
|
||||
|
||||
export async function fetchPendingProducts(
|
||||
alreadyCreatedRows: Set<string>,
|
||||
): Promise<PendingProduct[]> {
|
||||
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')
|
||||
|
||||
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()
|
||||
const tabs: string[] = (meta.sheets as Array<{ properties: { title: string } }>).map(
|
||||
s => s.properties.title,
|
||||
)
|
||||
|
||||
const all: PendingProduct[] = []
|
||||
for (const tab of tabs) {
|
||||
const range = encodeURIComponent(`${tab}!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 [headers, ...dataRows] = rows
|
||||
const pending = parseTabRows(tab, headers, dataRows).filter(
|
||||
p => !alreadyCreatedRows.has(`${p.tab}:${p.rowNumber}`),
|
||||
)
|
||||
all.push(...pending)
|
||||
}
|
||||
return all
|
||||
}
|
||||
Reference in New Issue
Block a user