feat: agregar campo Clave SAT en formularios de creación y edición de productos
This commit is contained in:
parent
e51f3fad0f
commit
e653add755
@ -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">
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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 || '-' }}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user