248 lines
10 KiB
JavaScript

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;