0
0
Fork 0

Merge branch 'dev' into 7671_dense_itemFixedPrices

This commit is contained in:
Javier Segarra 2024-07-16 09:23:18 +02:00
commit 2620fd2d94
73 changed files with 1988 additions and 1318 deletions

View File

@ -14,5 +14,5 @@
"[vue]": { "[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"cSpell.words": ["axios"] "cSpell.words": ["axios", "composables"]
} }

View File

@ -1,3 +1,169 @@
# Version 24.28 - 2024-07-09
### Added 🆕
- Change header titles style by:wbuezas
- chore: refs #7436 fix e2e (origin/7436-showQCheckbox) by:jorgep
- feat: #7196 eslint (origin/7196-cjsToEsm) by:jgallego
- feat: adapt tu VnTable → CrudModel by:alexm
- feat(CustomerFIlter): use correct table by:alexm
- feat(customerList): add searchbar by:alexm
- feat: customerList is customerExtendedList by:alexm
- feat: fixes #7196 by:jgallego
- feat: refs #6739 transferInvoice new checkbox and functionality by:Jon
- feat: refs #6825 create vnTable and add in CustomerExtendedList by:alexm
- feat: refs #6825 create vnTableColumn, cardActions by:alexm
- feat: refs #6825 fix modes by:alexm
- feat: refs #6825 qchip color by:alexm
- feat: refs #6825 right filter panel (6825-vnTable) by:alexm
- feat: refs #6825 scroll for table mode by:alexm
- feat: refs #6825 share filters, create popup by:alexm
- feat: refs #6825 VnComponent mix component and attrs Form to create new row by:alexm
- feat: refs #6825 VnTableFilter and VnPanelFilter init by:alexm
- feat: refs #6826 added rol summary link by:Jon
- feat: refs #6896 created VnImg and added to order module by:Jon
- feat: refs #6896 new filters by:Jon
- feat: refs #7129 fix some code and add order by:pablone
- feat: refs #7436 show checkbox by:jorgep
- feat: refs #7545 Deleted hasIncoterms client column (origin/7545-hasIncoterms) by:guillermo
- feat(TicketService): use correct format by:alexm
- feat(url): sepate filters by:alexm
- feat(VnFilter): merge objects by:alexm
- feat(VnTable): is-editable and use-model. fix: checkbox by:alexm
- feat(VnTable): refs #6825 actions sticky by:alexm
- feat(VnTable): refs #6825 addInWhere by:alexm
- feat(VnTable): refs #6825 dinamic columns by:alexm
- feat(VnTable): refs #6825 execute function when create by:alexm
- feat(VnTable): refs #6825 fix ellipsis and add titles by:alexm
- feat(VnTable): refs #6825 merge where's by:alexm
- feat(VnTable): refs #6825 move to folder. fix checkboxs by:alexm
- feat(VnTable): refs #6825 remove field prop. Add actions in table by:alexm
- feat(VnTable): refs #6825 use checkbox if startsWith 'is' or 'has' by:alexm
- feat(VnTable): refs #6825 VnTableChip component by:alexm
- feat(vnTable): reload data when change url by:alexm
- feat(WorkerFormation): add columnFilter by:alexm
- feat(WorkerFormation): is-editable and use-model by:alexm
- fix: notify icon style by:Javier Segarra
- refactor: refs #6896 fixed styles by:Jon
- Revert "feat: fixes #7196" by:alexm
- style: refs #6464 changed checkbox and qbtn styles by:Jon
### Changed 📦
- perf: Remove div.col by:Javier Segarra
- perf: remove ItemPicture by:Javier Segarra
- perf: replace ItemPicture in favour of VnImg by:Javier Segarra
- refactor by:wbuezas
- refactor: refs #5447 changed warehouse filter by:Jon
- refactor: refs #5447 changed warehouse out filter behavior by:Jon
- refactor: refs #5447 fixed filter if continent not selected by:Jon
- refactor: refs #5447 fix request by:Jon
- refactor: refs #5447 refactor filters by:Jon
- refactor: refs #6739 changed invoice functions' name by:Jon
- refactor: refs #6739 changed router.push by:Jon
- refactor: refs #6739 deleted useless const by:Jon
- refactor: refs #6739 fix redirect transferInvoice by:Jon
- refactor: refs #6739 new confirmation window by:Jon
- refactor: refs #6739 requested changes by:Jon
- refactor: refs #6739 updated transferInvoice function by:Jon
- refactor: refs #6896 changes requested in PR by:Jon
- refactor: refs #6896 end migration orders by:Jon
- refactor: refs #6896 fixed styles by:Jon
- refactor: refs #6896 fix qdrawer by:Jon
- refactor: refs #6896 refactor VnImg by:Jon
- refactor: refs #6896 requested changes by:Jon
- refactor: refs #6977 fix VnImg props (origin/6977-ClonedURL) by:Jon
- refactor: refs #6977 refactor VnImg by:Jon
- refactor: refs #6977 use VnImg by:Jon
- refactors by:alexm
### Fixed 🛠️
- chore: refs #7436 fix e2e (origin/7436-showQCheckbox) by:jorgep
- feat: fixes #7196 by:jgallego
- feat: refs #6825 fix modes by:alexm
- feat: refs #7129 fix some code and add order by:pablone
- feat(VnTable): is-editable and use-model. fix: checkbox by:alexm
- feat(VnTable): refs #6825 fix ellipsis and add titles by:alexm
- feat(VnTable): refs #6825 move to folder. fix checkboxs by:alexm
- fix(ArrayData): refs #6825 router.replace and use filter.where by:alexm
- fix: bug replace by:alexm
- fix: column hidden v-if by:Javier Segarra
- fix: comment 4 by:Javier Segarra
- fix: comments by:Javier Segarra
- fix: cypress.config to mjs by:alexm
- fix(EntryBuys): fix VnSubtoolbar by:alexm
- fixes: fix vnFilter params and redirect by:alexm
- fix: fix warnings by:alexm
- fix: invoiceDueDay test by:alexm
- fix log view not refreshing when changing id param by:wbuezas
- fix: map selected by:Javier Segarra
- fix: merge dev by:Javier Segarra
- fix: notify icon style by:Javier Segarra
- fix: point 1 by:Javier Segarra
- fix: point 3 by:Javier Segarra
- fix: refs #5447 deleted console.log by:Jon
- fix: refs 6464 deleted useless class in checkbox by:Jon
- fix: refs #6464 fix error isLoading by:Jon
- fix: refs #6739 changed checkbox field by:Jon
- fix: refs #6825 css by:carlossa
- fix: refs #6826 fix redirect by:Jon
- fix: refs #6826 fix roleDescriptor by:Jon
- fix: refs #7129 fix e2e by:pablone
- fix: refs #7129 fix module routes by:pablone
- fix: refs #7129 fix some issues on load and tools by:pablone
- fix: refs #7129 remove consoleLog by:pablone
- fix: refs #7129 remove fix from claim lines by:pablone
- fix: refs #7274 fix duplicate rows by:jorgep
- fix: refs #7433 skeleton by:jorgep
- fix: refs #7623 bugs & tests by:jorgep
- fix: refs #7623 disable router update by:jorgep
- fix: refs #7623 redirect by:jorgep
- fix: refs #7623 test by:jorgep
- fix: refs #7623 update add updateRoute prop in VnPaginate by:jorgep
- fix: refs #7623 updating skip param by:jorgep
- fix: revert cypress mjs by:alexm
- fix: SkeletonTable by:alexm
- fix: state translations by:Javier Segarra
- fix: ticket order by:Javier Segarra
- fix(ticket router): typo by:alexm
- fix(TicketService): pay use selected by:alexm
- fix: TravelLog by:Javier Segarra
- fix(url): filter by:alexm
- fix(url): redirect by:alexm
- fix(VnFilter): filter with params by:alexm
- fix(VnFilterPanel): remove key by:alexm
- fix(VnTable): create scss by:alexm
- fix(VnTable): duplicate fetch by:alexm
- fix(VnTable): Qtable v-bind by:alexm
- fix(VnTable): refs #6825 checkbox align and color by:alexm
- fix(VnTable): refs #6825 fix click sticky column by:alexm
- fix(VnTable): refs #6825 fix events and css by:alexm
- fix(VnTable): refs #6825 VnInputDate by:alexm
- fix(VnTable): showLabel by:alexm
- fix(VnTable): warns by:alexm
- fix: WorkerNotificationsManager test by:alexm
- fix: WorkerSelect option format by:Javier Segarra
- refactor: refs #5447 fixed filter if continent not selected by:Jon
- refactor: refs #5447 fix request by:Jon
- refactor: refs #6739 fix redirect transferInvoice by:Jon
- refactor: refs #6896 fixed styles by:Jon
- refactor: refs #6896 fix qdrawer by:Jon
- refactor: refs #6977 fix VnImg props (origin/6977-ClonedURL) by:Jon
- refs #6504 fix formModel claimFilter claimCard (origin/6504-fixCardClaim) by:carlossa
- refs #7406 fix components by:carlossa
- refs #7406 fix pr by:carlossa
- refs #7406 fix props by:carlossa
- refs #7406 fix Tb components create by:carlossa
- refs #7406 fix trad by:carlossa
- refs #7406 fix url by:carlossa
- refs #7406 fix VnTable columns by:carlossa
- refs #7409 fix balance and formation by:carlossa
- refs #7409 fix trad by:carlossa
- Revert "feat: fixes #7196" by:alexm
- test: fix intermitent e2e by:alexm
- test: fix vnSearchbar adapt to vnTable (origin/7648_dev_customerEntries) by:alexm
# Version 24.24 - 2024-06-11 # Version 24.24 - 2024-06-11
### Added 🆕 ### Added 🆕

View File

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

View File

