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"
|
to="pos.client-tiers.index"
|
||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
|
v-if="hasPermission('invoice-requests.index')"
|
||||||
icon="request_quote"
|
icon="request_quote"
|
||||||
name="pos.billingRequests"
|
name="pos.billingRequests"
|
||||||
to="pos.billingRequests.index"
|
to="pos.billingRequests.index"
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { hasPermission } from '@Plugins/RolePermission.js';
|
import { hasPermission } from '@Plugins/RolePermission.js';
|
||||||
|
|
||||||
// Ruta API
|
// Ruta API
|
||||||
const apiTo = (name, params = {}) => route(`factura.${name}`, params)
|
const apiTo = (name, params = {}) => route(`Solicitudes de factura.${name}`, params)
|
||||||
|
|
||||||
// Ruta visual
|
// Ruta visual
|
||||||
const viewTo = ({ name = '', params = {}, query = {} }) => ({ name: `pos.factura.${name}`, params, query })
|
const viewTo = ({ name = '', params = {}, query = {} }) => ({ name: `pos.factura.${name}`, params, query })
|
||||||
|
|
||||||
// Determina si un usuario puede hacer algo en base a los permisos
|
// 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 {
|
export {
|
||||||
can,
|
can,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useRouter } from 'vue-router';
|
|||||||
import { useSearcher, apiURL } from '@Services/Api';
|
import { useSearcher, apiURL } from '@Services/Api';
|
||||||
import { formatCurrency } from '@/utils/formatters';
|
import { formatCurrency } from '@/utils/formatters';
|
||||||
import { can } from './Module.js';
|
import { can } from './Module.js';
|
||||||
|
import reportService from '@Services/reportService';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ const showDeleteModal = ref(false);
|
|||||||
const showImportModal = ref(false);
|
const showImportModal = ref(false);
|
||||||
const editingProduct = ref(null);
|
const editingProduct = ref(null);
|
||||||
const deletingProduct = ref(null);
|
const deletingProduct = ref(null);
|
||||||
|
const isExporting = ref(false);
|
||||||
|
|
||||||
/** Métodos */
|
/** Métodos */
|
||||||
const searcher = useSearcher({
|
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 */
|
/** Ciclos */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
searcher.search();
|
searcher.search();
|
||||||
@ -119,6 +143,15 @@ onMounted(() => {
|
|||||||
placeholder="Buscar por nombre o SKU..."
|
placeholder="Buscar por nombre o SKU..."
|
||||||
@search="(x) => searcher.search(x)"
|
@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
|
<button
|
||||||
v-if="can('import')"
|
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"
|
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',
|
path: 'client-tiers',
|
||||||
name: 'pos.client-tiers.index',
|
name: 'pos.client-tiers.index',
|
||||||
|
beforeEnter: (to, from, next) => can(next, 'client-tiers.index'),
|
||||||
component: () => import('@Pages/POS/Tiers/Index.vue')
|
component: () => import('@Pages/POS/Tiers/Index.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'billing-requests',
|
path: 'billing-requests',
|
||||||
name: 'pos.billingRequests.index',
|
name: 'pos.billingRequests.index',
|
||||||
|
beforeEnter: (to, from, next) => can(next, 'invoice-requests.index'),
|
||||||
component: () => import('@Pages/POS/Clients/BillingRequests.vue')
|
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