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/css/app.scss b/src/css/app.scss
index 99170202d..02d4f8946 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -33,6 +33,7 @@ body.body--light {
--vn-gray: #f5f5f5;
--vn-label: #5f5f5f;
--vn-dark: white;
+ --vn-light-gray: #e7e3e3;
}
body.body--dark {
@@ -40,6 +41,7 @@ body.body--dark {
--vn-gray: #313131;
--vn-label: #a8a8a8;
--vn-dark: #292929;
+ --vn-light-gray: #424242;
}
.bg-vn-dark {
diff --git a/src/filters/toCurrency.js b/src/filters/toCurrency.js
index 5005e95bc..f820c0127 100644
--- a/src/filters/toCurrency.js
+++ b/src/filters/toCurrency.js
@@ -9,13 +9,10 @@ export default function (value, symbol = 'EUR', fractionSize = 2) {
style: 'currency',
currency: symbol,
minimumFractionDigits: fractionSize,
- maximumFractionDigits: fractionSize
+ maximumFractionDigits: fractionSize,
};
const lang = locale.value == 'es' ? 'de' : locale.value;
- return new Intl.NumberFormat(lang, options)
- .format(value);
-
-
-}
\ No newline at end of file
+ return new Intl.NumberFormat(lang, options).format(value);
+}
diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index f2b4c9c55..41bf03c5e 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -357,7 +357,7 @@ export default {
},
invoiceOut: {
pageTitles: {
- invoiceOuts: 'Invoices Out',
+ invoiceOuts: 'Create invoice',
list: 'List',
createInvoiceOut: 'Create invoice out',
summary: 'Summary',
@@ -401,6 +401,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',
@@ -526,6 +591,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 39d7d7ae0..7fcd2e9c6 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -357,7 +357,7 @@ export default {
},
invoiceOut: {
pageTitles: {
- invoiceOuts: 'Fact. emitidas',
+ invoiceOuts: 'Crear factura',
list: 'Listado',
createInvoiceOut: 'Crear fact. emitida',
summary: 'Resumen',
@@ -401,6 +401,69 @@ export default {
totalWithVat: 'Importe',
},
},
+ invoiceIn: {
+ pageTitles: {
+ invoiceIns: 'Fact. recibidas',
+ list: 'Listado',
+ createInvoiceIn: '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: 'Iva',
+ dueDay: 'Fecha de vencimiento',
+ },
+ summary: {
+ supplier: 'Proveedor',
+ supplierRef: 'Ref. proveedor',
+ 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',
@@ -526,6 +589,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/Claim/Card/ClaimDescriptorMenu.vue b/src/pages/Claim/Card/ClaimDescriptorMenu.vue
index 5688613d6..d88c3d120 100644
--- a/src/pages/Claim/Card/ClaimDescriptorMenu.vue
+++ b/src/pages/Claim/Card/ClaimDescriptorMenu.vue
@@ -37,7 +37,7 @@ function confirmPickupOrder() {
data: {
address: customer.email,
},
- send: sendPickupOrder,
+ promise: sendPickupOrder,
},
});
}
diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
new file mode 100644
index 000000000..4572c4c16
--- /dev/null
+++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
@@ -0,0 +1,710 @@
+
+
+ (suppliers = data)"
+ />
+ (currencies = data)"
+ />
+ (companies = data)"
+ />
+ (dmsTypes = data)"
+ />
+ (warehouses = data)"
+ />
+ (allowedContentTypes = data)"
+ />
+ (userConfig = data)"
+ auto-load
+ />
+
+
+
+
+
+
+
+
+ {{
+ `${scope.opt.id} - ${scope.opt.nickname}`
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Edit document') }}
+
+
+ {{ t('Create document') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Edit document') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Select a file') }}
+
+
+
+
+ {{
+ `${t(
+ 'Allowed content types'
+ )}: ${allowedContentTypes.join(', ')}`
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Create document') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Select a file') }}
+
+
+
+
+ {{
+ `${t(
+ 'Allowed content types'
+ )}: ${allowedContentTypes.join(', ')}`
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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..4e37102e9
--- /dev/null
+++ b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
@@ -0,0 +1,327 @@
+
+
+
+ (config = data)"
+ />
+
+
+
+
+ {{ t('To book') }}
+
+
+ {{ t('Delete invoice') }}
+
+
+ {{ t('Clone invoice') }}
+
+
+ {{ t('Show agricultural receipt as PDF') }}
+
+
+ {{ t('Send agricultural receipt as PDF') }}...
+
+
+ {{ t('components.smartCard.downloadFile') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('invoiceOut.card.ticketList') }}
+
+
+
+
+
+
+
+es:
+ To book: Contabilizar
+ Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
+ Delete invoice: Eliminar factura
+ Are you sure you want to delete this invoice?: Estas seguro de querer eliminar esta factura?
+ Invoice deleted: Factura eliminada
+ Clone invoice: Clonar factura
+ Invoice cloned: Factura clonada
+ Show agricultural receipt as PDF: Ver recibo agrícola como PDF
+ Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
+ Are you sure you want to send it?: Estás seguro que quieres enviarlo?
+ Send PDF invoice: Enviar factura a PDF
+
diff --git a/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue
new file mode 100644
index 000000000..e240e9a8c
--- /dev/null
+++ b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue
@@ -0,0 +1,294 @@
+
+
+ (banks = data)" />
+ (areRows = !!data.length)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ `${scope.opt.id}: ${scope.opt.bank}`
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ `${scope.opt.id}: ${scope.opt.bank}`
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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 @@
+
+
+ (countries = data)"
+ sort-by="country"
+ />
+ (intrastats = data)"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(invoceInIntrastat = data)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ `${scope.opt.id}: ${scope.opt.description}` }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ `${scope.opt.id}: ${scope.opt.description}`
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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..d7039fe3d
--- /dev/null
+++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
@@ -0,0 +1,428 @@
+
+
+
+ setData(data)"
+ >
+
+ {{ invoiceIn.id }} - {{ invoiceIn.supplier.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ toCurrency(invoiceIn.totals.totalDueDay) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t(col.label) }}
+
+
+
+
+
+
+ {{ toCurrency(invoiceIn.totals.totalTaxableBase) }}
+
+
+ {{
+ toCurrency(getTaxTotal(invoiceIn.invoiceInTax))
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t(col.label) }}
+
+
+
+
+
+
+
+ {{ toCurrency(invoiceIn.totals.totalDueDay) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t(col.label) }}
+
+
+
+
+
+
+ {{ toCurrency(intrastatTotals.amount) }}
+ {{ intrastatTotals.net }}
+ {{ intrastatTotals.stems }}
+
+
+
+
+
+
+
+
+
+
+ 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..d8e742706
--- /dev/null
+++ b/src/pages/InvoiceIn/Card/InvoiceInVat.vue
@@ -0,0 +1,500 @@
+
+
+ (expenses = data)"
+ />
+ (sageTaxTypes = data)" />
+ (sageTransactionTypes = data)"
+ />
+
+
+
+
+
+
+
+
+ {{ `${scope.opt.id}: ${scope.opt.name}` }}
+
+
+
+
+
+
+ {{ t('Create expense') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.opt.vat }}
+
+ {{ `#${scope.opt.id}` }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ scope.opt.transaction
+ }}
+
+ {{ `#${scope.opt.id}` }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ `${scope.opt.id}: ${scope.opt.name}` }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ scope.opt.vat
+ }}
+
+ {{ `#${scope.opt.id}` }}
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ scope.opt.transaction
+ }}
+
+ {{ `#${scope.opt.id}` }}
+
+
+
+
+
+
+
+ {{ toCurrency(taxRate(props.row)) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('New expense') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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 @@
+
+
+
+ (suppliers = data)"
+ />
+
+
+
+ {{ t(`params.${tag.label}`) }}:
+ {{ formatFn(tag.value) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('globals.collapseMenu') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('components.smartCard.openCard') }}
+
+
+
+
+ {{ t('components.smartCard.openSummary') }}
+
+
+
+
+ {{ t('components.smartCard.downloadFile') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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/claimAction.spec.js b/test/cypress/integration/claim/claimAction.spec.js
similarity index 100%
rename from test/cypress/integration/claimAction.spec.js
rename to test/cypress/integration/claim/claimAction.spec.js
diff --git a/test/cypress/integration/claimDevelopment.spec.js b/test/cypress/integration/claim/claimDevelopment.spec.js
similarity index 100%
rename from test/cypress/integration/claimDevelopment.spec.js
rename to test/cypress/integration/claim/claimDevelopment.spec.js
diff --git a/test/cypress/integration/claimNotes.spec.js b/test/cypress/integration/claim/claimNotes.spec.js
similarity index 100%
rename from test/cypress/integration/claimNotes.spec.js
rename to test/cypress/integration/claim/claimNotes.spec.js
diff --git a/test/cypress/integration/claimPhoto.spec.js b/test/cypress/integration/claim/claimPhoto.spec.js
similarity index 100%
rename from test/cypress/integration/claimPhoto.spec.js
rename to test/cypress/integration/claim/claimPhoto.spec.js
diff --git a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js
new file mode 100644
index 000000000..0013df343
--- /dev/null
+++ b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js
@@ -0,0 +1,53 @@
+///
+describe('InvoiceInBasicData', () => {
+ const selects = '.q-form .q-select';
+ const appendBtns = 'label button';
+ const dialogAppendBtns = '.q-dialog label button';
+ const dialogInputs = '.q-dialog input';
+ const dialogActionBtns = '.q-card__actions button';
+
+ beforeEach(() => {
+ cy.login('developer');
+ cy.visit(`/#/invoice-in/1/basic-data`);
+ });
+
+ it('should edit the provideer and supplier ref', () => {
+ cy.get(selects).eq(0).click();
+ cy.get(selects).eq(0).type('Bros');
+ cy.get(selects).eq(0).type('{enter}');
+
+ cy.get(appendBtns).eq(0).click();
+ cy.get('input').eq(2).type(4739);
+ cy.saveCard();
+
+ cy.get(`${selects} input`).eq(0).invoke('val').should('eq', 'Bros nick');
+ cy.get('input').eq(2).invoke('val').should('eq', '4739');
+ });
+
+ it('should edit the dms data', () => {
+ const firtsInput = 'Ticket:65';
+ const secondInput = "I don't know what posting here!";
+
+ cy.get(appendBtns).eq(3).click();
+ cy.get(dialogAppendBtns).eq(0).click();
+ cy.get(dialogInputs).eq(0).type(firtsInput);
+ cy.get(dialogAppendBtns).eq(1).click();
+ cy.get('textarea').type(secondInput);
+ cy.get(dialogActionBtns).eq(1).click();
+
+ cy.get(appendBtns).eq(3).click();
+
+ cy.get(dialogInputs).eq(0).invoke('val').should('eq', firtsInput);
+ cy.get('textarea').invoke('val').should('eq', secondInput);
+ });
+
+ it('should throw an error creating a new dms if a file is not attached', () => {
+ cy.get(appendBtns).eq(2).click();
+ cy.get(appendBtns).eq(1).click();
+ cy.get(dialogActionBtns).eq(1).click();
+ cy.get('.q-notification__message').should(
+ 'have.text',
+ "The files can't be empty"
+ );
+ });
+});
diff --git a/test/cypress/integration/invoiceIn/invoiceInDueDay.spec.js b/test/cypress/integration/invoiceIn/invoiceInDueDay.spec.js
new file mode 100644
index 000000000..ee5ffe08d
--- /dev/null
+++ b/test/cypress/integration/invoiceIn/invoiceInDueDay.spec.js
@@ -0,0 +1,30 @@
+///
+describe('InvoiceInDueDay', () => {
+ const inputs = 'label input';
+ const inputBtns = 'label button';
+ const addBtn = '.q-page-sticky > div > .q-btn > .q-btn__content';
+
+ beforeEach(() => {
+ cy.login('developer');
+ cy.visit(`/#/invoice-in/6/due-day`);
+ });
+
+ it('should update the amount', () => {
+ cy.get(inputBtns).eq(1).click();
+ cy.get(inputs).eq(3).type(23);
+ cy.saveCard();
+ cy.get('.q-notification__message').should('have.text', 'Data saved');
+ });
+
+ it('should remove the first line', () => {
+ cy.removeRow(1);
+ });
+
+ it('should add a new row ', () => {
+ cy.waitForElement('thead');
+ cy.get(addBtn).click();
+
+ cy.saveCard();
+ cy.get('.q-notification__message').should('have.text', 'Data saved');
+ });
+});
diff --git a/test/cypress/integration/invoiceIn/invoiceInIntrastat.spec.js b/test/cypress/integration/invoiceIn/invoiceInIntrastat.spec.js
new file mode 100644
index 000000000..5024b2f1c
--- /dev/null
+++ b/test/cypress/integration/invoiceIn/invoiceInIntrastat.spec.js
@@ -0,0 +1,38 @@
+///
+describe('InvoiceInIntrastat', () => {
+ const inputBtns = 'label button';
+ const thirdRow = 'tbody > :nth-child(3)';
+ const firstLineCode = 'tbody > :nth-child(1) > :nth-child(2)';
+
+ beforeEach(() => {
+ cy.login('developer');
+ cy.visit(`/#/invoice-in/1/intrastat`);
+ });
+
+ it('should edit the first line', () => {
+ cy.selectOption(firstLineCode, 'Plantas vivas: Esqueje/injerto, Vid');
+
+ cy.get(inputBtns).eq(1).click();
+
+ cy.saveCard();
+ cy.visit(`/#/invoice-in/1/intrastat`);
+
+ cy.getValue(firstLineCode).should(
+ 'have.value',
+ 'Plantas vivas: Esqueje/injerto, Vid'
+ );
+ });
+
+ it('should add a new row', () => {
+ const rowData = [false, 'Plantas vivas: Esqueje/injerto, Vid', 30, 10, 5, 'FR'];
+
+ cy.addRow();
+ cy.fillRow(thirdRow, rowData);
+ cy.saveCard();
+ cy.get('.q-notification__message').should('have.text', 'Data saved');
+ });
+
+ it('should remove the first line', () => {
+ cy.removeRow(1);
+ });
+});
diff --git a/test/cypress/integration/invoiceIn/invoiceInList.spec.js b/test/cypress/integration/invoiceIn/invoiceInList.spec.js
new file mode 100644
index 000000000..bd722edab
--- /dev/null
+++ b/test/cypress/integration/invoiceIn/invoiceInList.spec.js
@@ -0,0 +1,28 @@
+///
+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');
+ cy.get(summaryHeaders).eq(4).contains('Vat');
+ });
+});
diff --git a/test/cypress/integration/invoiceIn/invoiceInVat.spec.js b/test/cypress/integration/invoiceIn/invoiceInVat.spec.js
new file mode 100644
index 000000000..26c7750ad
--- /dev/null
+++ b/test/cypress/integration/invoiceIn/invoiceInVat.spec.js
@@ -0,0 +1,55 @@
+///
+describe('InvoiceInVat', () => {
+ const inputs = 'label input';
+ const inputBtns = 'label button';
+ const thirdRow = 'tbody > :nth-child(3)';
+ const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)';
+ const dialogInputs = '.q-dialog label input';
+ const dialogBtns = '.q-dialog button';
+ const randomInt = Math.floor(Math.random() * 100);
+
+ beforeEach(() => {
+ cy.login('developer');
+ cy.visit(`/#/invoice-in/1/vat`);
+ });
+
+ it('should edit the first line', () => {
+ cy.get(inputBtns).eq(1).click();
+ cy.get(inputs).eq(2).type(23);
+ cy.selectOption(firstLineVat, 'H.P. IVA 21% CEE');
+
+ cy.saveCard();
+ cy.visit(`/#/invoice-in/1/vat`);
+
+ cy.getValue(firstLineVat).should('have.value', 'H.P. IVA 21% CEE');
+ });
+
+ it('should add a new row', () => {
+ cy.addRow();
+ cy.fillRow(thirdRow, [true, 2000000001, 30, 'H.P. IVA 10']);
+ cy.saveCard();
+ cy.get('.q-notification__message').should('have.text', 'Data saved');
+ });
+
+ it('should remove the first line', () => {
+ cy.removeRow(1);
+ });
+
+ it('should throw an error if there are fields undefined', () => {
+ cy.get(inputBtns).eq(0).click();
+ cy.get(dialogBtns).eq(2).click();
+ cy.get('.q-notification__message').should('have.text', "The code can't be empty");
+ });
+
+ it('should correctly handle expense addition', () => {
+ cy.get(inputBtns).eq(0).click();
+
+ cy.get(dialogInputs).eq(0).click();
+ cy.get(dialogInputs).eq(0).type(randomInt);
+ cy.get(dialogInputs).eq(1).click();
+ cy.get(dialogInputs).eq(1).type('This is a dummy expense');
+
+ cy.get(dialogBtns).eq(2).click();
+ cy.get('.q-notification__message').should('have.text', 'Data saved');
+ });
+});
diff --git a/test/cypress/integration/ticketBoxing.spec.js b/test/cypress/integration/ticket/ticketBoxing.spec.js
similarity index 100%
rename from test/cypress/integration/ticketBoxing.spec.js
rename to test/cypress/integration/ticket/ticketBoxing.spec.js
diff --git a/test/cypress/integration/wagonCreate.spec.js b/test/cypress/integration/wagon/wagonCreate.spec.js
similarity index 100%
rename from test/cypress/integration/wagonCreate.spec.js
rename to test/cypress/integration/wagon/wagonCreate.spec.js
diff --git a/test/cypress/integration/wagonTypeCreate.spec.js b/test/cypress/integration/wagonType/wagonTypeCreate.spec.js
similarity index 100%
rename from test/cypress/integration/wagonTypeCreate.spec.js
rename to test/cypress/integration/wagonType/wagonTypeCreate.spec.js
diff --git a/test/cypress/integration/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js
similarity index 100%
rename from test/cypress/integration/workerList.spec.js
rename to test/cypress/integration/worker/workerList.spec.js
diff --git a/test/cypress/integration/workerNotificationsManager.spec.js b/test/cypress/integration/worker/workerNotificationsManager.spec.js
similarity index 100%
rename from test/cypress/integration/workerNotificationsManager.spec.js
rename to test/cypress/integration/worker/workerNotificationsManager.spec.js
diff --git a/test/cypress/integration/workerSummary.spec.js b/test/cypress/integration/worker/workerSummary.spec.js
similarity index 100%
rename from test/cypress/integration/workerSummary.spec.js
rename to test/cypress/integration/worker/workerSummary.spec.js
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 2136584e2..4dfde6e21 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -108,14 +108,19 @@ Cypress.Commands.add('fillRow', (rowSelector, data) => {
.then((td) => {
if (td.find('.q-select__dropdown-icon').length) {
cy.selectOption(td, value);
- }
- if (td.find('.q-checkbox__inner').length && value) {
+ } else if (td.find('.q-checkbox__inner').length && value) {
cy.checkOption(td);
- }
+ } else if (td.find('input[type="text"]') && value)
+ cy.get(td).find('input').type(value);
});
});
});
+Cypress.Commands.add('addRow', () => {
+ cy.waitForElement('tbody');
+ cy.get('.q-page-sticky > div > .q-btn > .q-btn__content').click();
+});
+
Cypress.Commands.add('validateRow', (rowSelector, expectedValues) => {
cy.waitForElement('tbody');
cy.get(rowSelector).within(() => {
@@ -131,6 +136,27 @@ Cypress.Commands.add('validateRow', (rowSelector, expectedValues) => {
});
});
+Cypress.Commands.add('removeRow', (rowIndex) => {
+ let rowsBefore;
+ let rowsAfter;
+
+ cy.get('tr')
+ .its('length')
+ .then((length) => {
+ rowsBefore = length;
+ cy.get('.q-checkbox').eq(rowIndex).click();
+ cy.removeCard();
+ cy.get('.q-dialog button').eq(2).click();
+ })
+ .then(() => {
+ cy.get('tr')
+ .its('length')
+ .then((length) => {
+ rowsAfter = length;
+ expect(rowsBefore).to.eq(rowsAfter + 1);
+ });
+ });
+});
Cypress.Commands.add('openListSummary', (row) => {
cy.get('.card-list-body .actions .q-btn:nth-child(2)').eq(row).click();
});
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();
+ });
+});
diff --git a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js
new file mode 100644
index 000000000..a3c383f74
--- /dev/null
+++ b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInBasicData.spec.js
@@ -0,0 +1,34 @@
+import { vi, describe, expect, it, beforeAll } from 'vitest';
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import InvoiceInBasicData from 'src/pages/InvoiceIn/Card/InvoiceInBasicData.vue';
+
+describe('InvoiceInBasicData', () => {
+ let vm;
+
+ beforeAll(() => {
+ vm = createWrapper(InvoiceInBasicData, {
+ global: {
+ stubs: [],
+ mocks: {
+ fetch: vi.fn(),
+ },
+ },
+ }).vm;
+ });
+
+ 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.upsert();
+
+ expect(vm.quasar.notify).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: `The company can't be empty`,
+ type: 'negative',
+ })
+ );
+ });
+ });
+});
diff --git a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js
new file mode 100644
index 000000000..55ca19d71
--- /dev/null
+++ b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInIntrastat.spec.js
@@ -0,0 +1,34 @@
+import { vi, describe, expect, it, beforeAll } from 'vitest';
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import InvoiceInIntrastat from 'src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue';
+
+describe('InvoiceInIntrastat', () => {
+ let vm;
+
+ beforeAll(() => {
+ vm = createWrapper(InvoiceInIntrastat, {
+ global: {
+ stubs: ['vnPaginate'],
+ mocks: {
+ fetch: vi.fn(),
+ },
+ },
+ }).vm;
+ vi.spyOn(axios, 'get').mockResolvedValue({ data: [{}] });
+ });
+
+ describe('getTotal()', () => {
+ it('should correctly handle the sum', () => {
+ vm.invoceInIntrastat = [
+ { amount: 10, stems: 162 },
+ { amount: 20, stems: 21 },
+ ];
+
+ const totalAmount = vm.getTotal('amount');
+ const totalStems = vm.getTotal('stems');
+
+ expect(totalAmount).toBe(10 + 20);
+ expect(totalStems).toBe(162 + 21);
+ });
+ });
+});
diff --git a/test/vitest/__tests__/pages/InvoiceIn/InvoiceInVat.spec.js b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInVat.spec.js
new file mode 100644
index 000000000..6cc082a35
--- /dev/null
+++ b/test/vitest/__tests__/pages/InvoiceIn/InvoiceInVat.spec.js
@@ -0,0 +1,73 @@
+import { vi, describe, expect, it, beforeAll } from 'vitest';
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import InvoiceInVat from 'src/pages/InvoiceIn/Card/InvoiceInVat.vue';
+
+describe('InvoiceInVat', () => {
+ let vm;
+
+ beforeAll(() => {
+ vm = createWrapper(InvoiceInVat, {
+ global: {
+ stubs: [],
+ mocks: {
+ fetch: vi.fn(),
+ },
+ },
+ }).vm;
+ });
+
+ describe('addExpense()', () => {
+ beforeAll(() => {
+ vi.spyOn(axios, 'post').mockResolvedValue({ data: [] });
+ vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
+ vi.spyOn(vm.quasar, 'notify');
+ });
+
+ it('should throw an error when the code property is undefined', async () => {
+ await vm.addExpense();
+
+ expect(vm.quasar.notify).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: `The code can't be empty`,
+ type: 'negative',
+ })
+ );
+ });
+
+ it('should correctly handle expense addition', async () => {
+ vm.newExpense = {
+ code: 123,
+ isWithheld: false,
+ description: 'Descripción del gasto',
+ };
+
+ await vm.addExpense();
+ expect(vm.quasar.notify).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: 'Data saved',
+ type: 'positive',
+ })
+ );
+ });
+ });
+
+ describe('taxRate()', () => {
+ it('should correctly compute the tax rate', () => {
+ const invoiceInTax = { taxableBase: 100, taxTypeSageFk: 1 };
+ vm.sageTaxTypes = [
+ { id: 1, rate: 10 },
+ { id: 2, rate: 20 },
+ ];
+ const result = vm.taxRate(invoiceInTax);
+ expect(result).toBe((10 / 100) * 100);
+ });
+
+ it('should return 0 if there is not tax rate', () => {
+ const invoiceInTax = { taxableBase: 100, taxTypeSageFk: 1 };
+ vm.sageTaxTypes = [];
+
+ const result = vm.taxRate(invoiceInTax);
+ expect(result).toBe(0);
+ });
+ });
+});