0,'path'=>'/','secure'=>true,'httponly'=>true,'samesite'=>'Strict']); session_start(); if (empty($_SESSION['ok'])) { http_response_code(403); exit('Sesión caducada'); } define('PRECIO_PROVISIONAL', '5'); define('MAX_ATTRS', 3); $job = jobRead(); $procesados = $job['procesados'] ?? []; if (!$procesados) { http_response_code(400); exit('Todavía no hay productos procesados'); } ksort($procesados); $cab = [ 'ID','Type','SKU','Name','Published','Is featured?','Visibility in catalog', 'Short description','Description','Tax status','Tax class','In stock?','Stock', 'Backorders allowed?','Sold individually?','Regular price','Categories','Tags', 'Shipping class','Images','Parent','Position', ]; for ($a = 1; $a <= MAX_ATTRS; $a++) { $cab[] = "Attribute $a name"; $cab[] = "Attribute $a value(s)"; $cab[] = "Attribute $a visible"; $cab[] = "Attribute $a global"; } $cab = array_merge($cab, [ 'Meta: _evl_montajes','Meta: _evl_proveedor','Meta: _evl_url_origen', 'Meta: _evl_precio_proveedor','Meta: _evl_import_source', ]); header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="catalogo-woocommerce-' . date('Ymd-His') . '.csv"'); $out = fopen('php://output', 'w'); fwrite($out, "\xEF\xBB\xBF"); // BOM: Excel y WooCommerce lo agradecen fputcsv($out, $cab); foreach ($procesados as $p) { $vars = $p['variaciones'] ?? []; $attrs = $p['atributos'] ?? []; $tipo = $vars ? 'variable' : 'simple'; // Valores únicos por atributo, en el orden en que aparecen $valoresPorAttr = []; foreach ($vars as $v) { foreach ($v['valores'] as $k => $val) { if ($k >= MAX_ATTRS) break; if (!isset($valoresPorAttr[$k])) $valoresPorAttr[$k] = []; if (!in_array($val, $valoresPorAttr[$k], true)) $valoresPorAttr[$k][] = $val; } } // ── Fila del producto padre ── $fila = [ '', // ID (lo asigna WooCommerce) $tipo, $p['sku'], $p['titulo'], 0, // 0 = se importa como borrador, para revisarlo antes 0, 'visible', $p['descripcion_corta'], $p['descripcion_larga'], 'taxable', '', 1, '', 0, 0, $vars ? '' : PRECIO_PROVISIONAL, // el padre variable no lleva precio $p['categoria'], implode(', ', $p['tags'] ?? []), '', $p['imagenes'], '', 0, ]; for ($a = 0; $a < MAX_ATTRS; $a++) { if (isset($attrs[$a]) && !empty($valoresPorAttr[$a])) { $fila[] = $attrs[$a]; $fila[] = implode(', ', $valoresPorAttr[$a]); $fila[] = 1; // visible en la ficha $fila[] = 1; // global: necesario para que funcionen los filtros del tema } else { $fila[] = ''; $fila[] = ''; $fila[] = ''; $fila[] = ''; } } // Si no hay variaciones, la marca se guarda como atributo global para poder filtrar por ella if (!$vars && !empty($p['marca'])) { $fila[22] = 'Marca'; $fila[23] = $p['marca']; $fila[24] = 1; $fila[25] = 1; } $fila = array_merge($fila, [ implode(" \n", $p['montajes'] ?? []), $p['proveedor'] ?? '', $p['url_origen'] ?? '', isset($p['precio_proveedor']) && $p['precio_proveedor'] !== null ? number_format((float)$p['precio_proveedor'], 2, '.', '') : '', 'csv_proveedor', ]); fputcsv($out, $fila); // ── Filas de cada variación ── foreach ($vars as $n => $v) { $f = [ '', 'variation', $p['sku'] . '-' . str_pad((string)($n + 1), 2, '0', STR_PAD_LEFT), $p['titulo'] . ' - ' . $v['etiqueta'], 0, 0, 'visible', '', '', 'taxable', '', 1, '', 0, 0, PRECIO_PROVISIONAL, '', '', '', '', // la variación hereda la imagen del padre $p['sku'], // Parent: enlaza con el producto padre por SKU $n, ]; for ($a = 0; $a < MAX_ATTRS; $a++) { if (isset($attrs[$a]) && isset($v['valores'][$a])) { $f[] = $attrs[$a]; $f[] = $v['valores'][$a]; $f[] = ''; $f[] = 1; } else { $f[] = ''; $f[] = ''; $f[] = ''; $f[] = ''; } } $f = array_merge($f, [ '', $p['proveedor'] ?? '', $p['url_origen'] ?? '', $v['precio'] !== null ? number_format($v['precio'], 2, '.', '') : '', 'csv_proveedor', ]); fputcsv($out, $f); } } fclose($out);