/** * 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 interface MarketPricingStrategy { id: 'competitive' | 'balanced' | 'premium'; name: string; badge: string; description: string; price: number; profitCOP: number; marginPercent: number; clientSavingsPercent: number; savingsCOP: number; } export function calculateMarketPricingStrategies( supplierCost: number, marketReferencePriceCOP?: number ): { competitive: MarketPricingStrategy; balanced: MarketPricingStrategy; premium: MarketPricingStrategy; marketReferencePrice: number; } { const cost = supplierCost > 0 ? supplierCost : 85000; const marketRef = marketReferencePriceCOP && marketReferencePriceCOP > cost * 1.5 ? marketReferencePriceCOP : Math.round(cost * 4.2); // Referencia promedio en tiendas retail oficiales de Colombia // Estrategia 1: ⚡ Máxima Competitividad (Alta Rotación) // Margen de ganancia de $30.000 a $35.000 COP const competitiveTarget = cost + Math.max(30000, Math.round(cost * 0.35)); const competitivePrice = roundUpToNearest5000(competitiveTarget); const competitiveProfit = competitivePrice - cost; const competitiveSavingsCOP = Math.max(0, marketRef - competitivePrice); const competitiveSavingsPct = Math.round((competitiveSavingsCOP / marketRef) * 100); // Estrategia 2: ⚖️ Precio Recomendado DripZone (Equilibrio Margen / Ahorro Cliente) // Margen de ganancia de $45.000 a $55.000 COP const balancedTarget = cost + Math.max(45000, Math.round(cost * 0.52)); const balancedPrice = roundUpToNearest5000(balancedTarget); const balancedProfit = balancedPrice - cost; const balancedSavingsCOP = Math.max(0, marketRef - balancedPrice); const balancedSavingsPct = Math.round((balancedSavingsCOP / marketRef) * 100); // Estrategia 3: 💎 Máxima Ganancia / Exclusividad (Mayor Rentabilidad) // Margen de ganancia de $70.000 a $95.000+ COP aprovechando la alta demanda o valor de mercado const premiumTarget = Math.min( marketRef * 0.55, cost + Math.max(70000, Math.round(cost * 0.85)) ); const premiumPrice = roundUpToNearest5000(Math.max(premiumTarget, balancedPrice + 25000)); const premiumProfit = premiumPrice - cost; const premiumSavingsCOP = Math.max(0, marketRef - premiumPrice); const premiumSavingsPct = Math.round((premiumSavingsCOP / marketRef) * 100); return { marketReferencePrice: marketRef, competitive: { id: 'competitive', name: 'Alta Rotación / Gancho', badge: '⚡ Mejor Precio', description: 'Precio imbatible para arrasar en ventas rápidas y captar nuevos clientes.', price: competitivePrice, profitCOP: competitiveProfit, marginPercent: Math.round((competitiveProfit / cost) * 100), clientSavingsPercent: competitiveSavingsPct, savingsCOP: competitiveSavingsCOP, }, balanced: { id: 'balanced', name: 'Recomendado DripZone', badge: '⚖️ Equilibrio Perfecto', description: 'Excelente ganancia neta manteniendo un descuento masivo frente a tiendas oficiales.', price: balancedPrice, profitCOP: balancedProfit, marginPercent: Math.round((balancedProfit / cost) * 100), clientSavingsPercent: balancedSavingsPct, savingsCOP: balancedSavingsCOP, }, premium: { id: 'premium', name: 'Máximo Margen / Hype', badge: '💎 Mayor Ganancia', description: 'Aprovecha modelos exclusivos o de alta demanda para maximizar tu ganancia neta.', price: premiumPrice, profitCOP: premiumProfit, marginPercent: Math.round((premiumProfit / cost) * 100), clientSavingsPercent: premiumSavingsPct, savingsCOP: premiumSavingsCOP, }, }; } 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} (EUR) 💎 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, }; }