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

This commit is contained in:
Javier Segarra 2024-07-08 08:57:51 +00:00
commit 500d582100
29 changed files with 1389 additions and 110 deletions

View File

@ -52,7 +52,7 @@ const toggleMarkAll = (val) => {
const getConfig = async (url, filter) => { const getConfig = async (url, filter) => {
const response = await axios.get(url, { const response = await axios.get(url, {
params: { filter: filter }, params: { filter: JSON.stringify(filter) },
}); });
return response.data && response.data.length > 0 ? response.data[0] : null; return response.data && response.data.length > 0 ? response.data[0] : null;
}; };
@ -60,7 +60,7 @@ const getConfig = async (url, filter) => {
const fetchViewConfigData = async () => { const fetchViewConfigData = async () => {
try { try {
const userConfigFilter = { const userConfigFilter = {
where: { tableCode: $props.tableCode, userFk: user.id }, where: { tableCode: $props.tableCode, userFk: user.value.id },
}; };
const userConfig = await getConfig('UserConfigViews', userConfigFilter); const userConfig = await getConfig('UserConfigViews', userConfigFilter);
@ -74,8 +74,14 @@ const fetchViewConfigData = async () => {
const defaultConfig = await getConfig('DefaultViewConfigs', defaultConfigFilter); const defaultConfig = await getConfig('DefaultViewConfigs', defaultConfigFilter);
if (defaultConfig) { if (defaultConfig) {
// Si el backend devuelve una configuración por defecto la usamos
setUserConfigViewData(defaultConfig.columns); setUserConfigViewData(defaultConfig.columns);
return; return;
} else {
// Si no hay configuración por defecto mostramos todas las columnas
const defaultColumns = {};
$props.allColumns.forEach((col) => (defaultColumns[col] = true));
setUserConfigViewData(defaultColumns);
} }
} catch (err) { } catch (err) {
console.err('Error fetching config view data', err); console.err('Error fetching config view data', err);

View File

@ -39,7 +39,7 @@ const arrayData = useArrayData(props.dataKey, {
onBeforeMount(async () => { onBeforeMount(async () => {
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id }; if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false, updateRouter: false });
}); });
if (props.baseUrl) { if (props.baseUrl) {

View File

@ -7,7 +7,7 @@ import VnImg from 'src/components/ui/VnImg.vue';
import OrderCatalogItemDialog from 'pages/Order/Card/OrderCatalogItemDialog.vue'; import OrderCatalogItemDialog from 'pages/Order/Card/OrderCatalogItemDialog.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import toCurrency from '../../../filters/toCurrency'; import { toCurrency } from 'filters/index';
const DEFAULT_PRICE_KG = 0; const DEFAULT_PRICE_KG = 0;
@ -18,6 +18,10 @@ defineProps({
type: Object, type: Object,
required: true, required: true,
}, },
isCatalog: {
type: Boolean,
default: false,
},
}); });
const dialog = ref(null); const dialog = ref(null);
@ -28,7 +32,7 @@ const dialog = ref(null);
<QCard class="card shadow-6"> <QCard class="card shadow-6">
<div class="img-wrapper"> <div class="img-wrapper">
<VnImg :id="item.id" zoom-size="lg" class="image" /> <VnImg :id="item.id" zoom-size="lg" class="image" />
<div v-if="item.hex" class="item-color-container"> <div v-if="item.hex && isCatalog" class="item-color-container">
<div <div
class="item-color" class="item-color"
:style="{ backgroundColor: `#${item.hex}` }" :style="{ backgroundColor: `#${item.hex}` }"
@ -50,11 +54,12 @@ const dialog = ref(null);
</template> </template>
<div class="footer"> <div class="footer">
<div class="price"> <div class="price">
<p> <p v-if="isCatalog">
{{ item.available }} {{ t('to') }} {{ item.available }} {{ t('to') }}
{{ toCurrency(item.price) }} {{ toCurrency(item.price) }}
</p> </p>
<QIcon name="add_circle" class="icon"> <slot name="price" />
<QIcon v-if="isCatalog" name="add_circle" class="icon">
<QTooltip>{{ t('globals.add') }}</QTooltip> <QTooltip>{{ t('globals.add') }}</QTooltip>
<QPopupProxy ref="dialog"> <QPopupProxy ref="dialog">
<OrderCatalogItemDialog <OrderCatalogItemDialog

View File

@ -112,9 +112,7 @@ async function search(evt) {
isLoading.value = true; isLoading.value = true;
const filter = { ...userParams.value }; const filter = { ...userParams.value };
store.userParamsChanged = true; store.userParamsChanged = true;
store.filter.skip = 0; arrayData.reset(['skip', 'filter.skip', 'page']);
store.skip = 0;
store.page = 1;
const { params: newParams } = await arrayData.addFilter({ params: userParams.value }); const { params: newParams } = await arrayData.addFilter({ params: userParams.value });
userParams.value = newParams; userParams.value = newParams;
@ -138,9 +136,7 @@ async function reload() {
async function clearFilters() { async function clearFilters() {
isLoading.value = true; isLoading.value = true;
store.userParamsChanged = true; store.userParamsChanged = true;
store.filter.skip = 0; arrayData.reset(['skip', 'filter.skip', 'page']);
store.skip = 0;
store.page = 1;
// 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)

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { onMounted, ref, watch } from 'vue'; import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
@ -95,6 +95,8 @@ onMounted(async () => {
mounted.value = true; mounted.value = true;
}); });
onBeforeUnmount(() => arrayData.reset());
watch( watch(
() => props.data, () => props.data,
() => { () => {
@ -118,8 +120,7 @@ const addFilter = async (filter, params) => {
async function fetch(params) { async function fetch(params) {
useArrayData(props.dataKey, params); useArrayData(props.dataKey, params);
store.filter.skip = 0; arrayData.reset(['filter.skip', 'skip']);
store.skip = 0;
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
if (!store.hasMoreData) { if (!store.hasMoreData) {
isLoading.value = false; isLoading.value = false;

View File

@ -103,8 +103,7 @@ async function search() {
const staticParams = Object.entries(store.userParams).filter( const staticParams = Object.entries(store.userParams).filter(
([key, value]) => value && (props.staticParams || []).includes(key) ([key, value]) => value && (props.staticParams || []).includes(key)
); );
store.skip = 0; arrayData.reset(['skip', 'page']);
store.page = 1;
if (props.makeFetch) if (props.makeFetch)
await arrayData.applyFilter({ await arrayData.applyFilter({

View File

@ -18,7 +18,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
onMounted(() => { onMounted(() => {
setOptions(); setOptions();
store.skip = 0; arrayDataStore.reset(['skip']);
const query = route.query; const query = route.query;
const searchUrl = store.searchUrl; const searchUrl = store.searchUrl;
if (query[searchUrl]) { if (query[searchUrl]) {
@ -84,8 +85,12 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
} }
Object.assign(filter, store.userFilter, exprFilter); Object.assign(filter, store.userFilter, exprFilter);
Object.assign(store.filter, filter); let where;
const params = { filter: store.filter }; if (filter?.where || store.filter?.where)
where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
Object.assign(filter, store.filter);
filter.where = where;
const params = { filter };
Object.assign(params, userParams); Object.assign(params, userParams);
params.filter.skip = store.skip; params.filter.skip = store.skip;
@ -126,6 +131,10 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
delete store[option]; delete store[option];
} }
function reset(opts = []) {
if (arrayDataStore.get(key)) arrayDataStore.reset(key, opts);
}
function cancelRequest() { function cancelRequest() {
if (canceller) { if (canceller) {
canceller.abort(); canceller.abort();
@ -143,21 +152,20 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
} }
async function addFilter({ filter, params }) { async function addFilter({ filter, params }) {
if (filter) store.userFilter = Object.assign(store.userFilter, filter); if (filter) store.filter = filter;
let userParams = { ...store.userParams, ...params }; let userParams = { ...store.userParams, ...params };
userParams = sanitizerParams(userParams, store?.exprBuilder); userParams = sanitizerParams(userParams, store?.exprBuilder);
store.userParams = userParams; store.userParams = userParams;
store.skip = 0; arrayDataStore.reset(['skip', 'filter.skip', 'page']);
store.filter.skip = 0;
store.page = 1;
await fetch({ append: false }); await fetch({ append: false });
return { filter, params }; return { filter, params };
} }
async function addFilterWhere(where) { async function addFilterWhere(where) {
const storedFilter = { ...store.userFilter }; const storedFilter = { ...store.filter };
if (!storedFilter?.where) storedFilter.where = {}; if (!storedFilter?.where) storedFilter.where = {};
where = { ...storedFilter.where, ...where }; where = { ...storedFilter.where, ...where };
await addFilter({ filter: { where } }); await addFilter({ filter: { where } });
@ -187,7 +195,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
store.page += 1; store.page += 1;
await fetch({ append: true }); await fetch({ append: true });
updateStateParams();
} }
async function refresh() { async function refresh() {
@ -241,5 +248,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
updateStateParams, updateStateParams,
isLoading, isLoading,
deleteOption, deleteOption,
reset,
}; };
} }

View File

@ -550,10 +550,14 @@ ticket:
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 services: Service
tracking: Tracking tracking: Tracking
components: Components
pictures: Pictures
packages: Packages
list: list:
nickname: Nickname nickname: Nickname
state: State state: State

View File

@ -239,6 +239,13 @@ globals:
mailForwarding: Reenvío de correo mailForwarding: Reenvío de correo
mailAlias: Alias de correo mailAlias: Alias de correo
privileges: Privilegios privileges: Privilegios
observation: Notas
expedition: Expedición
services: Servicios
tracking: Estados
components: Componentes
pictures: Fotos
packages: Bultos
created: Fecha creación created: Fecha creación
worker: Trabajador worker: Trabajador
now: Ahora now: Ahora
@ -548,10 +555,14 @@ ticket:
observation: Notas observation: Notas
ticketAdvance: Adelantar tickets ticketAdvance: Adelantar tickets
futureTickets: Tickets a futuro futureTickets: Tickets a futuro
expedition: Expedición
purchaseRequest: Petición de compra purchaseRequest: Petición de compra
weeklyTickets: Tickets programados weeklyTickets: Tickets programados
services: Servicios services: Servicios
tracking: Estados tracking: Estados
components: Componentes
pictures: Fotos
packages: Bultos
list: list:
nickname: Alias nickname: Alias
state: Estado state: Estado

View File

@ -13,7 +13,7 @@ const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const stateStore = computed(() => useStateStore()); const stateStore = computed(() => useStateStore());
const rows = ref([]); const rows = ref([]);
const totalAmount = ref(0); const totalAmount = ref();
const filter = { const filter = {
include: [ include: [
@ -75,7 +75,7 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
field: (value) => value.user.name, field: (value) => value?.user?.name,
label: t('Created by'), label: t('Created by'),
name: 'createdBy', name: 'createdBy',
}, },
@ -87,7 +87,7 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
field: (value) => value.greugeType.name, field: (value) => value?.greugeType?.name,
label: t('Type'), label: t('Type'),
name: 'type', name: 'type',
}, },
@ -108,26 +108,9 @@ const setRows = (data) => {
<template> <template>
<FetchData :filter="filter" @on-fetch="setRows" auto-load url="greuges" /> <FetchData :filter="filter" @on-fetch="setRows" auto-load url="greuges" />
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#actions-append">
<div class="row q-gutter-x-sm">
<QBtn
flat
@click="stateStore.toggleRightDrawer()"
round
dense
icon="menu"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }}
</QTooltip>
</QBtn>
</div>
</Teleport>
</template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="300" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="300" show-if-above>
<QCard class="full-width q-pa-sm"> <QCard class="full-width q-pa-sm">
<h6 class="flex justify-end q-my-lg q-pr-lg" v-if="totalAmount"> <h6 class="flex justify-end q-my-lg q-pr-lg" v-if="totalAmount !== undefined">
<span class="color-vn-label q-mr-md">{{ t('Total') }}:</span> <span class="color-vn-label q-mr-md">{{ t('Total') }}:</span>
{{ toCurrency(totalAmount) }} {{ toCurrency(totalAmount) }}
</h6> </h6>

View File

@ -408,7 +408,6 @@ function handleLocation(data, location) {
default-mode="table" default-mode="table"
redirect="customer" redirect="customer"
auto-load auto-load
:disable-option="{ card: true }"
> >
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnLocation <VnLocation

View File

@ -4,7 +4,7 @@ import { useRoute } from 'vue-router';
import { onMounted, onUnmounted, ref } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
import OrderCatalogItem from 'pages/Order/Card/OrderCatalogItem.vue'; import CatalogItem from 'components/ui/CatalogItem.vue';
import OrderCatalogFilter from 'pages/Order/Card/OrderCatalogFilter.vue'; import OrderCatalogFilter from 'pages/Order/Card/OrderCatalogFilter.vue';
const route = useRoute(); const route = useRoute();
@ -77,7 +77,12 @@ function extractValueTags(items) {
<div v-if="rows && !rows?.length" class="no-result"> <div v-if="rows && !rows?.length" class="no-result">
{{ t('globals.noResults') }} {{ t('globals.noResults') }}
</div> </div>
<OrderCatalogItem v-for="row in rows" :key="row.id" :item="row" /> <CatalogItem
v-for="row in rows"
:key="row.id"
:item="row"
is-catalog
/>
</div> </div>
</template> </template>
</VnPaginate> </VnPaginate>

View File

@ -0,0 +1,78 @@
<script setup>
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue';
import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
const $props = defineProps({
ticket: {
type: Boolean,
default: false,
},
withRoute: {
type: Boolean,
default: false,
},
selectedExpeditions: {
type: Array,
default: () => [],
},
});
const { t } = useI18n();
const router = useRouter();
const { notify } = useNotify();
const newTicketFormData = reactive({});
const createTicket = async () => {
try {
const expeditionIds = $props.selectedExpeditions.map(
(expedition) => expedition.id
);
const params = {
clientId: $props.ticket.clientFk,
landed: newTicketFormData.landed,
warehouseId: $props.ticket.warehouseFk,
addressId: $props.ticket.addressFk,
agencyModeId: $props.ticket.agencyModeFk,
routeId: newTicketFormData.routeFk,
expeditionIds: expeditionIds,
};
const { data } = await axios.post('Expeditions/moveExpeditions', params);
notify(t('globals.dataSaved'), 'positive');
router.push({ name: 'TicketSummary', params: { id: data.id } });
} catch (error) {
console.error(error);
}
};
</script>
<template>
<FormModelPopup
model="expeditionNewTicket"
:form-initial-data="newTicketFormData"
:save-fn="createTicket"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInputDate :label="t('expedition.landed')" v-model="data.landed" />
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
v-if="withRoute"
:label="t('expedition.routeId')"
v-model="data.routeFk"
/>
</VnRow>
</template>
</FormModelPopup>
</template>

View File

@ -0,0 +1,354 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import FetchData from 'components/FetchData.vue';
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import { useStateStore } from 'stores/useStateStore';
import { dashIfEmpty } from 'src/filters';
import { useArrayData } from 'composables/useArrayData';
import { toCurrency } from 'filters/index';
import axios from 'axios';
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
const salesRef = ref(null);
const arrayData = useArrayData('ticketData');
const { store } = arrayData;
const ticketData = computed(() => store.data);
const components = ref([]);
const componentsList = ref([]);
const theoricalCost = ref(0);
const ticketVolume = ref(null);
watch(
() => ticketData.value,
async () => {
await nextTick();
salesRef.value.fetch();
getComponentsSum();
getTheoricalCost();
if (ticketData.value?.zone && ticketData.value?.zone?.isVolumetric)
getTicketVolume();
},
{ immediate: true }
);
const salesFilter = computed(() => ({
order: 'concept ASC',
include: [
{
relation: 'item',
},
{
relation: 'components',
scope: {
fields: ['componentFk', 'value'],
include: {
relation: 'component',
scope: {
fields: ['typeFk', 'name'],
include: {
relation: 'componentType',
scope: {
fields: ['type', 'isBase', 'name'],
},
},
},
},
},
},
],
where: { ticketFk: route.params.id },
}));
const columns = computed(() => [
{
label: t('components.item'),
name: 'item',
align: 'left',
},
{
label: t('components.description'),
name: 'description',
align: 'left',
},
{
label: t('components.quantity'),
name: 'quantity',
field: 'quantity',
align: 'left',
format: (val) => dashIfEmpty(val),
},
{
label: t('components.serie'),
name: 'serie',
align: 'left',
},
{
label: t('components.components'),
name: 'components',
align: 'left',
},
{
label: t('components.import'),
name: 'import',
align: 'left',
},
{
label: t('components.total'),
name: 'total',
align: 'left',
},
]);
const getBase = computed(() => {
let sum = 0;
for (let sale of components.value) {
for (let saleComponent of sale.components) {
if (saleComponent.component.componentType.isBase) {
sum += sale.quantity * saleComponent.value;
}
}
}
return sum;
});
const getTotal = computed(() => {
let total = 0;
for (let sale of components.value) {
for (let saleComponent of sale.components) {
total += sale.quantity * saleComponent.value;
}
}
return total;
});
const getComponentsSum = async () => {
try {
const { data } = await axios.get(`Tickets/${route.params.id}/getComponentsSum`);
componentsList.value = data;
} catch (error) {
console.error(error);
}
};
const getTheoricalCost = async () => {
try {
const { data } = await axios.get(`Tickets/${route.params.id}/freightCost`);
theoricalCost.value = data;
} catch (error) {
console.error(error);
}
};
const getTicketVolume = async () => {
try {
if (!ticketData.value) return;
const { data } = await axios.get(`Tickets/${ticketData.value.id}/getVolume`);
ticketVolume.value = data[0].volume;
} catch (error) {
console.error(error);
}
};
onMounted(() => {
stateStore.rightDrawer = true;
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<FetchData
ref="salesRef"
url="Sales"
:filter="salesFilter"
@on-fetch="(data) => (components = data)"
auto-load
/>
<RightMenu>
<template #right-panel>
<QCard
class="q-pa-sm color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('components.total') }}
</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label"
>{{ t('components.baseToCommission') }}:
</span>
<span>{{ toCurrency(getBase) }}</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label"
>{{ t('components.totalWithoutVat') }}:
</span>
<span>{{ toCurrency(getTotal) }}</span>
</QCardSection>
</QCard>
<QCard
class="q-pa-sm color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('components.components') }}
</span>
</QCardSection>
<QCardSection
v-for="(component, index) in componentsList"
:key="index"
horizontal
>
<span v-if="component.name" class="q-mr-xs color-vn-label">
{{ component.name }}:
</span>
<span v-if="component.value">{{
toCurrency(component.value, 'EUR', 3)
}}</span>
</QCardSection>
</QCard>
<QCard
class="q-pa-sm color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('components.zoneBreakdown') }}
</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('components.price') }}:
</span>
<span>{{ toCurrency(ticketData?.zonePrice, 'EUR', 2) }}</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('components.bonus') }}:
</span>
<span>{{ toCurrency(ticketData?.zoneBonus, 'EUR', 2) }}</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('components.zone') }}:
</span>
<span class="link">
{{ dashIfEmpty(ticketData?.zone?.name) }}
<ZoneDescriptorProxy :id="ticketData?.zone?.id" />
</span>
</QCardSection>
<QCardSection v-if="ticketData?.zone?.isVolumetric" horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('components.volume') }}:
</span>
<span>{{ ticketVolume }}</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('components.packages') }}:
</span>
<span>{{ dashIfEmpty(ticketData?.packages) }}</span>
</QCardSection>
</QCard>
<QCard
class="q-pa-sm color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('components.theoricalCost') }}
</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('components.totalPrice') }}:
</span>
<span>{{ toCurrency(theoricalCost, 'EUR', 2) }}</span>
</QCardSection>
</QCard>
</template>
</RightMenu>
<QTable
:rows="components"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
>
<template #body-cell-item="{ row }">
<QTd>
<QBtn flat color="primary">
<span class="link">{{ row.itemFk }}</span>
<ItemDescriptorProxy :id="row.itemFk" />
</QBtn>
</QTd>
</template>
<template #body-cell-description="{ row }">
<QTd>
<div class="column">
<span>{{ row.item.name }}</span>
<span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" :max-length="6" />
</div>
</QTd>
</template>
<template #body-cell-serie="{ row }">
<QTd>
<div class="column">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ saleComponent.component?.componentType?.name }}
</span>
</div>
</QTd>
</template>
<template #body-cell-components="{ row }">
<QTd>
<div class="column">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ saleComponent.component?.name }}
</span>
</div>
</QTd>
</template>
<template #body-cell-import="{ row }">
<QTd>
<div class="column text-right">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ toCurrency(saleComponent.value, 'EUR', 3) }}
</span>
</div>
</QTd>
</template>
<template #body-cell-total="{ row }">
<QTd>
<div class="column text-right">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ toCurrency(saleComponent.value * row.quantity, 'EUR', 3) }}
</span>
</div>
</QTd>
</template>
</QTable>
</template>

View File

@ -66,6 +66,23 @@ const filter = {
fields: ['id', 'name'], fields: ['id', 'name'],
}, },
}, },
{
relation: 'zone',
scope: {
fields: [
'agencyModeFk',
'bonus',
'hour',
'id',
'isVolumetric',
'itemMaxSize',
'm3Max',
'name',
'price',
'travelingDays',
],
},
},
], ],
}; };