@ -98,19 +98,17 @@ defineExpose({
}); });
async function fetch(data) { async function fetch(data) {
if (data && Array.isArray(data)) {
let $index = 0;
data.map((d) => (d.$index = $index++));
}
resetData(data); resetData(data);
emit('onFetch', data); emit('onFetch', data);
return data; return data;
} }
function resetData(data) { function resetData(data) {
if (!data) return; if (!data) return;
if (data && Array.isArray(data)) {
let $index = 0;
data.map((d) => (d.$index = $index++));
}
originalData.value = JSON.parse(JSON.stringify(data)); originalData.value = JSON.parse(JSON.stringify(data));
formData.value = JSON.parse(JSON.stringify(data)); formData.value = JSON.parse(JSON.stringify(data));
@ -299,7 +297,7 @@ watch(formUrl, async () => {
:url="url" :url="url"
:limit="limit" :limit="limit"
@on-fetch="fetch" @on-fetch="fetch"
@on-change="resetData" @on-change="fetch"
:skeleton="false" :skeleton="false"
ref="vnPaginateRef" ref="vnPaginateRef"
v-bind="$attrs" v-bind="$attrs"

View File

@ -90,7 +90,7 @@ const $props = defineProps({
}); });
const emit = defineEmits(['onFetch', 'onDataSaved']); const emit = defineEmits(['onFetch', 'onDataSaved']);
const modelValue = computed( const modelValue = computed(
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`, () => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`
).value; ).value;
const componentIsRendered = ref(false); const componentIsRendered = ref(false);
const arrayData = useArrayData(modelValue); const arrayData = useArrayData(modelValue);
@ -137,7 +137,7 @@ onMounted(async () => {
JSON.stringify(newVal) !== JSON.stringify(originalData.value); JSON.stringify(newVal) !== JSON.stringify(originalData.value);
isResetting.value = false; isResetting.value = false;
}, },
{ deep: true }, { deep: true }
); );
} }
}); });
@ -145,7 +145,7 @@ onMounted(async () => {
if (!$props.url) if (!$props.url)
watch( watch(
() => arrayData.store.data, () => arrayData.store.data,
(val) => updateAndEmit('onFetch', val), (val) => updateAndEmit('onFetch', val)
); );
watch(formUrl, async () => { watch(formUrl, async () => {
@ -239,7 +239,7 @@ function filter(value, update, filterOptions) {
(ref) => { (ref) => {
ref.setOptionIndex(-1); ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true); ref.moveOptionSelection(1, true);
}, }
); );
} }
@ -261,13 +261,7 @@ defineExpose({
</script> </script>
<template> <template>
<div class="column items-center full-width"> <div class="column items-center full-width">
<QForm <QForm @submit="save" @reset="reset" class="q-pa-md" id="formModel">
@submit="save"
@reset="reset"
class="q-pa-md"
id="formModel"
>
<QCard> <QCard>
<slot <slot
v-if="formData" v-if="formData"
@ -276,70 +270,72 @@ defineExpose({
:validate="validate" :validate="validate"
:filter="filter" :filter="filter"
/> />
<SkeletonForm v-else/> <SkeletonForm v-else />
</QCard> </QCard>
</QForm> </QForm>
</div> </div>
<Teleport <Teleport
to="#st-actions" to="#st-actions"
v-if="stateStore?.isSubToolbarShown() && componentIsRendered" v-if="
$props.defaultActions &&
stateStore?.isSubToolbarShown() &&
componentIsRendered
"
> >
<div v-if="$props.defaultActions"> <QBtnGroup push class="q-gutter-x-sm">
<QBtnGroup push class="q-gutter-x-sm"> <slot name="moreActions" />
<slot name="moreActions" /> <QBtn
<QBtn :label="tMobile(defaultButtons.reset.label)"
:label="tMobile(defaultButtons.reset.label)" :color="defaultButtons.reset.color"
:color="defaultButtons.reset.color" :icon="defaultButtons.reset.icon"
:icon="defaultButtons.reset.icon" flat
flat @click="reset"
@click="reset" :disable="!hasChanges"
:disable="!hasChanges" :title="t(defaultButtons.reset.label)"
:title="t(defaultButtons.reset.label)" />
/> <QBtnDropdown
<QBtnDropdown v-if="$props.goTo"
v-if="$props.goTo" @click="saveAndGo"
@click="saveAndGo" :label="tMobile('globals.saveAndContinue')"
:label="tMobile('globals.saveAndContinue')" :title="t('globals.saveAndContinue')"
:title="t('globals.saveAndContinue')" :disable="!hasChanges"
:disable="!hasChanges" color="primary"
color="primary" icon="save"
icon="save" split
split >
> <QList>
<QList> <QItem
<QItem clickable
clickable v-close-popup
v-close-popup @click="save"
@click="save" :title="t('globals.save')"
:title="t('globals.save')" >
> <QItemSection>
<QItemSection> <QItemLabel>
<QItemLabel> <QIcon
<QIcon name="save"
name="save" color="white"
color="white" class="q-mr-sm"
class="q-mr-sm" size="sm"
size="sm" />
/> {{ t('globals.save').toUpperCase() }}
{{ t('globals.save').toUpperCase() }} </QItemLabel>
</QItemLabel> </QItemSection>
</QItemSection> </QItem>
</QItem> </QList>
</QList> </QBtnDropdown>
</QBtnDropdown> <QBtn
<QBtn v-else
v-else :label="tMobile('globals.save')"
:label="tMobile('globals.save')" color="primary"
color="primary" icon="save"
icon="save" @click="save"
@click="save" :disable="!hasChanges"
:disable="!hasChanges" :title="t(defaultButtons.save.label)"
:title="t(defaultButtons.save.label)" />
/> </QBtnGroup>
</QBtnGroup>
</div>
</Teleport> </Teleport>
<QInnerLoading <QInnerLoading
:showing="isLoading" :showing="isLoading"
:label="t('globals.pleaseWait')" :label="t('globals.pleaseWait')"

View File

@ -23,18 +23,15 @@ const formModelRef = ref(null);
const closeButton = ref(null); const closeButton = ref(null);
const onDataSaved = (formData, requestResponse) => { const onDataSaved = (formData, requestResponse) => {
closeForm(); if (closeButton.value) closeButton.value.click();
emit('onDataSaved', formData, requestResponse); emit('onDataSaved', formData, requestResponse);
}; };
const isLoading = computed(() => formModelRef.value?.isLoading); const isLoading = computed(() => formModelRef.value?.isLoading);
const closeForm = async () => {
if (closeButton.value) closeButton.value.click();
};
defineExpose({ defineExpose({
isLoading, isLoading,
onDataSaved,
}); });
</script> </script>

View File

@ -5,8 +5,10 @@ import { dashIfEmpty } from 'src/filters';
/* basic input */ /* basic input */
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnSelectCache from 'components/common/VnSelectCache.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnComponent from 'components/common/VnComponent.vue'; import VnComponent from 'components/common/VnComponent.vue';
const model = defineModel(undefined, { required: true }); const model = defineModel(undefined, { required: true });
@ -41,6 +43,17 @@ const $props = defineProps({
}, },
}); });
const defaultSelect = {
attrs: {
row: $props.row,
disable: !$props.isEditable,
class: 'fit',
},
forceAttrs: {
label: $props.showLabel && $props.column.label,
},
};
const defaultComponents = { const defaultComponents = {
input: { input: {
component: markRaw(VnInput), component: markRaw(VnInput),
@ -74,6 +87,15 @@ const defaultComponents = {
label: $props.showLabel && $props.column.label, label: $props.showLabel && $props.column.label,
}, },
}, },
time: {
component: markRaw(VnInputTime),
attrs: {
disable: !$props.isEditable,
},
forceAttrs: {
label: $props.showLabel && $props.column.label,
},
},
checkbox: { checkbox: {
component: markRaw(QCheckbox), component: markRaw(QCheckbox),
attrs: (prop) => { attrs: (prop) => {
@ -94,14 +116,12 @@ const defaultComponents = {
}, },
}, },
select: { select: {
component: markRaw(VnSelectCache),
...defaultSelect,
},
rawSelect: {
component: markRaw(VnSelect), component: markRaw(VnSelect),
attrs: { ...defaultSelect,
disable: !$props.isEditable,
class: 'fit',
},
forceAttrs: {
label: $props.showLabel && $props.column.label,
},
}, },
icon: { icon: {
component: markRaw(QIcon), component: markRaw(QIcon),

View File

@ -5,7 +5,7 @@ const model = defineModel({ type: Object, required: true });
const $props = defineProps({ const $props = defineProps({
name: { name: {
type: String, type: String,
required: true, default: '',
}, },
label: { label: {
type: String, type: String,
@ -28,6 +28,7 @@ const hover = ref();
const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl }); const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
async function orderBy(name, direction) { async function orderBy(name, direction) {
if (!name) return;
switch (direction) { switch (direction) {
case 'DESC': case 'DESC':
direction = undefined; direction = undefined;
@ -40,7 +41,7 @@ async function orderBy(name, direction) {
break; break;
} }
if (!direction) return await arrayData.deleteOrder(name); if (!direction) return await arrayData.deleteOrder(name);
await arrayData.addOrder($props.name, direction); await arrayData.addOrder(name, direction);
} }
defineExpose({ orderBy }); defineExpose({ orderBy });
@ -52,9 +53,9 @@ defineExpose({ orderBy });
@click="orderBy(name, model?.direction)" @click="orderBy(name, model?.direction)"
class="row items-center no-wrap cursor-pointer" class="row items-center no-wrap cursor-pointer"
> >
<span>{{ label }}</span> <span :title="label">{{ label }}</span>
<QChip <QChip
v-if="name != 'tableStatus'" v-if="name"
:label="!vertical && model?.index" :label="!vertical && model?.index"
:icon=" :icon="
(model?.index || hover) && !vertical (model?.index || hover) && !vertical
@ -68,8 +69,9 @@ defineExpose({ orderBy });
model?.index ? 'color-vn-text' : 'bg-transparent', model?.index ? 'color-vn-text' : 'bg-transparent',
vertical ? 'q-px-none' : '', vertical ? 'q-px-none' : '',
]" ]"
class="no-box-shadow q-mr-lg" class="no-box-shadow"
:clickable="true" :clickable="true"
style="min-width: 40px"
> >
<div <div
class="column flex-center" class="column flex-center"

View File

@ -3,17 +3,17 @@ import { ref, onMounted, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import FormModelPopup from 'components/FormModelPopup.vue';
import CrudModel from 'src/components/CrudModel.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnLv from 'components/ui/VnLv.vue';
import CrudModel from 'src/components/CrudModel.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnTableColumn from 'components/VnTable/VnColumn.vue'; import VnTableColumn from 'components/VnTable/VnColumn.vue';
import VnTableFilter from 'components/VnTable/VnFilter.vue'; import VnTableFilter from 'components/VnTable/VnFilter.vue';
import VnTableChip from 'components/VnTable/VnChip.vue'; import VnTableChip from 'components/VnTable/VnChip.vue';
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue'; import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
import VnLv from 'components/ui/VnLv.vue';
import VnTableOrder from 'src/components/VnTable/VnOrder.vue'; import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
const $props = defineProps({ const $props = defineProps({
@ -23,7 +23,7 @@ const $props = defineProps({
}, },
defaultMode: { defaultMode: {
type: String, type: String,
default: 'card', // 'table', 'card' default: 'table', // 'table', 'card'
}, },
columnSearch: { columnSearch: {
type: Boolean, type: Boolean,
@ -34,7 +34,7 @@ const $props = defineProps({
default: true, default: true,
}, },
rowClick: { rowClick: {
type: Function, type: [Function, Boolean],
default: null, default: null,
}, },
redirect: { redirect: {
@ -81,6 +81,10 @@ const $props = defineProps({
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
tableHeight: {
type: String,
default: '90vh',
},
}); });
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -98,6 +102,7 @@ const orders = ref(parseOrder(routeQuery.filter?.order));
const CrudModelRef = ref({}); const CrudModelRef = ref({});
const showForm = ref(false); const showForm = ref(false);
const splittedColumns = ref({ columns: [] }); const splittedColumns = ref({ columns: [] });
const columnsVisibilitySkiped = ref();
const tableModes = [ const tableModes = [
{ {
icon: 'view_column', icon: 'view_column',
@ -117,6 +122,12 @@ onMounted(() => {
mode.value = quasar.platform.is.mobile ? DEFAULT_MODE : $props.defaultMode; mode.value = quasar.platform.is.mobile ? DEFAULT_MODE : $props.defaultMode;
stateStore.rightDrawer = true; stateStore.rightDrawer = true;
setUserParams(route.query[$props.searchUrl]); setUserParams(route.query[$props.searchUrl]);
columnsVisibilitySkiped.value = [
...splittedColumns.value.columns
.filter((c) => c.visible == false)
.map((c) => c.name),
...['tableActions'],
];
}); });
watch( watch(
@ -130,12 +141,6 @@ watch(
(val) => setUserParams(val) (val) => setUserParams(val)
); );
const rowClickFunction = computed(() => {
if ($props.rowClick) return $props.rowClick;
if ($props.redirect) return ({ id }) => redirectFn(id);
return () => {};
});
const isTableMode = computed(() => mode.value == TABLE_MODE); const isTableMode = computed(() => mode.value == TABLE_MODE);
function setUserParams(watchedParams) { function setUserParams(watchedParams) {
@ -182,10 +187,17 @@ function splitColumns(columns) {
label: t('status'), label: t('status'),
name: 'tableStatus', name: 'tableStatus',
columnFilter: false, columnFilter: false,
orderBy: false,
}); });
} }
} }
const rowClickFunction = computed(() => {
if ($props.rowClick != undefined) return $props.rowClick;
if ($props.redirect) return ({ id }) => redirectFn(id);
return () => {};
});
function redirectFn(id) { function redirectFn(id) {
router.push({ path: `/${$props.redirect}/${id}` }); router.push({ path: `/${$props.redirect}/${id}` });
} }
@ -241,7 +253,6 @@ defineExpose({
:data-key="$attrs['data-key']" :data-key="$attrs['data-key']"
:search-button="true" :search-button="true"
v-model="params" v-model="params"
:disable-submit-event="true"
:search-url="searchUrl" :search-url="searchUrl"
:redirect="!!redirect" :redirect="!!redirect"
> >
@ -259,7 +270,7 @@ defineExpose({
/> />
<VnTableOrder <VnTableOrder
v-model="orders[col.name]" v-model="orders[col.name]"
:name="col.name" :name="col.orderBy ?? col.name"
:data-key="$attrs['data-key']" :data-key="$attrs['data-key']"
:search-url="searchUrl" :search-url="searchUrl"
:vertical="true" :vertical="true"
@ -303,14 +314,14 @@ defineExpose({
table-header-class="bg-header" table-header-class="bg-header"
card-container-class="grid-three" card-container-class="grid-three"
flat flat
:style="isTableMode && 'max-height: 90vh'" :style="isTableMode && `max-height: ${tableHeight}`"
virtual-scroll virtual-scroll
@virtual-scroll=" @virtual-scroll="
(event) => (event) =>
event.index > rows.length - 2 && event.index > rows.length - 2 &&
CrudModelRef.vnPaginateRef.paginate() CrudModelRef.vnPaginateRef.paginate()
" "
@row-click="(_, row) => rowClickFunction(row)" @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
@update:selected="emit('update:selected', $event)" @update:selected="emit('update:selected', $event)"
> >
<template #top-left v-if="!$props.withoutHeader"> <template #top-left v-if="!$props.withoutHeader">
@ -321,6 +332,7 @@ defineExpose({
v-if="isTableMode" v-if="isTableMode"
v-model="splittedColumns.columns" v-model="splittedColumns.columns"
:table-code="tableCode ?? route.name" :table-code="tableCode ?? route.name"
:skip="columnsVisibilitySkiped"
/> />
<QBtnToggle <QBtnToggle
v-model="mode" v-model="mode"
@ -339,11 +351,7 @@ defineExpose({
/> />
</template> </template>
<template #header-cell="{ col }"> <template #header-cell="{ col }">
<QTh <QTh v-if="col.visible ?? true" auto-width>
v-if="col.visible ?? true"
auto-width
style="min-width: 100px"
>
<div <div
class="column self-start q-ml-xs ellipsis" class="column self-start q-ml-xs ellipsis"
:class="`text-${col?.align ?? 'left'}`" :class="`text-${col?.align ?? 'left'}`"
@ -355,7 +363,7 @@ defineExpose({
> >
<VnTableOrder <VnTableOrder
v-model="orders[col.name]" v-model="orders[col.name]"
:name="col.name" :name="col.orderBy ?? col.name"
:label="col?.label" :label="col?.label"
:data-key="$attrs['data-key']" :data-key="$attrs['data-key']"
:search-url="searchUrl" :search-url="searchUrl"
@ -392,7 +400,7 @@ defineExpose({
<QTd <QTd
auto-width auto-width
class="no-margin q-px-xs" class="no-margin q-px-xs"
:class="getColAlign(col)" :class="[getColAlign(col), col.class, col.columnField?.class]"
v-if="col.visible ?? true" v-if="col.visible ?? true"
> >
<slot :name="`column-${col.name}`" :col="col" :row="row"> <slot :name="`column-${col.name}`" :col="col" :row="row">
@ -581,8 +589,12 @@ defineExpose({
<i18n> <i18n>
en: en:
status: Status status: Status
table view: Table view
grid view: Grid view
es: es:
status: Estados status: Estados
table view: Vista en tabla
grid view: Vista en cuadrícula
</i18n> </i18n>
<style lang="scss"> <style lang="scss">
@ -592,7 +604,7 @@ es:
} }
.bg-header { .bg-header {
background-color: #5d5d5d; background-color: var(--vn-header-color);
color: var(--vn-text-color); color: var(--vn-text-color);
} }
@ -605,7 +617,7 @@ es:
.q-table--dark tr, .q-table--dark tr,
.q-table--dark th, .q-table--dark th,
.q-table--dark td { .q-table--dark td {
border-color: #222222; border-color: var(--vn-section-color);
} }
.q-table__container > div:first-child { .q-table__container > div:first-child {
@ -675,7 +687,7 @@ es:
right: 0; right: 0;
} }
td.sticky { td.sticky {
background-color: var(--q-dark); background-color: var(--vn-section-color);
z-index: 1; z-index: 1;
} }
} }

View File

@ -12,6 +12,10 @@ const $props = defineProps({
type: String, type: String,
default: '', default: '',
}, },
skip: {
type: Array,
default: () => [],
},
}); });
const { notify } = useNotify(); const { notify } = useNotify();
@ -30,8 +34,12 @@ function setUserConfigViewData(data, isLocal) {
if (!data) return; if (!data) return;
// Importante: El name de las columnas de la tabla debe conincidir con el name de las variables que devuelve la view config // Importante: El name de las columnas de la tabla debe conincidir con el name de las variables que devuelve la view config
if (!isLocal) localColumns.value = []; if (!isLocal) localColumns.value = [];
// Array to Object
const skippeds = $props.skip.reduce((a, v) => ({ ...a, [v]: v }), {});
for (let column of columns.value) { for (let column of columns.value) {
const { label, name } = column; const { label, name } = column;
if (skippeds[name]) continue;
column.visible = data[name] ?? true; column.visible = data[name] ?? true;
if (!isLocal) localColumns.value.push({ name, label, visible: column.visible }); if (!isLocal) localColumns.value.push({ name, label, visible: column.visible });
} }

View File

@ -18,7 +18,7 @@ watchEffect(() => {
(matched) => Object.keys(matched.meta).length (matched) => Object.keys(matched.meta).length
); );
breadcrumbs.value.length = 0; breadcrumbs.value.length = 0;
if (!matched.value[0]) return;
if (matched.value[0].name != 'Dashboard') { if (matched.value[0].name != 'Dashboard') {
root.value = useCamelCase(matched.value[0].path.substring(1).toLowerCase()); root.value = useCamelCase(matched.value[0].path.substring(1).toLowerCase());

View File

@ -273,6 +273,10 @@ function shouldRenderButton(button, isExternal = false) {
if (button.name == 'download') return true; if (button.name == 'download') return true;
return button.external === isExternal; return button.external === isExternal;
} }
defineExpose({
dmsRef,
});
</script> </script>
<template> <template>
<VnPaginate <VnPaginate
@ -374,7 +378,11 @@ function shouldRenderButton(button, isExternal = false) {
/> />
</QDialog> </QDialog>
<QPageSticky position="bottom-right" :offset="[25, 25]"> <QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn fab color="primary" icon="add" @click="showFormDialog()" /> <QBtn fab color="primary" icon="add" @click="showFormDialog()" class="fill-icon">
<QTooltip>
{{ t('Upload file') }}
</QTooltip>
</QBtn>
</QPageSticky> </QPageSticky>
</template> </template>
<style scoped> <style scoped>
@ -392,4 +400,5 @@ en:
es: es:
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes} contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
Generate identifier for original file: Generar identificador para archivo original Generate identifier for original file: Generar identificador para archivo original
Upload file: Subir fichero
</i18n> </i18n>

View File

@ -123,6 +123,7 @@ watch(
> >
<QDate <QDate
v-model="popupDate" v-model="popupDate"
:landscape="true"
:today-btn="true" :today-btn="true"
:options="$attrs.options" :options="$attrs.options"
@update:model-value=" @update:model-value="

View File

@ -44,7 +44,7 @@ const formattedTime = computed({
if (time?.length > 5) time = dateToTime(time); if (time?.length > 5) time = dateToTime(time);
if (!props.timeOnly) { if (!props.timeOnly) {
const hours = time.split(':'); const hours = time.split(':');
const date = new Date(); const date = new Date(model.value);
date.setHours(hours[0], hours[1], 0); date.setHours(hours[0], hours[1], 0);
time = date.toISOString(); time = date.toISOString();
} }
@ -109,13 +109,7 @@ const focusField = () => {
self="top start" self="top start"
:no-focus="true" :no-focus="true"
> >
<QTime <QTime v-model="formattedTime" mask="HH:mm" landscape now-btn />
:format24h="false"
v-model="formattedTime"
mask="HH:mm"
landscape
now-btn
/>
</QMenu> </QMenu>
</QInput> </QInput>
</div> </div>

View File

@ -75,6 +75,7 @@ const myOptions = ref([]);
const myOptionsOriginal = ref([]); const myOptionsOriginal = ref([]);
const vnSelectRef = ref(); const vnSelectRef = ref();
const dataRef = ref(); const dataRef = ref();
const lastVal = ref();
const value = computed({ const value = computed({
get() { get() {
@ -85,14 +86,31 @@ const value = computed({
}, },
}); });
watch(options, (newValue) => {
setOptions(newValue);
});
watch(modelValue, (newValue) => {
if (!myOptions.value.some((option) => option[optionValue.value] == newValue))
fetchFilter(newValue);
});
onMounted(() => {
setOptions(options.value);
if ($props.url && $props.modelValue && !findKeyInOptions())
fetchFilter($props.modelValue);
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
});
function findKeyInOptions() {
if (!$props.options) return;
return filter($props.modelValue, $props.options)?.length;
}
function setOptions(data) { function setOptions(data) {
myOptions.value = JSON.parse(JSON.stringify(data)); myOptions.value = JSON.parse(JSON.stringify(data));
myOptionsOriginal.value = JSON.parse(JSON.stringify(data)); myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
} }
onMounted(() => {
setOptions(options.value);
if ($props.url && $props.modelValue) fetchFilter($props.modelValue);
});
function filter(val, options) { function filter(val, options) {
const search = val.toString().toLowerCase(); const search = val.toString().toLowerCase();
@ -125,15 +143,21 @@ async function fetchFilter(val) {
const defaultWhere = $props.useLike const defaultWhere = $props.useLike
? { [key]: { like: `%${val}%` } } ? { [key]: { like: `%${val}%` } }
: { [key]: val }; : { [key]: val };
const where = { ...defaultWhere, ...$props.where }; const where = { ...(val ? defaultWhere : {}), ...$props.where };
const fetchOptions = { where, order: sortBy, limit }; const fetchOptions = { where, order: sortBy, limit };
if (fields) fetchOptions.fields = fields; if (fields) fetchOptions.fields = fields;
return dataRef.value.fetch(fetchOptions); return dataRef.value.fetch(fetchOptions);
} }
async function filterHandler(val, update) { async function filterHandler(val, update) {
if (!$props.defaultFilter) return update(); if (!val && lastVal.value === val) {
lastVal.value = val;
return update();
}
lastVal.value = val;
let newOptions; let newOptions;
if (!$props.defaultFilter) return update();
if ($props.url) { if ($props.url) {
newOptions = await fetchFilter(val); newOptions = await fetchFilter(val);
} else newOptions = filter(val, myOptionsOriginal.value); } else newOptions = filter(val, myOptionsOriginal.value);
@ -149,19 +173,6 @@ async function filterHandler(val, update) {
} }
); );
} }
watch(options, (newValue) => {
setOptions(newValue);
});
watch(modelValue, (newValue) => {
if (!myOptions.value.some((option) => option[optionValue.value] == newValue))
fetchFilter(newValue);
});
onMounted(async () => {
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
});
</script> </script>
<template> <template>

View File

@ -0,0 +1,25 @@
<script setup>
import { ref, onBeforeMount, useAttrs } from 'vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const $props = defineProps({
row: {
type: [Object],
default: null,
},
find: {
type: String,
default: null,
},
});
const options = ref([]);
onBeforeMount(async () => {
const { url } = useAttrs();
const findBy = $props.find ?? url?.charAt(0)?.toLocaleLowerCase() + url?.slice(1, -1);
if (findBy) options.value = [$props.row[findBy]];
});
</script>
<template>
<VnSelect v-bind="$attrs" :options="$attrs.options ?? options" />
</template>

View File

@ -52,6 +52,10 @@ const dialog = ref(null);
:value="item?.[`value${index + 4}`]" :value="item?.[`value${index + 4}`]"
/> />
</template> </template>
<div v-if="item.minQuantity" class="min-quantity">
<QIcon name="production_quantity_limits" size="xs" />
{{ item.minQuantity }}
</div>
<div class="footer"> <div class="footer">
<div class="price"> <div class="price">
<p v-if="isCatalog"> <p v-if="isCatalog">
@ -133,6 +137,11 @@ const dialog = ref(null);
} }
} }
.min-quantity {
text-align: right;
color: $negative !important;
}
.footer { .footer {
.price { .price {
overflow: hidden; overflow: hidden;

View File

@ -18,19 +18,18 @@ const $props = defineProps({
}, },
searchButton: { searchButton: {
type: Boolean, type: Boolean,
required: false,
default: false, default: false,
}, },
showAll: { showAll: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
unremovableParams: { unRemovableParams: {
type: Array, type: Array,
required: false, required: false,
default: () => [], default: () => [],
description: description: `Some filters come with default search parameters and require a value.
'Algunos filtros vienen con parametros de búsqueda por default y necesitan tener si o si un valor, por eso de ser necesario, esta prop nos sirve para saber que filtros podemos remover y cuales no', This prop helps us determine which filters can be removed and which cannot.`,
}, },
exprBuilder: { exprBuilder: {
type: Function, type: Function,
@ -114,7 +113,6 @@ async function search(evt) {
if (evt?.param) Object.assign(userParams.value, evt.param); if (evt?.param) Object.assign(userParams.value, evt.param);
const filter = { ...userParams.value }; const filter = { ...userParams.value };
store.userParamsChanged = true; store.userParamsChanged = true;
arrayData.reset(['skip', 'filter.skip', 'page']);
const { params: newParams } = await arrayData.addFilter({ params: userParams.value }); const { params: newParams } = await arrayData.addFilter({ params: userParams.value });
userParams.value = newParams; userParams.value = newParams;
@ -141,7 +139,7 @@ async function clearFilters() {
arrayData.reset(['skip', 'filter.skip', 'page']); arrayData.reset(['skip', 'filter.skip', 'page']);
// Filtrar los params no removibles // Filtrar los params no removibles
const removableFilters = Object.keys(userParams.value).filter((param) => const removableFilters = Object.keys(userParams.value).filter((param) =>
$props.unremovableParams.includes(param) $props.unRemovableParams.includes(param)
); );
const newParams = {}; const newParams = {};
// Conservar solo los params que no son removibles // Conservar solo los params que no son removibles
@ -250,7 +248,7 @@ function formatValue(value) {
<VnFilterPanelChip <VnFilterPanelChip
v-for="chip of tags" v-for="chip of tags"
:key="chip.label" :key="chip.label"
:removable="!unremovableParams.includes(chip.label)" :removable="!unRemovableParams.includes(chip.label)"
@remove="remove(chip.label)" @remove="remove(chip.label)"
> >
<slot name="tags" :tag="chip" :format-fn="formatValue"> <slot name="tags" :tag="chip" :format-fn="formatValue">

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { onMounted, onBeforeUnmount, ref, nextTick } from 'vue'; import { onMounted, onBeforeUnmount, ref } from 'vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -30,6 +30,7 @@ onBeforeUnmount(() => stateStore.toggleSubToolbar());
<template> <template>
<QToolbar <QToolbar
id="subToolbar"
class="justify-end sticky" class="justify-end sticky"
v-show="hasContent || $slots['st-actions'] || $slots['st-data']" v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
> >

View File

@ -18,7 +18,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
onMounted(() => { onMounted(() => {
setOptions(); setOptions();
arrayDataStore.reset(['skip']); reset(['skip']);
const query = route.query; const query = route.query;
const searchUrl = store.searchUrl; const searchUrl = store.searchUrl;
@ -149,7 +149,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
store.filter = {}; store.filter = {};
if (params) store.userParams = { ...params }; if (params) store.userParams = { ...params };
const response = await fetch({ append: false }); const response = await fetch({});
return response; return response;
} }
@ -160,9 +160,9 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
userParams = sanitizerParams(userParams, store?.exprBuilder); userParams = sanitizerParams(userParams, store?.exprBuilder);
store.userParams = userParams; store.userParams = userParams;
arrayDataStore.reset(['skip', 'filter.skip', 'page']); reset(['skip', 'filter.skip', 'page']);
await fetch({ append: false }); await fetch({});
return { filter, params }; return { filter, params };
} }
@ -175,7 +175,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
async function addOrder(field, direction = 'ASC') { async function addOrder(field, direction = 'ASC') {
const newOrder = field + ' ' + direction; const newOrder = field + ' ' + direction;
let order = store.order ?? []; let order = store.order || [];
if (typeof order == 'string') order = [order]; if (typeof order == 'string') order = [order];
let index = order.findIndex((o) => o.split(' ')[0] === field); let index = order.findIndex((o) => o.split(' ')[0] === field);
@ -187,7 +187,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
} }
store.order = order; store.order = order;
fetch({ append: false, updateRouter: true }); reset(['skip', 'filter.skip', 'page']);
fetch({});
index++; index++;
return { index, order }; return { index, order };
@ -201,7 +202,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
if (index > -1) order.splice(index, 1); if (index > -1) order.splice(index, 1);
store.order = order; store.order = order;
fetch({ append: false, updateRouter: true }); fetch({});
} }
function sanitizerParams(params, exprBuilder) { function sanitizerParams(params, exprBuilder) {
@ -210,10 +211,20 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
delete store.userParams[param]; delete store.userParams[param];
delete params[param]; delete params[param];
if (store.filter?.where) { if (store.filter?.where) {
const key = Object.keys(exprBuilder ? exprBuilder(param) : param); let key;
if (key[0]) delete store.filter.where[key[0]]; if (exprBuilder) {
if (Object.keys(store.filter.where).length === 0) { const result = exprBuilder(param);
delete store.filter.where; if (result !== undefined && result !== null)
key = Object.keys(result);
} else {
if (typeof param === 'object' && param !== null)
key = Object.keys(param);
}
if (key && key[0]) {
delete store.filter.where[key[0]];
if (Object.keys(store.filter.where).length === 0) {
delete store.filter.where;
}
} }
} }
} }
@ -231,7 +242,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
} }
async function refresh() { async function refresh() {
if (Object.values(store.userParams).length) await fetch({ append: false }); if (Object.values(store.userParams).length) await fetch({});
} }
function updateStateParams() { function updateStateParams() {

View File

@ -4,9 +4,10 @@
body.body--light { body.body--light {
--font-color: black; --font-color: black;
--vn-header-color: #cecece;
--vn-page-color: #ffffff;
--vn-section-color: #e0e0e0; --vn-section-color: #e0e0e0;
--vn-section-hover-color: #b9b9b9; --vn-section-hover-color: #b9b9b9;
--vn-page-color: #ffffff;
--vn-text-color: var(--font-color); --vn-text-color: var(--font-color);
--vn-label-color: #5f5f5f; --vn-label-color: #5f5f5f;
--vn-accent-color: #e7e3e3; --vn-accent-color: #e7e3e3;
@ -18,6 +19,7 @@ body.body--light {
} }
} }
body.body--dark { body.body--dark {
--vn-header-color: #5d5d5d;
--vn-page-color: #222; --vn-page-color: #222;
--vn-section-color: #3d3d3d; --vn-section-color: #3d3d3d;
--vn-section-hover-color: #747474; --vn-section-hover-color: #747474;
@ -229,3 +231,29 @@ input::-webkit-inner-spin-button {
*::-webkit-scrollbar-track { *::-webkit-scrollbar-track {
background: transparent; background: transparent;
} }
.q-table {
thead,
tbody {
th {
.q-select {
max-width: 120px;
}
}
td {
padding: 1px 10px 1px 10px;
max-width: 100px;
div span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.expand {
max-width: 400px;
}
}
}

View File

@ -46,6 +46,7 @@ globals:
noPinnedModules: You don't have any pinned modules noPinnedModules: You don't have any pinned modules
summary: summary:
basicData: Basic data basicData: Basic data
daysOnward: Days onward
today: Today today: Today
yesterday: Yesterday yesterday: Yesterday
dateFormat: en-GB dateFormat: en-GB
@ -196,6 +197,12 @@ globals:
autonomous: Autonomous autonomous: Autonomous
suppliers: Suppliers suppliers: Suppliers
supplier: Supplier supplier: Supplier
expedition: Expedition
services: Service
components: Components
pictures: Pictures
packages: Packages
tracking: Tracking
labeler: Labeler labeler: Labeler
supplierCreate: New supplier supplierCreate: New supplier
accounts: Accounts accounts: Accounts
@ -232,6 +239,7 @@ globals:
formation: Formation formation: Formation
locations: Locations locations: Locations
warehouses: Warehouses warehouses: Warehouses
saleTracking: Sale tracking
roles: Roles roles: Roles
connections: Connections connections: Connections
acls: ACLs acls: ACLs
@ -546,18 +554,13 @@ ticket:
sms: Sms sms: Sms
notes: Notes notes: Notes
sale: Sale sale: Sale
dms: File management
volume: Volume volume: Volume
observation: Notes observation: Notes
ticketAdvance: Advance tickets ticketAdvance: Advance tickets
futureTickets: Future tickets futureTickets: Future tickets
expedition: Expedition
purchaseRequest: Purchase request purchaseRequest: Purchase request
weeklyTickets: Weekly tickets weeklyTickets: Weekly tickets
services: Service
tracking: Tracking
components: Components
pictures: Pictures
packages: Packages
list: list:
nickname: Nickname nickname: Nickname
state: State state: State

View File

@ -45,6 +45,7 @@ globals:
noPinnedModules: No has fijado ningún módulo noPinnedModules: No has fijado ningún módulo
summary: summary:
basicData: Datos básicos basicData: Datos básicos
daysOnward: Días adelante
today: Hoy today: Hoy
yesterday: Ayer yesterday: Ayer
dateFormat: es-ES dateFormat: es-ES
@ -241,6 +242,7 @@ globals:
privileges: Privilegios privileges: Privilegios
observation: Notas observation: Notas
expedition: Expedición expedition: Expedición
saleTracking: Líneas preparadas
services: Servicios services: Servicios
tracking: Estados tracking: Estados
components: Componentes components: Componentes
@ -551,6 +553,7 @@ ticket:
sms: Sms sms: Sms
notes: Notas notes: Notas
sale: Lineas del pedido sale: Lineas del pedido
dms: Gestión documental
volume: Volumen volume: Volumen
observation: Notas observation: Notas
ticketAdvance: Adelantar tickets ticketAdvance: Adelantar tickets
@ -558,6 +561,7 @@ ticket:
expedition: Expedición expedition: Expedición
purchaseRequest: Petición de compra purchaseRequest: Petición de compra
weeklyTickets: Tickets programados weeklyTickets: Tickets programados
saleTracking: Líneas preparadas
services: Servicios services: Servicios
tracking: Estados tracking: Estados
components: Componentes components: Componentes

View File

@ -211,7 +211,7 @@ async function changeState(value) {
> >
<QList> <QList>
<QVirtualScroll <QVirtualScroll
style="max-height: 300px" class="max-container-height"
:items="ClaimStates" :items="ClaimStates"
separator separator
v-slot="{ item, index }" v-slot="{ item, index }"
@ -279,13 +279,13 @@ async function changeState(value) {
<ClaimNotes <ClaimNotes
:id="entityId" :id="entityId"
:add-note="false" :add-note="false"
style="max-height: 300px" class="max-container-height"
order="created ASC" order="created ASC"
/> />
</QCard> </QCard>
<QCard class="vn-two" v-if="claimDms.length > 0"> <QCard class="vn-two" v-if="claimDms?.length">
<VnTitle :url="`#/claim/${entityId}/photos`" :text="t('claim.photos')" /> <VnTitle :url="`#/claim/${entityId}/photos`" :text="t('claim.photos')" />
<div class="container"> <div class="container max-container-height" style="overflow: auto">
<div <div
class="multimedia-container" class="multimedia-container"
v-for="(media, index) of claimDms" v-for="(media, index) of claimDms"
@ -302,7 +302,10 @@ async function changeState(value) {
> >
<QTooltip>Video</QTooltip> <QTooltip>Video</QTooltip>
</QIcon> </QIcon>
<QCard class="multimedia relative-position"> <QCard
class="multimedia relative-position"
style="max-height: 128px"
>
<QImg <QImg
:src="media.url" :src="media.url"
class="rounded-borders cursor-pointer fit" class="rounded-borders cursor-pointer fit"
@ -322,7 +325,7 @@ async function changeState(value) {
</div> </div>
</div> </div>
</QCard> </QCard>
<QCard class="vn-two" v-if="salesClaimed.length > 0"> <QCard class="vn-max" v-if="salesClaimed.length > 0">
<VnTitle :url="`#/claim/${entityId}/lines`" :text="t('claim.details')" /> <VnTitle :url="`#/claim/${entityId}/lines`" :text="t('claim.details')" />
<QTable <QTable
:columns="detailsColumns" :columns="detailsColumns"
@ -361,7 +364,7 @@ async function changeState(value) {
</template> </template>
</QTable> </QTable>
</QCard> </QCard>
<QCard class="vn-two" v-if="developments.length > 0"> <QCard class="vn-max" v-if="developments.length > 0">
<VnTitle :url="claimUrl + 'development'" :text="t('claim.development')" /> <VnTitle :url="claimUrl + 'development'" :text="t('claim.development')" />
<QTable <QTable
:columns="developmentColumns" :columns="developmentColumns"
@ -458,7 +461,7 @@ async function changeState(value) {
gap: 15px; gap: 15px;
} }
.multimedia-container { .multimedia-container {
flex: 1 0 21%; flex: 0 0 128px;
} }
.multimedia { .multimedia {
transition: all 0.5s; transition: all 0.5s;
@ -491,4 +494,8 @@ async function changeState(value) {
.change-state { .change-state {
width: 10%; width: 10%;
} }
.max-container-height {
max-height: 300px;
}
</style> </style>

View File

@ -71,7 +71,6 @@ const columns = [
<VnTable <VnTable
data-key="ClaimEndsTable" data-key="ClaimEndsTable"
url="ClaimEnds/filter" url="ClaimEnds/filter"
default-mode="table"
:right-search="false" :right-search="false"
:column-search="false" :column-search="false"
:disable-option="{ card: true, table: true }" :disable-option="{ card: true, table: true }"

View File

@ -17,6 +17,8 @@ const props = defineProps({
}); });
const states = ref([]); const states = ref([]);
defineExpose({ states });
</script> </script>
<template> <template>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed } from 'vue'; import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { toDate } from 'filters/index'; import { toDate } from 'filters/index';
import VnSearchbar from 'components/ui/VnSearchbar.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue';
@ -14,6 +14,7 @@ import VnTable from 'src/components/VnTable/VnTable.vue';
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const claimFilterRef = ref();
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left', align: 'left',
@ -29,18 +30,39 @@ const columns = computed(() => [
label: t('customer.extendedList.tableVisibleColumns.name'), label: t('customer.extendedList.tableVisibleColumns.name'),
name: 'clientName', name: 'clientName',
isTitle: true, isTitle: true,
visible: false,
}, },
{ {
align: 'left', align: 'left',
label: t('claim.customer'), label: t('claim.customer'),
name: 'clientFk', name: 'clientFk',
cardVisible: true, cardVisible: true,
columnFilter: {
component: 'select',
attrs: {
url: 'Clients',
fields: ['id', 'name'],
},
},
class: 'expand',
}, },
{ {
align: 'left', align: 'left',
label: t('claim.attendedBy'), label: t('claim.attendedBy'),
name: 'attendedBy', name: 'attendedBy',
cardVisible: true, cardVisible: true,
columnFilter: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
useLike: false,
optionValue: 'id',
optionLabel: 'name',
optionFilter: 'firstName',
},
},
}, },
{ {
align: 'left', align: 'left',
@ -48,6 +70,9 @@ const columns = computed(() => [
name: 'created', name: 'created',
format: ({ created }) => toDate(created), format: ({ created }) => toDate(created),
cardVisible: true, cardVisible: true,
columnFilter: {
component: 'date',
},
}, },
{ {
align: 'left', align: 'left',
@ -57,6 +82,14 @@ const columns = computed(() => [
condition: () => true, condition: () => true,
color: ({ stateCode }) => STATE_COLOR[stateCode] ?? 'bg-grey', color: ({ stateCode }) => STATE_COLOR[stateCode] ?? 'bg-grey',
}, },
columnFilter: {
name: 'claimStateFk',
component: 'select',
attrs: {
options: claimFilterRef.value?.states,
optionLabel: 'description',
},
},
}, },
{ {
align: 'right', align: 'right',
@ -86,7 +119,7 @@ const STATE_COLOR = {
/> />
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
<ClaimFilter data-key="ClaimList" /> <ClaimFilter data-key="ClaimList" ref="claimFilterRef" />
</template> </template>
</RightMenu> </RightMenu>
<VnTable <VnTable
@ -116,4 +149,9 @@ const STATE_COLOR = {
es: es:
Search claim: Buscar reclamación Search claim: Buscar reclamación
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
params:
stateCode: Estado
en:
params:
stateCode: State
</i18n> </i18n>

View File

@ -42,6 +42,9 @@ const columns = computed(() => [
name: 'name', name: 'name',
isTitle: true, isTitle: true,
create: true, create: true,
columnField: {
class: 'expand',
},
}, },
{ {
align: 'left', align: 'left',
@ -49,6 +52,9 @@ const columns = computed(() => [
label: t('customer.extendedList.tableVisibleColumns.socialName'), label: t('customer.extendedList.tableVisibleColumns.socialName'),
isTitle: true, isTitle: true,
create: true, create: true,
columnField: {
class: 'expand',
},
}, },
{ {
align: 'left', align: 'left',
@ -78,8 +84,8 @@ const columns = computed(() => [
align: 'left', align: 'left',
label: t('customer.extendedList.tableVisibleColumns.credit'), label: t('customer.extendedList.tableVisibleColumns.credit'),
name: 'credit', name: 'credit',
component: 'number',
columnFilter: { columnFilter: {
component: 'number',
inWhere: true, inWhere: true,
}, },
}, },
@ -87,8 +93,8 @@ const columns = computed(() => [
align: 'left', align: 'left',
label: t('customer.extendedList.tableVisibleColumns.creditInsurance'), label: t('customer.extendedList.tableVisibleColumns.creditInsurance'),
name: 'creditInsurance', name: 'creditInsurance',
component: 'number',
columnFilter: { columnFilter: {
component: 'number',
inWhere: true, inWhere: true,
}, },
}, },
@ -130,6 +136,9 @@ const columns = computed(() => [
columnFilter: { columnFilter: {
inWhere: true, inWhere: true,
}, },
columnField: {
class: 'expand',
},
}, },
{ {
align: 'left', align: 'left',
@ -179,8 +188,8 @@ const columns = computed(() => [
label: t('customer.extendedList.tableVisibleColumns.created'), label: t('customer.extendedList.tableVisibleColumns.created'),
name: 'created', name: 'created',
format: ({ created }) => toDate(created), format: ({ created }) => toDate(created),
component: 'date',
columnFilter: { columnFilter: {
component: 'date',
alias: 'c', alias: 'c',
inWhere: true, inWhere: true,
}, },
@ -405,7 +414,6 @@ function handleLocation(data, location) {
}" }"
order="id DESC" order="id DESC"
:columns="columns" :columns="columns"
default-mode="table"
redirect="customer" redirect="customer"
auto-load auto-load
> >

View File

@ -183,15 +183,10 @@ const fetchEntryBuys = async () => {
{{ t('globals.summary.basicData') }} {{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" /> <QIcon name="open_in_new" />
</router-link> </router-link>
<VnLv :label="t('entry.summary.commission')" :value="entry.commission" /> <VnLv :label="t('entry.summary.commission')" :value="entry.commission" />
<VnLv :label="t('entry.summary.currency')" :value="entry.currency.name" /> <VnLv :label="t('entry.summary.currency')" :value="entry.currency.name" />
<VnLv :label="t('entry.summary.company')" :value="entry.company.code" /> <VnLv :label="t('entry.summary.company')" :value="entry.company.code" />
<VnLv :label="t('entry.summary.reference')" :value="entry.reference" /> <VnLv :label="t('entry.summary.reference')" :value="entry.reference" />
<VnLv <VnLv
:label="t('entry.summary.invoiceNumber')" :label="t('entry.summary.invoiceNumber')"
:value="entry.invoiceNumber" :value="entry.invoiceNumber"
@ -205,7 +200,6 @@ const fetchEntryBuys = async () => {
{{ t('globals.summary.basicData') }} {{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" /> <QIcon name="open_in_new" />
</router-link> </router-link>
<VnLv :label="t('entry.summary.travelReference')"> <VnLv :label="t('entry.summary.travelReference')">
<template #value> <template #value>
<span class="link"> <span class="link">
@ -214,31 +208,25 @@ const fetchEntryBuys = async () => {
</span> </span>
</template> </template>
</VnLv> </VnLv>
<VnLv <VnLv
:label="t('entry.summary.travelAgency')" :label="t('entry.summary.travelAgency')"
:value="entry.travel.agency.name" :value="entry.travel.agency.name"
/> />
<VnLv :label="t('shipped')" :value="toDate(entry.travel.shipped)" /> <VnLv :label="t('shipped')" :value="toDate(entry.travel.shipped)" />
<VnLv <VnLv
:label="t('entry.summary.travelWarehouseOut')" :label="t('entry.summary.travelWarehouseOut')"
:value="entry.travel.warehouseOut.name" :value="entry.travel.warehouseOut.name"
/> />
<QCheckbox <QCheckbox
:label="t('entry.summary.travelDelivered')" :label="t('entry.summary.travelDelivered')"
v-model="entry.travel.isDelivered" v-model="entry.travel.isDelivered"
:disable="true" :disable="true"
/> />
<VnLv :label="t('landed')" :value="toDate(entry.travel.landed)" /> <VnLv :label="t('landed')" :value="toDate(entry.travel.landed)" />
<VnLv <VnLv
:label="t('entry.summary.travelWarehouseIn')" :label="t('entry.summary.travelWarehouseIn')"
:value="entry.travel.warehouseIn.name" :value="entry.travel.warehouseIn.name"
/> />
<QCheckbox <QCheckbox
:label="t('entry.summary.travelReceived')" :label="t('entry.summary.travelReceived')"
v-model="entry.travel.isReceived" v-model="entry.travel.isReceived"

View File

@ -63,7 +63,7 @@ const redirectToEntryBasicData = (_, { id }) => {
<VnSearchbar <VnSearchbar
url="Entries/filter" url="Entries/filter"
custom-route-redirect-name="EntrySummary" custom-route-redirect-name="EntrySummary"
data-key="EntrySummary" data-key="Entry"
:label="t('Search entries')" :label="t('Search entries')"
:info="t('You can search by entry reference')" :info="t('You can search by entry reference')"
/> />

View File

@ -191,7 +191,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
order="id DESC" order="id DESC"
:columns="columns" :columns="columns"
redirect="entry" redirect="entry"
default-mode="table"
auto-load auto-load
:right-search="false" :right-search="false"
/> />

View File

@ -199,7 +199,6 @@ onMounted(async () => {
order="id DESC" order="id DESC"
:columns="columns" :columns="columns"
redirect="entry" redirect="entry"
default-mode="table"
auto-load auto-load
:right-search="false" :right-search="false"
/> />

View File

@ -7,6 +7,7 @@ import { toDate } from 'src/filters/index';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import EntryBuysTableDialog from './EntryBuysTableDialog.vue'; import EntryBuysTableDialog from './EntryBuysTableDialog.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import VnInput from 'src/components/common/VnInput.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
const { t } = useI18n(); const { t } = useI18n();
@ -34,6 +35,10 @@ const columns = computed(() => [
isTitle: false, isTitle: false,
create: true, create: true,
cardVisible: true, cardVisible: true,
component: 'date',
columnField: {
component: null,
},
format: ({ shipped }) => toDate(shipped), format: ({ shipped }) => toDate(shipped),
}, },
{ {
@ -43,6 +48,10 @@ const columns = computed(() => [
isTitle: false, isTitle: false,
create: true, create: true,
cardVisible: false, cardVisible: false,
component: 'date',
columnField: {
component: null,
},
format: ({ landed }) => toDate(landed), format: ({ landed }) => toDate(landed),
}, },
{ {
@ -93,14 +102,23 @@ const printBuys = (rowId) => {
:columns="columns" :columns="columns"
default-mode="card" default-mode="card"
auto-load auto-load
:right-search="true"
> >
<template #moreFilterPanel="{ params }">
<VnInput
:label="t('globals.daysOnward')"
v-model="params.days"
class="q-px-xs row"
dense
filled
outlined
></VnInput>
</template>
</VnTable> </VnTable>
</div> </div>
</QPage> </QPage>
</template> </template>
<i18n> <i18n>
es:
Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada You can search by entry reference: Puedes buscar por referencia de la entrada
</i18n> </i18n>

View File

@ -207,7 +207,6 @@ watchEffect(selectedRows);
v-model:selected="selectedRows" v-model:selected="selectedRows"
order="id DESC" order="id DESC"
:columns="columns" :columns="columns"
default-mode="table"
redirect="invoice-out" redirect="invoice-out"
auto-load auto-load
:table="{ :table="{

View File

@ -164,7 +164,6 @@ const downloadCSV = async () => {
" "
:limit="0" :limit="0"
:columns="columns" :columns="columns"
default-mode="table"
auto-load auto-load
:is-editable="false" :is-editable="false"
:use-model="true" :use-model="true"

View File

@ -5,7 +5,9 @@ import { useRoute } from 'vue-router';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue'; import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
import OrderDescriptorProxy from 'src/pages/Order/Card/OrderDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
@ -45,8 +47,8 @@ const columns = computed(() => [
align: 'left', align: 'left',
}, },
{ {
label: t('itemDiary.id'), label: t('itemDiary.origin'),
name: 'id', name: 'originId',
align: 'left', align: 'left',
}, },
{ {
@ -65,8 +67,8 @@ const columns = computed(() => [
}, },
{ {
label: t('itemDiary.client'), label: t('itemDiary.entity'),
name: 'client', name: 'entityId',
align: 'left', align: 'left',
format: (val) => dashIfEmpty(val), format: (val) => dashIfEmpty(val),
}, },
@ -111,10 +113,28 @@ const getBadgeAttrs = (_date) => {
return attrs; return attrs;
}; };
const getIdDescriptor = (row) => { const originTypeMap = {
let descriptor = EntryDescriptorProxy; entry: {
if (row.isTicket) descriptor = TicketDescriptorProxy; descriptor: EntryDescriptorProxy,
return descriptor; icon: 'vn:entry',
},
ticket: {
descriptor: TicketDescriptorProxy,
icon: 'vn:ticket',
},
order: {
descriptor: OrderDescriptorProxy,
icon: 'vn:basket',
},
};
const entityTypeMap = {
client: {
descriptor: CustomerDescriptorProxy,
},
supplier: {
descriptor: SupplierDescriptorProxy,
},
}; };
onMounted(async () => { onMounted(async () => {
@ -206,21 +226,28 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</QBadge> </QBadge>
</QTd> </QTd>
</template> </template>
<template #body-cell-id="{ row }"> <template #body-cell-originId="{ row }">
<QTd @click.stop> <QTd @click.stop>
<component <component
:is="getIdDescriptor(row)" :is="originTypeMap[row.originType]?.descriptor"
:id="row.origin" :id="row.originId"
class="q-ma-none" class="q-ma-none"
dense dense
style="font-size: 14px" style="font-size: 14px"
> >
{{ row.origin }} {{ row.originId }}
</component> </component>
<span class="link">{{ row.origin }}</span> <span class="link">
<QIcon
:name="originTypeMap[row.originType]?.icon"
class="fill-icon q-mr-sm"
size="xs"
/>
{{ row.originId }}
</span>
</QTd> </QTd>
</template> </template>
<template #body-cell-client="{ row }"> <template #body-cell-entityId="{ row }">
<QTd @click.stop> <QTd @click.stop>
<QBadge <QBadge
:color="row.highlighted ? 'warning' : 'transparent'" :color="row.highlighted ? 'warning' : 'transparent'"
@ -228,11 +255,18 @@ onUnmounted(() => (stateStore.rightDrawer = false));
dense dense
style="font-size: 14px" style="font-size: 14px"
> >
<span v-if="row.isTicket" class="link"> <component
{{ dashIfEmpty(row.name) }} :is="entityTypeMap[row.entityType]?.descriptor"
<CustomerDescriptorProxy :id="row.clientFk" /> :id="row.entityId"
class="q-ma-none"
dense
style="font-size: 14px"
>
{{ row.entityId }}
</component>
<span class="link">
{{ dashIfEmpty(row.entityName) }}
</span> </span>
<span v-else>{{ dashIfEmpty(row.name) }}</span>
</QBadge> </QBadge>
</QTd> </QTd>
</template> </template>

View File

@ -135,10 +135,6 @@ const isAdministrative = computed(() => {
:label="t('item.summary.nonRecycledPlastic')" :label="t('item.summary.nonRecycledPlastic')"
:value="item.nonRecycledPlastic" :value="item.nonRecycledPlastic"
/> />
<VnLv
:label="t('item.summary.minSalesQuantity')"
:value="item.minQuantity"
/>
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<component <component

View File

@ -209,7 +209,7 @@ const decrement = (paramsObj, key) => {
<QItemSection> <QItemSection>
<VnInput <VnInput
v-model="params.scopeDays" v-model="params.scopeDays"
:label="t('params.scopeDays')" :label="t('globals.daysOnward')"
type="number" type="number"
dense dense
outlined outlined
@ -281,7 +281,6 @@ en:
requesterFk: Salesperson requesterFk: Salesperson
from: From from: From
to: To to: To
scopeDays: Days onward
mine: For me mine: For me
state: State state: State
dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time
@ -299,7 +298,6 @@ es:
requesterFk: Comercial requesterFk: Comercial
from: Desde from: Desde
to: Hasta to: Hasta
scopeDays: Días adelante
mine: Para mi mine: Para mi
state: Estado state: Estado
dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez

View File

@ -14,10 +14,10 @@ shelvings:
removeConfirmSubtitle: Are you sure you want to continue? removeConfirmSubtitle: Are you sure you want to continue?
itemDiary: itemDiary:
date: Date date: Date
id: Id origin: Origin
state: State state: State
reference: Reference reference: Reference
client: Client entity: Entity
in: In in: In
out: Out out: Out
balance: Balance balance: Balance

View File

@ -14,10 +14,10 @@ shelvings:
removeConfirmSubtitle: ¿Seguro que quieres continuar? removeConfirmSubtitle: ¿Seguro que quieres continuar?
itemDiary: itemDiary:
date: Fecha date: Fecha
id: Id origin: Origen
state: Estado state: Estado
reference: Referencia reference: Referencia
client: Cliente entity: Entidad
in: Entrada in: Entrada
out: Salida out: Salida
balance: Balance balance: Balance

View File

@ -0,0 +1,17 @@
<script setup>
import OrderDescriptor from './OrderDescriptor.vue';
import OrderSummary from './OrderSummary.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
},
});
</script>
<template>
<QPopupProxy>
<OrderDescriptor v-if="$props.id" :id="$props.id" :summary="OrderSummary" />
</QPopupProxy>
</template>

View File

@ -249,7 +249,6 @@ async function confirmOrder() {
data-key="OrderLines" data-key="OrderLines"
url="OrderRows" url="OrderRows"
:columns="columns" :columns="columns"
default-mode="table"
:right-search="false" :right-search="false"
:use-model="true" :use-model="true"
auto-load auto-load

View File

@ -154,7 +154,6 @@ async function fetchClientAddress(id, data) {
}, },
}" }"
:columns="columns" :columns="columns"
default-mode="table"
redirect="order" redirect="order"
auto-load auto-load
> >

View File

@ -78,10 +78,12 @@ const columns = computed(() => [
:columns="columns" :columns="columns"
:right-search="false" :right-search="false"
:use-model="true" :use-model="true"
redirect="agency"
default-mode="card"
/> />
</template> </template>
<i18n> <i18n>
es: es:
isOwn: Tiene propietario isOwn: Tiene propietario
isAnyVolumeAllowed: Permite cualquier volumen isAnyVolumeAllowed: Permite cualquier volumen
Search agency: Buscar agencia Search agency: Buscar agencia

View File

@ -1,162 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const countries = ref();
const warehouses = ref();
</script>
<template>
<FetchData url="Countries" @on-fetch="(data) => (countries = data)" auto-load />
<FetchData url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params }">
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.cmrFk')"
v-model="params.cmrFk"
is-outlined
>
<template #prepend>
<QIcon name="article" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('route.cmr.list.hasCmrDms')"
v-model="params.hasCmrDms"
lazy-rules
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.ticketFk')"
v-model="params.ticketFk"
is-outlined
>
<template #prepend>
<QIcon name="vn:ticket" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.routeFk')"
v-model="params.routeFk"
is-outlined
>
<template #prepend>
<QIcon name="vn:delivery" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.clientFk')"
v-model="params.clientFk"
is-outlined
>
<template #prepend>
<QIcon name="vn:client" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection v-if="!countries">
<QSkeleton type="QInput" class="full-width" />
</QItemSection>
<QItemSection v-if="countries">
<VnSelect
:label="t('route.cmr.list.country')"
v-model="params.country"
:options="countries"
option-label="name"
option-value="id"
transition-show="jump-down"
transition-hide="jump-up"
emit-value
map-options
dense
outlined
rounded
>
<template #prepend>
<QIcon name="flag" size="sm"></QIcon>
</template>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<VnSelect
:label="t('globals.warehouse')"
:options="warehouses"
hide-selected
option-label="name"
option-value="id"
v-model="params.warehouseFk"
rounded
dense
outlined
>
</VnSelect>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
v-model="params.shipped"
:label="t('route.cmr.list.shipped')"
is-outlined
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
cmrFk: Cmr id
hasCmrDms: Attached in gestdoc
ticketFk: Ticketd id
country: Country
clientFk: Client id
shipped: Preparation date
es:
params:
cmrFk: Id cmr
hasCmrDms: Gestdoc
ticketFk: Id ticket
country: País
clientFk: Id cliente
shipped: Fecha preparación
</i18n>

View File

@ -2,82 +2,93 @@
import { onBeforeMount, onMounted, computed, ref } from 'vue'; import { onBeforeMount, onMounted, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { Notify } from 'quasar'; import { Notify } from 'quasar';
import axios from 'axios';
import VnPaginate from 'components/ui/VnPaginate.vue';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { toDate } from 'filters/index'; import { toDate } from 'filters/index';
import CmrFilter from './CmrFilter.vue';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import { useStateStore } from 'src/stores/useStateStore'; import { useStateStore } from 'src/stores/useStateStore';
import axios from 'axios';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
const { t } = useI18n(); const { t } = useI18n();
const { getTokenMultimedia } = useSession(); const { getTokenMultimedia } = useSession();
const token = getTokenMultimedia(); const token = getTokenMultimedia();
const state = useStateStore(); const state = useStateStore();
const selected = ref([]);
const warehouses = ref([]); const warehouses = ref([]);
const selectedRows = ref([]);
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left',
name: 'cmrFk', name: 'cmrFk',
label: t('route.cmr.list.cmrFk'), label: t('route.cmr.list.cmrFk'),
field: (row) => row.cmrFk, chip: {
sortable: true, condition: () => true,
},
isId: true,
}, },
{ {
align: 'left',
name: 'hasCmrDms', name: 'hasCmrDms',
label: t('route.cmr.list.hasCmrDms'), label: t('route.cmr.list.hasCmrDms'),
field: (row) => row.hasCmrDms, component: 'checkbox',
align: 'center', cardVisible: true,
sortable: true,
}, },
{ {
name: 'ticketFk',
label: t('route.cmr.list.ticketFk'), label: t('route.cmr.list.ticketFk'),
field: (row) => row.ticketFk, name: 'ticketFk',
sortable: true,
}, },
{ {
name: 'routeFkFk',
label: t('route.cmr.list.routeFk'), label: t('route.cmr.list.routeFk'),
field: (row) => row.routeFk, name: 'routeFk',
sortable: true,
}, },
{ {
name: 'clientFk',
label: t('route.cmr.list.clientFk'), label: t('route.cmr.list.clientFk'),
field: (row) => row.clientFk, name: 'clientFk',
sortable: true,
}, },
{ {
name: 'country',
label: t('route.cmr.list.country'),
field: (row) => row.country,
align: 'left', align: 'left',
sortable: true, label: t('route.cmr.list.country'),
name: 'country',
cardVisible: true,
}, },
{ {
name: 'shipped',
label: t('route.cmr.list.shipped'),
field: (row) => toDate(row.shipped),
align: 'center', align: 'center',
sortable: true, label: t('route.cmr.list.shipped'),
name: 'shipped',
cardVisible: true,
component: 'date',
columnFilter: {
alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
}, },
{ {
align: 'center',
name: 'warehouseFk', name: 'warehouseFk',
label: t('globals.warehouse'), label: t('globals.warehouse'),
field: ({ warehouseFk }) => warehouseFk, component: 'select',
align: 'center', attrs: {
sortable: true, url: 'warehouses',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
}, },
{ {
name: 'icons', align: 'right',
align: 'center', name: 'tableActions',
field: (row) => row.cmrFk, actions: [
{
title: t('Ver cmr'),
icon: 'visibility',
isPrimary: true,
action: (row) => window.open(getCmrUrl(row?.cmrFk), '_blank'),
},
],
}, },
]); ]);
@ -95,7 +106,7 @@ function getCmrUrl(value) {
return `${getApiUrl()}/api/Routes/${value}/cmr?access_token=${token}`; return `${getApiUrl()}/api/Routes/${value}/cmr?access_token=${token}`;
} }
function downloadPdfs() { function downloadPdfs() {
if (!selected.value.length) { if (!selectedRows.value.length) {
Notify.create({ Notify.create({
message: t('globals.noSelectedRows'), message: t('globals.noSelectedRows'),
type: 'warning', type: 'warning',
@ -103,42 +114,51 @@ function downloadPdfs() {
return; return;
} }
let cmrs = []; let cmrs = [];
for (let value of selected.value) cmrs.push(value.cmrFk); for (let value of selectedRows.value) cmrs.push(value.cmrFk);
// prettier-ignore // prettier-ignore
return window.open(`${getApiUrl()}/api/Routes/downloadCmrsZip?ids=${cmrs.join(',')}&access_token=${token}`); return window.open(`${getApiUrl()}/api/Routes/downloadCmrsZip?ids=${cmrs.join(',')}&access_token=${token}`);
} }
</script> </script>
<template> <template>
<VnSubToolbar>
<template #st-actions>
<QBtn
icon="cloud_download"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="downloadPdfs"
>
<QTooltip>{{ t('route.cmr.list.downloadCmrs') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<VnTable <VnTable
ref="tableRef" ref="tableRef"
data-key="CmrList" data-key="CmrList"
url="Routes/cmrs" url="Routes/cmrs"
order="cmrFk DESC"
:columns="columns" :columns="columns"
:right-search="true" :right-search="true"
:use-model="true" default-mode="table"
/> v-model:selected="selectedRows"
table-height="85vh"
:table="{
'row-key': 'cmrFk',
selection: 'multiple',
}"
:disable-option="{ card: true }"
>
<template #column-ticketFk="{ row }">
<span class="link" @click.stop>
{{ row.ticketFk }}
<TicketDescriptorProxy :id="row.ticketFk" />
</span>
</template>
<template #column-clientFk="{ row }">
<span class="link" @click.stop>
{{ row.ticketFk }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
</VnTable>
</template> </template>
<style lang="scss" scoped>
.list {
padding-top: 15px;
padding-bottom: 15px;
max-width: 1000px;
width: 100%;
}
.grid-style-transition {
transition: transform 0.28s, background-color 0.28s;
}
#true {
background-color: $positive;
}
#false {
background-color: $negative;
}
:deep(.q-table th) {
max-width: 80px;
}
:deep(.q-table th:nth-child(3)) {
max-width: 100px;
}
</style>

View File

@ -1,88 +0,0 @@
<script setup>
import { useDialogPluginComponent } from 'quasar';
import { useI18n } from 'vue-i18n';
import { reactive, ref } from 'vue';
import axios from 'axios';
import FetchData from 'components/FetchData.vue';
import RoadmapAddStopForm from 'pages/Route/Roadmap/RoadmapAddStopForm.vue';
const emit = defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide } = useDialogPluginComponent();
const { t } = useI18n();
const props = defineProps({
roadmapFk: {
type: Number,
required: true,
},
});
const isLoading = ref(false);
const roadmapStopForm = reactive({
roadmapFk: Number(props.roadmapFk) || 0,
warehouseFk: null,
eta: new Date().toISOString(),
description: '',
});
const updateDefaultStop = (data) => {
const eta = new Date(data.etd);
eta.setDate(eta.getDate() + 1);
roadmapStopForm.eta = eta.toISOString();
};
const onSave = async () => {
isLoading.value = true;
try {
await axios.post('RoadmapStops', { ...roadmapStopForm });
emit('ok');
} finally {
isLoading.value = false;
emit('hide');
}
};
</script>
<template>
<FetchData
:url="`Roadmaps/${roadmapFk}`"
auto-load
:filter="{ fields: ['etd'] }"
@on-fetch="updateDefaultStop"
/>
<QDialog ref="dialogRef" @hide="onDialogHide">
<QCard class="q-pa-lg">
<QCardSection class="row absolute absolute-top z-fullscreen">
<QSpace />
<QBtn icon="close" flat round dense v-close-popup />
</QCardSection>
<RoadmapAddStopForm
:roadmap-fk="roadmapFk"
:form-data="roadmapStopForm"
layout="dialog"
/>
<QCardActions align="right">
<QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup />
<QBtn
:label="t('globals.confirm')"
color="primary"
:loading="isLoading"
@click="onSave"
unelevated
/>
</QCardActions>
</QCard>
</QDialog>
</template>
<style lang="scss" scoped>
.card-section {
gap: 16px;
}
</style>
<i18n>
es:
Warehouse: Almacén
ETA date: Fecha ETA
ETA time: Hora ETA
Description: Descripción
</i18n>

View File

@ -1,45 +0,0 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnRow from 'components/ui/VnRow.vue';
import FormModel from 'components/FormModel.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
const { t } = useI18n();
const router = useRouter();
const defaultInitialData = {
etd: Date.vnNew().toISOString(),
};
const onSave = (data, response) => {
router.push({ name: 'RoadmapSummary', params: { id: response?.id } });
};
</script>
<template>
<VnSubToolbar />
<FormModel
:url="null"
url-create="Roadmaps"
model="roadmap"
:observe-form-changes="false"
:auto-load="false"
:form-initial-data="defaultInitialData"
@on-data-saved="onSave"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInput v-model="data.name" :label="t('Roadmap')" clearable />
<VnInputDate v-model="data.etd" :label="t('ETD date')" />
<VnInputTime v-model="data.etd" :label="t('ETD hour')" />
</VnRow>
</template>
</FormModel>
</template>
<i18n>
es:
Roadmap: Troncal
ETD date: Fecha ETD
ETD hour: Hora ETD
</i18n>

View File

@ -2,14 +2,14 @@
import { computed, onMounted, onUnmounted, ref } from 'vue'; import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { QIcon, useQuasar } from 'quasar'; import { QIcon } from 'quasar';
import { dashIfEmpty, toDateHourMin } from 'src/filters'; import { dashIfEmpty, toDateHourMin } from 'src/filters';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import VnLv from 'components/ui/VnLv.vue'; import VnLv from 'components/ui/VnLv.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue'; import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
import VnLinkPhone from 'components/ui/VnLinkPhone.vue'; import VnLinkPhone from 'components/ui/VnLinkPhone.vue';
import RoadmapAddStopDialog from 'pages/Route/Roadmap/RoadmapAddStopDialog.vue';
const $props = defineProps({ const $props = defineProps({
id: { id: {
@ -20,7 +20,6 @@ const $props = defineProps({
const route = useRoute(); const route = useRoute();
const stateStore = useStateStore(); const stateStore = useStateStore();
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar();
const summary = ref(null); const summary = ref(null);
const entityId = computed(() => $props.id || route.params.id); const entityId = computed(() => $props.id || route.params.id);
@ -37,7 +36,7 @@ const columns = ref([
{ {
name: 'address', name: 'address',
label: t('Address'), label: t('Address'),
field: (row) => dashIfEmpty(row?.address?.nickname), field: (row) => dashIfEmpty(row?.address?.addressFk),
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
@ -69,15 +68,6 @@ const filter = {
], ],
where: { id: entityId }, where: { id: entityId },
}; };
const openAddStopDialog = () => {
quasar
.dialog({
component: RoadmapAddStopDialog,
componentProps: { roadmapFk: entityId.value },
})
.onOk(() => summary.value.fetch());
};
</script> </script>
<template> <template>
@ -98,6 +88,10 @@ const openAddStopDialog = () => {
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle
:url="`#/route/roadmap/${entityId}/basic-data`"
:text="t('Basic Data')"
/>
<VnLv :label="t('Carrier')"> <VnLv :label="t('Carrier')">
<template #value> <template #value>
<span class="link" v-if="entity?.supplier?.id"> <span class="link" v-if="entity?.supplier?.id">
@ -115,8 +109,6 @@ const openAddStopDialog = () => {
:label="t('Trailer Plate')" :label="t('Trailer Plate')"
:value="dashIfEmpty(entity?.trailerPlate)" :value="dashIfEmpty(entity?.trailerPlate)"
/> />
</QCard>
<QCard class="vn-one">
<VnLv :label="t('Phone')" :value="dashIfEmpty(entity?.phone)"> <VnLv :label="t('Phone')" :value="dashIfEmpty(entity?.phone)">
<template #value> <template #value>
<span> <span>
@ -139,26 +131,10 @@ const openAddStopDialog = () => {
/> />
</QCard> </QCard>
<QCard class="vn-max"> <QCard class="vn-max">
<div class="flex items-center"> <VnTitle
<a class="header" :href="`#/route/roadmap/${entityId}/stops`"> :url="`#/route/roadmap/${entityId}/stops`"
{{ t('Stops') }} :text="t('Stops')"
<QIcon name="open_in_new" color="primary"> />
<QTooltip>
{{ t('Go to stops') }}
</QTooltip>
</QIcon>
</a>
<QIcon
name="add_circle"
color="primary"
class="q-ml-lg header cursor-pointer"
@click.stop="openAddStopDialog"
>
<QTooltip>
{{ t('Add stop') }}
</QTooltip>
</QIcon>
</div>
<QTable <QTable
:columns="columns" :columns="columns"
:rows="entity?.roadmapStop" :rows="entity?.roadmapStop"

View File

@ -1,33 +1,29 @@
<script setup> <script setup>
import VnPaginate from 'components/ui/VnPaginate.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { computed, onMounted, onUnmounted, ref } from 'vue'; import { computed, onMounted, onUnmounted, ref } from 'vue';
import { dashIfEmpty, toCurrency, toDate } from 'src/filters'; import { toCurrency, toDate } from 'src/filters';
import VnSearchbar from 'components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'composables/useSummaryDialog';
import { useState } from 'composables/useState';
import useNotify from 'composables/useNotify';
import axios from 'axios';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import RouteDescriptorProxy from 'pages/Route/Card/RouteDescriptorProxy.vue'; import RouteDescriptorProxy from 'pages/Route/Card/RouteDescriptorProxy.vue';
import InvoiceInDescriptorProxy from 'pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue'; import InvoiceInDescriptorProxy from 'pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue'; import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
import VnLv from 'components/ui/VnLv.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue';
import useNotify from 'composables/useNotify';
import RouteAutonomousFilter from 'pages/Route/Card/RouteAutonomousFilter.vue';
import { useRouter } from 'vue-router';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import { useSummaryDialog } from 'composables/useSummaryDialog';
import VnDms from 'components/common/VnDms.vue'; import VnDms from 'components/common/VnDms.vue';
import { useState } from 'composables/useState'; import VnTable from 'components/VnTable/VnTable.vue';
import axios from 'axios'; import VnLv from 'components/ui/VnLv.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
const { viewSummary } = useSummaryDialog();
const { notify } = useNotify();
const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
const { t } = useI18n(); const refreshKey = ref(0);
const { notify } = useNotify();
const router = useRouter();
const { viewSummary } = useSummaryDialog();
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
const state = useState(); const state = useState();
const user = state.getUser(); const user = state.getUser();
const dmsDialog = ref({ const dmsDialog = ref({
@ -36,87 +32,89 @@ const dmsDialog = ref({
rowsToCreateInvoiceIn: [], rowsToCreateInvoiceIn: [],
}); });
const selectedRows = ref([]); const selectedRows = ref([]);
const tableRef = ref();
const columns = computed(() => [ const columns = computed(() => [
{ {
name: 'ID', align: 'left',
name: 'id',
label: 'Id', label: 'Id',
field: (row) => row.routeFk, chip: {
sortable: true, condition: () => true,
align: 'left', },
isId: true,
}, },
{ {
name: 'date', align: 'left',
name: 'created',
label: t('Date'), label: t('Date'),
field: (row) => toDate(row.created), component: 'date',
sortable: true, columnFilter: {
align: 'left', alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
}, },
{ {
name: 'agency-route', align: 'left',
name: 'agencyModeName',
label: t('Agency route'), label: t('Agency route'),
field: (row) => row?.agencyModeName, cardVisible: true,
sortable: true,
align: 'left',
}, },
{ {
name: 'agency-agreement', align: 'left',
name: 'agencyAgreement',
label: t('Agency agreement'), label: t('Agency agreement'),
field: (row) => row?.agencyAgreement, cardVisible: true,
sortable: true,
align: 'left',
}, },
{ {
align: 'left',
name: 'packages', name: 'packages',
label: t('Packages'), label: t('Packages'),
field: (row) => dashIfEmpty(row.packages),
sortable: true,
align: 'left',
}, },
{ {
name: 'volume', align: 'left',
name: 'm3',
label: 'm³', label: 'm³',
field: (row) => dashIfEmpty(row.m3), cardVisible: true,
sortable: true, columnFilter: {
align: 'left', inWhere: true,
},
}, },
{ {
name: 'km', align: 'left',
name: 'kmTotal',
label: t('Km'), label: t('Km'),
field: (row) => dashIfEmpty(row?.kmTotal), cardVisible: true,
sortable: true,
align: 'left',
}, },
{ {
align: 'left',
name: 'price', name: 'price',
label: t('Price'), label: t('Price'),
field: (row) => (row?.price ? toCurrency(row.price) : '-'),
sortable: true,
align: 'left',
}, },
{ {
name: 'received', align: 'left',
name: 'invoiceInFk',
label: t('Received'), label: t('Received'),
field: (row) => row?.invoiceInFk,
sortable: true,
align: 'left',
}, },
{ {
name: 'autonomous', align: 'left',
name: 'supplierName',
label: t('Autonomous'), label: t('Autonomous'),
field: (row) => row?.supplierName,
sortable: true,
align: 'left',
}, },
{ {
name: 'actions',
label: '',
sortable: false,
align: 'right', align: 'right',
name: 'tableActions',
actions: [
{
title: t('Preview'),
icon: 'preview',
isPrimary: true,
action: (row) => viewSummary(row?.routeFk, RouteSummary),
},
],
}, },
]); ]);
const refreshKey = ref(0);
const total = computed(() => { const total = computed(() => {
return selectedRows.value.reduce((sum, item) => sum + item.price, 0); return selectedRows.value.reduce((sum, item) => sum + item.price, 0);
}); });
@ -169,9 +167,8 @@ const onDmsSaved = async (dms, response) => {
refreshKey.value++; refreshKey.value++;
}; };
function navigateToRouteSummary(event, row) { onMounted(() => (stateStore.rightDrawer = true));
router.push({ name: 'RouteSummary', params: { id: row.routeFk } }); onUnmounted(() => (stateStore.rightDrawer = false));
}
</script> </script>
<template> <template>
@ -180,109 +177,63 @@ function navigateToRouteSummary(event, row) {
:label="t('Search autonomous')" :label="t('Search autonomous')"
:info="t('You can search by autonomous reference')" :info="t('You can search by autonomous reference')"
/> />
<RightMenu> <div class="q-pa-md">
<template #right-panel> <QCard class="vn-one q-py-sm q-px-lg flex justify-between">
<RouteAutonomousFilter data-key="RouteAutonomousList" /> <VnLv class="flex">
</template> <template #label>
</RightMenu> <span class="text-h6">{{ t('Total') }}</span>
<QPage class="column items-center">
<div class="route-list">
<div class="q-pa-md">
<QCard class="vn-one q-py-sm q-px-lg flex justify-between">
<VnLv class="flex">
<template #label>
<span class="text-h6">{{ t('Total') }}</span>
</template>
<template #value>
<span class="text-h6 q-ml-md">{{ toCurrency(total) }}</span>
</template>
</VnLv>
<QBtn
icon="vn:invoice-in-create"
color="primary"
:disable="!selectedRows?.length"
@click="openDmsUploadDialog"
>
<QTooltip>
{{ t('Create invoiceIn') }}
</QTooltip>
</QBtn>
</QCard>
</div>
<VnPaginate
:key="refreshKey"
data-key="RouteAutonomousList"
url="AgencyTerms/filter"
:limit="20"
>
<template #body="{ rows }">
<div class="q-pa-md">
<QTable
v-model:selected="selectedRows"
:columns="columns"
:rows="rows"
flat
row-key="routeFk"
selection="multiple"
:rows-per-page-options="[0]"
hide-pagination
@row-click="navigateToRouteSummary"
>
<template #body-cell-ID="props">
<QTd :props="props">
<span class="link" @click.stop>
{{ props.value }}
<RouteDescriptorProxy :id="props.value" />
</span>
</QTd>
</template>
<template #body-cell-received="props">
<QTd :props="props">
<span :class="props.value && 'link'" @click.stop>
{{ dashIfEmpty(props.value) }}
<InvoiceInDescriptorProxy
v-if="props.value"
:id="props.value"
/>
</span>
</QTd>
</template>
<template #body-cell-autonomous="props">
<QTd :props="props">
<span class="link" @click.stop>
{{ props.value }}
<SupplierDescriptorProxy
v-if="props.row?.supplierFk"
:id="props.row?.supplierFk"
/>
</span>
</QTd>
</template>
<template #body-cell-actions="props">
<QTd :props="props">
<div class="table-actions">
<QIcon
name="preview"
size="sm"
color="primary"
@click.stop="
viewSummary(
props?.row?.routeFk,
RouteSummary
)
"
>
<QTooltip>{{ t('Preview') }}</QTooltip>
</QIcon>
</div>
</QTd>
</template>
</QTable>
</div>
</template> </template>
</VnPaginate> <template #value>
</div> <span class="text-h6 q-ml-md">{{ toCurrency(total) }}</span>
</QPage> </template>
</VnLv>
<QBtn
icon="vn:invoice-in-create"
color="primary"
:disable="!selectedRows?.length"
@click="openDmsUploadDialog"
>
<QTooltip>
{{ t('Create invoiceIn') }}
</QTooltip>
</QBtn>
</QCard>
</div>
<VnTable
ref="tableRef"
data-key="RouteAutonomousList"
url="AgencyTerms/filter"
:columns="columns"
:right-search="true"
default-mode="table"
v-model:selected="selectedRows"
table-height="85vh"
redirect="route"
:row-click="({ routeFk }) => tableRef.redirect(routeFk)"
:table="{
'row-key': '$index',
selection: 'multiple',
}"
>
<template #column-id="{ row }">
<span class="link" @click.stop>
{{ row.routeFk }}
<RouteDescriptorProxy :id="row.route.id" />
</span>
</template>
<template #column-invoiceInFk="{ row }">
<span class="link" @click.stop>
{{ row.invoiceInFk }}
<InvoiceInDescriptorProxy v-if="row.invoiceInFk" :id="row.invoiceInFk" />
</span>
</template>
<template #column-supplierName="{ row }">
<span class="link" @click.stop>
{{ row.supplierName }}
<SupplierDescriptorProxy :id="row.supplierFk" />
</span>
</template>
</VnTable>
<QDialog v-model="dmsDialog.show"> <QDialog v-model="dmsDialog.show">
<VnDms <VnDms
url="dms/uploadFile" url="dms/uploadFile"
@ -292,29 +243,6 @@ function navigateToRouteSummary(event, row) {
/> />
</QDialog> </QDialog>
</template> </template>
<style lang="scss" scoped>
.route-list {
width: 100%;
}
.table-actions {
display: flex;
align-items: center;
gap: 12px;
i {
cursor: pointer;
}
}
th:last-child,
td:last-child {
background-color: var(--vn-section-color);
position: sticky;
right: 0;
}
</style>
<i18n> <i18n>
es: es:
Search autonomous: Buscar autónomos Search autonomous: Buscar autónomos

View File

@ -1,50 +1,41 @@
<script setup> <script setup>
import { useArrayData } from 'composables/useArrayData'; import { computed, ref } from 'vue';
import { dashIfEmpty, toHour } from 'src/filters';
import { computed, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useValidator } from 'composables/useValidator';
import { useSession } from 'composables/useSession'; import { useSession } from 'composables/useSession';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { toDate } from 'src/filters';
import { useRouter } from 'vue-router';
import axios from 'axios'; import axios from 'axios';
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue'; import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
import FetchData from 'components/FetchData.vue';
import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.vue'; import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.vue';
import { useRouter } from 'vue-router';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue'; import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import { useStateStore } from 'src/stores/useStateStore';
const { t } = useI18n(); const { t } = useI18n();
const { validate } = useValidator();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const quasar = useQuasar(); const quasar = useQuasar();
const session = useSession(); const session = useSession();
const paginate = ref();
const visibleColumns = ref([]);
const selectedRows = ref([]); const selectedRows = ref([]);
const workers = ref([]); const tableRef = ref([]);
const agencyList = ref([]);
const vehicleList = ref([]);
const allColumnNames = ref([]);
const confirmationDialog = ref(false); const confirmationDialog = ref(false);
const startingDate = ref(null); const startingDate = ref(null);
const refreshKey = ref(0); const refreshKey = ref(0);
const router = useRouter(); const router = useRouter();
const stateStore = useStateStore(); const routeFilter = {
include: [
{
relation: 'workers',
scope: {
fields: ['id', 'firstName'],
},
},
],
};
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left', align: 'left',
@ -60,75 +51,114 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
name: 'worker', name: 'workerFk',
label: t('Worker'), label: t('Worker'),
create: true, create: true,
component: 'select', component: 'select',
attrs: { attrs: {
url: 'payrollComponents', url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'], fields: ['id', 'nickname'],
optionValue: 'id',
optionLabel: 'nickname',
}, },
useLike: false,
cardVisible: true, cardVisible: true,
format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef),
}, },
{ {
name: 'agency',
label: t('Agency'),
align: 'left', align: 'left',
name: 'agencyModeFk',
label: t('Agency'),
isTitle: true, isTitle: true,
cardVisible: true, cardVisible: true,
create: true, create: true,
component: 'select',
attrs: {
url: 'agencyModes',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
}, },
{ {
name: 'vehicle', align: 'left',
name: 'vehicleFk',
label: t('Vehicle'), label: t('Vehicle'),
align: 'left',
cardVisible: true, cardVisible: true,
create: true, create: true,
component: 'select',
attrs: {
url: 'vehicles',
fields: ['id', 'numberPlate'],
optionLabel: 'numberPlate',
optionValue: 'id',
},
}, },
{ {
name: 'date', align: 'left',
name: 'created',
label: t('Date'), label: t('Date'),
align: 'left',
cardVisible: true, cardVisible: true,
create: true, create: true,
component: 'date',
columnFilter: {
alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
}, },
{ {
name: 'volume',
label: 'm³',
align: 'center', align: 'center',
name: 'm3',
label: 'volume',
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'left',
name: 'description', name: 'description',
label: t('Description'), label: t('Description'),
align: 'left',
isTitle: true, isTitle: true,
create: true, create: true,
component: 'input',
field: 'description',
}, },
{ {
align: 'left',
name: 'started', name: 'started',
label: t('hourStarted'), label: t('hourStarted'),
align: 'left', component: 'time',
}, },
{ {
align: 'left',
name: 'finished', name: 'finished',
label: t('hourFinished'), label: t('hourFinished'),
align: 'left', component: 'time',
}, },
{ {
name: 'isServed',
label: t('Served'),
align: 'left', align: 'left',
name: 'isOk',
label: t('Served'),
component: 'checkbox',
}, },
{ {
align: 'right', align: 'right',
label: 'asdasd',
name: 'tableActions', name: 'tableActions',
actions: [ actions: [
{ {
title: t('Client ticket list'), title: t('Add tickets'),
icon: 'vn:ticketAdd',
action: (row) => openTicketsDialog(row?.id),
},
{
title: t('Preview'),
icon: 'preview', icon: 'preview',
action: (row) => navigate(row.id), action: (row) => viewSummary(row?.id, RouteSummary),
},
{
title: t('Route summary'),
icon: 'arrow_forward',
isPrimary: true,
action: (row) => navigate(row?.id),
}, },
], ],
}, },
@ -138,22 +168,14 @@ function navigate(id) {
router.push({ path: `/route/${id}` }); router.push({ path: `/route/${id}` });
} }
const updateRoute = async (route) => {
try {
return await axios.patch(`Routes/${route.id}`, route);
} catch (err) {
return err;
}
};
const cloneRoutes = () => { const cloneRoutes = () => {
if (!selectedRows.value.length || !startingDate.value) return;
axios.post('Routes/clone', { axios.post('Routes/clone', {
created: startingDate.value, created: startingDate.value,
ids: selectedRows.value.map((row) => row?.id), ids: selectedRows.value.map((row) => row?.id),
}); });
refreshKey.value++; tableRef.value.reload();
startingDate.value = null; startingDate.value = null;
paginate.value.fetch();
}; };
const showRouteReport = () => { const showRouteReport = () => {
@ -175,16 +197,13 @@ const showRouteReport = () => {
function markAsServed() { function markAsServed() {
selectedRows.value.forEach(async (row) => { selectedRows.value.forEach(async (row) => {
if (row?.id) await axios.patch(`Routes/${row?.id}`, { isOk: true }); await axios.patch(`Routes/${row?.id}`, { isOk: true });
}); });
refreshKey.value++; tableRef.value.reload();
startingDate.value = null; startingDate.value = null;
} }
const openTicketsDialog = (id) => { const openTicketsDialog = (id) => {
if (!id) {
return;
}
quasar quasar
.dialog({ .dialog({
component: RouteListTicketsDialog, component: RouteListTicketsDialog,
@ -215,21 +234,10 @@ const openTicketsDialog = (id) => {
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" /> <QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
<QBtn color="primary" v-close-popup @click="cloneRoutes"> <QBtn color="primary" v-close-popup @click="cloneRoutes">
{{ t('globals.clone') }} {{ t('globals.clone') }}
<VnLv
:label="t('route.summary.packages')"
:value="getTotalPackages(entity.tickets)"
/>
</QBtn> </QBtn>
</QCardActions> </QCardActions>
</QCard> </QCard>
</QDialog> </QDialog>
<FetchData
url="Workers/activeWithInheritedRole"
@on-fetch="(data) => (workers = data)"
auto-load
/>
<FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
<FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
<VnSubToolbar /> <VnSubToolbar />
<VnTable <VnTable
ref="tableRef" ref="tableRef"
@ -237,15 +245,24 @@ const openTicketsDialog = (id) => {
url="Routes/filter" url="Routes/filter"
:columns="columns" :columns="columns"
:right-search="true" :right-search="true"
default-mode="table"
:is-editable="true" :is-editable="true"
:filter="routeFilter"
redirect="route"
:create="{ :create="{
urlCreate: 'Routes', urlCreate: 'Routes',
title: t('Create route'), title: t('Create route'),
onDataSaved: () => tableRef.reload(), onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}" }"
save-url="routes" save-url="Routes/crud"
:disable-option="{ card: true }" :disable-option="{ card: true }"
:use-model="true"
table-height="85vh"
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
> >
<template #moreBeforeActions> <template #moreBeforeActions>
<QBtn <QBtn

View File

@ -1,65 +1,68 @@
<script setup> <script setup>
import VnPaginate from 'components/ui/VnPaginate.vue';
import { useStateStore } from 'stores/useStateStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { computed, onMounted, onUnmounted, ref } from 'vue'; import { computed, ref } from 'vue';
import { dashIfEmpty, toDateHourMin } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
import { toDate } from 'filters/index';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import toCurrency from 'filters/toCurrency';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import RoadmapFilter from 'pages/Route/Roadmap/RoadmapFilter.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import axios from 'axios';
import VnInputDate from 'components/common/VnInputDate.vue';
import { useSummaryDialog } from 'composables/useSummaryDialog'; import { useSummaryDialog } from 'composables/useSummaryDialog';
import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue'; import toCurrency from 'filters/toCurrency';
import { useRouter } from 'vue-router'; import axios from 'axios';
import RightMenu from 'src/components/common/RightMenu.vue';
const stateStore = useStateStore(); import VnSearchbar from 'components/ui/VnSearchbar.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'src/components/common/VnInputTime.vue';
const { viewSummary } = useSummaryDialog();
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const router = useRouter();
const { viewSummary } = useSummaryDialog();
const to = Date.vnNew();
to.setDate(to.getDate() + 1);
to.setHours(0, 0, 0, 0);
const from = Date.vnNew();
from.setDate(from.getDate());
from.setHours(0, 0, 0, 0);
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
const selectedRows = ref([]); const selectedRows = ref([]);
const columns = computed(() => [ const columns = computed(() => [
{ {
name: 'roadmap', align: 'left',
name: 'id',
label: 'Id',
isId: true,
},
{
align: 'left',
name: 'name',
label: t('Roadmap'), label: t('Roadmap'),
field: (row) => row.name, create: true,
sortable: true, cardVisible: true,
align: 'left', columnFilter: {
inWhere: true,
},
}, },
{ {
name: 'ETD', align: 'left',
name: 'etd',
label: t('ETD'), label: t('ETD'),
field: (row) => toDateHourMin(row.etd), component: 'date',
sortable: true, columnFilter: {
align: 'left', alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
cardVisible: true,
}, },
{ {
name: 'carrier', align: 'left',
name: 'supplierFk',
label: t('Carrier'), label: t('Carrier'),
field: (row) => row.supplier?.nickname, component: 'select',
sortable: true, attrs: {
align: 'left', url: 'suppliers',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
}, },
{ {
name: 'plate', name: 'tractorPlate',
label: t('Plate'), label: t('Plate'),
field: (row) => row.tractorPlate, field: (row) => row.tractorPlate,
sortable: true, sortable: true,
@ -80,30 +83,29 @@ const columns = computed(() => [
align: 'left', align: 'left',
}, },
{ {
name: 'actions',
label: '',
sortable: false,
align: 'right', align: 'right',
name: 'tableActions',
actions: [
{
title: t('Ver cmr'),
icon: 'visibility',
isPrimary: true,
action: (row) => viewSummary(row?.id, RoadmapSummary),
},
],
}, },
]); ]);
const refreshKey = ref(0); const tableRef = ref(0);
const isCloneDialogOpen = ref(false); const isCloneDialogOpen = ref(false);
const etdDate = ref(null); const etdDate = ref(null);
const filter = {
include: { relation: 'supplier', scope: { fields: ['nickname'] } },
where: {
and: [{ etd: { gte: from } }, { etd: { lte: to } }],
},
};
const cloneSelection = async () => { const cloneSelection = async () => {
await axios.post('Roadmaps/clone', { await axios.post('Roadmaps/clone', {
etd: etdDate.value, etd: etdDate.value,
ids: selectedRows.value.map((row) => row?.id), ids: selectedRows.value.map((row) => row?.id),
}); });
refreshKey.value++; tableRef.value.reload();
etdDate.value = null; etdDate.value = null;
}; };
@ -125,11 +127,7 @@ function confirmRemove() {
promise: removeSelection, promise: removeSelection,
}, },
}) })
.onOk(() => refreshKey.value++); .onOk(() => tableRef.value++);
}
function navigateToRoadmapSummary(_, { id }) {
router.push({ name: 'RoadmapSummary', params: { id } });
} }
</script> </script>
@ -139,11 +137,6 @@ function navigateToRoadmapSummary(_, { id }) {
:label="t('Search roadmaps')" :label="t('Search roadmaps')"
:info="t('You can search by roadmap reference')" :info="t('You can search by roadmap reference')"
/> />
<RightMenu>
<template #right-panel>
<RoadmapFilter data-key="RoadmapList" />
</template>
</RightMenu>
<QDialog v-model="isCloneDialogOpen"> <QDialog v-model="isCloneDialogOpen">
<QCard style="min-width: 350px"> <QCard style="min-width: 350px">
<QCardSection> <QCardSection>
@ -163,95 +156,55 @@ function navigateToRoadmapSummary(_, { id }) {
</QCardActions> </QCardActions>
</QCard> </QCard>
</QDialog> </QDialog>
<QPage class="column items-center"> <VnSubToolbar class="justify-end">
<VnSubToolbar class="justify-end"> <template #st-actions>
<template #st-actions> <QBtn
<QBtn icon="vn:clone"
icon="vn:clone" color="primary"
color="primary" class="q-mr-sm"
class="q-mr-sm" :disable="!selectedRows?.length"
:disable="!selectedRows?.length" @click="isCloneDialogOpen = true"
@click="isCloneDialogOpen = true"
>
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
</QBtn>
<QBtn
icon="delete"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="confirmRemove"
>
<QTooltip>{{ t('Delete roadmap(s)') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<div class="route-list">
<VnPaginate
:key="refreshKey"
data-key="RoadmapList"
url="Roadmaps"
:limit="20"
:filter="filter"
> >
<template #body="{ rows }"> <QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
<div class="q-pa-md"> </QBtn>
<QTable <QBtn
v-model:selected="selectedRows" icon="delete"
:columns="columns" color="primary"
:rows="rows" class="q-mr-sm"
flat :disable="!selectedRows?.length"
row-key="id" @click="confirmRemove"
selection="multiple" >
:rows-per-page-options="[0]" <QTooltip>{{ t('Delete roadmap(s)') }}</QTooltip>
hide-pagination </QBtn>
:pagination="{ sortBy: 'ID', descending: true }" </template>
@row-click="navigateToRoadmapSummary" </VnSubToolbar>
> <VnTable
<template #body-cell-carrier="props"> ref="tableRef"
<QTd :props="props"> data-key="RoadmapList"
<span v-if="props.value" class="link" @click.stop> url="roadmaps"
{{ props.value }} :columns="columns"
<SupplierDescriptorProxy :right-search="true"
:id="props.row?.supplier?.id" :use-model="true"
/> default-mode="table"
</span> v-model:selected="selectedRows"
</QTd> table-height="85vh"
</template> :table="{
<template #body-cell-actions="props"> selection: 'multiple',
<QTd :props="props"> }"
<div class="flex items-center table-actions"> redirect="route/roadmap"
<QIcon :create="{
name="preview" urlCreate: 'Roadmaps',
size="sm" title: t('Create routemap'),
color="primary" onDataSaved: ({ id }) => tableRef.redirect(id),
@click.stop=" formInitialData: {},
viewSummary( }"
props?.row?.id, :disable-option="{ card: true }"
RoadmapSummary >
) <template #more-create-dialog="{ data }">
" <VnInputDate v-model="data.etd" />
class="cursor-pointer" <VnInputTime v-model="data.etd" />
> </template>
<QTooltip>{{ t('Preview') }}</QTooltip> </VnTable>
</QIcon>
</div>
</QTd>
</template>
</QTable>
</div>
</template>
</VnPaginate>
</div>
<QPageSticky :offset="[20, 20]">
<RouterLink :to="{ name: 'RouteRoadmapCreate' }">
<QBtn fab icon="add" color="primary" />
<QTooltip>
{{ t('Create roadmap') }}
</QTooltip>
</RouterLink>
</QPageSticky>
</QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -264,6 +217,7 @@ function navigateToRoadmapSummary(_, { id }) {
</style> </style>
<i18n> <i18n>
es: es:
Create routemap: Crear troncal
Search roadmaps: Buscar troncales Search roadmaps: Buscar troncales
You can search by roadmap reference: Puedes buscar por referencia del troncal You can search by roadmap reference: Puedes buscar por referencia del troncal
Delete roadmap(s): Eliminar troncal(es) Delete roadmap(s): Eliminar troncal(es)

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed, onUpdated } from 'vue'; import { ref, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';

View File

@ -99,7 +99,6 @@ const columns = computed(() => [
}" }"
order="id ASC" order="id ASC"
:columns="columns" :columns="columns"
default-mode="table"
auto-load auto-load
:right-search="false" :right-search="false"
:use-model="true" :use-model="true"

View File

@ -0,0 +1,47 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnDmsList from 'src/components/common/VnDmsList.vue';
import TicketDmsImportForm from 'src/pages/Ticket/Card/TicketDmsImportForm.vue';
const { t } = useI18n();
const dmsListRef = ref(null);
const showImportDialog = ref(false);
const onDataSaved = () => dmsListRef.value.dmsRef.fetch();
</script>
<template>
<VnDmsList
ref="dmsListRef"
model="TicketDms"
update-model="tickets"
delete-model="TicketDms"
download-model="dms"
default-dms-code="ticket"
filter="ticketFk"
allow-import
/>
<QDialog v-model="showImportDialog">
<TicketDmsImportForm @on-data-saved="onDataSaved()" />
</QDialog>
<QPageSticky position="bottom-right" :offset="[25, 90]">
<QBtn
fab
color="primary"
icon="file_copy"
@click="showImportDialog = true"
class="fill-icon"
>
<QTooltip>
{{ t('Import from existing') }}
</QTooltip>
</QBtn>
</QPageSticky>
</template>
<i18n>
es:
Import from existing: Importar desde existente
</i18n>

View File

@ -0,0 +1,76 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
import FetchData from 'components/FetchData.vue';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
const emit = defineEmits(['onDataSaved']);
const { t } = useI18n();
const { notify } = useNotify();
const route = useRoute();
const dmsOptions = ref([]);
const dmsId = ref(null);
const importDms = async () => {
try {
if (!dmsId.value) throw new Error(t(`The document identifier can't be empty`));
const data = {
ticketFk: route.params.id,
dmsFk: dmsId.value,
};
await axios.post('ticketDms', data);
notify(t('globals.dataSaved'), 'positive');
dmsId.value = null;
emit('onDataSaved');
} catch (e) {
throw new Error(e.message);
}
};
</script>
<template>
<FetchData
url="Dms"
:filter="{ fields: ['id'], order: 'id ASC' }"
auto-load
@on-fetch="(data) => (dmsOptions = data)"
/>
<FormModelPopup
url-create="genera"
model="DmsImport"
:title="t('Select document id')"
:form-initial-data="{}"
:save-fn="importDms"
>
<template #form-inputs>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
:label="t('Document')"
:options="dmsOptions"
hide-selected
option-label="id"
option-value="id"
v-model="dmsId"
/>
</VnRow>
</template>
</FormModelPopup>
</template>
<i18n>
es:
Select document id: Introduzca id de gestion documental
Document: Documento
The document indentifier can't be empty: El número de documento no puede estar vacío
</i18n>

View File

@ -0,0 +1,549 @@
<script setup>
import { ref, computed, nextTick, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import { dashIfEmpty } from 'src/filters';
import useNotify from 'src/composables/useNotify.js';
import { toDateTimeFormat } from 'src/filters/date';
import axios from 'axios';
const route = useRoute();
const { t } = useI18n();
const { notify } = useNotify();
const saleTrackingTableDialogRef = ref(null);
const itemShelvingSaleDialogRef = ref(null);
const saleTrackingFetchDataRef = ref(null);
const sales = ref([]);
const saleTrackings = ref([]);
const itemShelvignsSales = ref([]);
const shelvingsOptions = ref([]);
const parkingsOptions = ref([]);
const saleTrackingUrl = computed(() => `SaleTrackings/${route.params.id}/filter`);
const oldQuantity = ref(null);
watch(
() => route.params.id,
async () => nextTick(async () => await saleTrackingFetchDataRef.value.fetch())
);
const columns = computed(() => [
{
label: t('ticketSaleTracking.isChecked'),
name: 'isChecked',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.item'),
name: 'item',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.description'),
name: 'description',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.quantity'),
name: 'quantity',
field: 'quantity',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.parking'),
name: 'parking',
field: 'parkingCode',
align: 'left',
sortable: true,
format: (value) => dashIfEmpty(value),
},
{
label: '',
name: 'actions',
align: 'left',
sortable: true,
},
]);
const logTableColumns = computed(() => [
{
label: t('ticketSaleTracking.quantity'),
name: 'quantity',
field: 'quantity',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.original'),
name: 'original',
field: 'originalQuantity',
align: 'original',
sortable: true,
},
{
label: t('ticketSaleTracking.worker'),
name: 'worker',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.state'),
name: 'state',
field: 'state',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.created'),
name: 'created',
field: 'created',
align: 'left',
sortable: true,
format: (value) => toDateTimeFormat(value),
},
]);
const shelvingsTableColumns = computed(() => [
{
label: t('ticketSaleTracking.quantity'),
name: 'quantity',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.worker'),
name: 'worker',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.shelving'),
name: 'shelving',
align: 'original',
sortable: true,
},
{
label: t('ticketSaleTracking.parking'),
name: 'parking',
align: 'left',
sortable: true,
},
{
label: t('ticketSaleTracking.created'),
name: 'created',
field: 'created',
align: 'left',
sortable: true,
format: (value) => toDateTimeFormat(value),
},
]);
const getSaleTrackings = async (sale) => {
try {
const filter = {
where: { saleFk: sale.saleFk },
order: ['itemFk DESC'],
};
const { data } = await axios.get(`SaleTrackings/listSaleTracking`, {
params: { filter: JSON.stringify(filter) },
});
saleTrackings.value = data;
} catch (error) {
console.error(error);
}
};
const showLog = async (sale) => {
await getSaleTrackings(sale);
saleTrackingTableDialogRef.value.show();
};
const getItemShelvingSales = async (sale) => {
try {
const filter = {
where: { saleFk: sale.saleFk },
};
const { data } = await axios.get(`ItemShelvingSales/filter`, {
params: { filter: JSON.stringify(filter) },
});
itemShelvignsSales.value = data;
} catch (error) {
console.error(error);
}
};
const showShelving = async (sale) => {
await getItemShelvingSales(sale);
itemShelvingSaleDialogRef.value.show();
};
const updateQuantity = async (sale) => {
try {
if (oldQuantity.value === sale.quantity) return;
const params = {
quantity: sale.quantity,
};
await axios.patch(`ItemShelvingSales/${sale.id}`, params);
oldQuantity.value = null;
} catch (error) {
console.error(error);
}
};
const updateParking = async (sale) => {
try {
const filter = {
fields: ['id'],
where: {
code: sale.shelvingFk,
},
};
const { data } = await axios.get(`Shelvings/findOne`, {
params: { filter: JSON.stringify(filter) },
});
const params = {
parkingFk: sale.parkingFk,
};
await axios.patch(`Shelvings/${data.id}`, params);
} catch (error) {
console.error(error);
}
};
const updateShelving = async (sale) => {
const params = {
shelvingFk: sale.shelvingFk,
};
const { data: patchResponseData } = await axios.patch(
`ItemShelvings/${sale.itemShelvingFk}`,
params
);
const filter = {
fields: ['parkingFk'],
where: {
code: patchResponseData.shelvingFk,
},
};
const { data: getResponseData } = await axios.get(`Shelvings/findOne`, { filter });
sale.parkingFk = getResponseData.parkingFk;
};
const saleTrackingNew = async (sale, stateCode, isChecked) => {
try {
const params = {
saleFk: sale.saleFk,
isChecked,
quantity: sale.quantity,
stateCode,
};
await axios.post(`SaleTrackings/new`, params);
notify(t('globals.dataSaved'), 'positive');
} catch (error) {
console.error(error);
}
};
const saleTrackingDel = async ({ saleFk }, stateCode) => {
try {
const params = {
saleFk,
stateCodes: [stateCode],
};
await axios.post(`SaleTrackings/delete`, params);
notify(t('globals.dataSaved'), 'positive');
} catch (error) {
console.error(error);
}
};
const clickSaleGroupDetail = async (sale) => {
try {
if (!sale.saleGroupDetailFk) return;
await axios.delete(`SaleGroupDetails/${sale.saleGroupDetailFk}`);
sale.hasSaleGroupDetail = false;
notify(t('globals.dataSaved'), 'positive');
} catch (error) {
console.error(error);
}
};
const clickPreviousSelected = (sale) => {
try {
qCheckBoxController(sale, 'isPreviousSelected');
if (!sale.isPreviousSelected) sale.isPrevious = false;
} catch (error) {
console.error(error);
}
};
const clickPrevious = (sale) => {
try {
qCheckBoxController(sale, 'isPrevious');
if (sale.isPrevious) sale.isPreviousSelected = true;
} catch (error) {
console.error(error);
}
};
const qCheckBoxController = (sale, action) => {
const STATE_CODES = {
isControled: 'CHECKED',
isPrepared: 'PREPARED',
isPrevious: 'PREVIOUS_PREPARATION',
isPreviousSelected: 'PREVIOUS_PREPARATION',
};
const stateCode = STATE_CODES[action];
try {
if (!sale[action]) {
saleTrackingNew(sale, stateCode, true);
sale[action] = true;
} else {
saleTrackingDel(sale, stateCode);
sale[action] = false;
}
} catch (error) {
console.error(error);
}
};
</script>
<template>
<FetchData
ref="saleTrackingFetchDataRef"
:url="saleTrackingUrl"
:filter="{ order: ['concept ASC', 'quantity DESC'] }"
auto-load
@on-fetch="(data) => (sales = data)"
/>
<FetchData
url="Shelvings"
auto-load
@on-fetch="(data) => (shelvingsOptions = data)"
/>
<FetchData url="Parkings" auto-load @on-fetch="(data) => (parkingsOptions = data)" />
<QTable
:rows="sales"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
>
<template #body-cell-isChecked="{ row }">
<QTd @click.stop>
<QCheckbox
:model-value="!!row.hasSaleGroupDetail"
color="pink"
class="pink"
:toggle-indeterminate="false"
@update:model-value="clickSaleGroupDetail(row)"
>
<QTooltip>
{{ t('ticketSaleTracking.saleGroupDetail') }}
</QTooltip>
</QCheckbox>
<QCheckbox
:model-value="!!row.isPreviousSelected"
color="info"
class="info"
:toggle-indeterminate="false"
@update:model-value="clickPreviousSelected(row)"
>
<QTooltip>
{{ t('ticketSaleTracking.previousSelected') }}
</QTooltip>
</QCheckbox>
<QCheckbox
:model-value="!!row.isPrevious"
color="cyan"
class="cyan"
:toggle-indeterminate="false"
@update:model-value="clickPrevious(row)"
>
<QTooltip>
{{ t('ticketSaleTracking.previous') }}
</QTooltip>
</QCheckbox>
<QCheckbox
:model-value="!!row.isPrepared"
color="warning"
class="warning"
:toggle-indeterminate="false"
@update:model-value="qCheckBoxController(row, 'isPrepared')"
>
<QTooltip>
{{ t('ticketSaleTracking.prepared') }}
</QTooltip>
</QCheckbox>
<QCheckbox
:model-value="!!row.isControled"
color="yellow"
class="yellow"
:toggle-indeterminate="false"
@update:model-value="qCheckBoxController(row, 'isControled')"
>
<QTooltip>
{{ t('ticketSaleTracking.checked') }}
</QTooltip>
</QCheckbox>
</QTd>
</template>
<template #body-cell-item="{ row }">
<QTd @click.stop>
<div>
<QBtn flat color="primary">
{{ row.itemFk }}
</QBtn>
<ItemDescriptorProxy :id="row.itemFk" />
</div>
</QTd>
</template>
<template #body-cell-description="{ row }">
<QTd class="col">
<div class="column">
<span>{{ row.concept }}</span>
<span v-if="row.subName" class="color-vn-label">
{{ row.subName }}
</span>
<FetchedTags :item="row" :max-length="6" tag="value" />
</div>
</QTd>
</template>
<template #body-cell-actions="{ row }">
<QTd>
<QBtn
@click.stop="showLog(row)"
color="primary"
icon="history"
size="md"
flat
>
<QTooltip class="text-no-wrap">
{{ t('ticketSaleTracking.historyAction') }}
</QTooltip>
</QBtn>
<QBtn
@click.stop="showShelving(row)"
color="primary"
icon="vn:inventory"
size="md"
flat
>
<QTooltip class="text-no-wrap">
{{ t('ticketSaleTracking.shelvingAction') }}
</QTooltip>
</QBtn>
</QTd>
</template>
</QTable>
<QDialog
ref="saleTrackingTableDialogRef"
transition-show="scale"
transition-hide="scale"
>
<QTable
data-key="saleTrackingLog"
:rows="saleTrackings"
:columns="logTableColumns"
class="q-pa-sm"
>
<template #body-cell-worker="{ row }">
<QTd auto-width>
<QBtn flat dense color="primary">{{ row.name }}</QBtn>
<WorkerDescriptorProxy :id="row.workerFk" />
</QTd>
</template>
</QTable>
</QDialog>
<QDialog
ref="itemShelvingSaleDialogRef"
transition-show="scale"
transition-hide="scale"
>
<QTable
data-key="itemShelvingsSales"
:rows="itemShelvignsSales"
:columns="shelvingsTableColumns"
class="q-pa-sm"
>
<template #body-cell-quantity="{ row }">
<QTd auto-width>
<VnInput
v-model.number="row.quantity"
@keyup.enter="updateQuantity(row)"
@blur="updateQuantity(row)"
@focus="oldQuantity = row.quantity"
/>
</QTd>
</template>
<template #body-cell-worker="{ row }">
<QTd auto-width>
<QBtn flat dense color="primary">{{ row.name }}</QBtn>
<WorkerDescriptorProxy :id="row.userFk" />
</QTd>
</template>
<template #body-cell-shelving="{ row }">
<QTd auto-width>
<VnSelect
:options="shelvingsOptions"
hide-selected
option-label="code"
option-value="code"
v-model="row.shelvingFk"
@update:model-value="updateShelving(row)"
style="max-width: 120px"
/>
</QTd>
</template>
<template #body-cell-parking="{ row }">
<QTd auto-width>
<VnSelect
:options="parkingsOptions"
hide-selected
option-label="code"
option-value="id"
v-model="row.parkingFk"
@update:model-value="updateParking(row)"
style="max-width: 120px"
/>
</QTd>
</template>
</QTable>
</QDialog>
</template>
<style lang="scss">
$estados: (
'info': var(--q-info),
'warning': var(--q-warning),
'cyan': #00bcd4,
'pink': pink,
'yellow': #ffeb3b,
);
@each $estado, $color in $estados {
.q-checkbox.#{$estado} {
> .q-checkbox__inner > .q-checkbox__bg.absolute {
border-radius: 50% !important;
& .q-checkbox__svg > .q-checkbox__truthy {
stroke: $color;
}
}
}
}
</style>

View File

@ -9,6 +9,8 @@ import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.v
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 VnProgress from 'src/components/common/VnProgressModal.vue'; import VnProgress from 'src/components/common/VnProgressModal.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import TicketAdvanceFilter from './TicketAdvanceFilter.vue';
import { dashIfEmpty, toCurrency } from 'src/filters'; import { dashIfEmpty, toCurrency } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
@ -42,9 +44,9 @@ const exprBuilder = (param, value) => {
case 'notMovableLines': case 'notMovableLines':
case 'futureZoneFk': case 'futureZoneFk':
return { [param]: value }; return { [param]: value };
case 'ipt': case 'iptColFilter':
return { ipt: { like: `%${value}%` } }; return { ipt: { like: `%${value}%` } };
case 'futureIpt': case 'futureIptColFilter':
return { futureIpt: { like: `%${value}%` } }; return { futureIpt: { like: `%${value}%` } };
} }
}; };
@ -55,6 +57,7 @@ const arrayData = useArrayData('AdvanceTickets', {
url: 'Tickets/getTicketsAdvance', url: 'Tickets/getTicketsAdvance',
userParams: userParams, userParams: userParams,
exprBuilder: exprBuilder, exprBuilder: exprBuilder,
limit: 0,
}); });
const { store } = arrayData; const { store } = arrayData;
const tickets = computed(() => const tickets = computed(() =>
@ -110,7 +113,7 @@ const ticketColumns = computed(() => [
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: VnSelect,
filterParamKey: 'ipt', filterParamKey: 'iptColFilter',
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -194,7 +197,7 @@ const ticketColumns = computed(() => [
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: VnSelect,
filterParamKey: 'futureIpt', filterParamKey: 'futureIptColFilter',
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -451,7 +454,8 @@ onMounted(async () => {
userParams.dateFuture = tomorrow; userParams.dateFuture = tomorrow;
userParams.dateToAdvance = today; userParams.dateToAdvance = today;
userParams.warehouseFk = user.value.warehouseFk; userParams.warehouseFk = user.value.warehouseFk;
await arrayData.addFilter({ userParams }); const filter = { limit: 0 };
await arrayData.addFilter({ filter, userParams });
}); });
</script> </script>
@ -521,6 +525,11 @@ onMounted(async () => {
</QBtn> </QBtn>
</template> </template>
</VnSubToolbar> </VnSubToolbar>
<RightMenu>
<template #right-panel>
<TicketAdvanceFilter data-key="AdvanceTickets" />
</template>
</RightMenu>
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<QTable <QTable
:rows="tickets" :rows="tickets"

View File

@ -0,0 +1,173 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import axios from 'axios';
import { onMounted } from 'vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
exprBuilder: {
type: Function,
default: null,
},
});
const warehousesOptions = ref([]);
const itemPackingTypes = ref([]);
const getItemPackingTypes = async () => {
try {
const filter = {
where: { isActive: true },
};
const { data } = await axios.get('ItemPackingTypes', {
params: { filter: JSON.stringify(filter) },
});
itemPackingTypes.value = data.map((ipt) => ({
description: t(ipt.description),
code: ipt.code,
}));
} catch (error) {
console.error(error);
}
};
onMounted(async () => await getItemPackingTypes());
</script>
<template>
<FetchData
url="Warehouses"
@on-fetch="(data) => (warehousesOptions = data)"
auto-load
/>
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
:hidden-tags="['search']"
:unremovable-params="['warehouseFk', 'dateFuture', 'dateToAdvance']"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<QItem class="q-my-sm">
<QItemSection>
<VnInputDate
v-model="params.dateFuture"
:label="t('params.dateFuture')"
is-outlined
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem class="q-my-sm">
<QItemSection>
<VnInputDate
v-model="params.dateToAdvance"
:label="t('params.dateToAdvance')"
is-outlined
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.futureIpt')"
v-model="params.futureIpt"
:options="itemPackingTypes"
option-value="code"
option-label="description"
:info="t('iptInfo')"
@update:model-value="searchFn()"
dense
outlined
rounded
>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.ipt')"
v-model="params.ipt"
:options="itemPackingTypes"
option-value="code"
option-label="description"
:info="t('iptInfo')"
@update:model-value="searchFn()"
dense
outlined
rounded
>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('params.itemPackingTypes')"
v-model="params.itemPackingTypes"
toggle-indeterminate
:false-value="null"
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.warehouseFk')"
v-model="params.warehouseFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
@update:model-value="searchFn()"
dense
outlined
rounded
>
</VnSelect>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
iptInfo: IPT
params:
dateFuture: Origin date
dateToAdvance: Destination date
futureIpt: Origin IPT
ipt: Destination IPT
itemPackingTypes: 100% movable
warehouseFk: Warehouse
es:
Horizontal: Horizontal
Vertical: Vertical
iptInfo: Encajado
params:
dateFuture: fecha origen
dateToAdvance: Fecha destino
futureIpt: IPT Origen
ipt: IPT destino
itemPackingTypes: 100% movible
warehouseFk: Almacén
</i18n>

View File

@ -5,7 +5,6 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.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';
const { t } = useI18n(); const { t } = useI18n();

View File

@ -3,6 +3,7 @@ import { onMounted, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toDate, toCurrency } from 'src/filters/index';
import VnPaginate from 'src/components/ui/VnPaginate.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import TicketSummary from './Card/TicketSummary.vue'; import TicketSummary from './Card/TicketSummary.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';

View File

@ -168,6 +168,24 @@ weeklyTickets:
salesperson: Salesperson salesperson: Salesperson
search: Search weekly tickets search: Search weekly tickets
searchInfo: Search weekly tickets by id or client id searchInfo: Search weekly tickets by id or client id
ticketSaleTracking:
isChecked: Is checked
item: Item
description: Description
quantity: Quantity
parking: Parking
historyAction: Log states
shelvingAction: Shelvings sale
original: Original
worker: Worker
state: State
created: Created
shelving: Shelving
saleGroupDetail: sale group detail
previousSelected: previous selected
previous: previous
prepared: prepared
checked: checked
service: service:
pay: Pay pay: Pay
description: Description description: Description

View File

@ -207,6 +207,24 @@ package:
added: Añadido added: Añadido
addPackage: Añadir embalaje addPackage: Añadir embalaje
removePackage: Quitar embalaje removePackage: Quitar embalaje
ticketSaleTracking:
isChecked: Comprobado
item: Artículo
description: Descripción
quantity: Cantidad
parking: Parking
historyAction: Historial estados
shelvingAction: Carros línea
original: Original
worker: Trabajador
state: Estado
created: Fecha creación
shelving: Matrícula
saleGroupDetail: detalle grupo líneas
previousSelected: previa seleccionado
previous: previa
prepared: preparado
checked: revisado
Search ticket: Buscar tickets Search ticket: Buscar tickets
You can search by ticket id or alias: Puedes buscar por id o alias del ticket You can search by ticket id or alias: Puedes buscar por id o alias del ticket
Select lines to see the options: Selecciona líneas para ver las opciones Select lines to see the options: Selecciona líneas para ver las opciones

View File

@ -1,225 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const warehousesOptions = ref([]);
const continentsOptions = ref([]);
const agenciesOptions = ref([]);
const add = (paramsObj, key) => {
if (paramsObj[key] === undefined) {
paramsObj[key] = 1;
} else {
paramsObj[key]++;
}
};
const decrement = (paramsObj, key) => {
if (paramsObj[key] === 0) return;
paramsObj[key]--;
};
</script>
<template>
<FetchData
url="Warehouses"
@on-fetch="(data) => (warehousesOptions = data)"
auto-load
/>
<FetchData
url="Continents"
@on-fetch="(data) => (continentsOptions = data)"
auto-load
/>
<FetchData
url="AgencyModes"
@on-fetch="(data) => (agenciesOptions = data)"
auto-load
/>
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<QItem>
<QItemSection>
<VnSelect
:label="t('travelFilter.filter.agencyModeFk')"
v-model="params.agencyModeFk"
@update:model-value="searchFn()"
:options="agenciesOptions"
option-value="agencyFk"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('travelFilter.filter.warehouseOutFk')"
v-model="params.warehouseOutFk"
@update:model-value="searchFn()"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('travelFilter.filter.warehouseInFk')"
v-model="params.warehouseInFk"
@update:model-value="searchFn()"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QInput
v-model="params.scopeDays"
type="number"
:label="t('travelFilter.filter.scopeDays')"
dense
outlined
rounded
class="input-number"
>
<template #append>
<QBtn
icon="add"
flat
dense
size="12px"
@click="add(params, 'scopeDays')"
/>
<QBtn
icon="remove"
flat
dense
size="12px"
@click="decrement(params, 'scopeDays')"
/>
</template>
</QInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
v-model="params.landedFrom"
:label="t('params.landedFrom')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.landedTo')"
v-model="params.landedTo"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.continent')"
v-model="params.continent"
@update:model-value="searchFn()"
:options="continentsOptions"
option-value="code"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QInput
v-model="params.totalEntries"
type="number"
:label="t('params.totalEntries')"
dense
outlined
rounded
min="0"
class="input-number"
>
<template #append>
<QBtn
icon="add"
flat
dense
size="12px"
@click="add(params, 'totalEntries')"
/>
<QBtn
icon="remove"
flat
dense
size="12px"
@click="decrement(params, 'totalEntries')"
/>
</template>
</QInput>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
search: Id/Reference
landedFrom: Landed from
landedTo: Landed to
continent: Continent out
totalEntries: Total entries
es:
params:
search: Id/Referencia
landedFrom: Llegada desde
landedTo: Llegada hasta
continent: Cont. Salida
totalEntries: Ent. totales
</i18n>

View File

@ -198,7 +198,6 @@ const columns = computed(() => [
}" }"
order="landed DESC" order="landed DESC"
:columns="columns" :columns="columns"
default-mode="table"
auto-load auto-load
redirect="travel" redirect="travel"
:is-editable="false" :is-editable="false"

View File

@ -1,6 +0,0 @@
travelFilter:
filter:
warehouseOutFk: Warehouse Out
warehouseInFk: Warehouse In
agencyModeFk: Agency
scopeDays: Days onward

View File

@ -1,6 +0,0 @@
travelFilter:
filter:
warehouseInFk: Alm. entrada
warehouseOutFk: Alm. salida
agencyModeFk: Agencia
scopeDays: Días adelante

View File

@ -77,7 +77,6 @@ const columns = computed(() => [
}" }"
order="paymentDate DESC" order="paymentDate DESC"
:columns="columns" :columns="columns"
default-mode="table"
auto-load auto-load
:right-search="false" :right-search="false"
:is-editable="true" :is-editable="true"

View File

@ -116,7 +116,6 @@ const columns = computed(() => [
}" }"
order="id DESC" order="id DESC"
:columns="columns" :columns="columns"
default-mode="table"
auto-load auto-load
:right-search="false" :right-search="false"
:is-editable="true" :is-editable="true"

View File

@ -57,15 +57,6 @@ export default {
}, },
component: () => import('src/pages/Route/RouteRoadmap.vue'), component: () => import('src/pages/Route/RouteRoadmap.vue'),
}, },
{
path: 'roadmap/create',
name: 'RouteRoadmapCreate',
meta: {
title: 'RouteRoadmapCreate',
icon: 'vn:troncales',
},
component: () => import('src/pages/Route/Roadmap/RoadmapCreate.vue'),
},
{ {
path: 'cmr', path: 'cmr',
name: 'CmrList', name: 'CmrList',

View File

@ -14,19 +14,21 @@ export default {
main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'], main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'],
card: [ card: [
'TicketBasicData', 'TicketBasicData',
'TicketPurchaseRequest',
'TicketSale', 'TicketSale',
'TicketLog', 'TicketLog',
'TicketExpedition', 'TicketExpedition',
'TicketService', 'TicketDms',
'TicketVolume', 'TicketPurchaseRequest',
'TicketNotes',
'TicketTracking', 'TicketTracking',
'TicketBoxing', 'TicketNotes',
'TicketSms',
'TicketPicture', 'TicketPicture',
'TicketVolume',
'TicketComponents', 'TicketComponents',
'TicketPackage', 'TicketPackage',
'TicketService',
'TicketSaleTracking',
'TicketBoxing',
'TicketSms',
], ],
}, },
children: [ children: [
@ -173,7 +175,6 @@ export default {
}, },
component: () => import('src/pages/Ticket/Card/TicketVolume.vue'), component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
}, },
{ {
path: 'expedition', path: 'expedition',
name: 'TicketExpedition', name: 'TicketExpedition',
@ -197,7 +198,7 @@ export default {
name: 'TicketPackage', name: 'TicketPackage',
meta: { meta: {
title: 'packages', title: 'packages',
icon: 'vn:bucket', icon: 'vn:bin',
}, },
component: () => import('src/pages/Ticket/Card/TicketPackage.vue'), component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
}, },
@ -210,6 +211,26 @@ export default {
}, },
component: () => import('src/pages/Ticket/Card/TicketComponents.vue'), component: () => import('src/pages/Ticket/Card/TicketComponents.vue'),
}, },
{
path: 'sale-tracking',
name: 'TicketSaleTracking',
meta: {
title: 'saleTracking',
icon: 'assignment',
},
component: () =>
import('src/pages/Ticket/Card/TicketSaleTracking.vue'),
},
{
path: 'dms',
name: 'TicketDms',
meta: {
title: 'dms',
icon: 'cloud_upload',
},
component: () => import('src/pages/Ticket/Card/TicketDms.vue'),
},
{ {
path: 'boxing', path: 'boxing',
name: 'TicketBoxing', name: 'TicketBoxing',

View File

@ -21,9 +21,9 @@ export const useNavigationStore = defineStore('navigationStore', () => {
'route', 'route',
'ticket', 'ticket',
'worker', 'worker',
'account',
'wagon', 'wagon',
'zone', 'zone',
'account',
]; ];
const pinnedModules = ref([]); const pinnedModules = ref([]);
const role = useRole(); const role = useRole();

View File

@ -0,0 +1,15 @@
describe('Route', () => {
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer');
cy.visit(`/#/route/list`);
});
it('Route list create route', () => {
cy.visit(`/#/route/list`);
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.get('input[name="description"]').eq(1).type('routeTestOne{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created');
cy.url().should('include', '/summary');
});
});