#7354 end Zone migration #539
|
@ -22,7 +22,7 @@ const { t } = useI18n();
|
||||||
const { validate } = useValidator();
|
const { validate } = useValidator();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const myForm = ref(null);
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -109,11 +109,14 @@ const defaultButtons = computed(() => ({
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
icon: 'save',
|
icon: 'save',
|
||||||
label: 'globals.save',
|
label: 'globals.save',
|
||||||
|
click: () => myForm.value.submit(),
|
||||||
|
type: 'submit',
|
||||||
},
|
},
|
||||||
reset: {
|
reset: {
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
icon: 'restart_alt',
|
icon: 'restart_alt',
|
||||||
label: 'globals.reset',
|
label: 'globals.reset',
|
||||||
|
click: () => reset(),
|
||||||
},
|
},
|
||||||
...$props.defaultButtons,
|
...$props.defaultButtons,
|
||||||
}));
|
}));
|
||||||
|
@ -276,7 +279,14 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column items-center full-width">
|
<div class="column items-center full-width">
|
||||||
<QForm @submit="save" @reset="reset" class="q-pa-md" id="formModel">
|
<QForm
|
||||||
|
ref="myForm"
|
||||||
|
v-if="formData"
|
||||||
|
@submit="save"
|
||||||
|
@reset="reset"
|
||||||
|
class="q-pa-md"
|
||||||
|
id="formModel"
|
||||||
|
>
|
||||||
<QCard>
|
<QCard>
|
||||||
<slot
|
<slot
|
||||||
v-if="formData"
|
v-if="formData"
|
||||||
|
@ -304,7 +314,7 @@ defineExpose({
|
||||||
:color="defaultButtons.reset.color"
|
:color="defaultButtons.reset.color"
|
||||||
:icon="defaultButtons.reset.icon"
|
:icon="defaultButtons.reset.icon"
|
||||||
flat
|
flat
|
||||||
@click="reset"
|
@click="defaultButtons.reset.click"
|
||||||
:disable="!hasChanges"
|
:disable="!hasChanges"
|
||||||
:title="t(defaultButtons.reset.label)"
|
:title="t(defaultButtons.reset.label)"
|
||||||
/>
|
/>
|
||||||
|
@ -344,7 +354,7 @@ defineExpose({
|
||||||
:label="tMobile('globals.save')"
|
:label="tMobile('globals.save')"
|
||||||
color="primary"
|
color="primary"
|
||||||
icon="save"
|
icon="save"
|
||||||
@click="save"
|
@click="defaultButtons.save.click"
|
||||||
:disable="!hasChanges"
|
:disable="!hasChanges"
|
||||||
:title="t(defaultButtons.save.label)"
|
:title="t(defaultButtons.save.label)"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -178,10 +178,20 @@ function setUserParams(watchedParams, watchedOrder) {
|
||||||
watchedParams = { ...watchedParams, ...where };
|
watchedParams = { ...watchedParams, ...where };
|
||||||
delete watchedParams.filter;
|
delete watchedParams.filter;
|
||||||
delete params.value?.filter;
|
delete params.value?.filter;
|
||||||
params.value = { ...params.value, ...watchedParams };
|
params.value = { ...params.value, ...sanitizer(watchedParams) };
|
||||||
orders.value = parseOrder(order);
|
orders.value = parseOrder(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizer(params) {
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (typeof value == 'object') {
|
||||||
|
const param = Object.values(value)[0];
|
||||||
|
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
function splitColumns(columns) {
|
function splitColumns(columns) {
|
||||||
splittedColumns.value = {
|
splittedColumns.value = {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
'update:modelValue',
|
'update:modelValue',
|
||||||
|
@ -27,9 +28,11 @@ const $props = defineProps({
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const { validations } = useValidator();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
const requiredFieldRule = (val) => validations().required($attrs.required, val);
|
||||||
|
|
||||||
const vnInputRef = ref(null);
|
const vnInputRef = ref(null);
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -57,21 +60,22 @@ const focus = () => {
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus,
|
focus,
|
||||||
});
|
});
|
||||||
|
import { useAttrs } from 'vue';
|
||||||
|
const $attrs = useAttrs();
|
||||||
|
|
||||||
const inputRules = [
|
const mixinRules = [
|
||||||
|
requiredFieldRule,
|
||||||
|
...($attrs.rules ?? []),
|
||||||
(val) => {
|
(val) => {
|
||||||
const { min } = vnInputRef.value.$attrs;
|
const { min } = vnInputRef.value.$attrs;
|
||||||
|
if (!min) return null;
|
||||||
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
|
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||||
@mouseover="hover = true"
|
|
||||||
@mouseleave="hover = false"
|
|
||||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
|
||||||
>
|
|
||||||
<QInput
|
<QInput
|
||||||
ref="vnInputRef"
|
ref="vnInputRef"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
|
@ -80,7 +84,7 @@ const inputRules = [
|
||||||
:class="{ required: $attrs.required }"
|
:class="{ required: $attrs.required }"
|
||||||
@keyup.enter="emit('keyup.enter')"
|
@keyup.enter="emit('keyup.enter')"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:rules="inputRules"
|
:rules="mixinRules"
|
||||||
:lazy-rules="true"
|
:lazy-rules="true"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
>
|
>
|
||||||
|
|
|
@ -14,7 +14,7 @@ const props = defineProps({
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const initialDate = ref(model.value);
|
const initialDate = ref(model.value ?? Date.vnNew());
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
||||||
|
|
||||||
|
|
|
@ -92,16 +92,18 @@ function setUserParams(watchedParams) {
|
||||||
const order = watchedParams.filter?.order;
|
const order = watchedParams.filter?.order;
|
||||||
|
|
||||||
delete watchedParams.filter;
|
delete watchedParams.filter;
|
||||||
userParams.value = { ...userParams.value, ...sanitizer(watchedParams) };
|
userParams.value = sanitizer(watchedParams);
|
||||||
emit('setUserParams', userParams.value, order);
|
emit('setUserParams', userParams.value, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [route.query[$props.searchUrl], arrayData.store.userParams],
|
() => route.query[$props.searchUrl],
|
||||||
([newSearchUrl, newUserParams], [oldSearchUrl, oldUserParams]) => {
|
(val, oldValue) => (val || oldValue) && setUserParams(val)
|
||||||
if (newSearchUrl || oldSearchUrl) setUserParams(newSearchUrl);
|
);
|
||||||
if (newUserParams || oldUserParams) setUserParams(newUserParams);
|
|
||||||
}
|
watch(
|
||||||
|
() => arrayData.store.userParams,
|
||||||
|
(val, oldValue) => (val || oldValue) && setUserParams(val)
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -63,17 +63,13 @@ const props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
makeFetch: {
|
whereFilter: {
|
||||||
type: Boolean,
|
type: Function,
|
||||||
default: true,
|
default: undefined,
|
||||||
},
|
|
||||||
searchUrl: {
|
|
||||||
type: String,
|
|
||||||
default: 'params',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchText = ref('');
|
const searchText = ref();
|
||||||
let arrayDataProps = { ...props };
|
let arrayDataProps = { ...props };
|
||||||
if (props.redirect)
|
if (props.redirect)
|
||||||
arrayDataProps = {
|
arrayDataProps = {
|
||||||
|
@ -107,13 +103,20 @@ async function search() {
|
||||||
const staticParams = Object.entries(store.userParams);
|
const staticParams = Object.entries(store.userParams);
|
||||||
arrayData.reset(['skip', 'page']);
|
arrayData.reset(['skip', 'page']);
|
||||||
|
|
||||||
if (props.makeFetch)
|
const filter = {
|
||||||
await arrayData.applyFilter({
|
params: {
|
||||||
params: {
|
...Object.fromEntries(staticParams),
|
||||||
...Object.fromEntries(staticParams),
|
search: searchText.value,
|
||||||
search: searchText.value,
|
},
|
||||||
},
|
};
|
||||||
});
|
|
||||||
|
if (props.whereFilter) {
|
||||||
|
filter.filter = {
|
||||||
|
where: props.whereFilter(searchText.value),
|
||||||
|
};
|
||||||
|
delete filter.params.search;
|
||||||
|
}
|
||||||
|
await arrayData.applyFilter(filter);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function useValidator() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const validations = function (validation) {
|
const validations = function (validation = {}) {
|
||||||
return {
|
return {
|
||||||
format: (value) => {
|
format: (value) => {
|
||||||
const { allowNull, with: format, allowBlank } = validation;
|
const { allowNull, with: format, allowBlank } = validation;
|
||||||
|
@ -40,12 +40,15 @@ export function useValidator() {
|
||||||
if (!isValid) return message;
|
if (!isValid) return message;
|
||||||
},
|
},
|
||||||
presence: (value) => {
|
presence: (value) => {
|
||||||
let message = `Value can't be empty`;
|
let message = t(`globals.valueCantBeEmpty`);
|
||||||
if (validation.message)
|
if (validation.message)
|
||||||
message = t(validation.message) || validation.message;
|
message = t(validation.message) || validation.message;
|
||||||
|
|
||||||
return !validator.isEmpty(value ? String(value) : '') || message;
|
return !validator.isEmpty(value ? String(value) : '') || message;
|
||||||
},
|
},
|
||||||
|
required: (required, value) => {
|
||||||
|
return required ? !!value || t('globals.fieldRequired') : null;
|
||||||
|
},
|
||||||
length: (value) => {
|
length: (value) => {
|
||||||
const options = {
|
const options = {
|
||||||
min: validation.min || validation.is,
|
min: validation.min || validation.is,
|
||||||
|
@ -71,12 +74,17 @@ export function useValidator() {
|
||||||
return validator.isInt(value) || 'Value should be integer';
|
return validator.isInt(value) || 'Value should be integer';
|
||||||
return validator.isNumeric(value) || 'Value should be a number';
|
return validator.isNumeric(value) || 'Value should be a number';
|
||||||
},
|
},
|
||||||
|
min: (value, min) => {
|
||||||
|
if (min >= 0)
|
||||||
|
if (Math.floor(value) < min) return t('inputMin', { value: min });
|
||||||
|
},
|
||||||
custom: (value) => validation.bindedFunction(value) || 'Invalid value',
|
custom: (value) => validation.bindedFunction(value) || 'Invalid value',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
validate,
|
validate,
|
||||||
|
validations,
|
||||||
models,
|
models,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ globals:
|
||||||
allRows: 'All { numberRows } row(s)'
|
allRows: 'All { numberRows } row(s)'
|
||||||
markAll: Mark all
|
markAll: Mark all
|
||||||
requiredField: Required field
|
requiredField: Required field
|
||||||
|
valueCantBeEmpty: Value cannot be empty
|
||||||
class: clase
|
class: clase
|
||||||
type: Type
|
type: Type
|
||||||
reason: reason
|
reason: reason
|
||||||
|
|
|
@ -76,6 +76,9 @@ globals:
|
||||||
warehouse: Almacén
|
warehouse: Almacén
|
||||||
company: Empresa
|
company: Empresa
|
||||||
fieldRequired: Campo requerido
|
fieldRequired: Campo requerido
|
||||||
|
valueCantBeEmpty: El valor no puede estar vacío
|
||||||
|
Value can't be blank: El valor no puede estar en blanco
|
||||||
|
Value can't be null: El valor no puede ser nulo
|
||||||
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }'
|
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }'
|
||||||
smsSent: SMS enviado
|
smsSent: SMS enviado
|
||||||
confirmDeletion: Confirmar eliminación
|
confirmDeletion: Confirmar eliminación
|
||||||
|
@ -237,7 +240,7 @@ globals:
|
||||||
purchaseRequest: Petición de compra
|
purchaseRequest: Petición de compra
|
||||||
weeklyTickets: Tickets programados
|
weeklyTickets: Tickets programados
|
||||||
formation: Formación
|
formation: Formación
|
||||||
locations: Ubicaciones
|
locations: Localizaciones
|
||||||
warehouses: Almacenes
|
warehouses: Almacenes
|
||||||
roles: Roles
|
roles: Roles
|
||||||
connections: Conexiones
|
connections: Conexiones
|
||||||
|
|
|
@ -83,6 +83,7 @@ const agencyOptions = ref([]);
|
||||||
:label="t('Price')"
|
:label="t('Price')"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
|
required="true"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<VnInput
|
<VnInput
|
||||||
|
@ -95,7 +96,12 @@ const agencyOptions = ref([]);
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnInput v-model="data.inflation" :label="t('Inflation')" clearable />
|
<VnInput
|
||||||
|
v-model="data.inflation"
|
||||||
|
:label="t('Inflation')"
|
||||||
|
type="number"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
v-model="data.isVolumetric"
|
v-model="data.isVolumetric"
|
||||||
:label="t('Volumetric')"
|
:label="t('Volumetric')"
|
||||||
|
|
|
@ -2,36 +2,47 @@
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import VnCard from 'components/common/VnCard.vue';
|
import VnCard from 'components/common/VnCard.vue';
|
||||||
import ZoneDescriptor from './ZoneDescriptor.vue';
|
import ZoneDescriptor from './ZoneDescriptor.vue';
|
||||||
import ZoneSearchbar from './ZoneSearchbar.vue';
|
import ZoneFilterPanel from '../ZoneFilterPanel.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const routeName = computed(() => route.name);
|
const routeName = computed(() => route.name);
|
||||||
|
const customRouteRedirectName = computed(() => {
|
||||||
|
if (routeName.value === 'ZoneLocations') return null;
|
||||||
|
return routeName.value;
|
||||||
|
});
|
||||||
|
const searchbarMakeFetch = computed(() => routeName.value !== 'ZoneEvents');
|
||||||
const searchBarDataKeys = {
|
const searchBarDataKeys = {
|
||||||
ZoneWarehouses: 'ZoneWarehouses',
|
ZoneWarehouses: 'ZoneWarehouses',
|
||||||
ZoneSummary: 'ZoneSummary',
|
ZoneSummary: 'ZoneSummary',
|
||||||
ZoneLocations: 'ZoneLocations',
|
ZoneLocations: 'ZoneLocations',
|
||||||
ZoneEvents: 'ZoneEvents',
|
ZoneEvents: 'ZoneEvents',
|
||||||
};
|
};
|
||||||
|
function notIsLocations(ifIsFalse, ifIsTrue) {
|
||||||
|
if (routeName.value != 'ZoneLocations') return ifIsFalse;
|
||||||
|
return ifIsTrue;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnCard
|
<VnCard
|
||||||
data-key="Zone"
|
data-key="zone"
|
||||||
|
base-url="Zones"
|
||||||
:descriptor="ZoneDescriptor"
|
:descriptor="ZoneDescriptor"
|
||||||
:search-data-key="searchBarDataKeys[routeName]"
|
|
||||||
:filter-panel="ZoneFilterPanel"
|
:filter-panel="ZoneFilterPanel"
|
||||||
|
:search-data-key="notIsLocations('ZoneList', 'ZoneLocations')"
|
||||||
:searchbar-props="{
|
:searchbar-props="{
|
||||||
url: 'Zones',
|
url: 'Zones',
|
||||||
label: t('list.searchZone'),
|
label: notIsLocations(t('list.searchZone'), t('list.searchLocation')),
|
||||||
info: t('list.searchInfo'),
|
info: t('list.searchInfo'),
|
||||||
|
whereFilter: notIsLocations((value) => {
|
||||||
|
return /^\d+$/.test(value)
|
||||||
|
? { id: value }
|
||||||
|
: { name: { like: `%${value}%` } };
|
||||||
|
}),
|
||||||
}"
|
}"
|
||||||
>
|
/>
|
||||||
<template #searchbar>
|
|
||||||
<ZoneSearchbar />
|
|
||||||
</template>
|
|
||||||
</VnCard>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -8,13 +8,6 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const $props = defineProps({
|
|
||||||
zone: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { push, currentRoute } = useRouter();
|
const { push, currentRoute } = useRouter();
|
||||||
const zoneId = currentRoute.value.params.id;
|
const zoneId = currentRoute.value.params.id;
|
||||||
|
@ -22,32 +15,21 @@ const zoneId = currentRoute.value.params.id;
|
||||||
const actions = {
|
const actions = {
|
||||||
clone: async () => {
|
clone: async () => {
|
||||||
const opts = { message: t('Zone cloned'), type: 'positive' };
|
const opts = { message: t('Zone cloned'), type: 'positive' };
|
||||||
let clonedZoneId;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(`Zones/${zoneId}/clone`, {
|
const { data } = await axios.post(`Zones/${zoneId}/clone`, {});
|
||||||
shipped: $props.zone.value.shipped,
|
notify(opts);
|
||||||
});
|
push(`/zone/${data.id}/basic-data`);
|
||||||
clonedZoneId = data;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
opts.message = t('It was not able to clone the zone');
|
opts.message = t('It was not able to clone the zone');
|
||||||
opts.type = 'negative';
|
opts.type = 'negative';
|
||||||
} finally {
|
|
||||||
notify(opts);
|
|
||||||
|
|
||||||
if (clonedZoneId) push({ name: 'ZoneSummary', params: { id: clonedZoneId } });
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remove: async () => {
|
remove: async () => {
|
||||||
try {
|
try {
|
||||||
await axios.post(`Zones/${zoneId}/setDeleted`);
|
await axios.post(`Zones/${zoneId}/deleteZone`);
|
||||||
jon marked this conversation as resolved
|
|||||||
|
|
||||||
notify({ message: t('Zone deleted'), type: 'positive' });
|
notify({ message: t('Zone deleted'), type: 'positive' });
|
||||||
notify({
|
|
||||||
message: t('You can undo this action within the first hour'),
|
|
||||||
icon: 'info',
|
|
||||||
});
|
|
||||||
|
|
||||||
push({ name: 'ZoneList' });
|
push({ name: 'ZoneList' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notify({ message: e.message, type: 'negative' });
|
notify({ message: e.message, type: 'negative' });
|
||||||
|
@ -64,30 +46,31 @@ function openConfirmDialog(callback) {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QItem @click="openConfirmDialog('clone')" v-ripple clickable>
|
|
||||||
<QItemSection avatar>
|
|
||||||
<QIcon name="content_copy" />
|
|
||||||
</QItemSection>
|
|
||||||
<QItemSection>{{ t('To clone zone') }}</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
<QItem @click="openConfirmDialog('remove')" v-ripple clickable>
|
<QItem @click="openConfirmDialog('remove')" v-ripple clickable>
|
||||||
<QItemSection avatar>
|
<QItemSection avatar>
|
||||||
<QIcon name="delete" />
|
<QIcon name="delete" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection>{{ t('deleteZone') }}</QItemSection>
|
<QItemSection>{{ t('deleteZone') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
<QItem @click="openConfirmDialog('clone')" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="content_copy" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('cloneZone') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
deleteZone: Delete zone
|
deleteZone: Delete
|
||||||
|
cloneZone: Clone
|
||||||
confirmDeletion: Confirm deletion
|
confirmDeletion: Confirm deletion
|
||||||
confirmDeletionMessage: Are you sure you want to delete this zone?
|
confirmDeletionMessage: Are you sure you want to delete this zone?
|
||||||
|
|
||||||
es:
|
es:
|
||||||
To clone zone: Clonar zone
|
cloneZone: Clonar
|
||||||
deleteZone: Eliminar zona
|
deleteZone: Eliminar
|
||||||
confirmDeletion: Confirmar eliminación
|
confirmDeletion: Confirmar eliminación
|
||||||
confirmDeletionMessage: Seguro que quieres eliminar este zona?
|
confirmDeletionMessage: Seguro que quieres eliminar este zona?
|
||||||
|
Zone deleted: Zona eliminada
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -58,20 +58,12 @@ const arrayData = useArrayData('ZoneEvents');
|
||||||
|
|
||||||
const exclusionGeoCreate = async () => {
|
const exclusionGeoCreate = async () => {
|
||||||
try {
|
try {
|
||||||
if (isNew.value) {
|
const params = {
|
||||||
const params = {
|
zoneFk: parseInt(route.params.id),
|
||||||
zoneFk: parseInt(route.params.id),
|
date: dated.value,
|
||||||
date: dated.value,
|
geoIds: tickedNodes.value,
|
||||||
geoIds: tickedNodes.value,
|
};
|
||||||
};
|
await axios.post('Zones/exclusionGeo', params);
|
||||||
await axios.post('Zones/exclusionGeo', params);
|
|
||||||
} else {
|
|
||||||
const params = {
|
|
||||||
zoneExclusionFk: props.event?.zoneExclusionFk,
|
|
||||||
geoIds: tickedNodes.value,
|
|
||||||
};
|
|
||||||
await axios.post('Zones/updateExclusionGeo', params);
|
|
||||||
}
|
|
||||||
await refetchEvents();
|
await refetchEvents();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error creating exclusion geo: ', err);
|
console.error('Error creating exclusion geo: ', err);
|
||||||
|
@ -85,7 +77,7 @@ const exclusionCreate = async () => {
|
||||||
{ dated: dated.value },
|
{ dated: dated.value },
|
||||||
]);
|
]);
|
||||||
else
|
else
|
||||||
await axios.put(`Zones/${route.params.id}/exclusions/${props.event?.id}`, {
|
await axios.post(`Zones/${route.params.id}/exclusions`, {
|
||||||
dated: dated.value,
|
dated: dated.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,8 +95,7 @@ const onSubmit = async () => {
|
||||||
const deleteEvent = async () => {
|
const deleteEvent = async () => {
|
||||||
try {
|
try {
|
||||||
if (!props.event) return;
|
if (!props.event) return;
|
||||||
const exclusionId = props.event?.zoneExclusionFk || props.event?.id;
|
await axios.delete(`Zones/${route.params.id}/exclusions`);
|
||||||
await axios.delete(`Zones/${route.params.id}/exclusions/${exclusionId}`);
|
|
||||||
await refetchEvents();
|
await refetchEvents();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting event: ', err);
|
console.error('Error deleting event: ', err);
|
||||||
|
@ -141,7 +132,11 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
<template #form-inputs>
|
<template #form-inputs>
|
||||||
<VnRow class="row q-gutter-md q-mb-lg">
|
<VnRow class="row q-gutter-md q-mb-lg">
|
||||||
<VnInputDate :label="t('eventsInclusionForm.day')" v-model="dated" />
|
<VnInputDate
|
||||||
|
:label="t('eventsInclusionForm.day')"
|
||||||
|
v-model="dated"
|
||||||
|
:model-value="props.date"
|
||||||
|
/>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<div class="column q-gutter-y-sm q-mb-md">
|
<div class="column q-gutter-y-sm q-mb-md">
|
||||||
<QRadio
|
<QRadio
|
||||||
|
|
|
@ -13,8 +13,8 @@ import { reactive } from 'vue';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
const firstDay = ref(null);
|
const firstDay = ref();
|
||||||
const lastDay = ref(null);
|
const lastDay = ref();
|
||||||
|
|
||||||
const events = ref([]);
|
const events = ref([]);
|
||||||
const formModeName = ref('include');
|
const formModeName = ref('include');
|
||||||
|
@ -44,34 +44,15 @@ onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-if="stateStore.isHeaderMounted()">
|
<Teleport to="#right-panel" v-if="useStateStore().isHeaderMounted()">
|
||||||
jon marked this conversation as resolved
jsegarra
commented
El año aparece como 2001 y en salix como 01, confirmar como debe ser El año aparece como 2001 y en salix como 01, confirmar como debe ser
jon
commented
Lo he comentado con jgallego y se deja con el año completo Lo he comentado con jgallego y se deja con el año completo
|
|||||||
<Teleport to="#actions-append">
|
<ZoneEventsPanel
|
||||||
<div class="row q-gutter-x-sm">
|
:first-day="firstDay"
|
||||||
<QBtn
|
:last-day="lastDay"
|
||||||
flat
|
:events="events"
|
||||||
@click="stateStore.toggleRightDrawer()"
|
v-model:formModeName="formModeName"
|
||||||
round
|
@open-zone-form="openForm"
|
||||||
dense
|
/>
|
||||||
icon="menu"
|
</Teleport>
|
||||||
>
|
|
||||||
<QTooltip bottom anchor="bottom right">
|
|
||||||
{{ t('globals.collapseMenu') }}
|
|
||||||
</QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
</template>
|
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
|
||||||
<QScrollArea class="fit text-grey-8">
|
|
||||||
<ZoneEventsPanel
|
|
||||||
:first-day="firstDay"
|
|
||||||
:last-day="lastDay"
|
|
||||||
:events="events"
|
|
||||||
v-model:formModeName="formModeName"
|
|
||||||
@open-zone-form="openForm"
|
|
||||||
/>
|
|
||||||
</QScrollArea>
|
|
||||||
</QDrawer>
|
|
||||||
<QPage class="q-pa-md flex justify-center">
|
<QPage class="q-pa-md flex justify-center">
|
||||||
<ZoneCalendarGrid
|
<ZoneCalendarGrid
|
||||||
v-model:events="events"
|
v-model:events="events"
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed, watch, onUnmounted } from 'vue';
|
import { onMounted, ref, computed, watch, onUnmounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
|
||||||
|
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
@ -30,7 +27,6 @@ const props = defineProps({
|
||||||
|
|
||||||
const emit = defineEmits(['update:tickedNodes']);
|
const emit = defineEmits(['update:tickedNodes']);
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
|
|
||||||
|
@ -186,16 +182,6 @@ onUnmounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnInput
|
|
||||||
v-if="showSearchBar"
|
|
||||||
v-model="store.userParams.search"
|
|
||||||
:placeholder="t('globals.search')"
|
|
||||||
@keydown.enter.prevent="reFetch()"
|
|
||||||
>
|
|
||||||
<template #prepend>
|
|
||||||
<QIcon class="cursor-pointer" name="search" />
|
|
||||||
</template>
|
|
||||||
</VnInput>
|
|
||||||
<QTree
|
<QTree
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
:nodes="nodes"
|
:nodes="nodes"
|
||||||
|
|
|
@ -19,24 +19,14 @@ const exprBuilder = (param, value) => {
|
||||||
agencyModeFk: value,
|
agencyModeFk: value,
|
||||||
};
|
};
|
||||||
case 'search':
|
case 'search':
|
||||||
if (value) {
|
return /^\d+$/.test(value) ? { id: value } : { name: { like: `%${value}%` } };
|
||||||
if (!isNaN(value)) {
|
|
||||||
return { id: value };
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
name: {
|
|
||||||
like: `%${value}%`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnSearchbar
|
<VnSearchbar
|
||||||
data-key="ZoneList"
|
data-key="Zones"
|
||||||
url="Zones"
|
url="Zones"
|
||||||
:filter="{
|
:filter="{
|
||||||
include: { relation: 'agencyMode', scope: { fields: ['name'] } },
|
include: { relation: 'agencyMode', scope: { fields: ['name'] } },
|
||||||
|
|
|
@ -14,7 +14,7 @@ const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { openConfirmationModal } = useVnConfirm();
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
|
||||||
const paginateRef = ref(null);
|
const paginateRef = ref();
|
||||||
const createWarehouseDialogRef = ref(null);
|
const createWarehouseDialogRef = ref(null);
|
||||||
|
|
||||||
const arrayData = useArrayData('ZoneWarehouses');
|
const arrayData = useArrayData('ZoneWarehouses');
|
||||||
|
|
|
@ -1,47 +1,25 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, reactive } from 'vue';
|
import { onMounted, ref, reactive, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
|
||||||
import { useArrayData } from 'src/composables/useArrayData';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
import axios from 'axios';
|
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
import { watch } from 'vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
|
||||||
const deliveryMethodFk = ref(null);
|
const deliveryMethodFk = ref('delivery');
|
||||||
const deliveryMethods = ref([]);
|
const deliveryMethods = ref({});
|
||||||
|
const inq = ref([]);
|
||||||
const formData = reactive({});
|
const formData = reactive({});
|
||||||
|
|
||||||
const arrayData = useArrayData('ZoneDeliveryDays', {
|
const arrayData = useArrayData('ZoneDeliveryDays', {
|
||||||
url: 'Zones/getEvents',
|
url: 'Zones/getEvents',
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchDeliveryMethods = async (filter) => {
|
const deliveryMethodsConfig = { pickUp: ['PICKUP'], delivery: ['AGENCY', 'DELIVERY'] };
|
||||||
try {
|
|
||||||
const params = { filter: JSON.stringify(filter) };
|
|
||||||
const { data } = await axios.get('DeliveryMethods', { params });
|
|
||||||
return data.map((deliveryMethod) => deliveryMethod.id);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error fetching delivery methods: ', err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => deliveryMethodFk.value,
|
|
||||||
async (val) => {
|
|
||||||
let filter;
|
|
||||||
if (val === 'pickUp') filter = { where: { code: 'PICKUP' } };
|
|
||||||
else filter = { where: { code: { inq: ['DELIVERY', 'AGENCY'] } } };
|
|
||||||
|
|
||||||
deliveryMethods.value = await fetchDeliveryMethods(filter);
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
const fetchData = async (params) => {
|
const fetchData = async (params) => {
|
||||||
try {
|
try {
|
||||||
const { data } = params
|
const { data } = params
|
||||||
|
@ -62,14 +40,38 @@ const onSubmit = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
deliveryMethodFk.value = 'delivery';
|
|
||||||
formData.geoFk = arrayData.store?.userParams?.geoFk;
|
formData.geoFk = arrayData.store?.userParams?.geoFk;
|
||||||
formData.agencyModeFk = arrayData.store?.userParams?.agencyModeFk;
|
formData.agencyModeFk = arrayData.store?.userParams?.agencyModeFk;
|
||||||
if (formData.geoFk || formData.agencyModeFk) await fetchData();
|
if (formData.geoFk || formData.agencyModeFk) await fetchData();
|
||||||
});
|
});
|
||||||
|
watch(
|
||||||
|
() => deliveryMethodFk.value,
|
||||||
|
() => {
|
||||||
|
inq.value = {
|
||||||
|
deliveryMethodFk: { inq: deliveryMethods.value[deliveryMethodFk.value] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="DeliveryMethods"
|
||||||
|
:fields="['id', 'name', 'deliveryMethodFk']"
|
||||||
|
@on-fetch="
|
||||||
|
(data) => {
|
||||||
|
Object.entries(deliveryMethodsConfig).forEach(([key, value]) => {
|
||||||
|
deliveryMethods[key] = data
|
||||||
|
.filter((code) => value.includes(code.code))
|
||||||
|
.map((method) => method.id);
|
||||||
|
});
|
||||||
|
inq = {
|
||||||
|
deliveryMethodFk: { inq: deliveryMethods[deliveryMethodFk] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
<QForm @submit="onSubmit()" class="q-pa-md">
|
<QForm @submit="onSubmit()" class="q-pa-md">
|
||||||
<div class="column q-gutter-y-sm">
|
<div class="column q-gutter-y-sm">
|
||||||
<QRadio
|
<QRadio
|
||||||
|
@ -90,7 +92,7 @@ onMounted(async () => {
|
||||||
:label="t('deliveryPanel.postcode')"
|
:label="t('deliveryPanel.postcode')"
|
||||||
v-model="formData.geoFk"
|
v-model="formData.geoFk"
|
||||||
url="Postcodes/location"
|
url="Postcodes/location"
|
||||||
:fields="['geoFk', 'code', 'townFk']"
|
:fields="['geoFk', 'code', 'townFk', 'countryFk']"
|
||||||
sort-by="code, townFk"
|
sort-by="code, townFk"
|
||||||
option-value="geoFk"
|
option-value="geoFk"
|
||||||
option-label="code"
|
option-label="code"
|
||||||
|
@ -106,26 +108,35 @@ onMounted(async () => {
|
||||||
<QItemLabel>{{ opt.code }}</QItemLabel>
|
<QItemLabel>{{ opt.code }}</QItemLabel>
|
||||||
<QItemLabel caption
|
<QItemLabel caption
|
||||||
>{{ opt.town?.province?.name }},
|
>{{ opt.town?.province?.name }},
|
||||||
{{ opt.town?.province?.country?.country }}</QItemLabel
|
{{ opt.town?.province?.country?.name }}</QItemLabel
|
||||||
>
|
>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelect>
|
</VnSelect>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="
|
data-key="delivery"
|
||||||
t(
|
v-if="deliveryMethodFk == 'delivery'"
|
||||||
deliveryMethodFk === 'delivery'
|
:label="t('deliveryPanel.agency')"
|
||||||
? 'deliveryPanel.agency'
|
|
||||||
: 'deliveryPanel.warehouse'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
v-model="formData.agencyModeFk"
|
v-model="formData.agencyModeFk"
|
||||||
url="AgencyModes/isActive"
|
url="AgencyModes/isActive"
|
||||||
:fields="['id', 'name']"
|
:fields="['id', 'name']"
|
||||||
:where="{
|
:where="inq"
|
||||||
deliveryMethodFk: { inq: deliveryMethods },
|
sort-by="name ASC"
|
||||||
jgallego marked this conversation as resolved
Outdated
jgallego
commented
esto que hace? esto que hace?
jon
commented
Cuando deliveryMethodFk es delivery muestra el select de código postal y agencia. Sin embargo si el deliveryMethodFk es pickup estaba puesto el label de almacenes, pero en el select no mostraba nada porque no se tenía en cuenta la condición del v-if Cuando deliveryMethodFk es delivery muestra el select de código postal y agencia. Sin embargo si el deliveryMethodFk es pickup estaba puesto el label de almacenes, pero en el select no mostraba nada porque no se tenía en cuenta la condición del v-if
jsegarra
commented
Hola @jon , revisamos pero en /salix/modules/zone/front/delivery-days/index.html el campo deliveryMethodFk no se usa para distinguir la ruta sino la label del desplegable. Hola @jon , revisamos pero en /salix/modules/zone/front/delivery-days/index.html el campo deliveryMethodFk no se usa para distinguir la ruta sino la label del desplegable.
Es cierto que en local no hay registros cuando seleccionas recogida, sin embargo en entornos desplegados, si que hay registros.
|
|||||||
}"
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
v-else
|
||||||
|
:label="t('deliveryPanel.warehouse')"
|
||||||
|
v-model="formData.agencyModeFk"
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
:fields="['id', 'name']"
|
||||||
|
:where="inq"
|
||||||
sort-by="name ASC"
|
sort-by="name ASC"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
|
|
@ -27,6 +27,7 @@ const agencies = ref([]);
|
||||||
:data-key="props.dataKey"
|
:data-key="props.dataKey"
|
||||||
:search-button="true"
|
:search-button="true"
|
||||||
:hidden-tags="['search']"
|
:hidden-tags="['search']"
|
||||||
|
search-url="table"
|
||||||
>
|
>
|
||||||
<template #tags="{ tag }">
|
<template #tags="{ tag }">
|
||||||
<div class="q-gutter-x-xs">
|
<div class="q-gutter-x-xs">
|
||||||
|
|
|
@ -1,74 +1,120 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { onMounted, computed } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import { toCurrency } from 'src/filters';
|
import { toCurrency } from 'src/filters';
|
||||||
|
|
||||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
|
||||||
import ZoneSummary from 'src/pages/Zone/Card/ZoneSummary.vue';
|
|
||||||
|
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
|
||||||
import { toTimeFormat } from 'src/filters/date';
|
import { toTimeFormat } from 'src/filters/date';
|
||||||
import { useVnConfirm } from 'composables/useVnConfirm';
|
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import axios from 'axios';
|
import ZoneSummary from 'src/pages/Zone/Card/ZoneSummary.vue';
|
||||||
|
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnInputTime from 'src/components/common/VnInputTime.vue';
|
||||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||||
import ZoneFilterPanel from './ZoneFilterPanel.vue';
|
import ZoneFilterPanel from './ZoneFilterPanel.vue';
|
||||||
import ZoneSearchbar from './Card/ZoneSearchbar.vue';
|
import ZoneSearchbar from './Card/ZoneSearchbar.vue';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
const { openConfirmationModal } = useVnConfirm();
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const tableRef = ref();
|
||||||
|
const warehouseOptions = ref([]);
|
||||||
|
|
||||||
const redirectToZoneSummary = (event, { id }) => {
|
const tableFilter = {
|
||||||
router.push({ name: 'ZoneSummary', params: { id } });
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'agencyMode',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'ID',
|
|
||||||
label: t('list.id'),
|
|
||||||
field: (row) => row.id,
|
|
||||||
sortable: true,
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
name: 'id',
|
||||||
|
label: t('list.id'),
|
||||||
|
chip: {
|
||||||
|
condition: () => true,
|
||||||
|
},
|
||||||
|
isId: true,
|
||||||
|
columnFilter: {
|
||||||
|
inWhere: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
align: 'left',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: t('list.name'),
|
label: t('list.name'),
|
||||||
field: (row) => row.name,
|
isTitle: true,
|
||||||
sortable: true,
|
create: true,
|
||||||
align: 'left',
|
columnFilter: {
|
||||||
|
optionLabel: 'name',
|
||||||
|
optionValue: 'id',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'agency',
|
align: 'left',
|
||||||
|
name: 'agencyModeFk',
|
||||||
label: t('list.agency'),
|
label: t('list.agency'),
|
||||||
field: (row) => row?.agencyMode?.name,
|
cardVisible: true,
|
||||||
sortable: true,
|
columnFilter: {
|
||||||
align: 'left',
|
component: 'select',
|
||||||
|
inWhere: true,
|
||||||
|
attrs: {
|
||||||
|
url: 'AgencyModes',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columnField: {
|
||||||
|
component: null,
|
||||||
|
},
|
||||||
|
format: (row, dashIfEmpty) => dashIfEmpty(row?.agencyMode?.name),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'close',
|
|
||||||
label: t('list.close'),
|
|
||||||
field: (row) => (row?.hour ? toTimeFormat(row?.hour) : '-'),
|
|
||||||
sortable: true,
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'price',
|
name: 'price',
|
||||||
label: t('list.price'),
|
label: t('list.price'),
|
||||||
field: (row) => (row?.price ? toCurrency(row.price) : '-'),
|
cardVisible: true,
|
||||||
sortable: true,
|
format: (row) => toCurrency(row.price),
|
||||||
align: 'left',
|
columnFilter: {
|
||||||
|
inWhere: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
name: 'hour',
|
||||||
|
label: t('list.close'),
|
||||||
|
cardVisible: true,
|
||||||
|
format: (row) => toTimeFormat(row.hour),
|
||||||
|
hidden: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'actions',
|
|
||||||
label: '',
|
|
||||||
sortable: false,
|
|
||||||
align: 'right',
|
align: 'right',
|
||||||
|
name: 'tableActions',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
title: t('list.zoneSummary'),
|
||||||
|
icon: 'preview',
|
||||||
|
action: (row) => viewSummary(row.id, ZoneSummary),
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('globals.clone'),
|
||||||
|
icon: 'vn:clone',
|
||||||
|
action: (row) => handleClone(row.id),
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -84,6 +130,7 @@ const handleClone = (id) => {
|
||||||
() => clone(id)
|
() => clone(id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => (stateStore.rightDrawer = true));
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -91,82 +138,72 @@ onMounted(() => (stateStore.rightDrawer = true));
|
||||||
<ZoneSearchbar />
|
<ZoneSearchbar />
|
||||||
<RightMenu>
|
<RightMenu>
|
||||||
<template #right-panel>
|
<template #right-panel>
|
||||||
<ZoneFilterPanel data-key="ZoneList" :expr-builder="exprBuilder" />
|
<ZoneFilterPanel data-key="Zones" />
|
||||||
</template>
|
</template>
|
||||||
</RightMenu>
|
</RightMenu>
|
||||||
<QPage class="column items-center q-pa-md">
|
<VnTable
|
||||||
<div class="vn-card-list">
|
ref="tableRef"
|
||||||
<VnPaginate
|
data-key="Zones"
|
||||||
data-key="ZoneList"
|
url="Zones"
|
||||||
url="Zones"
|
:create="{
|
||||||
:filter="{
|
urlCreate: 'Zones',
|
||||||
include: { relation: 'agencyMode', scope: { fields: ['name'] } },
|
title: t('list.createZone'),
|
||||||
}"
|
onDataSaved: ({ id }) => tableRef.redirect(`${id}/location`),
|
||||||
:limit="20"
|
formInitialData: {},
|
||||||
auto-load
|
}"
|
||||||
>
|
:user-filter="tableFilter"
|
||||||
<template #body="{ rows }">
|
:columns="columns"
|
||||||
<div class="q-pa-md">
|
redirect="zone"
|
||||||
<QTable
|
:right-search="false"
|
||||||
:rows="rows"
|
auto-load
|
||||||
:columns="columns"
|
>
|
||||||
row-key="clientId"
|
<template #more-create-dialog="{ data }">
|
||||||
class="full-width"
|
<VnSelect
|
||||||
@row-click="redirectToZoneSummary"
|
url="AgencyModes"
|
||||||
>
|
v-model="data.agencyModeFk"
|
||||||
<template #header="props">
|
option-value="id"
|
||||||
<QTr :props="props" class="bg">
|
option-label="name"
|
||||||
<QTh
|
:label="t('list.agency')"
|
||||||
v-for="col in props.cols"
|
/>
|
||||||
:key="col.name"
|
<VnInput
|
||||||
:props="props"
|
v-model="data.price"
|
||||||
>
|
:label="t('list.price')"
|
||||||
{{ t(col.label) }}
|
min="0"
|
||||||
<QTooltip v-if="col.tooltip">{{
|
type="number"
|
||||||
col.tooltip
|
required="true"
|
||||||
}}</QTooltip>
|
/>
|
||||||
</QTh>
|
<VnInput
|
||||||
</QTr>
|
v-model="data.bonus"
|
||||||
</template>
|
:label="t('list.bonus')"
|
||||||
|
min="0"
|
||||||
<template #body-cell="props">
|
type="number"
|
||||||
<QTd :props="props">
|
/>
|
||||||
<QTr :props="props" class="cursor-pointer">
|
<VnInput
|
||||||
{{ props.value }}
|
v-model="data.travelingDays"
|
||||||
</QTr>
|
:label="t('list.travelingDays')"
|
||||||
</QTd>
|
type="number"
|
||||||
</template>
|
min="0"
|
||||||
<template #body-cell-actions="props">
|
/>
|
||||||
<QTd :props="props" class="q-gutter-x-sm">
|
<VnInputTime v-model="data.hour" :label="t('list.close')" />
|
||||||
<QIcon
|
<VnSelect
|
||||||
name="vn:clone"
|
url="Warehouses"
|
||||||
size="sm"
|
v-model="data.warehouseFK"
|
||||||
color="primary"
|
option-value="id"
|
||||||
@click.stop="handleClone(props.row.id)"
|
option-label="name"
|
||||||
>
|
:label="t('list.warehouse')"
|
||||||
<QTooltip>{{ t('globals.clone') }}</QTooltip>
|
:options="warehouseOptions"
|
||||||
</QIcon>
|
/>
|
||||||
<QIcon
|
<QCheckbox
|
||||||
name="preview"
|
v-model="data.isVolumetric"
|
||||||
size="sm"
|
:label="t('list.isVolumetric')"
|
||||||
color="primary"
|
:toggle-indeterminate="false"
|
||||||
@click.stop="
|
/>
|
||||||
viewSummary(props.row.id, ZoneSummary)
|
</template>
|
||||||
"
|
</VnTable>
|
||||||
>
|
|
||||||
<QTooltip>{{ t('Preview') }}</QTooltip>
|
|
||||||
</QIcon>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
</QTable>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VnPaginate>
|
|
||||||
</div>
|
|
||||||
<QPageSticky position="bottom-right" :offset="[18, 18]">
|
|
||||||
<QBtn :to="{ path: `/zone/create` }" fab icon="add" color="primary">
|
|
||||||
<QTooltip>{{ t('list.create') }}</QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
</QPageSticky>
|
|
||||||
</QPage>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search zone: Buscar zona
|
||||||
|
You can search zones by id or name: Puedes buscar zonas por id o nombre
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -18,9 +18,16 @@ list:
|
||||||
create: Create zone
|
create: Create zone
|
||||||
openSummary: Details
|
openSummary: Details
|
||||||
searchZone: Search zones
|
searchZone: Search zones
|
||||||
|
searchLocation: Search locations
|
||||||
searchInfo: Search zone by id or name
|
searchInfo: Search zone by id or name
|
||||||
confirmCloneTitle: All it's properties will be copied
|
confirmCloneTitle: All it's properties will be copied
|
||||||
confirmCloneSubtitle: Do you want to clone this zone?
|
confirmCloneSubtitle: Do you want to clone this zone?
|
||||||
|
travelingDays: Traveling days
|
||||||
|
warehouse: Warehouse
|
||||||
|
bonus: Bonus
|
||||||
|
isVolumetric: Volumetric
|
||||||
|
createZone: Create zone
|
||||||
|
zoneSummary: Summary
|
||||||
create:
|
create:
|
||||||
name: Name
|
name: Name
|
||||||
warehouse: Warehouse
|
warehouse: Warehouse
|
||||||
|
@ -30,6 +37,8 @@ create:
|
||||||
price: Price
|
price: Price
|
||||||
bonus: Bonus
|
bonus: Bonus
|
||||||
volumetric: Volumetric
|
volumetric: Volumetric
|
||||||
|
itemMaxSize: Max m³
|
||||||
|
inflation: Inflation
|
||||||
summary:
|
summary:
|
||||||
agency: Agency
|
agency: Agency
|
||||||
price: Price
|
price: Price
|
||||||
|
|
|
@ -18,9 +18,16 @@ list:
|
||||||
create: Crear zona
|
create: Crear zona
|
||||||
openSummary: Detalles
|
openSummary: Detalles
|
||||||
searchZone: Buscar zonas
|
searchZone: Buscar zonas
|
||||||
|
searchLocation: Buscar localizaciones
|
||||||
jon marked this conversation as resolved
jsegarra
commented
El texto de la sección aparece como ubicaciones en vez de localizaciones El texto de la sección aparece como ubicaciones en vez de localizaciones
|
|||||||
searchInfo: Buscar zonas por identificador o nombre
|
searchInfo: Buscar zonas por identificador o nombre
|
||||||
confirmCloneTitle: Todas sus propiedades serán copiadas
|
confirmCloneTitle: Todas sus propiedades serán copiadas
|
||||||
confirmCloneSubtitle: ¿Seguro que quieres clonar esta zona?
|
confirmCloneSubtitle: ¿Seguro que quieres clonar esta zona?
|
||||||
|
travelingDays: Días de viaje
|
||||||
|
warehouse: Almacén
|
||||||
|
bonus: Bonus
|
||||||
jgallego marked this conversation as resolved
jgallego
commented
hour es hora..si quieres que sea hora de cierre, cambia la clave sino puede ser muy confuso o colisionar en el futuro con otras claves hour es hora..si quieres que sea hora de cierre, cambia la clave sino puede ser muy confuso o colisionar en el futuro con otras claves
|
|||||||
|
isVolumetric: Volumétrico
|
||||||
|
createZone: Crear zona
|
||||||
|
zoneSummary: Resumen
|
||||||
create:
|
create:
|
||||||
name: Nombre
|
name: Nombre
|
||||||
warehouse: Almacén
|
warehouse: Almacén
|
||||||
|
@ -30,6 +37,8 @@ create:
|
||||||
price: Precio
|
price: Precio
|
||||||
bonus: Bonificación
|
bonus: Bonificación
|
||||||
volumetric: Volumétrico
|
volumetric: Volumétrico
|
||||||
|
itemMaxSize: Medida máxima
|
||||||
|
inflation: Inflación
|
||||||
summary:
|
summary:
|
||||||
agency: Agencia
|
agency: Agencia
|
||||||
price: Precio
|
price: Precio
|
||||||
|
|
|
@ -50,33 +50,6 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Zone/ZoneDeliveryDays.vue'),
|
component: () => import('src/pages/Zone/ZoneDeliveryDays.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'create',
|
|
||||||
name: 'ZoneCreate',
|
|
||||||
meta: {
|
|
||||||
title: 'zoneCreate',
|
|
||||||
icon: 'create',
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Zone/ZoneCreate.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':id/edit',
|
|
||||||
name: 'ZoneEdit',
|
|
||||||
meta: {
|
|
||||||
title: 'zoneEdit',
|
|
||||||
icon: 'edit',
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Zone/ZoneCreate.vue'),
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// path: 'counter',
|
|
||||||
// name: 'ZoneCounter',
|
|
||||||
// meta: {
|
|
||||||
// title: 'zoneCounter',
|
|
||||||
// icon: 'add_circle',
|
|
||||||
// },
|
|
||||||
// component: () => import('src/pages/Zone/ZoneCounter.vue'),
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
name: 'ZoneUpcomingDeliveries',
|
name: 'ZoneUpcomingDeliveries',
|
||||||
path: 'upcoming-deliveries',
|
path: 'upcoming-deliveries',
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
describe('ZoneBasicData', () => {
|
||||||
jon marked this conversation as resolved
Outdated
jsegarra
commented
No forma parte del test pero si del componente. Inflación permite texto y deberia ser numérico No forma parte del test pero si del componente. Inflación permite texto y deberia ser numérico
|
|||||||
|
const notification = '.q-notification__message';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1280, 720);
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit('/#/zone/4/basic-data');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the name is empty', () => {
|
||||||
|
cy.get('.q-card > :nth-child(1)').clear();
|
||||||
|
cy.get('.q-btn-group > .q-btn--standard').click();
|
||||||
|
cy.get(notification).should('contains.text', "can't be blank");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should edit the basicData's zone", () => {
|
||||||
|
cy.get('.q-card > :nth-child(1)').type(' modified');
|
||||||
|
cy.get('.q-btn-group > .q-btn--standard').click();
|
||||||
|
cy.get(notification).should('contains.text', 'Data saved');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,38 @@
|
||||||
|
describe('ZoneCreate', () => {
|
||||||
|
const notification = '.q-notification__message';
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
Name: { val: 'Zone pickup D' },
|
||||||
|
Price: { val: '3' },
|
||||||
|
Bonus: { val: '0' },
|
||||||
|
'Traveling days': { val: '0' },
|
||||||
|
Warehouse: { val: 'Algemesi', type: 'select' },
|
||||||
|
Volumetric: { val: 'true', type: 'checkbox' },
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1280, 720);
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit('/#/zone/list');
|
||||||
|
cy.get('.q-page-sticky > div > .q-btn').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if an agency has not been selected', () => {
|
||||||
|
cy.fillInForm({
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
cy.get('input[aria-label="Close"]').type('10:00');
|
||||||
|
cy.get('.q-mt-lg > .q-btn--standard').click();
|
||||||
|
cy.get(notification).should('contains.text', 'Agency cannot be blank');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a zone', () => {
|
||||||
|
cy.fillInForm({
|
||||||
|
...data,
|
||||||
|
Agency: { val: 'inhouse pickup', type: 'select' },
|
||||||
|
});
|
||||||
|
cy.get('input[aria-label="Close"]').type('10:00');
|
||||||
|
cy.get('.q-mt-lg > .q-btn--standard').click();
|
||||||
|
cy.get(notification).should('contains.text', 'Data created');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,15 +1,18 @@
|
||||||
describe('ZoneList', () => {
|
describe('ZoneList', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.viewport(1920, 1080);
|
cy.viewport(1280, 720);
|
||||||
cy.login('developer');
|
cy.login('developer');
|
||||||
cy.visit(`/#/zone/list`);
|
cy.visit('/#/zone/list');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should open the details', () => {
|
it('should filter by agency', () => {
|
||||||
cy.get(':nth-child(1) > .text-right > .material-symbols-outlined').click();
|
cy.get(
|
||||||
|
':nth-child(1) > .column > .q-field > .q-field__inner > .q-field__control > .q-field__control-container'
|
||||||
|
).type('{downArrow}{enter}');
|
||||||
});
|
});
|
||||||
it('should redirect to summary', () => {
|
|
||||||
cy.waitForElement('.q-page');
|
it('should open the zone summary', () => {
|
||||||
cy.get('tbody > :nth-child(1)').click();
|
cy.get('input[aria-label="Name"]').type('zone refund');
|
||||||
|
cy.get('.q-scrollarea__content > .q-btn--standard > .q-btn__content').click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
describe('ZoneWarehouse', () => {
|
||||||
|
const data = {
|
||||||
|
Warehouse: { val: 'Algemesi', type: 'select' },
|
||||||
|
};
|
||||||
|
const deviceProductionField =
|
||||||
|
'.vn-row > :nth-child(1) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container';
|
||||||
|
const dataError = "ER_DUP_ENTRY: Duplicate entry '2-2' for key 'zoneFk'";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1280, 720);
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/zone/2/warehouses`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the warehouse chosen is already put in the zone', () => {
|
||||||
|
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
|
||||||
|
cy.get(deviceProductionField).click();
|
||||||
|
cy.get(deviceProductionField).type('{upArrow}{enter}');
|
||||||
|
cy.get('.q-notification__message').should('have.text', dataError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a warehouse', () => {
|
||||||
|
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
|
||||||
|
cy.get(deviceProductionField).click();
|
||||||
|
cy.fillInForm(data);
|
||||||
|
cy.get('.q-mt-lg > .q-btn--standard').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a warehouse', () => {
|
||||||
|
cy.get('tbody > :nth-child(2) > :nth-child(2) > .q-icon').click();
|
||||||
|
cy.get('.q-card__actions > .q-btn--flat > .q-btn__content').click();
|
||||||
|
cy.reload();
|
||||||
|
});
|
||||||
|
});
|
|
@ -105,6 +105,12 @@ Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => {
|
||||||
case 'date':
|
case 'date':
|
||||||
cy.wrap(el).type(val.split('-').join(''));
|
cy.wrap(el).type(val.split('-').join(''));
|
||||||
break;
|
break;
|
||||||
|
case 'time':
|
||||||
|
cy.wrap(el).click();
|
||||||
|
cy.get('.q-time .q-time__clock').contains(val.h).click();
|
||||||
|
cy.get('.q-time .q-time__clock').contains(val.m).click();
|
||||||
|
cy.get('.q-time .q-time__link').contains(val.x).click();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cy.wrap(el).type(val);
|
cy.wrap(el).type(val);
|
||||||
break;
|
break;
|
||||||
|
|
Revisar la traducción para esta acción