'use server'; import { and, eq } from 'drizzle-orm'; import { revalidatePath } from 'next/cache'; import { db } from '@/db'; import { catalogItems, pricingConfig } from '@/db/schema'; import { getTenantId } from '@/db/pricing-queries'; import { parseCatalogCsv } from '@/budget/csv'; // Valida un importe en euros del formulario y lo convierte a céntimos. // Lanza un error en español si el valor no es un número finito >= 0. function eurosToCents(raw: FormDataEntryValue | null, campo: string): number { const euros = Number(raw); if (!Number.isFinite(euros) || euros < 0) { throw new Error(`El valor de "${campo}" debe ser un número mayor o igual que 0.`); } return Math.round(euros * 100); } function parsePositive(raw: FormDataEntryValue | null, campo: string): number { const n = Number(raw); if (!Number.isFinite(n) || n <= 0) { throw new Error(`El valor de "${campo}" debe ser un número mayor que 0.`); } return n; } export async function crearMaterial(formData: FormData) { const tenantId = await getTenantId(); await db.insert(catalogItems).values({ tenantId, categoria: formData.get('categoria') as 'suelo' | 'pared' | 'pintura' | 'mobiliario', nombre: String(formData.get('nombre') ?? ''), calidad: formData.get('calidad') as 'basica' | 'media' | 'premium', precioUnit: eurosToCents(formData.get('precioEuros'), 'precio'), unidad: formData.get('unidad') as 'm2' | 'ml' | 'ud', descriptorRender: String(formData.get('descriptorRender') ?? ''), esDefault: formData.get('esDefault') === 'on', sku: String(formData.get('sku') ?? ''), }); revalidatePath('/panel/precios'); } export async function actualizarPrecio(formData: FormData) { const tenantId = await getTenantId(); const id = String(formData.get('id') ?? ''); await db .update(catalogItems) .set({ precioUnit: eurosToCents(formData.get('precioEuros'), 'precio') }) .where(and(eq(catalogItems.id, id), eq(catalogItems.tenantId, tenantId))); revalidatePath('/panel/precios'); } export async function borrarMaterial(formData: FormData) { const tenantId = await getTenantId(); const id = String(formData.get('id') ?? ''); await db.delete(catalogItems).where(and(eq(catalogItems.id, id), eq(catalogItems.tenantId, tenantId))); revalidatePath('/panel/precios'); } export async function actualizarConfig(formData: FormData) { const tenantId = await getTenantId(); await db .update(pricingConfig) .set({ alturaTechoDefault: parsePositive(formData.get('alturaTechoDefault'), 'altura de techo'), manoObra: { demolicion: eurosToCents(formData.get('mo_demolicion'), 'demolición'), fontaneria: eurosToCents(formData.get('mo_fontaneria'), 'fontanería'), electricidad: eurosToCents(formData.get('mo_electricidad'), 'electricidad'), mano_de_obra: eurosToCents(formData.get('mo_mano_de_obra'), 'mano de obra'), }, updatedAt: new Date(), }) .where(eq(pricingConfig.tenantId, tenantId)); revalidatePath('/panel/precios'); } export type ImportResult = { ok: boolean; inserted: number; errors: { line: number; message: string }[] }; export async function importarCatalogoCsv(_prev: ImportResult | null, formData: FormData): Promise { const tenantId = await getTenantId(); const csv = String(formData.get('csv') ?? ''); const { rows, errors } = parseCatalogCsv(csv); if (errors.length > 0) return { ok: false, inserted: 0, errors }; for (const r of rows) { await db .insert(catalogItems) .values({ tenantId, ...r }) .onConflictDoUpdate({ target: [catalogItems.tenantId, catalogItems.sku], set: { categoria: r.categoria, nombre: r.nombre, calidad: r.calidad, precioUnit: r.precioUnit, unidad: r.unidad, descriptorRender: r.descriptorRender, }, }); } revalidatePath('/panel/precios'); return { ok: true, inserted: rows.length, errors: [] }; }