From 112e057cfa0ddec2f9bff572482bf8820d3b2ace Mon Sep 17 00:00:00 2001 From: jorgep Date: Tue, 31 Oct 2023 08:56:51 +0100 Subject: [PATCH 01/19] 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(); + }); +}); From a9241b17f570966055aae3634b94978c2f2dd1d9 Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 6 Nov 2023 16:03:36 +0100 Subject: [PATCH 02/19] ref #5835 tests basicData and list --- .../InvoiceIn/Card/InvoiceInBasicData.vue | 549 +++++++++--------- .../integration/invoiceInBasicData.spec.js | 24 + .../cypress/integration/invoiceInList.spec.js | 1 + .../InvoiceIn/InvoiceInBasicData.spec.js | 46 ++ 4 files changed, 337 insertions(+), 283 deletions(-) create mode 100644 test/cypress/integration/invoiceInBasicData.spec.js create mode 100644 test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index 3edfab0d6..3419492b7 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -85,10 +85,10 @@ async function setCreateDms() { async function edit() { try { - if (!dms.value.companyId) throw new Error(t(`The company can't be empty`)); - if (!dms.value.warehouseId) throw new Error(t(`The warehouse can't be empty`)); - if (!dms.value.dmsTypeId) throw new Error(t(`The DMS Type can't be empty`)); - if (!dms.value.description) throw new Error(t(`The description can't be empty`)); + if (!dms.value.companyId) throw Error(t(`The company can't be empty`)); + if (!dms.value.warehouseId) throw Error(t(`The warehouse can't be empty`)); + if (!dms.value.dmsTypeId) throw Error(t(`The DMS Type can't be empty`)); + if (!dms.value.description) throw Error(t(`The description can't be empty`)); const formData = new FormData(); @@ -111,7 +111,7 @@ async function edit() { }); } catch (error) { quasar.notify({ - message: t(`${error}`), + message: t(`${error.message}`), type: 'negative', }); } @@ -119,11 +119,11 @@ async function edit() { async function create() { try { - if (!dms.value.companyId) throw new Error(t(`The company can't be empty`)); - if (!dms.value.warehouseId) throw new Error(t(`The warehouse can't be empty`)); - if (!dms.value.dmsTypeId) throw new Error(t(`The DMS Type can't be empty`)); - if (!dms.value.description) throw new Error(t(`The description can't be empty`)); - if (!dms.value.files) throw new Error(t(`The files can't be empty`)); + if (!dms.value.companyId) throw Error(t(`The company can't be empty`)); + if (!dms.value.warehouseId) throw Error(t(`The warehouse can't be empty`)); + if (!dms.value.dmsTypeId) throw Error(t(`The DMS Type can't be empty`)); + if (!dms.value.description) throw Error(t(`The description can't be empty`)); + if (!dms.value.files) throw Error(t(`The files can't be empty`)); const formData = new FormData(); @@ -144,8 +144,9 @@ async function create() { type: 'positive', }); } catch (error) { + console.log(error); quasar.notify({ - message: t(`${error}`), + message: t(`${error.message}`), type: 'negative', }); } @@ -197,274 +198,257 @@ async function create() { @on-fetch="(data) => (userConfig = data)" auto-load /> -
- - - diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue index 79cdab02d..6c3f0838c 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue @@ -29,6 +29,7 @@ const entityId = computed(() => $props.id || route.params.id); const salixUrl = ref(); const invoiceInUrl = ref(); const amountsNotMatch = ref(null); +const intrastatTotals = ref({}); const vatColumns = ref([ { @@ -74,7 +75,7 @@ const vatColumns = ref([ name: 'currency', label: 'invoiceIn.summary.currency', field: (row) => row.foreignValue, - format: (value) => toCurrency(value), + format: (value) => value, sortable: true, align: 'left', }, @@ -138,7 +139,7 @@ const intrastatColumns = ref([ align: 'left', }, { - name: 'amount', + name: 'stems', label: 'invoiceIn.summary.stems', field: (row) => row.stems, format: (value) => value, @@ -155,14 +156,29 @@ const intrastatColumns = ref([ }, ]); -function setAmountNotMatch(entity) { +function getAmountNotMatch(totals) { + return ( + totals.totalDueDay != totals.totalTaxableBase && + totals.totalDueDay != totals.totalVat + ); +} + +function getIntrastatTotals(intrastat) { + const totals = {}; + totals.amount = intrastat.reduce((acc, cur) => acc + cur.amount, 0); + totals.net = intrastat.reduce((acc, cur) => acc + cur.net, 0); + totals.stems = intrastat.reduce((acc, cur) => acc + cur.stems, 0); + + return totals; +} + +function setData(entity) { if (!entity) return false; - const total = entity.totals; + amountsNotMatch.value = getAmountNotMatch(entity.totals); - amountsNotMatch.value = - total.totalDueDay != total.totalTaxableBase && - total.totalDueDay != total.totalVat; + if (entity.invoiceInIntrastat.length) + intrastatTotals.value = { ...getIntrastatTotals(entity.invoiceInIntrastat) }; } @@ -170,12 +186,13 @@ function setAmountNotMatch(entity) { From 7e7154525dc06d5062e992e592001f781536360a Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 22 Nov 2023 10:08:51 +0100 Subject: [PATCH 07/19] refs #5835 fix front test --- .../__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js index 1622bf59e..55ca19d71 100644 --- a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js +++ b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js @@ -6,15 +6,15 @@ describe('InvoiceInIntrastat', () => { let vm; beforeAll(() => { - vi.spyOn(axios, 'get').mockResolvedValue({ data: [{}] }); vm = createWrapper(InvoiceInIntrastat, { global: { - stubs: [], + stubs: ['vnPaginate'], mocks: { fetch: vi.fn(), }, }, }).vm; + vi.spyOn(axios, 'get').mockResolvedValue({ data: [{}] }); }); describe('getTotal()', () => { From 9388b873c6ce55c20c7fc2958e845e5785bebe57 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 22 Nov 2023 15:18:18 +0100 Subject: [PATCH 08/19] refs #5835 refactor basicData --- .../InvoiceIn/Card/InvoiceInBasicData.vue | 83 ++++++++----------- .../InvoiceIn/InvoiceInBasicData.spec.js | 20 +---- 2 files changed, 35 insertions(+), 68 deletions(-) diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index 0aee913a4..9f794b91a 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -50,14 +50,10 @@ async function checkFileExists(dmsId) { async function setEditDms(dmsId) { const { data } = await axios.get(`Dms/${dmsId}`); dms.value = { - id: data.id, warehouseId: data.warehouseFk, companyId: data.companyFk, dmsTypeId: data.dmsTypeFk, - reference: data.reference, - description: data.description, - hasFile: data.hasFile, - hasFileAttached: data.hasFileAttached, + ...data, }; if (!allowedContentTypes.value.length) await allowTypesRef.value.fetch(); @@ -83,12 +79,21 @@ async function setCreateDms() { createDmsRef.value.show(); } -async function edit() { +async function upsert() { try { - if (!dms.value.companyId) throw Error(t(`The company can't be empty`)); - if (!dms.value.warehouseId) throw Error(t(`The warehouse can't be empty`)); - if (!dms.value.dmsTypeId) throw Error(t(`The DMS Type can't be empty`)); - if (!dms.value.description) throw Error(t(`The description can't be empty`)); + const isEdit = !!dms.value.id; + const errors = { + companyId: `The company can't be empty`, + warehouseId: `The warehouse can't be empty`, + dmsTypeId: `The DMS Type can't be empty`, + description: `The description can't be empty`, + }; + + Object.keys(errors).forEach((key) => { + if (!dms.value[key]) throw Error(t(errors[key])); + }); + + if (!isEdit && !dms.value.files) throw Error(t(`The files can't be empty`)); const formData = new FormData(); @@ -98,47 +103,25 @@ async function edit() { dms.value.hasFileAttached = true; } - const { data } = await axios.post(`dms/${dms.value.id}/updateFile`, formData, { - params: dms.value, - }); + if (!isEdit) { + const { data } = await axios.post('Dms/uploadFile', formData, { + params: dms.value, + }); + if (data.length) invoiceIn.value.dmsFk = data[0].id; + createDmsRef.value.hide(); + } else if (isEdit) { + const { data } = await axios.post( + `dms/${dms.value.id}/updateFile`, + formData, + { + params: dms.value, + } + ); - if (data.length) invoiceIn.value.dmsFk = data[0].id; - editDmsRef.value.hide(); - - quasar.notify({ - message: t('globals.dataSaved'), - type: 'positive', - }); - } catch (error) { - quasar.notify({ - message: t(`${error.message}`), - type: 'negative', - }); - } -} - -async function create() { - try { - if (!dms.value.companyId) throw Error(t(`The company can't be empty`)); - if (!dms.value.warehouseId) throw Error(t(`The warehouse can't be empty`)); - if (!dms.value.dmsTypeId) throw Error(t(`The DMS Type can't be empty`)); - if (!dms.value.description) throw Error(t(`The description can't be empty`)); - if (!dms.value.files) throw Error(t(`The files can't be empty`)); - - const formData = new FormData(); - - if (dms.value.files) { - for (let i = 0; i < dms.value.files.length; i++) - formData.append(dms.value.files[i].name, dms.value.files[i]); - dms.value.hasFileAttached = true; + if (data.length) invoiceIn.value.dmsFk = data[0].id; + editDmsRef.value.hide(); } - const { data } = await axios.post('Dms/uploadFile', formData, { - params: dms.value, - }); - if (data.length) invoiceIn.value.dmsFk = data[0].id; - editDmsRef.value.hide(); - quasar.notify({ message: t('globals.dataSaved'), type: 'positive', @@ -558,7 +541,7 @@ async function create() { - + @@ -670,7 +653,7 @@ async function create() { - + diff --git a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js index 3e6d8f892..a3c383f74 100644 --- a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js +++ b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js @@ -16,28 +16,12 @@ describe('InvoiceInBasicData', () => { }).vm; }); - describe('edit()', () => { + describe('upsert()', () => { it('should throw an error when data is empty', async () => { vi.spyOn(axios, 'post').mockResolvedValue({ data: [] }); vi.spyOn(vm.quasar, 'notify'); - await vm.edit(); - - expect(vm.quasar.notify).toHaveBeenCalledWith( - expect.objectContaining({ - message: `The company can't be empty`, - type: 'negative', - }) - ); - }); - }); - - describe('create()', () => { - it('should throw an error when data is empty', async () => { - vi.spyOn(axios, 'post').mockResolvedValue({ data: [] }); - vi.spyOn(vm.quasar, 'notify'); - - await vm.create(); + await vm.upsert(); expect(vm.quasar.notify).toHaveBeenCalledWith( expect.objectContaining({ From dfddde1d5c6a193bd4ea7befd6b205aa580103f8 Mon Sep 17 00:00:00 2001 From: jorgep Date: Wed, 22 Nov 2023 15:22:25 +0100 Subject: [PATCH 09/19] refs #5835 refactor upsert --- .../InvoiceIn/Card/InvoiceInBasicData.vue | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index 9f794b91a..b169339d4 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -102,23 +102,16 @@ async function upsert() { formData.append(dms.value.files[i].name, dms.value.files[i]); dms.value.hasFileAttached = true; } + const url = !isEdit ? 'Dms/uploadFile' : `dms/${dms.value.id}/updateFile`; + const { data } = await axios.post(url, formData, { + params: dms.value, + }); + + if (data.length) invoiceIn.value.dmsFk = data[0].id; if (!isEdit) { - const { data } = await axios.post('Dms/uploadFile', formData, { - params: dms.value, - }); - if (data.length) invoiceIn.value.dmsFk = data[0].id; createDmsRef.value.hide(); } else if (isEdit) { - const { data } = await axios.post( - `dms/${dms.value.id}/updateFile`, - formData, - { - params: dms.value, - } - ); - - if (data.length) invoiceIn.value.dmsFk = data[0].id; editDmsRef.value.hide(); } From d97f69456327c153b0defc6e46d9277269554513 Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 23 Nov 2023 08:42:09 +0100 Subject: [PATCH 10/19] refs #5835 upsert refactor --- src/pages/InvoiceIn/Card/InvoiceInBasicData.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index b169339d4..20144d8ca 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -102,7 +102,7 @@ async function upsert() { formData.append(dms.value.files[i].name, dms.value.files[i]); dms.value.hasFileAttached = true; } - const url = !isEdit ? 'Dms/uploadFile' : `dms/${dms.value.id}/updateFile`; + const url = isEdit ? `dms/${dms.value.id}/updateFile` : 'Dms/uploadFile'; const { data } = await axios.post(url, formData, { params: dms.value, }); @@ -111,7 +111,7 @@ async function upsert() { if (!isEdit) { createDmsRef.value.hide(); - } else if (isEdit) { + } else { editDmsRef.value.hide(); } From b00cff8b0b5073e796a0eada1d4393cd342d4c6c Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 24 Nov 2023 10:08:51 +0100 Subject: [PATCH 11/19] refs #5835 refactor getTotals --- src/pages/InvoiceIn/Card/InvoiceInSummary.vue | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue index 2bb3f3f0a..d2855f56a 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue @@ -164,10 +164,11 @@ function getAmountNotMatch(totals) { } function getIntrastatTotals(intrastat) { - const totals = {}; - totals.amount = intrastat.reduce((acc, cur) => acc + cur.amount, 0); - totals.net = intrastat.reduce((acc, cur) => acc + cur.net, 0); - totals.stems = intrastat.reduce((acc, cur) => acc + cur.stems, 0); + const totals = { + amount: intrastat.reduce((acc, cur) => acc + cur.amount, 0), + net: intrastat.reduce((acc, cur) => acc + cur.net, 0), + stems: intrastat.reduce((acc, cur) => acc + cur.stems, 0), + }; return totals; } From 7595c01293a52237c755bbec3c4db7e43fa9ec80 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 24 Nov 2023 11:09:12 +0100 Subject: [PATCH 12/19] refs #5835 fix InvoiceInDescriptor --- src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue index ca660ce36..016d2779c 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue @@ -75,6 +75,7 @@ function setData(entity) { :title="data.title" :subtitle="data.subtitle" @on-fetch="setData" + data-key="invoiceInData" > From 3d046e4bf760c19758b4482add89494d41fa872c Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 4 Dec 2023 14:21:29 +0100 Subject: [PATCH 15/19] refs #5835 summary grid updated --- src/components/ui/CardSummary.vue | 3 +- src/components/ui/VnLv.vue | 8 ++- src/pages/InvoiceIn/Card/InvoiceInSummary.vue | 53 ++++++++++--------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index b54e44398..dc749f4a7 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -82,11 +82,13 @@ watch(props, async () => { .summaryBody { display: flex; flex-direction: row; + flex-wrap: wrap; justify-content: space-evenly; gap: 15px; padding: 15px; > .q-card.vn-one { + width: 350px; flex: 1; } > .q-card.vn-two { @@ -123,7 +125,6 @@ watch(props, async () => { width: max-content; overflow: hidden; white-space: nowrap; - text-overflow: ellipsis; } } .header { diff --git a/src/components/ui/VnLv.vue b/src/components/ui/VnLv.vue index 0f9b99713..b8da70feb 100644 --- a/src/components/ui/VnLv.vue +++ b/src/components/ui/VnLv.vue @@ -9,6 +9,7 @@ const $props = defineProps({ titleValue: { type: [Number, String, Boolean], default: null }, info: { type: String, default: null }, dash: { type: Boolean, default: true }, + ellipsisValue: { type: Boolean, default: true }, }); const isBooleanValue = computed(() => typeof $props.value === 'boolean'); @@ -19,7 +20,7 @@ const isBooleanValue = computed(() => typeof $props.value === 'boolean'); {{ $props.label }}
-
+
typeof $props.value === 'boolean');
+ diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue index 9c377e241..a5057ddd5 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue @@ -238,6 +238,7 @@ function taxRate(taxableBase, rate) { @@ -254,6 +255,30 @@ function taxRate(taxableBase, rate) { :value="toDate(invoiceIn.booked)" /> + + + + {{ t('invoiceIn.pageTitles.basicData') }} + + + + + + + + @@ -290,32 +315,8 @@ function taxRate(taxableBase, rate) { - - - - {{ t('invoiceIn.pageTitles.basicData') }} - - - - - - - - - + {{ t('invoiceIn.card.vat') }} @@ -348,7 +349,7 @@ function taxRate(taxableBase, rate) { - + {{ t('invoiceIn.card.dueDay') }} From b43985b9db209f9cf5bdef15b050ea4629dfbdba Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 4 Dec 2023 15:26:19 +0100 Subject: [PATCH 16/19] refs #5835 refactor --- src/i18n/en/index.js | 2 +- src/i18n/es/index.js | 2 +- src/pages/InvoiceIn/Card/InvoiceInSummary.vue | 18 +++++++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 522c11100..26d37ae5a 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -347,7 +347,7 @@ export default { }, invoiceOut: { pageTitles: { - invoiceOuts: 'Invoices Out', + invoiceOuts: 'Create invoice', list: 'List', createInvoiceOut: 'Create invoice out', summary: 'Summary', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index d4deed5ee..791ba85f0 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -347,7 +347,7 @@ export default { }, invoiceOut: { pageTitles: { - invoiceOuts: 'Fact. emitidas', + invoiceOuts: 'Crear factura', list: 'Listado', createInvoiceOut: 'Crear fact. emitida', summary: 'Resumen', diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue index a5057ddd5..b3b4f37b6 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue @@ -193,6 +193,10 @@ function taxRate(taxableBase, rate) { } return 0; } + +function getLink(param) { + return `#/invoice-in/${entityId.value}/${param}`; +} - diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 83893ece4..7fcd2e9c6 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -405,7 +405,7 @@ export default { pageTitles: { invoiceIns: 'Fact. recibidas', list: 'Listado', - createInvoiceOut: 'Crear fact. recibida', + createInvoiceIn: 'Crear fact. recibida', summary: 'Resumen', basicData: 'Datos básicos', vat: 'IVA', diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index 20144d8ca..4572c4c16 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -37,6 +37,10 @@ const inputFileRef = ref(); const editDmsRef = ref(); const createDmsRef = ref(); +const requiredFieldRule = (val) => val || t('Required field'); +const dateMask = '####-##-##'; +const fillMask = '_'; + async function checkFileExists(dmsId) { if (!dmsId) return; try { @@ -212,10 +216,14 @@ async function upsert() {