feat: agregar campo Clave SAT en formularios de creación y edición de productos

This commit is contained in:
Juan Felipe Zapata Moreno 2026-02-23 16:30:47 -06:00
parent e51f3fad0f
commit e653add755
4 changed files with 61 additions and 3 deletions

View File

@ -21,6 +21,7 @@ const units = ref([]);
/** Formulario */ /** Formulario */
const form = useForm({ const form = useForm({
name: '', name: '',
key_sat: '',
sku: '', sku: '',
barcode: '', barcode: '',
category_id: '', category_id: '',
@ -159,6 +160,21 @@ watch(() => form.track_serials, () => {
<FormError :message="form.errors?.name" /> <FormError :message="form.errors?.name" />
</div> </div>
<!-- Clave SAT -->
<div class="col-span-2">
<label class="block text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase mb-1.5">
CLAVE SAT
</label>
<FormInput
v-model="form.key_sat"
type="text"
placeholder="Clave SAT del producto"
required
maxlength="9"
/>
<FormError :message="form.errors?.key_sat" />
</div>
<!-- SKU --> <!-- SKU -->
<div> <div>
<label class="block text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase mb-1.5"> <label class="block text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase mb-1.5">

View File

@ -26,6 +26,7 @@ const units = ref([]);
/** Formulario */ /** Formulario */
const form = useForm({ const form = useForm({
name: '', name: '',
key_sat: '',
sku: '', sku: '',
barcode: '', barcode: '',
category_id: '', category_id: '',
@ -115,6 +116,7 @@ const openSerials = () => {
watch(() => props.product, (newProduct) => { watch(() => props.product, (newProduct) => {
if (newProduct) { if (newProduct) {
form.name = newProduct.name || ''; form.name = newProduct.name || '';
form.key_sat = newProduct.key_sat || '';
form.sku = newProduct.sku || ''; form.sku = newProduct.sku || '';
form.barcode = newProduct.barcode || ''; form.barcode = newProduct.barcode || '';
form.category_id = newProduct.category_id || ''; form.category_id = newProduct.category_id || '';
@ -177,6 +179,20 @@ watch(() => form.track_serials, () => {
<FormError :message="form.errors?.name" /> <FormError :message="form.errors?.name" />
</div> </div>
<!-- Clave SAT -->
<div class="col-span-2">
<label class="block text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase mb-1.5">
CLAVE SAT
</label>
<FormInput
v-model="form.key_sat"
type="number"
placeholder="Clave SAT del producto"
maxlength="9"
/>
<FormError :message="form.errors?.key_sat" />
</div>
<!-- SKU --> <!-- SKU -->
<div> <div>
<label class="block text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase mb-1.5"> <label class="block text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase mb-1.5">

View File

@ -321,6 +321,7 @@ onMounted(() => {
<template #head> <template #head>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU / CÓDIGO</th> <th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU / CÓDIGO</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRODUCTO</th> <th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRODUCTO</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CLAVE SAT</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CATEGORÍA</th> <th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CATEGORÍA</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRECIO</th> <th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRECIO</th>
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">STOCK</th> <th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">STOCK</th>
@ -341,6 +342,11 @@ onMounted(() => {
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ model.name }}</p> <p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ model.name }}</p>
</div> </div>
</td> </td>
<td class="px-6 py-4 text-center">
<div>
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ model.key_sat }}</p>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center"> <td class="px-6 py-4 whitespace-nowrap text-center">
<span class="text-sm text-gray-700 dark:text-gray-300"> <span class="text-sm text-gray-700 dark:text-gray-300">
{{ model.category?.name || '-' }} {{ model.category?.name || '-' }}

View File

@ -146,10 +146,30 @@ const addToCart = async (product) => {
try { try {
const response = await serialService.getAvailableSerials(product.id); const response = await serialService.getAvailableSerials(product.id);
const hasSerials = response.serials?.data?.length > 0; const allSerials = response.serials?.data || [];
if (hasSerials) { if (allSerials.length > 0) {
// Agregar track_serials al producto para que el store lo reconozca // Verificar cuántos quedan después de excluir los del carrito
const excluded = cart.getSelectedSerials();
const available = allSerials.filter(s => !excluded.includes(s.serial_number));
if (available.length === 0) {
// Todos los seriales están bloqueados buscar si un paquete del carrito los tiene
const blockingBundle = cart.items.find(item => {
if (!item.is_bundle || !item.serial_numbers) return false;
const bundleSerials = item.serial_numbers[product.id] || [];
return bundleSerials.length > 0;
});
if (blockingBundle) {
window.Notify.warning(`"${product.name}" forma parte del paquete "${blockingBundle.product_name}". No hay suficiente stock para vender`);
} else {
window.Notify.warning(`No hay seriales disponibles para "${product.name}"`);
}
return;
}
// Hay seriales disponibles abrir selector
product.track_serials = true; product.track_serials = true;
serialSelectorProduct.value = product; serialSelectorProduct.value = product;
showSerialSelector.value = true; showSerialSelector.value = true;