/** * Motor de Precios, Políticas de Cambio y Redondeo Automático al 5.000 Siguiente */ export const DEFAULT_MIN_PROFIT_MARGIN_PERCENT = 35; // % Mínimo de ganancia export const DEFAULT_MIN_PROFIT_COP = 30000; // Margen mínimo en pesos export const DEFAULT_SEPARATION_DEPOSIT_COP = 10000; // Separación fija export const ROUND_STEP_COP = 5000; // Redondeo siempre al múltiplo de $5.000 superior export const EXCHANGE_POLICY = { firstExchangeFree: true, badgeText: '1er Cambio de Talla GRATIS', shortNotice: 'Primer cambio de talla gratis. Cambios posteriores con flete estándar.', fullTerms: 'Cuentas con 1er cambio de talla 100% GRATIS dentro de los primeros 5 días. Para cambios adicionales o posteriores, el cliente asume el costo logístico del flete.', }; export function formatCOP(amount: number): string { if (isNaN(amount)) return '$0'; return '$' + Math.round(amount).toLocaleString('es-CO'); } export function roundUpToNearest5000(amount: number): number { if (!amount || amount <= 0) return 0; return Math.ceil(amount / ROUND_STEP_COP) * ROUND_STEP_COP; } export function calculateRetailPrice( supplierPrice: number, minMarginPercent: number = DEFAULT_MIN_PROFIT_MARGIN_PERCENT, minMarginCop: number = DEFAULT_MIN_PROFIT_COP ): number { if (!supplierPrice || supplierPrice <= 0) return 0; const priceByPercent = supplierPrice * (1 + minMarginPercent / 100); const priceByFixed = supplierPrice + minMarginCop; const baseTargetPrice = Math.max(priceByPercent, priceByFixed); return roundUpToNearest5000(baseTargetPrice); } export function getPricingBreakdown(supplierCost: number, minMarginPercent: number = DEFAULT_MIN_PROFIT_MARGIN_PERCENT) { const retailPrice = calculateRetailPrice(supplierCost, minMarginPercent); const grossProfit = retailPrice - supplierCost; const realMarginPercent = Math.round((grossProfit / supplierCost) * 100); return { supplierCost, retailPrice, grossProfit, realMarginPercent, isCeiledTo5k: true, }; } export function calculateAnchorPrice(retailPrice: number, discountPercentage: number = 30): number { if (!retailPrice || retailPrice <= 0) return 0; const anchor = retailPrice / (1 - discountPercentage / 100); return roundUpToNearest5000(anchor); } export function calculateSeparationBreakdown( totalAmount: number, depositAmount: number = DEFAULT_SEPARATION_DEPOSIT_COP ) { const deposit = Math.min(depositAmount, totalAmount); const balanceDue = Math.max(0, totalAmount - deposit); return { totalAmount, depositAmount: deposit, balanceDue, summaryText: `Separas hoy con ${formatCOP(deposit)} y cancelas ${formatCOP(balanceDue)} al recibir contraentrega en tu puerta.`, exchangeTerms: EXCHANGE_POLICY.shortNotice, }; } export function generateSalesCopy(params: { productName: string; brand: string; retailPrice: number; sizes: string[]; storeName?: string; cityName?: string; whatsappNumber?: string; }): string { const { productName, brand, retailPrice, sizes, storeName = 'TIENDA DE CALZADO', cityName = 'Cali', whatsappNumber = '573134848203', } = params; const breakdown = calculateSeparationBreakdown(retailPrice); const sizesFormatted = sizes.join(' - '); const waLink = `https://wa.me/${whatsappNumber}?text=${encodeURIComponent(`Hola ${storeName}! Quiero pedir las zapatillas ${productName} (${brand}) en talla ___ para entrega contraentrega en ${cityName}.`)}`; return `✨ ${storeName.toUpperCase()} • LANZAMIENTO EXCLUSIVO ✨ 👟 ${productName.toUpperCase()} (${brand}) 📏 Tallas Disponibles: ${sizesFormatted} (Horma Nacional) 💎 PRECIO DE VENTA: ${formatCOP(retailPrice)} COP 🚚 ¡DOMICILIO EN ${cityName.toUpperCase()} Y ENVÍOS A TODO EL PAÍS! 🤝 PAGO CONTRAENTREGA: Cancelas al recibir el producto en tus manos. ⚡ SEPARA CON SOLO ${formatCOP(breakdown.depositAmount)} COP y cancelas ${formatCOP(breakdown.balanceDue)} al recibir. 🛡️ POLÍTICA DE GARANTÍA: ✅ Calidad Premium 100% garantizada ✅ ${EXCHANGE_POLICY.badgeText} (posteriores con flete estándar) 📲 ¡Pídelas antes de que se agote tu talla! 👉 Pedir directo por WhatsApp: ${waLink}`; } /** * Obtiene el precio exacto de venta de un producto según el estilo/color y la talla seleccionada. */ export function getProductEffectivePrice( product: { retailPrice: number; sizePrices?: Record; }, selectedColor?: { price?: number; sizePrices?: Record; } | null, selectedSize?: string | null ): number { if (selectedColor && selectedSize && selectedColor.sizePrices && selectedColor.sizePrices[selectedSize]) { return selectedColor.sizePrices[selectedSize]; } if (selectedSize && product.sizePrices && product.sizePrices[selectedSize]) { return product.sizePrices[selectedSize]; } if (selectedColor && selectedColor.price && selectedColor.price > 0) { return selectedColor.price; } return product.retailPrice || 120000; } /** * Obtiene el rango de precios si varían según la talla o colorway */ export function getProductPriceRange(product: { retailPrice: number; sizePrices?: Record; colors?: Array<{ price?: number; sizePrices?: Record }>; }): { minPrice: number; maxPrice: number; hasVariation: boolean } { const prices: number[] = [product.retailPrice || 120000]; if (product.sizePrices) { Object.values(product.sizePrices).forEach((p) => { if (typeof p === 'number' && p > 0) prices.push(p); }); } if (Array.isArray(product.colors)) { product.colors.forEach((c) => { if (c.price && c.price > 0) prices.push(c.price); if (c.sizePrices) { Object.values(c.sizePrices).forEach((p) => { if (typeof p === 'number' && p > 0) prices.push(p); }); } }); } const minPrice = Math.min(...prices); const maxPrice = Math.max(...prices); return { minPrice, maxPrice, hasVariation: minPrice !== maxPrice, }; }