Refactor supplier and unit of measure components and services
- Updated SupplierModal.vue to include new fields for supplier information and improved form validation. - Enhanced Suppliers.vue to handle loading states and improved supplier data fetching logic. - Removed old supplierServices and unitOfMeasureService files, replacing them with updated service files that align with new interfaces. - Created new interfaces for suppliers and unit of measure to standardize data handling across the application. - Adjusted the store files to reference the new service files and interfaces. - Improved error handling and logging in service methods for better debugging.
This commit is contained in:
parent
df0b707064
commit
522235d441
@ -18,7 +18,7 @@ import ConfirmDialog from 'primevue/confirmdialog';
|
||||
import ProgressSpinner from 'primevue/progressspinner';
|
||||
import { useUnitOfMeasureStore } from '../stores/unitOfMeasureStore';
|
||||
import { unitTypesService } from '../services/unitsTypes';
|
||||
import type { UnitOfMeasure, CreateUnitOfMeasureData } from '../types/unitOfMeasure';
|
||||
import type { UnitOfMeasure, CreateUnitOfMeasureData } from '../types/unit-measure.interfaces';
|
||||
|
||||
const router = useRouter();
|
||||
const toast = useToast();
|
||||
|
||||
@ -1,378 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="es">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<title>System Document Types Management</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
|
||||
rel="stylesheet" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
||||
rel="stylesheet" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
||||
rel="stylesheet" />
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"primary": "#195de6",
|
||||
"background-light": "#f6f6f8",
|
||||
"background-dark": "#111621",
|
||||
"neutral-dark": "#1a202c",
|
||||
"neutral-sidebar": "#0f172a",
|
||||
},
|
||||
fontFamily: {
|
||||
"display": ["Inter", "sans-serif"]
|
||||
},
|
||||
borderRadius: {
|
||||
"DEFAULT": "0.25rem",
|
||||
"lg": "0.5rem",
|
||||
"xl": "0.75rem",
|
||||
"full": "9999px"
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-background-light dark:bg-background-dark font-display text-slate-900 overflow-hidden">
|
||||
<div class="flex h-screen w-full">
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-64 bg-neutral-sidebar flex flex-col h-full border-r border-slate-800 text-slate-300">
|
||||
<div class="p-6 flex items-center gap-3 border-b border-slate-800">
|
||||
<div class="bg-primary p-1.5 rounded-lg">
|
||||
<span class="material-symbols-outlined text-white">inventory_2</span>
|
||||
</div>
|
||||
<h2 class="text-white text-lg font-bold tracking-tight">LogisPro</h2>
|
||||
</div>
|
||||
<nav class="flex-1 px-4 py-6 space-y-2 overflow-y-auto">
|
||||
<div class="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 px-2">Navegación</div>
|
||||
<a class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-slate-800 transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-xl">dashboard</span>
|
||||
<span class="text-sm font-medium">Dashboard</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-slate-800 transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-xl">warehouse</span>
|
||||
<span class="text-sm font-medium">Almacén</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-slate-800 transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-xl">factory</span>
|
||||
<span class="text-sm font-medium">Producción</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-slate-800 transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-xl">analytics</span>
|
||||
<span class="text-sm font-medium">Reportes</span>
|
||||
</a>
|
||||
<div class="pt-6 text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2 px-2">Sistema</div>
|
||||
<a class="flex items-center gap-3 px-3 py-2 rounded-lg bg-primary text-white transition-colors"
|
||||
href="#">
|
||||
<span class="material-symbols-outlined text-xl">settings</span>
|
||||
<span class="text-sm font-medium">Configuración</span>
|
||||
</a>
|
||||
<a class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-slate-800 transition-colors" href="#">
|
||||
<span class="material-symbols-outlined text-xl">group</span>
|
||||
<span class="text-sm font-medium">Usuarios</span>
|
||||
</a>
|
||||
</nav>
|
||||
<div class="p-4 border-t border-slate-800">
|
||||
<div class="flex items-center gap-3 px-3 py-2">
|
||||
<div class="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center text-primary font-bold text-xs"
|
||||
data-alt="User avatar placeholder">AD</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-semibold text-white truncate">Admin Warehouse</p>
|
||||
<p class="text-xs text-slate-500 truncate">admin@logispro.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 flex flex-col min-w-0 bg-background-light overflow-hidden">
|
||||
<!-- Top Header -->
|
||||
<header class="h-16 bg-white border-b border-slate-200 flex items-center justify-between px-8 shrink-0">
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="material-symbols-outlined text-slate-400">menu</span>
|
||||
<h1 class="text-xl font-semibold text-slate-800">Administración de Documentos</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button class="p-2 text-slate-500 hover:bg-slate-100 rounded-full relative">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
<span
|
||||
class="absolute top-2 right-2 w-2 h-2 bg-red-500 rounded-full border-2 border-white"></span>
|
||||
</button>
|
||||
<button class="p-2 text-slate-500 hover:bg-slate-100 rounded-full">
|
||||
<span class="material-symbols-outlined">help_outline</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<!-- Scrollable Body -->
|
||||
<div class="flex-1 overflow-y-auto p-8 space-y-6">
|
||||
<!-- Breadcrumbs & Action -->
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div>
|
||||
<nav class="flex text-xs font-semibold uppercase tracking-wider text-slate-500 space-x-2">
|
||||
<a class="hover:text-primary transition-colors" href="#">Inicio</a>
|
||||
<span>/</span>
|
||||
<a class="hover:text-primary transition-colors" href="#">Configuración</a>
|
||||
<span>/</span>
|
||||
<span class="text-primary/70">Tipos de Documento</span>
|
||||
</nav>
|
||||
<h2 class="text-3xl font-black text-slate-900 mt-1 tracking-tight">Tipos de Documento</h2>
|
||||
<p class="text-slate-500 text-sm mt-1">Gestione los formatos y folios permitidos para los
|
||||
módulos de almacén y producción.</p>
|
||||
</div>
|
||||
<button
|
||||
class="bg-primary text-white px-6 py-2.5 rounded-lg font-bold text-sm shadow-lg shadow-primary/20 hover:bg-primary/90 transition-all flex items-center gap-2 shrink-0 self-start md:self-center">
|
||||
<span class="material-symbols-outlined text-lg">add</span>
|
||||
Agregar Nuevo Documento
|
||||
</button>
|
||||
</div>
|
||||
<!-- Filters -->
|
||||
<div class="bg-white p-4 rounded-xl border border-slate-200 shadow-sm flex flex-wrap gap-4 items-end">
|
||||
<div class="flex-1 min-w-[240px]">
|
||||
<label class="block text-xs font-bold text-slate-700 uppercase mb-1.5 ml-1">Buscar por
|
||||
nombre</label>
|
||||
<div class="relative">
|
||||
<span class="material-symbols-outlined absolute left-3 top-2.5 text-slate-400">search</span>
|
||||
<input
|
||||
class="w-full pl-10 pr-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-all text-sm"
|
||||
placeholder="Ej. Cotización, Factura..." type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-64">
|
||||
<label class="block text-xs font-bold text-slate-700 uppercase mb-1.5 ml-1">Módulo</label>
|
||||
<select
|
||||
class="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-all text-sm appearance-none bg-white">
|
||||
<option>Todos los módulos</option>
|
||||
<option>Almacén</option>
|
||||
<option>Producción</option>
|
||||
<option>Ventas</option>
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
class="px-4 py-2 text-primary font-bold text-sm hover:bg-primary/5 rounded-lg transition-colors flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-lg">filter_alt</span>
|
||||
Más Filtros
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-slate-500 font-medium text-sm hover:bg-slate-100 rounded-lg transition-colors">
|
||||
Limpiar
|
||||
</button>
|
||||
</div>
|
||||
<!-- Table Card -->
|
||||
<div class="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead class="bg-slate-50 border-b border-slate-200">
|
||||
<tr>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider">ID
|
||||
</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider">
|
||||
Nombre</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider">
|
||||
Módulo ID</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider">Tipo
|
||||
ID</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider">
|
||||
Folio Actual</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider">
|
||||
Fecha Creación</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-600 uppercase tracking-wider text-right">
|
||||
Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
<tr class="hover:bg-primary/5 transition-colors group">
|
||||
<td class="px-6 py-4 text-sm font-semibold text-slate-400">#1024</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-emerald-500"></span>
|
||||
<span class="text-sm font-semibold text-slate-700">Cotización</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<span
|
||||
class="px-2.5 py-1 rounded-full bg-slate-100 text-slate-600 text-[10px] font-bold uppercase">ALM-01</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">CT-DOC</td>
|
||||
<td class="px-6 py-4 font-mono text-sm text-slate-600">000542</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">12/10/2023</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-primary transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">edit</span>
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-red-500 transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">archive</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-primary/5 transition-colors group">
|
||||
<td class="px-6 py-4 text-sm font-semibold text-slate-400">#1025</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-emerald-500"></span>
|
||||
<span class="text-sm font-semibold text-slate-700">Orden de Compra</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<span
|
||||
class="px-2.5 py-1 rounded-full bg-slate-100 text-slate-600 text-[10px] font-bold uppercase">PRO-05</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">OC-PRO</td>
|
||||
<td class="px-6 py-4 font-mono text-sm text-slate-600">000128</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">15/10/2023</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-primary transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">edit</span>
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-red-500 transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">archive</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-primary/5 transition-colors group">
|
||||
<td class="px-6 py-4 text-sm font-semibold text-slate-400">#1026</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-emerald-500"></span>
|
||||
<span class="text-sm font-semibold text-slate-700">Vale de Salida</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<span
|
||||
class="px-2.5 py-1 rounded-full bg-slate-100 text-slate-600 text-[10px] font-bold uppercase">ALM-01</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">VS-INV</td>
|
||||
<td class="px-6 py-4 font-mono text-sm text-slate-600">000891</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">18/10/2023</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-primary transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">edit</span>
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-red-500 transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">archive</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="hover:bg-primary/5 transition-colors group">
|
||||
<td class="px-6 py-4 text-sm font-semibold text-slate-400">#1027</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-amber-400"></span>
|
||||
<span class="text-sm font-semibold text-slate-700">Ticket de
|
||||
Mantenimiento</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<span
|
||||
class="px-2.5 py-1 rounded-full bg-slate-100 text-slate-600 text-[10px] font-bold uppercase">PRO-02</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">TM-MAI</td>
|
||||
<td class="px-6 py-4 font-mono text-sm text-slate-600">000042</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">20/10/2023</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-primary transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">edit</span>
|
||||
</button>
|
||||
<button
|
||||
class="p-1.5 text-slate-400 hover:text-red-500 transition-colors hover:bg-white rounded-md shadow-none group-hover:shadow-sm">
|
||||
<span class="material-symbols-outlined text-lg">archive</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 bg-slate-50 border-t border-slate-200 flex items-center justify-between">
|
||||
<p class="text-sm text-slate-500">Mostrando <span class="font-bold text-slate-700">1-4</span> de
|
||||
<span class="font-bold text-slate-700">24</span> tipos de documento</p>
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
class="p-2 rounded hover:bg-slate-200 text-slate-500 transition-colors disabled:opacity-30"
|
||||
disabled="">
|
||||
<span class="material-symbols-outlined text-lg">chevron_left</span>
|
||||
</button>
|
||||
<button class="w-8 h-8 rounded bg-primary text-white text-sm font-bold shadow-sm">1</button>
|
||||
<button
|
||||
class="w-8 h-8 rounded hover:bg-slate-200 text-slate-600 text-sm font-medium transition-colors">2</button>
|
||||
<button
|
||||
class="w-8 h-8 rounded hover:bg-slate-200 text-slate-600 text-sm font-medium transition-colors">3</button>
|
||||
<span class="px-1 text-slate-400">...</span>
|
||||
<button
|
||||
class="w-8 h-8 rounded hover:bg-slate-200 text-slate-600 text-sm font-medium transition-colors">6</button>
|
||||
<button class="p-2 rounded hover:bg-slate-200 text-slate-500 transition-colors">
|
||||
<span class="material-symbols-outlined text-lg">chevron_right</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer Summary Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="bg-white p-6 rounded-xl border border-slate-200 flex items-center gap-4">
|
||||
<div class="w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center text-primary">
|
||||
<span class="material-symbols-outlined">description</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-slate-500 uppercase">Total Activos</p>
|
||||
<p class="text-2xl font-black text-slate-900">24</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded-xl border border-slate-200 flex items-center gap-4">
|
||||
<div
|
||||
class="w-12 h-12 bg-amber-100 rounded-full flex items-center justify-center text-amber-600">
|
||||
<span class="material-symbols-outlined">update</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-slate-500 uppercase">Último Folio Emitido</p>
|
||||
<p class="text-2xl font-black text-slate-900">ALM-542</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white p-6 rounded-xl border border-slate-200 flex items-center gap-4">
|
||||
<div
|
||||
class="w-12 h-12 bg-indigo-100 rounded-full flex items-center justify-center text-indigo-600">
|
||||
<span class="material-symbols-outlined">archive</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-bold text-slate-500 uppercase">Archivados</p>
|
||||
<p class="text-2xl font-black text-slate-900">12</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,17 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import Dialog from 'primevue/dialog';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import Dropdown from 'primevue/dropdown';
|
||||
import Textarea from 'primevue/textarea';
|
||||
import Button from 'primevue/button';
|
||||
import type { Supplier, SupplierFormErrors } from '../../types/suppliers';
|
||||
import ProgressSpinner from 'primevue/progressspinner';
|
||||
import type { Supplier, SupplierFormErrors, SupplierAddress } from '../../types/suppliers.interfaces';
|
||||
import { SupplierType } from '../../types/suppliers.interfaces';
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
isEditMode: boolean;
|
||||
supplier?: Supplier | null;
|
||||
formErrors: SupplierFormErrors;
|
||||
loading?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@ -20,36 +23,66 @@ const emit = defineEmits<{
|
||||
(e: 'cancel'): void;
|
||||
}>();
|
||||
|
||||
const defaultAddress = (): SupplierAddress => ({
|
||||
country: 'México',
|
||||
postal_code: '',
|
||||
state: '',
|
||||
municipality: '',
|
||||
city: '',
|
||||
street: '',
|
||||
num_ext: '',
|
||||
num_int: ''
|
||||
});
|
||||
|
||||
const form = ref({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
type: '',
|
||||
address: ''
|
||||
comercial_name: '',
|
||||
rfc: '',
|
||||
curp: '',
|
||||
type: SupplierType.PROVIDER as number,
|
||||
credit_limit: 0,
|
||||
payment_days: 30,
|
||||
addresses: [defaultAddress()]
|
||||
});
|
||||
|
||||
const address = computed(() => form.value.addresses[0] || defaultAddress());
|
||||
|
||||
watch(
|
||||
() => props.supplier,
|
||||
(supplier) => {
|
||||
if (props.isEditMode && supplier) {
|
||||
form.value = {
|
||||
name: supplier.name,
|
||||
email: supplier.contact_email,
|
||||
phone: supplier.phone_number,
|
||||
comercial_name: supplier.comercial_name,
|
||||
rfc: supplier.rfc,
|
||||
curp: supplier.curp,
|
||||
type: supplier.type,
|
||||
address: supplier.address
|
||||
credit_limit: parseFloat(supplier.credit_limit),
|
||||
payment_days: supplier.payment_days,
|
||||
addresses: supplier.addresses && supplier.addresses.length > 0
|
||||
? [...supplier.addresses]
|
||||
: [defaultAddress()]
|
||||
};
|
||||
} else {
|
||||
form.value = { name: '', email: '', phone: '', type: '', address: '' };
|
||||
form.value = {
|
||||
name: '',
|
||||
comercial_name: '',
|
||||
rfc: '',
|
||||
curp: '',
|
||||
type: SupplierType.PROVIDER,
|
||||
credit_limit: 0,
|
||||
payment_days: 30,
|
||||
addresses: [defaultAddress()]
|
||||
};
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const supplierTypeOptions = [
|
||||
{ label: 'General', value: 'general' },
|
||||
{ label: 'Compra', value: 'purchases' },
|
||||
{ label: 'Venta', value: 'sales' }
|
||||
{ label: 'Cliente', value: SupplierType.CLIENT },
|
||||
{ label: 'Proveedor', value: SupplierType.PROVIDER },
|
||||
{ label: 'Cliente/Proveedor', value: SupplierType.BOTH }
|
||||
];
|
||||
|
||||
const handleSubmit = () => {
|
||||
@ -62,42 +95,125 @@ const handleCancel = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog :visible="visible" modal :style="{ width: '600px' }" :header="isEditMode ? 'Editar Proveedor' : 'Crear Nuevo Proveedor'" :closable="true" @update:visible="val => emit('update:visible', val)">
|
||||
<form class="flex flex-col gap-8" @submit.prevent="handleSubmit">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-[#111418] dark:text-white text-sm font-semibold leading-normal">
|
||||
Nombre del Proveedor <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="form.name" placeholder="Ej: Suministros Industriales S.A." required class="w-full" />
|
||||
<div v-if="formErrors.name" class="text-red-500 text-xs mt-1" v-for="err in formErrors.name" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-[#111418] dark:text-white text-sm font-semibold leading-normal">
|
||||
Correo de Contacto <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="form.email" placeholder="contacto@proveedor.com" required class="w-full" type="email" />
|
||||
<div v-if="formErrors.contact_email" class="text-red-500 text-xs mt-1" v-for="err in formErrors.contact_email" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
<Dialog :visible="visible" modal :style="{ width: '800px' }" :header="isEditMode ? 'Editar Proveedor' : 'Crear Nuevo Proveedor'" :closable="true" @update:visible="val => emit('update:visible', val)">
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div v-if="loading" class="flex items-center justify-center py-20">
|
||||
<ProgressSpinner style="width: 50px; height: 50px" strokeWidth="4" />
|
||||
</div>
|
||||
|
||||
<form v-else class="flex flex-col gap-6" @submit.prevent="handleSubmit">
|
||||
|
||||
<!-- Información General -->
|
||||
<div class="border-b pb-4">
|
||||
<h3 class="text-lg font-semibold mb-4">Información General</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Razón Social <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="form.name" placeholder="Ej: Distribuidora Nacional S.A. de C.V." required />
|
||||
<div v-if="formErrors.name" class="text-red-500 text-xs" v-for="err in formErrors.name" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Nombre Comercial <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="form.comercial_name" placeholder="Ej: DistriNacional" required />
|
||||
<div v-if="formErrors.comercial_name" class="text-red-500 text-xs" v-for="err in formErrors.comercial_name" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">RFC <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="form.rfc" placeholder="XAXX010101000" required maxlength="13" class="font-mono uppercase" @input="form.rfc = form.rfc.toUpperCase()" />
|
||||
<div v-if="formErrors.rfc" class="text-red-500 text-xs" v-for="err in formErrors.rfc" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">CURP <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="form.curp" placeholder="XAXX010101HDFABC01" required maxlength="18" class="font-mono uppercase" @input="form.curp = form.curp.toUpperCase()" />
|
||||
<div v-if="formErrors.curp" class="text-red-500 text-xs" v-for="err in formErrors.curp" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-[#111418] dark:text-white text-sm font-semibold leading-normal">Teléfono</p>
|
||||
<InputText v-model="form.phone" placeholder="+52 ..." class="w-full" type="tel" />
|
||||
<div v-if="formErrors.phone_number" class="text-red-500 text-xs mt-1" v-for="err in formErrors.phone_number" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-[#111418] dark:text-white text-sm font-semibold leading-normal">Tipo de Proveedor <span class="text-red-500">*</span></p>
|
||||
<Dropdown v-model="form.type" :options="supplierTypeOptions" optionLabel="label" optionValue="value" placeholder="Seleccionar tipo" class="w-full" required />
|
||||
<div v-if="formErrors.type" class="text-red-500 text-xs mt-1" v-for="err in formErrors.type" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
|
||||
<!-- Información Comercial -->
|
||||
<div class="border-b pb-4">
|
||||
<h3 class="text-lg font-semibold mb-4">Información Comercial</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Tipo <span class="text-red-500">*</span></p>
|
||||
<Dropdown v-model="form.type" :options="supplierTypeOptions" optionLabel="label" optionValue="value" placeholder="Seleccionar tipo" required />
|
||||
<div v-if="formErrors.type" class="text-red-500 text-xs" v-for="err in formErrors.type" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Límite de Crédito <span class="text-red-500">*</span></p>
|
||||
<InputNumber v-model="form.credit_limit" mode="currency" currency="MXN" locale="es-MX" :minFractionDigits="2" required />
|
||||
<div v-if="formErrors.credit_limit" class="text-red-500 text-xs" v-for="err in formErrors.credit_limit" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Días de Pago <span class="text-red-500">*</span></p>
|
||||
<InputNumber v-model="form.payment_days" :min="0" :max="365" suffix=" días" required />
|
||||
<div v-if="formErrors.payment_days" class="text-red-500 text-xs" v-for="err in formErrors.payment_days" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-[#111418] dark:text-white text-sm font-semibold leading-normal">Dirección</p>
|
||||
<Textarea v-model="form.address" placeholder="Calle, Número, Colonia, Ciudad, Estado, CP" class="w-full" autoResize />
|
||||
<div v-if="formErrors.address" class="text-red-500 text-xs mt-1" v-for="err in formErrors.address" :key="err">{{ err }}</div>
|
||||
</label>
|
||||
<div class="mt-4 pt-8 border-t border-[#dbe0e6] dark:border-[#2d3a4a] flex flex-col sm:flex-row items-center justify-end gap-4">
|
||||
<Button label="Cancelar" text class="w-full sm:w-auto" @click="handleCancel" type="button" />
|
||||
<Button :label="isEditMode ? 'Actualizar Proveedor' : 'Guardar Proveedor'" type="submit" class="w-full sm:w-auto" />
|
||||
|
||||
<!-- Dirección -->
|
||||
<div class="border-b pb-4">
|
||||
<h3 class="text-lg font-semibold mb-4">Dirección</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">País</p>
|
||||
<InputText v-model="address.country" placeholder="México" />
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Código Postal <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="address.postal_code" placeholder="06000" required maxlength="5" />
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Estado <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="address.state" placeholder="CDMX" required />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Municipio/Alcaldía <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="address.municipality" placeholder="Cuauhtémoc" required />
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Ciudad <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="address.city" placeholder="Ciudad de México" required />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
||||
<label class="flex flex-col gap-2 md:col-span-1">
|
||||
<p class="text-sm font-semibold">Calle <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="address.street" placeholder="Av. Reforma" required />
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Número Ext. <span class="text-red-500">*</span></p>
|
||||
<InputText v-model="address.num_ext" placeholder="123" required />
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold">Número Int.</p>
|
||||
<InputText v-model="address.num_int" placeholder="4B" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="flex flex-col sm:flex-row items-center justify-end gap-3 pt-2">
|
||||
<Button label="Cancelar" text severity="secondary" @click="handleCancel" type="button" />
|
||||
<Button :label="isEditMode ? 'Actualizar Proveedor' : 'Crear Proveedor'" type="submit" icon="pi pi-check" />
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
|
||||
@ -15,8 +15,9 @@ import { useToast } from 'primevue/usetoast';
|
||||
|
||||
|
||||
|
||||
import { supplierServices } from '../../services/supplierServices';
|
||||
import type { Supplier, SupplierPaginatedResponse, SupplierFormErrors } from '../../types/suppliers';
|
||||
import { supplierServices } from '../../services/supplier.services';
|
||||
import type { Supplier, SupplierPaginatedResponse, SupplierFormErrors } from '../../types/suppliers.interfaces';
|
||||
import { SupplierType } from '../../types/suppliers.interfaces';
|
||||
import SupplierModal from './SupplierModal.vue';
|
||||
|
||||
|
||||
@ -29,6 +30,7 @@ const pagination = ref({
|
||||
lastPage: 1,
|
||||
});
|
||||
const loading = ref(false);
|
||||
const loadingSupplier = ref(false);
|
||||
|
||||
// Modal state and form fields
|
||||
const showModal = ref(false);
|
||||
@ -58,11 +60,12 @@ const handleDelete = (supplierId: number) => {
|
||||
};
|
||||
|
||||
|
||||
const mapTypeToApi = (type: string) => {
|
||||
switch (type) {
|
||||
case 'General': return 'general';
|
||||
case 'Compra': return 'purchases';
|
||||
case 'Venta': return 'sales';
|
||||
const mapTypeToApi = (typeLabel: string | null) => {
|
||||
if (!typeLabel) return undefined;
|
||||
switch (typeLabel) {
|
||||
case 'Cliente': return SupplierType.CLIENT;
|
||||
case 'Proveedor': return SupplierType.PROVIDER;
|
||||
case 'Ambos': return SupplierType.BOTH;
|
||||
default: return undefined;
|
||||
}
|
||||
};
|
||||
@ -71,17 +74,11 @@ const fetchSuppliers = async (page = 1) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const name = searchName.value ? searchName.value : undefined;
|
||||
const type = selectedType.value ? mapTypeToApi(selectedType.value) : undefined;
|
||||
const type = selectedType.value ? mapTypeToApi(selectedType.value)?.toString() : undefined;
|
||||
console.log('🔎 fetchSuppliers params:', { paginated: true, name, type, page });
|
||||
const response = await supplierServices.getSuppliers(true, name, type);
|
||||
const paginated = response as SupplierPaginatedResponse;
|
||||
suppliers.value = paginated.data.map(s => ({
|
||||
...s,
|
||||
email: s.contact_email,
|
||||
phone: s.phone_number,
|
||||
typeColor: s.type === 'general' ? 'info' : s.type === 'purchases' ? 'success' : 'warning',
|
||||
date: s.created_at ? new Date(s.created_at).toLocaleDateString() : ''
|
||||
}));
|
||||
suppliers.value = paginated.data;
|
||||
pagination.value.total = paginated.total;
|
||||
pagination.value.page = paginated.current_page;
|
||||
pagination.value.lastPage = paginated.last_page;
|
||||
@ -100,9 +97,9 @@ onMounted(() => {
|
||||
|
||||
const supplierTypes = [
|
||||
{ label: 'Todos', value: null },
|
||||
{ label: 'General', value: 'General' },
|
||||
{ label: 'Compras', value: 'Compra' },
|
||||
{ label: 'Ventas', value: 'Venta' },
|
||||
{ label: 'Cliente', value: 'Cliente' },
|
||||
{ label: 'Proveedor', value: 'Proveedor' },
|
||||
{ label: 'Ambos', value: 'Ambos' },
|
||||
];
|
||||
|
||||
const selectedType = ref(null);
|
||||
@ -133,11 +130,27 @@ const openCreateModal = () => {
|
||||
formErrors.value = {};
|
||||
};
|
||||
|
||||
const openEditModal = (supplier: Supplier) => {
|
||||
const openEditModal = async (supplier: Supplier) => {
|
||||
isEditMode.value = true;
|
||||
showModal.value = true;
|
||||
currentSupplier.value = supplier;
|
||||
formErrors.value = {};
|
||||
loadingSupplier.value = true;
|
||||
|
||||
try {
|
||||
// Obtener la información completa del proveedor incluyendo direcciones
|
||||
const response = await supplierServices.getSupplierById(supplier.id);
|
||||
currentSupplier.value = response.data;
|
||||
showModal.value = true;
|
||||
} catch (e: any) {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'No se pudo cargar la información del proveedor',
|
||||
life: 3000
|
||||
});
|
||||
console.error('Error loading supplier details:', e);
|
||||
} finally {
|
||||
loadingSupplier.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
@ -151,24 +164,10 @@ const handleModalSubmit = async (form: any) => {
|
||||
formErrors.value = {};
|
||||
try {
|
||||
if (isEditMode.value && currentSupplier.value) {
|
||||
const payload = {
|
||||
name: form.name,
|
||||
contact_email: form.email,
|
||||
phone_number: form.phone,
|
||||
address: form.address,
|
||||
type: form.type,
|
||||
};
|
||||
await supplierServices.updateSupplier(currentSupplier.value.id, payload, 'patch');
|
||||
await supplierServices.updateSupplier(currentSupplier.value.id, form, 'patch');
|
||||
toast.add({ severity: 'success', summary: 'Proveedor actualizado', detail: 'Proveedor actualizado correctamente', life: 3000 });
|
||||
} else {
|
||||
const payload = {
|
||||
name: form.name,
|
||||
contact_email: form.email,
|
||||
phone_number: form.phone,
|
||||
address: form.address,
|
||||
type: form.type,
|
||||
};
|
||||
await supplierServices.createSupplier(payload);
|
||||
await supplierServices.createSupplier(form);
|
||||
toast.add({ severity: 'success', summary: 'Proveedor creado', detail: 'Proveedor registrado correctamente', life: 3000 });
|
||||
}
|
||||
closeModal();
|
||||
@ -182,14 +181,24 @@ const handleModalSubmit = async (form: any) => {
|
||||
};
|
||||
|
||||
// Mapeo para mostrar el tipo de proveedor con label legible
|
||||
const typeLabel = (type: string) => {
|
||||
const typeLabel = (type: number) => {
|
||||
switch (type) {
|
||||
case 'general': return 'General';
|
||||
case 'purchases': return 'Compras';
|
||||
case 'sales': return 'Ventas';
|
||||
default: return type;
|
||||
case SupplierType.CLIENT: return 'Cliente';
|
||||
case SupplierType.PROVIDER: return 'Proveedor';
|
||||
case SupplierType.BOTH: return 'Ambos';
|
||||
default: return 'Desconocido';
|
||||
}
|
||||
};
|
||||
|
||||
const typeSeverity = (type: number) => {
|
||||
switch (type) {
|
||||
case SupplierType.CLIENT: return 'info';
|
||||
case SupplierType.PROVIDER: return 'success';
|
||||
case SupplierType.BOTH: return 'warning';
|
||||
default: return 'secondary';
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -204,7 +213,7 @@ const typeLabel = (type: string) => {
|
||||
</div>
|
||||
<Button label="Nuevo Proveedor" icon="pi pi-plus" @click="openCreateModal" />
|
||||
<SupplierModal :visible="showModal" :isEditMode="isEditMode" :supplier="currentSupplier"
|
||||
:formErrors="formErrors" @update:visible="val => { if (!val) closeModal(); }"
|
||||
:formErrors="formErrors" :loading="loadingSupplier" @update:visible="val => { if (!val) closeModal(); }"
|
||||
@submit="handleModalSubmit" @cancel="closeModal" />
|
||||
</div>
|
||||
|
||||
@ -233,27 +242,45 @@ const typeLabel = (type: string) => {
|
||||
<Card>
|
||||
<template #content>
|
||||
<DataTable :value="suppliers" :loading="loading" stripedRows responsiveLayout="scroll"
|
||||
class="p-datatable-sm">
|
||||
<Column field="id" header="ID" style="min-width: 80px" />
|
||||
<Column field="name" header="Nombre" style="min-width: 200px">
|
||||
class="p-datatable-sm"><
|
||||
<Column field="name" header="Razón Social" style="min-width: 180px">
|
||||
<template #body="{ data }">
|
||||
<span class="font-bold text-surface-900 dark:text-white">{{ data.name }}</span>
|
||||
<div>
|
||||
<div class="font-bold text-surface-900 dark:text-white">{{ data.name }}</div>
|
||||
<div class="text-xs text-gray-500">{{ data.comercial_name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="email" header="Correo de Contacto" style="min-width: 200px">
|
||||
<Column field="rfc" header="RFC" style="min-width: 130px">
|
||||
<template #body="{ data }">
|
||||
<span class="text-primary-600 dark:text-primary-400">{{ data.email }}</span>
|
||||
<span class="font-mono text-sm">{{ data.rfc }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="curp" header="CURP" style="min-width: 180px">
|
||||
<template #body="{ data }">
|
||||
<span class="font-mono text-sm">{{ data.curp }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="phone" header="Teléfono" style="min-width: 140px" />
|
||||
<Column field="address" header="Dirección" style="min-width: 200px" />
|
||||
<Column field="type" header="Tipo" style="min-width: 100px">
|
||||
<template #body="{ data }">
|
||||
<Tag :value="typeLabel(data.type)" :severity="data.typeColor" />
|
||||
<Tag :value="typeLabel(data.type)" :severity="typeSeverity(data.type)" />
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="credit_limit" header="Límite de Crédito" style="min-width: 140px">
|
||||
<template #body="{ data }">
|
||||
<span class="font-semibold text-green-600 dark:text-green-400">${{ parseFloat(data.credit_limit).toLocaleString('es-MX', { minimumFractionDigits: 2 }) }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="payment_days" header="Días de Pago" style="min-width: 120px">
|
||||
<template #body="{ data }">
|
||||
<span class="font-semibold">{{ data.payment_days }} días</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="created_at" header="Fecha de Registro" style="min-width: 120px">
|
||||
<template #body="{ data }">
|
||||
<span class="text-sm">{{ new Date(data.created_at).toLocaleDateString('es-MX') }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
<Column field="date" header="Fecha de Registro" style="min-width: 120px" />
|
||||
<Column header="Acciones" headerStyle="text-align: right" bodyStyle="text-align: right"
|
||||
style="min-width: 120px">
|
||||
<template #body="{ data }">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import api from "../../../services/api";
|
||||
import type { SupplierCreateRequest, SupplierCreateResponse, SupplierDeleteResponse, SupplierListResponse, SupplierPaginatedResponse, SupplierUpdateRequest, SupplierUpdateResponse } from "../types/suppliers";
|
||||
import type { SupplierCreateRequest, SupplierCreateResponse, SupplierDeleteResponse, SupplierListResponse, SupplierPaginatedResponse, SupplierUpdateRequest, SupplierUpdateResponse } from "../types/suppliers.interfaces";
|
||||
|
||||
|
||||
const supplierServices = {
|
||||
@ -26,7 +26,18 @@ const supplierServices = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async getSupplierById(supplierId: number): Promise<SupplierCreateResponse> {
|
||||
try {
|
||||
const response = await api.get(`/api/suppliers/${supplierId}`);
|
||||
console.log(`📦 Supplier with ID ${supplierId} response:`, response);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(`❌ Error fetching supplier with ID ${supplierId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async createSupplier(data: SupplierCreateRequest): Promise<SupplierCreateResponse> {
|
||||
try {
|
||||
const response = await api.post('/api/suppliers', data);
|
||||
@ -4,7 +4,7 @@ import type {
|
||||
CreateUnitOfMeasureData,
|
||||
UpdateUnitOfMeasureData,
|
||||
SingleUnitOfMeasureResponse
|
||||
} from '../types/unitOfMeasure';
|
||||
} from '../types/unit-measure.interfaces';
|
||||
|
||||
export const unitOfMeasureService = {
|
||||
/**
|
||||
@ -1,7 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { supplierServices } from '../services/supplierServices';
|
||||
import type { Supplier } from '../types/suppliers';
|
||||
import { supplierServices } from '../services/supplier.services';
|
||||
import type { Supplier } from '../types/suppliers.interfaces';
|
||||
|
||||
export const useSupplierStore = defineStore('supplier', () => {
|
||||
const suppliers = ref<Supplier[]>([]);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import { unitOfMeasureService } from '../services/unitOfMeasureService';
|
||||
import type { UnitOfMeasure, CreateUnitOfMeasureData, UpdateUnitOfMeasureData } from '../types/unitOfMeasure';
|
||||
import { unitOfMeasureService } from '../services/unit-measure.services';
|
||||
import type { UnitOfMeasure, CreateUnitOfMeasureData, UpdateUnitOfMeasureData } from '../types/unit-measure.interfaces';
|
||||
|
||||
export const useUnitOfMeasureStore = defineStore('unitOfMeasure', () => {
|
||||
// State
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
// Enum para tipos de proveedor
|
||||
export const SupplierType = {
|
||||
CLIENT: 0,
|
||||
PROVIDER: 1,
|
||||
BOTH: 2
|
||||
} as const;
|
||||
|
||||
export type SupplierType = typeof SupplierType[keyof typeof SupplierType];
|
||||
|
||||
// Errores de validación del formulario de proveedor
|
||||
export interface SupplierFormErrors {
|
||||
name?: string[];
|
||||
contact_email?: string[];
|
||||
phone_number?: string[];
|
||||
address?: string[];
|
||||
comercial_name?: string[];
|
||||
rfc?: string[];
|
||||
curp?: string[];
|
||||
type?: string[];
|
||||
credit_limit?: string[];
|
||||
payment_days?: string[];
|
||||
}
|
||||
// Respuesta simple de proveedores (sin paginación)
|
||||
export interface SupplierListResponse {
|
||||
@ -15,10 +25,13 @@ export interface SupplierListResponse {
|
||||
export interface Supplier {
|
||||
id: number;
|
||||
name: string;
|
||||
contact_email: string;
|
||||
phone_number: string;
|
||||
address: string;
|
||||
type: string;
|
||||
comercial_name: string;
|
||||
rfc: string;
|
||||
curp: string;
|
||||
type: SupplierType;
|
||||
credit_limit: string;
|
||||
payment_days: number;
|
||||
addresses?: SupplierAddress[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
@ -51,12 +64,26 @@ export interface SupplierDeleteResponse {
|
||||
data: null;
|
||||
}
|
||||
|
||||
export interface SupplierAddress {
|
||||
country: string;
|
||||
postal_code: string;
|
||||
state: string;
|
||||
municipality: string;
|
||||
city: string;
|
||||
street: string;
|
||||
num_ext: string;
|
||||
num_int: string;
|
||||
}
|
||||
|
||||
export interface SupplierCreateRequest {
|
||||
name: string;
|
||||
contact_email: string;
|
||||
phone_number: string;
|
||||
address: string;
|
||||
type: string;
|
||||
comercial_name: string;
|
||||
rfc: string;
|
||||
curp: string;
|
||||
type: SupplierType;
|
||||
credit_limit: number;
|
||||
payment_days: number;
|
||||
addresses: SupplierAddress[];
|
||||
}
|
||||
|
||||
export interface SupplierCreateResponse {
|
||||
2
src/modules/purchases/types/purchases.d.ts
vendored
2
src/modules/purchases/types/purchases.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import type { Supplier } from '../../catalog/types/suppliers';
|
||||
import type { Supplier } from '../../catalog/types/suppliers.interfaces';
|
||||
|
||||
export interface Product {
|
||||
id: number;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user