From 112e057cfa0ddec2f9bff572482bf8820d3b2ace Mon Sep 17 00:00:00 2001 From: jorgep Date: Tue, 31 Oct 2023 08:56:51 +0100 Subject: [PATCH] ref #5835 invoiceIn created --- src/components/common/VnSelectFilter.vue | 24 +- src/components/ui/VnSearchbar.vue | 4 +- src/composables/downloadFile.js | 11 + src/composables/getUrl.js | 13 +- src/i18n/en/index.js | 66 ++ src/i18n/es/index.js | 64 ++ .../InvoiceIn/Card/InvoiceInBasicData.vue | 742 ++++++++++++++++++ src/pages/InvoiceIn/Card/InvoiceInCard.vue | 91 +++ .../InvoiceIn/Card/InvoiceInDescriptor.vue | 108 +++ src/pages/InvoiceIn/Card/InvoiceInDueDay.vue | 290 +++++++ .../InvoiceIn/Card/InvoiceInIntrastat.vue | 280 +++++++ src/pages/InvoiceIn/Card/InvoiceInSummary.vue | 409 ++++++++++ .../InvoiceIn/Card/InvoiceInSummaryDialog.vue | 29 + src/pages/InvoiceIn/Card/InvoiceInVat.vue | 500 ++++++++++++ src/pages/InvoiceIn/InvoiceInFilter.vue | 307 ++++++++ src/pages/InvoiceIn/InvoiceInList.vue | 179 +++++ src/pages/InvoiceIn/InvoiceInMain.vue | 17 + src/router/modules/index.js | 11 +- src/router/modules/invoiceIn.js | 98 +++ src/router/routes.js | 2 + src/stores/useNavigationStore.js | 11 +- ...{ClaimNotes.spec.js => claimNotes.spec.js} | 0 .../cypress/integration/invoiceInList.spec.js | 27 + .../composables/downloadFile.spec.js | 25 + 24 files changed, 3281 insertions(+), 27 deletions(-) create mode 100644 src/composables/downloadFile.js create mode 100644 src/pages/InvoiceIn/Card/InvoiceInBasicData.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInCard.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInDueDay.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInSummary.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue create mode 100644 src/pages/InvoiceIn/Card/InvoiceInVat.vue create mode 100644 src/pages/InvoiceIn/InvoiceInFilter.vue create mode 100644 src/pages/InvoiceIn/InvoiceInList.vue create mode 100644 src/pages/InvoiceIn/InvoiceInMain.vue create mode 100644 src/router/modules/invoiceIn.js rename test/cypress/integration/{ClaimNotes.spec.js => claimNotes.spec.js} (100%) create mode 100644 test/cypress/integration/invoiceInList.spec.js create mode 100644 test/vitest/__tests__/composables/downloadFile.spec.js diff --git a/src/components/common/VnSelectFilter.vue b/src/components/common/VnSelectFilter.vue index 55395f260..43827b831 100644 --- a/src/components/common/VnSelectFilter.vue +++ b/src/components/common/VnSelectFilter.vue @@ -15,6 +15,10 @@ const $props = defineProps({ type: String, default: '', }, + filterOptions: { + type: Array, + default: () => [], + }, }); const { optionLabel, options } = toRefs($props); const myOptions = ref([]); @@ -28,18 +32,22 @@ function setOptions(data) { setOptions(options.value); const filter = (val, options) => { - const search = val.toLowerCase(); + const search = val.toString().toLowerCase(); - if (val === '') return options; + if (!search) return options; return options.filter((row) => { + if ($props.filterOptions.length) { + return $props.filterOptions.some((prop) => { + const propValue = String(row[prop]).toLowerCase(); + return propValue.includes(search); + }); + } + const id = row.id; - const name = row[$props.optionLabel].toLowerCase(); + const optionLabel = String(row[$props.optionLabel]).toLowerCase(); - const idMatches = id == search; - const nameMatches = name.indexOf(search) > -1; - - return idMatches || nameMatches; + return id == search || optionLabel.includes(search); }); }; @@ -89,7 +97,7 @@ const value = computed({ diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index 693d6fce2..35f6c1548 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -76,9 +76,9 @@ async function search() { const module = route.matched[1]; if (rows.length === 1) { const [firstRow] = rows; - await router.push({ path: `/${module.name}/${firstRow.id}` }); + await router.push({ path: `${module.path}/${firstRow.id}` }); } else if (route.matched.length > 3) { - await router.push({ path: `/${module.name}` }); + await router.push({ path: `/${module.path}` }); arrayData.updateStateParams(); } } diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js new file mode 100644 index 000000000..877d2a254 --- /dev/null +++ b/src/composables/downloadFile.js @@ -0,0 +1,11 @@ +import { useSession } from 'src/composables/useSession'; +import { getUrl } from './getUrl'; + +const session = useSession(); +const token = session.getToken(); + +export async function downloadFile(dmsId) { + let appUrl = await getUrl('', 'lilium'); + appUrl = appUrl.replace('/#/', ''); + window.open(`${appUrl}/api/dms/${dmsId}/downloadFile?access_token=${token}`); +} diff --git a/src/composables/getUrl.js b/src/composables/getUrl.js index f2bd9ddb9..1e6aec4c6 100644 --- a/src/composables/getUrl.js +++ b/src/composables/getUrl.js @@ -1,11 +1,10 @@ import axios from 'axios'; -export async function getUrl(route, appName = 'salix') { - const filter = { - where: { and: [{ appName: appName }, { environment: process.env.NODE_ENV }] }, - }; +export async function getUrl(route, app = 'salix') { + let url; - const { data } = await axios.get('Urls/findOne', { params: { filter } }); - const url = data.url; - return route ? url + route : url; + await axios.get('Urls/getUrl', { params: { app } }).then((res) => { + url = res.data + route; + }); + return url; } diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 948332d46..b3fb32d9b 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -390,6 +390,71 @@ export default { totalWithVat: 'Amount', }, }, + invoiceIn: { + pageTitles: { + invoiceIns: 'Invoices In', + list: 'List', + createInvoiceIn: 'Create invoice in', + summary: 'Summary', + basicData: 'Basic Data', + vat: 'VAT', + dueDay: 'Due day', + intrastat: 'Intrastat', + log: 'Logs', + }, + list: { + ref: 'Reference', + supplier: 'Supplier', + supplierRef: 'Supplier ref.', + serialNumber: 'Serial number', + serial: 'Serial', + file: 'File', + issued: 'Issued', + isBooked: 'Is booked', + awb: 'AWB', + amount: 'Amount', + }, + card: { + issued: 'Issued', + amount: 'Amount', + client: 'Client', + company: 'Company', + customerCard: 'Customer card', + ticketList: 'Ticket List', + vat: 'Vat', + dueDay: 'Due day', + intrastat: 'Intrastat', + }, + summary: { + supplier: 'Supplier', + supplierRef: 'Supplier ref.', + currency: 'Currency', + docNumber: 'Doc number', + issued: 'Expedition date', + operated: 'Operation date', + bookEntried: 'Entry date', + bookedDate: 'Booked date', + sage: 'Sage withholding', + vat: 'Undeductible VAT', + company: 'Company', + booked: 'Booked', + expense: 'Expense', + taxableBase: 'Taxable base', + rate: 'Rate', + sageVat: 'Sage vat', + sageTransaction: 'Sage transaction', + dueDay: 'Date', + bank: 'Bank', + amount: 'Amount', + foreignValue: 'Foreign value', + dueTotal: 'Due day', + noMatch: 'Do not match', + code: 'Code', + net: 'Net', + stems: 'Stems', + country: 'Country', + }, + }, worker: { pageTitles: { workers: 'Workers', @@ -514,6 +579,7 @@ export default { openCard: 'View card', openSummary: 'Open summary', viewDescription: 'View description', + downloadFile: 'Download file', }, cardDescriptor: { mainList: 'Main list', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 9b452ab22..b7dc1c132 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -390,6 +390,69 @@ export default { totalWithVat: 'Importe', }, }, + invoiceIn: { + pageTitles: { + invoiceIns: 'Fact. recibidas', + list: 'Listado', + createInvoiceOut: 'Crear fact. recibida', + summary: 'Resumen', + basicData: 'Datos básicos', + vat: 'IVA', + dueDay: 'Vencimiento', + intrastat: 'Intrastat', + log: 'Registros de auditoría', + }, + list: { + ref: 'Referencia', + supplier: 'Proveedor', + supplierRef: 'Ref. proveedor', + serialNumber: 'Num. serie', + shortIssued: 'F. emisión', + serial: 'Serie', + file: 'Fichero', + issued: 'Fecha emisión', + isBooked: 'Conciliada', + awb: 'AWB', + amount: 'Importe', + }, + card: { + issued: 'Fecha emisión', + amount: 'Importe', + client: 'Cliente', + company: 'Empresa', + customerCard: 'Ficha del cliente', + ticketList: 'Listado de tickets', + vat: 'Vat', + dueDay: 'Fecha de vencimiento', + }, + summary: { + supplier: 'Supplier', + supplierRef: 'Supplier ref.', + currency: 'Divisa', + docNumber: 'Número documento', + issued: 'Fecha de expedición', + operated: 'Fecha operación', + bookEntried: 'Fecha asiento', + bookedDate: 'Fecha contable', + sage: 'Retención sage', + vat: 'Iva no deducible', + company: 'Empresa', + booked: 'Contabilizada', + expense: 'Gasto', + taxableBase: 'Base imp.', + rate: 'Tasa', + sageTransaction: 'Sage transación', + dueDay: 'Fecha', + bank: 'Caja', + amount: 'Importe', + foreignValue: 'Divisa', + dueTotal: 'Vencimiento', + code: 'Código', + net: 'Neto', + stems: 'Tallos', + country: 'País', + }, + }, worker: { pageTitles: { workers: 'Trabajadores', @@ -514,6 +577,7 @@ export default { openCard: 'Ver ficha', openSummary: 'Abrir detalles', viewDescription: 'Ver descripción', + downloadFile: 'Descargar archivo', }, cardDescriptor: { mainList: 'Listado principal', diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue new file mode 100644 index 000000000..3edfab0d6 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -0,0 +1,742 @@ + + + + + en: + supplierFk: Supplier + es: + supplierFk: Proveedor + Supplier Ref: Ref. proveedor + Expedition date: Fecha expedición + Operation date: Fecha operación + Undeductible VAT: Iva no deducible + Document: Documento + Download file: Descargar archivo + Entry date: Fecha asiento + Accounted date: Fecha contable + Currency: Moneda + Company: Empresa + Edit document: Editar documento + Reference: Referencia + Type: Tipo + Description: Descripción + Generate identifier for original file: Generar identificador para archivo original + Required field: Campo obligatorio + File: Fichero + Create document: Crear documento + Select a file: Seleccione un fichero + Allowed content types: Tipos de archivo permitidos + The company can't be empty: La empresa no puede estar vacía + The warehouse can't be empty: El almacén no puede estar vacío + The DMS Type can't be empty: El dms no puede estar vacío + The description can't be empty: La descripción no puede estar vacía + The files can't be empty: Los archivos no pueden estar vacíos + diff --git a/src/pages/InvoiceIn/Card/InvoiceInCard.vue b/src/pages/InvoiceIn/Card/InvoiceInCard.vue new file mode 100644 index 000000000..4d06cb41e --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInCard.vue @@ -0,0 +1,91 @@ + + + + + es: + Search invoice: Buscar factura emitida + You can search by invoice reference: Puedes buscar por referencia de la factura + diff --git a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue new file mode 100644 index 000000000..f37852fbd --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue @@ -0,0 +1,108 @@ + + + diff --git a/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue new file mode 100644 index 000000000..d657b09d0 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue @@ -0,0 +1,290 @@ + + + + + es: + Date: Fecha + Bank: Caja + Amount: Importe + Foreign value: Divisa + diff --git a/src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue b/src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue new file mode 100644 index 000000000..58f521534 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue @@ -0,0 +1,280 @@ + + + + + + en: + amount: Amount + net: Net + stems: Stems + country: Country + es: + Code: Código + amount: Cantidad + net: Neto + stems: Tallos + country: País + Total amount: Total importe + Total net: Total neto + Total stems: Total tallos + diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue new file mode 100644 index 000000000..79cdab02d --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue @@ -0,0 +1,409 @@ + + + + + + es: + Search invoice: Buscar factura emitida + You can search by invoice reference: Puedes buscar por referencia de la factura + diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue b/src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue new file mode 100644 index 000000000..8657bf6ac --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/pages/InvoiceIn/Card/InvoiceInVat.vue b/src/pages/InvoiceIn/Card/InvoiceInVat.vue new file mode 100644 index 000000000..8a83dfbd4 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInVat.vue @@ -0,0 +1,500 @@ + + + + +es: + Expense: Gasto + Create expense: Crear gasto + Add tax: Crear gasto + Taxable base: Base imp. + Sage tax: Sage iva + Sage transaction: Sage transacción + Rate: Tasa + Foreign value: Divisa + New expense: Nuevo gasto + Code: Código + It's a withholding: Es una retención + Descripction: Descripción + The code can't be empty: El código no puede estar vacío + The description can't be empty: La descripción no puede estar vacía + The code have to be a number: El código debe ser un número. + diff --git a/src/pages/InvoiceIn/InvoiceInFilter.vue b/src/pages/InvoiceIn/InvoiceInFilter.vue new file mode 100644 index 000000000..6348d4167 --- /dev/null +++ b/src/pages/InvoiceIn/InvoiceInFilter.vue @@ -0,0 +1,307 @@ + + + + + +en: + params: + search: ID + supplierRef: Supplier ref. + supplierFk: Supplier + fi: Supplier fiscal id + clientFk: Customer + amount: Amount + created: Created + awb: AWB + dued: Dued + serialNumber: Serial Number + serial: Serial + account: Account + isBooked: is booked +es: + params: + search: Contiene + supplierRef: Ref. proveedor + supplierFk: Proveedor + clientFk: Cliente + fi: CIF proveedor + serialNumber: Num. serie + serial: Serie + awb: AWB + amount: Importe + issued: Emitida + isBooked: Conciliada + account: Cuenta + created: Creada + dued: Vencida + From: Desde + To: Hasta + Amount: Importe + Issued: Fecha factura + Id or supplier: Id o proveedor + More options: Más opciones + diff --git a/src/pages/InvoiceIn/InvoiceInList.vue b/src/pages/InvoiceIn/InvoiceInList.vue new file mode 100644 index 000000000..692251bff --- /dev/null +++ b/src/pages/InvoiceIn/InvoiceInList.vue @@ -0,0 +1,179 @@ + + + + + + + +es: + Search invoice: Buscar factura emitida + You can search by invoice reference: Puedes buscar por referencia de la factura + diff --git a/src/pages/InvoiceIn/InvoiceInMain.vue b/src/pages/InvoiceIn/InvoiceInMain.vue new file mode 100644 index 000000000..66ce78f23 --- /dev/null +++ b/src/pages/InvoiceIn/InvoiceInMain.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/router/modules/index.js b/src/router/modules/index.js index 2916b98ef..2bb28c817 100644 --- a/src/router/modules/index.js +++ b/src/router/modules/index.js @@ -2,16 +2,9 @@ import Customer from './customer'; import Ticket from './ticket'; import Claim from './claim'; import InvoiceOut from './invoiceOut'; +import invoiceIn from './invoiceIn'; import Worker from './worker'; import Wagon from './wagon'; import Route from './route'; -export default [ - Customer, - Ticket, - Claim, - InvoiceOut, - Worker, - Wagon, - Route -] +export default [Customer, Ticket, Claim, InvoiceOut, invoiceIn, Worker, Wagon, Route]; diff --git a/src/router/modules/invoiceIn.js b/src/router/modules/invoiceIn.js new file mode 100644 index 000000000..a5509908c --- /dev/null +++ b/src/router/modules/invoiceIn.js @@ -0,0 +1,98 @@ +import { RouterView } from 'vue-router'; + +export default { + path: '/invoice-in', + name: 'InvoiceIn', + meta: { + title: 'invoiceIns', + icon: 'vn:invoice-in', + }, + component: RouterView, + redirect: { name: 'InvoiceInMain' }, + menus: { + main: ['InvoiceInList'], + card: [ + 'InvoiceInBasicData', + 'InvoiceInVat', + 'InvoiceInDueDay', + 'InvoiceInIntrastat', + ], + }, + children: [ + { + path: '', + name: 'InvoiceInMain', + component: () => import('src/pages/InvoiceIn/InvoiceInMain.vue'), + redirect: { name: 'InvoiceInList' }, + children: [ + { + path: 'list', + name: 'InvoiceInList', + meta: { + title: 'list', + icon: 'view_list', + }, + component: () => import('src/pages/InvoiceIn/InvoiceInList.vue'), + }, + ], + }, + { + name: 'InvoiceInCard', + path: ':id', + component: () => import('src/pages/InvoiceIn/Card/InvoiceInCard.vue'), + redirect: { name: 'InvoiceInSummary' }, + children: [ + { + name: 'InvoiceInSummary', + path: 'summary', + meta: { + title: 'summary', + icon: 'view_list', + }, + component: () => + import('src/pages/InvoiceIn/Card/InvoiceInSummary.vue'), + }, + { + name: 'InvoiceInBasicData', + path: 'basic-data', + meta: { + title: 'basicData', + icon: 'vn:settings', + roles: ['salesPerson'], + }, + component: () => + import('src/pages/InvoiceIn/Card/InvoiceInBasicData.vue'), + }, + { + name: 'InvoiceInVat', + path: 'vat', + meta: { + title: 'vat', + icon: 'vn:tax', + }, + component: () => import('src/pages/InvoiceIn/Card/InvoiceInVat.vue'), + }, + { + name: 'InvoiceInDueDay', + path: 'due-day', + meta: { + title: 'dueDay', + icon: 'vn:calendar', + }, + component: () => + import('src/pages/InvoiceIn/Card/InvoiceInDueDay.vue'), + }, + { + name: 'InvoiceInIntrastat', + path: 'intrastat', + meta: { + title: 'intrastat', + icon: 'vn:lines', + }, + component: () => + import('src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue'), + }, + ], + }, + ], +}; diff --git a/src/router/routes.js b/src/router/routes.js index e3aa22a06..6632979d3 100644 --- a/src/router/routes.js +++ b/src/router/routes.js @@ -3,6 +3,7 @@ import ticket from './modules/ticket'; import claim from './modules/claim'; import worker from './modules/worker'; import invoiceOut from './modules/invoiceOut'; +import invoiceIn from './modules/invoiceIn'; import wagon from './modules/wagon'; import route from './modules/route'; @@ -49,6 +50,7 @@ const routes = [ claim, worker, invoiceOut, + invoiceIn, { path: '/:catchAll(.*)*', name: 'NotFound', diff --git a/src/stores/useNavigationStore.js b/src/stores/useNavigationStore.js index 168c1f9dc..5db0049cf 100644 --- a/src/stores/useNavigationStore.js +++ b/src/stores/useNavigationStore.js @@ -6,7 +6,16 @@ import { useRole } from 'src/composables/useRole'; import routes from 'src/router/modules'; export const useNavigationStore = defineStore('navigationStore', () => { - const modules = ['customer', 'claim', 'ticket', 'invoiceOut', 'worker', 'wagon', 'route']; + const modules = [ + 'customer', + 'claim', + 'ticket', + 'invoiceOut', + 'invoiceIn', + 'worker', + 'wagon', + 'route', + ]; const pinnedModules = ref([]); const role = useRole(); diff --git a/test/cypress/integration/ClaimNotes.spec.js b/test/cypress/integration/claimNotes.spec.js similarity index 100% rename from test/cypress/integration/ClaimNotes.spec.js rename to test/cypress/integration/claimNotes.spec.js diff --git a/test/cypress/integration/invoiceInList.spec.js b/test/cypress/integration/invoiceInList.spec.js new file mode 100644 index 000000000..20b8403a3 --- /dev/null +++ b/test/cypress/integration/invoiceInList.spec.js @@ -0,0 +1,27 @@ +/// +describe('InvoiceInList', () => { + const firstCard = '.q-card:nth-child(1)'; + const firstId = + '.q-card:nth-child(1) .list-items > .vn-label-value:first-child > .value > span'; + const firstDetailBtn = '.q-card:nth-child(1) .q-btn:nth-child(2)'; + const summaryHeaders = '.summaryBody .header'; + + beforeEach(() => { + cy.login('developer'); + cy.visit(`/#/invoice-in`); + }); + + it('should redirect on clicking a invoice', () => { + cy.get(firstId) + .invoke('text') + .then((id) => { + cy.get(firstCard).click(); + cy.url().should('include', `/invoice-in/${id}/summary`); + }); + }); + + it('should open the details', () => { + cy.get(firstDetailBtn).click(); + cy.get(summaryHeaders).eq(1).contains('Basic Data'); + }); +}); diff --git a/test/vitest/__tests__/composables/downloadFile.spec.js b/test/vitest/__tests__/composables/downloadFile.spec.js new file mode 100644 index 000000000..f611479bf --- /dev/null +++ b/test/vitest/__tests__/composables/downloadFile.spec.js @@ -0,0 +1,25 @@ +import { vi, describe, expect, it } from 'vitest'; +import { axios } from 'app/test/vitest/helper'; +import { downloadFile } from 'src/composables/downloadFile'; +import { useSession } from 'src/composables/useSession'; + +const session = useSession(); +const token = session.getToken(); + +describe('downloadFile', () => { + it('should open a new window to download the file', async () => { + const url = 'http://localhost:9000'; + + vi.spyOn(axios, 'get').mockResolvedValueOnce({ data: url }); + + const mockWindowOpen = vi.spyOn(window, 'open'); + + await downloadFile(1); + + expect(mockWindowOpen).toHaveBeenCalledWith( + `${url}/api/dms/1/downloadFile?access_token=${token}` + ); + + mockWindowOpen.mockRestore(); + }); +});