View File

@ -0,0 +1,474 @@
<script setup>
import { onMounted, ref, computed, onUnmounted, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import TicketEditManaProxy from './TicketEditMana.vue';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import ExpeditionNewTicket from './ExpeditionNewTicket.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency, toPercentage } from 'src/filters';
import { useArrayData } from 'composables/useArrayData';
import { useVnConfirm } from 'composables/useVnConfirm';
import useNotify from 'src/composables/useNotify.js';
import { toDateTimeFormat } from 'src/filters/date';
import axios from 'axios';
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
const { notify } = useNotify();
const { openConfirmationModal } = useVnConfirm();
const editPriceProxyRef = ref(null);
const newTicketDialogRef = ref(null);
const logsTableDialogRef = ref(null);
const expeditionsLogsData = ref([]);
const selectedExpeditions = ref([]);
const allColumnNames = ref([]);
const visibleColumns = ref([]);
const newTicketWithRoute = ref(false);
const itemsOptions = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
case 'expeditionFk':
return { id: value };
case 'packageItemName':
return { packagingItemFk: value };
}
};
const expeditionsFilter = computed(() => ({
where: { ticketFk: route.params.id },
order: ['created DESC'],
}));
const expeditionsArrayData = useArrayData('ticketExpeditions', {
url: 'Expeditions/filter',
filter: expeditionsFilter.value,
exprBuilder: exprBuilder,
});
const expeditionsStore = expeditionsArrayData.store;
const ticketExpeditions = computed(() => expeditionsStore.data);
const ticketArrayData = useArrayData('ticketData');
const ticketStore = ticketArrayData.store;
const ticketData = computed(() => ticketStore.data);
const refetchExpeditions = async () => {
await expeditionsArrayData.applyFilter({
params: { filter: JSON.stringify(expeditionsFilter.value) },
});
};
watch(
() => route.params.id,
async () => await refetchExpeditions(),
{ immediate: true }
);
const params = reactive({});
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await expeditionsArrayData.addFilter({ params });
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const columns = computed(() => [
{
label: t('expedition.id'),
name: 'id',
field: 'id',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterParamKey: 'expeditionFk',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('expedition.item'),
name: 'item',
align: 'left',
columnFilter: {
component: VnInput,
type: 'text',
filterParamKey: 'packageItemName',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('expedition.name'),
name: 'name',
field: 'packageItemName',
align: 'left',
columnFilter: {
component: VnSelect,
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: itemsOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{
label: t('expedition.packageType'),
name: 'packageType',
field: 'freightItemName',
align: 'left',
columnFilter: {
component: VnInput,
type: 'text',
// filterParamKey: 'expeditionFk',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('expedition.counter'),
name: 'counter',
field: 'counter',
align: 'left',
columnFilter: null,
},
{
label: t('expedition.externalId'),
name: 'externalId',
field: 'externalId',
align: 'left',
columnFilter: null,
},
{
label: t('expedition.created'),
name: 'created',
field: 'created',
align: 'left',
columnFilter: null,
format: (value) => toDateTimeFormat(value),
},
{
label: t('expedition.state'),
name: 'state',
field: 'state',
align: 'left',
columnFilter: null,
},
{
label: '',
name: 'history',
align: 'left',
columnFilter: null,
},
]);
const logTableColumns = computed(() => [
{
label: t('expedition.state'),
name: 'state',
field: 'state',
align: 'left',
sortable: true,
},
{
label: t('expedition.name'),
name: 'name',
align: 'name',
columnFilter: null,
},
{
label: t('expedition.created'),
name: 'created',
field: 'created',
align: 'left',
columnFilter: null,
format: (value) => toDateTimeFormat(value),
},
]);
const showNewTicketDialog = (withRoute = false) => {
newTicketWithRoute.value = withRoute;
newTicketDialogRef.value.show();
};
const deleteExpedition = async () => {
try {
const expeditionIds = selectedExpeditions.value.map(
(expedition) => expedition.id
);
const params = { expeditionIds };
await axios.post('Expeditions/deleteExpeditions', params);
await refetchExpeditions();
selectedExpeditions.value = [];
notify(t('expedition.expeditionRemoved'), 'positive');
} catch (error) {
console.error(error);
}
};
const showLog = async (expedition) => {
await getExpeditionState(expedition);
logsTableDialogRef.value.show();
};
const getExpeditionState = async (expedition) => {
try {
const filter = {
where: { expeditionFk: expedition.id },
order: ['created DESC'],
};
const { data } = await axios.get(`ExpeditionStates/filter`, {
params: { filter: JSON.stringify(filter) },
});
expeditionsLogsData.value = data;
} catch (error) {
console.error(error);
}
};
onMounted(async () => {
stateStore.rightDrawer = true;
const filteredColumns = columns.value.filter((col) => col.name !== 'history');
allColumnNames.value = filteredColumns.map((col) => col.name);
// await expeditionsArrayData.fetch({ append: false });
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<FetchData
url="Items"
auto-load
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
@on-fetch="(data) => (itemsOptions = data)"
/>
<VnSubToolbar>
<template #st-data>
<TableVisibleColumns
:all-columns="allColumnNames"
table-code="expeditionIndex"
labels-traductions-path="expedition"
@on-config-saved="visibleColumns = [...$event, 'history']"
/>
</template>
<template #st-actions>
<QBtnGroup push class="q-gutter-x-sm" flat>
<QBtnDropdown
ref="btnDropdownRef"
color="primary"
:label="t('expedition.move')"
:disable="!selectedExpeditions.length"
>
<template #label>
<QTooltip>{{ t('Select lines to see the options') }}</QTooltip>
</template>
<QList>
<QItem
clickable
v-close-popup
v-ripple
@click="showNewTicketDialog(false)"
>
<QItemSection>
<QItemLabel>{{
t('expedition.newTicketWithoutRoute')
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem
clickable
v-close-popup
v-ripple
@click="showNewTicketDialog(true)"
>
<QItemSection>
<QItemLabel>{{
t('expedition.newTicketWithRoute')
}}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QBtnDropdown>
<QBtn
:disable="!selectedExpeditions.length"
icon="delete"
color="primary"
@click="
openConfirmationModal(
'',
t('expedition.removeExpeditionSubtitle'),
deleteExpedition
)
"
/>
</QBtnGroup>
</template>
</VnSubToolbar>
<QTable
:rows="ticketExpeditions"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
selection="multiple"
v-model:selected="selectedExpeditions"
:visible-columns="visibleColumns"
:no-data-label="t('globals.noResults')"
>
<template #top-row="{ cols }">
<QTr>
<QTd />
<QTd v-for="(col, index) in cols" :key="index" style="max-width: 100px">
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #body-cell-item="{ row }">
<QTd auto-width @click.stop>
<QBtn flat color="primary">{{ row.packagingItemFk }}</QBtn>
<ItemDescriptorProxy :id="row.packagingItemFk" />
</QTd>
</template>
<template #body-cell-available="{ row }">
<QTd @click.stop>
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
{{ row.available }}
</QBadge>
</QTd>
</template>
<template #body-cell-price="{ row }">
<QTd>
<template v-if="isTicketEditable && row.id">
<QBtn flat color="primary" dense @click="onOpenEditPricePopover(row)">
{{ toCurrency(row.price) }}
</QBtn>
<TicketEditManaProxy
ref="editPriceProxyRef"
:mana="mana"
:new-price="getNewPrice"
@save="updatePrice(row)"
>
<VnInput
v-model.number="edit.price"
:label="t('ticketSale.price')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toCurrency(row.price) }}</span>
</QTd>
</template>
<template #body-cell-discount="{ row }">
<QTd>
<template v-if="!isLocked && row.id">
<QBtn
flat
color="primary"
dense
@click="onOpenEditDiscountPopover(row)"
>
{{ toPercentage(row.discount / 100) }}
</QBtn>
<TicketEditManaProxy
:mana="mana"
:new-price="getNewPrice"
@save="changeDiscount(row)"
>
<VnInput
v-model.number="edit.discount"
:label="t('ticketSale.discount')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span>
</QTd>
</template>
<template #body-cell-history="{ row }">
<QTd>
<QBtn
@click.stop="showLog(row)"
color="primary"
icon="history"
size="md"
flat
>
<QTooltip class="text-no-wrap">
{{ t('expedition.historyAction') }}
</QTooltip>
</QBtn>
</QTd>
</template>
</QTable>
<QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale">
<ExpeditionNewTicket
:ticket="ticketData"
:with-route="newTicketWithRoute"
:selected-expeditions="selectedExpeditions"
/>
</QDialog>
<QDialog ref="logsTableDialogRef" transition-show="scale" transition-hide="scale">
<QTable
ref="tableRef"
data-key="TicketExpeditionLog"
:rows="expeditionsLogsData"
:columns="logTableColumns"
class="q-pa-sm"
>
<template #body-cell-name="{ row }">
<QTd auto-width>
<QBtn flat dense color="primary">{{ row.name }}</QBtn>
<WorkerDescriptorProxy :id="row.workerFk" />
</QTd>
</template>
</QTable>
</QDialog>
</template>

View File

@ -0,0 +1,132 @@
<script setup>
import { ref, watch, computed, reactive } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CrudModel from 'components/CrudModel.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import { useArrayData } from 'src/composables/useArrayData';
const route = useRoute();
const { t } = useI18n();
const ticketPackagingsCrudRef = ref(null);
const listPackagingsOptions = ref([]);
const arrayData = useArrayData('TicketPackagings');
const { store } = arrayData;
const crudModelFilter = reactive({
fields: ['id', 'ticketFk', 'packagingFk', 'quantity', 'created'],
where: { ticketFk: route.params.id },
});
const crudModelRequiredData = computed(() => ({
packagingFk: null,
quantity: 0,
created: Date.vnNew(),
ticketFk: route.params.id,
}));
const handleInputQuantityClear = (row) => {
if (row.quantity === null || row.quantity === undefined) row.quantity = 0;
};
watch(
() => route.params.id,
async () => {
crudModelFilter.where.ticketFk = route.params.id;
store.filter = crudModelFilter;
await ticketPackagingsCrudRef.value.reload();
}
);
</script>
<template>
<FetchData
@on-fetch="(data) => (listPackagingsOptions = data)"
auto-load
:filter="{ fields: ['packagingFk', 'name'], order: 'name ASC', limit: 30 }"
url="Packagings/listPackaging"
/>
<div class="flex justify-center">
<CrudModel
ref="ticketPackagingsCrudRef"
data-key="TicketPackagings"
url="TicketPackagings"
model="TicketPackagings"
:filter="crudModelFilter"
:data-required="crudModelRequiredData"
:default-remove="false"
auto-load
style="max-width: 800px"
>
<template #body="{ rows, validate }">
<QCard class="q-px-lg q-py-md">
<div
v-for="(row, index) in rows"
:key="index"
class="q-mb-md row items-center q-gutter-x-md"
>
<VnSelect
:label="t('package.package')"
:options="listPackagingsOptions"
hide-selected
option-label="name"
option-value="packagingFk"
v-model="row.packagingFk"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name }}
</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.itemFk }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInput
:label="t('package.quantity')"
v-model.number="row.quantity"
class="col"
type="number"
min="1"
:required="true"
@update:model-value="handleInputQuantityClear(row)"
:rules="validate('TicketPackaging.quantity')"
/>
<VnInputDate :label="t('package.added')" v-model="row.created" />
<QIcon
name="delete"
size="sm"
class="cursor-pointer"
color="primary"
@click="ticketPackagingsCrudRef.remove([row])"
>
<QTooltip>
{{ t('package.removePackage') }}
</QTooltip>
</QIcon>
</div>
<QIcon
name="add_circle"
class="fill-icon-on-hover q-ml-md"
size="sm"
color="primary"
@click="ticketPackagingsCrudRef.insert()"
>
<QTooltip>
{{ t('package.addPackage') }}
</QTooltip>
</QIcon>
</QCard>
</template>
</CrudModel>
</div>
</template>

View File

@ -0,0 +1,60 @@
<script setup>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import CatalogItem from 'components/ui/CatalogItem.vue';
import { toCurrency } from 'filters/index';
const { t } = useI18n();
const route = useRoute();
const salesFilter = {
include: {
relation: 'item',
scope: {
field: ['name', 'image'],
},
},
where: { ticketFk: route.params.id },
};
const sales = ref([]);
</script>
<template>
<FetchData
ref="salesRef"
url="sales"
:filter="salesFilter"
@on-fetch="(data) => (sales = data)"
auto-load
/>
<div class="pictures-list">
<CatalogItem v-for="(sale, index) in sales" :key="index" :item="sale.item">
<template #price>
<div class="q-mt-md full-width row justify-between items-center">
<span class="text-h6">{{ sale.quantity }}</span>
<span>{{ t('by') }}</span>
<span class="text-h6">{{ toCurrency(sale?.price) }}</span>
</div>
</template>
</CatalogItem>
</div>
</template>
<style lang="scss" scoped>
.pictures-list {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
justify-content: center;
gap: 16px;
}
</style>
<i18n>
es:
by: por
</i18n>

View File

@ -774,6 +774,5 @@ es:
Continue anyway?: ¿Continuar de todas formas? Continue anyway?: ¿Continuar de todas formas?
You are going to delete lines of the ticket: Vas a eliminar lineas del ticket You are going to delete lines of the ticket: Vas a eliminar lineas del ticket
Add item: Añadir artículo Add item: Añadir artículo
Select lines to see the options: Selecciona líneas para ver las opciones
Transfer lines: Transferir líneas Transfer lines: Transferir líneas
</i18n> </i18n>

View File

@ -77,7 +77,6 @@ const getDefaultTaxClass = async () => {
params: { filter: JSON.stringify(filter) }, params: { filter: JSON.stringify(filter) },
}); });
defaultTaxClass.value = data; defaultTaxClass.value = data;
console.log('defaultTaxClass', defaultTaxClass.value);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -93,6 +93,25 @@ futureTickets:
moveTicketSuccess: Tickets moved successfully! moveTicketSuccess: Tickets moved successfully!
searchInfo: Search future tickets by date searchInfo: Search future tickets by date
futureTicket: Future tickets futureTicket: Future tickets
expedition:
id: Expedition
item: Item
name: Name
packageType: Package type
counter: Counter
externalId: externalId
created: Created
state: State
historyAction: Status log
newTicketWithRoute: New ticket with route
newTicketWithoutRoute: New ticket without route
landed: Landed
routeId: Route id
deleteExpedition: Delete expedition
expeditionRemoved: Expedition removed
removeExpeditionSubtitle: Are you sure you want to delete this expedition?
worker: Worker
move: Move
basicData: basicData:
next: Next next: Next
back: Back back: Back
@ -159,8 +178,32 @@ service:
addService: Add service addService: Add service
quantityInfo: To create services with negative amounts mark the service on the source ticket and press the pay button. quantityInfo: To create services with negative amounts mark the service on the source ticket and press the pay button.
createRefundSuccess: 'The following refund ticket have been created: { ticketId }' createRefundSuccess: 'The following refund ticket have been created: { ticketId }'
components:
item: Item
description: Description
quantity: Quantity
serie: Serie
components: Components
import: Import
total: Total
baseToCommission: Base to commission
totalWithoutVat: Total without VAT
zoneBreakdown: Zone breakdown
price: Price
bonus: Bonus
zone: Zone
volume: Volume
theoricalCost: Theorical cost
totalPrice: Total price
packages: Packages
tracking: tracking:
state: State state: State
worker: Worker worker: Worker
created: Created created: Created
addState: Add state addState: Add state
package:
package: Package
quantity: Quantity
added: Added
addPackage: Add package
removePackage: Remove package

View File

@ -164,5 +164,49 @@ ticketSale:
shipped: F. Envío shipped: F. Envío
agency: Agencia agency: Agencia
address: Consignatario address: Consignatario
components:
item: Artículo
description: Descripción
quantity: Cantidad
serie: Serie
components: Componentes
import: Importe
total: Total
baseToCommission: Base comisionable
totalWithoutVat: Total sin IVA
zoneBreakdown: Desglose zona
price: Precio
bonus: Bonificación
zone: Zona
volume: Volúmen
theoricalCost: Porte teórico
totalPrice: Precio total
packages: Bultos
expedition:
id: Expedición
item: Artículo
name: Nombre
packageType: Package type
counter: Contador
externalId: externalId
created: Fecha creación
state: Estado
historyAction: Historial de estados
newTicketWithRoute: Nuevo ticket con ruta
newTicketWithoutRoute: Nuevo ticket sin ruta
landed: F. entrega
routeId: Id ruta
deleteExpedition: Eliminar expedición
expeditionRemoved: Expedición eliminada
removeExpeditionSubtitle: ¿Está seguro de eliminar esta expedición?
worker: Trabajador
move: Mover
package:
package: Embalaje
quantity: Cantidad
added: Añadido
addPackage: Añadir embalaje
removePackage: Quitar embalaje
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

View File

@ -65,7 +65,6 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
:subtitle="data.subtitle" :subtitle="data.subtitle"
:filter="filter" :filter="filter"
data-key="travelData" data-key="travelData"
:summary="$attrs"
@on-fetch="setData" @on-fetch="setData"
> >
<template #menu="{ entity }"> <template #menu="{ entity }">

View File

@ -71,6 +71,7 @@ const columns = computed(() => [
url: 'agencyModes', url: 'agencyModes',
fields: ['id', 'name'], fields: ['id', 'name'],
}, },
format: (row) => row.agencyModeName,
columnField: { columnField: {
component: null, component: null,
}, },
@ -112,7 +113,10 @@ const columns = computed(() => [
attrs: { attrs: {
url: 'warehouses', url: 'warehouses',
fields: ['id', 'name'], fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
}, },
format: (row) => row.warehouseInName,
columnField: { columnField: {
component: null, component: null,
}, },
@ -129,6 +133,7 @@ const columns = computed(() => [
url: 'warehouses', url: 'warehouses',
fields: ['id', 'name'], fields: ['id', 'name'],
}, },
format: (row) => row.warehouseOutName,
columnField: { columnField: {
component: null, component: null,
}, },
@ -196,7 +201,6 @@ const columns = computed(() => [
default-mode="table" default-mode="table"
auto-load auto-load
redirect="travel" redirect="travel"
:right-search="false"
:is-editable="false" :is-editable="false"
:use-model="true" :use-model="true"
/> />

View File

@ -14,15 +14,19 @@ export default {
main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'], main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'],
card: [ card: [
'TicketBasicData', 'TicketBasicData',
'TicketBoxing', 'TicketPurchaseRequest',
'TicketSms',
'TicketSale', 'TicketSale',
'TicketLog', 'TicketLog',
'TicketPurchaseRequest', 'TicketExpedition',
'TicketService', 'TicketService',
'TicketTracking',
'TicketVolume', 'TicketVolume',
'TicketNotes', 'TicketNotes',
'TicketTracking',
'TicketBoxing',
'TicketSms',
'TicketPicture',
'TicketComponents',
'TicketPackage',
], ],
}, },
children: [ children: [
@ -70,8 +74,8 @@ export default {
component: () => import('src/pages/Ticket/TicketFuture.vue'), component: () => import('src/pages/Ticket/TicketFuture.vue'),
}, },
{ {
path: 'advance',
name: 'TicketAdvance', name: 'TicketAdvance',
path: 'advance',
meta: { meta: {
title: 'ticketAdvance', title: 'ticketAdvance',
icon: 'keyboard_double_arrow_left', icon: 'keyboard_double_arrow_left',
@ -142,12 +146,76 @@ export default {
}, },
component: () => import('src/pages/Ticket/Card/TicketLog.vue'), component: () => import('src/pages/Ticket/Card/TicketLog.vue'),
}, },
{
path: 'observation',
name: 'TicketNotes',
meta: {
title: 'notes',
icon: 'vn:notes',
},
component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
},
{
path: 'picture',
name: 'TicketPicture',
meta: {
title: 'pictures',
icon: 'vn:photo',
},
component: () => import('src/pages/Ticket/Card/TicketPicture.vue'),
},
{
path: 'volume',
name: 'TicketVolume',
meta: {
title: 'volume',
icon: 'vn:volume',
},
component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
},
{
path: 'expedition',
name: 'TicketExpedition',
meta: {
title: 'expedition',
icon: 'vn:package',
},
component: () => import('src/pages/Ticket/Card/TicketExpedition.vue'),
},
{
path: 'service',
name: 'TicketService',
meta: {
title: 'services',
icon: 'vn:services',
},
component: () => import('src/pages/Ticket/Card/TicketService.vue'),
},
{
path: 'package',
name: 'TicketPackage',
meta: {
title: 'packages',
icon: 'vn:bucket',
},
component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
},
{
path: 'components',
name: 'TicketComponents',
meta: {
title: 'components',
icon: 'vn:components',
},
component: () => import('src/pages/Ticket/Card/TicketComponents.vue'),
},
{ {
path: 'boxing', path: 'boxing',
name: 'TicketBoxing', name: 'TicketBoxing',
meta: { meta: {
title: 'boxing', title: 'boxing',
icon: 'vn:package', icon: 'science',
}, },
component: () => import('src/pages/Ticket/Card/TicketBoxing.vue'), component: () => import('src/pages/Ticket/Card/TicketBoxing.vue'),
}, },
@ -160,34 +228,6 @@ export default {
}, },
component: () => import('src/pages/Ticket/Card/TicketSms.vue'), component: () => import('src/pages/Ticket/Card/TicketSms.vue'),
}, },
{
path: 'service',
name: 'TicketService',
meta: {
title: 'services',
icon: 'vn:services',
},
component: () => import('src/pages/Ticket/Card/TicketService.vue'),
},
{
path: 'volume',
name: 'TicketVolume',
meta: {
title: 'volume',
icon: 'vn:volume',
},
component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
},
{
path: 'observation',
name: 'TicketNotes',
meta: {
title: 'notes',
icon: 'vn:notes',
},
component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
},
], ],
}, },
], ],

