Improve backend

This commit is contained in:
Urtzi Alfaro
2025-11-18 07:17:17 +01:00
parent d36f2ab9af
commit 5c45164c8e
61 changed files with 9846 additions and 495 deletions

View File

@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { WizardStep, WizardStepProps } from '../../../ui/WizardModal/WizardModal';
import {
Edit3,
@@ -12,6 +13,7 @@ import {
CreditCard,
Loader2,
X,
AlertCircle,
} from 'lucide-react';
import { useTenant } from '../../../../stores/tenant.store';
import { salesService } from '../../../../api/services/sales';
@@ -24,6 +26,7 @@ import { showToast } from '../../../../utils/toast';
const EntryMethodStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNext }) => {
const data = dataRef?.current || {};
const { t } = useTranslation('wizards');
const [selectedMethod, setSelectedMethod] = useState<'manual' | 'upload'>(
data.entryMethod || 'manual'
);
@@ -40,10 +43,10 @@ const EntryMethodStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div className="space-y-6">
<div className="text-center pb-4 border-b border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
¿Cómo deseas registrar las ventas?
{t('salesEntry.entryMethod.title')}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
Elige el método que mejor se adapte a tus necesidades
{t('salesEntry.entryMethod.subtitle')}
</p>
</div>
@@ -77,23 +80,23 @@ const EntryMethodStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
</div>
<div className="flex-1">
<h4 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Entrada Manual
{t('salesEntry.entryMethod.manual.title')}
</h4>
<p className="text-sm text-[var(--text-secondary)] mb-3">
Ingresa una o varias ventas de forma individual
{t('salesEntry.entryMethod.manual.description')}
</p>
<div className="space-y-1 text-xs text-[var(--text-tertiary)]">
<p className="flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-green-600" />
Ideal para totales diarios
{t('salesEntry.entryMethod.manual.benefits.1')}
</p>
<p className="flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-green-600" />
Control detallado por venta
{t('salesEntry.entryMethod.manual.benefits.2')}
</p>
<p className="flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-green-600" />
Fácil y rápido
{t('salesEntry.entryMethod.manual.benefits.3')}
</p>
</div>
</div>
@@ -117,7 +120,7 @@ const EntryMethodStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
{/* Recommended Badge */}
<div className="absolute top-3 right-3">
<span className="px-2 py-1 text-xs rounded-full bg-gradient-to-r from-amber-100 to-orange-100 text-orange-800 font-semibold">
Recomendado para históricos
{t('salesEntry.entryMethod.file.recommended')}
</span>
</div>
@@ -136,23 +139,23 @@ const EntryMethodStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
</div>
<div className="flex-1">
<h4 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Cargar Archivo
{t('salesEntry.entryMethod.file.title')}
</h4>
<p className="text-sm text-[var(--text-secondary)] mb-3">
Importa desde Excel o CSV
{t('salesEntry.entryMethod.file.description')}
</p>
<div className="space-y-1 text-xs text-[var(--text-tertiary)]">
<p className="flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-green-600" />
Ideal para datos históricos
{t('salesEntry.entryMethod.file.benefits.1')}
</p>
<p className="flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-green-600" />
Carga masiva (cientos de registros)
{t('salesEntry.entryMethod.file.benefits.2')}
</p>
<p className="flex items-center gap-1.5">
<CheckCircle2 className="w-3.5 h-3.5 text-green-600" />
Ahorra tiempo significativo
{t('salesEntry.entryMethod.file.benefits.3')}
</p>
</div>
</div>
@@ -169,6 +172,7 @@ const EntryMethodStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNext }) => {
const data = dataRef?.current || {};
const { t } = useTranslation('wizards');
const { currentTenant } = useTenant();
const [products, setProducts] = useState<any[]>([]);
const [loadingProducts, setLoadingProducts] = useState(true);
@@ -189,7 +193,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
setProducts(finishedProducts);
} catch (err: any) {
console.error('Error fetching products:', err);
setError('Error al cargar los productos');
setError(t('salesEntry.messages.errorLoadingProducts'));
} finally {
setLoadingProducts(false);
}
@@ -249,10 +253,10 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div className="space-y-6">
<div className="text-center pb-4 border-b border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Registrar Venta Manual
{t('salesEntry.manualEntry.title')}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
Ingresa los detalles de la venta
{t('salesEntry.manualEntry.subtitle')}
</p>
</div>
@@ -261,7 +265,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
<Calendar className="w-4 h-4 inline mr-1.5" />
Fecha de Venta *
{t('salesEntry.manualEntry.fields.saleDate')} *
</label>
<input
type="date"
@@ -274,18 +278,18 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
<CreditCard className="w-4 h-4 inline mr-1.5" />
Método de Pago *
{t('salesEntry.manualEntry.fields.paymentMethod')} *
</label>
<select
value={data.paymentMethod || 'cash'}
onChange={(e) => onDataChange?.({ ...data, paymentMethod: e.target.value })}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
>
<option value="cash">Efectivo</option>
<option value="card">Tarjeta</option>
<option value="mobile">Pago Móvil</option>
<option value="transfer">Transferencia</option>
<option value="other">Otro</option>
<option value="cash">{t('salesEntry.paymentMethods.cash')}</option>
<option value="card">{t('salesEntry.paymentMethods.card')}</option>
<option value="mobile">{t('salesEntry.paymentMethods.mobile')}</option>
<option value="transfer">{t('salesEntry.paymentMethods.transfer')}</option>
<option value="other">{t('salesEntry.paymentMethods.other')}</option>
</select>
</div>
</div>
@@ -302,33 +306,33 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-[var(--text-secondary)]">
<Package className="w-4 h-4 inline mr-1.5" />
Productos Vendidos
{t('salesEntry.manualEntry.products.title')}
</label>
<button
onClick={handleAddItem}
disabled={loadingProducts || products.length === 0}
disabled={loadingProducts}
className="px-3 py-1.5 text-sm bg-[var(--color-primary)] text-white rounded-md hover:bg-[var(--color-primary)]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
+ Agregar Producto
{t('salesEntry.manualEntry.products.addProduct')}
</button>
</div>
{loadingProducts ? (
<div className="flex items-center justify-center py-8">
<Loader2 className="w-6 h-6 animate-spin text-[var(--color-primary)]" />
<span className="ml-3 text-[var(--text-secondary)]">Cargando productos...</span>
<span className="ml-3 text-[var(--text-secondary)]">{t('salesEntry.manualEntry.products.loading')}</span>
</div>
) : products.length === 0 ? (
<div className="text-center py-8 border-2 border-dashed border-[var(--border-secondary)] rounded-lg text-[var(--text-tertiary)]">
<Package className="w-8 h-8 mx-auto mb-2 opacity-50" />
<p>No hay productos terminados disponibles</p>
<p className="text-sm">Agrega productos al inventario primero</p>
<p>{t('salesEntry.manualEntry.products.noFinishedProducts')}</p>
<p className="text-sm">{t('salesEntry.manualEntry.products.addToInventory')}</p>
</div>
) : (data.salesItems || []).length === 0 ? (
<div className="text-center py-8 border-2 border-dashed border-[var(--border-secondary)] rounded-lg text-[var(--text-tertiary)]">
<Package className="w-8 h-8 mx-auto mb-2 opacity-50" />
<p>No hay productos agregados</p>
<p className="text-sm">Haz clic en "Agregar Producto" para comenzar</p>
<p>{t('salesEntry.manualEntry.products.noProductsAdded')}</p>
<p className="text-sm">{t('salesEntry.manualEntry.products.clickToBegin')}</p>
</div>
) : (
<div className="space-y-2">
@@ -344,7 +348,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
onChange={(e) => handleUpdateItem(index, 'productId', e.target.value)}
className="w-full px-2 py-1.5 text-sm border border-[var(--border-secondary)] rounded focus:outline-none focus:ring-1 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
>
<option value="">Seleccionar producto...</option>
<option value="">{t('salesEntry.manualEntry.products.selectProduct')}</option>
{products.map((product: any) => (
<option key={product.id} value={product.id}>
{product.name} - {(product.average_cost || product.last_purchase_price || 0).toFixed(2)}
@@ -355,7 +359,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div className="col-span-4 sm:col-span-2">
<input
type="number"
placeholder="Cant."
placeholder={t('salesEntry.manualEntry.products.quantity')}
value={item.quantity}
onChange={(e) =>
handleUpdateItem(index, 'quantity', parseFloat(e.target.value) || 0)
@@ -368,7 +372,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
<div className="col-span-4 sm:col-span-2">
<input
type="number"
placeholder="Precio"
placeholder={t('salesEntry.manualEntry.products.price')}
value={item.unitPrice}
onChange={(e) =>
handleUpdateItem(index, 'unitPrice', parseFloat(e.target.value) || 0)
@@ -399,7 +403,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
{(data.salesItems || []).length > 0 && (
<div className="pt-3 border-t border-[var(--border-primary)] text-right">
<span className="text-lg font-bold text-[var(--text-primary)]">
Total: {calculateTotal().toFixed(2)}
{t('salesEntry.manualEntry.products.total')} {calculateTotal().toFixed(2)}
</span>
</div>
)}
@@ -408,12 +412,12 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
{/* Notes */}
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Notas (Opcional)
{t('salesEntry.manualEntry.fields.notes')}
</label>
<textarea
value={data.notes || ''}
onChange={(e) => onDataChange?.({ ...data, notes: e.target.value })}
placeholder="Información adicional sobre esta venta..."
placeholder={t('salesEntry.manualEntry.fields.notesPlaceholder')}
rows={3}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)] text-sm"
/>
@@ -428,6 +432,7 @@ const ManualEntryStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onN
const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNext }) => {
const data = dataRef?.current || {};
const { t } = useTranslation('wizards');
const { currentTenant } = useTenant();
const [validating, setValidating] = useState(false);
const [importing, setImporting] = useState(false);
@@ -461,7 +466,7 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
setValidationResult(result);
} catch (err: any) {
console.error('Error validating file:', err);
setError(err.response?.data?.detail || 'Error al validar el archivo');
setError(err.response?.data?.detail || t('salesEntry.messages.errorValidatingFile'));
} finally {
setValidating(false);
}
@@ -479,7 +484,7 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
onNext?.();
} catch (err: any) {
console.error('Error importing file:', err);
setError(err.response?.data?.detail || 'Error al importar el archivo');
setError(err.response?.data?.detail || t('salesEntry.messages.errorImportingFile'));
} finally {
setImporting(false);
}
@@ -501,7 +506,7 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
document.body.removeChild(a);
} catch (err: any) {
console.error('Error downloading template:', err);
setError('Error al descargar la plantilla');
setError(t('salesEntry.messages.errorValidatingFile'));
} finally {
setDownloadingTemplate(false);
}
@@ -511,10 +516,10 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
<div className="space-y-6">
<div className="text-center pb-4 border-b border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Cargar Archivo de Ventas
{t('salesEntry.fileUpload.title')}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
Importa tus ventas desde Excel o CSV
{t('salesEntry.fileUpload.subtitle')}
</p>
</div>
@@ -535,12 +540,12 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
{downloadingTemplate ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Descargando...
{t('salesEntry.fileUpload.downloading')}
</>
) : (
<>
<Download className="w-4 h-4" />
Descargar Plantilla CSV
{t('salesEntry.fileUpload.downloadTemplate')}
</>
)}
</button>
@@ -551,10 +556,10 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
<div className="border-2 border-dashed border-[var(--border-secondary)] rounded-xl p-8 text-center bg-[var(--bg-secondary)]/30">
<FileSpreadsheet className="w-16 h-16 mx-auto mb-4 text-[var(--color-primary)]/50" />
<h4 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Arrastra un archivo aquí
{t('salesEntry.fileUpload.dragDrop.title')}
</h4>
<p className="text-sm text-[var(--text-secondary)] mb-4">
o haz clic para seleccionar
{t('salesEntry.fileUpload.dragDrop.subtitle')}
</p>
<label className="inline-block">
<input
@@ -564,11 +569,11 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
className="hidden"
/>
<span className="px-6 py-2 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary)]/90 transition-colors cursor-pointer inline-block">
Seleccionar Archivo
{t('salesEntry.fileUpload.dragDrop.button')}
</span>
</label>
<p className="text-xs text-[var(--text-tertiary)] mt-3">
Formatos soportados: CSV, Excel (.xlsx, .xls)
{t('salesEntry.fileUpload.dragDrop.supportedFormats')}
</p>
</div>
) : (
@@ -595,14 +600,14 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
{validationResult && (
<div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<p className="text-sm text-blue-800 font-medium mb-2">
Archivo validado correctamente
{t('salesEntry.fileUpload.validated.title')}
</p>
<div className="text-xs text-blue-700 space-y-1">
<p>Registros encontrados: {validationResult.total_rows || 0}</p>
<p>Registros válidos: {validationResult.valid_rows || 0}</p>
<p>{t('salesEntry.fileUpload.validated.recordsFound')} {validationResult.total_rows || 0}</p>
<p>{t('salesEntry.fileUpload.validated.validRecords')} {validationResult.valid_rows || 0}</p>
{validationResult.errors?.length > 0 && (
<p className="text-red-600">
Errores: {validationResult.errors.length}
{t('salesEntry.fileUpload.validated.errors')} {validationResult.errors.length}
</p>
)}
</div>
@@ -620,12 +625,12 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
{validating ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Validando...
{t('salesEntry.fileUpload.validating')}
</>
) : (
<>
<CheckCircle2 className="w-4 h-4" />
Validar Archivo
{t('salesEntry.fileUpload.validateButton')}
</>
)}
</button>
@@ -638,12 +643,12 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
{importing ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Importando...
{t('salesEntry.fileUpload.importing')}
</>
) : (
<>
<Upload className="w-4 h-4" />
Importar Datos
{t('salesEntry.fileUpload.importButton')}
</>
)}
</button>
@@ -652,9 +657,9 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
)}
<div className="text-center text-sm text-[var(--text-tertiary)]">
<p>El archivo debe contener las columnas:</p>
<p>{t('salesEntry.fileUpload.instructions.title')}</p>
<p className="font-mono text-xs mt-1">
fecha, producto, cantidad, precio_unitario, método_pago
{t('salesEntry.fileUpload.instructions.columns')}
</p>
</div>
</div>
@@ -667,6 +672,7 @@ const FileUploadStep: React.FC<WizardStepProps> = ({ dataRef, onDataChange, onNe
const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
const data = dataRef?.current || {};
const { t } = useTranslation('wizards');
const isManual = data.entryMethod === 'manual';
const isUpload = data.entryMethod === 'upload';
@@ -680,10 +686,10 @@ const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
</div>
</div>
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Revisar y Confirmar
{t('salesEntry.review.title')}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
Verifica que toda la información sea correcta
{t('salesEntry.review.subtitle')}
</p>
</div>
@@ -693,11 +699,11 @@ const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
<div className="p-4 bg-[var(--bg-secondary)]/50 rounded-lg">
<div className="grid grid-cols-2 gap-3 text-sm">
<div>
<span className="text-[var(--text-secondary)]">Fecha:</span>
<span className="text-[var(--text-secondary)]">{t('salesEntry.review.fields.date')}</span>
<p className="font-semibold text-[var(--text-primary)]">{data.saleDate}</p>
</div>
<div>
<span className="text-[var(--text-secondary)]">Método de Pago:</span>
<span className="text-[var(--text-secondary)]">{t('salesEntry.review.fields.paymentMethod')}</span>
<p className="font-semibold text-[var(--text-primary)] capitalize">
{data.paymentMethod}
</p>
@@ -708,7 +714,7 @@ const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
{/* Items */}
<div>
<h4 className="text-sm font-semibold text-[var(--text-secondary)] mb-2">
Productos ({(data.salesItems || []).length})
{t('salesEntry.review.fields.products')} ({(data.salesItems || []).length})
</h4>
<div className="space-y-2">
{(data.salesItems || []).map((item: any) => (
@@ -735,7 +741,7 @@ const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
{/* Total */}
<div className="p-4 bg-gradient-to-r from-[var(--color-primary)]/5 to-[var(--color-primary)]/10 rounded-lg border-2 border-[var(--color-primary)]/20">
<div className="flex justify-between items-center">
<span className="text-lg font-semibold text-[var(--text-primary)]">Total:</span>
<span className="text-lg font-semibold text-[var(--text-primary)]">{t('salesEntry.review.fields.total')}</span>
<span className="text-2xl font-bold text-[var(--color-primary)]">
{data.totalAmount?.toFixed(2)}
</span>
@@ -745,7 +751,7 @@ const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
{/* Notes */}
{data.notes && (
<div className="p-3 bg-[var(--bg-secondary)]/30 rounded-lg">
<p className="text-sm text-[var(--text-secondary)] mb-1">Notas:</p>
<p className="text-sm text-[var(--text-secondary)] mb-1">{t('salesEntry.review.fields.notes')}</p>
<p className="text-sm text-[var(--text-primary)]">{data.notes}</p>
</div>
)}
@@ -756,11 +762,11 @@ const ReviewStep: React.FC<WizardStepProps> = ({ dataRef }) => {
<div className="space-y-4">
<div className="p-4 bg-green-50 border border-green-200 rounded-lg">
<p className="text-green-800 font-semibold mb-2">
Archivo importado correctamente
{t('salesEntry.review.imported.title')}
</p>
<div className="text-sm text-green-700 space-y-1">
<p>Registros importados: {data.importResult.successful_imports || 0}</p>
<p>Registros fallidos: {data.importResult.failed_imports || 0}</p>
<p>{t('salesEntry.review.imported.recordsImported')} {data.importResult.successful_imports || 0}</p>
<p>{t('salesEntry.review.imported.recordsFailed')} {data.importResult.failed_imports || 0}</p>
</div>
</div>
</div>
@@ -784,8 +790,8 @@ export const SalesEntryWizardSteps = (
const steps: WizardStep[] = [
{
id: 'entry-method',
title: 'Método de Entrada',
description: 'Elige cómo registrar las ventas',
title: 'salesEntry.steps.entryMethod',
description: 'salesEntry.steps.entryMethodDescription',
component: EntryMethodStep,
},
];
@@ -793,8 +799,8 @@ export const SalesEntryWizardSteps = (
if (entryMethod === 'manual') {
steps.push({
id: 'manual-entry',
title: 'Ingresar Datos',
description: 'Registra los detalles de la venta',
title: 'salesEntry.steps.manualEntry',
description: 'salesEntry.steps.manualEntryDescription',
component: ManualEntryStep,
validate: () => {
const data = dataRef.current;
@@ -804,16 +810,16 @@ export const SalesEntryWizardSteps = (
} else if (entryMethod === 'upload') {
steps.push({
id: 'file-upload',
title: 'Cargar Archivo',
description: 'Importa ventas desde archivo',
title: 'salesEntry.steps.fileUpload',
description: 'salesEntry.steps.fileUploadDescription',
component: FileUploadStep,
});
}
steps.push({
id: 'review',
title: 'Revisar',
description: 'Confirma los datos antes de guardar',
title: 'salesEntry.steps.review',
description: 'salesEntry.steps.reviewDescription',
component: ReviewStep,
validate: async () => {
const { useTenant } = await import('../../../../stores/tenant.store');
@@ -824,6 +830,7 @@ export const SalesEntryWizardSteps = (
const { currentTenant } = useTenant.getState();
if (!currentTenant?.id) {
const { showToast } = await import('../../../../utils/toast');
showToast.error('No se pudo obtener información del tenant');
return false;
}
@@ -850,10 +857,12 @@ export const SalesEntryWizardSteps = (
}
}
const { showToast } = await import('../../../../utils/toast');
showToast.success('Registro de ventas guardado exitosamente');
return true;
} catch (err: any) {
console.error('Error saving sales data:', err);
const { showToast } = await import('../../../../utils/toast');
const errorMessage = err.response?.data?.detail || 'Error al guardar los datos de ventas';
showToast.error(errorMessage);
return false;