/** * BOGO Service - Handles Buy One Get One logic * * Processes BOGO rules from the API and calculates free items/discounts */ const BogoService = { /** * Check if a product has any BOGO rules * @param {Object} product * @returns {boolean} */ hasBogoRules(product) { return product.bogo_rules && product.bogo_rules.length > 0; }, /** * Get the primary BOGO rule for a product (first one) * @param {Object} product * @returns {Object|null} */ getPrimaryBogoRule(product) { if (!this.hasBogoRules(product)) return null; return product.bogo_rules[0]; }, /** * Get BOGO label for display * @param {Object} product * @returns {string|null} */ getBogoLabel(product) { const rule = this.getPrimaryBogoRule(product); return rule ? rule.label : null; }, /** * Calculate BOGO discount for cart items * * @param {Array} cartItems - Array of cart items with product data * @param {Array} products - Full products list with BOGO rules * @returns {Object} { freeItems: [], discountAmount: number } */ calculateBogoForCart(cartItems, products) { const result = { freeItems: [], discountAmount: 0, appliedRules: [] }; // Group cart items by product ID const itemsByProduct = this.groupCartItemsByProduct(cartItems); // Check each product group for BOGO eligibility for (const [productId, items] of Object.entries(itemsByProduct)) { const product = products.find(p => p.id == productId); if (!product || !this.hasBogoRules(product)) continue; const rule = this.getPrimaryBogoRule(product); const quantity = items.length; // Calculate how many free items based on the rule const bogoResult = this.applyBogoRule(rule, quantity, product, products); if (bogoResult.freeItems.length > 0 || bogoResult.discountAmount > 0) { result.freeItems.push(...bogoResult.freeItems); result.discountAmount += bogoResult.discountAmount; result.appliedRules.push({ rule, productId: parseInt(productId), productName: product.name, ...bogoResult }); } } return result; }, /** * Group cart items by product ID * @param {Array} cartItems * @returns {Object} */ groupCartItemsByProduct(cartItems) { return cartItems.reduce((groups, item) => { const id = item.id; if (!groups[id]) groups[id] = []; groups[id].push(item); return groups; }, {}); }, /** * Apply a BOGO rule to calculate free items/discount * * @param {Object} rule - BOGO rule from API * @param {number} quantity - Number of items in cart * @param {Object} product - The product * @param {Array} products - All products (for Buy X Get Y) * @returns {Object} */ applyBogoRule(rule, quantity, product, products) { const result = { freeItems: [], discountAmount: 0, freeQuantity: 0 }; if (quantity < rule.buy_qty) { return result; // Not enough items to trigger BOGO } // Calculate how many times the rule applies let timesApplied = 1; if (rule.recursive) { timesApplied = Math.floor(quantity / rule.buy_qty); } const freeQty = timesApplied * rule.get_qty; result.freeQuantity = freeQty; if (rule.type === 'buy_x_get_x') { // Same product is free or discounted if (rule.discount_type === 'free_product' || rule.discount_value >= 100) { // Fully free - automatically add to cart result.discountAmount = freeQty * parseFloat(product.price); result.freeItems.push({ id: product.id, name: product.name + ' (GRATIS)', price: '0.00', originalPrice: product.price, isFree: true, ruleId: rule.rule_id }); } // For percentage and flat discounts, we DON'T automatically add items // These are handled manually via addBogoDiscountedItem() in cart.js // The user clicks a button to add the discounted item } else if (rule.type === 'buy_x_get_y' && rule.get_product_ids) { // Different product is free for (const freeProductId of rule.get_product_ids) { const freeProduct = products.find(p => p.id == freeProductId); if (!freeProduct) continue; if (rule.discount_type === 'free_product' || rule.discount_value >= 100) { result.discountAmount += freeQty * parseFloat(freeProduct.price); result.freeItems.push({ id: freeProduct.id, name: freeProduct.name + ' (GRATIS)', price: '0.00', originalPrice: freeProduct.price, isFree: true, ruleId: rule.rule_id }); } } } return result; }, /** * Get suggested free items for a product (for display before adding to cart) * * @param {Object} product * @param {Array} products * @returns {Array} */ getSuggestedFreeItems(product, products) { if (!this.hasBogoRules(product)) return []; const rule = this.getPrimaryBogoRule(product); const suggestions = []; if (rule.type === 'buy_x_get_y' && rule.get_product_ids) { for (const freeProductId of rule.get_product_ids) { const freeProduct = products.find(p => p.id == freeProductId); if (freeProduct) { suggestions.push({ ...freeProduct, bogoLabel: `Gratis bij ${rule.buy_qty}x ${product.name}` }); } } } return suggestions; } };