Merge branch 'dev' into 8813-arreglarFormatoClaimLines
gitea/salix-front/pipeline/pr-dev This commit looks good Details

This commit is contained in:
PAU ROVIRA ROSALENY 2024-12-18 12:06:04 +00:00
commit 8293a314c8
72 changed files with 1004 additions and 728 deletions

View File

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

View File

@ -1,13 +1,28 @@
<script setup> <script setup>
import VnInput from 'src/components/common/VnInput.vue'; 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 model = defineModel({ type: [Number, String] });
const $attrs = useAttrs();
const step = ref($attrs.step || 0.01);
</script> </script>
<template> <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> </template>

View File

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

View File

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

View File

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

View File

@ -74,6 +74,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
mapKey: {
type: String,
default: '',
},
}); });
const emit = defineEmits(['onFetch', 'onPaginate', 'onChange']); const emit = defineEmits(['onFetch', 'onPaginate', 'onChange']);
@ -96,6 +100,7 @@ const arrayData = useArrayData(props.dataKey, {
exprBuilder: props.exprBuilder, exprBuilder: props.exprBuilder,
keepOpts: props.keepOpts, keepOpts: props.keepOpts,
searchUrl: props.searchUrl, searchUrl: props.searchUrl,
mapKey: props.mapKey,
}); });
const store = arrayData.store; 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'; import { toCurrency } from 'src/filters';
export function getTotal(rows, key, opts = {}) { 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); const total = rows.reduce((acc, row) => acc + +(cb ? cb(row) : row[key] || 0), 0);
return currency return currency
? toCurrency(total, currency == 'default' ? undefined : 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', 'exprBuilder',
'searchUrl', 'searchUrl',
'navigate', 'navigate',
'mapKey',
]; ];
if (typeof userOptions === 'object') { if (typeof userOptions === 'object') {
for (const option in userOptions) { for (const option in userOptions) {
@ -119,17 +120,12 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
const { limit } = filter; const { limit } = filter;
store.hasMoreData = limit && response.data.length >= limit; store.hasMoreData = limit && response.data.length >= limit;
if (append) { processData(response.data, { map: !!store.mapKey, append });
if (!store.data) store.data = []; if (!append && !isDialogOpened()) updateRouter && updateStateParams();
for (const row of response.data) store.data.push(row);
} else {
store.data = response.data;
if (!isDialogOpened()) updateRouter && updateStateParams();
}
store.isLoading = false; store.isLoading = false;
canceller = null; canceller = null;
return response; return response;
} }
@ -288,6 +284,31 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
router.replace(newUrl); 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 totalRows = computed(() => (store.data && store.data.length) || 0);
const isLoading = computed(() => store.isLoading || false); const isLoading = computed(() => store.isLoading || false);

View File

@ -333,11 +333,23 @@ globals:
packing: ITP packing: ITP
myTeam: My team myTeam: My team
departmentFk: Department 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 countryFk: Country
companyFk: Company
changePass: Change password changePass: Change password
deleteConfirmTitle: Delete selected elements deleteConfirmTitle: Delete selected elements
changeState: Change state changeState: Change state
raid: 'Raid {daysInForward} days' raid: 'Raid {daysInForward} days'
isVies: Vies
errors: errors:
statusUnauthorized: Access denied statusUnauthorized: Access denied
statusInternalServerError: An internal server error has ocurred statusInternalServerError: An internal server error has ocurred
@ -731,7 +743,6 @@ supplier:
sageTransactionTypeFk: Sage transaction type sageTransactionTypeFk: Sage transaction type
supplierActivityFk: Supplier activity supplierActivityFk: Supplier activity
isTrucker: Trucker isTrucker: Trucker
isVies: Vies
billingData: billingData:
payMethodFk: Billing data payMethodFk: Billing data
payDemFk: Payment deadline payDemFk: Payment deadline

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,41 +1,66 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import FetchData from 'components/FetchData.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.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 } }); 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> </script>
<template> <template>
<FetchData <VnFilterPanel :data-key="dataKey" :search-button="true" :hidden-tags="['daysAgo']">
url="SupplierActivities" <template #tags="{ tag, formatFn, getLocale }">
auto-load
@on-fetch="(data) => (activities = data)"
/>
<VnFilterPanel :data-key="dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong> <strong>{{ getLocale(tag.label) }}: </strong>
<span>{{ formatFn(tag.value) }}</span> <span>{{ formatFn(tag.value) }}</span>
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn, getLocale }">
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputDate :label="t('From')" v-model="params.from" is-outlined /> <VnInputDate
:label="$t('globals.from')"
v-model="params.from"
is-outlined
/>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <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> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
@ -44,20 +69,18 @@ const activities = ref([]);
v-model="params.supplierFk" v-model="params.supplierFk"
url="Suppliers" url="Suppliers"
:fields="['id', 'nickname']" :fields="['id', 'nickname']"
:label="t('params.supplierFk')" :label="getLocale('supplierFk')"
option-value="id"
option-label="nickname" option-label="nickname"
dense dense
outlined outlined
rounded rounded
:filter-options="['id', 'name']"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.supplierRef')" :label="getLocale('supplierRef')"
v-model="params.supplierRef" v-model="params.supplierRef"
is-outlined is-outlined
lazy-rules lazy-rules
@ -67,7 +90,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.fi')" :label="getLocale('fi')"
v-model="params.fi" v-model="params.fi"
is-outlined is-outlined
lazy-rules lazy-rules
@ -77,7 +100,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.serial')" :label="getLocale('serial')"
v-model="params.serial" v-model="params.serial"
is-outlined is-outlined
lazy-rules lazy-rules
@ -87,7 +110,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.account')" :label="getLocale('account')"
v-model="params.account" v-model="params.account"
is-outlined is-outlined
lazy-rules lazy-rules
@ -97,7 +120,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.awb')" :label="getLocale('globals.params.awbCode')"
v-model="params.awbCode" v-model="params.awbCode"
is-outlined is-outlined
lazy-rules lazy-rules
@ -107,24 +130,34 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputNumber <VnInputNumber
:label="t('Amount')" :label="$t('globals.amount')"
v-model="params.amount" v-model="params.amount"
is-outlined is-outlined
/> />
</QItemSection> </QItemSection>
</QItem> </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> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('invoiceIn.isBooked')" :label="$t('InvoiceIn.isBooked')"
v-model="params.isBooked" v-model="params.isBooked"
@update:model-value="searchFn()" @update:model-value="searchFn()"
toggle-indeterminate toggle-indeterminate
/> />
</QItemSection>
<QItemSection>
<QCheckbox <QCheckbox
:label="t('params.correctingFk')" :label="getLocale('params.correctingFk')"
v-model="params.correctingFk" v-model="params.correctingFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
toggle-indeterminate toggle-indeterminate
@ -134,54 +167,3 @@ const activities = ref([]);
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </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

@ -2,6 +2,7 @@
import { ref, computed, onMounted, onUnmounted } from 'vue'; import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState';
import { downloadFile } from 'src/composables/downloadFile'; import { downloadFile } from 'src/composables/downloadFile';
import { toDate, toCurrency } from 'src/filters/index'; import { toDate, toCurrency } from 'src/filters/index';
import InvoiceInFilter from './InvoiceInFilter.vue'; import InvoiceInFilter from './InvoiceInFilter.vue';
@ -14,8 +15,10 @@ import VnTable from 'src/components/VnTable/VnTable.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import FetchData from 'src/components/FetchData.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
const user = useState().getUser();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const { t } = useI18n(); const { t } = useI18n();
@ -23,7 +26,14 @@ onMounted(async () => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
const tableRef = ref(); const tableRef = ref();
const companies = ref([]);
const cols = computed(() => [ const cols = computed(() => [
{
align: 'left',
name: 'isBooked',
label: t('InvoiceIn.isBooked'),
columnFilter: false,
},
{ {
align: 'left', align: 'left',
name: 'id', name: 'id',
@ -32,7 +42,7 @@ const cols = computed(() => [
{ {
align: 'left', align: 'left',
name: 'supplierFk', name: 'supplierFk',
label: t('invoiceIn.list.supplier'), label: t('InvoiceIn.list.supplier'),
columnFilter: { columnFilter: {
component: 'select', component: 'select',
attrs: { attrs: {
@ -45,16 +55,16 @@ const cols = computed(() => [
{ {
align: 'left', align: 'left',
name: 'supplierRef', name: 'supplierRef',
label: t('invoiceIn.list.supplierRef'), label: t('InvoiceIn.list.supplierRef'),
}, },
{ {
align: 'left', align: 'left',
name: 'serial', name: 'serial',
label: t('invoiceIn.serial'), label: t('InvoiceIn.serial'),
}, },
{ {
align: 'left', align: 'left',
label: t('invoiceIn.list.issued'), label: t('InvoiceIn.list.issued'),
name: 'issued', name: 'issued',
component: null, component: null,
columnFilter: { columnFilter: {
@ -64,21 +74,38 @@ const cols = computed(() => [
}, },
{ {
align: 'left', align: 'left',
name: 'isBooked', label: t('InvoiceIn.list.dueDated'),
label: t('invoiceIn.isBooked'), name: 'dueDated',
columnFilter: false, component: null,
columnFilter: {
component: 'date',
},
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.dueDated)),
}, },
{ {
align: 'left', align: 'left',
name: 'awbCode', name: 'awbCode',
label: t('invoiceIn.list.awb'), label: t('InvoiceIn.list.awb'),
}, },
{ {
align: 'left', align: 'left',
name: 'amount', name: 'amount',
label: t('invoiceIn.list.amount'), label: t('InvoiceIn.list.amount'),
format: ({ amount }) => toCurrency(amount), format: ({ amount }) => toCurrency(amount),
}, },
{
name: 'companyFk',
label: t('globals.company'),
columnFilter: {
component: 'select',
attrs: {
url: 'Companies',
fields: ['id', 'code'],
optionLabel: 'code',
},
},
format: (row) => row.code,
},
{ {
align: 'right', align: 'right',
name: 'tableActions', name: 'tableActions',
@ -101,6 +128,7 @@ const cols = computed(() => [
]); ]);
</script> </script>
<template> <template>
<FetchData url="Companies" @on-fetch="(data) => (companies = data)" auto-load />
<InvoiceInSearchbar /> <InvoiceInSearchbar />
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
@ -116,7 +144,7 @@ const cols = computed(() => [
urlCreate: 'InvoiceIns', urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'), title: t('globals.createInvoiceIn'),
onDataSaved: ({ id }) => tableRef.redirect(id), onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {}, formInitialData: { companyFk: user.companyFk, issued: Date.vnNew() },
}" }"
redirect="invoice-in" redirect="invoice-in"
:columns="cols" :columns="cols"
@ -151,7 +179,7 @@ const cols = computed(() => [
</template> </template>
</VnSelect> </VnSelect>
<VnInput <VnInput
:label="t('invoiceIn.list.supplierRef')" :label="t('InvoiceIn.list.supplierRef')"
v-model="data.supplierRef" v-model="data.supplierRef"
/> />
<VnSelect <VnSelect
@ -163,7 +191,7 @@ const cols = computed(() => [
option-label="code" option-label="code"
:required="true" :required="true"
/> />
<VnInputDate :label="t('invoiceIn.summary.issued')" v-model="data.issued" /> <VnInputDate :label="t('InvoiceIn.summary.issued')" v-model="data.issued" />
</template> </template>
</VnTable> </VnTable>
</template> </template>

View File

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

View File

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

View File

@ -1,4 +1,4 @@
invoiceIn: InvoiceIn:
serial: Serial serial: Serial
isBooked: Is booked isBooked: Is booked
list: list:
@ -7,8 +7,11 @@ invoiceIn:
supplierRef: Supplier ref. supplierRef: Supplier ref.
file: File file: File
issued: Issued issued: Issued
dueDated: Due dated
awb: AWB awb: AWB
amount: Amount amount: Amount
descriptor:
ticketList: Ticket list
card: card:
client: Client client: Client
company: Company company: Company
@ -39,3 +42,9 @@ invoiceIn:
net: Net net: Net
stems: Stems stems: Stems
country: Country 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 serial: Serie
isBooked: Contabilizada isBooked: Contabilizada
list: list:
ref: Referencia ref: Referencia
supplier: Proveedor supplier: Proveedor
supplierRef: Ref. proveedor supplierRef: Ref. proveedor
shortIssued: F. emisión issued: F. emisión
dueDated: F. vencimiento
file: Fichero file: Fichero
issued: Fecha emisión
awb: AWB awb: AWB
amount: Importe amount: Importe
descriptor:
ticketList: Listado de tickets
card: card:
client: Cliente client: Cliente
company: Empresa company: Empresa
@ -38,3 +40,8 @@ invoiceIn:
net: Neto net: Neto
stems: Tallos stems: Tallos
country: País 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)" @on-fetch="(data) => (warehousesOptions = data)"
auto-load auto-load
url="Warehouses" url="Warehouses"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
/> />
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
@ -394,191 +394,186 @@ function handleOnDataSave({ CrudModelRef }) {
/> />
</template> </template>
</VnSubToolbar> </VnSubToolbar>
<QPage> <VnTable
<VnTable @on-fetch="
@on-fetch=" (data) =>
(data) => data.forEach((item) => {
data.forEach((item) => { item.hasMinPrice = `${item.hasMinPrice !== 0}`;
item.hasMinPrice = `${item.hasMinPrice !== 0}`; })
}) "
" :default-remove="false"
:default-remove="false" :default-reset="false"
:default-reset="false" :default-save="false"
:default-save="false" data-key="ItemFixedPrices"
data-key="ItemFixedPrices" url="FixedPrices/filter"
url="FixedPrices/filter" :order="['itemFk DESC', 'name DESC']"
:order="['itemFk DESC', 'name DESC']" save-url="FixedPrices/crud"
save-url="FixedPrices/crud" ref="tableRef"
ref="tableRef" dense
dense :filter="{
:filter="{ where: {
where: { warehouseFk: user.warehouseFk,
warehouseFk: user.warehouseFk, },
}, }"
}" :columns="columns"
:columns="columns" default-mode="table"
default-mode="table" auto-load
auto-load :is-editable="true"
:is-editable="true" :right-search="false"
:right-search="false" :table="{
:table="{ 'row-key': 'id',
'row-key': 'id', selection: 'multiple',
selection: 'multiple', }"
}" :use-model="true"
:crud-model="{ v-model:selected="rowsSelected"
disableInfiniteScroll: true, :create-as-dialog="false"
}" :create="{
v-model:selected="rowsSelected" onDataSaved: handleOnDataSave,
:create-as-dialog="false" }"
:create="{ :disable-option="{ card: true }"
onDataSaved: handleOnDataSave, >
}" <template #header-selection="scope">
:use-model="true" <QCheckbox v-model="scope.selected" />
:disable-option="{ card: true }" </template>
> <template #body-selection="scope">
<template #header-selection="scope"> {{ scope }}
<QCheckbox v-model="scope.selected" /> <QCheckbox flat v-model="scope.selected" />
</template> </template>
<template #body-selection="scope">
{{ scope }}
<QCheckbox flat v-model="scope.selected" />
</template>
<template #column-itemFk="props"> <template #column-itemFk="props">
<VnSelect <VnSelect
style="max-width: 100px" style="max-width: 100px"
url="Items/withName" url="Items/withName"
hide-selected hide-selected
option-label="id" option-label="id"
option-value="id" option-value="id"
v-model="props.row.itemFk" v-model="props.row.itemFk"
v-on="getRowUpdateInputEvents(props, true, 'select')" 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" />
</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"> <template #append></template>
<QItem v-bind="scope.itemProps"> </VnInput>
<QItemSection> </QTd>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel> </template>
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel> <template #column-rate3="props">
</QItemSection> <QTd class="col">
</QItem> <VnInput
</template> style="width: 75px"
</VnSelect> type="currency"
</template> v-model.number="props.row.rate3"
<template #column-name="{ row }"> v-on="getRowUpdateInputEvents(props)"
<span class="link"> >
{{ row.name }} <template #append></template>
</span> </VnInput>
<span class="subName">{{ row.subName }}</span> </QTd>
<ItemDescriptorProxy :id="row.itemFk" /> </template>
<FetchedTags :item="row" /> <template #column-minPrice="props">
</template> <QTd class="col">
<template #column-rate2="props"> <div class="row" style="align-items: center">
<QTd class="col"> <QCheckbox
<VnInput :model-value="props.row.hasMinPrice"
type="currency" @update:model-value="updateMinPrice($event, props)"
style="width: 75px" :false-value="'false'"
v-model.number="props.row.rate2" :true-value="'true'"
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')"
/> />
</QTd> <VnInput
</template> class="col"
<template #column-deleteAction="{ row, rowIndex }"> type="currency"
<QIcon mask="###.##"
name="delete" :disable="props.row.hasMinPrice === 1"
size="sm" v-model.number="props.row.minPrice"
class="cursor-pointer fill-icon-on-hover" v-on="getRowUpdateInputEvents(props)"
color="primary" >
@click.stop=" <template #append></template>
openConfirmationModal( </VnInput>
t('globals.rowWillBeRemoved'), </div>
t('Do you want to clone this item?'), </QTd>
() => removePrice(row.id, rowIndex) </template>
) <template #column-started="props">
" <VnInputDate
> class="vnInputDate"
<QTooltip class="text-no-wrap"> :show-event="true"
{{ t('globals.delete') }} v-model="props.row.started"
</QTooltip> v-on="getRowUpdateInputEvents(props, false, 'date')"
</QIcon> v-bind="dateStyle(isBigger(props.row.started))"
</template>
</VnTable>
<QDialog ref="editTableCellDialogRef">
<EditTableCellValueForm
edit-url="FixedPrices/editFixedPrice"
:rows="rowsSelected"
:fields-options="editTableFieldsOptions"
@on-data-saved="onEditCellDataSaved()"
/> />
</QDialog> </template>
</QPage> <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> </template>
<style lang="scss"> <style lang="scss">
.q-table th, .q-table th,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -37,6 +37,7 @@ const userParams = reactive({
ipt: 'H', ipt: 'H',
futureIpt: 'H', futureIpt: 'H',
isFullMovable: true, isFullMovable: true,
onlyWithDestination: true,
}); });
const ticketColumns = computed(() => [ const ticketColumns = computed(() => [
@ -441,6 +442,7 @@ watch(
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<VnTable <VnTable
data-key="advanceTickets" data-key="advanceTickets"
:map-key="false"
ref="vnTableRef" ref="vnTableRef"
url="Tickets/getTicketsAdvance" url="Tickets/getTicketsAdvance"
search-url="advanceTickets" search-url="advanceTickets"
@ -464,6 +466,7 @@ watch(
color="primary" color="primary"
name="vn:agency-term" name="vn:agency-term"
size="xs" size="xs"
class="q-mr-xs"
> >
<QTooltip class="column"> <QTooltip class="column">
<span> <span>
@ -482,6 +485,14 @@ watch(
</span> </span>
</QTooltip> </QTooltip>
</QIcon> </QIcon>
<QIcon
v-if="row.saleClonedFk"
color="primary"
name="content_copy"
size="xs"
>
<QTooltip>{{ t('advanceTickets.clonedSales') }}</QTooltip>
</QIcon>
</template> </template>
<template #column-id="{ row }"> <template #column-id="{ row }">
<QBtn flat class="link"> <QBtn flat class="link">

View File

@ -168,6 +168,16 @@ onMounted(async () => await getItemPackingTypes());
</VnSelect> </VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem>
<QItemSection>
<QCheckbox
toggle-indeterminate
label="only with destination"
v-model="params.onlyWithDestination"
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>
@ -182,6 +192,7 @@ en:
ipt: Destination IPT ipt: Destination IPT
isFullMovable: 100% movable isFullMovable: 100% movable
warehouseFk: Warehouse warehouseFk: Warehouse
onlyWithDestination: Only with destination
es: es:
Horizontal: Horizontal Horizontal: Horizontal
Vertical: Vertical Vertical: Vertical
@ -193,4 +204,5 @@ es:
ipt: IPT destino ipt: IPT destino
isFullMovable: 100% movible isFullMovable: 100% movible
warehouseFk: Almacén warehouseFk: Almacén
onlyWithDestination: Solo con destino
</i18n> </i18n>

View File

@ -53,6 +53,7 @@ advanceTickets:
errorsList: Errors list errorsList: Errors list
search: Search advance tickets search: Search advance tickets
searchInfo: Search advance tickets by ID or client ID searchInfo: Search advance tickets by ID or client ID
clonedSales: Has turn lines
futureTickets: futureTickets:
problems: Problems problems: Problems
shipped: Date shipped: Date

View File

@ -91,6 +91,7 @@ advanceTickets:
errorsList: Lista de errores errorsList: Lista de errores
search: Buscar por tickets adelantados search: Buscar por tickets adelantados
searchInfo: Buscar tickets adelantados por el identificador o el identificador del cliente searchInfo: Buscar tickets adelantados por el identificador o el identificador del cliente
clonedSales: Tiene líneas de turno
futureTickets: futureTickets:
problems: Problemas problems: Problemas
shipped: Fecha shipped: Fecha

View File

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

View File

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

View File

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

View File

@ -103,9 +103,12 @@ const formattedWeekTotalHours = computed(() =>
const onInputChange = async (date) => { const onInputChange = async (date) => {
if (!date) return; 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); const _date = new Date(year, month - 1, day);
setDate(_date); setDate(_date);
if (outside) getMailStates(_date);
}; };
const setDate = async (date) => { const setDate = async (date) => {

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ describe('EntryStockBought', () => {
cy.get('.q-notification__message').should('have.text', 'Data saved'); cy.get('.q-notification__message').should('have.text', 'Data saved');
}); });
it('Should add a new reserved space for buyerBoss', () => { 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="Reserve"]').type('1');
cy.get('input[aria-label="Date"]').eq(1).clear(); cy.get('input[aria-label="Date"]').eq(1).clear();
cy.get('input[aria-label="Date"]').eq(1).type('01-01'); cy.get('input[aria-label="Date"]').eq(1).type('01-01');

View File

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

View File

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

View File

@ -2,6 +2,7 @@
describe('InvoiceInVat', () => { describe('InvoiceInVat', () => {
const thirdRow = 'tbody > :nth-child(3)'; const thirdRow = 'tbody > :nth-child(3)';
const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)'; const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)';
const vats = '[data-cy="vat-sageiva"]';
const dialogInputs = '.q-dialog label input'; const dialogInputs = '.q-dialog label input';
const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon'; const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon';
const randomInt = Math.floor(Math.random() * 100); const randomInt = Math.floor(Math.random() * 100);
@ -14,9 +15,9 @@ describe('InvoiceInVat', () => {
}); });
it('should edit the sage iva', () => { 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.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', () => { it('should add a new row', () => {

View File

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

View File

@ -5,7 +5,7 @@ describe('RoadMap', () => {
cy.visit(`/#/route/roadmap`); cy.visit(`/#/route/roadmap`);
}); });
it('Route list create roadmap and redirect', () => { 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('input[name="name"]').eq(1).type('roadMapTestOne{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created'); cy.get('.q-notification__message').should('have.text', 'Data created');
cy.url().should('include', '/summary'); cy.url().should('include', '/summary');

View File

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

View File

@ -7,7 +7,7 @@ describe('WorkerPda', () => {
}); });
it('assign pda', () => { 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).click();
cy.get(select).type('{downArrow}{enter}'); cy.get(select).type('{downArrow}{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created'); cy.get('.q-notification__message').should('have.text', 'Data created');

View File

@ -1,10 +1,10 @@
describe('ZoneWarehouse', () => { describe('ZoneWarehouse', () => {
const data = { 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';
const dataError = "ER_DUP_ENTRY: Duplicate entry '2-2' for key 'zoneFk'"; const saveBtn = '.q-btn--standard > .q-btn__content > .block';
beforeEach(() => { beforeEach(() => {
cy.viewport(1280, 720); 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', () => { 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.addBtnClick();
cy.get(deviceProductionField).click(); cy.selectOption('[data-cy="Warehouse_select"]', 'Warehouse Two');
cy.get(deviceProductionField).type('{upArrow}{enter}'); cy.get(saveBtn).click();
cy.get('.q-notification__message').should('have.text', dataError); cy.checkNotification(dataError);
}); });
it('should create a warehouse', () => { it('should create & remove a warehouse', () => {
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click(); cy.addBtnClick();
cy.get(deviceProductionField).click();
cy.fillInForm(data); cy.fillInForm(data);
cy.get(saveBtn).click();
cy.get('.q-mt-lg > .q-btn--standard').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('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(); cy.reload();
}); });
}); });

View File

@ -58,8 +58,9 @@ Cypress.Commands.add('domContentLoad', (element, timeout = 5000) => {
cy.waitUntil(() => cy.document().then((doc) => doc.readyState === 'complete')); cy.waitUntil(() => cy.document().then((doc) => doc.readyState === 'complete'));
}); });
Cypress.Commands.add('waitForElement', (element, timeout = 5000) => { 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) => { Cypress.Commands.add('getValue', (selector) => {
cy.get(selector).then(($el) => { cy.get(selector).then(($el) => {
if ($el.find('.q-checkbox__inner').length > 0) { if ($el.find('.q-checkbox__inner').length > 0) {
@ -86,15 +87,40 @@ Cypress.Commands.add('getValue', (selector) => {
}); });
// Fill Inputs // Fill Inputs
Cypress.Commands.add('selectOption', (selector, option, timeout) => { Cypress.Commands.add('selectOption', (selector, option, timeout = 5000) => {
cy.waitForElement(selector); cy.waitForElement(selector, timeout);
cy.get(selector).click(); cy.get(selector).click();
cy.wait(timeout || 1000); cy.get(selector).invoke('data', 'url').as('dataUrl');
cy.get('.q-menu .q-item').contains(option).click(); 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) => { Cypress.Commands.add('countSelectOptions', (selector, option) => {
cy.waitForElement(selector); cy.waitForElement(selector);
cy.get(selector).click(); cy.get(selector).click({ force: true });
cy.get('.q-menu .q-item').should('have.length', option); 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; const { type, val } = field;
switch (type) { switch (type) {
case 'select': case 'select':
cy.get(el).click(); cy.selectOption(el, val);
cy.get('.q-menu .q-item').contains(val).click();
break; break;
case 'date': case 'date':
cy.get(el).type(val.split('-').join('')); 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') => { Cypress.Commands.add('dataCy', (tag, attr = 'data-cy') => {
return cy.get(`[${attr}="${tag}"]`); 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

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