import { defineStore } from 'pinia'; const useCart = defineStore('cart', { state: () => ({ items: [], // Productos en el carrito paymentMethod: 'cash' // Método de pago por defecto }), getters: { // Total de items itemCount: (state) => state.items.reduce((sum, item) => sum + item.quantity, 0), // Subtotal (sin impuestos) subtotal: (state) => state.items.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0), // Total de impuestos tax: (state) => { return state.items.reduce((sum, item) => { const itemSubtotal = item.unit_price * item.quantity; const itemTax = (itemSubtotal * item.tax_rate) / 100; return sum + itemTax; }, 0); }, // Total final total: (state) => { const subtotal = state.items.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0); const tax = state.items.reduce((sum, item) => { const itemSubtotal = item.unit_price * item.quantity; const itemTax = (itemSubtotal * item.tax_rate) / 100; return sum + itemTax; }, 0); return subtotal + tax; }, // Verificar si el carrito está vacío isEmpty: (state) => state.items.length === 0 }, actions: { // Agregar producto al carrito // config puede incluir: serialNumbers, selectionMode (para seriales) // unit_of_measure_id, unit_price, unit_name (para equivalencias) addProduct(product, config = null) { const key = 'p:' + product.id; const existingItem = this.items.find(item => item.item_key === key); if (existingItem) { // Si ya existe y tiene seriales, abrir selector de nuevo if (existingItem.track_serials) { window.Notify.warning('Este producto requiere selección de seriales'); // Emitir evento para que Point.vue abra el selector window.dispatchEvent(new CustomEvent('open-serial-selector', { detail: { productId: product.id } })); return; } // Si NO tiene seriales, incrementar normalmente if (existingItem.quantity < product.stock) { existingItem.quantity++; } else { window.Notify.warning('No hay suficiente stock disponible'); } } else { // Determinar precio: usar el de la equivalencia si se proporcionó, si no el base const unitPrice = config?.unit_price !== undefined ? parseFloat(config.unit_price) : parseFloat(product.price?.retail_price || 0); const conversionFactor = parseFloat(config?.conversion_factor || 1); const maxStock = conversionFactor !== 1 ? Math.floor(product.stock / conversionFactor) : product.stock; // Agregar nuevo item this.items.push({ item_key: key, inventory_id: product.id, bundle_id: null, is_bundle: false, product_name: product.name, sku: product.sku, quantity: 1, unit_price: unitPrice, tax_rate: parseFloat(product.price?.tax || 16), max_stock: maxStock, // Campos para seriales track_serials: product.track_serials || false, serial_numbers: config?.serialNumbers || [], serial_selection_mode: config?.selectionMode || null, // Campos para unidad de medida base unit_of_measure: product.unit_of_measure || null, allows_decimals: product.unit_of_measure?.allows_decimals || false, // Campos para equivalencia de unidad seleccionada unit_of_measure_id: config?.unit_of_measure_id || null, unit_name: config?.unit_name || null, }); } }, // Agregar bundle/kit al carrito addBundle(bundle, serialConfig = null) { const key = 'b:' + bundle.id; const hasSerials = serialConfig && Object.keys(serialConfig.serialNumbers || {}).length > 0; const existingItem = this.items.find(item => item.item_key === key); if (existingItem) { // Bundles con seriales no se pueden incrementar, se deben agregar de nuevo if (existingItem.track_serials) { window.Notify.warning('Este paquete requiere selección de seriales. Elimínalo y agrégalo de nuevo.'); return; } if (existingItem.quantity < bundle.available_stock) { existingItem.quantity++; } else { window.Notify.warning('No hay suficiente stock disponible'); } } else { this.items.push({ item_key: key, bundle_id: bundle.id, inventory_id: null, is_bundle: true, product_name: bundle.name, sku: bundle.sku, quantity: 1, unit_price: parseFloat(bundle.price?.retail_price || 0), tax_rate: parseFloat(bundle.price?.tax || 16), max_stock: bundle.available_stock, track_serials: hasSerials, serial_numbers: hasSerials ? serialConfig.serialNumbers : {}, serial_selection_mode: null, unit_of_measure: null, allows_decimals: false, }); } }, // Agregar producto con seriales ya configurados addProductWithSerials(product, quantity, serialConfig) { const key = 'p:' + product.id; const existingItem = this.items.find(item => item.item_key === key); const newSerials = serialConfig.serialNumbers || []; if (existingItem) { // COMBINAR seriales con los existentes const combinedSerials = [...existingItem.serial_numbers, ...newSerials]; // Eliminar duplicados existingItem.serial_numbers = [...new Set(combinedSerials)]; existingItem.quantity = existingItem.serial_numbers.length; } else { // Agregar nuevo item con seriales this.items.push({ item_key: key, inventory_id: product.id, bundle_id: null, is_bundle: false, product_name: product.name, sku: product.sku, quantity: quantity, unit_price: parseFloat(product.price?.retail_price || 0), tax_rate: parseFloat(product.price?.tax || 16), max_stock: product.stock, track_serials: product.track_serials, serial_numbers: newSerials, // Campos para unidad de medida unit_of_measure: product.unit_of_measure || null, allows_decimals: product.unit_of_measure?.allows_decimals || false }); } }, // Actualizar seriales de un item updateSerials(itemKey, serialConfig) { const item = this.items.find(i => i.item_key === itemKey); if (item) { item.serial_numbers = serialConfig.serialNumbers || []; item.serial_selection_mode = serialConfig.selectionMode; } }, // Obtener seriales ya seleccionados (para excluir del selector) getSelectedSerials() { const serials = []; this.items.forEach(item => { if (!item.track_serials) return; if (item.is_bundle && item.serial_numbers) { // Bundle: serial_numbers es { inventoryId: [serial_number, ...] } Object.values(item.serial_numbers).forEach(arr => serials.push(...arr)); } else if (Array.isArray(item.serial_numbers)) { // Producto: serial_numbers es [serial_number, ...] serials.push(...item.serial_numbers); } }); return serials; }, // Verificar si un item necesita selección de seriales needsSerialSelection(itemKey) { const item = this.items.find(i => i.item_key === itemKey); if (!item || !item.track_serials) return false; // Necesita selección si es manual y no tiene suficientes seriales if (item.serial_selection_mode === 'manual') { return (item.serial_numbers?.length || 0) < item.quantity; } return false; }, // Actualizar cantidad de un item (por item_key) updateQuantity(itemKey, quantity) { const item = this.items.find(i => i.item_key === itemKey); if (item) { // Convertir a número (puede ser decimal) const numQuantity = parseFloat(quantity); if (isNaN(numQuantity) || numQuantity <= 0) { this.removeProduct(itemKey); } else if (numQuantity <= item.max_stock) { // Si NO permite decimales, redondear a entero item.quantity = item.allows_decimals ? numQuantity : Math.floor(numQuantity); } else { window.Notify.warning('No hay suficiente stock disponible'); } } }, // Remover producto o bundle (por item_key) removeProduct(itemKey) { const index = this.items.findIndex(i => i.item_key === itemKey); if (index !== -1) { this.items.splice(index, 1); } }, // Limpiar carrito clear() { this.items = []; this.paymentMethod = 'cash'; }, // Cambiar método de pago setPaymentMethod(method) { this.paymentMethod = method; } } }); export default useCart;