View File

@ -3,37 +3,56 @@ import { defineStore } from 'pinia';
export const useArrayDataStore = defineStore('arrayDataStore', () => { export const useArrayDataStore = defineStore('arrayDataStore', () => {
const state = ref({}); const state = ref({});
const defaultOpts = {
filter: {},
userFilter: {},
userParams: {},
url: '',
limit: 10,
skip: 0,
order: '',
isLoading: false,
userParamsChanged: false,
exprBuilder: null,
searchUrl: 'params',
navigate: null,
page: 1,
};
function get(key) { function get(key) {
return state.value[key]; return state.value[key];
} }
function set(key) { function set(key) {
state.value[key] = { state.value[key] = getDefaultState();
filter: {},
userFilter: {},
userParams: {},
url: '',
limit: 10,
skip: 0,
order: '',
data: ref(),
isLoading: false,
userParamsChanged: false,
exprBuilder: null,
searchUrl: 'params',
navigate: null,
page: 1,
};
} }
function clear(key) { function clear(key) {
delete state.value[key]; delete state.value[key];
} }
function reset(key, opts = []) {
if (!opts.length) state.value[key] = getDefaultState();
else
opts.forEach((opt) => {
if (opt.includes('.')) {
const [parent, child] = opt.split('.');
state.value[key][parent][child] = defaultOpts[child];
} else if (Object.hasOwn(state.value[key], opt))
state.value[key][opt] = defaultOpts[opt];
});
}
function getDefaultState() {
return Object.assign(JSON.parse(JSON.stringify(defaultOpts)), {
data: ref(),
});
}
return { return {
get, get,
set, set,
clear, clear,
reset,
}; };
}); });

