feat: Implementacion para generar un excel para inventario y modificacion a permisos
This commit is contained in:
parent
c9251e0c8f
commit
69c015d51b
@ -73,6 +73,7 @@ onMounted(() => {
|
||||
to="pos.client-tiers.index"
|
||||
/>
|
||||
<Link
|
||||
v-if="hasPermission('invoice-requests.index')"
|
||||
icon="request_quote"
|
||||
name="pos.billingRequests"
|
||||
to="pos.billingRequests.index"
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { hasPermission } from '@Plugins/RolePermission.js';
|
||||
|
||||
// Ruta API
|
||||
const apiTo = (name, params = {}) => route(`factura.${name}`, params)
|
||||
const apiTo = (name, params = {}) => route(`Solicitudes de factura.${name}`, params)
|
||||
|
||||
// Ruta visual
|
||||
const viewTo = ({ name = '', params = {}, query = {} }) => ({ name: `pos.factura.${name}`, params, query })
|
||||
|
||||
// Determina si un usuario puede hacer algo en base a los permisos
|
||||
const can = (permission) => hasPermission(`factura.${permission}`)
|
||||
const can = (permission) => hasPermission(`invoice-requests.${permission}`)
|
||||
|
||||
export {
|
||||
can,
|
||||
|
||||
@ -4,6 +4,7 @@ import { useRouter } from 'vue-router';
|
||||
import { useSearcher, apiURL } from '@Services/Api';
|
||||
import { formatCurrency } from '@/utils/formatters';
|
||||
import { can } from './Module.js';
|
||||
import reportService from '@Services/reportService';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -23,6 +24,7 @@ const showDeleteModal = ref(false);
|
||||
const showImportModal = ref(false);
|
||||
const editingProduct = ref(null);
|
||||
const deletingProduct = ref(null);
|
||||
const isExporting = ref(false);
|
||||
|
||||
/** Métodos */
|
||||
const searcher = useSearcher({
|
||||
@ -106,6 +108,28 @@ const confirmDelete = async (id) => {
|
||||
}
|
||||
};
|
||||
|
||||
const exportReport = async () => {
|
||||
try {
|
||||
isExporting.value = true;
|
||||
|
||||
// Puedes agregar filtros aquí si lo necesitas
|
||||
const filters = {
|
||||
// category_id: 5, // Opcional: filtrar por categoría específica
|
||||
// with_serials_only: true, // Opcional: solo productos con seguimiento de seriales
|
||||
// low_stock_threshold: 10 // Opcional: solo productos con stock bajo o igual al umbral
|
||||
};
|
||||
|
||||
await reportService.exportInventoryToExcel(filters);
|
||||
|
||||
Notify.success('Reporte exportado exitosamente');
|
||||
} catch (error) {
|
||||
console.error('Error al exportar:', error);
|
||||
Notify.error('Error al exportar el reporte');
|
||||
} finally {
|
||||
isExporting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** Ciclos */
|
||||
onMounted(() => {
|
||||
searcher.search();
|
||||
@ -119,6 +143,15 @@ onMounted(() => {
|
||||
placeholder="Buscar por nombre o SKU..."
|
||||
@search="(x) => searcher.search(x)"
|
||||
>
|
||||
<button
|
||||
class="flex items-center gap-2 px-3 py-2 bg-emerald-600 hover:bg-emerald-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="exportReport"
|
||||
:disabled="isExporting"
|
||||
title="Exportar inventario a Excel"
|
||||
>
|
||||
<GoogleIcon :name="isExporting ? 'hourglass_empty' : 'download'" class="text-xl" :class="{ 'animate-spin': isExporting }" />
|
||||
{{ isExporting ? 'Exportando...' : 'Exportar' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="can('import')"
|
||||
class="flex items-center gap-2 px-3 py-2 bg-green-600 hover:bg-green-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm"
|
||||
|
||||
@ -92,11 +92,13 @@ const router = createRouter({
|
||||
{
|
||||
path: 'client-tiers',
|
||||
name: 'pos.client-tiers.index',
|
||||
beforeEnter: (to, from, next) => can(next, 'client-tiers.index'),
|
||||
component: () => import('@Pages/POS/Tiers/Index.vue')
|
||||
},
|
||||
{
|
||||
path: 'billing-requests',
|
||||
name: 'pos.billingRequests.index',
|
||||
beforeEnter: (to, from, next) => can(next, 'invoice-requests.index'),
|
||||
component: () => import('@Pages/POS/Clients/BillingRequests.vue')
|
||||
}
|
||||
]
|
||||
|
||||
@ -51,6 +51,79 @@ const reportService = {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Exports inventory report to Excel file.
|
||||
* @param {Object} filters - Optional filters for the export.
|
||||
* @param {number|null} filters.category_id - Filter by category ID.
|
||||
* @param {boolean|null} filters.with_serials_only - Only products with serial number tracking.
|
||||
* @param {number|null} filters.low_stock_threshold - Filter products below this stock level.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async exportInventoryToExcel(filters = {}) {
|
||||
try {
|
||||
// Construir URL con parámetros
|
||||
let url = apiURL('reports/inventory/excel');
|
||||
const params = [];
|
||||
|
||||
if (filters.category_id) params.push(`category_id=${filters.category_id}`);
|
||||
if (filters.with_serials_only !== undefined) params.push(`with_serials_only=${filters.with_serials_only ? 'true' : 'false'}`);
|
||||
if (filters.low_stock_threshold) params.push(`low_stock_threshold=${filters.low_stock_threshold}`);
|
||||
|
||||
if (params.length > 0) {
|
||||
url += `?${params.join('&')}`;
|
||||
}
|
||||
|
||||
// Hacer petición con axios para descargar archivo
|
||||
const response = await window.axios.get(url, {
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${sessionStorage.token}`,
|
||||
}
|
||||
});
|
||||
|
||||
// Crear blob
|
||||
const blob = new Blob([response.data], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
|
||||
// Generar el nombre del archivo con fecha actual
|
||||
const now = new Date();
|
||||
const timestamp = now.toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
const filename = `inventario_${timestamp}.xlsx`;
|
||||
|
||||
// Crear URL del blob
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
// Crear elemento <a> temporal para descargar
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// Limpiar
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
console.error('Error al exportar inventario:', error);
|
||||
|
||||
// Manejar errores de blob
|
||||
if (error.response && error.response.data instanceof Blob) {
|
||||
const text = await error.response.data.text();
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
return Promise.reject(new Error(json.message || 'Error al exportar el reporte'));
|
||||
} catch {
|
||||
return Promise.reject(new Error('Error al exportar el reporte'));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user