feat: actualizar nombres de columnas y mejorar gestión de seriales en componentes
This commit is contained in:
parent
2bb50c48c9
commit
e51f3fad0f
@ -94,7 +94,7 @@ onMounted(() => {
|
|||||||
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">NOMBRE</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">NOMBRE</th>
|
||||||
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU</th>
|
||||||
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">COMPONENTES</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">COMPONENTES</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">PAQ. ESTIMADO</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">ACCIONES</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">ACCIONES</th>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -88,6 +88,11 @@ const serialsArray = computed(() => {
|
|||||||
.filter(s => s.length > 0);
|
.filter(s => s.length > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Si tiene seriales y no permite decimales, la cantidad se controla por seriales
|
||||||
|
const quantityLockedBySerials = computed(() => {
|
||||||
|
return hasSerials.value && !allowsDecimals.value;
|
||||||
|
});
|
||||||
|
|
||||||
const serialsValidation = computed(() => {
|
const serialsValidation = computed(() => {
|
||||||
if (!hasSerials.value) return { valid: true, message: '' };
|
if (!hasSerials.value) return { valid: true, message: '' };
|
||||||
|
|
||||||
@ -114,6 +119,13 @@ const serialsValidation = computed(() => {
|
|||||||
return { valid: true, message: '' };
|
return { valid: true, message: '' };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Actualizar cantidad automáticamente cuando cambian los seriales
|
||||||
|
const updateQuantityFromSerials = () => {
|
||||||
|
if (!quantityLockedBySerials.value) return;
|
||||||
|
const count = serialsArray.value.length;
|
||||||
|
form.quantity = count > 0 ? count : 1;
|
||||||
|
};
|
||||||
|
|
||||||
/** Métodos */
|
/** Métodos */
|
||||||
const loadWarehouses = () => {
|
const loadWarehouses = () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -305,11 +317,15 @@ watch(() => props.show, (isShown) => {
|
|||||||
<FormInput
|
<FormInput
|
||||||
v-model="form.quantity"
|
v-model="form.quantity"
|
||||||
type="number"
|
type="number"
|
||||||
:min="allowsDecimals ? '0.001' : '1'"
|
min="1"
|
||||||
:step="allowsDecimals ? '0.001' : '1'"
|
step="1"
|
||||||
:placeholder="allowsDecimals ? '0.000' : '0'"
|
placeholder="0"
|
||||||
|
:disabled="quantityLockedBySerials"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<p v-if="quantityLockedBySerials" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
Controlado por seriales
|
||||||
|
</p>
|
||||||
<FormError :message="form.errors?.quantity" />
|
<FormError :message="form.errors?.quantity" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -321,7 +337,7 @@ watch(() => props.show, (isShown) => {
|
|||||||
NÚMEROS DE SERIE
|
NÚMEROS DE SERIE
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<SerialInputList v-model="serialsList" />
|
<SerialInputList v-model="serialsList" @update:model-value="updateQuantityFromSerials" />
|
||||||
|
|
||||||
<!-- Validación -->
|
<!-- Validación -->
|
||||||
<div class="mt-2 flex items-center justify-between">
|
<div class="mt-2 flex items-center justify-between">
|
||||||
|
|||||||
@ -479,18 +479,15 @@ watch(() => form.warehouse_id, (newWarehouseId, oldWarehouseId) => {
|
|||||||
<input
|
<input
|
||||||
v-model="item.quantity"
|
v-model="item.quantity"
|
||||||
type="number"
|
type="number"
|
||||||
:min="item.allows_decimals ? '0.001' : '1'"
|
min="1"
|
||||||
:step="item.allows_decimals ? '0.001' : '1'"
|
step="1"
|
||||||
:placeholder="item.allows_decimals ? '0.000' : '0'"
|
placeholder="0"
|
||||||
:disabled="item.track_serials && canUseSerials(item)"
|
:disabled="item.track_serials && canUseSerials(item)"
|
||||||
class="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
class="w-full px-2 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
/>
|
/>
|
||||||
<p v-if="item.track_serials && canUseSerials(item)" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p v-if="item.track_serials && canUseSerials(item)" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
Controlado por seriales
|
Controlado por seriales
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="item.allows_decimals" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
||||||
Permite hasta 3 decimales (ej: 25.750 {{ item.unit_of_measure?.abbreviation }})
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Botón eliminar -->
|
<!-- Botón eliminar -->
|
||||||
|
|||||||
@ -144,7 +144,7 @@ watch(() => props.show, (val) => {
|
|||||||
<div class="flex items-center justify-between mb-5">
|
<div class="flex items-center justify-between mb-5">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<GoogleIcon name="assignment" class="text-2xl text-emerald-600" />
|
<GoogleIcon name="assignment" class="text-2xl text-emerald-600" />
|
||||||
<h3 class="text-lg font-bold text-gray-900 dark:text-gray-100">Exportar Kardex</h3>
|
<h3 class="text-lg font-bold text-gray-900 dark:text-gray-100">Exportar reporte</h3>
|
||||||
</div>
|
</div>
|
||||||
<button @click="emit('close')" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
<button @click="emit('close')" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||||
<GoogleIcon name="close" class="text-xl" />
|
<GoogleIcon name="close" class="text-xl" />
|
||||||
|
|||||||
@ -292,7 +292,8 @@ const ticketService = {
|
|||||||
quantity: 1,
|
quantity: 1,
|
||||||
unit_price: bundlePrice,
|
unit_price: bundlePrice,
|
||||||
serials: data.serials,
|
serials: data.serials,
|
||||||
is_bundle: true
|
is_bundle: true,
|
||||||
|
components: data.components
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -333,10 +334,32 @@ const ticketService = {
|
|||||||
|
|
||||||
yPosition += 4;
|
yPosition += 4;
|
||||||
|
|
||||||
// Números de serie (si existen)
|
// Componentes del paquete (mostrar productos con SKU y sus seriales)
|
||||||
|
if (item.is_bundle && item.components && item.components.length > 0) {
|
||||||
|
doc.setFontSize(7);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.setTextColor(...darkGrayColor);
|
||||||
|
|
||||||
|
item.components.forEach((comp) => {
|
||||||
|
const compName = comp.product_name || comp.name || comp.inventory?.name || '';
|
||||||
|
const compSku = comp.sku || comp.inventory?.sku || '';
|
||||||
|
const label = compSku ? ` - ${compSku}` : ` - ${compName}`;
|
||||||
|
doc.text(label, leftMargin, yPosition);
|
||||||
|
yPosition += 3;
|
||||||
|
|
||||||
|
// Seriales del componente
|
||||||
|
const compSerials = comp.serial_numbers || comp.serials || [];
|
||||||
|
compSerials.forEach((serial) => {
|
||||||
|
const serialNumber = typeof serial === 'string' ? serial : serial.serial_number;
|
||||||
|
doc.text(`S/N: ${serialNumber}`, leftMargin, yPosition);
|
||||||
|
yPosition += 3;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Números de serie para productos individuales (no bundle)
|
||||||
const serials = item.serials || item.serial_numbers || [];
|
const serials = item.serials || item.serial_numbers || [];
|
||||||
if (serials.length > 0) {
|
if (serials.length > 0) {
|
||||||
doc.setFontSize(6);
|
doc.setFontSize(7);
|
||||||
doc.setFont('helvetica', 'normal');
|
doc.setFont('helvetica', 'normal');
|
||||||
doc.setTextColor(...darkGrayColor);
|
doc.setTextColor(...darkGrayColor);
|
||||||
|
|
||||||
@ -346,6 +369,7 @@ const ticketService = {
|
|||||||
yPosition += 3;
|
yPosition += 3;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
yPosition += 2;
|
yPosition += 2;
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user