From 5dbb52a9e9eb33686283a50bd6c37f3653d510f7 Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Sat, 21 Mar 2026 11:49:00 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20agregar=20gesti=C3=B3n=20de=20facturas?= =?UTF-8?q?=20con=20creaci=C3=B3n,=20edici=C3=B3n=20y=20eliminaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lang/es.js | 34 +++- src/layouts/AppLayout.vue | 6 + src/pages/Admin/Bills/Create.vue | 153 ++++++++++++++++ src/pages/Admin/Bills/Delete.vue | 96 ++++++++++ src/pages/Admin/Bills/Edit.vue | 181 ++++++++++++++++++ src/pages/Admin/Bills/Index.vue | 244 +++++++++++++++++++++++++ src/pages/Admin/Bills/Module.js | 21 +++ src/pages/POS/Movements/EntryModal.vue | 31 +++- src/router/Index.js | 31 +++- src/services/Api.js | 2 +- 10 files changed, 788 insertions(+), 11 deletions(-) create mode 100644 src/pages/Admin/Bills/Create.vue create mode 100644 src/pages/Admin/Bills/Delete.vue create mode 100644 src/pages/Admin/Bills/Edit.vue create mode 100644 src/pages/Admin/Bills/Index.vue create mode 100644 src/pages/Admin/Bills/Module.js diff --git a/src/lang/es.js b/src/lang/es.js index 9f262d5..ba2d4bf 100644 --- a/src/lang/es.js +++ b/src/lang/es.js @@ -1,7 +1,34 @@ -import { success } from "toastr"; - export default { '&':'y', + bills: { + title: 'Facturas / Gastos', + name: 'Nombre', + cost: 'Costo', + file: 'Archivo (PDF/Imagen)', + deadline: 'Fecha límite de pago', + file_replace: 'Reemplazar archivo', + view_file: 'Ver archivo', + current_file: 'Archivo actual', + search: 'Buscar por nombre...', + create: { + title: 'Nueva Factura', + description: 'Registra una nueva factura o gasto. Sube el archivo PDF o imagen correspondiente.', + }, + edit: { + title: 'Editar Factura', + description: 'Actualiza los datos de la factura. Si subes un nuevo archivo, el anterior será reemplazado.', + }, + paid: 'Pagada', + pending: 'Pendiente', + export: 'Exportar pendientes', + toggle_paid: 'Marcar como pagada', + toggle_unpaid: 'Marcar como pendiente', + delete: { + title: 'Eliminar Factura', + confirm: '¿Estás seguro de que deseas eliminar esta factura?', + warning: 'Esta acción es permanente e incluye el archivo adjunto. No se puede deshacer.', + }, + }, account: { delete: { confirm:'¿Está seguro de que quiere eliminar su cuenta? Una vez eliminada su cuenta, todos sus recursos y datos se borrarán permanentemente. Por favor, introduzca su contraseña para confirmar que desea eliminar permanentemente su cuenta.', @@ -466,7 +493,8 @@ export default { clientTiers: 'Niveles de Clientes', billingRequests: 'Solicitudes de Facturación', warehouses: 'Almacenes', - movements: 'Movimientos' + movements: 'Movimientos', + bills: 'Facturas / Gastos' }, cashRegister: { title: 'Caja Registradora', diff --git a/src/layouts/AppLayout.vue b/src/layouts/AppLayout.vue index 3901656..6ac0da1 100644 --- a/src/layouts/AppLayout.vue +++ b/src/layouts/AppLayout.vue @@ -118,6 +118,12 @@ onMounted(() => { name="pos.billingRequests" to="pos.billingRequests.index" /> +
+import { useForm, apiURL } from '@Services/Api'; +import { transl } from './Module'; + +import Modal from '@Holos/Modal.vue'; +import FormInput from '@Holos/Form/Input.vue'; +import FormError from '@Holos/Form/Elements/Error.vue'; +import SingleFile from '@Holos/Form/SingleFile.vue'; + +/** Eventos */ +const emit = defineEmits(['close', 'created']); + +/** Propiedades */ +defineProps({ + show: Boolean +}); + +/** Formulario */ +const form = useForm({ + name: '', + cost: '', + deadline: '', + paid: false, + file: null, +}); + +/** Métodos */ +const create = () => { + form.post(apiURL('bills'), { + onSuccess: (data) => { + window.Notify.success(Lang('register.create.onSuccess')); + emit('created', data.bill); + closeModal(); + }, + }); +}; + +const closeModal = () => { + form.reset(); + emit('close'); +}; + + + diff --git a/src/pages/Admin/Bills/Delete.vue b/src/pages/Admin/Bills/Delete.vue new file mode 100644 index 0000000..03bc24f --- /dev/null +++ b/src/pages/Admin/Bills/Delete.vue @@ -0,0 +1,96 @@ + + + diff --git a/src/pages/Admin/Bills/Edit.vue b/src/pages/Admin/Bills/Edit.vue new file mode 100644 index 0000000..3a39f74 --- /dev/null +++ b/src/pages/Admin/Bills/Edit.vue @@ -0,0 +1,181 @@ + + + diff --git a/src/pages/Admin/Bills/Index.vue b/src/pages/Admin/Bills/Index.vue new file mode 100644 index 0000000..537c2e8 --- /dev/null +++ b/src/pages/Admin/Bills/Index.vue @@ -0,0 +1,244 @@ + + + diff --git a/src/pages/Admin/Bills/Module.js b/src/pages/Admin/Bills/Module.js new file mode 100644 index 0000000..2d521bb --- /dev/null +++ b/src/pages/Admin/Bills/Module.js @@ -0,0 +1,21 @@ +import { lang } from '@Lang/i18n'; +import { hasPermission } from '@Plugins/RolePermission.js'; + +// Ruta API +const apiTo = (name, params = {}) => route(`admin.bills.${name}`, params) + +// Ruta visual +const viewTo = ({ name = '', params = {}, query = {} }) => view({ name: `admin.bills.${name}`, params, query }) + +// Obtener traducción del componente +const transl = (str) => lang(`bills.${str}`) + +// Determina si un usuario puede hacer algo en base a los permisos +const can = (permission) => hasPermission(`bills.${permission}`) + +export { + can, + viewTo, + apiTo, + transl +} diff --git a/src/pages/POS/Movements/EntryModal.vue b/src/pages/POS/Movements/EntryModal.vue index 0a39af4..b3f40c1 100644 --- a/src/pages/POS/Movements/EntryModal.vue +++ b/src/pages/POS/Movements/EntryModal.vue @@ -8,6 +8,7 @@ import FormInput from '@Holos/Form/Input.vue'; import FormError from '@Holos/Form/Elements/Error.vue'; import GoogleIcon from '@Shared/GoogleIcon.vue'; import SerialInputList from '@Components/POS/SerialInputList.vue'; +import BillCreateModal from '@Pages/Admin/Bills/Create.vue'; /** Eventos */ const emit = defineEmits(['close', 'created']); @@ -18,6 +19,7 @@ const props = defineProps({ }); /** Estado */ +const showBillModal = ref(false); const products = ref([]); const warehouses = ref([]); const suppliers = ref([]); @@ -628,12 +630,23 @@ watch(() => form.warehouse_id, (newWarehouseId, oldWarehouseId) => { - +
+ + +
@@ -674,4 +687,10 @@ watch(() => form.warehouse_id, (newWarehouseId, oldWarehouseId) => { + + diff --git a/src/router/Index.js b/src/router/Index.js index d2c1cea..8586eea 100644 --- a/src/router/Index.js +++ b/src/router/Index.js @@ -141,6 +141,12 @@ const router = createRouter({ name: 'pos.unitMeasure.index', beforeEnter: (to, from, next) => can(next, 'units.index'), component: () => import('@Pages/POS/UnitMeasure/Index.vue') + }, + { + path: 'bills', + name: 'admin.bills.index', + beforeEnter: (to, from, next) => can(next, 'bills.index'), + component: () => import('@Pages/Admin/Bills/Index.vue') } ] }, @@ -232,7 +238,30 @@ const router = createRouter({ component: () => import('@Pages/Admin/Activities/Index.vue') } ] - } + }, + { + path: 'bills', + children: [ + { + path: '', + name: 'admin.bills.index', + beforeEnter: (to, from, next) => can(next, 'bills.index'), + component: () => import('@Pages/Admin/Bills/Index.vue') + }, + { + path: 'create', + name: 'admin.bills.create', + beforeEnter: (to, from, next) => can(next, 'bills.create'), + component: () => import('@Pages/Admin/Bills/Create.vue') + }, + { + path: ':id/edit', + name: 'admin.bills.edit', + beforeEnter: (to, from, next) => can(next, 'bills.edit'), + component: () => import('@Pages/Admin/Bills/Edit.vue') + } + ] + }, ] }, { diff --git a/src/services/Api.js b/src/services/Api.js index 643bc99..2061811 100644 --- a/src/services/Api.js +++ b/src/services/Api.js @@ -207,7 +207,7 @@ const api = { }) }, patch(url, options) { - this.load('patch', { + this.load({ method: 'patch', url, options