Merge branch 'dev' of https: refs #8004//gitea.verdnatura.es/verdnatura/salix-front into 8004-liliumStyleRemake

This commit is contained in:
Pablo Natek 2024-12-19 10:32:44 +01:00
commit 3eaaea4503
76 changed files with 1017 additions and 746 deletions

View File

@ -1,6 +1,6 @@
{
"name": "salix-front",
"version": "24.52.0",
"version": "25.02.0",
"description": "Salix frontend",
"productName": "Salix",
"author": "Verdnatura",

View File

@ -3,12 +3,12 @@ import { useSession } from 'src/composables/useSession';
import { Router } from 'src/router';
import useNotify from 'src/composables/useNotify.js';
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
import { i18n } from 'src/boot/i18n';
const session = useSession();
const { notify } = useNotify();
const stateQuery = useStateQueryStore();
const baseUrl = '/api/';
axios.defaults.baseURL = baseUrl;
const axiosNoError = axios.create({ baseURL: baseUrl });
@ -16,6 +16,7 @@ const onRequest = (config) => {
const token = session.getToken();
if (token.length && !config.headers.Authorization) {
config.headers.Authorization = token;
config.headers['Accept-Language'] = i18n.global.locale.value;
}
stateQuery.add(config);
return config;

View File

@ -1,9 +1,11 @@
import { boot } from 'quasar/wrappers';
import { createI18n } from 'vue-i18n';
import messages from 'src/i18n';
import { useState } from 'src/composables/useState';
const user = useState().getUser();
const i18n = createI18n({
locale: navigator.language || navigator.userLanguage,
locale: user.value.lang || navigator.language || navigator.userLanguage,
fallbackLocale: 'en',
globalInjection: true,
messages,

View File

@ -87,10 +87,10 @@ async function saveDarkMode(value) {
async function saveLanguage(value) {
const query = `/VnUsers/${user.value.id}`;
try {
await axios.patch(query, {
lang: value,
});
await axios.patch(query, { lang: value });
user.value.lang = value;
useState().setUser(user.value);
onDataSaved();
} catch (error) {
onDataError();

View File

@ -1,13 +1,28 @@
<script setup>
import VnInput from 'src/components/common/VnInput.vue';
import { ref } from 'vue';
import { useAttrs } from 'vue';
defineProps({
step: { type: Number, default: 0.01 },
decimalPlaces: { type: Number, default: 2 },
positive: { type: Boolean, default: true },
});
const model = defineModel({ type: [Number, String] });
const $attrs = useAttrs();
const step = ref($attrs.step || 0.01);
</script>
<template>
<VnInput v-bind="$attrs" v-model.number="model" type="number" :step="step" />
<VnInput
v-bind="$attrs"
v-model.number="model"
type="number"
:step="step"
@input="
(evt) => {
const val = evt.target.value;
if (positive && val < 0) return (model = 0);
const [, decimal] = val.split('.');
if (val && decimal?.length > decimalPlaces)
model = parseFloat(val).toFixed(decimalPlaces);
}
"
/>
</template>

View File

@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
import { useRequired } from 'src/composables/useRequired';
import dataByOrder from 'src/utils/dataByOrder';
import { QItemLabel } from 'quasar';
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
const $attrs = useAttrs();
@ -33,6 +34,10 @@ const $props = defineProps({
type: String,
default: 'id',
},
optionCaption: {
type: String,
default: null,
},
optionFilter: {
type: String,
default: null,
@ -101,6 +106,10 @@ const $props = defineProps({
type: String,
default: null,
},
isOutlined: {
type: Boolean,
default: false,
},
});
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
@ -115,6 +124,15 @@ const noOneOpt = ref({
[optionValue.value]: false,
[optionLabel.value]: noOneText,
});
const styleAttrs = computed(() => {
return $props.isOutlined
? {
dense: true,
outlined: true,
rounded: true,
}
: {};
});
const isLoading = ref(false);
const useURL = computed(() => $props.url);
const value = computed({
@ -288,7 +306,7 @@ function handleKeyDown(event) {
}
const focusableElements = document.querySelectorAll(
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
'a:not([disabled]), button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), details:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])'
);
const currentIndex = Array.prototype.indexOf.call(
focusableElements,
@ -307,9 +325,8 @@ function handleKeyDown(event) {
:options="myOptions"
:option-label="optionLabel"
:option-value="optionValue"
v-bind="$attrs"
v-bind="{ ...$attrs, ...styleAttrs }"
@filter="filterHandler"
@keydown="handleKeyDown"
:emit-value="nullishToTrue($attrs['emit-value'])"
:map-options="nullishToTrue($attrs['map-options'])"
:use-input="nullishToTrue($attrs['use-input'])"
@ -324,13 +341,15 @@ function handleKeyDown(event) {
:input-debounce="useURL ? '300' : '0'"
:loading="isLoading"
@virtual-scroll="onScroll"
@keydown="handleKeyDown"
:data-cy="$attrs.dataCy ?? $attrs.label + '_select'"
:data-url="url"
>
<template #append>
<QIcon
v-show="isClearable && value"
name="close"
@click.stop="
@click="
() => {
value = null;
emit('remove');
@ -358,6 +377,21 @@ function handleKeyDown(event) {
</div>
<slot v-else :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
</template>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection v-if="opt[optionValue] == opt[optionLabel]">
<QItemLabel>{{ opt[optionLabel] }}</QItemLabel>
</QItemSection>
<QItemSection v-else>
<QItemLabel>
{{ opt[optionLabel] }}
</QItemLabel>
<QItemLabel caption v-if="optionCaption !== false">
{{ `#${opt[optionCaption] || opt[optionValue]}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</QSelect>
</template>

View File

@ -98,6 +98,7 @@ function cancel() {
/>
<QBtn
:label="t('globals.confirm')"
:title="t('globals.confirm')"
color="primary"
:loading="isLoading"
@click="confirm()"

View File

@ -6,7 +6,7 @@ import { useRoute } from 'vue-router';
import toDate from 'filters/toDate';
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
const { t } = useI18n();
const { t, te } = useI18n();
const $props = defineProps({
modelValue: {
type: Object,
@ -228,6 +228,14 @@ function sanitizer(params) {
}
return params;
}
const getLocale = (label) => {
const param = label.split('.').at(-1);
const globalLocale = `globals.params.${param}`;
if (te(globalLocale)) return t(globalLocale);
else if (te(t(`params.${param}`)));
else return t(`${route.meta.moduleName}.params.${param}`);
};
</script>
<template>
@ -277,7 +285,12 @@ function sanitizer(params) {
@remove="remove(chip.label)"
data-cy="vnFilterPanelChip"
>
<slot name="tags" :tag="chip" :format-fn="formatValue">
<slot
name="tags"
:tag="chip"
:format-fn="formatValue"
:get-locale="getLocale"
>
<div class="q-gutter-x-xs">
<strong>{{ chip.label }}:</strong>
<span>"{{ formatValue(chip.value) }}"</span>
@ -290,6 +303,7 @@ function sanitizer(params) {
:params="userParams"
:tags="customTags"
:format-fn="formatValue"
:get-locale="getLocale"
:search-fn="search"
/>
</div>
@ -297,7 +311,12 @@ function sanitizer(params) {
<QSeparator />
</QList>
<QList dense class="list q-gutter-y-sm q-mt-sm">
<slot name="body" :params="sanitizer(userParams)" :search-fn="search"></slot>
<slot
name="body"
:params="sanitizer(userParams)"
:get-locale="getLocale"
:search-fn="search"
></slot>
</QList>
</QForm>
<QInnerLoading

View File

@ -74,6 +74,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
mapKey: {
type: String,
default: '',
},
});
const emit = defineEmits(['onFetch', 'onPaginate', 'onChange']);
@ -96,6 +100,7 @@ const arrayData = useArrayData(props.dataKey, {
exprBuilder: props.exprBuilder,
keepOpts: props.keepOpts,
searchUrl: props.searchUrl,
mapKey: props.mapKey,
});
const store = arrayData.store;

View File

@ -0,0 +1,16 @@
import axios from 'axios';
export async function getExchange(amount, currencyFk, dated, decimalPlaces = 2) {
try {
const { data } = await axios.get('ReferenceRates/findOne', {
params: {
filter: {
fields: ['value'],
where: { currencyFk, dated },
},
},
});
return (amount / data.value).toFixed(decimalPlaces);
} catch (e) {
return null;
}
}

View File

@ -1,10 +1,10 @@
import { toCurrency } from 'src/filters';
export function getTotal(rows, key, opts = {}) {
const { currency, cb } = opts;
const { currency, cb, decimalPlaces } = opts;
const total = rows.reduce((acc, row) => acc + +(cb ? cb(row) : row[key] || 0), 0);
return currency
? toCurrency(total, currency == 'default' ? undefined : currency)
: total;
: parseFloat(total).toFixed(decimalPlaces ?? 2);
}

View File

@ -0,0 +1,4 @@
export function useAccountShortToStandard(val) {
if (!val || !/^\d+(\.\d*)$/.test(val)) return;
return val?.replace('.', '0'.repeat(11 - val.length));
}

View File

@ -49,6 +49,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
'exprBuilder',
'searchUrl',
'navigate',
'mapKey',
];
if (typeof userOptions === 'object') {
for (const option in userOptions) {
@ -119,17 +120,12 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
const { limit } = filter;
store.hasMoreData = limit && response.data.length >= limit;
if (append) {
if (!store.data) store.data = [];
for (const row of response.data) store.data.push(row);
} else {
store.data = response.data;
if (!isDialogOpened()) updateRouter && updateStateParams();
}
processData(response.data, { map: !!store.mapKey, append });
if (!append && !isDialogOpened()) updateRouter && updateStateParams();
store.isLoading = false;
canceller = null;
return response;
}
@ -288,6 +284,31 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
router.replace(newUrl);
}
function processData(data, { map = true, append = true }) {
if (!append) {
store.data = [];
store.map = new Map();
}
if (!Array.isArray(data)) store.data = data;
else if (!map && append) for (const row of data) store.data.push(row);
else
for (const row of data) {
const key = row[store.mapKey];
const val = { ...row, key };
if (store.map.has(key)) {
const { position } = store.map.get(key);
val.position = position;
store.map.set(key, val);
store.data[position] = val;
} else {
val.position = store.map.size;
store.map.set(key, val);
store.data.push(val);
}
}
}
const totalRows = computed(() => (store.data && store.data.length) || 0);
const isLoading = computed(() => store.isLoading || false);

View File

@ -333,11 +333,23 @@ globals:
packing: ITP
myTeam: My team
departmentFk: Department
from: From
to: To
supplierFk: Supplier
supplierRef: Supplier ref
serial: Serial
amount: Importe
awbCode: AWB
correctedFk: Rectified
correctingFk: Rectificative
daysOnward: Days onward
countryFk: Country
companyFk: Company
changePass: Change password
deleteConfirmTitle: Delete selected elements
changeState: Change state
raid: 'Raid {daysInForward} days'
isVies: Vies
errors:
statusUnauthorized: Access denied
statusInternalServerError: An internal server error has ocurred
@ -731,7 +743,6 @@ supplier:
sageTransactionTypeFk: Sage transaction type
supplierActivityFk: Supplier activity
isTrucker: Trucker
isVies: Vies
billingData:
payMethodFk: Billing data
payDemFk: Payment deadline

View File

@ -336,12 +336,22 @@ globals:
SSN: NSS
fi: NIF
myTeam: Mi equipo
from: Desde
to: Hasta
supplierFk: Proveedor
supplierRef: Ref. proveedor
serial: Serie
amount: Importe
awbCode: AWB
daysOnward: Días adelante
packing: ITP
countryFk: País
companyFk: Empresa
changePass: Cambiar contraseña
deleteConfirmTitle: Eliminar los elementos seleccionados
changeState: Cambiar estado
raid: 'Redada {daysInForward} días'
isVies: Vies
errors:
statusUnauthorized: Acceso denegado
statusInternalServerError: Ha ocurrido un error interno del servidor
@ -726,7 +736,6 @@ supplier:
sageTransactionTypeFk: Tipo de transacción sage
supplierActivityFk: Actividad proveedor
isTrucker: Transportista
isVies: Vies
billingData:
payMethodFk: Forma de pago
payDemFk: Plazo de pago

View File

@ -57,6 +57,7 @@ function onFetch(rows, newRows) {
const price = row.quantity * sale.price;
const discount = (sale.discount * price) / 100;
amountClaimed.value = amountClaimed.value + (price - discount);
}
}
@ -207,9 +208,10 @@ async function saveWhenHasChanges() {
selection="multiple"
v-model:selected="selected"
:grid="$q.screen.lt.md"
>
<template #body-cell-claimed="{ row }">
<QTd auto-width align="right" class="text-primary">
<QTd auto-width align="right" class="text-primary shrink">
<QInput
v-model.number="row.quantity"
type="number"
@ -220,7 +222,7 @@ async function saveWhenHasChanges() {
</QTd>
</template>
<template #body-cell-description="{ row, value }">
<QTd auto-width align="right" class="text-primary">
<QTd auto-width align="right" class="link expand">
{{ value }}
<ItemDescriptorProxy
:id="row.sale.itemFk"
@ -228,7 +230,7 @@ async function saveWhenHasChanges() {
</QTd>
</template>
<template #body-cell-discount="{ row, value, rowIndex }">
<QTd auto-width align="right" class="text-primary">
<QTd auto-width align="right" class="text-primary shrink">
{{ value }}
<VnDiscount
:quantity="row.quantity"
@ -264,7 +266,7 @@ async function saveWhenHasChanges() {
</QItemSection>
<QItemSection side>
<template v-if="column.name === 'claimed'">
<QItemLabel class="text-primary">
<QItemLabel class="text-primary shrink">
<QInput
v-model.number="
props.row.quantity
@ -282,7 +284,7 @@ async function saveWhenHasChanges() {
<template
v-else-if="column.name === 'discount'"
>
<QItemLabel class="text-primary">
<QItemLabel class="text-primary shrink">
{{ column.value }}
<VnDiscount
:quantity="props.row.quantity"
@ -330,6 +332,7 @@ async function saveWhenHasChanges() {
.grid-style-transition {
transition: transform 0.28s, background-color 0.28s;
}
</style>
<i18n>

View File

@ -345,12 +345,9 @@ function claimUrl(section) {
<span v-if="col.name != 'description'">{{
t(col.value)
}}</span>
<QBtn
v-if="col.name == 'description'"
flat
color="blue"
>{{ col.value }}</QBtn
>
<span class="link" v-if="col.name === 'description'">{{
t(col.value)
}}</span>
<ItemDescriptorProxy
v-if="col.name == 'description'"
:id="props.row.sale.itemFk"

View File

@ -110,7 +110,7 @@ function handleLocation(data, location) {
<VnRow>
<QCheckbox :label="t('Has to invoice')" v-model="data.hasToInvoice" />
<div>
<QCheckbox :label="t('Vies')" v-model="data.isVies" />
<QCheckbox :label="t('globals.isVies')" v-model="data.isVies" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip>
{{ t('whenActivatingIt') }}
@ -169,7 +169,6 @@ es:
Active: Activo
Frozen: Congelado
Has to invoice: Factura
Vies: Vies
Notify by email: Notificar vía e-mail
Invoice by address: Facturar por consignatario
Is equalizated: Recargo de equivalencia

View File

@ -173,7 +173,7 @@ const sumRisk = ({ clientRisks }) => {
:label="t('customer.summary.notifyByEmail')"
:value="entity.isToBeMailed"
/>
<VnLv :label="t('customer.summary.vies')" :value="entity.isVies" />
<VnLv :label="t('globals.isVies')" :value="entity.isVies" />
</VnRow>
</QCard>
<QCard class="vn-one">

View File

@ -262,7 +262,7 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.isVies'),
label: t('globals.isVies'),
name: 'isVies',
columnFilter: {
inWhere: true,

View File

@ -88,7 +88,6 @@ customer:
businessTypeFk: Business type
sageTaxTypeFk: Sage tax type
sageTransactionTypeFk: Sage tr. type
isVies: Vies
isTaxDataChecked: Verified data
isFreezed: Freezed
hasToInvoice: Invoice

View File

@ -90,7 +90,6 @@ customer:
businessTypeFk: Tipo de negocio
sageTaxTypeFk: Tipo de impuesto Sage
sageTransactionTypeFk: Tipo tr. sage
isVies: Vies
isTaxDataChecked: Datos comprobados
isFreezed: Congelado
hasToInvoice: Factura

View File

@ -249,6 +249,7 @@ function deleteFile(dmsFk) {
:options="currencies"
option-value="id"
option-label="code"
sort-by="id"
/>
<VnSelect
@ -262,7 +263,7 @@ function deleteFile(dmsFk) {
</VnRow>
<VnRow>
<VnSelect
:label="t('invoiceIn.summary.sage')"
:label="t('InvoiceIn.summary.sage')"
v-model="data.withholdingSageFk"
:options="sageWithholdings"
option-value="id"

View File

@ -1,23 +1,21 @@
<script setup>
import { ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import { ref, computed, capitalize } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
import { useCapitalize } from 'src/composables/useCapitalize';
import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { push, currentRoute } = useRouter();
const route = useRoute();
const { t } = useI18n();
const invoiceId = +currentRoute.value.params.id;
const arrayData = useArrayData();
const invoiceIn = computed(() => arrayData.store.data);
const invoiceInCorrectionRef = ref();
const filter = {
include: { relation: 'invoiceIn' },
where: { correctingFk: invoiceId },
where: { correctingFk: route.params.id },
};
const columns = computed(() => [
{
@ -31,7 +29,7 @@ const columns = computed(() => [
},
{
name: 'type',
label: useCapitalize(t('globals.type')),
label: capitalize(t('globals.type')),
field: (row) => row.cplusRectificationTypeFk,
options: cplusRectificationTypes.value,
model: 'cplusRectificationTypeFk',
@ -43,10 +41,10 @@ const columns = computed(() => [
},
{
name: 'class',
label: useCapitalize(t('globals.class')),
field: (row) => row.siiTypeInvoiceOutFk,
options: siiTypeInvoiceOuts.value,
model: 'siiTypeInvoiceOutFk',
label: capitalize(t('globals.class')),
field: (row) => row.siiTypeInvoiceInFk,
options: siiTypeInvoiceIns.value,
model: 'siiTypeInvoiceInFk',
optionValue: 'id',
optionLabel: 'code',
sortable: true,
@ -55,7 +53,7 @@ const columns = computed(() => [
},
{
name: 'reason',
label: useCapitalize(t('globals.reason')),
label: capitalize(t('globals.reason')),
field: (row) => row.invoiceCorrectionTypeFk,
options: invoiceCorrectionTypes.value,
model: 'invoiceCorrectionTypeFk',
@ -67,13 +65,10 @@ const columns = computed(() => [
},
]);
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceOuts = ref([]);
const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]);
const rowsSelected = ref([]);
const requiredFieldRule = (val) => val || t('globals.requiredField');
const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`);
</script>
<template>
<FetchData
@ -82,9 +77,9 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
auto-load
/>
<FetchData
url="SiiTypeInvoiceOuts"
url="SiiTypeInvoiceIns"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)"
@on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load
/>
<FetchData
@ -99,17 +94,14 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
url="InvoiceInCorrections"
:filter="filter"
auto-load
v-model:selected="rowsSelected"
primary-key="correctingFk"
@save-changes="onSave"
:default-remove="false"
>
<template #body="{ rows }">
<QTable
v-model:selected="rowsSelected"
:columns="columns"
:rows="rows"
row-key="$index"
selection="single"
:grid="$q.screen.lt.sm"
:pagination="{ rowsPerPage: 0 }"
>
@ -121,8 +113,17 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
:readonly="row.invoiceIn.isBooked"
/>
:disable="row.invoiceIn.isBooked"
:filter-options="['description']"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel>{{ opt.description }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QTd>
</template>
<template #body-cell-class="{ row, col }">
@ -134,8 +135,20 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:option-value="col.optionValue"
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
:readonly="row.invoiceIn.isBooked"
/>
:filter-options="['code', 'description']"
:disable="row.invoiceIn.isBooked"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.code }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QTd>
</template>
<template #body-cell-reason="{ row, col }">
@ -147,7 +160,7 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:option-value="col.optionValue"
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
:readonly="row.invoiceIn.isBooked"
:disable="row.invoiceIn.isBooked"
/>
</QTd>
</template>
@ -155,7 +168,6 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
</template>
</CrudModel>
</template>
<style lang="scss" scoped></style>
<i18n>
es:
Original invoice: Factura origen

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, reactive, computed, onBeforeMount } from 'vue';
import { ref, reactive, computed, onBeforeMount, capitalize } from 'vue';
import { useRouter, onBeforeRouteUpdate } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
@ -15,7 +15,6 @@ import FetchData from 'src/components/FetchData.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import { useCapitalize } from 'src/composables/useCapitalize';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import InvoiceInToBook from '../InvoiceInToBook.vue';
@ -37,7 +36,7 @@ const totalAmount = ref();
const currentAction = ref();
const config = ref();
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceOuts = ref([]);
const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]);
const actions = {
unbook: {
@ -91,7 +90,7 @@ const routes = reactive({
return {
name: 'InvoiceInList',
query: {
params: JSON.stringify({ supplierFk: id }),
table: JSON.stringify({ supplierFk: id }),
},
};
},
@ -100,7 +99,7 @@ const routes = reactive({
return {
name: 'InvoiceInList',
query: {
params: JSON.stringify({ correctedFk: entityId.value }),
table: JSON.stringify({ correctedFk: entityId.value }),
},
};
}
@ -119,21 +118,21 @@ const routes = reactive({
const correctionFormData = reactive({
invoiceReason: 2,
invoiceType: 2,
invoiceClass: 6,
invoiceClass: 8,
});
const isNotFilled = computed(() => Object.values(correctionFormData).includes(null));
onBeforeMount(async () => {
await setInvoiceCorrection(entityId.value);
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
totalAmount.value = data.totalDueDay;
totalAmount.value = data.totalTaxableBase;
});
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
await setInvoiceCorrection(to.params.id);
const { data } = await axios.get(`InvoiceIns/${to.params.id}/getTotals`);
totalAmount.value = data.totalDueDay;
totalAmount.value = data.totalTaxableBase;
}
});
@ -207,7 +206,8 @@ const isAgricultural = () => {
};
function showPdfInvoice() {
if (isAgricultural()) openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`);
if (isAgricultural())
openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`, null, '_blank');
}
function sendPdfInvoiceConfirmation() {
@ -262,9 +262,9 @@ const createInvoiceInCorrection = async () => {
auto-load
/>
<FetchData
url="SiiTypeInvoiceOuts"
url="siiTypeInvoiceIns"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)"
@on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load
/>
<FetchData
@ -355,10 +355,13 @@ const createInvoiceInCorrection = async () => {
</QItem>
</template>
<template #body="{ entity }">
<VnLv :label="t('invoiceIn.list.issued')" :value="toDate(entity.issued)" />
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
<VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoiceIn.list.supplier')">
<VnLv :label="t('InvoiceIn.list.issued')" :value="toDate(entity.issued)" />
<VnLv
:label="t('InvoiceIn.summary.bookedDate')"
:value="toDate(entity.booked)"
/>
<VnLv :label="t('InvoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('InvoiceIn.list.supplier')">
<template #value>
<span class="link">
{{ entity?.supplier?.nickname }}
@ -375,7 +378,7 @@ const createInvoiceInCorrection = async () => {
color="primary"
:to="routes.getSupplier(entity.supplierFk)"
>
<QTooltip>{{ t('invoiceIn.list.supplier') }}</QTooltip>
<QTooltip>{{ t('InvoiceIn.list.supplier') }}</QTooltip>
</QBtn>
<QBtn
size="md"
@ -391,7 +394,7 @@ const createInvoiceInCorrection = async () => {
color="primary"
:to="routes.getTickets(entity.supplierFk)"
>
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
<QTooltip>{{ t('InvoiceIn.descriptor.ticketList') }}</QTooltip>
</QBtn>
<QBtn
v-if="
@ -435,9 +438,9 @@ const createInvoiceInCorrection = async () => {
readonly
/>
<VnSelect
:label="`${useCapitalize(t('globals.class'))}`"
:label="`${capitalize(t('globals.class'))}`"
v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceOuts"
:options="siiTypeInvoiceIns"
option-value="id"
option-label="code"
:required="true"
@ -445,15 +448,27 @@ const createInvoiceInCorrection = async () => {
</QItemSection>
<QItemSection>
<VnSelect
:label="`${useCapitalize(t('globals.type'))}`"
:label="`${capitalize(t('globals.type'))}`"
v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes"
option-value="id"
option-label="description"
:required="true"
/>
>
<template #option="{ opt }">
<QItem>
<QItemSection>
<QItemLabel
>{{ opt.code }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
<div></div>
</template>
</VnSelect>
<VnSelect
:label="`${useCapitalize(t('globals.reason'))}`"
:label="`${capitalize(t('globals.reason'))}`"
v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes"
option-value="id"

View File

@ -25,6 +25,7 @@ const banks = ref([]);
const invoiceInFormRef = ref();
const invoiceId = +route.params.id;
const filter = { where: { invoiceInFk: invoiceId } };
const areRows = ref(false);
const columns = computed(() => [
{
@ -143,8 +144,6 @@ async function insert() {
}"
:disable="!isNotEuro(currency)"
v-model="row.foreignValue"
clearable
clear-icon="close"
/>
</QTd>
</template>
@ -230,7 +229,14 @@ async function insert() {
</template>
</CrudModel>
<QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn color="primary" icon="add" shortcut="+" size="lg" round @click="insert" />
<QBtn
color="primary"
icon="add"
shortcut="+"
size="lg"
round
@click="!areRows ? insert() : invoiceInFormRef.insert()"
/>
</QPageSticky>
</template>
<style lang="scss" scoped>

View File

@ -26,7 +26,7 @@ const columns = computed(() => [
options: intrastats.value,
model: 'intrastatFk',
optionValue: 'id',
optionLabel: 'description',
optionLabel: (row) => `${row.id}: ${row.description}`,
sortable: true,
tabIndex: 1,
align: 'left',
@ -68,12 +68,6 @@ const columns = computed(() => [
align: 'left',
},
]);
const formatOpt = (row, { model, options }, prop) => {
const obj = row[model];
const option = options.find(({ id }) => id == obj);
return option ? `${obj}:${option[prop]}` : '';
};
</script>
<template>
<FetchData
@ -118,12 +112,10 @@ const formatOpt = (row, { model, options }, prop) => {
<VnSelect
v-model="row[col.model]"
:options="col.options"
option-value="id"
option-label="description"
:option-value="col.optionValue"
:option-label="col.optionLabel"
:filter-options="['id', 'description']"
:hide-selected="false"
:fill-input="false"
:display-value="formatOpt(row, col, 'description')"
data-cy="intrastat-code"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -138,8 +130,8 @@ const formatOpt = (row, { model, options }, prop) => {
<VnSelect
v-model="row[col.model]"
:options="col.options"
option-value="id"
option-label="code"
:option-value="col.optionValue"
:option-label="col.optionLabel"
/>
</QTd>
</template>
@ -154,7 +146,7 @@ const formatOpt = (row, { model, options }, prop) => {
{{ getTotal(rows, 'net') }}
</QTd>
<QTd>
{{ getTotal(rows, 'stems') }}
{{ getTotal(rows, 'stems', { decimalPlaces: 0 }) }}
</QTd>
<QTd />
</QTr>
@ -174,7 +166,9 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['intrastatFk']"
:options="intrastats"
option-value="id"
option-label="description"
:option-label="
(row) => `${row.id}:${row.description}`
"
:filter-options="['id', 'description']"
>
<template #option="scope">
@ -248,11 +242,6 @@ const formatOpt = (row, { model, options }, prop) => {
}
}
</style>
<style lang="scss" scoped>
:deep(.q-table tr .q-td:nth-child(2) input) {
display: none;
}
</style>
<i18n>
en:
amount: Amount
@ -261,7 +250,7 @@ const formatOpt = (row, { model, options }, prop) => {
country: Country
es:
Code: Código
amount: Cantidad
amount: Valor mercancía
net: Neto
stems: Tallos
country: País

View File

@ -26,14 +26,14 @@ const intrastatTotals = ref({ amount: 0, net: 0, stems: 0 });
const vatColumns = ref([
{
name: 'expense',
label: 'invoiceIn.summary.expense',
label: 'InvoiceIn.summary.expense',
field: (row) => row.expenseFk,
sortable: true,
align: 'left',
},
{
name: 'landed',
label: 'invoiceIn.summary.taxableBase',
label: 'InvoiceIn.summary.taxableBase',
field: (row) => row.taxableBase,
format: (value) => toCurrency(value),
sortable: true,
@ -41,7 +41,7 @@ const vatColumns = ref([
},
{
name: 'vat',
label: 'invoiceIn.summary.sageVat',
label: 'InvoiceIn.summary.sageVat',
field: (row) => {
if (row.taxTypeSage) return `#${row.taxTypeSage.id} : ${row.taxTypeSage.vat}`;
},
@ -51,7 +51,7 @@ const vatColumns = ref([
},
{
name: 'transaction',
label: 'invoiceIn.summary.sageTransaction',
label: 'InvoiceIn.summary.sageTransaction',
field: (row) => {
if (row.transactionTypeSage)
return `#${row.transactionTypeSage.id} : ${row.transactionTypeSage?.transaction}`;
@ -62,7 +62,7 @@ const vatColumns = ref([
},
{
name: 'rate',
label: 'invoiceIn.summary.rate',
label: 'InvoiceIn.summary.rate',
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
format: (value) => toCurrency(value),
sortable: true,
@ -70,7 +70,7 @@ const vatColumns = ref([
},
{
name: 'currency',
label: 'invoiceIn.summary.currency',
label: 'InvoiceIn.summary.currency',
field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value),
sortable: true,
@ -81,21 +81,21 @@ const vatColumns = ref([
const dueDayColumns = ref([
{
name: 'date',
label: 'invoiceIn.summary.dueDay',
label: 'InvoiceIn.summary.dueDay',
field: (row) => toDate(row.dueDated),
sortable: true,
align: 'left',
},
{
name: 'bank',
label: 'invoiceIn.summary.bank',
label: 'InvoiceIn.summary.bank',
field: (row) => row.bank.bank,
sortable: true,
align: 'left',
},
{
name: 'amount',
label: 'invoiceIn.list.amount',
label: 'InvoiceIn.list.amount',
field: (row) => row.amount,
format: (value) => toCurrency(value),
sortable: true,
@ -103,7 +103,7 @@ const dueDayColumns = ref([
},
{
name: 'landed',
label: 'invoiceIn.summary.foreignValue',
label: 'InvoiceIn.summary.foreignValue',
field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value),
sortable: true,
@ -114,7 +114,7 @@ const dueDayColumns = ref([
const intrastatColumns = ref([
{
name: 'code',
label: 'invoiceIn.summary.code',
label: 'InvoiceIn.summary.code',
field: (row) => {
return `${row.intrastat.id}: ${row.intrastat?.description}`;
},
@ -123,21 +123,21 @@ const intrastatColumns = ref([
},
{
name: 'amount',
label: 'invoiceIn.list.amount',
label: 'InvoiceIn.list.amount',
field: (row) => toCurrency(row.amount),
sortable: true,
align: 'left',
},
{
name: 'net',
label: 'invoiceIn.summary.net',
label: 'InvoiceIn.summary.net',
field: (row) => row.net,
sortable: true,
align: 'left',
},
{
name: 'stems',
label: 'invoiceIn.summary.stems',
label: 'InvoiceIn.summary.stems',
field: (row) => row.stems,
format: (value) => value,
sortable: true,
@ -145,7 +145,7 @@ const intrastatColumns = ref([
},
{
name: 'landed',
label: 'invoiceIn.summary.country',
label: 'InvoiceIn.summary.country',
field: (row) => row.country?.code,
format: (value) => value,
sortable: true,
@ -210,7 +210,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/>
</QCardSection>
<VnLv
:label="t('invoiceIn.list.supplier')"
:label="t('InvoiceIn.list.supplier')"
:value="entity.supplier?.name"
>
<template #value>
@ -221,14 +221,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</template>
</VnLv>
<VnLv
:label="t('invoiceIn.list.supplierRef')"
:label="t('InvoiceIn.list.supplierRef')"
:value="entity.supplierRef"
/>
<VnLv
:label="t('invoiceIn.summary.currency')"
:label="t('InvoiceIn.summary.currency')"
:value="entity.currency?.code"
/>
<VnLv :label="t('invoiceIn.serial')" :value="`${entity.serial}`" />
<VnLv :label="t('InvoiceIn.serial')" :value="`${entity.serial}`" />
<VnLv
:label="t('globals.country')"
:value="entity.supplier?.country?.code"
/>
</QCard>
<QCard class="vn-one">
<QCardSection class="q-pa-none">
@ -239,21 +243,22 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCardSection>
<VnLv
:ellipsis-value="false"
:label="t('invoiceIn.summary.issued')"
:label="t('InvoiceIn.summary.issued')"
:value="toDate(entity.issued)"
/>
<VnLv
:label="t('invoiceIn.summary.operated')"
:label="t('InvoiceIn.summary.operated')"
:value="toDate(entity.operated)"
/>
<VnLv
:label="t('invoiceIn.summary.bookEntried')"
:label="t('InvoiceIn.summary.bookEntried')"
:value="toDate(entity.bookEntried)"
/>
<VnLv
:label="t('invoiceIn.summary.bookedDate')"
:label="t('InvoiceIn.summary.bookedDate')"
:value="toDate(entity.booked)"
/>
<VnLv :label="t('globals.isVies')" :value="entity.supplier?.isVies" />
</QCard>
<QCard class="vn-one">
<QCardSection class="q-pa-none">
@ -263,18 +268,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/>
</QCardSection>
<VnLv
:label="t('invoiceIn.summary.sage')"
:label="t('InvoiceIn.summary.sage')"
:value="entity.sageWithholding?.withholding"
/>
<VnLv
:label="t('invoiceIn.summary.vat')"
:label="t('InvoiceIn.summary.vat')"
:value="entity.expenseDeductible?.name"
/>
<VnLv
:label="t('invoiceIn.card.company')"
:label="t('InvoiceIn.card.company')"
:value="entity.company?.code"
/>
<VnLv :label="t('invoiceIn.isBooked')" :value="invoiceIn?.isBooked" />
<VnLv :label="t('InvoiceIn.isBooked')" :value="invoiceIn?.isBooked" />
</QCard>
<QCard class="vn-one">
<QCardSection class="q-pa-none">
@ -285,11 +290,11 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCardSection>
<QCardSection class="q-pa-none">
<VnLv
:label="t('invoiceIn.summary.taxableBase')"
:label="t('InvoiceIn.summary.taxableBase')"
:value="toCurrency(entity.totals.totalTaxableBase)"
/>
<VnLv label="Total" :value="toCurrency(entity.totals.totalVat)" />
<VnLv :label="t('invoiceIn.summary.dueTotal')">
<VnLv :label="t('InvoiceIn.summary.dueTotal')">
<template #value>
<QChip
dense
@ -297,8 +302,8 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
:color="amountsNotMatch ? 'negative' : 'transparent'"
:title="
amountsNotMatch
? t('invoiceIn.summary.noMatch')
: t('invoiceIn.summary.dueTotal')
? t('InvoiceIn.summary.noMatch')
: t('InvoiceIn.summary.dueTotal')
"
>
{{ toCurrency(entity.totals.totalDueDay) }}
@ -309,7 +314,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCard>
<!--Vat-->
<QCard v-if="entity.invoiceInTax.length" class="vat">
<VnTitle :url="getLink('vat')" :text="t('invoiceIn.card.vat')" />
<VnTitle :url="getLink('vat')" :text="t('InvoiceIn.card.vat')" />
<QTable
:columns="vatColumns"
:rows="entity.invoiceInTax"
@ -357,7 +362,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCard>
<!--Due Day-->
<QCard v-if="entity.invoiceInDueDay.length" class="due-day">
<VnTitle :url="getLink('due-day')" :text="t('invoiceIn.card.dueDay')" />
<VnTitle :url="getLink('due-day')" :text="t('InvoiceIn.card.dueDay')" />
<QTable :columns="dueDayColumns" :rows="entity.invoiceInDueDay" flat>
<template #header="dueDayProps">
<QTr :props="dueDayProps" class="bg">
@ -395,7 +400,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCard v-if="entity.invoiceInIntrastat.length">
<VnTitle
:url="getLink('intrastat')"
:text="t('invoiceIn.card.intrastat')"
:text="t('InvoiceIn.card.intrastat')"
/>
<QTable
:columns="intrastatColumns"

View File

@ -11,12 +11,14 @@ import CrudModel from 'src/components/CrudModel.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue';
import { getExchange } from 'src/composables/getExchange';
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
const { t } = useI18n();
const arrayData = useArrayData();
const route = useRoute();
const invoiceIn = computed(() => arrayData.store.data);
const invoiceId = +useRoute().params.id;
const currency = computed(() => invoiceIn.value?.currency?.code);
const expenses = ref([]);
const sageTaxTypes = ref([]);
@ -39,9 +41,8 @@ const columns = computed(() => [
options: expenses.value,
model: 'expenseFk',
optionValue: 'id',
optionLabel: 'id',
optionLabel: (row) => `${row.id}: ${row.name}`,
sortable: true,
tabIndex: 1,
align: 'left',
},
{
@ -50,7 +51,6 @@ const columns = computed(() => [
field: (row) => row.taxableBase,
model: 'taxableBase',
sortable: true,
tabIndex: 2,
align: 'left',
},
{
@ -60,9 +60,8 @@ const columns = computed(() => [
options: sageTaxTypes.value,
model: 'taxTypeSageFk',
optionValue: 'id',
optionLabel: 'id',
optionLabel: (row) => `${row.id}: ${row.vat}`,
sortable: true,
tabindex: 3,
align: 'left',
},
{
@ -72,16 +71,14 @@ const columns = computed(() => [
options: sageTransactionTypes.value,
model: 'transactionTypeSageFk',
optionValue: 'id',
optionLabel: 'id',
optionLabel: (row) => `${row.id}: ${row.transaction}`,
sortable: true,
tabIndex: 4,
align: 'left',
},
{
name: 'rate',
label: t('Rate'),
sortable: true,
tabIndex: 5,
field: (row) => taxRate(row, row.taxTypeSageFk),
align: 'left',
},
@ -89,7 +86,6 @@ const columns = computed(() => [
name: 'foreignvalue',
label: t('Foreign value'),
sortable: true,
tabIndex: 6,
field: (row) => row.foreignValue,
align: 'left',
},
@ -106,7 +102,7 @@ const filter = {
'transactionTypeSageFk',
],
where: {
invoiceInFk: invoiceId,
invoiceInFk: route.params.id,
},
};
@ -120,14 +116,20 @@ function taxRate(invoiceInTax) {
const taxTypeSage = taxRateSelection?.rate ?? 0;
const taxableBase = invoiceInTax?.taxableBase ?? 0;
return (taxTypeSage / 100) * taxableBase;
return ((taxTypeSage / 100) * taxableBase).toFixed(2);
}
const formatOpt = (row, { model, options }, prop) => {
const obj = row[model];
const option = options.find(({ id }) => id == obj);
return option ? `${obj}:${option[prop]}` : '';
};
function autocompleteExpense(evt, row, col) {
const val = evt.target.value;
if (!val) return;
const param = isNaN(val) ? row[col.model] : val;
const lookup = expenses.value.find(
({ id }) => id == useAccountShortToStandard(param)
);
if (lookup) row[col.model] = lookup;
}
</script>
<template>
<FetchData
@ -148,10 +150,10 @@ const formatOpt = (row, { model, options }, prop) => {
data-key="InvoiceInTaxes"
url="InvoiceInTaxes"
:filter="filter"
:data-required="{ invoiceInFk: invoiceId }"
:data-required="{ invoiceInFk: $route.params.id }"
auto-load
v-model:selected="rowsSelected"
:go-to="`/invoice-in/${invoiceId}/due-day`"
:go-to="`/invoice-in/${$route.params.id}/due-day`"
>
<template #body="{ rows }">
<QTable
@ -171,6 +173,7 @@ const formatOpt = (row, { model, options }, prop) => {
:option-label="col.optionLabel"
:filter-options="['id', 'name']"
:tooltip="t('Create a new expense')"
@keydown.tab="autocompleteExpense($event, row, col)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -187,13 +190,7 @@ const formatOpt = (row, { model, options }, prop) => {
</template>
<template #body-cell-taxablebase="{ row }">
<QTd>
{{ currency }}
<VnInputNumber
:class="{
'no-pointer-events': isNotEuro(currency),
}"
:disable="isNotEuro(currency)"
label=""
clear-icon="close"
v-model="row.taxableBase"
clearable
@ -208,9 +205,7 @@ const formatOpt = (row, { model, options }, prop) => {
:option-value="col.optionValue"
:option-label="col.optionLabel"
:filter-options="['id', 'vat']"
:hide-selected="false"
:fill-input="false"
:display-value="formatOpt(row, col, 'vat')"
data-cy="vat-sageiva"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -233,11 +228,6 @@ const formatOpt = (row, { model, options }, prop) => {
:option-value="col.optionValue"
:option-label="col.optionLabel"
:filter-options="['id', 'transaction']"
:autofocus="col.tabIndex == 1"
input-debounce="0"
:hide-selected="false"
:fill-input="false"
:display-value="formatOpt(row, col, 'transaction')"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -262,6 +252,16 @@ const formatOpt = (row, { model, options }, prop) => {
}"
:disable="!isNotEuro(currency)"
v-model="row.foreignValue"
@update:model-value="
async (val) => {
if (!isNotEuro(currency)) return;
row.taxableBase = await getExchange(
val,
row.currencyFk,
invoiceIn.issued
);
}
"
/>
</QTd>
</template>
@ -305,7 +305,7 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['expenseFk']"
:options="expenses"
option-value="id"
option-label="name"
:option-label="(row) => `${row.id}:${row.name}`"
:filter-options="['id', 'name']"
:tooltip="t('Create a new expense')"
>
@ -339,7 +339,7 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['taxTypeSageFk']"
:options="sageTaxTypes"
option-value="id"
option-label="vat"
:option-label="(row) => `${row.id}:${row.vat}`"
:filter-options="['id', 'vat']"
>
<template #option="scope">
@ -362,7 +362,9 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['transactionTypeSageFk']"
:options="sageTransactionTypes"
option-value="id"
option-label="transaction"
:option-label="
(row) => `${row.id}:${row.transaction}`
"
:filter-options="['id', 'transaction']"
>
<template #option="scope">
@ -418,11 +420,6 @@ const formatOpt = (row, { model, options }, prop) => {
.bg {
background-color: var(--vn-light-gray);
}
:deep(.q-table tr td:nth-child(n + 4):nth-child(-n + 5) input) {
display: none;
}
@media (max-width: $breakpoint-xs) {
.q-dialog {
.q-card {

View File

@ -83,7 +83,7 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
</template>
</VnSelect>
<VnInput
:label="t('invoiceIn.list.supplierRef')"
:label="t('InvoiceIn.list.supplierRef')"
v-model="data.supplierRef"
/>
</VnRow>
@ -97,10 +97,10 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
map-options
hide-selected
:required="true"
:rules="validate('invoiceIn.companyFk')"
:rules="validate('InvoiceIn.companyFk')"
/>
<VnInputDate
:label="t('invoiceIn.summary.issued')"
:label="t('InvoiceIn.summary.issued')"
v-model="data.issued"
/>
</VnRow>

View File

@ -1,41 +1,66 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSelect from 'components/common/VnSelect.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import FetchData from 'components/FetchData.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { dateRange } from 'src/filters';
import { date } from 'quasar';
const { t } = useI18n();
defineProps({ dataKey: { type: String, required: true } });
const activities = ref([]);
const dateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
function handleDaysAgo(params, daysAgo) {
const [from, to] = dateRange(Date.vnNew());
if (!daysAgo && daysAgo !== 0) {
Object.assign(params, { from: undefined, to: undefined });
} else {
from.setDate(from.getDate() - daysAgo);
Object.assign(params, {
from: date.formatDate(from, dateFormat),
to: date.formatDate(to, dateFormat),
});
}
}
</script>
<template>
<FetchData
url="SupplierActivities"
auto-load
@on-fetch="(data) => (activities = data)"
/>
<VnFilterPanel :data-key="dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<VnFilterPanel :data-key="dataKey" :search-button="true" :hidden-tags="['daysAgo']">
<template #tags="{ tag, formatFn, getLocale }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<strong>{{ getLocale(tag.label) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<template #body="{ params, searchFn, getLocale }">
<QItem>
<QItemSection>
<VnInputDate :label="t('From')" v-model="params.from" is-outlined />
<VnInputDate
:label="$t('globals.from')"
v-model="params.from"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
<VnInputDate
:label="$t('globals.to')"
v-model="params.to"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputNumber
:label="$t('globals.daysAgo')"
v-model="params.daysAgo"
is-outlined
:step="0"
@update:model-value="(val) => handleDaysAgo(params, val)"
@remove="(val) => handleDaysAgo(params, val)"
/>
</QItemSection>
</QItem>
<QItem>
@ -44,20 +69,18 @@ const activities = ref([]);
v-model="params.supplierFk"
url="Suppliers"
:fields="['id', 'nickname']"
:label="t('params.supplierFk')"
option-value="id"
:label="getLocale('supplierFk')"
option-label="nickname"
dense
outlined
rounded
:filter-options="['id', 'name']"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('params.supplierRef')"
:label="getLocale('supplierRef')"
v-model="params.supplierRef"
is-outlined
lazy-rules
@ -67,7 +90,7 @@ const activities = ref([]);
<QItem>
<QItemSection>
<VnInput
:label="t('params.fi')"
:label="getLocale('fi')"
v-model="params.fi"
is-outlined
lazy-rules
@ -77,7 +100,7 @@ const activities = ref([]);
<QItem>
<QItemSection>
<VnInput
:label="t('params.serial')"
:label="getLocale('serial')"
v-model="params.serial"
is-outlined
lazy-rules
@ -87,7 +110,7 @@ const activities = ref([]);
<QItem>
<QItemSection>
<VnInput
:label="t('params.account')"
:label="getLocale('account')"
v-model="params.account"
is-outlined
lazy-rules
@ -97,7 +120,7 @@ const activities = ref([]);
<QItem>
<QItemSection>
<VnInput
:label="t('params.awb')"
:label="getLocale('globals.params.awbCode')"
v-model="params.awbCode"
is-outlined
lazy-rules
@ -107,24 +130,34 @@ const activities = ref([]);
<QItem>
<QItemSection>
<VnInputNumber
:label="t('Amount')"
:label="$t('globals.amount')"
v-model="params.amount"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
v-model="params.companyFk"
:label="$t('globals.company')"
url="Companies"
option-label="code"
:fields="['id', 'code']"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('invoiceIn.isBooked')"
:label="$t('InvoiceIn.isBooked')"
v-model="params.isBooked"
@update:model-value="searchFn()"
toggle-indeterminate
/>
</QItemSection>
<QItemSection>
<QCheckbox
:label="t('params.correctingFk')"
:label="getLocale('params.correctingFk')"
v-model="params.correctingFk"
@update:model-value="searchFn()"
toggle-indeterminate
@ -134,54 +167,3 @@ const activities = ref([]);
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
search: Id or supplier name
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: Ledger account
isBooked: is booked
correctedFk: Rectified
issued: Issued
to: To
from: From
awbCode: AWB
correctingFk: Rectificative
supplierActivityFk: Supplier activity
es:
params:
search: Id o nombre proveedor
supplierRef: Ref. proveedor
supplierFk: Proveedor
clientFk: Cliente
fi: CIF proveedor
serialNumber: Num. serie
serial: Serie
awb: AWB
amount: Importe
issued: Emitida
isBooked: Contabilizada
account: Cuenta contable
created: Creada
dued: Vencida
correctedFk: Rectificada
correctingFk: Rectificativa
supplierActivityFk: Actividad proveedor
from: Desde
to: Hasta
From: Desde
To: Hasta
Amount: Importe
Issued: Fecha factura
Id or supplier: Id o proveedor
</i18n>

View File

@ -1,6 +1,8 @@
<script setup>
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState';
import { downloadFile } from 'src/composables/downloadFile';
import { toDate, toCurrency } from 'src/filters/index';
import InvoiceInFilter from './InvoiceInFilter.vue';
@ -13,12 +15,22 @@ import VnTable from 'src/components/VnTable/VnTable.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import FetchData from 'src/components/FetchData.vue';
const stateStore = useStateStore();
const user = useState().getUser();
const { viewSummary } = useSummaryDialog();
const { t } = useI18n();
const tableRef = ref();
const companies = ref([]);
const cols = computed(() => [
{
align: 'left',
name: 'isBooked',
label: t('InvoiceIn.isBooked'),
columnFilter: false,
},
{
align: 'left',
name: 'id',
@ -31,7 +43,7 @@ const cols = computed(() => [
{
align: 'left',
name: 'supplierFk',
label: t('invoiceIn.list.supplier'),
label: t('InvoiceIn.list.supplier'),
columnFilter: {
component: 'select',
attrs: {
@ -45,16 +57,16 @@ const cols = computed(() => [
{
align: 'left',
name: 'supplierRef',
label: t('invoiceIn.list.supplierRef'),
label: t('InvoiceIn.list.supplierRef'),
},
{
align: 'left',
name: 'serial',
label: t('invoiceIn.serial'),
label: t('InvoiceIn.serial'),
},
{
align: 'left',
label: t('invoiceIn.list.issued'),
label: t('InvoiceIn.list.issued'),
name: 'issued',
component: null,
columnFilter: {
@ -64,23 +76,53 @@ const cols = computed(() => [
},
{
align: 'left',
name: 'isBooked',
label: t('invoiceIn.isBooked'),
columnFilter: false,
label: t('InvoiceIn.list.dueDated'),
name: 'dueDated',
component: null,
columnFilter: {
component: 'date',
},
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.dueDated)),
cardVisible: true,
},
{
align: 'left',
name: 'awbCode',
label: t('invoiceIn.list.awb'),
label: t('InvoiceIn.list.awb'),
},
{
align: 'left',
name: 'amount',
label: t('invoiceIn.list.amount'),
label: t('InvoiceIn.list.amount'),
format: ({ amount }) => toCurrency(amount),
cardVisible: true,
},
{
name: 'companyFk',
label: t('globals.company'),
columnFilter: {
component: 'select',
attrs: {
url: 'Companies',
fields: ['id', 'code'],
optionLabel: 'code',
},
},
format: (row) => row.code,
},
{
name: 'companyFk',
label: t('globals.company'),
columnFilter: {
component: 'select',
attrs: {
url: 'Companies',
fields: ['id', 'code'],
optionLabel: 'code',
},
},
format: (row) => row.code,
},
{
align: 'right',
name: 'tableActions',
@ -104,6 +146,7 @@ const cols = computed(() => [
]);
</script>
<template>
<FetchData url="Companies" @on-fetch="(data) => (companies = data)" auto-load />
<InvoiceInSearchbar />
<RightMenu>
<template #right-panel>
@ -119,7 +162,7 @@ const cols = computed(() => [
urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
formInitialData: { companyFk: user.companyFk, issued: Date.vnNew() },
}"
redirect="invoice-in"
:columns="cols"
@ -154,7 +197,7 @@ const cols = computed(() => [
</template>
</VnSelect>
<VnInput
:label="t('invoiceIn.list.supplierRef')"
:label="t('InvoiceIn.list.supplierRef')"
v-model="data.supplierRef"
/>
<VnSelect
@ -166,7 +209,7 @@ const cols = computed(() => [
option-label="code"
:required="true"
/>
<VnInputDate :label="t('invoiceIn.summary.issued')" v-model="data.issued" />
<VnInputDate :label="t('InvoiceIn.summary.issued')" v-model="data.issued" />
</template>
</VnTable>
</template>

View File

@ -58,6 +58,14 @@ onBeforeMount(async () => {
:right-search="false"
:user-params="{ daysAgo }"
:disable-option="{ card: true }"
:row-click="
(row) => {
$router.push({
name: 'InvoiceInList',
query: { table: JSON.stringify({ serial: row.serial }) },
});
}
"
auto-load
/>
</template>

View File

@ -8,7 +8,11 @@ defineProps({ dataKey: { type: String, required: true } });
const { t } = useI18n();
</script>
<template>
<VnFilterPanel :data-key="dataKey" :search-button="true">
<VnFilterPanel
:data-key="dataKey"
:search-button="true"
:unremovable-params="['daysAgo']"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>

View File

@ -1,4 +1,4 @@
invoiceIn:
InvoiceIn:
serial: Serial
isBooked: Is booked
list:
@ -7,8 +7,11 @@ invoiceIn:
supplierRef: Supplier ref.
file: File
issued: Issued
dueDated: Due dated
awb: AWB
amount: Amount
descriptor:
ticketList: Ticket list
card:
client: Client
company: Company
@ -39,3 +42,9 @@ invoiceIn:
net: Net
stems: Stems
country: Country
params:
search: Id or supplier name
account: Ledger account
correctingFk: Rectificative
correctedFk: Corrected
isBooked: Is booked

View File

@ -1,15 +1,17 @@
invoiceIn:
InvoiceIn:
serial: Serie
isBooked: Contabilizada
list:
ref: Referencia
supplier: Proveedor
supplierRef: Ref. proveedor
shortIssued: F. emisión
issued: F. emisión
dueDated: F. vencimiento
file: Fichero
issued: Fecha emisión
awb: AWB
amount: Importe
descriptor:
ticketList: Listado de tickets
card:
client: Cliente
company: Empresa
@ -38,3 +40,8 @@ invoiceIn:
net: Neto
stems: Tallos
country: País
params:
search: Id o nombre proveedor
account: Cuenta contable
correctingFk: Rectificativa
correctedFk: Rectificada

View File

@ -361,7 +361,7 @@ function handleOnDataSave({ CrudModelRef }) {
@on-fetch="(data) => (warehousesOptions = data)"
auto-load
url="Warehouses"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
/>
<RightMenu>
<template #right-panel>
@ -394,191 +394,186 @@ function handleOnDataSave({ CrudModelRef }) {
/>
</template>
</VnSubToolbar>
<QPage>
<VnTable
@on-fetch="
(data) =>
data.forEach((item) => {
item.hasMinPrice = `${item.hasMinPrice !== 0}`;
})
"
:default-remove="false"
:default-reset="false"
:default-save="false"
data-key="ItemFixedPrices"
url="FixedPrices/filter"
:order="['itemFk DESC', 'name DESC']"
save-url="FixedPrices/crud"
ref="tableRef"
dense
:filter="{
where: {
warehouseFk: user.warehouseFk,
},
}"
:columns="columns"
default-mode="table"
auto-load
:is-editable="true"
:right-search="false"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
:crud-model="{
disableInfiniteScroll: true,
}"
v-model:selected="rowsSelected"
:create-as-dialog="false"
:create="{
onDataSaved: handleOnDataSave,
}"
:use-model="true"
:disable-option="{ card: true }"
>
<template #header-selection="scope">
<QCheckbox v-model="scope.selected" />
</template>
<template #body-selection="scope">
{{ scope }}
<QCheckbox flat v-model="scope.selected" />
</template>
<VnTable
@on-fetch="
(data) =>
data.forEach((item) => {
item.hasMinPrice = `${item.hasMinPrice !== 0}`;
})
"
:default-remove="false"
:default-reset="false"
:default-save="false"
data-key="ItemFixedPrices"
url="FixedPrices/filter"
:order="['itemFk DESC', 'name DESC']"
save-url="FixedPrices/crud"
ref="tableRef"
dense
:filter="{
where: {
warehouseFk: user.warehouseFk,
},
}"
:columns="columns"
default-mode="table"
auto-load
:is-editable="true"
:right-search="false"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
:use-model="true"
v-model:selected="rowsSelected"
:create-as-dialog="false"
:create="{
onDataSaved: handleOnDataSave,
}"
:disable-option="{ card: true }"
>
<template #header-selection="scope">
<QCheckbox v-model="scope.selected" />
</template>
<template #body-selection="scope">
{{ scope }}
<QCheckbox flat v-model="scope.selected" />
</template>
<template #column-itemFk="props">
<VnSelect
style="max-width: 100px"
url="Items/withName"
hide-selected
option-label="id"
option-value="id"
v-model="props.row.itemFk"
v-on="getRowUpdateInputEvents(props, true, 'select')"
<template #column-itemFk="props">
<VnSelect
style="max-width: 100px"
url="Items/withName"
hide-selected
option-label="id"
option-value="id"
v-model="props.row.itemFk"
v-on="getRowUpdateInputEvents(props, true, 'select')"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</template>
<template #column-name="{ row }">
<span class="link">
{{ row.name }}
</span>
<span class="subName">{{ row.subName }}</span>
<ItemDescriptorProxy :id="row.itemFk" />
<FetchedTags :item="row" :columns="3" />
</template>
<template #column-rate2="props">
<QTd class="col">
<VnInput
type="currency"
style="width: 75px"
v-model.number="props.row.rate2"
v-on="getRowUpdateInputEvents(props)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</template>
<template #column-name="{ row }">
<span class="link">
{{ row.name }}
</span>
<span class="subName">{{ row.subName }}</span>
<ItemDescriptorProxy :id="row.itemFk" />
<FetchedTags :item="row" :columns="3" />
</template>
<template #column-rate2="props">
<QTd class="col">
<VnInput
type="currency"
style="width: 75px"
v-model.number="props.row.rate2"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</QTd>
</template>
<template #column-rate3="props">
<QTd class="col">
<VnInput
style="width: 75px"
type="currency"
v-model.number="props.row.rate3"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</QTd>
</template>
<template #column-minPrice="props">
<QTd class="col">
<div class="row" style="align-items: center">
<QCheckbox
:model-value="props.row.hasMinPrice"
@update:model-value="updateMinPrice($event, props)"
:false-value="'false'"
:true-value="'true'"
/>
<VnInput
class="col"
type="currency"
mask="###.##"
:disable="props.row.hasMinPrice === 1"
v-model.number="props.row.minPrice"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</div>
</QTd>
</template>
<template #column-started="props">
<VnInputDate
class="vnInputDate"
:show-event="true"
v-model="props.row.started"
v-on="getRowUpdateInputEvents(props, false, 'date')"
v-bind="dateStyle(isBigger(props.row.started))"
/>
</template>
<template #column-ended="props">
<VnInputDate
class="vnInputDate"
:show-event="true"
v-model="props.row.ended"
v-on="getRowUpdateInputEvents(props, false, 'date')"
v-bind="dateStyle(isLower(props.row.ended))"
/>
</template>
<template #column-warehouseFk="props">
<QTd class="col">
<VnSelect
style="max-width: 150px"
:options="warehousesOptions"
hide-selected
option-label="name"
option-value="id"
v-model="props.row.warehouseFk"
v-on="getRowUpdateInputEvents(props, false, 'select')"
<template #append></template>
</VnInput>
</QTd>
</template>
<template #column-rate3="props">
<QTd class="col">
<VnInput
style="width: 75px"
type="currency"
v-model.number="props.row.rate3"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</QTd>
</template>
<template #column-minPrice="props">
<QTd class="col">
<div class="row" style="align-items: center">
<QCheckbox
:model-value="props.row.hasMinPrice"
@update:model-value="updateMinPrice($event, props)"
:false-value="'false'"
:true-value="'true'"
/>
</QTd>
</template>
<template #column-deleteAction="{ row, rowIndex }">
<QIcon
name="delete"
size="sm"
class="cursor-pointer fill-icon-on-hover"
color="primary"
@click.stop="
openConfirmationModal(
t('globals.rowWillBeRemoved'),
t('Do you want to clone this item?'),
() => removePrice(row.id, rowIndex)
)
"
>
<QTooltip class="text-no-wrap">
{{ t('globals.delete') }}
</QTooltip>
</QIcon>
</template>
</VnTable>
<QDialog ref="editTableCellDialogRef">
<EditTableCellValueForm
edit-url="FixedPrices/editFixedPrice"
:rows="rowsSelected"
:fields-options="editTableFieldsOptions"
@on-data-saved="onEditCellDataSaved()"
<VnInput
class="col"
type="currency"
mask="###.##"
:disable="props.row.hasMinPrice === 1"
v-model.number="props.row.minPrice"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</div>
</QTd>
</template>
<template #column-started="props">
<VnInputDate
class="vnInputDate"
:show-event="true"
v-model="props.row.started"
v-on="getRowUpdateInputEvents(props, false, 'date')"
v-bind="dateStyle(isBigger(props.row.started))"
/>
</QDialog>
</QPage>
</template>
<template #column-ended="props">
<VnInputDate
class="vnInputDate"
:show-event="true"
v-model="props.row.ended"
v-on="getRowUpdateInputEvents(props, false, 'date')"
v-bind="dateStyle(isLower(props.row.ended))"
/>
</template>
<template #column-warehouseFk="props">
<QTd class="col">
<VnSelect
style="max-width: 150px"
:options="warehousesOptions"
hide-selected
option-label="name"
option-value="id"
v-model="props.row.warehouseFk"
v-on="getRowUpdateInputEvents(props, false, 'select')"
/>
</QTd>
</template>
<template #column-deleteAction="{ row, rowIndex }">
<QIcon
name="delete"
size="sm"
class="cursor-pointer fill-icon-on-hover"
color="primary"
@click.stop="
openConfirmationModal(
t('globals.rowWillBeRemoved'),
t('Do you want to clone this item?'),
() => removePrice(row.id, rowIndex)
)
"
>
<QTooltip class="text-no-wrap">
{{ t('globals.delete') }}
</QTooltip>
</QIcon>
</template>
</VnTable>
<QDialog ref="editTableCellDialogRef">
<EditTableCellValueForm
edit-url="FixedPrices/editFixedPrice"
:rows="rowsSelected"
:fields-options="editTableFieldsOptions"
@on-data-saved="onEditCellDataSaved()"
/>
</QDialog>
</template>
<style lang="scss">
.q-table th,

View File

@ -29,7 +29,7 @@ const exprBuilder = (param, value) => {
return { 'a.supplierName': value };
case 'routeFk':
return { 'a.routeFk': value };
case 'created':
case 'dated':
case 'agencyFk':
case 'packages':
case 'm3':
@ -145,7 +145,7 @@ const exprBuilder = (param, value) => {
<QItem class="q-my-sm">
<QItemSection>
<VnInputDate
v-model="params.created"
v-model="params.dated"
:label="t('Date')"
is-outlined
/>

View File

@ -28,7 +28,7 @@ const filter = {
'id',
'workerFk',
'agencyModeFk',
'created',
'dated',
'm3',
'warehouseFk',
'description',
@ -78,7 +78,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.code, entity
@on-fetch="setData"
>
<template #body="{ entity }">
<VnLv :label="t('Date')" :value="toDate(entity?.created)" />
<VnLv :label="t('Date')" :value="toDate(entity?.dated)" />
<VnLv :label="t('Agency')" :value="entity?.agencyMode?.name" />
<VnLv :label="t('Zone')" :value="entity?.zone?.name" />
<VnLv

View File

@ -20,7 +20,7 @@ const shelvingId = ref(route.params?.id || null);
const isNew = Boolean(!shelvingId.value);
const defaultInitialData = {
agencyModeFk: null,
created: null,
dated: null,
description: '',
vehicleFk: null,
workerFk: null,
@ -33,7 +33,7 @@ const routeFilter = {
'id',
'workerFk',
'agencyModeFk',
'created',
'dated',
'm3',
'warehouseFk',
'description',
@ -116,7 +116,7 @@ const onSave = (data, response) => {
option-label="name"
:input-debounce="0"
/>
<VnInputDate v-model="data.created" :label="t('Created')" />
<VnInputDate v-model="data.dated" :label="t('Dated')" />
</VnRow>
<template v-if="!isNew">
<VnRow>
@ -170,7 +170,7 @@ es:
Hour finished: Hora fin
Description: Descripción
Is served: Se ha servido
Created: Creado
Dated: Fecha
The km can not exceed: La distancia debe ser inferior a {maxDistance}
en:
The km can not exceed: Distance must be lesser than {maxDistance}

View File

@ -139,7 +139,7 @@ const ticketColumns = ref([
<QCard class="vn-one">
<VnLv
:label="t('route.summary.date')"
:value="toDate(entity?.route.created)"
:value="toDate(entity?.route.dated)"
/>
<VnLv
:label="t('route.summary.agency')"

View File

@ -46,10 +46,10 @@ const columns = computed(() => [
},
{
align: 'left',
name: 'created',
name: 'dated',
label: t('Date'),
columnFilter: false,
format: ({ created }) => toDate(created),
format: ({ dated }) => toDate(dated),
},
{
align: 'left',

View File

@ -111,7 +111,7 @@ const columns = computed(() => [
},
{
align: 'left',
name: 'created',
name: 'dated',
label: t('route.Date'),
columnFilter: false,
cardVisible: true,

View File

@ -45,7 +45,7 @@ const columns = computed(() => [
columnFilter: {
inWhere: true,
},
format: ({ created }) => toDate(created),
format: ({ dated }) => toDate(dated),
cardVisible: true,
},
{

View File

@ -109,7 +109,7 @@ const ticketList = ref([]);
const cloneRoutes = () => {
axios.post('Routes/clone', {
created: startingDate.value,
dated: startingDate.value,
ids: selectedRows.value.map((row) => row?.id),
});
refreshKey.value++;

View File

@ -5,7 +5,7 @@ route:
Description: Description
hourStarted: H.Start
hourFinished: H.End
createRoute: Create route
dated: Dated
From: From
To: To
Date: Date

View File

@ -180,10 +180,7 @@ function handleLocation(data, location) {
:label="t('supplier.fiscalData.isTrucker')"
/>
<div class="row items-center">
<QCheckbox
v-model="data.isVies"
:label="t('supplier.fiscalData.isVies')"
/>
<QCheckbox v-model="data.isVies" :label="t('globals.isVies')" />
<QIcon name="info" size="xs" class="cursor-pointer q-ml-sm">
<QTooltip>
{{

View File

@ -23,13 +23,13 @@ const countriesOptions = ref([]);
<template>
<FetchData
url="Provinces"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
:filter="{ fields: ['id', 'name'], order: 'name ASC'}"
@on-fetch="(data) => (provincesOptions = data)"
auto-load
/>
<FetchData
url="countries"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
:filter="{ fields: ['id', 'name'], order: 'name ASC'}"
@on-fetch="(data) => (countriesOptions = data)"
auto-load
/>

View File

@ -86,71 +86,67 @@ async function getVideoList(expeditionId, timed) {
<template>
<Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()">
<QScrollArea class="fit">
<QList bordered separator style="max-width: 318px">
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QItemLabel class="text-h6">
{{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
time.max
}})
</QItemLabel>
<QRange
v-model="time"
@change="getVideoList(lastExpedition, time)"
:min="0"
:max="24"
:step="1"
:left-label-value="time.min + ':00'"
:right-label-value="time.max + ':00'"
label
markers
snap
color="primary"
/>
</QItemSection>
</QItem>
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QSelect
color="primary"
v-model="slide"
:options="videoList"
:label="t('ticket.boxing.selectVideo')"
emit-value
map-options
>
<template #prepend>
<QIcon name="schedule" />
</template>
</QSelect>
</QItemSection>
</QItem>
<QItem
v-for="expedition in expeditions"
:key="expedition.id"
@click="getVideoList(expedition.id)"
clickable
v-ripple
>
<QItemSection>
<QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
</QItemSection>
<QItemSection>
<QItemLabel caption>{{ t('globals.created') }}</QItemLabel>
<QItemLabel>
{{
date.formatDate(expedition.created, 'YYYY-MM-DD HH:mm:ss')
}}
</QItemLabel>
<QItemLabel caption>{{ t('globals.item') }}</QItemLabel>
<QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
<QItemLabel caption>{{ t('ticket.boxing.worker') }}</QItemLabel>
<QItemLabel>{{ expedition.userName }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QScrollArea>
<QList bordered separator style="max-width: 318px">
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QItemLabel class="text-h6">
{{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
time.max
}})
</QItemLabel>
<QRange
v-model="time"
@change="getVideoList(lastExpedition, time)"
:min="0"
:max="24"
:step="1"
:left-label-value="time.min + ':00'"
:right-label-value="time.max + ':00'"
label
markers
snap
color="primary"
/>
</QItemSection>
</QItem>
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QSelect
color="primary"
v-model="slide"
:options="videoList"
:label="t('ticket.boxing.selectVideo')"
emit-value
map-options
>
<template #prepend>
<QIcon name="schedule" />
</template>
</QSelect>
</QItemSection>
</QItem>
<QItem
v-for="expedition in expeditions"
:key="expedition.id"
@click="getVideoList(expedition.id)"
clickable
v-ripple
>
<QItemSection>
<QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
</QItemSection>
<QItemSection>
<QItemLabel caption>{{ t('globals.created') }}</QItemLabel>
<QItemLabel>
{{ date.formatDate(expedition.created, 'YYYY-MM-DD HH:mm:ss') }}
</QItemLabel>
<QItemLabel caption>{{ t('globals.item') }}</QItemLabel>
<QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
<QItemLabel caption>{{ t('ticket.boxing.worker') }}</QItemLabel>
<QItemLabel>{{ expedition.userName }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</Teleport>
<QCard>

View File

@ -442,6 +442,7 @@ watch(
<QPage class="column items-center q-pa-md">
<VnTable
data-key="advanceTickets"
:map-key="false"
ref="vnTableRef"
url="Tickets/getTicketsAdvance"
search-url="advanceTickets"

View File

@ -8,7 +8,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
import FetchData from 'src/components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import { toDate, toCurrency } from 'src/filters';
import axios from 'axios';
@ -256,16 +256,20 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
:label="t('globals.warehouseOut')"
:value="travel.warehouseOut?.name"
/>
<QCheckbox
:label="t('travel.basicData.isRaid')"
v-model="travel.isRaid"
:disable="true"
/>
<QCheckbox
:label="t('travel.summary.delivered')"
v-model="travel.isDelivered"
:disable="true"
/>
<VnRow>
<QCheckbox
:label="t('travel.basicData.isRaid')"
v-model="travel.isRaid"
:disable="true"
/>
</VnRow>
<VnRow>
<QCheckbox
:label="t('travel.summary.delivered')"
v-model="travel.isDelivered"
:disable="true"
/>
</VnRow>
</QCard>
<QCard class="vn-one">
<QCardSection class="q-pa-none">
@ -320,7 +324,6 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QTd>
<QCheckbox
v-if="col.name === 'isConfirmed'"
:label="t('travel.summary.received')"
:true-value="1"
:false-value="0"
v-model="row[col.name]"

View File

@ -77,7 +77,6 @@ defineExpose({ states });
:label="t('travel.shipped')"
v-model="params.shipped"
@update:model-value="searchFn()"
dense
outlined
rounded
/>
@ -154,7 +153,7 @@ es:
Id: Id
ref: Referencia
agency: Agencia
warehouseInFk: Alm.Salida
warehouseInFk: Alm.Entrada
shipped: F.Envío
shipmentHour: Hora de envío
warehouseOut: Alm.Entrada

View File

@ -53,6 +53,9 @@ const columns = computed(() => [
condition: () => true,
},
isId: true,
chip: {
condition: () => true,
},
},
{
align: 'left',
@ -101,14 +104,14 @@ const columns = computed(() => [
},
{
align: 'left',
name: 'shipped',
label: t('globals.shipped'),
name: 'shipped',
create: true,
cardVisible: true,
component: 'date',
columnField: {
component: null,
},
cardVisible: true,
create: true,
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.shipped)),
},
{
@ -198,7 +201,7 @@ const columns = computed(() => [
/>
<RightMenu>
<template #right-panel>
<TravelFilter data-key="TravelList" ref="travelFilterRef" />
<TravelFilter data-key="TravelList" />
</template>
</RightMenu>
<VnTable

View File

@ -103,9 +103,12 @@ const formattedWeekTotalHours = computed(() =>
const onInputChange = async (date) => {
if (!date) return;
const { year, month, day } = date.scope.timestamp;
const { timestamp, outside } = date.scope;
const { year, month, day } = timestamp;
const _date = new Date(year, month - 1, day);
setDate(_date);
if (outside) getMailStates(_date);
};
const setDate = async (date) => {

View File

@ -30,17 +30,15 @@ const warehousesOptions = ref([]);
>
<template #form-inputs>
<VnRow>
<div class="col">
<VnSelect
:label="t('list.warehouse')"
v-model="ZoneWarehouseFormData.warehouseFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
:required="true"
/>
</div>
<VnSelect
:label="t('list.warehouse')"
v-model="ZoneWarehouseFormData.warehouseFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
:required="true"
/>
</VnRow>
</template>
</FormPopup>

View File

@ -17,6 +17,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
searchUrl: 'params',
navigate: null,
page: 1,
mapKey: 'id',
};
function get(key) {
@ -46,6 +47,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
function getDefaultState() {
return Object.assign(JSON.parse(JSON.stringify(defaultOpts)), {
data: ref(),
map: ref(new Map()),
});
}

View File

@ -3,6 +3,8 @@ describe('ClaimDevelopment', () => {
const claimId = 1;
const firstLineReason = 'tbody > :nth-child(1) > :nth-child(2)';
const thirdRow = 'tbody > :nth-child(3)';
const lastReason = 'Incompetencia';
const newReason = 'Calor';
beforeEach(() => {
cy.viewport(1920, 1080);
@ -14,22 +16,22 @@ describe('ClaimDevelopment', () => {
});
it('should reset line', () => {
cy.selectOption(firstLineReason, 'Novato');
cy.selectOption(firstLineReason, newReason);
cy.resetCard();
cy.getValue(firstLineReason).should('equal', 'Prisas');
cy.getValue(firstLineReason).should('equal', lastReason);
});
it('should edit line', () => {
cy.selectOption(firstLineReason, 'Novato');
cy.selectOption(firstLineReason, newReason);
cy.saveCard();
cy.login('developer');
cy.visit(`/#/claim/${claimId}/development`);
cy.getValue(firstLineReason).should('equal', 'Novato');
cy.getValue(firstLineReason).should('equal', newReason);
//Restart data
cy.selectOption(firstLineReason, 'Prisas');
cy.selectOption(firstLineReason, lastReason);
cy.saveCard();
});
@ -42,7 +44,7 @@ describe('ClaimDevelopment', () => {
const rowData = [
false,
'Novato',
newReason,
'Roces',
'Compradores',
'administrativeNick',

View File

@ -16,7 +16,7 @@ describe('Client list', () => {
});
it('Client list create new client', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
const randomInt = Math.floor(Math.random() * 90) + 10;
const data = {
@ -26,8 +26,8 @@ describe('Client list', () => {
'Web user': { val: `user_test_${randomInt}` },
Street: { val: `C/ STREET ${randomInt}` },
Email: { val: `user.test${randomInt}@cypress.com` },
'Sales person': { val: 'employee', type: 'select' },
Location: { val: '46000, Valencia(Province one), España', type: 'select' },
'Sales person': { val: 'salesPerson', type: 'select' },
Location: { val: '46000', type: 'select' },
'Business type': { val: 'Otros', type: 'select' },
};
cy.fillInForm(data);

View File

@ -11,7 +11,7 @@ describe('EntryStockBought', () => {
cy.get('.q-notification__message').should('have.text', 'Data saved');
});
it('Should add a new reserved space for buyerBoss', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
cy.get('input[aria-label="Reserve"]').type('1');
cy.get('input[aria-label="Date"]').eq(1).clear();
cy.get('input[aria-label="Date"]').eq(1).type('01-01');

View File

@ -2,7 +2,7 @@
describe('InvoiceInIntrastat', () => {
const firstRow = 'tbody > :nth-child(1)';
const thirdRow = 'tbody > :nth-child(3)';
const firstRowCode = `${firstRow} > :nth-child(2)`;
const codes = `[data-cy="intrastat-code"]`;
const firstRowAmount = `${firstRow} > :nth-child(3)`;
beforeEach(() => {
@ -11,13 +11,12 @@ describe('InvoiceInIntrastat', () => {
});
it('should edit the first line', () => {
cy.selectOption(firstRowCode, 'Plantas vivas: Esqueje/injerto, Vid');
cy.selectOption(`${firstRow} ${codes}`, 'Plantas vivas: Esqueje/injerto, Vid');
cy.get(firstRowAmount).clear();
cy.saveCard();
cy.get(`${firstRowCode} span`).should(
'have.text',
'6021010:Plantas vivas: Esqueje/injerto, Vid'
);
cy.get(codes)
.eq(0)
.should('have.value', '6021010: Plantas vivas: Esqueje/injerto, Vid');
});
it('should add a new row', () => {

View File

@ -1,7 +1,7 @@
/// <reference types="cypress" />
describe('InvoiceInList', () => {
const firstRow = 'tbody.q-virtual-scroll__content tr:nth-child(1)';
const firstId = `${firstRow} > td:nth-child(1) span`;
const firstId = `${firstRow} > td:nth-child(2) span`;
const firstDetailBtn = `${firstRow} .q-btn:nth-child(1)`;
const summaryHeaders = '.summaryBody .header-link';

View File

@ -2,6 +2,7 @@
describe('InvoiceInVat', () => {
const thirdRow = 'tbody > :nth-child(3)';
const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)';
const vats = '[data-cy="vat-sageiva"]';
const dialogInputs = '.q-dialog label input';
const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon';
const randomInt = Math.floor(Math.random() * 100);
@ -14,9 +15,9 @@ describe('InvoiceInVat', () => {
});
it('should edit the sage iva', () => {
cy.selectOption(firstLineVat, 'H.P. IVA 21% CEE');
cy.selectOption(`${firstLineVat} ${vats}`, 'H.P. IVA 21% CEE');
cy.saveCard();
cy.get(`${firstLineVat} span`).should('have.text', '8:H.P. IVA 21% CEE');
cy.get(vats).eq(0).should('have.value', '8: H.P. IVA 21% CEE');
});
it('should add a new row', () => {

View File

@ -19,7 +19,7 @@ describe('Handle Items FixedPrice', () => {
cy.selectOption('.list > :nth-child(2)', 'Alstroemeria');
cy.get('.q-gutter-x-sm > .q-btn > .q-btn__content > .q-icon').click();
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
cy.selectOption(`${firstRow} > :nth-child(2)`, '#13');
cy.get(`${firstRow} > :nth-child(4)`).find('input').type(1);
cy.get(`${firstRow} > :nth-child(5)`).find('input').type('2');
@ -29,7 +29,7 @@ describe('Handle Items FixedPrice', () => {
});
it('Create and delete ', function () {
cy.get('.q-gutter-x-sm > .q-btn > .q-btn__content > .q-icon').click();
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
cy.selectOption(`${firstRow} > :nth-child(2)`, '#11');
cy.get(`${firstRow} > :nth-child(4)`).type('1');
cy.get(`${firstRow} > :nth-child(5)`).type('2');

View File

@ -5,7 +5,7 @@ describe('RoadMap', () => {
cy.visit(`/#/route/roadmap`);
});
it('Route list create roadmap and redirect', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
cy.get('input[name="name"]').eq(1).type('roadMapTestOne{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created');
cy.url().should('include', '/summary');

View File

@ -9,7 +9,7 @@ describe('Route', () => {
const getRowColumn = (row, column) => `:nth-child(${row}) > :nth-child(${column})`;
it('Route list create route', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
cy.get('input[name="description"]').type('routeTestOne{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created');
cy.url().should('include', '/summary');

View File

@ -9,15 +9,15 @@ describe('VnLog', () => {
cy.visit(`/#/claim/${1}/log`);
cy.openRightMenu();
});
// Se tiene que cambiar el Accept-Language a 'en', ya hay una tarea para eso #7189.
xit('should filter by insert actions', () => {
it('should filter by insert actions', () => {
cy.checkOption(':nth-child(7) > .q-checkbox');
cy.get('.q-page').click();
cy.validateContent(chips[0], 'Document');
cy.validateContent(chips[1], 'Beginning');
});
xit('should filter by entity', () => {
it('should filter by entity', () => {
cy.selectOption('.q-drawer--right .q-item > .q-select', 'Claim');
cy.get('.q-page').click();
cy.validateContent(chips[0], 'Claim');

View File

@ -7,7 +7,7 @@ describe('WorkerPda', () => {
});
it('assign pda', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.addBtnClick();
cy.get(select).click();
cy.get(select).type('{downArrow}{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created');

View File

@ -1,10 +1,10 @@
describe('ZoneWarehouse', () => {
const data = {
Warehouse: { val: 'Algemesi', type: 'select' },
Warehouse: { val: 'Warehouse One', type: 'select' },
};
const deviceProductionField =
'.vn-row > :nth-child(1) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container';
const dataError = "ER_DUP_ENTRY: Duplicate entry '2-2' for key 'zoneFk'";
const dataError = 'ER_DUP_ENTRY: Duplicate entry';
const saveBtn = '.q-btn--standard > .q-btn__content > .block';
beforeEach(() => {
cy.viewport(1280, 720);
@ -13,22 +13,21 @@ describe('ZoneWarehouse', () => {
});
it('should throw an error if the warehouse chosen is already put in the zone', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.get(deviceProductionField).click();
cy.get(deviceProductionField).type('{upArrow}{enter}');
cy.get('.q-notification__message').should('have.text', dataError);
cy.addBtnClick();
cy.selectOption('[data-cy="Warehouse_select"]', 'Warehouse Two');
cy.get(saveBtn).click();
cy.checkNotification(dataError);
});
it('should create a warehouse', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.get(deviceProductionField).click();
it('should create & remove a warehouse', () => {
cy.addBtnClick();
cy.fillInForm(data);
cy.get(saveBtn).click();
cy.get('.q-mt-lg > .q-btn--standard').click();
});
it('should delete a warehouse', () => {
cy.get('tbody > :nth-child(2) > :nth-child(2) > .q-icon').click();
cy.get('.q-card__actions > .q-btn--flat > .q-btn__content').click();
cy.get('[title="Confirm"]').click();
cy.reload();
});
});

View File

@ -58,8 +58,9 @@ Cypress.Commands.add('domContentLoad', (element, timeout = 5000) => {
cy.waitUntil(() => cy.document().then((doc) => doc.readyState === 'complete'));
});
Cypress.Commands.add('waitForElement', (element, timeout = 5000) => {
cy.waitUntil(() => cy.get(element).then(($el) => $el.is(':visible')));
cy.get(element, { timeout }).should('be.visible').and('not.be.disabled');
});
Cypress.Commands.add('getValue', (selector) => {
cy.get(selector).then(($el) => {
if ($el.find('.q-checkbox__inner').length > 0) {
@ -86,15 +87,40 @@ Cypress.Commands.add('getValue', (selector) => {
});
// Fill Inputs
Cypress.Commands.add('selectOption', (selector, option, timeout) => {
cy.waitForElement(selector);
Cypress.Commands.add('selectOption', (selector, option, timeout = 5000) => {
cy.waitForElement(selector, timeout);
cy.get(selector).click();
cy.wait(timeout || 1000);
cy.get('.q-menu .q-item').contains(option).click();
cy.get(selector).invoke('data', 'url').as('dataUrl');
cy.get(selector)
.clear()
.type(option)
.then(() => {
cy.get('.q-menu', { timeout })
.should('be.visible') // Asegurarse de que el menú está visible
.and('exist') // Verificar que el menú existe
.then(() => {
cy.get('@dataUrl').then((url) => {
if (url) {
cy.log('url: ', url);
// Esperar a que el menú no esté visible (desaparezca)
cy.get('.q-menu').should('not.be.visible');
// Ahora esperar a que el menú vuelva a aparecer
cy.get('.q-menu').should('be.visible').and('exist');
}
});
});
});
// Finalmente, seleccionar la opción deseada
cy.get('.q-menu:visible') // Asegurarse de que estamos dentro del menú visible
.find('.q-item') // Encontrar los elementos de las opciones
.contains(option) // Verificar que existe una opción que contenga el texto deseado
.click(); // Hacer clic en la opción
});
Cypress.Commands.add('countSelectOptions', (selector, option) => {
cy.waitForElement(selector);
cy.get(selector).click();
cy.get(selector).click({ force: true });
cy.get('.q-menu .q-item').should('have.length', option);
});
@ -110,8 +136,7 @@ Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => {
const { type, val } = field;
switch (type) {
case 'select':
cy.get(el).click();
cy.get('.q-menu .q-item').contains(val).click();
cy.selectOption(el, val);
break;
case 'date':
cy.get(el).type(val.split('-').join(''));
@ -347,3 +372,10 @@ Cypress.Commands.add('searchByLabel', (label, value) => {
Cypress.Commands.add('dataCy', (tag, attr = 'data-cy') => {
return cy.get(`[${attr}="${tag}"]`);
});
Cypress.Commands.add('addBtnClick', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon')
.should('exist')
.and('be.visible')
.click();
});

View File

@ -1,4 +1,3 @@
import { Notify } from 'quasar';
import { onRequest, onResponseError } from 'src/boot/axios';
import { describe, expect, it, vi } from 'vitest';
@ -27,6 +26,7 @@ describe('Axios boot', () => {
expect(resultConfig).toEqual(
expect.objectContaining({
headers: {
'Accept-Language': 'en-US',
Authorization: 'DEFAULT_TOKEN',
},
})

View File

@ -4,7 +4,11 @@ import VnPaginate from 'src/components/ui/VnPaginate.vue';
describe('VnPaginate', () => {
const expectedUrl = '/api/customers';
const defaultData = [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
];
let vm;
beforeAll(() => {
const options = {
@ -28,11 +32,7 @@ describe('VnPaginate', () => {
describe('paginate()', () => {
it('should call to the paginate() method and set the data on the rows property', async () => {
vi.spyOn(vm.arrayData, 'loadMore');
vm.store.data = [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
];
vm.store.data = defaultData;
await vm.paginate();
@ -42,26 +42,25 @@ describe('VnPaginate', () => {
it('should call to the paginate() method and then call it again to paginate', async () => {
vi.spyOn(axios, 'get').mockResolvedValue({
data: [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
],
data: defaultData,
});
vm.store.hasMoreData = true;
await vm.$nextTick();
vm.store.data = [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
];
vm.store.data = defaultData;
await vm.paginate();
expect(vm.store.skip).toEqual(3);
expect(vm.store.data.length).toEqual(6);
vi.spyOn(axios, 'get').mockResolvedValue({
data: [
{ id: 4, name: 'Peter Parker' },
{ id: 5, name: 'Clark Kent' },
{ id: 6, name: 'Barry Allen' },
],
});
await vm.paginate();
expect(vm.store.skip).toEqual(6);
@ -85,11 +84,7 @@ describe('VnPaginate', () => {
const index = 1;
const done = vi.fn();
vm.store.data = [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
];
vm.store.data = defaultData;
await vm.onLoad(index, done);
@ -105,11 +100,7 @@ describe('VnPaginate', () => {
],
});
vm.store.data = [
{ id: 1, name: 'Tony Stark' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
];
vm.store.data = defaultData;
expect(vm.pagination.page).toEqual(1);

View File

@ -0,0 +1,45 @@
import { describe, expect, it, vi } from 'vitest';
import axios from 'axios';
import { getExchange } from 'src/composables/getExchange';
vi.mock('axios');
describe('getExchange()', () => {
it('should return the correct exchange rate', async () => {
axios.get.mockResolvedValue({
data: { value: 1.2 },
});
const amount = 100;
const currencyFk = 1;
const dated = '2023-01-01';
const result = await getExchange(amount, currencyFk, dated);
expect(result).toBe('83.33');
});
it('should return the correct exchange rate with custom decimal places', async () => {
axios.get.mockResolvedValue({
data: { value: 1.2 },
});
const amount = 100;
const currencyFk = 1;
const dated = '2023-01-01';
const decimalPlaces = 3;
const result = await getExchange(amount, currencyFk, dated, decimalPlaces);
expect(result).toBe('83.333');
});
it('should return null if the API call fails', async () => {
axios.get.mockRejectedValue(new Error('Network error'));
const amount = 100;
const currencyFk = 1;
const dated = '2023-01-01';
const result = await getExchange(amount, currencyFk, dated);
expect(result).toBeNull();
});
});

View File

@ -0,0 +1,55 @@
import { vi, describe, expect, it } from 'vitest';
import { getTotal } from 'src/composables/getTotal';
vi.mock('src/filters', () => ({
toCurrency: vi.fn((value, currency) => `${currency} ${value.toFixed(2)}`),
}));
describe('getTotal()', () => {
const rows = [
{ amount: 10.5, tax: 2.1 },
{ amount: 20.75, tax: 3.25 },
{ amount: 30.25, tax: 4.75 },
];
it('should calculate the total for a given key', () => {
const total = getTotal(rows, 'amount');
expect(total).toBe('61.50');
});
it('should calculate the total with a callback function', () => {
const total = getTotal(rows, null, { cb: (row) => row.amount + row.tax });
expect(total).toBe('71.60');
});
it('should format the total as currency', () => {
const total = getTotal(rows, 'amount', { currency: 'USD' });
expect(total).toBe('USD 61.50');
});
it('should format the total as currency with default currency', () => {
const total = getTotal(rows, 'amount', { currency: 'default' });
expect(total).toBe('undefined 61.50');
});
it('should calculate the total with integer formatting', () => {
const total = getTotal(rows, 'amount', { decimalPlaces: 0 });
expect(total).toBe('62');
});
it('should calculate the total with custom decimal places', () => {
const total = getTotal(rows, 'amount', { decimalPlaces: 1 });
expect(total).toBe('61.5');
});
it('should handle rows with missing keys', () => {
const rowsWithMissingKeys = [{ amount: 10.5 }, { amount: 20.75 }, {}];
const total = getTotal(rowsWithMissingKeys, 'amount');
expect(total).toBe('31.25');
});
it('should handle empty rows', () => {
const total = getTotal([], 'amount');
expect(total).toBe('0.00');
});
});

View File

@ -0,0 +1,9 @@
import { describe, expect, it } from 'vitest';
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
describe('useAccountShortToStandard()', () => {
it('should pad the decimal part with zeros for short numbers', () => {
expect(useAccountShortToStandard('123.45')).toBe('1230000045');
expect(useAccountShortToStandard('123.')).toBe('1230000000');
});
});

View File

@ -1,34 +0,0 @@
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', () => {
const invoceInIntrastat = [
{ amount: 10, stems: 162 },
{ amount: 20, stems: 21 },
];
const totalAmount = vm.getTotal(invoceInIntrastat, 'amount');
const totalStems = vm.getTotal(invoceInIntrastat, 'stems');
expect(totalAmount).toBe(10 + 20);
expect(totalStems).toBe(162 + 21);
});
});
});

View File

@ -1,38 +0,0 @@
import { vi, describe, expect, it, beforeAll } from 'vitest';
import { createWrapper } 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('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);
});
});
});