View File

@ -1,4 +1,4 @@
describe('WagonTypeCreate', () => { describe('EntryDms', () => {
const entryId = 1; const entryId = 1;
beforeEach(() => { beforeEach(() => {

View File

@ -1,7 +1,7 @@
describe('WagonTypeCreate', () => { describe('My entry when is supplier', () => {
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080); cy.viewport(1920, 1080);
cy.login('customer'); cy.login('supplier');
cy.visit(`/#/entry/my`, { cy.visit(`/#/entry/my`, {
onBeforeLoad(win) { onBeforeLoad(win) {
cy.stub(win, 'open'); cy.stub(win, 'open');
@ -10,7 +10,7 @@ describe('WagonTypeCreate', () => {
cy.waitForElement('.q-page', 6000); cy.waitForElement('.q-page', 6000);
}); });
it('should create edit and remove new dms', () => { it('should open buyLabel when is supplier', () => {
cy.get( cy.get(
'[to="/null/2"] > .q-card > .column > .q-btn > .q-btn__content > .q-icon' '[to="/null/2"] > .q-card > .column > .q-btn > .q-btn__content > .q-icon'
).click(); ).click();

View File

@ -239,5 +239,5 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
}); });
Cypress.Commands.add('openActionsDescriptor', () => { Cypress.Commands.add('openActionsDescriptor', () => {
cy.get('.descriptor > .header > .q-btn').click(); cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click();
}); });