-
+
props.show, (newVal) => {
Nivel Actual
-
{{ stats.current_tier.tier_name }}
+
{{ stats.current_tier.name }}
Descuento
-
{{ stats.current_tier.discount_percentage }}%
+
{{ stats.current_tier.discount }}%
-
+
@@ -177,19 +177,6 @@ watch(() => props.show, (newVal) => {
-
-
-
-
-
-
Descuentos Recibidos
-
- {{ formatCurrency(stats.total_discounts_received) }}
-
-
-
-
-
diff --git a/src/pages/POS/Point.vue b/src/pages/POS/Point.vue
index b2d7326..d932cc8 100644
--- a/src/pages/POS/Point.vue
+++ b/src/pages/POS/Point.vue
@@ -142,7 +142,7 @@ const handleCodeDetected = async (barcode) => {
try {
window.Notify.info('Buscando producto...');
- // Buscar producto por código de barras usando el API
+ // Buscar producto por código de barras
const response = await fetch(apiURL(`inventario?q=${encodeURIComponent(barcode)}`), {
headers: {
'Authorization': `Bearer ${sessionStorage.token}`,
@@ -185,7 +185,7 @@ const handleConfirmSale = async (paymentData) => {
user_id: page.user.id,
subtotal: parseFloat(cart.subtotal.toFixed(2)),
tax: parseFloat(cart.tax.toFixed(2)),
- total: parseFloat(cart.total.toFixed(2)),
+ total: parseFloat(cart.total.toFixed(2)), // El backend recalculará con descuento
payment_method: paymentData.paymentMethod,
items: cart.items.map(item => ({
inventory_id: item.inventory_id,
@@ -196,6 +196,12 @@ const handleConfirmSale = async (paymentData) => {
serial_numbers: item.track_serials ? (item.serial_numbers || []) : undefined
}))
};
+
+ // Agregar client_number si se seleccionó un cliente
+ if (paymentData.clientNumber) {
+ saleData.client_number = paymentData.clientNumber;
+ }
+
// Agregar cash_received si es pago en efectivo
if (paymentData.paymentMethod === 'cash' && paymentData.cashReceived) {
saleData.cash_received = parseFloat(paymentData.cashReceived.toFixed(2));
@@ -205,6 +211,29 @@ const handleConfirmSale = async (paymentData) => {
window.Notify.info('Procesando venta...');
const response = await salesService.createSale(saleData);
+ // Mostrar información del descuento aplicado si hay
+ if (response.discount_amount && parseFloat(response.discount_amount) > 0) {
+ window.Notify.success(
+ `¡Descuento aplicado! -$${parseFloat(response.discount_amount).toFixed(2)} (${parseFloat(response.discount_percentage).toFixed(2)}%)`,
+ '',
+ 5
+ );
+ }
+
+ // Detectar upgrade/downgrade de tier
+ if (paymentData.clientData?.tier?.tier_name && response.client?.tier?.tier_name) {
+ const oldTier = paymentData.clientData.tier.tier_name;
+ const newTier = response.client.tier.tier_name;
+
+ if (oldTier !== newTier) {
+ window.Notify.success(
+ `¡El cliente subió de nivel! ${oldTier} → ${newTier} (${response.client.tier.discount_percentage}% descuento)`,
+ '',
+ 8
+ );
+ }
+ }
+
// Mostrar mensaje de éxito con cambio si es efectivo
if (paymentData.paymentMethod === 'cash' && response.change !== undefined) {
window.Notify.success(
@@ -250,12 +279,15 @@ const handleConfirmSale = async (paymentData) => {
// Recargar productos para actualizar stock
searcher.search();
- showClientModal.value = true;
+ // Solo mostrar modal de registro de cliente si NO se asoció un cliente a la venta
+ if (!paymentData.clientNumber) {
+ showClientModal.value = true;
+ }
} catch (error) {
console.error('Error en venta:', error);
- // Manejar errores de validación del backend (422)
+ // Manejar errores de validación
if (error.status === 422 && error.errors) {
const errorMessages = Object.values(error.errors).flat();
errorMessages.forEach(msg => window.Notify.error(msg));
diff --git a/src/pages/POS/Tiers/Create.vue b/src/pages/POS/Tiers/Create.vue
index b545408..532e6af 100644
--- a/src/pages/POS/Tiers/Create.vue
+++ b/src/pages/POS/Tiers/Create.vue
@@ -16,8 +16,8 @@ const props = defineProps({
/** Formulario */
const form = useForm({
tier_name: '',
- min_purchases_amount: '',
- max_purchases_amount: '',
+ min_purchase_amount: '',
+ max_purchase_amount: '',
discount_percentage: '',
is_active: true,
});
@@ -84,50 +84,59 @@ const closeModal = () => {
MONTO MÍNIMO ACUMULADO
-
+
- MONTO MÁXIMO ACUMULADO
+ MONTO MÁXIMO ACUMULADO (opcional)
-
+
-
+
- DESCUENTO
+ DESCUENTO (%)
-
diff --git a/src/pages/POS/Tiers/Delete.vue b/src/pages/POS/Tiers/Delete.vue
index 205f542..13ace52 100644
--- a/src/pages/POS/Tiers/Delete.vue
+++ b/src/pages/POS/Tiers/Delete.vue
@@ -37,7 +37,7 @@ const handleClose = () => {
- Eliminar Cliente
+ Eliminar Nivel de Cliente
{
- ¿Estás seguro de que deseas eliminar este cliente?
+ ¿Estás seguro de que deseas eliminar este nivel de cliente?
- {{ client.name }}
+ {{ client.tier_name }}
+
+
Descuento: {{ parseFloat(client.discount_percentage).toFixed(2) }}%
+
Rango: ${{ parseFloat(client.min_purchase_amount).toFixed(2) }} - {{ client.max_purchase_amount ? '$' + parseFloat(client.max_purchase_amount).toFixed(2) : 'Sin límite' }}
+
+ Clientes asignados: {{ client.clients_count || 0 }}
+
+
+
+
+
+ Este nivel tiene {{ client.clients_count }} cliente(s) asignado(s). No podrás eliminarlo hasta que todos los clientes sean reasignados a otro nivel.
+
+
+
@@ -86,10 +100,11 @@ const handleClose = () => {
- Eliminar Cliente
+ Eliminar Nivel
diff --git a/src/pages/POS/Tiers/Edit.vue b/src/pages/POS/Tiers/Edit.vue
index 6b7f38e..fdb87b7 100644
--- a/src/pages/POS/Tiers/Edit.vue
+++ b/src/pages/POS/Tiers/Edit.vue
@@ -17,23 +17,23 @@ const props = defineProps({
/** Formulario */
const form = useForm({
- name: '',
- email: '',
- phone: '',
- address: '',
- rfc: '',
+ tier_name: '',
+ min_purchase_amount: '',
+ max_purchase_amount: '',
+ discount_percentage: '',
+ is_active: true,
});
/** Métodos */
-const updateClient = () => {
- form.put(apiURL(`clients/${props.client.id}`), {
+const updateTier = () => {
+ form.put(apiURL(`client-tiers/${props.client.id}`), {
onSuccess: () => {
- window.Notify.success('Cliente actualizado exitosamente');
+ window.Notify.success('Nivel de cliente actualizado exitosamente');
emit('updated');
closeModal();
},
onError: () => {
- window.Notify.error('Error al actualizar el cliente');
+ window.Notify.error('Error al actualizar el nivel de cliente');
}
});
};
@@ -44,13 +44,13 @@ const closeModal = () => {
};
/** Observadores */
-watch(() => props.client, (newClient) => {
- if (newClient) {
- form.name = newClient.name || '';
- form.email = newClient.email || '';
- form.phone = newClient.phone || '';
- form.address = newClient.address || '';
- form.rfc = newClient.rfc || '';
+watch(() => props.client, (newTier) => {
+ if (newTier) {
+ form.tier_name = newTier.tier_name || '';
+ form.min_purchase_amount = newTier.min_purchase_amount || '';
+ form.max_purchase_amount = newTier.max_purchase_amount || '';
+ form.discount_percentage = newTier.discount_percentage || '';
+ form.is_active = newTier.is_active ?? true;
}
}, { immediate: true });
@@ -61,7 +61,7 @@ watch(() => props.client, (newClient) => {
- Editar Cliente
+ Editar Nivel de Cliente
props.client, (newClient) => {
-