Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 6897-e2eFixAndMinorfixes
gitea/salix-front/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Pablo Natek 2025-02-17 11:13:22 +01:00
commit a5b4341c97
34 changed files with 346 additions and 493 deletions

View File

@ -2,7 +2,6 @@
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectProvince from 'src/components/VnSelectProvince.vue'; import VnSelectProvince from 'src/components/VnSelectProvince.vue';
@ -21,14 +20,11 @@ const postcodeFormData = reactive({
provinceFk: null, provinceFk: null,
townFk: null, townFk: null,
}); });
const townsFetchDataRef = ref(false);
const townFilter = ref({}); const townFilter = ref({});
const countriesRef = ref(false); const countriesRef = ref(false);
const provincesOptions = ref([]); const provincesOptions = ref([]);
const townsOptions = ref([]);
const town = ref({}); const town = ref({});
const countryFilter = ref({});
function onDataSaved(formData) { function onDataSaved(formData) {
const newPostcode = { const newPostcode = {
@ -51,7 +47,6 @@ async function setCountry(countryFk, data) {
data.townFk = null; data.townFk = null;
data.provinceFk = null; data.provinceFk = null;
data.countryFk = countryFk; data.countryFk = countryFk;
await fetchTowns();
} }
// Province // Province
@ -60,22 +55,11 @@ async function setProvince(id, data) {
const newProvince = provincesOptions.value.find((province) => province.id == id); const newProvince = provincesOptions.value.find((province) => province.id == id);
if (newProvince) data.countryFk = newProvince.countryFk; if (newProvince) data.countryFk = newProvince.countryFk;
postcodeFormData.provinceFk = id; postcodeFormData.provinceFk = id;
await fetchTowns();
} }
async function onProvinceCreated(data) { async function onProvinceCreated(data) {
postcodeFormData.provinceFk = data.id; postcodeFormData.provinceFk = data.id;
} }
function provinceByCountry(countryFk = postcodeFormData.countryFk) {
return provincesOptions.value
.filter((province) => province.countryFk === countryFk)
.map(({ id }) => id);
}
// Town
async function handleTowns(data) {
townsOptions.value = data;
}
function setTown(newTown, data) { function setTown(newTown, data) {
town.value = newTown; town.value = newTown;
data.provinceFk = newTown?.provinceFk ?? newTown; data.provinceFk = newTown?.provinceFk ?? newTown;
@ -88,18 +72,6 @@ async function onCityCreated(newTown, formData) {
formData.townFk = newTown; formData.townFk = newTown;
setTown(newTown, formData); setTown(newTown, formData);
} }
async function fetchTowns(countryFk = postcodeFormData.countryFk) {
if (!countryFk) return;
const provinces = postcodeFormData.provinceFk
? [postcodeFormData.provinceFk]
: provinceByCountry();
townFilter.value.where = {
provinceFk: {
inq: provinces,
},
};
await townsFetchDataRef.value?.fetch();
}
async function filterTowns(name) { async function filterTowns(name) {
if (name !== '') { if (name !== '') {
@ -108,22 +80,11 @@ async function filterTowns(name) {
like: `%${name}%`, like: `%${name}%`,
}, },
}; };
await townsFetchDataRef.value?.fetch();
} }
} }
</script> </script>
<template> <template>
<FetchData
ref="townsFetchDataRef"
:sort-by="['name ASC']"
:limit="30"
:filter="townFilter"
@on-fetch="handleTowns"
auto-load
url="Towns/location"
/>
<FormModelPopup <FormModelPopup
url-create="postcodes" url-create="postcodes"
model="postcode" model="postcode"
@ -149,14 +110,13 @@ async function filterTowns(name) {
@filter="filterTowns" @filter="filterTowns"
:tooltip="t('Create city')" :tooltip="t('Create city')"
v-model="data.townFk" v-model="data.townFk"
:options="townsOptions" url="Towns/location"
option-label="name"
option-value="id"
:rules="validate('postcode.city')" :rules="validate('postcode.city')"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
:emit-value="false" :emit-value="false"
required required
data-cy="locationTown" data-cy="locationTown"
sort-by="name ASC"
> >
<template #option="{ itemProps, opt }"> <template #option="{ itemProps, opt }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
@ -197,16 +157,12 @@ async function filterTowns(name) {
/> />
<VnSelect <VnSelect
ref="countriesRef" ref="countriesRef"
:limit="30"
:filter="countryFilter"
:sort-by="['name ASC']" :sort-by="['name ASC']"
auto-load auto-load
url="Countries" url="Countries"
required required
:label="t('Country')" :label="t('Country')"
hide-selected hide-selected
option-label="name"
option-value="id"
v-model="data.countryFk" v-model="data.countryFk"
:rules="validate('postcode.countryFk')" :rules="validate('postcode.countryFk')"
@update:model-value="(value) => setCountry(value, data)" @update:model-value="(value) => setCountry(value, data)"

View File

@ -62,12 +62,9 @@ const where = computed(() => {
auto-load auto-load
:where="where" :where="where"
url="Autonomies/location" url="Autonomies/location"
:sort-by="['name ASC']" sort-by="name ASC"
:limit="30"
:label="t('Autonomy')" :label="t('Autonomy')"
hide-selected hide-selected
option-label="name"
option-value="id"
v-model="data.autonomyFk" v-model="data.autonomyFk"
:rules="validate('province.autonomyFk')" :rules="validate('province.autonomyFk')"
> >

View File

@ -42,7 +42,6 @@ const itemFilter = {
const itemFilterParams = reactive({}); const itemFilterParams = reactive({});
const closeButton = ref(null); const closeButton = ref(null);
const isLoading = ref(false); const isLoading = ref(false);
const producersOptions = ref([]);
const ItemTypesOptions = ref([]); const ItemTypesOptions = ref([]);
const InksOptions = ref([]); const InksOptions = ref([]);
const tableRows = ref([]); const tableRows = ref([]);
@ -121,23 +120,17 @@ const selectItem = ({ id }) => {
</script> </script>
<template> <template>
<FetchData
url="Producers"
@on-fetch="(data) => (producersOptions = data)"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
auto-load
/>
<FetchData <FetchData
url="ItemTypes" url="ItemTypes"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
order="name" order="name ASC"
@on-fetch="(data) => (ItemTypesOptions = data)" @on-fetch="(data) => (ItemTypesOptions = data)"
auto-load auto-load
/> />
<FetchData <FetchData
url="Inks" url="Inks"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
order="name" order="name ASC"
@on-fetch="(data) => (InksOptions = data)" @on-fetch="(data) => (InksOptions = data)"
auto-load auto-load
/> />
@ -152,11 +145,11 @@ const selectItem = ({ id }) => {
<VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" /> <VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" />
<VnSelect <VnSelect
:label="t('globals.producer')" :label="t('globals.producer')"
:options="producersOptions"
hide-selected hide-selected
option-label="name"
option-value="id"
v-model="itemFilterParams.producerFk" v-model="itemFilterParams.producerFk"
url="Producers"
:fields="['id', 'name']"
sort-by="name ASC"
/> />
<VnSelect <VnSelect
:label="t('globals.type')" :label="t('globals.type')"

View File

@ -124,7 +124,7 @@ const selectTravel = ({ id }) => {
<FetchData <FetchData
url="AgencyModes" url="AgencyModes"
@on-fetch="(data) => (agenciesOptions = data)" @on-fetch="(data) => (agenciesOptions = data)"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
auto-load auto-load
/> />
<FetchData <FetchData

View File

@ -121,9 +121,7 @@ const removeTag = (index, params, search) => {
applyTags(params, search); applyTags(params, search);
}; };
const setCategoryList = (data) => { const setCategoryList = (data) => {
categoryList.value = (data || []) categoryList.value = (data || []).map((category) => ({
.filter((category) => category.display)
.map((category) => ({
...category, ...category,
icon: `vn:${(category.icon || '').split('-')[1]}`, icon: `vn:${(category.icon || '').split('-')[1]}`,
})); }));
@ -132,12 +130,16 @@ const setCategoryList = (data) => {
</script> </script>
<template> <template>
<FetchData url="ItemCategories" limit="30" auto-load @on-fetch="setCategoryList" /> <FetchData
url="ItemCategories"
auto-load
@on-fetch="setCategoryList"
:where="{ display: { neq: 0 } }"
/>
<FetchData <FetchData
url="Tags" url="Tags"
:filter="{ fields: ['id', 'name', 'isFree'] }" :filter="{ fields: ['id', 'name', 'isFree'] }"
auto-load auto-load
limit="30"
@on-fetch="(data) => (tagOptions = data)" @on-fetch="(data) => (tagOptions = data)"
/> />
<VnFilterPanel <VnFilterPanel
@ -195,8 +197,6 @@ const setCategoryList = (data) => {
:label="t('components.itemsFilterPanel.typeFk')" :label="t('components.itemsFilterPanel.typeFk')"
v-model="params.typeFk" v-model="params.typeFk"
:options="itemTypesOptions" :options="itemTypesOptions"
option-value="id"
option-label="name"
dense dense
outlined outlined
rounded rounded
@ -234,7 +234,6 @@ const setCategoryList = (data) => {
:label="t('globals.tag')" :label="t('globals.tag')"
v-model="value.selectedTag" v-model="value.selectedTag"
:options="tagOptions" :options="tagOptions"
option-label="name"
dense dense
outlined outlined
rounded rounded

View File

@ -532,6 +532,9 @@ function formatColumnValue(col, row, dashIfEmpty) {
} }
} }
const checkbox = ref(null); const checkbox = ref(null);
function cardClick(_, row) {
if ($props.redirect) router.push({ path: `/${$props.redirect}/${row.id}` });
}
</script> </script>
<template> <template>
<QDrawer <QDrawer
@ -627,6 +630,7 @@ const checkbox = ref(null);
<template #header-cell="{ col }"> <template #header-cell="{ col }">
<QTh <QTh
v-if="col.visible ?? true" v-if="col.visible ?? true"
v-bind:class="col.headerClass"
class="body-cell" class="body-cell"
:style="col?.width ? `max-width: ${col?.width}` : ''" :style="col?.width ? `max-width: ${col?.width}` : ''"
style="padding: inherit" style="padding: inherit"
@ -767,18 +771,13 @@ const checkbox = ref(null);
</template> </template>
<template #item="{ row, colsMap }"> <template #item="{ row, colsMap }">
<component <component
:is="$props.redirect ? 'router-link' : 'span'" v-bind:is="'div'"
:to="`/${$props.redirect}/` + row.id" @click="(event) => cardClick(event, row)"
> >
<QCard <QCard
bordered bordered
flat flat
class="row no-wrap justify-between cursor-pointer q-pa-sm" class="row no-wrap justify-between cursor-pointer q-pa-sm"
@click="
(_, row) => {
$props.rowClick && $props.rowClick(row);
}
"
style="height: 100%" style="height: 100%"
> >
<QCardSection <QCardSection
@ -1047,7 +1046,7 @@ es:
.grid-three { .grid-three {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, max-content)); grid-template-columns: repeat(auto-fit, minmax(300px, max-content));
max-width: 100%; width: 100%;
grid-gap: 20px; grid-gap: 20px;
margin: 0 auto; margin: 0 auto;
} }

View File

@ -11,6 +11,7 @@ export async function useCau(res, message) {
const { config, headers, request, status, statusText, data } = res || {}; const { config, headers, request, status, statusText, data } = res || {};
const { params, url, method, signal, headers: confHeaders } = config || {}; const { params, url, method, signal, headers: confHeaders } = config || {};
const { message: resMessage, code, name } = data?.error || {}; const { message: resMessage, code, name } = data?.error || {};
delete confHeaders.Authorization;
const additionalData = { const additionalData = {
path: location.hash, path: location.hash,
@ -40,7 +41,7 @@ export async function useCau(res, message) {
handler: async () => { handler: async () => {
const locale = i18n.global.t; const locale = i18n.global.t;
const reason = ref( const reason = ref(
code == 'ACCESS_DENIED' ? locale('cau.askPrivileges') : '' code == 'ACCESS_DENIED' ? locale('cau.askPrivileges') : '',
); );
openConfirmationModal( openConfirmationModal(
locale('cau.title'), locale('cau.title'),
@ -59,10 +60,9 @@ export async function useCau(res, message) {
'onUpdate:modelValue': (val) => (reason.value = val), 'onUpdate:modelValue': (val) => (reason.value = val),
label: locale('cau.inputLabel'), label: locale('cau.inputLabel'),
class: 'full-width', class: 'full-width',
required: true,
autofocus: true, autofocus: true,
}, },
} },
); );
}, },
}, },

View File

@ -35,6 +35,12 @@ account.value.hasAccount = hasAccount.value;
const entityId = computed(() => +route.params.id); const entityId = computed(() => +route.params.id);
const hasitManagementAccess = ref(); const hasitManagementAccess = ref();
const hasSysadminAccess = ref(); const hasSysadminAccess = ref();
const isHimself = computed(() => user.value.id === account.value.id);
const url = computed(() =>
isHimself.value
? 'Accounts/change-password'
: `Accounts/${entityId.value}/setPassword`
);
async function updateStatusAccount(active) { async function updateStatusAccount(active) {
if (active) { if (active) {
@ -107,11 +113,8 @@ onMounted(() => {
:ask-old-pass="askOldPass" :ask-old-pass="askOldPass"
:submit-fn=" :submit-fn="
async (newPassword, oldPassword) => { async (newPassword, oldPassword) => {
await axios.patch(`Accounts/change-password`, { const body = isHimself ? { userId: entityId, oldPassword } : {};
userId: entityId, await axios.patch(url, { ...body, newPassword });
newPassword,
oldPassword,
});
} }
" "
/> />
@ -155,16 +158,10 @@ onMounted(() => {
> >
<QItemSection>{{ t('globals.delete') }}</QItemSection> <QItemSection>{{ t('globals.delete') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem v-if="hasSysadminAccess || isHimself" v-ripple clickable>
v-if="hasSysadminAccess" <QItemSection @click="onChangePass(isHimself)">
v-ripple {{ isHimself ? t('globals.changePass') : t('globals.setPass') }}
clickable
@click="user.id === account.id ? onChangePass(true) : onChangePass(false)"
>
<QItemSection v-if="user.id === account.id">
{{ t('globals.changePass') }}
</QItemSection> </QItemSection>
<QItemSection v-else>{{ t('globals.setPass') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
v-if="!account.hasAccount && hasSysadminAccess" v-if="!account.hasAccount && hasSysadminAccess"

View File

@ -190,7 +190,7 @@ async function saveWhenHasChanges() {
ref="claimLinesForm" ref="claimLinesForm"
:url="`Claims/${route.params.id}/lines`" :url="`Claims/${route.params.id}/lines`"
save-url="ClaimBeginnings/crud" save-url="ClaimBeginnings/crud"
:filter="linesFilter" :user-filter="linesFilter"
@on-fetch="onFetch" @on-fetch="onFetch"
v-model:selected="selected" v-model:selected="selected"
:default-save="false" :default-save="false"

View File

@ -156,7 +156,6 @@ function onDrag() {
url="Claims" url="Claims"
:filter="claimDmsFilter" :filter="claimDmsFilter"
@on-fetch="([data]) => setClaimDms(data)" @on-fetch="([data]) => setClaimDms(data)"
limit="20"
auto-load auto-load
ref="claimDmsRef" ref="claimDmsRef"
/> />

View File

@ -1,8 +1,6 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
@ -14,15 +12,14 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
states: {
type: Array,
default: () => [],
},
}); });
const states = ref([]);
defineExpose({ states });
</script> </script>
<template> <template>
<FetchData url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load />
<VnFilterPanel :data-key="props.dataKey" :search-button="true"> <VnFilterPanel :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">

View File

@ -10,12 +10,13 @@ import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import VnTable from 'src/components/VnTable/VnTable.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import ZoneDescriptorProxy from '../Zone/Card/ZoneDescriptorProxy.vue'; import ZoneDescriptorProxy from '../Zone/Card/ZoneDescriptorProxy.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
import FetchData from 'src/components/FetchData.vue';
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const dataKey = 'ClaimList'; const dataKey = 'ClaimList';
const claimFilterRef = ref(); const states = ref([]);
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left', align: 'left',
@ -81,8 +82,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
label: t('claim.state'), label: t('claim.state'),
format: ({ stateCode }) => format: ({ stateCode }) =>
claimFilterRef.value?.states.find(({ code }) => code === stateCode) states.value?.find(({ code }) => code === stateCode)?.description,
?.description,
name: 'stateCode', name: 'stateCode',
chip: { chip: {
condition: () => true, condition: () => true,
@ -92,7 +92,7 @@ const columns = computed(() => [
name: 'claimStateFk', name: 'claimStateFk',
component: 'select', component: 'select',
attrs: { attrs: {
options: claimFilterRef.value?.states, options: states.value,
optionLabel: 'description', optionLabel: 'description',
}, },
}, },
@ -125,6 +125,7 @@ const STATE_COLOR = {
</script> </script>
<template> <template>
<FetchData url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load />
<VnSection <VnSection
:data-key="dataKey" :data-key="dataKey"
:columns="columns" :columns="columns"
@ -135,7 +136,7 @@ const STATE_COLOR = {
}" }"
> >
<template #advanced-menu> <template #advanced-menu>
<ClaimFilter data-key="ClaimList" ref="claimFilterRef" /> <ClaimFilter :data-key ref="claimFilterRef" :states />
</template> </template>
<template #body> <template #body>
<VnTable <VnTable

View File

@ -17,8 +17,7 @@ const bankEntitiesRef = ref(null);
const filter = { const filter = {
fields: ['id', 'bic', 'name'], fields: ['id', 'bic', 'name'],
order: 'bic ASC', order: 'bic ASC'
limit: 30,
}; };
const getBankEntities = (data, formData) => { const getBankEntities = (data, formData) => {

View File

@ -10,6 +10,7 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnLocation from 'src/components/common/VnLocation.vue'; import VnLocation from 'src/components/common/VnLocation.vue';
import VnCheckbox from 'src/components/common/VnCheckbox.vue'; import VnCheckbox from 'src/components/common/VnCheckbox.vue';
import { getDifferences, getUpdatedValues } from 'src/filters';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
@ -24,6 +25,12 @@ function handleLocation(data, location) {
data.provinceFk = provinceFk; data.provinceFk = provinceFk;
data.countryFk = countryFk; data.countryFk = countryFk;
} }
function onBeforeSave(formData, originalData) {
return getUpdatedValues(
Object.keys(getDifferences(formData, originalData)),
formData,
);
}
</script> </script>
<template> <template>
@ -37,6 +44,7 @@ function handleLocation(data, location) {
:url-update="`Clients/${route.params.id}/updateFiscalData`" :url-update="`Clients/${route.params.id}/updateFiscalData`"
auto-load auto-load
model="Customer" model="Customer"
:mapper="onBeforeSave"
> >
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow> <VnRow>

View File

@ -87,7 +87,7 @@ onMounted(async () => {
<FetchData <FetchData
url="Campaigns/latest" url="Campaigns/latest"
@on-fetch="(data) => (campaignsOptions = data)" @on-fetch="(data) => (campaignsOptions = data)"
:filter="{ fields: ['id', 'code', 'dated'], order: 'code ASC', limit: 30 }" :filter="{ fields: ['id', 'code', 'dated'], order: 'code ASC' }"
auto-load auto-load
/> />
<FetchData <FetchData

View File

@ -1,8 +1,6 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
@ -18,18 +16,10 @@ defineProps({
}, },
}); });
const itemTypeWorkersOptions = ref([]);
const tagValues = ref([]); const tagValues = ref([]);
</script> </script>
<template> <template>
<FetchData
url="TicketRequests/getItemTypeWorker"
limit="30"
auto-load
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
/>
<ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']"> <ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem class="q-my-md"> <QItem class="q-my-md">
@ -37,9 +27,10 @@ const tagValues = ref([]);
<VnSelect <VnSelect
:label="t('components.itemsFilterPanel.salesPersonFk')" :label="t('components.itemsFilterPanel.salesPersonFk')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
:options="itemTypeWorkersOptions" url="TicketRequests/getItemTypeWorker"
option-value="id"
option-label="nickname" option-label="nickname"
:fields="['id', 'nickname']"
sort-by="nickname ASC"
dense dense
outlined outlined
rounded rounded
@ -52,8 +43,9 @@ const tagValues = ref([]);
<QItemSection> <QItemSection>
<VnSelectSupplier <VnSelectSupplier
v-model="params.supplierFk" v-model="params.supplierFk"
@update:model-value="searchFn()" url="Suppliers"
hide-selected :fields="['id', 'name', 'nickname']"
sort-by="name ASC"
dense dense
outlined outlined
rounded rounded

View File

@ -7,7 +7,6 @@ import { toDate } from 'src/filters';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { getTotal } from 'src/composables/getTotal'; import { getTotal } from 'src/composables/getTotal';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
@ -22,7 +21,6 @@ const invoiceIn = computed(() => arrayData.store.data);
const currency = computed(() => invoiceIn.value?.currency?.code); const currency = computed(() => invoiceIn.value?.currency?.code);
const rowsSelected = ref([]); const rowsSelected = ref([]);
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 } };
@ -41,10 +39,9 @@ const columns = computed(() => [
name: 'bank', name: 'bank',
label: t('Bank'), label: t('Bank'),
field: (row) => row.bankFk, field: (row) => row.bankFk,
options: banks.value,
model: 'bankFk', model: 'bankFk',
optionValue: 'id',
optionLabel: 'bank', optionLabel: 'bank',
url: 'Accountings',
sortable: true, sortable: true,
tabIndex: 2, tabIndex: 2,
align: 'left', align: 'left',
@ -82,12 +79,6 @@ onBeforeMount(async () => {
}); });
</script> </script>
<template> <template>
<FetchData
url="Accountings"
auto-load
limit="30"
@on-fetch="(data) => (banks = data)"
/>
<CrudModel <CrudModel
v-if="invoiceIn" v-if="invoiceIn"
ref="invoiceInFormRef" ref="invoiceInFormRef"
@ -117,9 +108,9 @@ onBeforeMount(async () => {
<QTd> <QTd>
<VnSelect <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :url="col.url"
:option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:option-value="col.optionValue"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -193,8 +184,7 @@ onBeforeMount(async () => {
:label="t('Bank')" :label="t('Bank')"
class="full-width" class="full-width"
v-model="props.row['bankFk']" v-model="props.row['bankFk']"
:options="banks" url="Accountings"
option-value="id"
option-label="bank" option-label="bank"
> >
<template #option="scope"> <template #option="scope">

View File

@ -1,8 +1,6 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue'; import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
@ -16,17 +14,9 @@ const props = defineProps({
}, },
}); });
const itemTypeWorkersOptions = ref([]);
</script> </script>
<template> <template>
<FetchData
url="TicketRequests/getItemTypeWorker"
limit="30"
auto-load
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
/>
<ItemsFilterPanel :data-key="props.dataKey" :custom-tags="['tags']"> <ItemsFilterPanel :data-key="props.dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem class="q-my-md"> <QItem class="q-my-md">
@ -34,14 +24,15 @@ const itemTypeWorkersOptions = ref([]);
<VnSelect <VnSelect
:label="t('params.buyerFk')" :label="t('params.buyerFk')"
v-model="params.buyerFk" v-model="params.buyerFk"
:options="itemTypeWorkersOptions" url="TicketRequests/getItemTypeWorker"
option-value="id" :fields="['id', 'nickname']"
option-label="nickname" option-label="nickname"
dense dense
outlined outlined
rounded rounded
use-input use-input
@update:model-value="searchFn()" @update:model-value="searchFn()"
sort-by="nickname ASC"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -50,11 +41,10 @@ const itemTypeWorkersOptions = ref([]);
<VnSelect <VnSelect
url="Warehouses" url="Warehouses"
auto-load auto-load
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :fields="['id', 'name']"
sort-by="name ASC"
:label="t('params.warehouseFk')" :label="t('params.warehouseFk')"
v-model="params.warehouseFk" v-model="params.warehouseFk"
option-label="name"
option-value="id"
dense dense
outlined outlined
rounded rounded

View File

@ -61,6 +61,7 @@ function exprBuilder(param, value) {
case 'nickname': case 'nickname':
return { [`t.nickname`]: { like: `%${value}%` } }; return { [`t.nickname`]: { like: `%${value}%` } };
case 'zoneFk': case 'zoneFk':
return { 't.zoneFk': value };
case 'department': case 'department':
return { 'd.name': value }; return { 'd.name': value };
case 'totalWithVat': case 'totalWithVat':

View File

@ -39,7 +39,7 @@ const addToOrder = async () => {
}); });
const { data: orderTotal } = await axios.get( const { data: orderTotal } = await axios.get(
`Orders/${Number(route.params.id)}/getTotal` `Orders/${Number(route.params.id)}/getTotal`,
); );
state.set('orderTotal', orderTotal); state.set('orderTotal', orderTotal);
@ -56,7 +56,7 @@ const canAddToOrder = () => {
if (canAddToOrder) { if (canAddToOrder) {
const excedQuantity = prices.value.reduce( const excedQuantity = prices.value.reduce(
(acc, { quantity }) => acc + quantity, (acc, { quantity }) => acc + quantity,
0 0,
); );
if (excedQuantity > props.item.available) { if (excedQuantity > props.item.available) {
canAddToOrder = false; canAddToOrder = false;

View File

@ -46,7 +46,6 @@ const exprBuilder = (param, value) => {
url="AgencyModes" url="AgencyModes"
:filter="{ fields: ['id', 'name'] }" :filter="{ fields: ['id', 'name'] }"
sort-by="name ASC" sort-by="name ASC"
limit="30"
@on-fetch="(data) => (agencyList = data)" @on-fetch="(data) => (agencyList = data)"
auto-load auto-load
/> />
@ -54,7 +53,6 @@ const exprBuilder = (param, value) => {
url="Agencies" url="Agencies"
:filter="{ fields: ['id', 'name'] }" :filter="{ fields: ['id', 'name'] }"
sort-by="name ASC" sort-by="name ASC"
limit="30"
@on-fetch="(data) => (agencyAgreementList = data)" @on-fetch="(data) => (agencyAgreementList = data)"
auto-load auto-load
/> />
@ -120,7 +118,11 @@ const exprBuilder = (param, value) => {
<VnSelectSupplier <VnSelectSupplier
:label="t('Autonomous')" :label="t('Autonomous')"
v-model="params.supplierFk" v-model="params.supplierFk"
hide-selected url="Suppliers"
:fields="['name']"
sort-by="name ASC"
option-value="name"
option-label="name"
dense dense
outlined outlined
rounded rounded

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import CardDescriptor from 'components/ui/CardDescriptor.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'components/ui/VnLv.vue'; import VnLv from 'components/ui/VnLv.vue';
@ -16,10 +16,30 @@ const $props = defineProps({
}); });
const route = useRoute(); const route = useRoute();
const { t } = useI18n();
const zone = ref();
const zoneId = ref();
const entityId = computed(() => { const entityId = computed(() => {
return $props.id || route.params.id; return $props.id || route.params.id;
}); });
const getZone = async () => {
const filter = {
where: { routeFk: $props.id ? $props.id : route.params.id },
};
const { data } = await axios.get('Tickets/findOne', {
params: {
filter: JSON.stringify(filter),
},
});
zoneId.value = data.zoneFk;
const { data: zoneData } = await axios.get(`Zones/${zoneId.value}`);
zone.value = zoneData.name;
};
const data = ref(useCardDescription());
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
onMounted(async () => {
getZone();
});
</script> </script>
<template> <template>
<CardDescriptor <CardDescriptor
@ -30,9 +50,9 @@ const entityId = computed(() => {
width="lg-width" width="lg-width"
> >
<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="zone" />
<VnLv <VnLv
:label="$t('Volume')" :label="$t('Volume')"
:value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty( :value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty(

View File

@ -29,6 +29,51 @@ const defaultInitialData = {
}; };
const maxDistance = ref(); const maxDistance = ref();
const routeFilter = {
fields: [
'id',
'workerFk',
'agencyModeFk',
'dated',
'm3',
'warehouseFk',
'description',
'vehicleFk',
'kmStart',
'kmEnd',
'started',
'finished',
'cost',
'isOk',
],
include: [
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
{
relation: 'vehicle',
scope: { fields: ['id', 'm3'] },
},
{
relation: 'ticket',
scope: {
fields: ['id', 'name', 'zoneFk'],
include: { relation: 'zone', scope: { fields: ['id', 'name'] } },
},
},
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['id'],
include: { relation: 'emailUser', scope: { fields: ['email'] } },
},
},
},
},
],
};
const onSave = (data, response) => { const onSave = (data, response) => {
if (isNew) { if (isNew) {
axios.post(`Routes/${response?.id}/updateWorkCenter`); axios.post(`Routes/${response?.id}/updateWorkCenter`);

View File

@ -48,7 +48,6 @@ const onFetch = (data) => {
}, },
], ],
}" }"
limit="30"
@on-fetch="onFetch" @on-fetch="onFetch"
/> />
<div :class="[isDialog ? 'column' : 'form-gap', 'full-width flex']"> <div :class="[isDialog ? 'column' : 'form-gap', 'full-width flex']">

View File

@ -18,6 +18,7 @@ const onSave = (data, response) => {
<template> <template>
<FormModel <FormModel
:update-url="`Roadmaps/${$route.params?.id}`" :update-url="`Roadmaps/${$route.params?.id}`"
:url="`Roadmaps/${$route.params?.id}`"
observe-form-changes observe-form-changes
model="Roadmap" model="Roadmap"
auto-load auto-load

View File

@ -1,7 +1,5 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
@ -65,6 +63,7 @@ const emit = defineEmits(['search']);
<QItemSection> <QItemSection>
<VnSelectSupplier <VnSelectSupplier
:label="t('Carrier')" :label="t('Carrier')"
:fields="['id', 'nickname']"
v-model="params.supplierFk" v-model="params.supplierFk"
dense dense
outlined outlined

View File

@ -8,6 +8,11 @@ import VnSelect from 'src/components/common/VnSelect.vue';
const sectors = ref([]); const sectors = ref([]);
const sectorFilter = { fields: ['id', 'description'] }; const sectorFilter = { fields: ['id', 'description'] };
const filter = {
fields: ['sectorFk', 'code', 'pickingOrder'],
include: [{ relation: 'sector', scope: sectorFilter }],
};
</script> </script>
<template> <template>
<FetchData <FetchData
@ -26,10 +31,6 @@ const sectorFilter = { fields: ['id', 'description'] };
:label="$t('parking.pickingOrder')" :label="$t('parking.pickingOrder')"
/> />
</VnRow> </VnRow>
<VnRow>
<VnInput v-model="data.row" :label="$t('parking.row')" />
<VnInput v-model="data.column" :label="$t('parking.column')" />
</VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
v-model="data.sectorFk" v-model="data.sectorFk"

View File

@ -1,7 +1,5 @@
parking: parking:
pickingOrder: Picking order pickingOrder: Picking order
sector: Sector sector: Sector
row: Row
column: Column
search: Search parking search: Search parking
searchInfo: You can search by parking code searchInfo: You can search by parking code

View File

@ -1,7 +1,5 @@
parking: parking:
pickingOrder: Orden de recogida pickingOrder: Orden de recogida
row: Fila
sector: Sector sector: Sector
column: Columna
search: Buscar parking search: Buscar parking
searchInfo: Puedes buscar por código de parking searchInfo: Puedes buscar por código de parking

View File

@ -49,7 +49,7 @@ watch(
<FetchData <FetchData
@on-fetch="(data) => (listPackagingsOptions = data)" @on-fetch="(data) => (listPackagingsOptions = data)"
auto-load auto-load
:filter="{ fields: ['packagingFk', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['packagingFk', 'name'], order: 'name ASC' }"
url="Packagings/listPackaging" url="Packagings/listPackaging"
/> />
<div class="flex justify-center"> <div class="flex justify-center">

View File

@ -1,19 +1,16 @@
<script setup> <script setup>
import { onMounted, ref, computed, reactive } from 'vue'; import { ref, computed, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import VnTable from 'src/components/VnTable/VnTable.vue';
import TicketFutureFilter from './TicketFutureFilter.vue'; import TicketFutureFilter from './TicketFutureFilter.vue';
import { dashIfEmpty, toCurrency } from 'src/filters'; import { dashIfEmpty, toCurrency } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
import { useArrayData } from 'composables/useArrayData';
import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js'; import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
@ -26,214 +23,126 @@ const { openConfirmationModal } = useVnConfirm();
const { notify } = useNotify(); const { notify } = useNotify();
const user = state.getUser(); const user = state.getUser();
const itemPackingTypesOptions = ref([]);
const selectedTickets = ref([]); const selectedTickets = ref([]);
const vnTableRef = ref({});
const exprBuilder = (param, value) => { const originElRef = ref(null);
switch (param) { const destinationElRef = ref(null);
case 'id':
return { id: value };
case 'futureId':
return { futureId: value };
case 'liters':
return { liters: value };
case 'lines':
return { lines: value };
case 'iptColFilter':
return { ipt: { like: `%${value}%` } };
case 'futureIptColFilter':
return { futureIpt: { like: `%${value}%` } };
case 'totalWithVat':
return { totalWithVat: value };
}
};
const userParams = reactive({ const userParams = reactive({
futureScopeDays: Date.vnNew().toISOString(), futureScopeDays: Date.vnNew().toISOString(),
originScopeDays: Date.vnNew().toISOString(), originScopeDays: Date.vnNew().toISOString(),
warehouseFk: user.value.warehouseFk, warehouseFk: user.value.warehouseFk,
}); });
const arrayData = useArrayData('FutureTickets', {
url: 'Tickets/getTicketsFuture',
userParams: userParams,
exprBuilder: exprBuilder,
});
const { store } = arrayData;
const params = reactive({
futureScopeDays: Date.vnNew(),
originScopeDays: Date.vnNew(),
warehouseFk: user.value.warehouseFk,
});
const applyColumnFilter = async (col) => {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await arrayData.addFilter({ params });
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const tickets = computed(() => store.data);
const ticketColumns = computed(() => [ const ticketColumns = computed(() => [
{ {
label: t('futureTickets.problems'), label: '',
name: 'problems', name: 'problems',
headerClass: 'horizontal-separator',
align: 'left', align: 'left',
columnFilter: null, columnFilter: false,
}, },
{ {
label: t('advanceTickets.ticketId'), label: t('advanceTickets.ticketId'),
name: 'ticketId', name: 'id',
align: 'center', align: 'center',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'id',
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
label: t('futureTickets.shipped'), label: t('futureTickets.shipped'),
name: 'shipped', name: 'shipped',
align: 'left', align: 'left',
sortable: true, columnFilter: false,
columnFilter: null, headerClass: 'horizontal-separator',
}, },
{ {
align: 'center',
class: 'shrink',
label: t('advanceTickets.ipt'), label: t('advanceTickets.ipt'),
name: 'ipt', name: 'ipt',
field: 'ipt',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
filterParamKey: 'iptColFilter',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: { attrs: {
options: itemPackingTypesOptions.value, url: 'itemPackingTypes',
'option-value': 'code', fields: ['code', 'description'],
'option-label': 'description', where: { isActive: true },
dense: true, optionValue: 'code',
optionLabel: 'description',
inWhere: false,
}, },
}, },
format: (val) => dashIfEmpty(val), format: (row, dashIfEmpty) => dashIfEmpty(row.ipt),
headerClass: 'horizontal-separator',
}, },
{ {
label: t('ticketList.state'), label: t('ticketList.state'),
name: 'state', name: 'state',
align: 'left', align: 'left',
sortable: true, columnFilter: false,
columnFilter: null, headerClass: 'horizontal-separator',
}, },
{ {
label: t('advanceTickets.liters'), label: t('advanceTickets.liters'),
name: 'liters', name: 'liters',
field: 'liters',
align: 'left', align: 'left',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
label: t('advanceTickets.import'), label: t('advanceTickets.import'),
field: 'import',
name: 'import', name: 'import',
align: 'left', align: 'left',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: false,
format: (row) => toCurrency(row.totalWithVat),
}, },
{ {
label: t('futureTickets.availableLines'), label: t('futureTickets.availableLines'),
name: 'lines', name: 'lines',
field: 'lines', field: 'lines',
align: 'center', align: 'center',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: { format: (row, dashIfEmpty) => dashIfEmpty(row.lines),
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
}, },
{ {
label: t('advanceTickets.futureId'), label: t('advanceTickets.futureId'),
name: 'futureId', name: 'futureId',
align: 'left', align: 'center',
sortable: true, headerClass: 'horizontal-separator vertical-separator ',
columnFilter: { columnClass: 'vertical-separator',
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'futureId',
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
label: t('futureTickets.futureShipped'), label: t('futureTickets.futureShipped'),
name: 'futureShipped', name: 'futureShipped',
align: 'left', align: 'left',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: null, columnFilter: false,
format: (val) => dashIfEmpty(val), format: (row) => toDateTimeFormat(row.futureShipped),
}, },
{ {
align: 'center',
label: t('advanceTickets.futureIpt'), label: t('advanceTickets.futureIpt'),
class: 'shrink',
name: 'futureIpt', name: 'futureIpt',
field: 'futureIpt',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
filterParamKey: 'futureIptColFilter',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: { attrs: {
options: itemPackingTypesOptions.value, url: 'itemPackingTypes',
'option-value': 'code', fields: ['code', 'description'],
'option-label': 'description', where: { isActive: true },
dense: true, optionValue: 'code',
optionLabel: 'description',
}, },
}, },
format: (val) => dashIfEmpty(val), headerClass: 'horizontal-separator',
format: (row, dashIfEmpty) => dashIfEmpty(row.futureIpt),
}, },
{ {
label: t('advanceTickets.futureState'), label: t('advanceTickets.futureState'),
name: 'futureState', name: 'futureState',
align: 'right', align: 'right',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: null, class: 'expand',
format: (val) => dashIfEmpty(val), columnFilter: false,
format: (row, dashIfEmpty) => dashIfEmpty(row.futureState),
}, },
]); ]);
@ -258,26 +167,51 @@ const moveTicketsFuture = async () => {
await axios.post('Tickets/merge', params); await axios.post('Tickets/merge', params);
notify(t('advanceTickets.moveTicketSuccess'), 'positive'); notify(t('advanceTickets.moveTicketSuccess'), 'positive');
selectedTickets.value = []; selectedTickets.value = [];
arrayData.fetch({ append: false }); vnTableRef.value.reload();
}; };
onMounted(async () => {
await arrayData.fetch({ append: false }); watch(
}); () => vnTableRef.value.tableRef?.$el,
($el) => {
if (!$el) return;
const head = $el.querySelector('thead');
const firstRow = $el.querySelector('thead > tr');
const newRow = document.createElement('tr');
destinationElRef.value = document.createElement('th');
originElRef.value = document.createElement('th');
newRow.classList.add('bg-header');
destinationElRef.value.classList.add('text-uppercase', 'color-vn-label');
originElRef.value.classList.add('text-uppercase', 'color-vn-label');
destinationElRef.value.setAttribute('colspan', '7');
originElRef.value.setAttribute('colspan', '9');
originElRef.value.textContent = `${t('advanceTickets.origin')}`;
destinationElRef.value.textContent = `${t('advanceTickets.destination')}`;
newRow.append(destinationElRef.value, originElRef.value);
head.insertBefore(newRow, firstRow);
},
{ once: true, inmmediate: true },
);
watch(
() => vnTableRef.value.params,
() => {
if (originElRef.value && destinationElRef.value) {
destinationElRef.value.textContent = `${t('advanceTickets.origin')}`;
originElRef.value.textContent = `${t('advanceTickets.destination')}`;
}
},
{ deep: true },
);
</script> </script>
<template> <template>
<FetchData
url="itemPackingTypes"
:filter="{
fields: ['code', 'description'],
order: 'description ASC',
where: { isActive: true },
}"
auto-load
@on-fetch="(data) => (itemPackingTypesOptions = data)"
/>
<VnSearchbar <VnSearchbar
data-key="FutureTickets" data-key="futureTicket"
:label="t('Search ticket')" :label="t('Search ticket')"
:info="t('futureTickets.searchInfo')" :info="t('futureTickets.searchInfo')"
/> />
@ -293,7 +227,7 @@ onMounted(async () => {
t(`futureTickets.moveTicketDialogSubtitle`, { t(`futureTickets.moveTicketDialogSubtitle`, {
selectedTickets: selectedTickets.length, selectedTickets: selectedTickets.length,
}), }),
moveTicketsFuture moveTicketsFuture,
) )
" "
> >
@ -305,77 +239,29 @@ onMounted(async () => {
</VnSubToolbar> </VnSubToolbar>
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
<TicketFutureFilter data-key="FutureTickets" /> <TicketFutureFilter data-key="futureTickets" />
</template> </template>
</RightMenu> </RightMenu>
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<QTable <VnTable
:rows="tickets" data-key="futureTickets"
ref="vnTableRef"
url="Tickets/getTicketsFuture"
search-url="futureTickets"
:user-params="userParams"
:limit="0"
:columns="ticketColumns" :columns="ticketColumns"
row-key="id" :table="{
selection="multiple" 'row-key': '$index',
selection: 'multiple',
}"
v-model:selected="selectedTickets" v-model:selected="selectedTickets"
:pagination="{ rowsPerPage: 0 }" :right-search="false"
:no-data-label="t('globals.noResults')" auto-load
style="max-width: 99%" :disable-option="{ card: true }"
> >
<template #header="props"> <template #column-problems="{ row }">
<QTr> <span class="q-gutter-x-xs">
<QTh class="horizontal-separator" />
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="8"
translate
>
{{ t('advanceTickets.origin') }}
</QTh>
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="4"
translate
>
{{ t('advanceTickets.destination') }}
</QTh>
</QTr>
<QTr>
<QTh>
<QCheckbox v-model="props.selected" />
</QTh>
<QTh
v-for="(col, index) in ticketColumns"
:key="index"
:class="{ 'vertical-separator': col.name === 'futureId' }"
>
{{ col.label }}
</QTh>
</QTr>
</template>
<template #top-row="{ cols }">
<QTr>
<QTd />
<QTd
v-for="(col, index) in cols"
:key="index"
style="max-width: 100px"
>
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #header-cell-availableLines="{ col }">
<QTh class="vertical-separator">
{{ col.label }}
</QTh>
</template>
<template #body-cell-problems="{ row }">
<QTd class="q-gutter-x-xs">
<QIcon <QIcon
v-if="row.futureAgencyFk !== row.agencyFk && row.agencyFk" v-if="row.futureAgencyFk !== row.agencyFk && row.agencyFk"
color="primary" color="primary"
@ -465,18 +351,15 @@ onMounted(async () => {
{{ t('futureTickets.rounding') }} {{ t('futureTickets.rounding') }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
</QTd> </span>
</template> </template>
<template #body-cell-ticketId="{ row }"> <template #column-id="{ row }">
<QTd> <QBtn flat class="link" @click.stop dense>
<QBtn flat class="link">
{{ row.id }} {{ row.id }}
<TicketDescriptorProxy :id="row.id" /> <TicketDescriptorProxy :id="row.id" />
</QBtn> </QBtn>
</QTd>
</template> </template>
<template #body-cell-shipped="{ row }"> <template #column-shipped="{ row }">
<QTd class="shipped">
<QBadge <QBadge
text-color="black" text-color="black"
:color="getDateQBadgeColor(row.shipped)" :color="getDateQBadgeColor(row.shipped)"
@ -484,11 +367,10 @@ onMounted(async () => {
> >
{{ toDateTimeFormat(row.shipped) }} {{ toDateTimeFormat(row.shipped) }}
</QBadge> </QBadge>
</QTd>
</template> </template>
<template #body-cell-state="{ row }"> <template #column-state="{ row }">
<QTd>
<QBadge <QBadge
v-if="row.state"
text-color="black" text-color="black"
:color="row.classColor" :color="row.classColor"
class="q-ma-none" class="q-ma-none"
@ -496,10 +378,9 @@ onMounted(async () => {
> >
{{ row.state }} {{ row.state }}
</QBadge> </QBadge>
</QTd> <span v-else> {{ dashIfEmpty(row.state) }}</span>
</template> </template>
<template #body-cell-import="{ row }"> <template #column-import="{ row }">
<QTd>
<QBadge <QBadge
:text-color=" :text-color="
totalPriceColor(row.totalWithVat) === 'warning' totalPriceColor(row.totalWithVat) === 'warning'
@ -512,18 +393,14 @@ onMounted(async () => {
> >
{{ toCurrency(row.totalWithVat || 0) }} {{ toCurrency(row.totalWithVat || 0) }}
</QBadge> </QBadge>
</QTd>
</template> </template>
<template #body-cell-futureId="{ row }"> <template #column-futureId="{ row }">
<QTd class="vertical-separator"> <QBtn flat class="link" @click.stop dense>
<QBtn flat class="link" dense>
{{ row.futureId }} {{ row.futureId }}
<TicketDescriptorProxy :id="row.futureId" /> <TicketDescriptorProxy :id="row.futureId" />
</QBtn> </QBtn>
</QTd>
</template> </template>
<template #body-cell-futureShipped="{ row }"> <template #column-futureShipped="{ row }">
<QTd class="shipped">
<QBadge <QBadge
text-color="black" text-color="black"
:color="getDateQBadgeColor(row.futureShipped)" :color="getDateQBadgeColor(row.futureShipped)"
@ -531,33 +408,30 @@ onMounted(async () => {
> >
{{ toDateTimeFormat(row.futureShipped) }} {{ toDateTimeFormat(row.futureShipped) }}
</QBadge> </QBadge>
</QTd>
</template> </template>
<template #body-cell-futureState="{ row }"> <template #column-futureState="{ row }">
<QTd>
<QBadge <QBadge
text-color="black" text-color="black"
:color="row.futureClassColor" :color="row.futureClassColor"
class="q-ma-none" class="q-mr-xs"
dense dense
> >
{{ row.futureState }} {{ row.futureState }}
</QBadge> </QBadge>
</QTd>
</template> </template>
</QTable> </VnTable>
</QPage> </QPage>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.shipped { :deep(.vertical-separator) {
min-width: 132px;
}
.vertical-separator {
border-left: 4px solid white !important; border-left: 4px solid white !important;
} }
.horizontal-separator { :deep(.horizontal-separator) {
border-top: 4px solid white !important;
}
:deep(.horizontal-bottom-separator) {
border-bottom: 4px solid white !important; border-bottom: 4px solid white !important;
} }
</style> </style>

View File

@ -12,7 +12,7 @@ import axios from 'axios';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ defineProps({
dataKey: { dataKey: {
type: String, type: String,
required: true, required: true,
@ -58,7 +58,7 @@ onMounted(async () => {
auto-load auto-load
/> />
<VnFilterPanel <VnFilterPanel
:data-key="props.dataKey" :data-key
:un-removable-params="['warehouseFk', 'originScopeDays ', 'futureScopeDays']" :un-removable-params="['warehouseFk', 'originScopeDays ', 'futureScopeDays']"
> >
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">

View File

@ -31,20 +31,18 @@ onMounted(async () => {
ref="summary" ref="summary"
:url="`Departments/${entityId}`" :url="`Departments/${entityId}`"
class="full-width" class="full-width"
style="max-width: 900px"
module-name="Department"
> >
<template #header="{ entity }"> <template #header="{ entity }">
<div>{{ entity.name }}</div> <div>{{ entity.name }}</div>
</template> </template>
<template #body="{ entity: department }"> <template #body="{ entity: department }">
<QCard class="column"> <QCard class="vn-one">
<VnTitle <VnTitle
:url="`#/worker/department/${entityId}/basic-data`" :url="`#/worker/department/${entityId}/basic-data`"
:text="t('Basic data')" :text="t('Basic data')"
/> />
<div class="full-width row wrap justify-between content-between"> <div class="full-width row wrap justify-between content-between">
<div class="column" style="min-width: 50%"> <div class="column">
<VnLv :label="t('globals.name')" :value="department.name" dash /> <VnLv :label="t('globals.name')" :value="department.name" dash />
<VnLv :label="t('globals.code')" :value="department.code" dash /> <VnLv :label="t('globals.code')" :value="department.code" dash />
<VnLv <VnLv

View File

@ -35,7 +35,7 @@ const filterWhere = computed(() => ({
auto-load auto-load
@on-fetch="(data) => (validAddresses = data)" @on-fetch="(data) => (validAddresses = data)"
/> />
<FormModel :url="`Zones/${route.params.id}`" auto-load model="zone"> <FormModel :url="`Zones/${$route.params.id}`" auto-load data-key="Zone">
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow> <VnRow>
<VnInput <VnInput