210 lines
11 KiB
JavaScript
210 lines
11 KiB
JavaScript
/**
|
|
* seed.cjs — Populate database with realistic dev/test data.
|
|
* Usage: node scripts/seed.cjs
|
|
*/
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { randomUUID } = require('crypto');
|
|
const { Client } = require('pg');
|
|
|
|
// ── Read DATABASE_URL from .env ──────────────────────────────────────────────
|
|
const envPath = path.join(__dirname, '..', '.env');
|
|
if (fs.existsSync(envPath)) {
|
|
fs.readFileSync(envPath, 'utf8')
|
|
.split('\n')
|
|
.filter((l) => l.trim() && !l.startsWith('#'))
|
|
.forEach((l) => {
|
|
const eq = l.indexOf('=');
|
|
if (eq === -1) return;
|
|
const k = l.slice(0, eq).trim();
|
|
const v = l.slice(eq + 1).trim();
|
|
if (k && !process.env[k]) process.env[k] = v;
|
|
});
|
|
}
|
|
|
|
const DATABASE_URL = process.env.DATABASE_URL;
|
|
if (!DATABASE_URL) {
|
|
console.error('DATABASE_URL not set');
|
|
process.exit(1);
|
|
}
|
|
|
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
async function sql(client, text, params, label) {
|
|
try {
|
|
const result = await client.query(text, params);
|
|
return result;
|
|
} catch (err) {
|
|
console.error(`SQL ERROR [${label}]: ${err.message}`);
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
async function seed(client) {
|
|
await client.query('BEGIN');
|
|
console.log('Seeding database...\n');
|
|
|
|
// ── Clear existing seed data ──────────────────────────────────────────
|
|
await sql(client, 'DELETE FROM inventory_stock', [], 'clear-stock');
|
|
await sql(client, 'DELETE FROM pricing_variant_prices', [], 'clear-prices');
|
|
await sql(client, 'DELETE FROM catalog_product_variants', [], 'clear-variants');
|
|
await sql(client, 'DELETE FROM catalog_product_categories', [], 'clear-prod-cats');
|
|
await sql(client, 'DELETE FROM catalog_products', [], 'clear-products');
|
|
await sql(client, "DELETE FROM categories_categories WHERE parent_id IS NOT NULL", [], 'clear-children');
|
|
await sql(client, 'DELETE FROM categories_categories', [], 'clear-categories');
|
|
await sql(client, 'DELETE FROM brands_brands', [], 'clear-brands');
|
|
await sql(client, 'DELETE FROM shipping_methods', [], 'clear-methods');
|
|
await sql(client, 'DELETE FROM shipping_zones', [], 'clear-zones');
|
|
console.log('Cleared existing data\n');
|
|
|
|
const catBySlug = {};
|
|
const brandBySlug = {};
|
|
|
|
// ── 1. Categories ─────────────────────────────────────────────────────
|
|
const catData = [
|
|
{ name: 'Alimentacion', slug: 'alimentacion', parentSlug: null },
|
|
{ name: 'Suplementos', slug: 'suplementos', parentSlug: null },
|
|
{ name: 'Cosmetica Natural', slug: 'cosmetica-natural', parentSlug: null },
|
|
{ name: 'Limpieza Ecologica', slug: 'limpieza-ecologica', parentSlug: null },
|
|
{ name: 'Frutos Secos', slug: 'frutos-secos', parentSlug: 'alimentacion' },
|
|
{ name: 'Aceites', slug: 'aceites', parentSlug: 'alimentacion' },
|
|
{ name: 'Hierbas e Infusiones', slug: 'hierbas-infusiones', parentSlug: 'alimentacion' },
|
|
{ name: 'Vitaminas', slug: 'vitaminas', parentSlug: 'suplementos' },
|
|
{ name: 'Proteinas', slug: 'proteinas', parentSlug: 'suplementos' },
|
|
{ name: 'Cremas', slug: 'cremas', parentSlug: 'cosmetica-natural' },
|
|
{ name: 'Jabones', slug: 'jabones', parentSlug: 'cosmetica-natural' },
|
|
];
|
|
|
|
// Parents first (no FK dep), then children
|
|
const parents = catData.filter((c) => !c.parentSlug);
|
|
const children = catData.filter((c) => c.parentSlug);
|
|
for (const cat of [...parents, ...children]) {
|
|
const parentId = cat.parentSlug ? catBySlug[cat.parentSlug] : null;
|
|
await sql(client,
|
|
'INSERT INTO categories_categories (id, parent_id, name, slug, seo_title) VALUES ($1,$2,$3,$4,$5) ON CONFLICT (slug) DO UPDATE SET name=EXCLUDED.name, seo_title=EXCLUDED.seo_title',
|
|
[randomUUID(), parentId, cat.name, cat.slug, cat.name],
|
|
`cat-${cat.slug}`
|
|
);
|
|
const r = await client.query('SELECT id FROM categories_categories WHERE slug = $1', [cat.slug]);
|
|
if (r.rows[0]) catBySlug[cat.slug] = r.rows[0].id;
|
|
}
|
|
console.log(`+ ${catData.length} categories`);
|
|
|
|
// ── 2. Brands ────────────────────────────────────────────────────────
|
|
const brandData = [
|
|
{ name: 'EcoVida', slug: 'ecovida' },
|
|
{ name: 'NaturGreen', slug: 'naturgreen' },
|
|
{ name: 'BioSana', slug: 'biosana' },
|
|
{ name: 'VerdePura', slug: 'verdepura' },
|
|
{ name: 'TierraNatural', slug: 'tierranatural' },
|
|
{ name: 'SolNatural', slug: 'solnatural' },
|
|
];
|
|
|
|
for (const brand of brandData) {
|
|
await sql(client,
|
|
'INSERT INTO brands_brands (id, name, slug, seo_title) VALUES ($1,$2,$3,$4) ON CONFLICT (slug) DO UPDATE SET name=EXCLUDED.name, seo_title=EXCLUDED.seo_title',
|
|
[randomUUID(), brand.name, brand.slug, brand.name],
|
|
`brand-${brand.slug}`
|
|
);
|
|
const r = await client.query('SELECT id FROM brands_brands WHERE slug = $1', [brand.slug]);
|
|
if (r.rows[0]) brandBySlug[brand.slug] = r.rows[0].id;
|
|
}
|
|
console.log(`+ ${brandData.length} brands`);
|
|
|
|
// ── 3. Products + variants + prices + stock ───────────────────────────
|
|
const productData = [
|
|
{ name: 'Almendras Crudas Ecologicas', slug: 'almendras-crudas-ecologicas', brandSlug: 'ecovida', catSlug: 'frutos-secos', price: 895, vat: 'general' },
|
|
{ name: 'Aceite de Oliva Virgen Extra Bio', slug: 'aceite-oliva-virgen-extra-bio', brandSlug: 'naturgreen', catSlug: 'aceites', price: 1290, vat: 'general' },
|
|
{ name: 'Te Verde Matcha Premium', slug: 'te-verde-matcha-premium', brandSlug: 'biosana', catSlug: 'hierbas-infusiones', price: 1590, vat: 'general' },
|
|
{ name: 'Vitamina D3 + K2', slug: 'vitamina-d3-k2', brandSlug: 'verdepura', catSlug: 'vitaminas', price: 1890, vat: 'reduced' },
|
|
{ name: 'Proteina Guisante Ecologica', slug: 'proteina-guisante-ecologica', brandSlug: 'tierranatural', catSlug: 'proteinas', price: 2490, vat: 'reduced' },
|
|
{ name: 'Complejo B Natural', slug: 'complejo-b-natural', brandSlug: 'ecovida', catSlug: 'vitaminas', price: 1190, vat: 'reduced' },
|
|
{ name: 'Crema Facial Hidratante Aloe Vera', slug: 'crema-facial-aloe-vera', brandSlug: 'solnatural', catSlug: 'cremas', price: 2190, vat: 'general' },
|
|
{ name: 'Jabon Negro Africano', slug: 'jabon-negro-africano', brandSlug: 'biosana', catSlug: 'jabones', price: 790, vat: 'general' },
|
|
{ name: 'Serum Antioxidante Rosa Mosqueta', slug: 'serum-rosa-mosqueta', brandSlug: 'naturgreen', catSlug: 'cremas', price: 2690, vat: 'general' },
|
|
{ name: 'Detergente Ecologico Ropa', slug: 'detergente-ecologico-ropa', brandSlug: 'verdepura', catSlug: 'limpieza-ecologica', price: 990, vat: 'general' },
|
|
{ name: 'Limpiador Multiusos Bio', slug: 'limpiador-multiusos-bio', brandSlug: 'tierranatural', catSlug: 'limpieza-ecologica', price: 890, vat: 'general' },
|
|
{ name: 'Desinfectante Natural Superficies', slug: 'desinfectante-natural-superficies', brandSlug: 'solnatural', catSlug: 'limpieza-ecologica', price: 1190, vat: 'general' },
|
|
];
|
|
|
|
for (let pi = 0; pi < productData.length; pi++) {
|
|
const p = productData[pi];
|
|
const brandId = brandBySlug[p.brandSlug];
|
|
const catId = catBySlug[p.catSlug];
|
|
if (!brandId || !catId) { console.warn(` Skipping ${p.slug}: missing brand or cat`); continue; }
|
|
|
|
const productId = randomUUID();
|
|
const variantId = randomUUID();
|
|
const sku = `SKU-${p.slug.replace(/-/g, '').toUpperCase().slice(0, 8)}-001`;
|
|
const ean = `590123412345${String(pi + 1).padStart(2, '0')}`;
|
|
const desc = `${p.name} de primera calidad. Producto 100% natural y ecologico.`;
|
|
|
|
await sql(client,
|
|
'INSERT INTO catalog_products (id, brand_id, name, slug, state, seo_title, description) VALUES ($1,$2,$3,$4,$5,$6,$7) ON CONFLICT (slug) DO UPDATE SET name=EXCLUDED.name, brand_id=EXCLUDED.brand_id, description=EXCLUDED.description',
|
|
[productId, brandId, p.name, p.slug, 'active', p.name, desc],
|
|
`product-${p.slug}`
|
|
);
|
|
await sql(client,
|
|
'INSERT INTO catalog_product_categories (product_id, category_id) VALUES ($1,$2) ON CONFLICT DO NOTHING',
|
|
[productId, catId],
|
|
`prodcat-${p.slug}`
|
|
);
|
|
await sql(client,
|
|
'INSERT INTO catalog_product_variants (id, product_id, sku, ean) VALUES ($1,$2,$3,$4) ON CONFLICT (sku) DO UPDATE SET ean=EXCLUDED.ean',
|
|
[variantId, productId, sku, ean],
|
|
`variant-${sku}`
|
|
);
|
|
await sql(client,
|
|
'INSERT INTO pricing_variant_prices (variant_id, vat_rate, net_unit_amount_cents) VALUES ($1,$2,$3) ON CONFLICT (variant_id) DO UPDATE SET net_unit_amount_cents=EXCLUDED.net_unit_amount_cents, vat_rate=EXCLUDED.vat_rate',
|
|
[variantId, p.vat, p.price],
|
|
`price-${sku}`
|
|
);
|
|
await sql(client,
|
|
'INSERT INTO inventory_stock (variant_id, available, reserved) VALUES ($1,$2,$3) ON CONFLICT (variant_id) DO UPDATE SET available=EXCLUDED.available, reserved=EXCLUDED.reserved',
|
|
[variantId, 25, 0],
|
|
`stock-${sku}`
|
|
);
|
|
}
|
|
console.log(`+ ${productData.length} products (variants, prices, stock)`);
|
|
|
|
// ── 4. Shipping ───────────────────────────────────────────────────
|
|
const zoneId = randomUUID();
|
|
await sql(client,
|
|
'INSERT INTO shipping_zones (id, name, country, postal_code_prefix) VALUES ($1,$2,$3,$4) ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name',
|
|
[zoneId, 'Espania Peninsular', 'ES', null],
|
|
'zone-es'
|
|
);
|
|
await sql(client,
|
|
'INSERT INTO shipping_methods (id, zone_id, name, base_cost_cents, free_shipping_threshold_cents) VALUES ($1,$2,$3,$4,$5)',
|
|
[randomUUID(), zoneId, 'Estandar', 499, 4900],
|
|
'method-std'
|
|
);
|
|
await sql(client,
|
|
'INSERT INTO shipping_methods (id, zone_id, name, base_cost_cents, free_shipping_threshold_cents) VALUES ($1,$2,$3,$4,$5)',
|
|
[randomUUID(), zoneId, 'Express 24h', 899, null],
|
|
'method-express'
|
|
);
|
|
console.log('+ 1 shipping zone + 2 methods');
|
|
|
|
await client.query('COMMIT');
|
|
console.log('\nSeed complete!');
|
|
}
|
|
|
|
// ── Run ──────────────────────────────────────────────────────────────────
|
|
async function main() {
|
|
const client = new Client({ connectionString: DATABASE_URL });
|
|
try {
|
|
await client.connect();
|
|
await seed(client);
|
|
} catch (err) {
|
|
await client.query('ROLLBACK').catch(() => {});
|
|
console.error('Seed failed:', err.message);
|
|
process.exit(1);
|
|
} finally {
|
|
await client.end();
|
|
}
|
|
}
|
|
|
|
main();
|