forked from verdnatura/salix-front
Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 6677-urlSelectFilter
This commit is contained in:
commit
021d851655
|
@ -58,7 +58,7 @@ module.exports = {
|
||||||
rules: {
|
rules: {
|
||||||
'prefer-promise-reject-errors': 'off',
|
'prefer-promise-reject-errors': 'off',
|
||||||
'no-unused-vars': 'warn',
|
'no-unused-vars': 'warn',
|
||||||
|
"vue/no-multiple-template-root": "off" ,
|
||||||
// allow debugger during development only
|
// allow debugger during development only
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,13 @@ import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
showEntityField: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onDataSaved']);
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -73,7 +80,7 @@ const onDataSaved = (data) => {
|
||||||
:rules="validate('bankEntity.countryFk')"
|
:rules="validate('bankEntity.countryFk')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div v-if="showEntityField" class="col">
|
||||||
<QInput :label="t('id')" v-model="data.id" />
|
<QInput :label="t('id')" v-model="data.id" />
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
|
@ -19,8 +19,8 @@ const cityFormData = reactive({
|
||||||
|
|
||||||
const provincesOptions = ref([]);
|
const provincesOptions = ref([]);
|
||||||
|
|
||||||
const onDataSaved = () => {
|
const onDataSaved = (dataSaved) => {
|
||||||
emit('onDataSaved');
|
emit('onDataSaved', dataSaved);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -28,16 +28,24 @@ const countriesOptions = ref([]);
|
||||||
const provincesOptions = ref([]);
|
const provincesOptions = ref([]);
|
||||||
const townsLocationOptions = ref([]);
|
const townsLocationOptions = ref([]);
|
||||||
|
|
||||||
const onDataSaved = () => {
|
const onDataSaved = (dataSaved) => {
|
||||||
emit('onDataSaved');
|
emit('onDataSaved', dataSaved);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCityCreated = async () => {
|
const onCityCreated = async ({ name, provinceFk }, formData) => {
|
||||||
await townsFetchDataRef.value.fetch();
|
await townsFetchDataRef.value.fetch();
|
||||||
|
formData.townFk = townsLocationOptions.value.find((town) => town.name === name).id;
|
||||||
|
formData.provinceFk = provinceFk;
|
||||||
|
formData.countryFk = provincesOptions.value.find(
|
||||||
|
(province) => province.id === provinceFk
|
||||||
|
).countryFk;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onProvinceCreated = async () => {
|
const onProvinceCreated = async ({ name }, formData) => {
|
||||||
await provincesFetchDataRef.value.fetch();
|
await provincesFetchDataRef.value.fetch();
|
||||||
|
formData.provinceFk = provincesOptions.value.find(
|
||||||
|
(province) => province.name === name
|
||||||
|
).id;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -88,7 +96,9 @@ const onProvinceCreated = async () => {
|
||||||
:roles-allowed-to-create="['deliveryAssistant']"
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
>
|
>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewCityForm @on-data-saved="onCityCreated($event)" />
|
<CreateNewCityForm
|
||||||
|
@on-data-saved="onCityCreated($event, data)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectCreate>
|
</VnSelectCreate>
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,7 +117,7 @@ const onProvinceCreated = async () => {
|
||||||
>
|
>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewProvinceForm
|
<CreateNewProvinceForm
|
||||||
@on-data-saved="onProvinceCreated($event)"
|
@on-data-saved="onProvinceCreated($event, data)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectCreate>
|
</VnSelectCreate>
|
||||||
|
@ -131,7 +141,7 @@ const onProvinceCreated = async () => {
|
||||||
es:
|
es:
|
||||||
New postcode: Nuevo código postal
|
New postcode: Nuevo código postal
|
||||||
Please, ensure you put the correct data!: ¡Por favor, asegúrese de poner los datos correctos!
|
Please, ensure you put the correct data!: ¡Por favor, asegúrese de poner los datos correctos!
|
||||||
City: Ciudad
|
City: Población
|
||||||
Province: Provincia
|
Province: Provincia
|
||||||
Country: País
|
Country: País
|
||||||
Postcode: Código postal
|
Postcode: Código postal
|
||||||
|
|
|
@ -19,8 +19,8 @@ const provinceFormData = reactive({
|
||||||
|
|
||||||
const autonomiesOptions = ref([]);
|
const autonomiesOptions = ref([]);
|
||||||
|
|
||||||
const onDataSaved = () => {
|
const onDataSaved = (dataSaved) => {
|
||||||
emit('onDataSaved');
|
emit('onDataSaved', dataSaved);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -225,15 +225,19 @@ function getDifferences(obj1, obj2) {
|
||||||
delete obj2.$index;
|
delete obj2.$index;
|
||||||
|
|
||||||
for (let key in obj1) {
|
for (let key in obj1) {
|
||||||
if (obj2[key] && obj1[key] !== obj2[key]) {
|
if (obj2[key] && JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
|
||||||
diff[key] = obj2[key];
|
diff[key] = obj2[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let key in obj2) {
|
for (let key in obj2) {
|
||||||
if (obj1[key] === undefined || obj1[key] !== obj2[key]) {
|
if (
|
||||||
|
obj1[key] === undefined ||
|
||||||
|
JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])
|
||||||
|
) {
|
||||||
diff[key] = obj2[key];
|
diff[key] = obj2[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,13 +128,14 @@ async function save() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||||
|
let response
|
||||||
if ($props.urlCreate) {
|
if ($props.urlCreate) {
|
||||||
await axios.post($props.urlCreate, body);
|
response = await axios.post($props.urlCreate, body);
|
||||||
notify('globals.dataCreated', 'positive');
|
notify('globals.dataCreated', 'positive');
|
||||||
} else {
|
} else {
|
||||||
await axios.patch($props.urlUpdate || $props.url, body);
|
response = await axios.patch($props.urlUpdate || $props.url, body);
|
||||||
}
|
}
|
||||||
emit('onDataSaved', formData.value);
|
emit('onDataSaved', formData.value, response);
|
||||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -176,13 +177,6 @@ watch(formUrl, async () => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QBanner
|
|
||||||
v-if="$props.observeFormChanges && hasChanges"
|
|
||||||
class="text-white bg-warning full-width"
|
|
||||||
>
|
|
||||||
<QIcon name="warning" size="md" class="q-mr-md" />
|
|
||||||
<span>{{ t('globals.changesToSave') }}</span>
|
|
||||||
</QBanner>
|
|
||||||
<div class="column items-center full-width">
|
<div class="column items-center full-width">
|
||||||
<QForm
|
<QForm
|
||||||
v-if="formData"
|
v-if="formData"
|
||||||
|
|
|
@ -42,8 +42,8 @@ const { t } = useI18n();
|
||||||
const closeButton = ref(null);
|
const closeButton = ref(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
const onDataSaved = () => {
|
const onDataSaved = (dataSaved) => {
|
||||||
emit('onDataSaved');
|
emit('onDataSaved', dataSaved);
|
||||||
closeForm();
|
closeForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ const closeForm = () => {
|
||||||
:default-actions="false"
|
:default-actions="false"
|
||||||
:url-create="urlCreate"
|
:url-create="urlCreate"
|
||||||
:model="model"
|
:model="model"
|
||||||
@on-data-saved="onDataSaved()"
|
@on-data-saved="onDataSaved($event)"
|
||||||
>
|
>
|
||||||
<template #form="{ data, validate }">
|
<template #form="{ data, validate }">
|
||||||
<span ref="closeButton" class="close-icon" v-close-popup>
|
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, computed } from 'vue';
|
import { onMounted, computed } from 'vue';
|
||||||
import { Dark, Quasar, useQuasar } from 'quasar';
|
import { Dark, Quasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { localeEquivalence } from "src/i18n/index";
|
import { localeEquivalence } from 'src/i18n/index';
|
||||||
|
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
const quasar = useQuasar();
|
import { useClipboard } from 'src/composables/useClipboard';
|
||||||
|
const { copyText } = useClipboard();
|
||||||
const userLocale = computed({
|
const userLocale = computed({
|
||||||
get() {
|
get() {
|
||||||
return locale.value;
|
return locale.value;
|
||||||
|
@ -21,7 +21,7 @@ const userLocale = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
locale.value = value;
|
locale.value = value;
|
||||||
|
|
||||||
value = localeEquivalence[value] ?? value
|
value = localeEquivalence[value] ?? value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* @vite-ignore */
|
/* @vite-ignore */
|
||||||
|
@ -81,12 +81,8 @@ function logout() {
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyUserToken(){
|
function copyUserToken() {
|
||||||
navigator.clipboard.writeText(session.getToken());
|
copyText(session.getToken(), { label: 'components.userPanel.copyToken' });
|
||||||
quasar.notify({
|
|
||||||
type: 'positive',
|
|
||||||
message: t('components.userPanel.copyToken'),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -129,7 +125,11 @@ function copyUserToken(){
|
||||||
<div class="text-subtitle1 q-mt-md">
|
<div class="text-subtitle1 q-mt-md">
|
||||||
<strong>{{ user.nickname }}</strong>
|
<strong>{{ user.nickname }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-subtitle3 text-grey-7 q-mb-xs copyUserToken" @click="copyUserToken()" >@{{ user.name }}
|
<div
|
||||||
|
class="text-subtitle3 text-grey-7 q-mb-xs copyText"
|
||||||
|
@click="copyUserToken()"
|
||||||
|
>
|
||||||
|
@{{ user.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<QBtn
|
<QBtn
|
||||||
|
@ -152,8 +152,8 @@ function copyUserToken(){
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyUserToken {
|
.copyText {
|
||||||
&:hover{
|
&:hover {
|
||||||
cursor: alias;
|
cursor: alias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'update:options']);
|
const emit = defineEmits(['update:modelValue', 'update:options', 'keyup.enter']);
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -32,6 +32,10 @@ const styleAttrs = computed(() => {
|
||||||
}
|
}
|
||||||
: {};
|
: {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onEnterPress = () => {
|
||||||
|
emit('keyup.enter');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -41,6 +45,7 @@ const styleAttrs = computed(() => {
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
type="text"
|
type="text"
|
||||||
:class="{ required: $attrs.required }"
|
:class="{ required: $attrs.required }"
|
||||||
|
@keyup.enter="onEnterPress()"
|
||||||
>
|
>
|
||||||
<template v-if="$slots.prepend" #prepend>
|
<template v-if="$slots.prepend" #prepend>
|
||||||
<slot name="prepend" />
|
<slot name="prepend" />
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
<script setup>
|
||||||
|
import {computed, ref} from 'vue';
|
||||||
|
import { toHour} from 'src/filters';
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import isValidDate from "filters/isValidDate";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
readonly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isOutlined: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
const value = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
const [hours, minutes] = value.split(':')
|
||||||
|
const date = new Date()
|
||||||
|
date.setUTCHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0)
|
||||||
|
emit('update:modelValue', value ? date.toISOString() : null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDateUpdate = (date) => {
|
||||||
|
internalValue.value = date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
value.value = internalValue.value;
|
||||||
|
};
|
||||||
|
const formatTime = (dateString) => {
|
||||||
|
if (!isValidDate(dateString)){
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const date = new Date(dateString || '');
|
||||||
|
return `${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const internalValue = ref(formatTime(value))
|
||||||
|
|
||||||
|
const styleAttrs = computed(() => {
|
||||||
|
return props.isOutlined
|
||||||
|
? {
|
||||||
|
dense: true,
|
||||||
|
outlined: true,
|
||||||
|
rounded: true,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QInput
|
||||||
|
class="vn-input-time"
|
||||||
|
rounded
|
||||||
|
readonly
|
||||||
|
:model-value="toHour(value)"
|
||||||
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon name="event" class="cursor-pointer">
|
||||||
|
<QPopupProxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
:no-parent-event="props.readonly"
|
||||||
|
>
|
||||||
|
<QTime
|
||||||
|
:format24h="false"
|
||||||
|
:model-value="formatTime(value)"
|
||||||
|
@update:model-value="onDateUpdate"
|
||||||
|
>
|
||||||
|
<div class="row items-center justify-end q-gutter-sm">
|
||||||
|
<QBtn :label="t('Cancel')" color="primary" flat v-close-popup />
|
||||||
|
<QBtn label="Ok" color="primary" flat @click="save" v-close-popup />
|
||||||
|
</div>
|
||||||
|
</QTime>
|
||||||
|
</QPopupProxy>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</QInput>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vn-input-time.q-field--standard.q-field--readonly .q-field__control:before {
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vn-input-time.q-field--outlined.q-field--readonly .q-field__control:before {
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Cancel: Cancelar
|
||||||
|
</i18n>
|
|
@ -0,0 +1,135 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, toRefs, computed, watch, onMounted } from 'vue';
|
||||||
|
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
import VnSelectCreate from 'components/common/VnSelectCreate.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
const emit = defineEmits(['update:modelValue', 'update:options']);
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const postcodesOptions = ref([]);
|
||||||
|
const postcodesRef = ref(null);
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number, Object],
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
optionLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
optionValue: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
filterOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
isClearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
defaultFilter: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { options } = toRefs($props);
|
||||||
|
const myOptions = ref([]);
|
||||||
|
const myOptionsOriginal = ref([]);
|
||||||
|
|
||||||
|
const value = computed({
|
||||||
|
get() {
|
||||||
|
return $props.modelValue;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
locationFilter()
|
||||||
|
});
|
||||||
|
|
||||||
|
function setOptions(data) {
|
||||||
|
myOptions.value = JSON.parse(JSON.stringify(data));
|
||||||
|
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
setOptions(options.value);
|
||||||
|
|
||||||
|
watch(options, (newValue) => {
|
||||||
|
setOptions(newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
function showLabel(data) {
|
||||||
|
return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function locationFilter(search) {
|
||||||
|
let where = { search };
|
||||||
|
postcodesRef.value.fetch({filter:{ where}, limit: 30});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFetch( data) {
|
||||||
|
postcodesOptions.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="postcodesRef"
|
||||||
|
url="Postcodes/filter"
|
||||||
|
@on-fetch="(data) =>handleFetch(data)"
|
||||||
|
/>
|
||||||
|
<VnSelectCreate
|
||||||
|
v-if="postcodesRef"
|
||||||
|
v-model="value"
|
||||||
|
:options="postcodesOptions"
|
||||||
|
:label="t('Location')"
|
||||||
|
:option-label="showLabel"
|
||||||
|
:placeholder="t('Search by postalCode, town, province or country')"
|
||||||
|
@input-value="locationFilter"
|
||||||
|
:default-filter="false"
|
||||||
|
:input-debounce="300"
|
||||||
|
:class="{ required: $attrs.required }"
|
||||||
|
v-bind="$attrs"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
clearable
|
||||||
|
hide-selected
|
||||||
|
fill-input
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CreateNewPostcode @on-data-saved="locationFilter()" />
|
||||||
|
</template>
|
||||||
|
<template #option="{itemProps, opt}">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection v-if="opt">
|
||||||
|
<QItemLabel>{{ opt.code }}</QItemLabel>
|
||||||
|
<QItemLabel caption>{{ showLabel(opt) }}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.add-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: $primary;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Location: Ubicación
|
||||||
|
Search by postalcode, town, province or country: Buscar por código postal, ciudad o país
|
||||||
|
</i18n>
|
|
@ -14,7 +14,7 @@ const $props = defineProps({
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
optionLabel: {
|
optionLabel: {
|
||||||
type: String,
|
type: [String],
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
optionValue: {
|
optionValue: {
|
||||||
|
@ -26,7 +26,7 @@ const $props = defineProps({
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
filterOptions: {
|
filterOptions: {
|
||||||
type: Array,
|
type: [Array],
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
isClearable: {
|
isClearable: {
|
||||||
|
|
|
@ -9,6 +9,7 @@ const $props = defineProps({
|
||||||
isSelected: { type: Boolean, default: false },
|
isSelected: { type: Boolean, default: false },
|
||||||
title: { type: String, default: null },
|
title: { type: String, default: null },
|
||||||
showCheckbox: { type: Boolean, default: false },
|
showCheckbox: { type: Boolean, default: false },
|
||||||
|
hasInfoIcons: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['toggleCardCheck']);
|
const emit = defineEmits(['toggleCardCheck']);
|
||||||
|
@ -39,6 +40,9 @@ const toggleCardCheck = (item) => {
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
<div class="card-list-body">
|
<div class="card-list-body">
|
||||||
|
<div v-if="hasInfoIcons" class="column q-mr-md q-gutter-y-xs">
|
||||||
|
<slot name="info-icons" />
|
||||||
|
</div>
|
||||||
<div class="list-items row flex-wrap-wrap">
|
<div class="list-items row flex-wrap-wrap">
|
||||||
<slot name="list-items" />
|
<slot name="list-items" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { dashIfEmpty } from 'src/filters';
|
import { dashIfEmpty } from 'src/filters';
|
||||||
|
|
||||||
|
import { useClipboard } from 'src/composables/useClipboard';
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
label: { type: String, default: null },
|
label: { type: String, default: null },
|
||||||
value: {
|
value: {
|
||||||
|
@ -10,8 +11,19 @@ const $props = defineProps({
|
||||||
},
|
},
|
||||||
info: { type: String, default: null },
|
info: { type: String, default: null },
|
||||||
dash: { type: Boolean, default: true },
|
dash: { type: Boolean, default: true },
|
||||||
|
copy: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
const isBooleanValue = computed(() => typeof $props.value === 'boolean');
|
const isBooleanValue = computed(() => typeof $props.value === 'boolean');
|
||||||
|
|
||||||
|
const { copyText } = useClipboard();
|
||||||
|
|
||||||
|
function copyValueText() {
|
||||||
|
copyText($props.value, {
|
||||||
|
component: {
|
||||||
|
copyValue: $props.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.label,
|
.label,
|
||||||
|
@ -48,5 +60,16 @@ const isBooleanValue = computed(() => typeof $props.value === 'boolean');
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="copy" v-if="$props.copy && $props.value" @click="copyValueText()">
|
||||||
|
<QIcon name="Content_Copy" color="primary" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.copy {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
export function useClipboard() {
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} value Value to send to clipboardAPI
|
||||||
|
* @param {Object} {label, component} Refer to Quasar notify configuration. Label is the text to translate
|
||||||
|
*/
|
||||||
|
function copyText(value, { label = 'components.VnLv.copyText', component = {} }) {
|
||||||
|
navigator.clipboard.writeText(value);
|
||||||
|
quasar.notify({ type: 'positive', message: t(label, component) });
|
||||||
|
}
|
||||||
|
return { copyText };
|
||||||
|
}
|
|
@ -17,9 +17,9 @@ a {
|
||||||
// Removes chrome autofill background
|
// Removes chrome autofill background
|
||||||
input:-webkit-autofill,
|
input:-webkit-autofill,
|
||||||
select:-webkit-autofill {
|
select:-webkit-autofill {
|
||||||
color: var(--vn-text) ;
|
color: var(--vn-text);
|
||||||
font-family: $typography-font-family;
|
font-family: $typography-font-family;
|
||||||
-webkit-text-fill-color: var(--vn-text) ;
|
-webkit-text-fill-color: var(--vn-text);
|
||||||
-webkit-background-clip: text !important;
|
-webkit-background-clip: text !important;
|
||||||
background-clip: text !important;
|
background-clip: text !important;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,10 @@ body.body--dark {
|
||||||
background-color: var(--vn-dark);
|
background-color: var(--vn-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-vn-text {
|
||||||
|
color: var(--vn-text);
|
||||||
|
}
|
||||||
|
|
||||||
.vn-card {
|
.vn-card {
|
||||||
background-color: var(--vn-gray);
|
background-color: var(--vn-gray);
|
||||||
color: var(--vn-text);
|
color: var(--vn-text);
|
||||||
|
|
|
@ -8,11 +8,13 @@ import toPercentage from './toPercentage';
|
||||||
import toLowerCamel from './toLowerCamel';
|
import toLowerCamel from './toLowerCamel';
|
||||||
import dashIfEmpty from './dashIfEmpty';
|
import dashIfEmpty from './dashIfEmpty';
|
||||||
import dateRange from './dateRange';
|
import dateRange from './dateRange';
|
||||||
|
import toHour from './toHour';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
toLowerCase,
|
toLowerCase,
|
||||||
toLowerCamel,
|
toLowerCamel,
|
||||||
toDate,
|
toDate,
|
||||||
|
toHour,
|
||||||
toDateString,
|
toDateString,
|
||||||
toDateHour,
|
toDateHour,
|
||||||
toRelativeDate,
|
toRelativeDate,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function isValidDate(date) {
|
||||||
|
return !isNaN(new Date(date).getTime());
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import isValidDate from 'filters/isValidDate';
|
||||||
|
|
||||||
|
export default function toHour(date) {
|
||||||
|
if (!isValidDate(date)) {
|
||||||
|
return '--:--';
|
||||||
|
}
|
||||||
|
const dateHour = new Date(date);
|
||||||
|
let hours = dateHour.getUTCHours();
|
||||||
|
hours = hours % 12;
|
||||||
|
hours = hours ? hours : 12;
|
||||||
|
|
||||||
|
let minutes = dateHour.getUTCMinutes();
|
||||||
|
minutes = minutes < 10 ? minutes.toString().padStart(2, '0') : minutes;
|
||||||
|
|
||||||
|
return `${hours}:${minutes} ${dateHour.getUTCHours() >= 12 ? 'PM' : 'AM'}`;
|
||||||
|
}
|
|
@ -261,12 +261,89 @@ export default {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
entries: 'Entries',
|
entries: 'Entries',
|
||||||
list: 'List',
|
list: 'List',
|
||||||
createEntry: 'New entry',
|
|
||||||
summary: 'Summary',
|
summary: 'Summary',
|
||||||
create: 'Create',
|
basicData: 'Basic data',
|
||||||
|
buys: 'Buys',
|
||||||
|
notes: 'Notes',
|
||||||
|
log: 'Log',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
newEntry: 'New entry',
|
newEntry: 'New entry',
|
||||||
|
landed: 'Landed',
|
||||||
|
invoiceNumber: 'Invoice number',
|
||||||
|
supplier: 'Supplier',
|
||||||
|
booked: 'Booked',
|
||||||
|
confirmed: 'Confirmed',
|
||||||
|
ordered: 'Ordered',
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
commission: 'Commission',
|
||||||
|
currency: 'Currency',
|
||||||
|
company: 'Company',
|
||||||
|
reference: 'Reference',
|
||||||
|
invoiceNumber: 'Invoice number',
|
||||||
|
ordered: 'Ordered',
|
||||||
|
confirmed: 'Confirmed',
|
||||||
|
booked: 'Booked',
|
||||||
|
raid: 'Raid',
|
||||||
|
excludedFromAvailable: 'Inventory',
|
||||||
|
travelReference: 'Reference',
|
||||||
|
travelAgency: 'Agency',
|
||||||
|
travelShipped: 'Shipped',
|
||||||
|
travelWarehouseOut: 'Warehouse Out',
|
||||||
|
travelDelivered: 'Delivered',
|
||||||
|
travelLanded: 'Landed',
|
||||||
|
travelWarehouseIn: 'Warehouse In',
|
||||||
|
travelReceived: 'Received',
|
||||||
|
buys: 'Buys',
|
||||||
|
quantity: 'Quantity',
|
||||||
|
stickers: 'Stickers',
|
||||||
|
package: 'Package',
|
||||||
|
weight: 'Weight',
|
||||||
|
packing: 'Packing',
|
||||||
|
grouping: 'Grouping',
|
||||||
|
buyingValue: 'Buying value',
|
||||||
|
import: 'Import',
|
||||||
|
pvp: 'PVP',
|
||||||
|
item: 'Item',
|
||||||
|
},
|
||||||
|
basicData: {
|
||||||
|
supplier: 'Supplier',
|
||||||
|
travel: 'Travel',
|
||||||
|
reference: 'Reference',
|
||||||
|
invoiceNumber: 'Invoice number',
|
||||||
|
company: 'Company',
|
||||||
|
currency: 'Currency',
|
||||||
|
commission: 'Commission',
|
||||||
|
observation: 'Observation',
|
||||||
|
ordered: 'Ordered',
|
||||||
|
confirmed: 'Confirmed',
|
||||||
|
booked: 'Booked',
|
||||||
|
raid: 'Raid',
|
||||||
|
excludedFromAvailable: 'Inventory',
|
||||||
|
},
|
||||||
|
buys: {
|
||||||
|
groupingPrice: 'Grouping price',
|
||||||
|
packingPrice: 'Packing price',
|
||||||
|
reference: 'Reference',
|
||||||
|
observations: 'Observations',
|
||||||
|
item: 'Item',
|
||||||
|
description: 'Description',
|
||||||
|
size: 'Size',
|
||||||
|
packing: 'Packing',
|
||||||
|
grouping: 'Grouping',
|
||||||
|
buyingValue: 'Buying value',
|
||||||
|
packagingFk: 'Box',
|
||||||
|
file: 'File',
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
observationType: 'Observation type',
|
||||||
|
description: 'Description',
|
||||||
|
},
|
||||||
|
descriptor: {
|
||||||
|
agency: 'Agency',
|
||||||
|
landed: 'Landed',
|
||||||
|
warehouseOut: 'Warehouse Out',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ticket: {
|
ticket: {
|
||||||
|
@ -807,6 +884,10 @@ export default {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
routes: 'Routes',
|
routes: 'Routes',
|
||||||
cmrsList: 'External CMRs list',
|
cmrsList: 'External CMRs list',
|
||||||
|
RouteList: 'List',
|
||||||
|
create: 'Create',
|
||||||
|
basicData: 'Basic Data',
|
||||||
|
summary: 'Summary',
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
@ -877,6 +958,72 @@ export default {
|
||||||
create: {
|
create: {
|
||||||
supplierName: 'Supplier name',
|
supplierName: 'Supplier name',
|
||||||
},
|
},
|
||||||
|
basicData: {
|
||||||
|
alias: 'Alias',
|
||||||
|
workerFk: 'Responsible',
|
||||||
|
isSerious: 'Verified',
|
||||||
|
isActive: 'Active',
|
||||||
|
isPayMethodChecked: 'PayMethod checked',
|
||||||
|
note: 'Notes',
|
||||||
|
},
|
||||||
|
fiscalData: {
|
||||||
|
name: 'Social name *',
|
||||||
|
nif: 'Tax number *',
|
||||||
|
account: 'Account',
|
||||||
|
sageTaxTypeFk: 'Sage tax type',
|
||||||
|
sageWithholdingFk: 'Sage withholding',
|
||||||
|
sageTransactionTypeFk: 'Sage transaction type',
|
||||||
|
supplierActivityFk: 'Supplier activity',
|
||||||
|
healthRegister: 'Health register',
|
||||||
|
street: 'Street',
|
||||||
|
postcode: 'Postcode',
|
||||||
|
city: 'City *',
|
||||||
|
provinceFk: 'Province',
|
||||||
|
country: 'Country',
|
||||||
|
isTrucker: 'Trucker',
|
||||||
|
isVies: 'Vies',
|
||||||
|
},
|
||||||
|
billingData: {
|
||||||
|
payMethodFk: 'Billing data',
|
||||||
|
payDemFk: 'Payment deadline',
|
||||||
|
payDay: 'Pay day',
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
iban: 'Iban',
|
||||||
|
bankEntity: 'Bank entity',
|
||||||
|
beneficiary: 'Beneficiary',
|
||||||
|
},
|
||||||
|
contacts: {
|
||||||
|
name: 'Name',
|
||||||
|
phone: 'Phone',
|
||||||
|
mobile: 'Mobile',
|
||||||
|
email: 'Email',
|
||||||
|
observation: 'Notes',
|
||||||
|
},
|
||||||
|
addresses: {
|
||||||
|
street: 'Street',
|
||||||
|
postcode: 'Postcode',
|
||||||
|
phone: 'Phone',
|
||||||
|
name: 'Name',
|
||||||
|
city: 'City',
|
||||||
|
province: 'Province',
|
||||||
|
mobile: 'Mobile',
|
||||||
|
},
|
||||||
|
agencyTerms: {
|
||||||
|
agencyFk: 'Agency',
|
||||||
|
minimumM3: 'Minimum M3',
|
||||||
|
packagePrice: 'Package Price',
|
||||||
|
kmPrice: 'Km Price',
|
||||||
|
m3Price: 'M3 Price',
|
||||||
|
routePrice: 'Route price',
|
||||||
|
minimumKm: 'Minimum Km',
|
||||||
|
addRow: 'Add row',
|
||||||
|
},
|
||||||
|
consumption: {
|
||||||
|
entry: 'Entry',
|
||||||
|
date: 'Date',
|
||||||
|
reference: 'Reference',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
travel: {
|
travel: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
|
@ -913,6 +1060,7 @@ export default {
|
||||||
totalEntries: 'Total entries',
|
totalEntries: 'Total entries',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
topbar: {},
|
topbar: {},
|
||||||
userPanel: {
|
userPanel: {
|
||||||
|
@ -936,5 +1084,8 @@ export default {
|
||||||
addToPinned: 'Add to pinned',
|
addToPinned: 'Add to pinned',
|
||||||
removeFromPinned: 'Remove from pinned',
|
removeFromPinned: 'Remove from pinned',
|
||||||
},
|
},
|
||||||
|
VnLv: {
|
||||||
|
copyText: '{copyValue} has been copied to the clipboard',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -261,10 +261,88 @@ export default {
|
||||||
entries: 'Entradas',
|
entries: 'Entradas',
|
||||||
list: 'Listado',
|
list: 'Listado',
|
||||||
summary: 'Resumen',
|
summary: 'Resumen',
|
||||||
create: 'Crear',
|
basicData: 'Datos básicos',
|
||||||
|
buys: 'Compras',
|
||||||
|
notes: 'Notas',
|
||||||
|
log: 'Historial',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
newEntry: 'Nueva entrada',
|
newEntry: 'Nueva entrada',
|
||||||
|
landed: 'F. entrega',
|
||||||
|
invoiceNumber: 'Núm. factura',
|
||||||
|
supplier: 'Proveedor',
|
||||||
|
booked: 'Asentado',
|
||||||
|
confirmed: 'Confirmado',
|
||||||
|
ordered: 'Pedida',
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
commission: 'Comisión',
|
||||||
|
currency: 'Moneda',
|
||||||
|
company: 'Empresa',
|
||||||
|
reference: 'Referencia',
|
||||||
|
invoiceNumber: 'Núm. factura',
|
||||||
|
ordered: 'Pedida',
|
||||||
|
confirmed: 'Confirmado',
|
||||||
|
booked: 'Asentado',
|
||||||
|
raid: 'Redada',
|
||||||
|
excludedFromAvailable: 'Inventario',
|
||||||
|
travelReference: 'Referencia',
|
||||||
|
travelAgency: 'Agencia',
|
||||||
|
travelShipped: 'F. envio',
|
||||||
|
travelWarehouseOut: 'Alm. salida',
|
||||||
|
travelDelivered: 'Enviada',
|
||||||
|
travelLanded: 'F. entrega',
|
||||||
|
travelWarehouseIn: 'Alm. entrada',
|
||||||
|
travelReceived: 'Recibida',
|
||||||
|
buys: 'Compras',
|
||||||
|
quantity: 'Cantidad',
|
||||||
|
stickers: 'Etiquetas',
|
||||||
|
package: 'Embalaje',
|
||||||
|
weight: 'Peso',
|
||||||
|
packing: 'Packing',
|
||||||
|
grouping: 'Grouping',
|
||||||
|
buyingValue: 'Coste',
|
||||||
|
import: 'Importe',
|
||||||
|
pvp: 'PVP',
|
||||||
|
item: 'Artículo',
|
||||||
|
},
|
||||||
|
basicData: {
|
||||||
|
supplier: 'Proveedor',
|
||||||
|
travel: 'Envío',
|
||||||
|
reference: 'Referencia',
|
||||||
|
invoiceNumber: 'Núm. factura',
|
||||||
|
company: 'Empresa',
|
||||||
|
currency: 'Moneda',
|
||||||
|
observation: 'Observación',
|
||||||
|
commission: 'Comisión',
|
||||||
|
ordered: 'Pedida',
|
||||||
|
confirmed: 'Confirmado',
|
||||||
|
booked: 'Asentado',
|
||||||
|
raid: 'Redada',
|
||||||
|
excludedFromAvailable: 'Inventario',
|
||||||
|
},
|
||||||
|
buys: {
|
||||||
|
groupingPrice: 'Precio grouping',
|
||||||
|
packingPrice: 'Precio packing',
|
||||||
|
reference: 'Referencia',
|
||||||
|
observations: 'Observaciónes',
|
||||||
|
item: 'Artículo',
|
||||||
|
description: 'Descripción',
|
||||||
|
size: 'Medida',
|
||||||
|
packing: 'Packing',
|
||||||
|
grouping: 'Grouping',
|
||||||
|
buyingValue: 'Coste',
|
||||||
|
packagingFk: 'Embalaje',
|
||||||
|
file: 'Fichero',
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
observationType: 'Tipo de observación',
|
||||||
|
description: 'Descripción',
|
||||||
|
},
|
||||||
|
descriptor: {
|
||||||
|
agency: 'Agencia',
|
||||||
|
landed: 'F. entrega',
|
||||||
|
warehouseOut: 'Alm. salida',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ticket: {
|
ticket: {
|
||||||
|
@ -806,6 +884,10 @@ export default {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
routes: 'Rutas',
|
routes: 'Rutas',
|
||||||
cmrsList: 'Listado de CMRs externos',
|
cmrsList: 'Listado de CMRs externos',
|
||||||
|
RouteList: 'Listado',
|
||||||
|
create: 'Crear',
|
||||||
|
basicData: 'Datos básicos',
|
||||||
|
summary: 'Summary',
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
@ -876,6 +958,72 @@ export default {
|
||||||
create: {
|
create: {
|
||||||
supplierName: 'Nombre del proveedor',
|
supplierName: 'Nombre del proveedor',
|
||||||
},
|
},
|
||||||
|
basicData: {
|
||||||
|
alias: 'Alias',
|
||||||
|
workerFk: 'Responsable',
|
||||||
|
isSerious: 'Verificado',
|
||||||
|
isActive: 'Activo',
|
||||||
|
isPayMethodChecked: 'Método de pago validado',
|
||||||
|
note: 'Notas',
|
||||||
|
},
|
||||||
|
fiscalData: {
|
||||||
|
name: 'Razón social *',
|
||||||
|
nif: 'NIF/CIF *',
|
||||||
|
account: 'Cuenta',
|
||||||
|
sageTaxTypeFk: 'Tipo de impuesto sage',
|
||||||
|
sageWithholdingFk: 'Retención sage',
|
||||||
|
sageTransactionTypeFk: 'Tipo de transacción sage',
|
||||||
|
supplierActivityFk: 'Actividad proveedor',
|
||||||
|
healthRegister: 'Pasaporte sanitario',
|
||||||
|
street: 'Calle',
|
||||||
|
postcode: 'Código postal',
|
||||||
|
city: 'Población *',
|
||||||
|
provinceFk: 'Provincia',
|
||||||
|
country: 'País',
|
||||||
|
isTrucker: 'Transportista',
|
||||||
|
isVies: 'Vies',
|
||||||
|
},
|
||||||
|
billingData: {
|
||||||
|
payMethodFk: 'Forma de pago',
|
||||||
|
payDemFk: 'Plazo de pago',
|
||||||
|
payDay: 'Día de pago',
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
iban: 'Iban',
|
||||||
|
bankEntity: 'Entidad bancaria',
|
||||||
|
beneficiary: 'Beneficiario',
|
||||||
|
},
|
||||||
|
contacts: {
|
||||||
|
name: 'Nombre',
|
||||||
|
phone: 'Teléfono',
|
||||||
|
mobile: 'Móvil',
|
||||||
|
email: 'Email',
|
||||||
|
observation: 'Notas',
|
||||||
|
},
|
||||||
|
addresses: {
|
||||||
|
street: 'Dirección',
|
||||||
|
postcode: 'Código postal',
|
||||||
|
phone: 'Teléfono',
|
||||||
|
name: 'Nombre',
|
||||||
|
city: 'Población',
|
||||||
|
province: 'Provincia',
|
||||||
|
mobile: 'Móvil',
|
||||||
|
},
|
||||||
|
agencyTerms: {
|
||||||
|
agencyFk: 'Agencia',
|
||||||
|
minimumM3: 'M3 mínimos',
|
||||||
|
packagePrice: 'Precio bulto',
|
||||||
|
kmPrice: 'Precio Km',
|
||||||
|
m3Price: 'Precio M3',
|
||||||
|
routePrice: 'Precio ruta',
|
||||||
|
minimumKm: 'Km mínimos',
|
||||||
|
addRow: 'Añadir fila',
|
||||||
|
},
|
||||||
|
consumption: {
|
||||||
|
entry: 'Entrada',
|
||||||
|
date: 'Fecha',
|
||||||
|
reference: 'Referencia',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
travel: {
|
travel: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
|
@ -935,5 +1083,8 @@ export default {
|
||||||
addToPinned: 'Añadir a fijados',
|
addToPinned: 'Añadir a fijados',
|
||||||
removeFromPinned: 'Eliminar de fijados',
|
removeFromPinned: 'Eliminar de fijados',
|
||||||
},
|
},
|
||||||
|
VnLv: {
|
||||||
|
copyText: '{copyValue} se ha copiado al portapepeles',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,7 @@ const marker_labels = [
|
||||||
{ value: DEFAULT_MIN_RESPONSABILITY, label: t('claim.summary.company') },
|
{ value: DEFAULT_MIN_RESPONSABILITY, label: t('claim.summary.company') },
|
||||||
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
|
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
|
||||||
];
|
];
|
||||||
|
const multiplicatorValue = ref();
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
|
@ -134,17 +135,7 @@ async function regularizeClaim() {
|
||||||
message: t('globals.dataSaved'),
|
message: t('globals.dataSaved'),
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
});
|
});
|
||||||
if (claim.value.responsibility >= Math.ceil(DEFAULT_MAX_RESPONSABILITY) / 2) {
|
await onUpdateGreugeAccept();
|
||||||
quasar
|
|
||||||
.dialog({
|
|
||||||
component: VnConfirm,
|
|
||||||
componentProps: {
|
|
||||||
title: t('confirmGreuges'),
|
|
||||||
message: t('confirmGreugesMessage'),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.onOk(async () => await onUpdateGreugeAccept());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onUpdateGreugeAccept() {
|
async function onUpdateGreugeAccept() {
|
||||||
|
@ -153,9 +144,9 @@ async function onUpdateGreugeAccept() {
|
||||||
filter: { where: { code: 'freightPickUp' } },
|
filter: { where: { code: 'freightPickUp' } },
|
||||||
})
|
})
|
||||||
).data.id;
|
).data.id;
|
||||||
const freightPickUpPrice = (await axios.get(`GreugeConfigs/findOne`)).data
|
const freightPickUpPrice =
|
||||||
.freightPickUpPrice;
|
(await axios.get(`GreugeConfigs/findOne`)).data.freightPickUpPrice *
|
||||||
|
multiplicatorValue.value;
|
||||||
await axios.post(`Greuges`, {
|
await axios.post(`Greuges`, {
|
||||||
clientFk: claim.value.clientFk,
|
clientFk: claim.value.clientFk,
|
||||||
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
|
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
|
||||||
|
@ -226,10 +217,10 @@ async function importToNewRefundTicket() {
|
||||||
show-if-above
|
show-if-above
|
||||||
v-if="claim"
|
v-if="claim"
|
||||||
>
|
>
|
||||||
<QCard class="totalClaim vn-card q-my-md q-pa-sm">
|
<QCard class="totalClaim q-my-md q-pa-sm no-box-shadow">
|
||||||
{{ `${t('Total claimed')}: ${toCurrency(totalClaimed)}` }}
|
{{ `${t('Total claimed')}: ${toCurrency(totalClaimed)}` }}
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-card q-mb-md q-pa-sm">
|
<QCard class="q-mb-md q-pa-sm no-box-shadow">
|
||||||
<QItem class="justify-between">
|
<QItem class="justify-between">
|
||||||
<QItemLabel class="slider-container">
|
<QItemLabel class="slider-container">
|
||||||
<p class="text-primary">
|
<p class="text-primary">
|
||||||
|
@ -250,6 +241,7 @@ async function importToNewRefundTicket() {
|
||||||
</QItemLabel>
|
</QItemLabel>
|
||||||
</QItem>
|
</QItem>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
<QCard class="q-mb-md q-pa-sm no-box-shadow" style="margin-bottom: 1em">
|
||||||
<QItemLabel class="mana q-mb-md">
|
<QItemLabel class="mana q-mb-md">
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
v-model="claim.isChargedToMana"
|
v-model="claim.isChargedToMana"
|
||||||
|
@ -257,6 +249,23 @@ async function importToNewRefundTicket() {
|
||||||
/>
|
/>
|
||||||
<span>{{ t('mana') }}</span>
|
<span>{{ t('mana') }}</span>
|
||||||
</QItemLabel>
|
</QItemLabel>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="q-mb-md q-pa-sm no-box-shadow" style="position: static">
|
||||||
|
<QInput
|
||||||
|
:disable="
|
||||||
|
!(claim.responsibility >= Math.ceil(DEFAULT_MAX_RESPONSABILITY) / 2)
|
||||||
|
"
|
||||||
|
:label="t('confirmGreuges')"
|
||||||
|
class="q-field__native text-grey-2"
|
||||||
|
type="number"
|
||||||
|
placeholder="0"
|
||||||
|
id="multiplicatorValue"
|
||||||
|
name="multiplicatorValue"
|
||||||
|
min="0"
|
||||||
|
max="50"
|
||||||
|
v-model="multiplicatorValue"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()"> </Teleport>
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()"> </Teleport>
|
||||||
<CrudModel
|
<CrudModel
|
||||||
|
@ -494,4 +503,5 @@ es:
|
||||||
Id item: Id artículo
|
Id item: Id artículo
|
||||||
confirmGreuges: ¿Desea insertar greuges?
|
confirmGreuges: ¿Desea insertar greuges?
|
||||||
confirmGreugesMessage: Insertar greuges en la ficha del cliente
|
confirmGreugesMessage: Insertar greuges en la ficha del cliente
|
||||||
|
Apply Greuges: Aplicar Greuges
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,3 +1,130 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const payMethods = ref([]);
|
||||||
|
const bankEntitiesOptions = ref([]);
|
||||||
|
const bankEntitiesRef = ref(null);
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: ['id', 'bic', 'name'],
|
||||||
|
order: 'bic ASC',
|
||||||
|
limit: 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBankEntities = () => {
|
||||||
|
bankEntitiesRef.value.fetch();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">Billing data</div>
|
<fetch-data @on-fetch="(data) => (payMethods = data)" auto-load url="PayMethods" />
|
||||||
|
<fetch-data
|
||||||
|
ref="bankEntitiesRef"
|
||||||
|
@on-fetch="(data) => (bankEntitiesOptions = data)"
|
||||||
|
:filter="filter"
|
||||||
|
auto-load
|
||||||
|
url="BankEntities"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormModel
|
||||||
|
:url-update="`Clients/${route.params.id}`"
|
||||||
|
:url="`Clients/${route.params.id}/getCard`"
|
||||||
|
auto-load
|
||||||
|
model="customer"
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Billing data')"
|
||||||
|
:options="payMethods"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.payMethod"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('Due day')"
|
||||||
|
:rules="validate('client.socialName')"
|
||||||
|
v-model="data.dueDay"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('IBAN')" v-model="data.iban" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('Swift / BIC')"
|
||||||
|
:options="bankEntitiesOptions"
|
||||||
|
:roles-allowed-to-create="['salesAssistant', 'hr']"
|
||||||
|
:rules="validate('Worker.bankEntity')"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.bankEntityFk"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CreateBankEntityForm @on-data-saved="getBankEntities()" />
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel
|
||||||
|
>{{ scope.opt.bic }}
|
||||||
|
{{ scope.opt.name }}</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Received LCR')" v-model="data.hasLcr" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('VNL core received')"
|
||||||
|
v-model="data.hasCoreVnl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('VNL B2B received')" v-model="data.hasSepaVnl" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Billing data: Forma de pago
|
||||||
|
Due day: Vencimiento
|
||||||
|
IBAN: IBAN
|
||||||
|
Swift / BIC: Swift / BIC
|
||||||
|
Received LCR: Recibido LCR
|
||||||
|
VNL core received: Recibido core VNL
|
||||||
|
VNL B2B received: Recibido B2B VNL
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,3 +1,176 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const provincesLocation = ref([]);
|
||||||
|
|
||||||
|
const consigneeFilter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'isDefaultAddress',
|
||||||
|
'isActive',
|
||||||
|
'nickname',
|
||||||
|
'street',
|
||||||
|
'city',
|
||||||
|
'provinceFk',
|
||||||
|
'phone',
|
||||||
|
'mobile',
|
||||||
|
'isEqualizated',
|
||||||
|
'isLogifloraAllowed',
|
||||||
|
'postalCode',
|
||||||
|
],
|
||||||
|
order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'observations',
|
||||||
|
scope: {
|
||||||
|
include: 'observationType',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'province',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const setProvince = (provinceFk) => {
|
||||||
|
const result = provincesLocation.value.filter(
|
||||||
|
(province) => province.id === provinceFk
|
||||||
|
);
|
||||||
|
return result[0]?.name || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const toCustomerConsigneeCreate = () => {
|
||||||
|
router.push({ name: 'CustomerConsigneeCreate' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toCustomerConsigneeEdit = (consigneeId) => {
|
||||||
|
router.push({
|
||||||
|
name: 'CustomerConsigneeEdit',
|
||||||
|
params: {
|
||||||
|
id: route.params.id,
|
||||||
|
consigneeId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">Consignees</div>
|
<FetchData
|
||||||
|
@on-fetch="(data) => (provincesLocation = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces/location"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<QCard class="q-pa-lg">
|
||||||
|
<VnPaginate
|
||||||
|
data-key="CustomerConsignees"
|
||||||
|
:url="`Clients/${route.params.id}/addresses`"
|
||||||
|
order="id"
|
||||||
|
auto-load
|
||||||
|
:filter="consigneeFilter"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard
|
||||||
|
v-for="(item, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
:class="{
|
||||||
|
'consignees-card': true,
|
||||||
|
'q-mb-md': index < rows.length - 1,
|
||||||
|
}"
|
||||||
|
@click="toCustomerConsigneeEdit(item.id)"
|
||||||
|
>
|
||||||
|
<div class="q-ml-xs q-mr-md flex items-center">
|
||||||
|
<QIcon name="star" size="md" color="primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-weight-bold q-mb-sm">
|
||||||
|
{{ item.nickname }} - #{{ item.id }}
|
||||||
|
</div>
|
||||||
|
<div>{{ item.street }}</div>
|
||||||
|
<div>
|
||||||
|
{{ item.postalCode }} - {{ item.city }},
|
||||||
|
{{ setProvince(item.provinceFk) }}
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Is equalizated')"
|
||||||
|
v-model="item.isEqualizated"
|
||||||
|
class="q-mr-lg"
|
||||||
|
disable
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Is logiflora allowed')"
|
||||||
|
v-model="item.isLogifloraAllowed"
|
||||||
|
disable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<QSeparator
|
||||||
|
class="q-mx-lg"
|
||||||
|
v-if="item.observations.length"
|
||||||
|
vertical
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-if="item.observations.length">
|
||||||
|
<div
|
||||||
|
:key="index"
|
||||||
|
class="flex q-mb-sm"
|
||||||
|
v-for="(observation, index) in item.observations"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-mr-sm">
|
||||||
|
{{ observation.observationType.description }}:
|
||||||
|
</div>
|
||||||
|
<div>{{ observation.description }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
|
|
||||||
|
<QPageSticky :offset="[18, 18]">
|
||||||
|
<QBtn
|
||||||
|
@click.stop="toCustomerConsigneeCreate()"
|
||||||
|
color="primary"
|
||||||
|
fab
|
||||||
|
icon="add"
|
||||||
|
/>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('New consignee') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.consignees-card {
|
||||||
|
border: 2px solid var(--vn-light-gray);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--vn-light-gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Is equalizated: Recargo de equivalencia
|
||||||
|
Is logiflora allowed: Compra directa en Holanda
|
||||||
|
New consignee: Nuevo consignatario
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,3 +1,148 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onBeforeMount, onMounted } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { date, QBtn } from 'quasar';
|
||||||
|
|
||||||
|
import { toCurrency, toDate } from 'src/filters';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
|
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
|
const arrayData = ref(null);
|
||||||
|
const workerId = ref(0);
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const filter = {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: { relation: 'user', scope: { fields: ['name'] } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
where: { clientFk: `${route.params.id}` },
|
||||||
|
order: ['created DESC'],
|
||||||
|
limit: 20,
|
||||||
|
};
|
||||||
|
arrayData.value = useArrayData('CustomerCreditsCard', {
|
||||||
|
url: 'ClientCredits',
|
||||||
|
filter,
|
||||||
|
});
|
||||||
|
await arrayData.value.fetch({ append: false });
|
||||||
|
stateStore.rightDrawer = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const filteredColumns = columns.value.filter(
|
||||||
|
(col) => col.name !== 'actions' && col.name !== 'customerStatus'
|
||||||
|
);
|
||||||
|
allColumnNames.value = filteredColumns.map((col) => col.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = computed(() => arrayData.value.store.data);
|
||||||
|
const allColumnNames = ref([]);
|
||||||
|
|
||||||
|
const tableColumnComponents = {
|
||||||
|
created: {
|
||||||
|
component: 'span',
|
||||||
|
props: () => {},
|
||||||
|
event: () => {},
|
||||||
|
},
|
||||||
|
employee: {
|
||||||
|
component: QBtn,
|
||||||
|
props: () => ({ flat: true, color: 'blue' }),
|
||||||
|
event: (prop) => {
|
||||||
|
selectWorkerId(prop.row.clientFk);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
component: 'span',
|
||||||
|
props: () => {},
|
||||||
|
event: () => {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'created',
|
||||||
|
label: t('Since'),
|
||||||
|
name: 'created',
|
||||||
|
format: (value) => date.formatDate(value, 'DD/MM/YYYY hh:mm:ss'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: (value) => value.worker.user.name,
|
||||||
|
label: t('Employee'),
|
||||||
|
name: 'employee',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'amount',
|
||||||
|
label: t('Credit'),
|
||||||
|
name: 'amount',
|
||||||
|
format: (value) => toCurrency(value),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selectWorkerId = (id) => {
|
||||||
|
workerId.value = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toCustomerCreditCreate = () => {
|
||||||
|
router.push({ name: 'CustomerCreditCreate' });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">Credits</div>
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:pagination="{ rowsPerPage: 12 }"
|
||||||
|
:rows="rows"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
row-key="id"
|
||||||
|
>
|
||||||
|
<template #body-cell="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<QTr :props="props" class="cursor-pointer">
|
||||||
|
<component
|
||||||
|
:is="tableColumnComponents[props.col.name].component"
|
||||||
|
@click="tableColumnComponents[props.col.name].event(props)"
|
||||||
|
class="rounded-borders q-pa-sm"
|
||||||
|
v-bind="tableColumnComponents[props.col.name].props(props)"
|
||||||
|
>
|
||||||
|
{{ props.value }}
|
||||||
|
<WorkerDescriptorProxy :id="workerId" />
|
||||||
|
</component>
|
||||||
|
</QTr>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QPage>
|
||||||
|
|
||||||
|
<QPageSticky :offset="[18, 18]">
|
||||||
|
<QBtn @click.stop="toCustomerCreditCreate()" color="primary" fab icon="add" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('New credit') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Since: Desde
|
||||||
|
Employee: Empleado
|
||||||
|
Credit: Credito
|
||||||
|
New credit: Nuevo credito
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,3 +1,295 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const townsFetchDataRef = ref(null);
|
||||||
|
const postcodeFetchDataRef = ref(null);
|
||||||
|
const typesTaxes = ref([]);
|
||||||
|
const typesTransactions = ref([]);
|
||||||
|
const citiesLocationOptions = ref([]);
|
||||||
|
const provincesLocationOptions = ref([]);
|
||||||
|
const countriesOptions = ref([]);
|
||||||
|
const postcodesOptions = ref([]);
|
||||||
|
|
||||||
|
const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formData) => {
|
||||||
|
await postcodeFetchDataRef.value.fetch();
|
||||||
|
await townsFetchDataRef.value.fetch();
|
||||||
|
formData.postcode = code;
|
||||||
|
formData.provinceFk = provinceFk;
|
||||||
|
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||||
|
formData.countryFk = countryFk;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">Fiscal data</div>
|
<fetch-data auto-load @on-fetch="(data) => (typesTaxes = data)" url="SageTaxTypes" />
|
||||||
|
<fetch-data
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (typesTransactions = data)"
|
||||||
|
url="SageTransactionTypes"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="townsFetchDataRef"
|
||||||
|
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Towns/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (countriesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Countries"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="postcodeFetchDataRef"
|
||||||
|
url="Postcodes/location"
|
||||||
|
@on-fetch="(data) => (postcodesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url-update="`Clients/${route.params.id}/updateFiscalData`"
|
||||||
|
:url="`Clients/${route.params.id}/getCard`"
|
||||||
|
auto-load
|
||||||
|
model="customer"
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('Social name')"
|
||||||
|
:required="true"
|
||||||
|
:rules="validate('client.socialName')"
|
||||||
|
v-model="data.socialName"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Tax number')" v-model="data.fi" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Street')" v-model="data.street" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Sage tax type')"
|
||||||
|
:options="typesTaxes"
|
||||||
|
hide-selected
|
||||||
|
option-label="vat"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.sageTaxTypeFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Sage transaction type')"
|
||||||
|
:options="typesTransactions"
|
||||||
|
hide-selected
|
||||||
|
option-label="vat"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.sageTransactionTypeFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('Postcode')"
|
||||||
|
:options="postcodesOptions"
|
||||||
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
|
:rules="validate('Worker.postcode')"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.postcode"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomerCreateNewPostcode
|
||||||
|
@on-data-saved="onPostcodeCreated($event, data)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||||
|
<QItemLabel caption
|
||||||
|
>{{ scope.opt.code }} -
|
||||||
|
{{ scope.opt.town.name }} ({{
|
||||||
|
scope.opt.town.province.name
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
scope.opt.town.province.country.country
|
||||||
|
}})</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('City')"
|
||||||
|
:options="citiesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="name"
|
||||||
|
v-model="data.city"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{
|
||||||
|
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
||||||
|
}}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Province')"
|
||||||
|
:options="provincesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.provinceFk"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{
|
||||||
|
`${scope.opt.name} (${scope.opt.country.country})`
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Country')"
|
||||||
|
:options="countriesOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="country"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.countryFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Active')" v-model="data.isActive" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Frozen')" v-model="data.isFreezed" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Has to invoice')" v-model="data.hasToInvoice" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Vies')" v-model="data.isVies" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Notify by email')"
|
||||||
|
v-model="data.isToBeMailed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Invoice by address')"
|
||||||
|
v-model="data.hasToInvoiceByAddress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Is equalizated')"
|
||||||
|
v-model="data.isEqualizated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Verified data')"
|
||||||
|
v-model="data.isTaxDataChecked"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Incoterms authorization')"
|
||||||
|
v-model="data.hasIncoterms"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Electronic invoice')"
|
||||||
|
v-model="data.hasElectronicInvoice"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Social name: Razón social
|
||||||
|
Tax number: NIF / CIF
|
||||||
|
Street: Dirección fiscal
|
||||||
|
Sage tax type: Tipo de impuesto Sage
|
||||||
|
Sage transaction type: Tipo de transacción Sage
|
||||||
|
Postcode: Código postal
|
||||||
|
City: Población
|
||||||
|
Province: Provincia
|
||||||
|
Country: País
|
||||||
|
Active: Activo
|
||||||
|
Frozen: Congelado
|
||||||
|
Has to invoice: Factura
|
||||||
|
Vies: Vies
|
||||||
|
Notify by email: Notificar vía e-mail
|
||||||
|
Invoice by address: Facturar por consignatario
|
||||||
|
Is equalizated: Recargo de equivalencia
|
||||||
|
Verified data: Datos comprobados
|
||||||
|
Incoterms authorization: Autorización incoterms
|
||||||
|
Electronic invoice: Factura electrónica
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,3 +1,96 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { date } from 'quasar';
|
||||||
|
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const noteFilter = {
|
||||||
|
order: 'created DESC',
|
||||||
|
where: {
|
||||||
|
clientFk: `${route.params.id}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const toCustomerNoteCreate = () => {
|
||||||
|
router.push({ name: 'CustomerNoteCreate' });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">Notes</div>
|
<QCard class="q-pa-lg">
|
||||||
|
<VnPaginate
|
||||||
|
data-key="CustomerNotes"
|
||||||
|
:url="'clientObservations'"
|
||||||
|
auto-load
|
||||||
|
:filter="noteFilter"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div v-if="rows.length">
|
||||||
|
<QCard
|
||||||
|
v-for="(item, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
:class="{
|
||||||
|
'q-pa-md': true,
|
||||||
|
'q-rounded': true,
|
||||||
|
'custom-border': true,
|
||||||
|
'q-mb-md': index < rows.length - 1,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<p class="label-color">{{ item.worker.user.nickname }}</p>
|
||||||
|
<p class="label-color">
|
||||||
|
{{
|
||||||
|
date.formatDate(item?.created, 'DD-MM-YYYY HH:mm:ss')
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<h6 class="q-mt-xs q-mb-none">{{ item.text }}</h6>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<h5 class="flex justify-center label-color">
|
||||||
|
{{ t('globals.noResults') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<QPageSticky :offset="[18, 18]">
|
||||||
|
<QBtn
|
||||||
|
@click.stop="toCustomerConsigneeCreate()"
|
||||||
|
color="primary"
|
||||||
|
fab
|
||||||
|
icon="add"
|
||||||
|
/>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('New consignee') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</QCard>
|
||||||
|
|
||||||
|
<QPageSticky :offset="[18, 18]">
|
||||||
|
<QBtn @click.stop="toCustomerNoteCreate()" color="primary" fab icon="add" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('New consignee') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.custom-border {
|
||||||
|
border: 2px solid var(--vn-light-gray);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-color {
|
||||||
|
color: var(--vn-label);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -81,7 +81,7 @@ const creditWarning = computed(() => {
|
||||||
<VnLinkPhone :phone-number="entity.mobile" />
|
<VnLinkPhone :phone-number="entity.mobile" />
|
||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
<VnLv :label="t('customer.summary.email')" :value="entity.email" />
|
<VnLv :label="t('customer.summary.email')" :value="entity.email" copy />
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('customer.summary.salesPerson')"
|
:label="t('customer.summary.salesPerson')"
|
||||||
:value="entity?.salesPersonUser?.name"
|
:value="entity?.salesPersonUser?.name"
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
import VnLocation from 'src/components/common/VnLocation.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -29,17 +28,18 @@ const newClientForm = reactive({
|
||||||
isEqualizated: false,
|
isEqualizated: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const postcodeFetchDataRef = ref(null);
|
|
||||||
const workersOptions = ref([]);
|
const workersOptions = ref([]);
|
||||||
const businessTypesOptions = ref([]);
|
const businessTypesOptions = ref([]);
|
||||||
const citiesLocationOptions = ref([]);
|
|
||||||
const provincesLocationOptions = ref([]);
|
|
||||||
const countriesOptions = ref([]);
|
|
||||||
const postcodesOptions = ref([]);
|
const postcodesOptions = ref([]);
|
||||||
|
|
||||||
const onPostcodeCreated = async () => {
|
|
||||||
postcodeFetchDataRef.value.fetch();
|
function handleLocation(data, location ) {
|
||||||
};
|
const { town, code, provinceFk, countryFk } = location ?? {}
|
||||||
|
data.postcode = code;
|
||||||
|
data.city = town;
|
||||||
|
data.provinceFk = provinceFk;
|
||||||
|
data.countryFk = countryFk;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -48,37 +48,15 @@ const onPostcodeCreated = async () => {
|
||||||
auto-load
|
auto-load
|
||||||
url="Workers/search?departmentCodes"
|
url="Workers/search?departmentCodes"
|
||||||
/>
|
/>
|
||||||
<FetchData
|
|
||||||
ref="postcodeFetchDataRef"
|
|
||||||
url="Postcodes/location"
|
|
||||||
@on-fetch="(data) => (postcodesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
<FetchData
|
||||||
@on-fetch="(data) => (businessTypesOptions = data)"
|
@on-fetch="(data) => (businessTypesOptions = data)"
|
||||||
auto-load
|
auto-load
|
||||||
url="BusinessTypes"
|
url="BusinessTypes"
|
||||||
/>
|
/>
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Towns/location"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Provinces/location"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (countriesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Countries"
|
|
||||||
/>
|
|
||||||
<QPage>
|
<QPage>
|
||||||
<VnSubToolbar />
|
<VnSubToolbar />
|
||||||
<FormModel
|
<FormModel
|
||||||
:form-initial-data="newClientForm"
|
:form-initial-data="newClientForm"
|
||||||
:observe-form-changes="false"
|
|
||||||
model="client"
|
model="client"
|
||||||
url-create="Clients/createWithUser"
|
url-create="Clients/createWithUser"
|
||||||
>
|
>
|
||||||
|
@ -133,96 +111,19 @@ const onPostcodeCreated = async () => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectCreate
|
<VnLocation
|
||||||
v-model="data.postcode"
|
|
||||||
:label="t('Postcode')"
|
|
||||||
:rules="validate('Worker.postcode')"
|
:rules="validate('Worker.postcode')"
|
||||||
:roles-allowed-to-create="['deliveryAssistant']"
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
:options="postcodesOptions"
|
:options="postcodesOptions"
|
||||||
option-label="code"
|
v-model="data.location"
|
||||||
option-value="code"
|
@update:model-value="
|
||||||
hide-selected
|
(location) => handleLocation(data, location)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #form>
|
</VnLocation>
|
||||||
<CustomerCreateNewPostcode
|
|
||||||
@on-data-saved="onPostcodeCreated($event)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection v-if="scope.opt">
|
|
||||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
|
||||||
<QItemLabel caption
|
|
||||||
>{{ scope.opt.code }} -
|
|
||||||
{{ scope.opt.town.name }} ({{
|
|
||||||
scope.opt.town.province.name
|
|
||||||
}},
|
|
||||||
{{
|
|
||||||
scope.opt.town.province.country.country
|
|
||||||
}})</QItemLabel
|
|
||||||
>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectCreate>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<!-- ciudades -->
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('City')"
|
|
||||||
:options="citiesLocationOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="name"
|
|
||||||
v-model="data.city"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
|
||||||
<QItemLabel caption>
|
|
||||||
{{
|
|
||||||
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('Province')"
|
|
||||||
:options="provincesLocationOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="id"
|
|
||||||
v-model="data.provinceFk"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
`${scope.opt.name} (${scope.opt.country.country})`
|
|
||||||
}}</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('Country')"
|
|
||||||
:options="countriesOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="country"
|
|
||||||
option-value="id"
|
|
||||||
v-model="data.countryFk"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<QInput v-model="data.userName" :label="t('Web user')" />
|
<QInput v-model="data.userName" :label="t('Web user')" />
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { ref, computed, onBeforeMount } from 'vue';
|
import { ref, computed, onBeforeMount } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { QBtn, QCheckbox } from 'quasar';
|
import { QBtn, QCheckbox, useQuasar } from 'quasar';
|
||||||
|
|
||||||
import { toCurrency, toDate } from 'filters/index';
|
import { toCurrency, toDate } from 'filters/index';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
@ -12,34 +12,19 @@ import CustomerNotificationsFilter from './CustomerDefaulterFilter.vue';
|
||||||
import CustomerBalanceDueTotal from './CustomerBalanceDueTotal.vue';
|
import CustomerBalanceDueTotal from './CustomerBalanceDueTotal.vue';
|
||||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import CustomerDefaulterAddObservation from './CustomerDefaulterAddObservation.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
const arrayData = ref(null);
|
const arrayData = ref(null);
|
||||||
const balanceDueTotal = ref(0);
|
const balanceDueTotal = ref(0);
|
||||||
|
const customerId = ref(0);
|
||||||
onBeforeMount(async () => {
|
|
||||||
arrayData.value = useArrayData('CustomerDefaulter', {
|
|
||||||
url: 'Defaulters/filter',
|
|
||||||
limit: 0,
|
|
||||||
});
|
|
||||||
await arrayData.value.fetch({ append: false });
|
|
||||||
balanceDueTotal.value = arrayData.value.store.data.reduce(
|
|
||||||
(accumulator, currentValue) => {
|
|
||||||
return accumulator + (currentValue['amount'] || 0);
|
|
||||||
},
|
|
||||||
0
|
|
||||||
);
|
|
||||||
console.log(balanceDueTotal.value);
|
|
||||||
stateStore.rightDrawer = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const rows = computed(() => arrayData.value.store.data);
|
|
||||||
|
|
||||||
const selected = ref([]);
|
const selected = ref([]);
|
||||||
const workerId = ref(0);
|
const workerId = ref(0);
|
||||||
const customerId = ref(0);
|
|
||||||
|
const rows = computed(() => arrayData.value.store.data);
|
||||||
|
|
||||||
const tableColumnComponents = {
|
const tableColumnComponents = {
|
||||||
client: {
|
client: {
|
||||||
|
@ -49,11 +34,10 @@ const tableColumnComponents = {
|
||||||
},
|
},
|
||||||
isWorker: {
|
isWorker: {
|
||||||
component: QCheckbox,
|
component: QCheckbox,
|
||||||
props: ({ value }) => ({
|
props: ({ row }) => ({
|
||||||
disable: true,
|
disable: true,
|
||||||
'model-value': Boolean(value),
|
'model-value': Boolean(row.selected),
|
||||||
}),
|
}),
|
||||||
event: () => {},
|
|
||||||
},
|
},
|
||||||
salesperson: {
|
salesperson: {
|
||||||
component: QBtn,
|
component: QBtn,
|
||||||
|
@ -171,6 +155,25 @@ const columns = computed(() => [
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
getArrayData();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getArrayData = async () => {
|
||||||
|
arrayData.value = useArrayData('CustomerDefaulter', {
|
||||||
|
url: 'Defaulters/filter',
|
||||||
|
limit: 0,
|
||||||
|
});
|
||||||
|
await arrayData.value.fetch({ append: false });
|
||||||
|
balanceDueTotal.value = arrayData.value.store.data.reduce(
|
||||||
|
(accumulator, currentValue) => {
|
||||||
|
return accumulator + (currentValue['amount'] || 0);
|
||||||
|
},
|
||||||
|
0
|
||||||
|
);
|
||||||
|
stateStore.rightDrawer = true;
|
||||||
|
};
|
||||||
|
|
||||||
const selectCustomerId = (id) => {
|
const selectCustomerId = (id) => {
|
||||||
workerId.value = 0;
|
workerId.value = 0;
|
||||||
customerId.value = id;
|
customerId.value = id;
|
||||||
|
@ -180,6 +183,20 @@ const selectWorkerId = (id) => {
|
||||||
customerId.value = 0;
|
customerId.value = 0;
|
||||||
workerId.value = id;
|
workerId.value = id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const viewAddObservation = (rowsSelected) => {
|
||||||
|
quasar.dialog({
|
||||||
|
component: CustomerDefaulterAddObservation,
|
||||||
|
componentProps: {
|
||||||
|
clients: rowsSelected,
|
||||||
|
promise: refreshData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshData = () => {
|
||||||
|
getArrayData();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -190,11 +207,17 @@ const selectWorkerId = (id) => {
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
|
|
||||||
<QToolbar class="bg-vn-dark">
|
<QToolbar class="bg-vn-dark">
|
||||||
<div id="st-data">
|
<div id="st-data" class="row">
|
||||||
<CustomerBalanceDueTotal :amount="balanceDueTotal" />
|
<CustomerBalanceDueTotal :amount="balanceDueTotal" />
|
||||||
|
<div class="flex items-center q-ml-lg">
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
icon="vn:notes"
|
||||||
|
:disabled="!selected.length"
|
||||||
|
@click.stop="viewAddObservation(selected)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<QSpace />
|
|
||||||
<div id="st-actions"></div>
|
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
|
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
|
@ -204,7 +227,7 @@ const selectWorkerId = (id) => {
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
hide-bottom
|
hide-bottom
|
||||||
row-key="id"
|
row-key="clientFk"
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
clients: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
promise: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const newObservation = ref(null);
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const data = $props.clients.map((item) => {
|
||||||
|
return { clientFk: item.clientFk, text: newObservation.value };
|
||||||
|
});
|
||||||
|
await axios.post('ClientObservations', data);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
defaulters: $props.clients,
|
||||||
|
observation: newObservation.value,
|
||||||
|
};
|
||||||
|
await axios.post('Defaulters/observationEmail', payload);
|
||||||
|
|
||||||
|
await $props.promise();
|
||||||
|
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
quasar.notify({
|
||||||
|
message: t(`${error.message}`),
|
||||||
|
type: 'negative',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef">
|
||||||
|
<QCard class="q-pa-md q-mb-md">
|
||||||
|
<QCardSection>
|
||||||
|
<QForm @submit="onSubmit()" class="q-pa-sm">
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
t('Add observation to all selected clients', {
|
||||||
|
numberClients: t($props.clients.length),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('Message')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="newObservation"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<div class="q-mt-lg row justify-end">
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
class="q-mr-md"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.save')"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</QForm>
|
||||||
|
</QCardSection>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Add observation to all selected clients: Añadir observación a { numberClients } cliente(s) seleccionado(s)
|
||||||
|
Message: Mensaje
|
||||||
|
</i18n>
|
|
@ -485,7 +485,6 @@ const selectCustomerId = (id) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectSalesPersonId = (id) => {
|
const selectSalesPersonId = (id) => {
|
||||||
console.log('selectedSalesPersonId:: ', selectedSalesPersonId.value);
|
|
||||||
selectedSalesPersonId.value = id;
|
selectedSalesPersonId.value = id;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
<script setup>
|
||||||
|
import { onBeforeMount, reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const formInitialData = reactive({ isDefaultAddress: false });
|
||||||
|
|
||||||
|
const townsFetchDataRef = ref(null);
|
||||||
|
const postcodeFetchDataRef = ref(null);
|
||||||
|
const urlCreate = ref('');
|
||||||
|
|
||||||
|
const postcodesOptions = ref([]);
|
||||||
|
const citiesLocationOptions = ref([]);
|
||||||
|
const provincesLocationOptions = ref([]);
|
||||||
|
const agencyModes = ref([]);
|
||||||
|
const incoterms = ref([]);
|
||||||
|
const customsAgents = ref([]);
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
urlCreate.value = `Clients/${route.params.id}/createAddress`;
|
||||||
|
getCustomsAgents();
|
||||||
|
});
|
||||||
|
|
||||||
|
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
||||||
|
await postcodeFetchDataRef.value.fetch();
|
||||||
|
await townsFetchDataRef.value.fetch();
|
||||||
|
formData.postalCode = code;
|
||||||
|
formData.provinceFk = provinceFk;
|
||||||
|
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCustomsAgents = async () => {
|
||||||
|
const { data } = await axios.get('CustomsAgents');
|
||||||
|
customsAgents.value = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshData = () => {
|
||||||
|
getCustomsAgents();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toCustomerConsignees = () => {
|
||||||
|
router.push({
|
||||||
|
name: 'CustomerConsignees',
|
||||||
|
params: {
|
||||||
|
id: route.params.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (postcodesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
ref="postcodeFetchDataRef"
|
||||||
|
url="Postcodes/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
ref="townsFetchDataRef"
|
||||||
|
url="Towns/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces/location"
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
@on-fetch="(data) => (agencyModes = data)"
|
||||||
|
auto-load
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
/>
|
||||||
|
<fetch-data @on-fetch="(data) => (incoterms = data)" auto-load url="Incoterms" />
|
||||||
|
|
||||||
|
<FormModel
|
||||||
|
:form-initial-data="formInitialData"
|
||||||
|
:observe-form-changes="false"
|
||||||
|
:url-create="urlCreate"
|
||||||
|
@on-data-saved="toCustomerConsignees()"
|
||||||
|
model="client"
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Consignee')" v-model="data.nickname" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Street address')" v-model="data.street" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('Postcode')"
|
||||||
|
:options="postcodesOptions"
|
||||||
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
|
:rules="validate('Worker.postcode')"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.postalCode"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomerCreateNewPostcode
|
||||||
|
@on-data-saved="onPostcodeCreated($event, data)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt.code }} -
|
||||||
|
{{ scope.opt.town.name }}
|
||||||
|
({{ scope.opt.town.province.name }},
|
||||||
|
{{ scope.opt.town.province.country.country }})
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<!-- ciudades -->
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('City')"
|
||||||
|
:options="citiesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="name"
|
||||||
|
v-model="data.city"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{
|
||||||
|
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
||||||
|
}}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Province')"
|
||||||
|
:options="provincesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.provinceFk"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{
|
||||||
|
`${scope.opt.name} (${scope.opt.country.country})`
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
:options="agencyModes"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.agencyModeFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Phone')" v-model="data.phone" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Mobile')" v-model="data.mobile" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Incoterms')"
|
||||||
|
:options="incoterms"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.incotermsFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('Customs agent')"
|
||||||
|
:options="customsAgents"
|
||||||
|
hide-selected
|
||||||
|
option-label="fiscalName"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.customsAgentFk"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomsNewCustomsAgent @on-data-saved="refreshData()" />
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.add-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: $primary;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Default: Predeterminado
|
||||||
|
Consignee: Consignatario
|
||||||
|
Street address: Dirección postal
|
||||||
|
Postcode: Código postal
|
||||||
|
City: Población
|
||||||
|
Province: Provincia
|
||||||
|
Agency: Agencia
|
||||||
|
Phone: Teléfono
|
||||||
|
Mobile: Movíl
|
||||||
|
Incoterms: Incoterms
|
||||||
|
Customs agent: Agente de aduanas
|
||||||
|
</i18n>
|
|
@ -0,0 +1,371 @@
|
||||||
|
<script setup>
|
||||||
|
import { onBeforeMount, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const townsFetchDataRef = ref(null);
|
||||||
|
const postcodeFetchDataRef = ref(null);
|
||||||
|
const urlUpdate = ref('');
|
||||||
|
|
||||||
|
const postcodesOptions = ref([]);
|
||||||
|
const citiesLocationOptions = ref([]);
|
||||||
|
const provincesLocationOptions = ref([]);
|
||||||
|
const agencyModes = ref([]);
|
||||||
|
const incoterms = ref([]);
|
||||||
|
const customsAgents = ref([]);
|
||||||
|
const observationTypes = ref([]);
|
||||||
|
const notes = ref([]);
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
urlUpdate.value = `Clients/${route.params.id}/updateAddress/${route.params.consigneeId}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
||||||
|
await postcodeFetchDataRef.value.fetch();
|
||||||
|
await townsFetchDataRef.value.fetch();
|
||||||
|
formData.postalCode = code;
|
||||||
|
formData.provinceFk = provinceFk;
|
||||||
|
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async (observations) => {
|
||||||
|
observationTypes.value = observations;
|
||||||
|
|
||||||
|
if (observationTypes.value.length) {
|
||||||
|
const filter = {
|
||||||
|
fields: ['id', 'addressFk', 'observationTypeFk', 'description'],
|
||||||
|
where: { addressFk: `${route.params.consigneeId}` },
|
||||||
|
};
|
||||||
|
const { data } = await axios.get('AddressObservations', {
|
||||||
|
params: { filter: JSON.stringify(filter) },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.length) {
|
||||||
|
notes.value = data
|
||||||
|
.map((observation) => {
|
||||||
|
const type = observationTypes.value.find(
|
||||||
|
(type) => type.id === observation.observationTypeFk
|
||||||
|
);
|
||||||
|
return type
|
||||||
|
? {
|
||||||
|
$isNew: false,
|
||||||
|
$oldData: null,
|
||||||
|
$orgIndex: null,
|
||||||
|
addressFk: `${route.params.consigneeId}`,
|
||||||
|
description: observation.description,
|
||||||
|
observationTypeFk: type.id,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.filter((item) => item !== null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNote = () => {
|
||||||
|
notes.value.push({
|
||||||
|
$isNew: true,
|
||||||
|
$oldData: null,
|
||||||
|
$orgIndex: null,
|
||||||
|
addressFk: `${route.params.consigneeId}`,
|
||||||
|
description: '',
|
||||||
|
observationTypeFk: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteNote = (index) => {
|
||||||
|
notes.value.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDataSaved = () => {
|
||||||
|
const payload = {
|
||||||
|
creates: notes.value,
|
||||||
|
};
|
||||||
|
axios.post('AddressObservations/crud', payload);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="postcodeFetchDataRef"
|
||||||
|
@on-fetch="(data) => (postcodesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Postcodes/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="townsFetchDataRef"
|
||||||
|
@on-fetch="(data) => (citiesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Towns/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces/location"
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
@on-fetch="(data) => (agencyModes = data)"
|
||||||
|
auto-load
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
/>
|
||||||
|
<fetch-data @on-fetch="(data) => (incoterms = data)" auto-load url="Incoterms" />
|
||||||
|
<fetch-data
|
||||||
|
@on-fetch="(data) => (customsAgents = data)"
|
||||||
|
auto-load
|
||||||
|
url="CustomsAgents"
|
||||||
|
/>
|
||||||
|
<fetch-data @on-fetch="getData" auto-load url="ObservationTypes" />
|
||||||
|
|
||||||
|
<FormModel
|
||||||
|
:observe-form-changes="false"
|
||||||
|
:url-update="urlUpdate"
|
||||||
|
:url="`Addresses/${route.params.consigneeId}`"
|
||||||
|
@on-data-saved="onDataSaved()"
|
||||||
|
auto-load
|
||||||
|
model="client"
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox :label="t('Enabled')" v-model="data.isActive" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Is equalizated')"
|
||||||
|
v-model="data.isEqualizated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('Is Loginflora allowed')"
|
||||||
|
v-model="data.isLogifloraAllowed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Consignee')" v-model="data.nickname" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Street address')" v-model="data.street" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('Postcode')"
|
||||||
|
:options="postcodesOptions"
|
||||||
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
|
:rules="validate('Worker.postcode')"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.postalCode"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomerCreateNewPostcode
|
||||||
|
@on-data-saved="onPostcodeCreated($event, data)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt.code }} -
|
||||||
|
{{ scope.opt.town.name }}
|
||||||
|
({{ scope.opt.town.province.name }},
|
||||||
|
{{ scope.opt.town.province.country.country }})
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<!-- ciudades -->
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('City')"
|
||||||
|
:options="citiesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="name"
|
||||||
|
v-model="data.city"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{
|
||||||
|
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
||||||
|
}}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Province')"
|
||||||
|
:options="provincesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.provinceFk"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{
|
||||||
|
`${scope.opt.name} (${scope.opt.country.country})`
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
:options="agencyModes"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.agencyModeFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Phone')" v-model="data.phone" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Mobile')" v-model="data.mobile" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Incoterms')"
|
||||||
|
:options="incoterms"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.incotermsFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('Customs agent')"
|
||||||
|
:options="customsAgents"
|
||||||
|
hide-selected
|
||||||
|
option-label="fiscalName"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.customsAgentFk"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomsNewCustomsAgent />
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<h4 class="q-mb-xs">{{ t('Notes') }}</h4>
|
||||||
|
<VnRow
|
||||||
|
:key="index"
|
||||||
|
class="row q-gutter-md q-mb-md"
|
||||||
|
v-for="(note, index) in notes"
|
||||||
|
>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Observation type')"
|
||||||
|
:options="observationTypes"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="note.observationTypeFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Description')" v-model="note.description" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<QIcon
|
||||||
|
@click.stop="deleteNote(index)"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Remove') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
|
||||||
|
<QIcon
|
||||||
|
@click.stop="addNote()"
|
||||||
|
class="cursor-pointer add-icon q-mt-md"
|
||||||
|
name="add"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add note') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.add-icon {
|
||||||
|
background-color: $primary;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Enabled: Activo
|
||||||
|
Is equalizated: Recargo de equivalencia
|
||||||
|
Is Loginflora allowed: Compra directa en Holanda
|
||||||
|
Consignee: Consignatario
|
||||||
|
Street address: Dirección postal
|
||||||
|
Postcode: Código postal
|
||||||
|
City: Población
|
||||||
|
Province: Provincia
|
||||||
|
Agency: Agencia
|
||||||
|
Phone: Teléfono
|
||||||
|
Mobile: Movíl
|
||||||
|
Incoterms: Incoterms
|
||||||
|
Customs agent: Agente de aduanas
|
||||||
|
Notes: Notas
|
||||||
|
Observation type: Tipo de observación
|
||||||
|
Description: Descripción
|
||||||
|
Add note: Añadir nota
|
||||||
|
Remove note: Eliminar nota
|
||||||
|
</i18n>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const initialData = reactive({
|
||||||
|
credit: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toCustomerCredits = () => {
|
||||||
|
router.push({
|
||||||
|
name: 'CustomerCredits',
|
||||||
|
params: {
|
||||||
|
id: route.params.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormModel
|
||||||
|
:form-initial-data="initialData"
|
||||||
|
:observe-form-changes="false"
|
||||||
|
:url-update="`/Clients/${route.params.id}`"
|
||||||
|
@on-data-saved="toCustomerCredits()"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('Credit')"
|
||||||
|
type="number"
|
||||||
|
v-model.number="data.credit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Credit: Crédito
|
||||||
|
</i18n>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FormModelPopup from 'src/components/FormModelPopup.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const initialData = reactive({
|
||||||
|
nif: null,
|
||||||
|
fiscalName: null,
|
||||||
|
street: null,
|
||||||
|
phone: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDataSaved = (dataSaved) => {
|
||||||
|
emit('onDataSaved', dataSaved);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormModelPopup
|
||||||
|
:title="t('New customs agent')"
|
||||||
|
url-create="CustomsAgents"
|
||||||
|
model="customer"
|
||||||
|
:form-initial-data="initialData"
|
||||||
|
@on-data-saved="onDataSaved($event)"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('NIF')" :required="true" v-model="data.nif" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('Fiscal name')"
|
||||||
|
:required="true"
|
||||||
|
v-model="data.fiscalName"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Street')" v-model="data.street" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput :label="t('Phone')" v-model="data.phone" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
New customs agent: Nuevo agente de aduanas
|
||||||
|
NIF: NIF
|
||||||
|
Fiscal name: Nombre fiscal
|
||||||
|
Street: Dirección fiscal
|
||||||
|
Phone: Teléfono
|
||||||
|
</i18n>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const initialData = reactive({
|
||||||
|
text: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initialData.clientFk = `${route.params.id}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const toCustomerNotes = () => {
|
||||||
|
router.push({
|
||||||
|
name: 'CustomerNotes',
|
||||||
|
params: {
|
||||||
|
id: route.params.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormModel
|
||||||
|
:form-initial-data="initialData"
|
||||||
|
:observe-form-changes="false"
|
||||||
|
url-create="ClientObservations"
|
||||||
|
@on-data-saved="toCustomerNotes()"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput :label="t('Note')" type="textarea" v-model="data.text" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Note: Nota
|
||||||
|
</i18n>
|
|
@ -90,17 +90,15 @@ const removeDepartment = () => {
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv :label="t('department.chat')" :value="entity.chatName" dash />
|
<VnLv :label="t('department.chat')" :value="entity.chatName" />
|
||||||
<VnLv :label="t('department.email')" :value="entity.notificationEmail" dash />
|
<VnLv :label="t('department.email')" :value="entity.notificationEmail" copy />
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('department.selfConsumptionCustomer')"
|
:label="t('department.selfConsumptionCustomer')"
|
||||||
:value="entity.client?.name"
|
:value="entity.client?.name"
|
||||||
dash
|
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('department.bossDepartment')"
|
:label="t('department.bossDepartment')"
|
||||||
:value="entity.worker?.user?.name"
|
:value="entity.worker?.user?.name"
|
||||||
dash
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
|
import { toDate } from 'src/filters';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const suppliersOptions = ref([]);
|
||||||
|
const travelsOptions = ref([]);
|
||||||
|
const companiesOptions = ref([]);
|
||||||
|
const currenciesOptions = ref([]);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers"
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
order="nickname"
|
||||||
|
@on-fetch="(data) => (suppliersOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Travels/filter"
|
||||||
|
:filter="{ fields: ['id', 'warehouseInName'] }"
|
||||||
|
order="id"
|
||||||
|
@on-fetch="(data) => (travelsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="companiesRef"
|
||||||
|
url="Companies"
|
||||||
|
:filter="{ fields: ['id', 'code'] }"
|
||||||
|
order="code"
|
||||||
|
@on-fetch="(data) => (companiesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="currenciesRef"
|
||||||
|
url="Currencies"
|
||||||
|
:filter="{ fields: ['id', 'code'] }"
|
||||||
|
order="code"
|
||||||
|
@on-fetch="(data) => (currenciesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url="`Entries/${route.params.id}`"
|
||||||
|
:url-update="`Entries/${route.params.id}`"
|
||||||
|
model="entry"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('entry.basicData.supplier')"
|
||||||
|
v-model="data.supplierFk"
|
||||||
|
:options="suppliersOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
hide-selected
|
||||||
|
:required="true"
|
||||||
|
map-options
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('entry.basicData.travel')"
|
||||||
|
v-model="data.travelFk"
|
||||||
|
:options="travelsOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="warehouseInName"
|
||||||
|
map-options
|
||||||
|
hide-selected
|
||||||
|
:required="true"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel
|
||||||
|
>{{ scope.opt?.agencyModeName }} -
|
||||||
|
{{ scope.opt?.warehouseInName }} ({{
|
||||||
|
toDate(scope.opt?.shipped)
|
||||||
|
}}) → {{ scope.opt?.warehouseOutName }} ({{
|
||||||
|
toDate(scope.opt?.landed)
|
||||||
|
}})</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.reference"
|
||||||
|
:label="t('entry.basicData.reference')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.invoiceNumber"
|
||||||
|
:label="t('entry.basicData.invoiceNumber')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('entry.basicData.company')"
|
||||||
|
v-model="data.companyFk"
|
||||||
|
:options="companiesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="code"
|
||||||
|
map-options
|
||||||
|
hide-selected
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('entry.basicData.currency')"
|
||||||
|
v-model="data.currencyFk"
|
||||||
|
:options="currenciesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('entry.basicData.commission')"
|
||||||
|
v-model="data.commission"
|
||||||
|
type="number"
|
||||||
|
autofocus
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('entry.basicData.observation')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="data.observation"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isOrdered"
|
||||||
|
:label="t('entry.basicData.ordered')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isConfirmed"
|
||||||
|
:label="t('entry.basicData.confirmed')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isExcludedFromAvailable"
|
||||||
|
:label="t('entry.basicData.excludedFromAvailable')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isBooked"
|
||||||
|
:label="t('entry.basicData.booked')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,409 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { toCurrency } from 'src/filters';
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const rowsSelected = ref([]);
|
||||||
|
const entryBuysPaginateRef = ref(null);
|
||||||
|
const packagingsOptions = ref(null);
|
||||||
|
|
||||||
|
const tableColumnComponents = computed(() => ({
|
||||||
|
item: {
|
||||||
|
component: () => 'span',
|
||||||
|
props: () => {},
|
||||||
|
event: () => ({}),
|
||||||
|
},
|
||||||
|
quantity: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
class: 'input-number',
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
packagingFk: {
|
||||||
|
component: () => VnSelectFilter,
|
||||||
|
props: () => ({
|
||||||
|
'option-value': 'id',
|
||||||
|
'option-label': 'id',
|
||||||
|
'emit-value': true,
|
||||||
|
'map-options': true,
|
||||||
|
'use-input': true,
|
||||||
|
'hide-selected': true,
|
||||||
|
options: packagingsOptions.value,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'update:modelValue': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
stickers: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
class: 'input-number',
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
weight: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
packing: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
grouping: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
buyingValue: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
price2: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
price3: {
|
||||||
|
component: () => VnInput,
|
||||||
|
props: (col) => ({
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
label: col.label,
|
||||||
|
}),
|
||||||
|
event: (props) => ({
|
||||||
|
'keyup.enter': () => saveChange(props.row),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
import: {
|
||||||
|
component: () => 'span',
|
||||||
|
props: () => {},
|
||||||
|
event: () => ({}),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const entriesTableColumns = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: t('entry.summary.item'),
|
||||||
|
field: 'id',
|
||||||
|
name: 'item',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.quantity'),
|
||||||
|
field: 'quantity',
|
||||||
|
name: 'quantity',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.package'),
|
||||||
|
field: 'packagingFk',
|
||||||
|
name: 'packagingFk',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.stickers'),
|
||||||
|
field: 'stickers',
|
||||||
|
name: 'stickers',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.weight'),
|
||||||
|
field: 'weight',
|
||||||
|
name: 'weight',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.packing'),
|
||||||
|
field: 'packing',
|
||||||
|
name: 'packing',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.grouping'),
|
||||||
|
field: 'grouping',
|
||||||
|
name: 'grouping',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.buyingValue'),
|
||||||
|
field: 'buyingValue',
|
||||||
|
name: 'buyingValue',
|
||||||
|
align: 'left',
|
||||||
|
format: (value) => toCurrency(value),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.groupingPrice'),
|
||||||
|
field: 'price2',
|
||||||
|
name: 'price2',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.packingPrice'),
|
||||||
|
field: 'price3',
|
||||||
|
name: 'price3',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.import'),
|
||||||
|
name: 'import',
|
||||||
|
align: 'left',
|
||||||
|
format: (_, row) => toCurrency(row.buyingValue * row.quantity),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveChange = async (rowData) => {
|
||||||
|
await axios.patch(`Buys/${rowData.id}`, rowData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openRemoveDialog = async () => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Confirm deletion'),
|
||||||
|
message: t(
|
||||||
|
`Are you sure you want to delete this buy${
|
||||||
|
rowsSelected.value.length > 1 ? 's' : ''
|
||||||
|
}?`
|
||||||
|
),
|
||||||
|
data: rowsSelected.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
try {
|
||||||
|
await deleteBuys();
|
||||||
|
const notifyMessage = t(
|
||||||
|
`Buy${rowsSelected.value.length > 1 ? 's' : ''} deleted`
|
||||||
|
);
|
||||||
|
notify(notifyMessage, 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting buys');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteBuys = async () => {
|
||||||
|
await axios.post('Buys/deleteBuys', { buys: rowsSelected.value });
|
||||||
|
entryBuysPaginateRef.value.fetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
const importBuys = () => {
|
||||||
|
router.push({ name: 'EntryBuysImport' });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="expensesRef"
|
||||||
|
url="Packagings"
|
||||||
|
:filter="{ fields: ['id'], where: { freightItemFk: true }, order: 'id ASC' }"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (packagingsOptions = data)"
|
||||||
|
/>
|
||||||
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||||
|
<QBtnGroup push style="column-gap: 10px">
|
||||||
|
<slot name="moreBeforeActions" />
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.remove')"
|
||||||
|
color="primary"
|
||||||
|
icon="delete"
|
||||||
|
flat
|
||||||
|
@click="openRemoveDialog()"
|
||||||
|
:disable="!rowsSelected?.length"
|
||||||
|
:title="t('globals.remove')"
|
||||||
|
/>
|
||||||
|
</QBtnGroup>
|
||||||
|
</Teleport>
|
||||||
|
<VnPaginate
|
||||||
|
ref="entryBuysPaginateRef"
|
||||||
|
data-key="EntryBuys"
|
||||||
|
:url="`Entries/${route.params.id}/getBuys`"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QTable
|
||||||
|
:rows="rows"
|
||||||
|
:columns="entriesTableColumns"
|
||||||
|
selection="multiple"
|
||||||
|
row-key="id"
|
||||||
|
hide-bottom
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
:grid="$q.screen.lt.md"
|
||||||
|
v-model:selected="rowsSelected"
|
||||||
|
>
|
||||||
|
<template #body="props">
|
||||||
|
<QTr>
|
||||||
|
<QTd>
|
||||||
|
<QCheckbox v-model="props.selected" />
|
||||||
|
</QTd>
|
||||||
|
<QTd v-for="col in props.cols" :key="col.name">
|
||||||
|
<component
|
||||||
|
:is="tableColumnComponents[col.name].component()"
|
||||||
|
v-bind="tableColumnComponents[col.name].props(col)"
|
||||||
|
v-model="props.row[col.field]"
|
||||||
|
v-on="tableColumnComponents[col.name].event(props)"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-if="col.name === 'item' || col.name === 'import'"
|
||||||
|
>
|
||||||
|
{{ col.value }}
|
||||||
|
</template>
|
||||||
|
</component>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
<QTr no-hover>
|
||||||
|
<QTd />
|
||||||
|
<QTd>
|
||||||
|
<span>{{ props.row.item.itemType.code }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ props.row.item.id }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ props.row.item.size }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ toCurrency(props.row.item.minPrice) }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd colspan="7">
|
||||||
|
<span>{{ props.row.item.concept }}</span>
|
||||||
|
<span v-if="props.row.item.subName" class="subName">
|
||||||
|
{{ props.row.item.subName }}
|
||||||
|
</span>
|
||||||
|
<fetched-tags :item="props.row.item" :max-length="5" />
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
||||||
|
<QTr v-if="props.rowIndex !== rows.length - 1" class="separation-row">
|
||||||
|
<QTd colspan="12" style="height: 24px" />
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
<template #item="props">
|
||||||
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
|
<QCard bordered flat>
|
||||||
|
<QCardSection>
|
||||||
|
<QCheckbox v-model="props.selected" dense />
|
||||||
|
</QCardSection>
|
||||||
|
<QSeparator />
|
||||||
|
<QList dense>
|
||||||
|
<QItem v-for="col in props.cols" :key="col.name">
|
||||||
|
<component
|
||||||
|
:is="tableColumnComponents[col.name].component()"
|
||||||
|
v-bind="
|
||||||
|
tableColumnComponents[col.name].props(col)
|
||||||
|
"
|
||||||
|
v-model="props.row[col.field]"
|
||||||
|
v-on="
|
||||||
|
tableColumnComponents[col.name].event(props)
|
||||||
|
"
|
||||||
|
class="full-width"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
col.name === 'item' ||
|
||||||
|
col.name === 'import'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ col.label + ': ' + col.value }}
|
||||||
|
</template>
|
||||||
|
</component>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<QBtn fab icon="upload" color="primary" @click="importBuys()" />
|
||||||
|
<QTooltip class="text-no-wrap">
|
||||||
|
{{ t('Import buys') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.separation-row {
|
||||||
|
background-color: var(--vn-gray) !important;
|
||||||
|
}
|
||||||
|
.grid-style-transition {
|
||||||
|
transition: transform 0.28s, background-color 0.28s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Import buys: Importar compras
|
||||||
|
Buy deleted: Compra eliminada
|
||||||
|
Buys deleted: Compras eliminadas
|
||||||
|
Confirm deletion: Confirmar eliminación
|
||||||
|
Are you sure you want to delete this buy?: Seguro que quieres eliminar esta compra?
|
||||||
|
Are you sure you want to delete this buys?: Seguro que quieres eliminar estas compras?
|
||||||
|
</i18n>
|
|
@ -0,0 +1,282 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { toCurrency } from 'filters/index';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const importData = ref({
|
||||||
|
file: null,
|
||||||
|
invoice: null,
|
||||||
|
buys: [],
|
||||||
|
observation: null,
|
||||||
|
ref: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lastItemBuysOptions = ref([]);
|
||||||
|
const packagingsOptions = ref([]);
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
label: t('entry.buys.item'),
|
||||||
|
name: 'item',
|
||||||
|
field: 'itemFk',
|
||||||
|
options: lastItemBuysOptions.value,
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'name',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.description'),
|
||||||
|
name: 'description',
|
||||||
|
field: 'description',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.size'),
|
||||||
|
name: 'size',
|
||||||
|
field: 'size',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.packing'),
|
||||||
|
name: 'packing',
|
||||||
|
field: 'packing',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.grouping'),
|
||||||
|
name: 'grouping',
|
||||||
|
field: 'grouping',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.buyingValue'),
|
||||||
|
name: 'buyingValue',
|
||||||
|
field: 'buyingValue',
|
||||||
|
align: 'left',
|
||||||
|
format: (val) => toCurrency(val),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.packagingFk'),
|
||||||
|
name: 'packagingFk',
|
||||||
|
field: 'packagingFk',
|
||||||
|
options: packagingsOptions.value,
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const onFileChange = (e) => {
|
||||||
|
importData.value.file = e;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => fillData(e.target.result);
|
||||||
|
reader.readAsText(importData.value.file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fillData = async (rawData) => {
|
||||||
|
const data = JSON.parse(rawData);
|
||||||
|
const [invoice] = data.invoices;
|
||||||
|
|
||||||
|
importData.value.observation = invoice.tx_awb;
|
||||||
|
const companyName = invoice.tx_company;
|
||||||
|
const boxes = invoice.boxes;
|
||||||
|
const buys = [];
|
||||||
|
|
||||||
|
for (let box of boxes) {
|
||||||
|
const boxVolume = box.nu_length * box.nu_width * box.nu_height;
|
||||||
|
for (let product of box.products) {
|
||||||
|
const packing = product.nu_stems_bunch * product.nu_bunches;
|
||||||
|
buys.push({
|
||||||
|
description: product.nm_product,
|
||||||
|
companyName: companyName,
|
||||||
|
size: product.nu_length,
|
||||||
|
packing: packing,
|
||||||
|
grouping: product.nu_stems_bunch,
|
||||||
|
buyingValue: parseFloat(product.mny_rate_stem),
|
||||||
|
volume: boxVolume,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxesId = boxes.map((box) => box.id_box);
|
||||||
|
importData.value.ref = boxesId.join(', ');
|
||||||
|
await fetchBuys(buys);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchBuys = async (buys) => {
|
||||||
|
try {
|
||||||
|
const params = { buys };
|
||||||
|
const { data } = await axios.post(
|
||||||
|
`Entries/${route.params.id}/importBuysPreview`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
importData.value.buys = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching buys');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const params = importData.value;
|
||||||
|
const hasAnyEmptyRow = params.buys.some((buy) => {
|
||||||
|
return buy.itemFk === null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasAnyEmptyRow) {
|
||||||
|
notify(t('Some of the imported buys does not have an item'), 'negative');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await axios.post(`Entries/${route.params.id}/importBuys`, params);
|
||||||
|
notify('globals.dataSaved', 'positive');
|
||||||
|
redirectToBuysView();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error importing buys', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToBuysView = () => {
|
||||||
|
router.push({ name: 'EntryBuys' });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
:url="`Entries/${route.params.id}/lastItemBuys`"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
order="id DESC"
|
||||||
|
@on-fetch="(data) => (lastItemBuysOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Packagings"
|
||||||
|
:filter="{ fields: ['id'], where: { isBox: true } }"
|
||||||
|
order="id ASC"
|
||||||
|
@on-fetch="(data) => (packagingsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<QForm>
|
||||||
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||||
|
<div>
|
||||||
|
<QBtnGroup push class="q-gutter-x-sm">
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
icon="restart_alt"
|
||||||
|
flat
|
||||||
|
@click="redirectToBuysView()"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.save')"
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
type="submit"
|
||||||
|
:disable="!importData.file"
|
||||||
|
@click="onSubmit()"
|
||||||
|
/>
|
||||||
|
</QBtnGroup>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
<QCard class="q-pa-lg">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QFile
|
||||||
|
:label="t('entry.buys.file')"
|
||||||
|
:multiple="false"
|
||||||
|
v-model="importData.file"
|
||||||
|
@update:model-value="onFileChange($event)"
|
||||||
|
class="required"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon name="vn:attach" class="cursor-pointer">
|
||||||
|
<QTooltip>{{ t('Select a file') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</QFile>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<div v-if="importData.file">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('entry.buys.reference')"
|
||||||
|
v-model="importData.ref"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('entry.buys.observations')"
|
||||||
|
v-model="importData.observation"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:rows="importData.buys"
|
||||||
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
|
<template #body-cell-item="{ row, col }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<VnSelectFilter
|
||||||
|
v-model="row[col.field]"
|
||||||
|
:options="col.options"
|
||||||
|
:option-value="col.optionValue"
|
||||||
|
:option-label="col.optionLabel"
|
||||||
|
hide-selected
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ scope.opt?.id }} -
|
||||||
|
{{ scope.opt?.name }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-packagingFk="{ row, col }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<VnSelectFilter
|
||||||
|
v-model="row[col.field]"
|
||||||
|
:options="col.options"
|
||||||
|
:option-value="col.optionValue"
|
||||||
|
:option-label="col.optionLabel"
|
||||||
|
hide-selected
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</VnRow>
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
|
</QForm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Select a file: Selecciona un fichero
|
||||||
|
Some of the imported buys does not have an item: Algunas de las compras importadas no tienen un artículo
|
||||||
|
</i18n>
|
|
@ -1,15 +1,29 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import EntryDescriptor from './EntryDescriptor.vue';
|
||||||
|
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<!-- Entry searchbar -->
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="EntryList"
|
||||||
|
:label="t('Search entries')"
|
||||||
|
:info="t('You can search by entry reference')"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<QScrollArea class="fit">
|
<QScrollArea class="fit">
|
||||||
<!-- EntryDescriptor -->
|
<EntryDescriptor />
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
<LeftMenu source="card" />
|
<LeftMenu source="card" />
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
|
@ -22,3 +36,9 @@ const stateStore = useStateStore();
|
||||||
</QPage>
|
</QPage>
|
||||||
</QPageContainer>
|
</QPageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search entries: Buscar entradas
|
||||||
|
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
|
|
||||||
|
import { toDate } from 'src/filters';
|
||||||
|
import { usePrintService } from 'composables/usePrintService';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openReport } = usePrintService();
|
||||||
|
|
||||||
|
const entryFilter = {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'travel',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'landed', 'agencyModeFk', 'warehouseOutFk'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'agency',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'warehouseOut',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'warehouseIn',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'supplier',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'nickname'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const entityId = computed(() => {
|
||||||
|
return $props.id || route.params.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = ref(useCardDescription());
|
||||||
|
const setData = (entity) =>
|
||||||
|
(data.value = useCardDescription(entity.supplier.nickname, entity.id));
|
||||||
|
|
||||||
|
const getEntryRedirectionFilter = (entry) => {
|
||||||
|
let entryTravel = entry && entry.travel;
|
||||||
|
|
||||||
|
if (!entryTravel || !entryTravel.landed) return null;
|
||||||
|
|
||||||
|
const date = new Date(entryTravel.landed);
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const from = new Date(date.getTime());
|
||||||
|
from.setDate(from.getDate() - 10);
|
||||||
|
|
||||||
|
const to = new Date(date.getTime());
|
||||||
|
to.setDate(to.getDate() + 10);
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
supplierFk: entry.supplierFk,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showEntryReport = () => {
|
||||||
|
openReport(`Entries/${route.params.id}/entry-order-pdf`);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardDescriptor
|
||||||
|
module="Entry"
|
||||||
|
:url="`Entries/${entityId}`"
|
||||||
|
:filter="entryFilter"
|
||||||
|
:title="data.title"
|
||||||
|
:subtitle="data.subtitle"
|
||||||
|
@on-fetch="setData"
|
||||||
|
data-key="entryData"
|
||||||
|
>
|
||||||
|
<template #menu="{ entity }">
|
||||||
|
<QItem v-ripple clickable @click="showEntryReport(entity)">
|
||||||
|
<QItemSection>{{ t('Show entry report') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-ripple clickable>
|
||||||
|
<QItemSection>
|
||||||
|
<RouterLink :to="{ name: 'EntryList' }" class="color-vn-text">
|
||||||
|
{{ t('Go to module index') }}
|
||||||
|
</RouterLink>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.descriptor.agency')"
|
||||||
|
:value="entity.travel.agency.name"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.descriptor.landed')"
|
||||||
|
:value="toDate(entity.travel.landed)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.descriptor.warehouseOut')"
|
||||||
|
:value="entity.travel.warehouseOut.name"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #icons="{ entity }">
|
||||||
|
<QCardActions class="q-gutter-x-md">
|
||||||
|
<QIcon
|
||||||
|
v-if="entity.isExcludedFromAvailable"
|
||||||
|
name="vn:inventory"
|
||||||
|
color="primary"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon v-if="entity.isRaid" name="vn:web" color="primary" size="xs">
|
||||||
|
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QCardActions>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ entity }">
|
||||||
|
<QCardActions>
|
||||||
|
<QBtn
|
||||||
|
:to="`/supplier/${entity.supplier.id}`"
|
||||||
|
size="md"
|
||||||
|
icon="vn:supplier"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Supplier card') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
:to="{
|
||||||
|
name: 'TravelMain',
|
||||||
|
query: {
|
||||||
|
params: JSON.stringify({
|
||||||
|
agencyModeFk: entity.travel?.agencyModeFk,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
size="md"
|
||||||
|
icon="local_airport"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('All travels with current agency') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
:to="{
|
||||||
|
name: 'EntryMain',
|
||||||
|
query: {
|
||||||
|
params: getEntryRedirectionFilter(entity),
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
size="md"
|
||||||
|
icon="vn:entry"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('All entries with current supplier') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</template>
|
||||||
|
</CardDescriptor>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Supplier card: Ficha del proveedor
|
||||||
|
All travels with current agency: Todos los envíos con la agencia actual
|
||||||
|
All entries with current supplier: Todas las entradas con el proveedor actual
|
||||||
|
Show entry report: Ver informe del pedido
|
||||||
|
Go to module index: Ir al índice del modulo
|
||||||
|
Inventory entry: Es inventario
|
||||||
|
Virtual entry: Es una redada
|
||||||
|
</i18n>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup>
|
||||||
|
import EntryDescriptor from './EntryDescriptor.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QPopupProxy>
|
||||||
|
<EntryDescriptor v-if="$props.id" :id="$props.id" />
|
||||||
|
</QPopupProxy>
|
||||||
|
</template>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<script setup>
|
||||||
|
import VnLog from 'src/components/common/VnLog.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnLog model="Entry" url="/EntryLogs"></VnLog>
|
||||||
|
</template>
|
|
@ -0,0 +1,99 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const entryObservationsRef = ref(null);
|
||||||
|
const entryObservationsOptions = ref([]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (entryObservationsRef.value) entryObservationsRef.value.reload();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="ObservationTypes"
|
||||||
|
@on-fetch="(data) => (entryObservationsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<CrudModel
|
||||||
|
data-key="EntryAccount"
|
||||||
|
url="EntryObservations"
|
||||||
|
model="EntryAccount"
|
||||||
|
:filter="{
|
||||||
|
fields: ['id', 'entryFk', 'observationTypeFk', 'description'],
|
||||||
|
where: { entryFk: route.params.id },
|
||||||
|
}"
|
||||||
|
ref="entryObservationsRef"
|
||||||
|
:default-remove="false"
|
||||||
|
:data-required="{ entryFk: route.params.id }"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard class="q-pa-md">
|
||||||
|
<VnRow
|
||||||
|
v-for="(row, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
class="row q-gutter-md q-mb-md"
|
||||||
|
>
|
||||||
|
<div class="col-3">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('entry.notes.observationType')"
|
||||||
|
v-model="row.observationTypeFk"
|
||||||
|
:options="entryObservationsOptions"
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
hide-selected
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('entry.notes.description')"
|
||||||
|
v-model="row.description"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 row justify-center items-center">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="entryObservationsRef.remove([row])"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Remove note') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<QIcon
|
||||||
|
name="add"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="entryObservationsRef.insert()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add note') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Add note: Añadir nota
|
||||||
|
Remove note: Quitar nota
|
||||||
|
</i18n>
|
|
@ -0,0 +1,359 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, computed, onUpdated } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
||||||
|
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||||
|
|
||||||
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
onUpdated(() => summaryRef.value.fetch());
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const summaryRef = ref();
|
||||||
|
const entry = ref();
|
||||||
|
const entryBuys = ref([]);
|
||||||
|
const entryUrl = ref();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
entryUrl.value = (await getUrl('entry/')) + entityId.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableColumnComponents = {
|
||||||
|
quantity: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
stickers: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
packagingFk: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
weight: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
packing: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
grouping: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
buyingValue: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
pvp: {
|
||||||
|
component: () => 'span',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const entriesTableColumns = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: t('entry.summary.quantity'),
|
||||||
|
field: 'quantity',
|
||||||
|
name: 'quantity',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.stickers'),
|
||||||
|
field: 'stickers',
|
||||||
|
name: 'stickers',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.package'),
|
||||||
|
field: 'packagingFk',
|
||||||
|
name: 'packagingFk',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.weight'),
|
||||||
|
field: 'weight',
|
||||||
|
name: 'weight',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.packing'),
|
||||||
|
field: 'packing',
|
||||||
|
name: 'packing',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.grouping'),
|
||||||
|
field: 'grouping',
|
||||||
|
name: 'grouping',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.buyingValue'),
|
||||||
|
field: 'buyingValue',
|
||||||
|
name: 'buyingValue',
|
||||||
|
align: 'left',
|
||||||
|
format: (value) => toCurrency(value),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.import'),
|
||||||
|
name: 'amount',
|
||||||
|
align: 'left',
|
||||||
|
format: (_, row) => toCurrency(row.buyingValue * row.quantity),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.summary.pvp'),
|
||||||
|
name: 'pvp',
|
||||||
|
align: 'left',
|
||||||
|
format: (_, row) => toCurrency(row.price2) + ' / ' + toCurrency(row.price3),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
async function setEntryData(data) {
|
||||||
|
if (data) entry.value = data;
|
||||||
|
await fetchEntryBuys();
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchEntryBuys = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(`Entries/${entry.value.id}/getBuys`);
|
||||||
|
if (data) entryBuys.value = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching entry buys');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardSummary
|
||||||
|
ref="summaryRef"
|
||||||
|
:url="`Entries/${entityId}/getEntry`"
|
||||||
|
@on-fetch="(data) => setEntryData(data)"
|
||||||
|
>
|
||||||
|
<template #header-left>
|
||||||
|
<a class="header link" :href="entryUrl">
|
||||||
|
<QIcon name="open_in_new" color="white" size="sm" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template #header>
|
||||||
|
<span>{{ entry.id }} - {{ entry.supplier.nickname }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<a class="header link" :href="entryUrl">
|
||||||
|
{{ t('globals.summary.basicData') }}
|
||||||
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
</a>
|
||||||
|
<VnRow>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.commission')"
|
||||||
|
:value="entry.commission"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.currency')"
|
||||||
|
:value="entry.currency.name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.company')"
|
||||||
|
:value="entry.company.code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.reference')"
|
||||||
|
:value="entry.reference"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.invoiceNumber')"
|
||||||
|
:value="entry.invoiceNumber"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.ordered')"
|
||||||
|
:value="entry.isOrdered"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.confirmed')"
|
||||||
|
:value="entry.isConfirmed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.booked')"
|
||||||
|
:value="entry.isBooked"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv :label="t('entry.summary.raid')" :value="entry.isRaid" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.excludedFromAvailable')"
|
||||||
|
:value="entry.isExcludedFromAvailable"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<a class="header link" :href="entryUrl">
|
||||||
|
{{ t('Travel data') }}
|
||||||
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
</a>
|
||||||
|
<VnRow>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv :label="t('entry.summary.travelReference')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ entry.travel.ref }}
|
||||||
|
<TravelDescriptorProxy :id="entry.travel.id" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelAgency')"
|
||||||
|
:value="entry.travel.agency.name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelShipped')"
|
||||||
|
:value="toDate(entry.travel.shipped)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelWarehouseOut')"
|
||||||
|
:value="entry.travel.warehouseOut.name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelDelivered')"
|
||||||
|
:value="entry.travel.isDelivered"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelLanded')"
|
||||||
|
:value="toDate(entry.travel.landed)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelWarehouseIn')"
|
||||||
|
:value="entry.travel.warehouseIn.name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.summary.travelReceived')"
|
||||||
|
:value="entry.travel.isReceived"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-two" style="min-width: 100%">
|
||||||
|
<a class="header">
|
||||||
|
{{ t('entry.summary.buys') }}
|
||||||
|
</a>
|
||||||
|
<QTable
|
||||||
|
:rows="entryBuys"
|
||||||
|
:columns="entriesTableColumns"
|
||||||
|
hide-bottom
|
||||||
|
row-key="index"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
>
|
||||||
|
<template #body="{ cols, row, rowIndex }">
|
||||||
|
<QTr no-hover>
|
||||||
|
<QTd v-for="col in cols" :key="col.name">
|
||||||
|
<component
|
||||||
|
:is="tableColumnComponents[col.name].component()"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
col.name !== 'observation' &&
|
||||||
|
col.name !== 'isConfirmed'
|
||||||
|
"
|
||||||
|
>{{ col.value }}</template
|
||||||
|
>
|
||||||
|
<QTooltip v-if="col.toolTip">{{
|
||||||
|
col.toolTip
|
||||||
|
}}</QTooltip>
|
||||||
|
</component>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
<QTr no-hover>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ row.item.itemType.code }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ row.item.id }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ row.item.size }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ toCurrency(row.item.minPrice) }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd colspan="6">
|
||||||
|
<span>{{ row.item.concept }}</span>
|
||||||
|
<span v-if="row.item.subName" class="subName">
|
||||||
|
{{ row.item.subName }}
|
||||||
|
</span>
|
||||||
|
<fetched-tags :item="row.item" :max-length="5" />
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
||||||
|
<QTr
|
||||||
|
v-if="rowIndex !== entryBuys.length - 1"
|
||||||
|
class="separation-row"
|
||||||
|
>
|
||||||
|
<QTd colspan="10" style="height: 24px" />
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CardSummary>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.separation-row {
|
||||||
|
background-color: var(--vn-gray) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Travel data: 'Datos envío'
|
||||||
|
</i18n>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script setup>
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import EntrySummary from './EntrySummary.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<EntrySummary v-if="$props.id" :id="$props.id" />
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.q-dialog .summary .header {
|
||||||
|
position: sticky;
|
||||||
|
z-index: $z-max;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,246 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const currenciesOptions = ref([]);
|
||||||
|
const companiesOptions = ref([]);
|
||||||
|
const suppliersOptions = ref([]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="companiesRef"
|
||||||
|
url="Companies"
|
||||||
|
:filter="{ fields: ['id', 'code'] }"
|
||||||
|
order="code"
|
||||||
|
@on-fetch="(data) => (companiesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="currenciesRef"
|
||||||
|
url="Currencies"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
order="code"
|
||||||
|
@on-fetch="(data) => (currenciesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers"
|
||||||
|
:filter="{ fields: ['id', 'nickname', 'name'] }"
|
||||||
|
order="nickname"
|
||||||
|
@on-fetch="(data) => (suppliersOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
|
||||||
|
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params }">
|
||||||
|
<QList dense class="list q-gutter-y-sm q-mt-sm">
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.search"
|
||||||
|
:label="t('params.search')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.reference"
|
||||||
|
:label="t('params.reference')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.invoiceNumber"
|
||||||
|
:label="t('params.invoiceNumber')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.travelFk"
|
||||||
|
:label="t('params.travelFk')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('params.companyFk')"
|
||||||
|
v-model="params.companyFk"
|
||||||
|
:options="companiesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="code"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('params.currencyFk')"
|
||||||
|
v-model="params.currencyFk"
|
||||||
|
:options="currenciesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('params.supplierFk')"
|
||||||
|
v-model="params.supplierFk"
|
||||||
|
:options="suppliersOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{
|
||||||
|
scope.opt?.name + ': ' + scope.opt?.nickname
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('params.created')"
|
||||||
|
is-outlined
|
||||||
|
v-model="params.created"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('params.from')"
|
||||||
|
is-outlined
|
||||||
|
v-model="params.from"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('params.to')"
|
||||||
|
is-outlined
|
||||||
|
v-model="params.to"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('params.isBooked')"
|
||||||
|
v-model="params.isBooked"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('params.isConfirmed')"
|
||||||
|
v-model="params.isConfirmed"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('params.isOrdered')"
|
||||||
|
v-model="params.isOrdered"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.list {
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
.list * {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
search: General search
|
||||||
|
reference: Reference
|
||||||
|
invoiceNumber: Invoice number
|
||||||
|
travelFk: Travel
|
||||||
|
companyFk: Company
|
||||||
|
currencyFk: Currency
|
||||||
|
supplierFk: Supplier
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
created: Created
|
||||||
|
isBooked: Booked
|
||||||
|
isConfirmed: Confirmed
|
||||||
|
isOrdered: Ordered
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
search: Búsqueda general
|
||||||
|
reference: Referencia
|
||||||
|
invoiceNumber: Núm. factura
|
||||||
|
travelFk: Envío
|
||||||
|
companyFk: Empresa
|
||||||
|
currencyFk: Moneda
|
||||||
|
supplierFk: Proveedor
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
created: Fecha creación
|
||||||
|
isBooked: Asentado
|
||||||
|
isConfirmed: Confirmado
|
||||||
|
isOrdered: Pedida
|
||||||
|
</i18n>
|
|
@ -1,22 +1,144 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
|
import EntrySummaryDialog from './Card/EntrySummaryDialog.vue';
|
||||||
|
import EntryFilter from './EntryFilter.vue';
|
||||||
|
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { toDate } from 'src/filters/index';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
function navigate(id) {
|
||||||
|
router.push({ path: `/entry/${id}` });
|
||||||
|
}
|
||||||
|
|
||||||
const redirectToCreateView = () => {
|
const redirectToCreateView = () => {
|
||||||
router.push({ name: 'EntryCreate' });
|
router.push({ name: 'EntryCreate' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function viewSummary(id) {
|
||||||
|
quasar.dialog({
|
||||||
|
component: EntrySummaryDialog,
|
||||||
|
componentProps: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
stateStore.rightDrawer = true;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit text-grey-8">
|
||||||
|
<EntryFilter data-key="EntryList" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="card-list">
|
||||||
|
<VnPaginate
|
||||||
|
data-key="EntryList"
|
||||||
|
url="Entries/filter"
|
||||||
|
order="landed DESC, id DESC"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<CardList
|
||||||
|
v-for="row of rows"
|
||||||
|
:key="row.id"
|
||||||
|
:title="row.reference"
|
||||||
|
@click="navigate(row.id)"
|
||||||
|
:id="row.id"
|
||||||
|
:has-info-icons="!!row.isExcludedFromAvailable || !!row.isRaid"
|
||||||
|
>
|
||||||
|
<template #info-icons>
|
||||||
|
<QIcon
|
||||||
|
v-if="row.isExcludedFromAvailable"
|
||||||
|
name="vn:inventory"
|
||||||
|
color="primary"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon
|
||||||
|
v-if="row.isRaid"
|
||||||
|
name="vn:web"
|
||||||
|
color="primary"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
<template #list-items>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.list.landed')"
|
||||||
|
:value="toDate(row.landed)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.list.booked')"
|
||||||
|
:value="!!row.isBooked"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.list.invoiceNumber')"
|
||||||
|
:value="row.invoiceNumber"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.list.confirmed')"
|
||||||
|
:value="!!row.isConfirmed"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.list.supplier')"
|
||||||
|
:value="row.supplierName"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('entry.list.ordered')"
|
||||||
|
:value="!!row.isOrdered"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<QBtn
|
||||||
|
:label="t('components.smartCard.openSummary')"
|
||||||
|
@click.stop="viewSummary(row.id)"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</CardList>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
</QPage>
|
||||||
<QPageSticky :offset="[20, 20]">
|
<QPageSticky :offset="[20, 20]">
|
||||||
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('entry.list.newEntry') }}
|
{{ t('entry.list.newEntry') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</QPage>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card-list {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 60em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search entries: Buscar entradas
|
||||||
|
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||||
|
Inventory entry: Es inventario
|
||||||
|
Virtual entry: Es una redada
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useI18n } from 'vue-i18n';
|
||||||
import LeftMenu from 'src/components/LeftMenu.vue';
|
import LeftMenu from 'src/components/LeftMenu.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="EntryList"
|
||||||
|
:label="t('Search entries')"
|
||||||
|
:info="t('You can search by entry reference')"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<QScrollArea class="fit text-grey-8">
|
<QScrollArea class="fit text-grey-8">
|
||||||
<LeftMenu />
|
<LeftMenu />
|
||||||
|
@ -15,3 +28,9 @@ const stateStore = useStateStore();
|
||||||
<RouterView></RouterView>
|
<RouterView></RouterView>
|
||||||
</QPageContainer>
|
</QPageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search entries: Buscar entradas
|
||||||
|
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script setup>
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||||
|
// import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
|
<QScrollArea class="fit">
|
||||||
|
<RouteDescriptor />
|
||||||
|
<QSeparator />
|
||||||
|
<LeftMenu source="card" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPageContainer>
|
||||||
|
<QPage>
|
||||||
|
<RouterView></RouterView>
|
||||||
|
</QPage>
|
||||||
|
</QPageContainer>
|
||||||
|
</template>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import useCardDescription from 'composables/useCardDescription';
|
||||||
|
import { dashIfEmpty, toDate } from 'src/filters';
|
||||||
|
import RouteDescriptorMenu from 'pages/Route/Card/RouteDescriptorMenu.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const entityId = computed(() => {
|
||||||
|
return $props.id || route.params.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'workerFk',
|
||||||
|
'agencyModeFk',
|
||||||
|
'created',
|
||||||
|
'm3',
|
||||||
|
'warehouseFk',
|
||||||
|
'description',
|
||||||
|
'vehicleFk',
|
||||||
|
'kmStart',
|
||||||
|
'kmEnd',
|
||||||
|
'started',
|
||||||
|
'finished',
|
||||||
|
'cost',
|
||||||
|
'zoneFk',
|
||||||
|
'isOk',
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
|
||||||
|
{
|
||||||
|
relation: 'vehicle',
|
||||||
|
scope: { fields: ['id', 'm3'] },
|
||||||
|
},
|
||||||
|
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||||
|
{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: { relation: 'emailUser', scope: { fields: ['email'] } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const data = ref(useCardDescription());
|
||||||
|
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardDescriptor
|
||||||
|
module="Route"
|
||||||
|
:url="`Routes/${entityId}`"
|
||||||
|
:filter="filter"
|
||||||
|
:title="data.title"
|
||||||
|
:subtitle="data.subtitle"
|
||||||
|
data-key="Routes"
|
||||||
|
@on-fetch="setData"
|
||||||
|
>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<VnLv :label="t('Date')" :value="toDate(entity?.created)" />
|
||||||
|
<VnLv :label="t('Agency')" :value="entity?.agencyMode?.name" />
|
||||||
|
<VnLv :label="t('Zone')" :value="entity?.zone?.name" />
|
||||||
|
<VnLv
|
||||||
|
:label="t('Volume')"
|
||||||
|
:value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty(
|
||||||
|
entity?.vehicle?.m3
|
||||||
|
)} m³`"
|
||||||
|
/>
|
||||||
|
<VnLv :label="t('Description')" :value="entity?.description" />
|
||||||
|
</template>
|
||||||
|
<template #menu="{ entity }">
|
||||||
|
<RouteDescriptorMenu :route="entity" />
|
||||||
|
</template>
|
||||||
|
</CardDescriptor>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Date: Fecha
|
||||||
|
Agency: Agencia
|
||||||
|
Zone: Zona
|
||||||
|
Volume: Volumen
|
||||||
|
Description: Descripción
|
||||||
|
</i18n>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
route: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
function confirmRemove() {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('confirmDeletion'),
|
||||||
|
message: t('confirmDeletionMessage'),
|
||||||
|
promise: remove,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => await router.push({ name: 'RouteList' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove() {
|
||||||
|
if (!props.route.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await axios.delete(`Routes/${props.route.id}`);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataDeleted'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add reports
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QItem @click="confirmRemove" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="delete" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('deleteRoute') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
confirmDeletion: Confirm deletion
|
||||||
|
confirmDeletionMessage: Are you sure you want to delete this route?
|
||||||
|
deleteRoute: Delete route
|
||||||
|
es:
|
||||||
|
confirmDeletion: Confirmar eliminación,
|
||||||
|
confirmDeletionMessage: Seguro que quieres eliminar esta ruta?
|
||||||
|
deleteRoute: Eliminar ruta
|
||||||
|
</i18n>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup>
|
||||||
|
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QPopupProxy>
|
||||||
|
<RouteDescriptor v-if="$props.id" :id="$props.id" />
|
||||||
|
</QPopupProxy>
|
||||||
|
</template>
|
|
@ -0,0 +1,234 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const workerList = ref([]);
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const vehicleList = ref([]);
|
||||||
|
const warehouseList = ref([]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
sort-by="nickname ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (workerList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (agencyList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Vehicles"
|
||||||
|
:filter="{ fields: ['id', 'numberPlate'] }"
|
||||||
|
sort-by="numberPlate ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (vehicleList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData url="Warehouses" @on-fetch="(data) => (warehouseList = data)" auto-load />
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:search-button="true"
|
||||||
|
@search="emit('search')"
|
||||||
|
>
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params }">
|
||||||
|
<QList dense>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="workerList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Worker')"
|
||||||
|
v-model="params.workerFk"
|
||||||
|
:options="workerList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ opt.nickname }},{{ opt.code }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="agencyList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="params.agencyModeFk"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.from"
|
||||||
|
:label="t('From')"
|
||||||
|
is-outlined
|
||||||
|
:disable="Boolean(params.scopeDays)"
|
||||||
|
@update:model-value="params.scopeDays = null"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.to"
|
||||||
|
:label="t('To')"
|
||||||
|
is-outlined
|
||||||
|
:disable="Boolean(params.scopeDays)"
|
||||||
|
@update:model-value="params.scopeDays = null"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.scopeDays"
|
||||||
|
type="number"
|
||||||
|
:label="t('Days Onward')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
:disable="Boolean(params.from || params.to)"
|
||||||
|
@update:model-value="
|
||||||
|
params.to = null;
|
||||||
|
params.from = null;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="vehicleList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Vehicle')"
|
||||||
|
v-model="params.vehicleFk"
|
||||||
|
:options="vehicleList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="numberPlate"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.m3" label="m³" is-outlined clearable />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="vehicleList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Warehouse')"
|
||||||
|
v-model="params.warehouseFk"
|
||||||
|
:options="warehouseList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.description"
|
||||||
|
:label="t('Description')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
warehouseFk: Warehouse
|
||||||
|
description: Description
|
||||||
|
m3: m³
|
||||||
|
vehicleFk: Vehicle
|
||||||
|
agencyModeFk: Agency
|
||||||
|
workerFk: Worker
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
warehouseFk: Almacén
|
||||||
|
description: Descripción
|
||||||
|
m3: m³
|
||||||
|
vehicleFk: Vehículo
|
||||||
|
agencyModeFk: Agencia
|
||||||
|
workerFk: Trabajador
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
Warehouse: Almacén
|
||||||
|
Description: Descripción
|
||||||
|
Vehicle: Vehículo
|
||||||
|
Agency: Agencia
|
||||||
|
Worker: Trabajador
|
||||||
|
From: Desde
|
||||||
|
To: Hasta
|
||||||
|
</i18n>
|
|
@ -0,0 +1,214 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const shelvingId = route.params?.id || null;
|
||||||
|
const isNew = Boolean(!shelvingId);
|
||||||
|
const defaultInitialData = {
|
||||||
|
agencyModeFk: null,
|
||||||
|
created: null,
|
||||||
|
description: '',
|
||||||
|
vehicleFk: null,
|
||||||
|
workerFk: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const workerList = ref([]);
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const vehicleList = ref([]);
|
||||||
|
|
||||||
|
const routeFilter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'workerFk',
|
||||||
|
'agencyModeFk',
|
||||||
|
'created',
|
||||||
|
'm3',
|
||||||
|
'warehouseFk',
|
||||||
|
'description',
|
||||||
|
'vehicleFk',
|
||||||
|
'kmStart',
|
||||||
|
'kmEnd',
|
||||||
|
'started',
|
||||||
|
'finished',
|
||||||
|
'cost',
|
||||||
|
'zoneFk',
|
||||||
|
'isOk',
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
|
||||||
|
{
|
||||||
|
relation: 'vehicle',
|
||||||
|
scope: { fields: ['id', 'm3'] },
|
||||||
|
},
|
||||||
|
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||||
|
{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: { relation: 'emailUser', scope: { fields: ['email'] } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const onSave = (data, response) => {
|
||||||
|
if (isNew) {
|
||||||
|
axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
|
||||||
|
router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnSubToolbar />
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
sort-by="nickname ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (workerList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (agencyList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Vehicles"
|
||||||
|
:filter="{ fields: ['id', 'numberPlate'] }"
|
||||||
|
sort-by="numberPlate ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (vehicleList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url="isNew ? null : `Routes/${shelvingId}`"
|
||||||
|
:url-create="isNew ? 'Routes' : null"
|
||||||
|
:observe-form-changes="!isNew"
|
||||||
|
:filter="routeFilter"
|
||||||
|
model="route"
|
||||||
|
:auto-load="!isNew"
|
||||||
|
:form-initial-data="defaultInitialData"
|
||||||
|
@on-data-saved="onSave"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Worker')"
|
||||||
|
v-model="data.workerFk"
|
||||||
|
:options="workerList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ opt.nickname }},{{ opt.code }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Vehicle')"
|
||||||
|
v-model="data.vehicleFk"
|
||||||
|
:options="vehicleList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="numberPlate"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="data.agencyModeFk"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate v-model="data.created" :label="t('Created')" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<template v-if="!isNew">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.kmStart"
|
||||||
|
:label="t('Km Start')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput v-model="data.kmEnd" :label="t('Km End')" clearable />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInputTime
|
||||||
|
v-model="data.started"
|
||||||
|
:label="t('Hour started')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputTime
|
||||||
|
v-model="data.finished"
|
||||||
|
:label="t('Hour finished')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.description"
|
||||||
|
:label="t('Description')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script setup>
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
const { t } = useI18n();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="RouteList"
|
||||||
|
:label="t('Search route')"
|
||||||
|
:info="t('You can search by route reference')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search route: Buscar rutas
|
||||||
|
You can search by route reference: Puedes buscar por referencia de la ruta
|
||||||
|
</i18n>
|
|
@ -0,0 +1,314 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import { QIcon } from 'quasar';
|
||||||
|
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
|
||||||
|
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const route = useRoute();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const isDialog = Boolean($props.id);
|
||||||
|
const hideRightDrawer = () => {
|
||||||
|
if (!isDialog) {
|
||||||
|
stateStore.rightDrawer = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onMounted(hideRightDrawer);
|
||||||
|
onUnmounted(hideRightDrawer);
|
||||||
|
const getTotalPackages = (tickets) => {
|
||||||
|
return (tickets || []).reduce((sum, ticket) => sum + ticket.packages, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ticketColumns = ref([
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
label: t('route.summary.order'),
|
||||||
|
field: (row) => dashIfEmpty(row?.priority),
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'street',
|
||||||
|
label: t('route.summary.street'),
|
||||||
|
field: (row) => row?.street,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'city',
|
||||||
|
label: t('route.summary.city'),
|
||||||
|
field: (row) => row?.city,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pc',
|
||||||
|
label: t('route.summary.pc'),
|
||||||
|
field: (row) => row?.postalCode,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'client',
|
||||||
|
label: t('route.summary.client'),
|
||||||
|
field: (row) => row?.nickname,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warehouse',
|
||||||
|
label: t('route.summary.warehouse'),
|
||||||
|
field: (row) => row?.warehouseName,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packages',
|
||||||
|
label: t('route.summary.packages'),
|
||||||
|
field: (row) => row?.packages,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'volume',
|
||||||
|
label: t('route.summary.m3'),
|
||||||
|
field: (row) => row?.volume,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packaging',
|
||||||
|
label: t('route.summary.packaging'),
|
||||||
|
field: (row) => row?.ipt,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ticket',
|
||||||
|
label: t('route.summary.ticket'),
|
||||||
|
field: (row) => row?.id,
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'observations',
|
||||||
|
label: '',
|
||||||
|
field: (row) => row?.ticketObservation,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const openBuscaman = async (route, ticket) => {
|
||||||
|
if (!route.vehicleFk) throw new Error(`The route doesn't have a vehicle`);
|
||||||
|
const response = await axios.get(`Routes/${route.vehicleFk}/getDeliveryPoint`);
|
||||||
|
if (!response.data)
|
||||||
|
throw new Error(`The route's vehicle doesn't have a delivery point`);
|
||||||
|
|
||||||
|
const address = `${response.data}+to:${ticket.postalCode} ${ticket.city} ${ticket.street}`;
|
||||||
|
window.open(
|
||||||
|
'https://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=' +
|
||||||
|
encodeURI(address),
|
||||||
|
'_blank'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<CardSummary ref="summary" :url="`Routes/${entityId}/summary`">
|
||||||
|
<template #header-left>
|
||||||
|
<RouterLink :to="{ name: `RouteSummary`, params: { id: entityId } }">
|
||||||
|
<QIcon name="open_in_new" color="white" size="sm" />
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #header="{ entity }">
|
||||||
|
<span>{{ `${entity?.route.id} - ${entity?.route?.description}` }}</span>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnLv :label="t('ID')" :value="entity?.route.id" />
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.date')"
|
||||||
|
:value="toDate(entity?.route.created)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.agency')"
|
||||||
|
:value="entity?.route?.agencyMode?.name"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.vehicle')"
|
||||||
|
:value="entity.route?.vehicle?.numberPlate"
|
||||||
|
/>
|
||||||
|
<VnLv :label="t('route.summary.driver')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ dashIfEmpty(entity?.route?.worker?.user?.name) }}
|
||||||
|
<WorkerDescriptorProxy :id="entity.route?.workerFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.cost')"
|
||||||
|
:value="toCurrency(entity.route?.cost)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.started')"
|
||||||
|
:value="toHour(entity?.route.started)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.finished')"
|
||||||
|
:value="toHour(entity?.route.finished)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.kmStart')"
|
||||||
|
:value="dashIfEmpty(entity?.route?.kmStart)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.kmEnd')"
|
||||||
|
:value="dashIfEmpty(entity?.route?.kmEnd)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.volume')"
|
||||||
|
:value="`${dashIfEmpty(entity?.route?.m3)} / ${dashIfEmpty(
|
||||||
|
entity?.route?.vehicle?.m3
|
||||||
|
)} m³`"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.packages')"
|
||||||
|
:value="getTotalPackages(entity.tickets)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<div class="header">
|
||||||
|
{{ t('route.summary.description') }}
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{{ dashIfEmpty(entity?.route?.description) }}
|
||||||
|
</p>
|
||||||
|
</QCard>
|
||||||
|
|
||||||
|
<QCard class="vn-max">
|
||||||
|
<div class="header">
|
||||||
|
{{ t('route.summary.tickets') }}
|
||||||
|
</div>
|
||||||
|
<QTable
|
||||||
|
:columns="ticketColumns"
|
||||||
|
:rows="entity?.tickets"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
row-key="id"
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
|
<template #body-cell-city="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span
|
||||||
|
class="text-primary cursor-pointer"
|
||||||
|
@click="openBuscaman(entity?.route, row)"
|
||||||
|
>
|
||||||
|
{{ value }}
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-client="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span class="text-primary cursor-pointer">
|
||||||
|
{{ value }}
|
||||||
|
<CustomerDescriptorProxy :id="row?.clientFk" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-ticket="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span class="text-primary cursor-pointer">
|
||||||
|
{{ value }}
|
||||||
|
<TicketDescriptorProxy :id="row?.id" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-observations="{ value }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<QIcon
|
||||||
|
v-if="value"
|
||||||
|
name="vn:notes"
|
||||||
|
color="primary"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ value }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CardSummary>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
route:
|
||||||
|
summary:
|
||||||
|
date: Date
|
||||||
|
agency: Agency
|
||||||
|
vehicle: Vehicle
|
||||||
|
driver: Driver
|
||||||
|
cost: Cost
|
||||||
|
started: Started time
|
||||||
|
finished: Finished time
|
||||||
|
kmStart: Km start
|
||||||
|
kmEnd: Km end
|
||||||
|
volume: Volume
|
||||||
|
packages: Packages
|
||||||
|
description: Description
|
||||||
|
tickets: Tickets
|
||||||
|
order: Order
|
||||||
|
street: Street
|
||||||
|
city: City
|
||||||
|
pc: PC
|
||||||
|
client: Client
|
||||||
|
warehouse: Warehouse
|
||||||
|
m3: m³
|
||||||
|
packaging: Packaging
|
||||||
|
ticket: Ticket
|
||||||
|
es:
|
||||||
|
route:
|
||||||
|
summary:
|
||||||
|
date: Fecha
|
||||||
|
agency: Agencia
|
||||||
|
vehicle: Vehículo
|
||||||
|
driver: Conductor
|
||||||
|
cost: Costo
|
||||||
|
started: Hora inicio
|
||||||
|
finished: Hora fin
|
||||||
|
kmStart: Km inicio
|
||||||
|
kmEnd: Km fin
|
||||||
|
volume: Volumen
|
||||||
|
packages: Bultos
|
||||||
|
description: Descripción
|
||||||
|
tickets: Tickets
|
||||||
|
order: Orden
|
||||||
|
street: Dirección fiscal
|
||||||
|
city: Población
|
||||||
|
pc: CP
|
||||||
|
client: Cliente
|
||||||
|
warehouse: Almacén
|
||||||
|
packaging: Encajado
|
||||||
|
</i18n>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script setup>
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<RouteSummary v-if="$props.id" :id="$props.id" />
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.q-dialog .route .header {
|
||||||
|
position: sticky;
|
||||||
|
z-index: $z-max;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,530 @@
|
||||||
|
<script setup>
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import { useValidator } from 'composables/useValidator';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
||||||
|
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import RouteSummaryDialog from 'pages/Route/Card/RouteSummaryDialog.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { validate } = useValidator();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const to = Date.vnNew();
|
||||||
|
to.setDate(to.getDate() + 1);
|
||||||
|
to.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const from = Date.vnNew();
|
||||||
|
from.setDate(from.getDate());
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const params = ref({ from, to });
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'ID',
|
||||||
|
label: t('ID'),
|
||||||
|
field: (row) => row.id,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'worker',
|
||||||
|
label: t('Worker'),
|
||||||
|
field: (row) => row.workerUserName,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'agency',
|
||||||
|
label: t('Agency'),
|
||||||
|
field: (row) => row.agencyName,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vehicle',
|
||||||
|
label: t('Vehicle'),
|
||||||
|
field: (row) => row.vehiclePlateNumber,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'date',
|
||||||
|
label: t('Date'),
|
||||||
|
field: (row) => row.created,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'volume',
|
||||||
|
label: 'm³',
|
||||||
|
field: (row) => dashIfEmpty(row.m3),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: t('Description'),
|
||||||
|
field: (row) => row.description,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'started',
|
||||||
|
label: t('Hour started'),
|
||||||
|
field: (row) => toHour(row.started),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finished',
|
||||||
|
label: t('Hour finished'),
|
||||||
|
field: (row) => toHour(row.finished),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
const workers = ref([]);
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const vehicleList = ref([]);
|
||||||
|
const updateRoute = async (route) => {
|
||||||
|
try {
|
||||||
|
return await axios.patch(`Routes/${route.id}`, route);
|
||||||
|
} catch (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateVehicle = (row, vehicle) => {
|
||||||
|
row.vehicleFk = vehicle.id;
|
||||||
|
row.vehiclePlateNumber = vehicle.numberPlate;
|
||||||
|
updateRoute(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAgency = (row, agency) => {
|
||||||
|
row.agencyModeFk = agency.id;
|
||||||
|
row.agencyName = agency.name;
|
||||||
|
updateRoute(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateWorker = (row, worker) => {
|
||||||
|
row.workerFk = worker.id;
|
||||||
|
row.workerUserName = worker.name;
|
||||||
|
updateRoute(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmationDialog = ref(false);
|
||||||
|
const startingDate = ref(null);
|
||||||
|
|
||||||
|
const cloneRoutes = () => {
|
||||||
|
axios.post('Routes/clone', {
|
||||||
|
created: startingDate.value,
|
||||||
|
ids: selectedRows.value.map((row) => row?.id),
|
||||||
|
});
|
||||||
|
refreshKey.value++;
|
||||||
|
startingDate.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const markAsServed = () => {
|
||||||
|
selectedRows.value.forEach((row) => {
|
||||||
|
if (row?.id) {
|
||||||
|
axios.patch(`Routes/${row?.id}`, { isOk: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
refreshKey.value++;
|
||||||
|
startingDate.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function previewRoute(id) {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quasar.dialog({
|
||||||
|
component: RouteSummaryDialog,
|
||||||
|
componentProps: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<RouteSearchbar />
|
||||||
|
</Teleport>
|
||||||
|
<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>
|
||||||
|
<QDialog v-model="confirmationDialog">
|
||||||
|
<QCard style="min-width: 350px">
|
||||||
|
<QCardSection>
|
||||||
|
<p class="text-h6 q-ma-none">{{ t('Select the starting date') }}</p>
|
||||||
|
</QCardSection>
|
||||||
|
|
||||||
|
<QCardSection class="q-pt-none">
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('Stating date')"
|
||||||
|
v-model="startingDate"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<!-- TODO: Add report -->
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
||||||
|
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
||||||
|
{{ t('Clone') }}
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit text-grey-8">
|
||||||
|
<RouteFilter data-key="RouteList" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
@on-fetch="(data) => (workers = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
|
||||||
|
<FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
|
||||||
|
<QPage class="column items-center">
|
||||||
|
<QToolbar class="bg-vn-dark justify-end">
|
||||||
|
<div id="st-actions" class="q-pa-sm">
|
||||||
|
<QBtn
|
||||||
|
icon="vn:clone"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="confirmationDialog = true"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
|
||||||
|
<QBtn
|
||||||
|
icon="check"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="markAsServed"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Mark as served') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</QToolbar>
|
||||||
|
<div class="route-list">
|
||||||
|
<VnPaginate
|
||||||
|
:key="refreshKey"
|
||||||
|
data-key="RouteList"
|
||||||
|
url="Routes/filter"
|
||||||
|
:order="['created DESC', 'id DESC']"
|
||||||
|
:limit="20"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<QTable
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
flat
|
||||||
|
row-key="id"
|
||||||
|
selection="multiple"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
|
<template #body-cell-worker="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.workerUserName }}
|
||||||
|
<QPopupEdit
|
||||||
|
:model-value="props.row.workerFk"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="
|
||||||
|
(worker) => updateWorker(props.row, worker)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Worker')"
|
||||||
|
v-model="scope.value"
|
||||||
|
:options="workers"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:emit-value="false"
|
||||||
|
:rules="validate('Route.workerFk')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
>
|
||||||
|
<template #option="{ opt, itemProps }">
|
||||||
|
<QItem
|
||||||
|
v-bind="itemProps"
|
||||||
|
class="q-pa-xs row items-center"
|
||||||
|
>
|
||||||
|
<QItemSection
|
||||||
|
class="col-9 justify-center"
|
||||||
|
>
|
||||||
|
<span>{{ opt.name }}</span>
|
||||||
|
<span class="text-grey">{{
|
||||||
|
opt.nickname
|
||||||
|
}}</span>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-agency="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.agencyName }}
|
||||||
|
<QPopupEdit
|
||||||
|
:model-value="props.row.agencyModeFk"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="
|
||||||
|
(agency) => updateAgency(props.row, agency)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="scope.value"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:emit-value="false"
|
||||||
|
:rules="validate('route.agencyFk')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-vehicle="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.vehiclePlateNumber }}
|
||||||
|
<QPopupEdit
|
||||||
|
:model-value="props.row.vehicleFk"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="
|
||||||
|
(vehicle) => updateVehicle(props.row, vehicle)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Vehicle')"
|
||||||
|
v-model="scope.value"
|
||||||
|
:options="vehicleList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="numberPlate"
|
||||||
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:emit-value="false"
|
||||||
|
:rules="validate('route.vehicleFk')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-date="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ toDate(props.row?.created) }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.created"
|
||||||
|
v-slot="scope"
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
buttons
|
||||||
|
>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Date')"
|
||||||
|
:rules="validate('route.created')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-description="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.description }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.description"
|
||||||
|
v-slot="scope"
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
buttons
|
||||||
|
>
|
||||||
|
<VnInput
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Description')"
|
||||||
|
:rules="validate('route.description')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-started="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ toHour(props.row.started) }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.started"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
>
|
||||||
|
<VnInputTime
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Hour started')"
|
||||||
|
:rules="validate('route.started')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-finished="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ toHour(props.row.finished) }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.finished"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
>
|
||||||
|
<VnInputTime
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Hour finished')"
|
||||||
|
:rules="validate('route.finished')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-actions="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<div class="table-actions">
|
||||||
|
<QIcon
|
||||||
|
name="vn:ticketAdd"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Add ticket') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon
|
||||||
|
name="preview"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
@click="previewRoute(props?.row?.id)"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Preview') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<RouterLink :to="{ name: 'RouteCreate' }">
|
||||||
|
<QBtn fab icon="add" color="primary" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('newRoute') }}
|
||||||
|
</QTooltip>
|
||||||
|
</RouterLink>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.route-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
newRoute: New Route
|
||||||
|
es:
|
||||||
|
ID: ID
|
||||||
|
Worker: Trabajador
|
||||||
|
Agency: Agencia
|
||||||
|
Vehicle: Vehículo
|
||||||
|
Date: Fecha
|
||||||
|
Description: Descripción
|
||||||
|
Hour started: Hora inicio
|
||||||
|
Hour finished: Hora fin
|
||||||
|
newRoute: Nueva Ruta
|
||||||
|
Clone Selected Routes: Clonar rutas seleccionadas
|
||||||
|
Select the starting date: Seleccione la fecha de inicio
|
||||||
|
Stating date: Fecha de inicio
|
||||||
|
Cancel: Cancelar
|
||||||
|
Clone: Clonar
|
||||||
|
Mark as served: Marcar como servidas
|
||||||
|
</i18n>
|
|
@ -1 +1,177 @@
|
||||||
<template>Supplier accounts</template>
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const supplier = ref(null);
|
||||||
|
const supplierAccountRef = ref(null);
|
||||||
|
const wireTransferFk = ref(null);
|
||||||
|
const bankEntitiesOptions = ref([]);
|
||||||
|
|
||||||
|
const onBankEntityCreated = (data) => {
|
||||||
|
bankEntitiesOptions.value.push(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangesSaved = () => {
|
||||||
|
if (supplier.value.payMethodFk !== wireTransferFk.value)
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
message: t('Do you want to change the pay method to wire transfer?'),
|
||||||
|
ok: {
|
||||||
|
push: true,
|
||||||
|
color: 'primary',
|
||||||
|
},
|
||||||
|
cancel: true,
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
await setWireTransfer();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setWireTransfer = async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
id: route.params.id,
|
||||||
|
payMethodFk: wireTransferFk.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.patch(`Suppliers/${route.params.id}`, params);
|
||||||
|
notify('globals.dataSaved', 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error setting wire transfer', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (supplierAccountRef.value) supplierAccountRef.value.reload();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="BankEntities"
|
||||||
|
@on-fetch="(data) => (bankEntitiesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="payMethods/findOne"
|
||||||
|
@on-fetch="(data) => (wireTransferFk = data.id)"
|
||||||
|
:filter="{ where: { code: 'wireTransfer' } }"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
:url="`Suppliers/${route.params.id}`"
|
||||||
|
@on-fetch="(data) => (supplier = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<CrudModel
|
||||||
|
data-key="SupplierAccount"
|
||||||
|
url="SupplierAccounts"
|
||||||
|
model="SupplierAccounts"
|
||||||
|
:filter="{
|
||||||
|
fields: ['id', 'supplierFk', 'iban', 'bankEntityFk', 'beneficiary'],
|
||||||
|
where: { supplierFk: route.params.id },
|
||||||
|
}"
|
||||||
|
ref="supplierAccountRef"
|
||||||
|
:default-remove="false"
|
||||||
|
:data-required="{ supplierFk: route.params.id }"
|
||||||
|
@save-changes="onChangesSaved()"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard class="q-pa-md">
|
||||||
|
<VnRow
|
||||||
|
v-for="(row, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
class="row q-gutter-md q-mb-md"
|
||||||
|
>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('supplier.accounts.iban')"
|
||||||
|
v-model="row.iban"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('worker.create.bankEntity')"
|
||||||
|
v-model="row.bankEntityFk"
|
||||||
|
:options="bankEntitiesOptions"
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
hide-selected
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CreateBankEntityForm
|
||||||
|
@on-data-saved="onBankEntityCreated($event)"
|
||||||
|
:show-entity-field="false"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel
|
||||||
|
>{{ scope.opt.bic }}
|
||||||
|
{{ scope.opt.name }}</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('supplier.accounts.beneficiary')"
|
||||||
|
v-model="row.beneficiary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 row justify-center items-center">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="supplierAccountRef.remove([row])"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Remove account') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<QIcon
|
||||||
|
name="add"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="supplierAccountRef.insert()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add account') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Do you want to change the pay method to wire transfer?: ¿Quieres modificar la forma de pago a transferencia?
|
||||||
|
Add account: Añadir cuenta
|
||||||
|
Remove account: Remover cuenta
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1 +1,97 @@
|
||||||
<template>Supplier addresses</template>
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const addressesFilter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'nickname',
|
||||||
|
'street',
|
||||||
|
'city',
|
||||||
|
'provinceFk',
|
||||||
|
'phone',
|
||||||
|
'mobile',
|
||||||
|
'postalCode',
|
||||||
|
],
|
||||||
|
order: ['nickname ASC'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'province',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToCreateView = () => {
|
||||||
|
router.push({ name: 'SupplierAddressesCreate' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToUpdateView = (addressData) => {
|
||||||
|
const stringifiedAddressData = JSON.stringify(addressData);
|
||||||
|
router.push({
|
||||||
|
name: 'SupplierAddressesCreate',
|
||||||
|
query: { addressData: stringifiedAddressData },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="card-list">
|
||||||
|
<VnPaginate
|
||||||
|
data-key="SupplierAddress"
|
||||||
|
:url="`Suppliers/${route.params.id}/addresses`"
|
||||||
|
:filter="addressesFilter"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<CardList
|
||||||
|
v-for="row of rows"
|
||||||
|
:key="row.id"
|
||||||
|
:title="row.nickname"
|
||||||
|
:id="row.id"
|
||||||
|
@click="redirectToUpdateView(row)"
|
||||||
|
>
|
||||||
|
<template #list-items>
|
||||||
|
<VnLv
|
||||||
|
:label="t('supplier.addresses.street')"
|
||||||
|
:value="row.street"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('supplier.addresses.postcode')"
|
||||||
|
:value="`${row.postalCode} - ${row.city}, ${row.province.name}`"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('supplier.addresses.phone')"
|
||||||
|
:value="`${row.phone}, ${row.mobile}`"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</CardList>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('supplier.list.newSupplier') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card-list {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 60em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { reactive, ref, onMounted, onBeforeMount } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const provincesOptions = ref([]);
|
||||||
|
const postcodesOptions = ref([]);
|
||||||
|
const townsLocationOptions = ref([]);
|
||||||
|
const viewAction = ref();
|
||||||
|
const updateAddressId = ref(null);
|
||||||
|
const newAddressForm = reactive({
|
||||||
|
nickname: null,
|
||||||
|
street: null,
|
||||||
|
postalCode: null,
|
||||||
|
city: null,
|
||||||
|
provinceFk: null,
|
||||||
|
phone: null,
|
||||||
|
mobile: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDataSaved = () => {
|
||||||
|
router.push({ name: 'SupplierAddresses' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAddressForm = (addressData) => {
|
||||||
|
for (let key in newAddressForm) {
|
||||||
|
if (key in addressData) {
|
||||||
|
newAddressForm[key] = addressData[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
viewAction.value = route.query.addressData ? 'update' : 'create';
|
||||||
|
|
||||||
|
if (viewAction.value === 'create') newAddressForm.supplierFk = route.params.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (viewAction.value === 'update' && route.query.addressData) {
|
||||||
|
const addressData = JSON.parse(route.query.addressData);
|
||||||
|
updateAddressId.value = addressData.id;
|
||||||
|
updateAddressForm(addressData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="postcodeFetchDataRef"
|
||||||
|
url="Postcodes/location"
|
||||||
|
@on-fetch="(data) => (postcodesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="provincesFetchDataRef"
|
||||||
|
@on-fetch="(data) => (provincesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="townsFetchDataRef"
|
||||||
|
@on-fetch="(data) => (townsLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Towns/location"
|
||||||
|
/>
|
||||||
|
<QPage>
|
||||||
|
<FormModel
|
||||||
|
model="supplierAddresses"
|
||||||
|
:form-initial-data="newAddressForm"
|
||||||
|
:url-update="
|
||||||
|
viewAction !== 'create' ? `SupplierAddresses/${updateAddressId}` : null
|
||||||
|
"
|
||||||
|
:url-create="viewAction === 'create' ? 'SupplierAddresses' : null"
|
||||||
|
:observe-form-changes="viewAction === 'create'"
|
||||||
|
@on-data-saved="onDataSaved()"
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.nickname"
|
||||||
|
:label="t('supplier.addresses.name')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.street"
|
||||||
|
:label="t('supplier.addresses.street')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
v-model="data.postalCode"
|
||||||
|
:label="t('supplier.addresses.postcode')"
|
||||||
|
:rules="validate('supplierAddress.postcode')"
|
||||||
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
|
:options="postcodesOptions"
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
hide-selected
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomerCreateNewPostcode
|
||||||
|
@on-data-saved="onPostcodeCreated($event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||||
|
<QItemLabel caption
|
||||||
|
>{{ scope.opt.code }} -
|
||||||
|
{{ scope.opt.town.name }} ({{
|
||||||
|
scope.opt.town.province.name
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
scope.opt.town.province.country.country
|
||||||
|
}})</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.addresses.city')"
|
||||||
|
:options="townsLocationOptions"
|
||||||
|
v-model="data.city"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.addresses.province')"
|
||||||
|
:options="provincesOptions"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.provinceFk"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.phone"
|
||||||
|
:label="t('supplier.addresses.phone')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.mobile"
|
||||||
|
:label="t('supplier.addresses.mobile')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col" />
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
|
@ -1 +1,139 @@
|
||||||
<template>Supplier agency term</template>
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const supplierAgencyTermRef = ref(null);
|
||||||
|
const agenciesOptions = ref(null);
|
||||||
|
|
||||||
|
const supplierAgencyFilter = {
|
||||||
|
include: {
|
||||||
|
relation: 'agency',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
where: { supplierFk: route.params.id },
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToCreateView = () => {
|
||||||
|
router.push({ name: 'SupplierAgencyTermCreate' });
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (supplierAgencyTermRef.value) supplierAgencyTermRef.value.reload();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers/freeAgencies"
|
||||||
|
@on-fetch="(data) => (agenciesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<CrudModel
|
||||||
|
ref="supplierAgencyTermRef"
|
||||||
|
data-key="SupplierAgencyTerm"
|
||||||
|
save-url="SupplierAgencyTerms/crud"
|
||||||
|
url="SupplierAgencyTerms"
|
||||||
|
model="SupplierAgencyTerm"
|
||||||
|
primary-key="agencyFk"
|
||||||
|
:filter="supplierAgencyFilter"
|
||||||
|
:default-remove="false"
|
||||||
|
:data-required="{
|
||||||
|
supplierFk: route.params.id,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard class="q-pa-md">
|
||||||
|
<VnRow
|
||||||
|
v-for="(row, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
class="row q-gutter-md q-mb-md"
|
||||||
|
>
|
||||||
|
<div class="col">
|
||||||
|
<QField :label="t('supplier.agencyTerms.agencyFk')" stack-label>
|
||||||
|
<template #control>
|
||||||
|
<div tabindex="0">
|
||||||
|
{{ row.agency?.name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QField>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.minimumM3')"
|
||||||
|
v-model.number="row.minimumM3"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.packagePrice')"
|
||||||
|
v-model.number="row.packagePrice"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.kmPrice')"
|
||||||
|
v-model.number="row.kmPrice"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.m3Price')"
|
||||||
|
v-model.number="row.m3Price"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.routePrice')"
|
||||||
|
v-model.number="row.routePrice"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.minimumKm')"
|
||||||
|
v-model.number="row.minimumKm"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 row justify-center items-center">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="supplierAgencyTermRef.remove([row])"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Remove row') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('supplier.agencyTerms.addRow') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Remove row: Eliminar fila
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const agenciesOptions = ref(null);
|
||||||
|
const newAgencyTermForm = reactive({
|
||||||
|
agencyFk: null,
|
||||||
|
minimumM3: null,
|
||||||
|
packagePrice: null,
|
||||||
|
kmPrice: null,
|
||||||
|
m3Price: null,
|
||||||
|
routePrice: null,
|
||||||
|
minimumKm: null,
|
||||||
|
supplierFk: route.params.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDataSaved = () => {
|
||||||
|
router.push({ name: 'SupplierAgencyTerm' });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers/freeAgencies"
|
||||||
|
@on-fetch="(data) => (agenciesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
|
||||||
|
<QPage>
|
||||||
|
<FormModel
|
||||||
|
model="supplierAgencyTerm"
|
||||||
|
:form-initial-data="newAgencyTermForm"
|
||||||
|
url-create="SupplierAgencyTerms"
|
||||||
|
:observe-form-changes="true"
|
||||||
|
@on-data-saved="onDataSaved()"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.agencyTerms.agencyFk')"
|
||||||
|
v-model="data.agencyFk"
|
||||||
|
:options="agenciesOptions"
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
hide-selected
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.minimumM3')"
|
||||||
|
v-model.number="data.minimumM3"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.packagePrice')"
|
||||||
|
v-model.number="data.packagePrice"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.kmPrice')"
|
||||||
|
v-model.number="data.kmPrice"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.m3Price')"
|
||||||
|
v-model.number="data.m3Price"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.routePrice')"
|
||||||
|
v-model.number="data.routePrice"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.agencyTerms.minimumKm')"
|
||||||
|
v-model.number="data.minimumKm"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col" />
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
|
@ -1 +1,97 @@
|
||||||
<template>Supplier basic data</template>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const workersOptions = ref([]);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
|
||||||
|
@on-fetch="(data) => (workersOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url="`Suppliers/${route.params.id}`"
|
||||||
|
:url-update="`Suppliers/${route.params.id}`"
|
||||||
|
model="supplier"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.nickname"
|
||||||
|
:label="t('supplier.basicData.alias')"
|
||||||
|
:rules="validate('supplier.nickname')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.basicData.workerFk')"
|
||||||
|
v-model="data.workerFk"
|
||||||
|
:options="workersOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
:rules="validate('supplier.workerFk')"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isSerious"
|
||||||
|
:label="t('supplier.basicData.isSerious')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isActive"
|
||||||
|
:label="t('supplier.basicData.isActive')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isPayMethodChecked"
|
||||||
|
:label="t('supplier.basicData.isPayMethodChecked')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.basicData.note')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="data.note"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1 +1,68 @@
|
||||||
<template>Supplier billing data</template>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const paymethodsOptions = ref([]);
|
||||||
|
const payDemsOptions = ref([]);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Paymethods"
|
||||||
|
@on-fetch="(data) => (paymethodsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData url="PayDems" @on-fetch="(data) => (payDemsOptions = data)" auto-load />
|
||||||
|
<FormModel
|
||||||
|
:url="`Suppliers/${route.params.id}`"
|
||||||
|
:url-update="`Suppliers/${route.params.id}`"
|
||||||
|
model="supplier"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.billingData.payMethodFk')"
|
||||||
|
v-model="data.payMethodFk"
|
||||||
|
:options="paymethodsOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
:rules="validate('supplier.payMethodFk')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.billingData.payDemFk')"
|
||||||
|
v-model="data.payDemFk"
|
||||||
|
:options="payDemsOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="payDem"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
:rules="validate('supplier.payDemFk')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.billingData.payDay')"
|
||||||
|
type="number"
|
||||||
|
v-model="data.payDay"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
|
@ -29,7 +29,6 @@ const { t } = useI18n();
|
||||||
<QPageContainer>
|
<QPageContainer>
|
||||||
<QPage>
|
<QPage>
|
||||||
<VnSubToolbar />
|
<VnSubToolbar />
|
||||||
|
|
||||||
<div class="q-pa-md"><RouterView></RouterView></div>
|
<div class="q-pa-md"><RouterView></RouterView></div>
|
||||||
</QPage>
|
</QPage>
|
||||||
</QPageContainer>
|
</QPageContainer>
|
||||||
|
@ -63,3 +62,8 @@ const { t } = useI18n();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search suppliers: Buscar proveedores
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1 +1,192 @@
|
||||||
<template>Supplier consumption</template>
|
<script setup>
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||||
|
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||||
|
|
||||||
|
import { toDate, toDateString } from 'src/filters';
|
||||||
|
import { dashIfEmpty } from 'src/filters';
|
||||||
|
import { usePrintService } from 'composables/usePrintService';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const { openReport, sendEmail } = usePrintService();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const suppliersConsumption = ref();
|
||||||
|
|
||||||
|
const userParams = computed(() => {
|
||||||
|
const minDate = Date.vnNew();
|
||||||
|
minDate.setHours(0, 0, 0, 0);
|
||||||
|
minDate.setMonth(minDate.getMonth() - 2);
|
||||||
|
|
||||||
|
const maxDate = Date.vnNew();
|
||||||
|
maxDate.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: toDateString(minDate),
|
||||||
|
to: toDateString(maxDate),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const reportParams = computed(() => ({
|
||||||
|
recipientId: Number(route.params.id),
|
||||||
|
...userParams.value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const rows = computed(() => suppliersConsumption.value || []);
|
||||||
|
|
||||||
|
const openReportPdf = () => {
|
||||||
|
openReport(`Suppliers/${route.params.id}/campaign-metrics-pdf`, reportParams.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSupplierContacts = async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
where: {
|
||||||
|
supplierFk: route.params.id,
|
||||||
|
email: { neq: null },
|
||||||
|
},
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await axios.get('SupplierContacts', params);
|
||||||
|
|
||||||
|
if (data && data.length) return data[0].email;
|
||||||
|
} catch (err) {
|
||||||
|
notify('This supplier does not have a contact with an email address', 'negative');
|
||||||
|
console.error('Error fetching supplierContacts: ', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSendEmailDialog = async () => {
|
||||||
|
const email = await getSupplierContacts();
|
||||||
|
quasar.dialog({
|
||||||
|
component: SendEmailDialog,
|
||||||
|
componentProps: {
|
||||||
|
data: {
|
||||||
|
address: email,
|
||||||
|
},
|
||||||
|
promise: sendCampaignMetricsEmail,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendCampaignMetricsEmail = ({ address }) => {
|
||||||
|
sendEmail(`Suppliers/${route.params.id}/campaign-metrics-email`, {
|
||||||
|
recipient: address,
|
||||||
|
...reportParams.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateTotal = (buysArray) => {
|
||||||
|
return buysArray.reduce((accumulator, { total }) => accumulator + total, 0);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers/consumption"
|
||||||
|
@on-fetch="(data) => (suppliersConsumption = data)"
|
||||||
|
:filter="{
|
||||||
|
where: { supplierFk: route.params.id },
|
||||||
|
order: ['itemTypeFk', 'itemName', 'itemSize'],
|
||||||
|
}"
|
||||||
|
:params="userParams"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<QToolbar class="bg-vn-dark justify-end">
|
||||||
|
<div id="st-data">
|
||||||
|
<QBtn
|
||||||
|
v-if="userParams.from && userParams.to"
|
||||||
|
color="primary"
|
||||||
|
icon-right="picture_as_pdf"
|
||||||
|
no-caps
|
||||||
|
class="q-mr-md"
|
||||||
|
@click="openReportPdf()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Open as PDF') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
v-if="userParams.from && userParams.to"
|
||||||
|
color="primary"
|
||||||
|
icon-right="email"
|
||||||
|
no-caps
|
||||||
|
@click="openSendEmailDialog()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Send to email') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
<QSpace />
|
||||||
|
<div id="st-actions"></div>
|
||||||
|
</QToolbar>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<QTable
|
||||||
|
:rows="rows"
|
||||||
|
hide-bottom
|
||||||
|
row-key="id"
|
||||||
|
hide-header
|
||||||
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
>
|
||||||
|
<template #body="{ row }">
|
||||||
|
<QTr>
|
||||||
|
<QTd no-hover class="label">{{
|
||||||
|
t('supplier.consumption.entry')
|
||||||
|
}}</QTd>
|
||||||
|
<QTd no-hover>{{ row.id }}</QTd>
|
||||||
|
<QTd no-hover class="label">{{ t('supplier.consumption.date') }}</QTd>
|
||||||
|
<QTd no-hover>{{ toDate(row.shipped) }}</QTd>
|
||||||
|
<QTd no-hover class="label">{{
|
||||||
|
t('supplier.consumption.reference')
|
||||||
|
}}</QTd>
|
||||||
|
<QTd no-hover>{{ row.invoiceNumber }}</QTd>
|
||||||
|
</QTr>
|
||||||
|
<QTr v-for="(buy, index) in row.buys" :key="index">
|
||||||
|
<QTd no-hover> {{ buy.itemName }}</QTd>
|
||||||
|
|
||||||
|
<QTd no-hover>
|
||||||
|
<span>{{ buy.subName }}</span>
|
||||||
|
<fetched-tags :item="buy" :max-length="5" />
|
||||||
|
</QTd>
|
||||||
|
<QTd no-hover> {{ dashIfEmpty(buy.quantity) }}</QTd>
|
||||||
|
<QTd no-hover> {{ dashIfEmpty(buy.price) }}</QTd>
|
||||||
|
<QTd colspan="2" no-hover> {{ dashIfEmpty(buy.total) }}</QTd>
|
||||||
|
</QTr>
|
||||||
|
<QTr>
|
||||||
|
<QTd colspan="6" no-hover>
|
||||||
|
<span class="label">{{ t('Total entry') }}: </span>
|
||||||
|
<span>{{ calculateTotal(row.buys) }}</span>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.label {
|
||||||
|
color: var(--vn-label);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Total entry: Total entrada
|
||||||
|
Open as PDF: Abrir como PDF
|
||||||
|
Send to email: Enviar por email
|
||||||
|
This supplier does not have a contact with an email address: Este proveedor no tiene un email de contacto
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1 +1,118 @@
|
||||||
<template>Supplier contacts</template>
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const supplierContactRef = ref(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (supplierContactRef.value) supplierContactRef.value.reload();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CrudModel
|
||||||
|
data-key="SupplierContact"
|
||||||
|
url="SupplierContacts"
|
||||||
|
model="SupplierContact"
|
||||||
|
:filter="{
|
||||||
|
where: { supplierFk: route.params.id },
|
||||||
|
}"
|
||||||
|
ref="supplierContactRef"
|
||||||
|
:default-remove="false"
|
||||||
|
:data-required="{ supplierFk: route.params.id }"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard class="q-pa-md">
|
||||||
|
<QCardSection
|
||||||
|
v-for="(row, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
class="border q-pa-md q-mb-md"
|
||||||
|
>
|
||||||
|
<VnRow class="row q-gutter-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('supplier.contacts.name')"
|
||||||
|
v-model="row.name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('supplier.contacts.phone')"
|
||||||
|
v-model="row.phone"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('supplier.contacts.mobile')"
|
||||||
|
v-model="row.mobile"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
:label="t('supplier.contacts.email')"
|
||||||
|
v-model="row.email"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
:label="t('supplier.contacts.observation')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="row.observation"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 row justify-center items-center">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="supplierContactRef.remove([row])"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Remove contact') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</QCardSection>
|
||||||
|
<VnRow>
|
||||||
|
<QIcon
|
||||||
|
name="add"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="supplierContactRef.insert()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add contact') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.border {
|
||||||
|
border-radius: 0px !important;
|
||||||
|
border: 1px solid var(--vn-text) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Add contact: Añadir contacto
|
||||||
|
Remove contact: Remover contacto
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1 +1,298 @@
|
||||||
<template>Supplier fiscal data</template>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const postcodeFetchDataRef = ref(null);
|
||||||
|
const townsFetchDataRef = ref(null);
|
||||||
|
const sageTaxTypesOptions = ref([]);
|
||||||
|
const sageWithholdingsOptions = ref([]);
|
||||||
|
const sageTransactionTypesOptions = ref([]);
|
||||||
|
const supplierActivitiesOptions = ref([]);
|
||||||
|
const postcodesOptions = ref([]);
|
||||||
|
const provincesLocationOptions = ref([]);
|
||||||
|
const townsLocationOptions = ref([]);
|
||||||
|
const countriesOptions = ref([]);
|
||||||
|
|
||||||
|
const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formData) => {
|
||||||
|
await postcodeFetchDataRef.value.fetch();
|
||||||
|
await townsFetchDataRef.value.fetch();
|
||||||
|
formData.postCode = code;
|
||||||
|
formData.provinceFk = provinceFk;
|
||||||
|
formData.city = townsLocationOptions.value.find((town) => town.id === townFk).name;
|
||||||
|
formData.countryFk = countryFk;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="SageTaxTypes"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (sageTaxTypesOptions = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="SageWithholdings"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (sageWithholdingsOptions = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="sageTransactionTypes"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (sageTransactionTypesOptions = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="SupplierActivities"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (supplierActivitiesOptions = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="postcodeFetchDataRef"
|
||||||
|
url="Postcodes/location"
|
||||||
|
@on-fetch="(data) => (postcodesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="townsFetchDataRef"
|
||||||
|
@on-fetch="(data) => (townsLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Towns/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (provincesLocationOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Provinces/location"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (countriesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
url="Countries"
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url="`Suppliers/${route.params.id}`"
|
||||||
|
:url-update="`Suppliers/${route.params.id}/updateFiscalData`"
|
||||||
|
model="supplier"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.name"
|
||||||
|
:label="t('supplier.fiscalData.name')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.nif"
|
||||||
|
:label="t('supplier.fiscalData.nif')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.account"
|
||||||
|
:label="t('supplier.fiscalData.account')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.sageTaxTypeFk')"
|
||||||
|
v-model="data.sageTaxTypeFk"
|
||||||
|
:options="sageTaxTypesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="vat"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.sageWithholdingFk')"
|
||||||
|
v-model="data.sageWithholdingFk"
|
||||||
|
:options="sageWithholdingsOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="withholding"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.sageTransactionTypeFk')"
|
||||||
|
v-model="data.sageTransactionTypeFk"
|
||||||
|
:options="sageTransactionTypesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="transaction"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.supplierActivityFk')"
|
||||||
|
v-model="data.supplierActivityFk"
|
||||||
|
:options="supplierActivitiesOptions"
|
||||||
|
option-value="code"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.healthRegister"
|
||||||
|
:label="t('supplier.fiscalData.healthRegister')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.street"
|
||||||
|
:label="t('supplier.fiscalData.street')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectCreate
|
||||||
|
:label="t('supplier.fiscalData.postcode')"
|
||||||
|
v-model="data.postCode"
|
||||||
|
:options="postcodesOptions"
|
||||||
|
:rules="validate('supplier.postCode')"
|
||||||
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
hide-selected
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<CustomerCreateNewPostcode
|
||||||
|
@on-data-saved="onPostcodeCreated($event, data)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection v-if="scope.opt">
|
||||||
|
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
||||||
|
<QItemLabel caption
|
||||||
|
>{{ scope.opt.code }} -
|
||||||
|
{{ scope.opt.town.name }} ({{
|
||||||
|
scope.opt.town.province.name
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
scope.opt.town.province.country.country
|
||||||
|
}})</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectCreate>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.city')"
|
||||||
|
:options="townsLocationOptions"
|
||||||
|
v-model="data.city"
|
||||||
|
option-value="name"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
:rules="validate('supplier.city')"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption
|
||||||
|
>{{ scope.opt.name }},
|
||||||
|
{{ scope.opt.province.name }} ({{
|
||||||
|
scope.opt.province.country.country
|
||||||
|
}})</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.provinceFk')"
|
||||||
|
:options="provincesLocationOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.provinceFk"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{
|
||||||
|
`${scope.opt.name} (${scope.opt.country.country})`
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('supplier.fiscalData.country')"
|
||||||
|
:options="countriesOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="country"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.countryFk"
|
||||||
|
:rules="validate('postcode.countryFk')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col flex justify-around">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isTrucker"
|
||||||
|
:label="t('supplier.fiscalData.isTrucker')"
|
||||||
|
/>
|
||||||
|
<div class="row items-center">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.isVies"
|
||||||
|
:label="t('supplier.fiscalData.isVies')"
|
||||||
|
/>
|
||||||
|
<QIcon name="info" size="xs" class="cursor-pointer q-ml-sm">
|
||||||
|
<QTooltip>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
'When activating it, do not enter the country code in the ID field.'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
When activating it, do not enter the country code in the ID field.: Al activarlo, no informar el código del país en el campo nif
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
<template>Supplier log</template>
|
<script setup>
|
||||||
|
import VnLog from 'src/components/common/VnLog.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnLog model="Supplier" url="/SupplierLogs"></VnLog>
|
||||||
|
</template>
|
||||||
|
|
|
@ -182,4 +182,3 @@ const isAdministrative = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
</CardSummary>
|
</CardSummary>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { QCheckbox, QIcon } from 'quasar';
|
import { QCheckbox, QIcon } from 'quasar';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
|
||||||
|
|
||||||
import travelService from 'src/services/travel.service';
|
import travelService from 'src/services/travel.service';
|
||||||
import { toDate, toCurrency } from 'src/filters';
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
|
@ -218,21 +217,12 @@ const openEntryDescriptor = () => {};
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<QCard class="vn-one row justify-around" style="min-width: 100%">
|
<QCard class="vn-one">
|
||||||
<VnRow>
|
<VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" />
|
||||||
<div class="col">
|
|
||||||
<VnLv
|
|
||||||
:label="t('globals.shipped')"
|
|
||||||
:value="toDate(travel.shipped)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('globals.wareHouseOut')"
|
:label="t('globals.wareHouseOut')"
|
||||||
:value="travel.warehouseOut?.name"
|
:value="travel.warehouseOut?.name"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv :label="t('travel.summary.delivered')" class="q-mb-xs">
|
<VnLv :label="t('travel.summary.delivered')" class="q-mb-xs">
|
||||||
<template #value>
|
<template #value>
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
|
@ -243,22 +233,13 @@ const openEntryDescriptor = () => {};
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
</div>
|
</QCard>
|
||||||
</VnRow>
|
<QCard class="vn-one">
|
||||||
<VnRow>
|
<VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" />
|
||||||
<div class="col">
|
|
||||||
<VnLv
|
|
||||||
:label="t('globals.landed')"
|
|
||||||
:value="toDate(travel.landed)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('globals.wareHouseIn')"
|
:label="t('globals.wareHouseIn')"
|
||||||
:value="travel.warehouseIn?.name"
|
:value="travel.warehouseIn?.name"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv :label="t('travel.summary.received')" class="q-mb-xs">
|
<VnLv :label="t('travel.summary.received')" class="q-mb-xs">
|
||||||
<template #value>
|
<template #value>
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
|
@ -269,24 +250,14 @@ const openEntryDescriptor = () => {};
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
</div>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" />
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv label="m³" :value="travel.m3" />
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnLv :label="t('globals.totalEntries')" :value="travel.m3" />
|
|
||||||
</div>
|
|
||||||
</VnRow>
|
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-two" v-if="entriesTableRows.length > 0">
|
<QCard class="vn-one">
|
||||||
|
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" />
|
||||||
|
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
||||||
|
<VnLv label="m³" :value="travel.m3" />
|
||||||
|
<VnLv :label="t('globals.totalEntries')" :value="travel.m3" />
|
||||||
|
</QCard>
|
||||||
|
<QCard class="full-width" v-if="entriesTableRows.length > 0">
|
||||||
<a class="header" :href="travelUrl + 'entry'">
|
<a class="header" :href="travelUrl + 'entry'">
|
||||||
{{ t('travel.summary.entries') }}
|
{{ t('travel.summary.entries') }}
|
||||||
<QIcon name="open_in_new" color="primary" />
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
|
|
@ -9,6 +9,7 @@ import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.v
|
||||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
import ExtraCommunityFilter from './ExtraCommunityFilter.vue';
|
import ExtraCommunityFilter from './ExtraCommunityFilter.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import EntryDescriptorProxy from '../Entry/Card/EntryDescriptorProxy.vue';
|
||||||
|
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { toCurrency } from 'src/filters';
|
import { toCurrency } from 'src/filters';
|
||||||
|
@ -231,7 +232,7 @@ const navigateToTravelId = (id) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopEventPropagation = (event, col) => {
|
const stopEventPropagation = (event, col) => {
|
||||||
if (!['ref', 'id', 'supplier'].includes(col.name)) return;
|
if (!['ref', 'id', 'cargoSupplierNickname'].includes(col.name)) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
};
|
};
|
||||||
|
@ -262,7 +263,16 @@ onMounted(async () => {
|
||||||
<div id="st-data"></div>
|
<div id="st-data"></div>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<div id="st-actions">
|
<div id="st-actions">
|
||||||
<QBtn color="primary" icon-right="archive" no-caps @click="openReportPdf()" />
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
icon-right="picture_as_pdf"
|
||||||
|
no-caps
|
||||||
|
@click="openReportPdf()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Open as PDF') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
</div>
|
</div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
@ -350,10 +360,9 @@ onMounted(async () => {
|
||||||
:props="props"
|
:props="props"
|
||||||
class="secondary-row"
|
class="secondary-row"
|
||||||
>
|
>
|
||||||
<QTd
|
<QTd>
|
||||||
><QBtn flat color="blue" class="col-content">{{ entry.id }}</QBtn>
|
<QBtn flat color="blue" class="col-content">{{ entry.id }} </QBtn>
|
||||||
<!-- Cuando se cree el modulo relacionado a entries, crear su descriptor y colocarlo acá -->
|
<EntryDescriptorProxy :id="entry.id" />
|
||||||
<!-- <EntryDescriptorProxy :id="entry.id"/> -->
|
|
||||||
</QTd>
|
</QTd>
|
||||||
<QTd
|
<QTd
|
||||||
><QBtn flat color="blue" class="col-content">{{
|
><QBtn flat color="blue" class="col-content">{{
|
||||||
|
@ -415,4 +424,5 @@ es:
|
||||||
physicKg: KG físico
|
physicKg: KG físico
|
||||||
shipped: F. envío
|
shipped: F. envío
|
||||||
landed: F. llegada
|
landed: F. llegada
|
||||||
|
Open as PDF: Abrir como PDF
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -101,7 +101,7 @@ const setData = (entity) => {
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" />
|
<VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" />
|
||||||
<VnLv :label="t('worker.card.email')" :value="entity.user?.email"> </VnLv>
|
<VnLv :label="t('worker.card.email')" :value="entity.user?.email" copy />
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('worker.list.department')"
|
:label="t('worker.list.department')"
|
||||||
:value="entity.department ? entity.department.department.name : null"
|
:value="entity.department ? entity.department.department.name : null"
|
||||||
|
|
|
@ -80,7 +80,7 @@ const filter = {
|
||||||
:label="t('worker.list.department')"
|
:label="t('worker.list.department')"
|
||||||
:value="worker.department.department.name"
|
:value="worker.department.department.name"
|
||||||
/>
|
/>
|
||||||
<VnLv :label="t('worker.list.email')" :value="worker.user.email" />
|
<VnLv :label="t('worker.list.email')" :value="worker.user.email" copy />
|
||||||
<VnLv :label="t('worker.summary.boss')" link>
|
<VnLv :label="t('worker.summary.boss')" link>
|
||||||
<template #value>
|
<template #value>
|
||||||
<VnUserLink
|
<VnUserLink
|
||||||
|
|
|
@ -9,7 +9,8 @@ import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
|
||||||
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
|
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
|
||||||
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
import VnLocation from 'src/components/common/VnLocation.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
|
||||||
|
@ -21,14 +22,6 @@ const workerConfigFilter = {
|
||||||
field: ['payMethodFk'],
|
field: ['payMethodFk'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const provincesFilter = {
|
|
||||||
fields: ['id', 'name', 'countryFk'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const townsFilter = {
|
|
||||||
fields: ['id', 'name', 'provinceFk'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const newWorkerForm = ref({
|
const newWorkerForm = ref({
|
||||||
companyFk: null,
|
companyFk: null,
|
||||||
payMethodFk: null,
|
payMethodFk: null,
|
||||||
|
@ -49,9 +42,6 @@ const newWorkerForm = ref({
|
||||||
bankEntityFk: null,
|
bankEntityFk: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const postcodeFetchDataRef = ref(null);
|
|
||||||
const provincesOptions = ref([]);
|
|
||||||
const townsOptions = ref([]);
|
|
||||||
const companiesOptions = ref([]);
|
const companiesOptions = ref([]);
|
||||||
const workersOptions = ref([]);
|
const workersOptions = ref([]);
|
||||||
const payMethodsOptions = ref([]);
|
const payMethodsOptions = ref([]);
|
||||||
|
@ -66,9 +56,14 @@ const onBankEntityCreated = (data) => {
|
||||||
bankEntitiesOptions.value.push(data);
|
bankEntitiesOptions.value.push(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPostcodeCreated = async () => {
|
|
||||||
postcodeFetchDataRef.value.fetch();
|
function handleLocation(data, location ) {
|
||||||
};
|
const { town, postcode: code, provinceFk, countryFk } = location ?? {}
|
||||||
|
data.postcode = code;
|
||||||
|
data.city = town;
|
||||||
|
data.provinceFk = provinceFk;
|
||||||
|
data.countryFk = countryFk;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const userInfo = await useUserConfig().fetch();
|
const userInfo = await useUserConfig().fetch();
|
||||||
|
@ -83,24 +78,7 @@ onMounted(async () => {
|
||||||
:filter="workerConfigFilter"
|
:filter="workerConfigFilter"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FetchData
|
|
||||||
ref="postcodeFetchDataRef"
|
|
||||||
url="Postcodes/location"
|
|
||||||
@on-fetch="(data) => (postcodesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
url="Provinces/location"
|
|
||||||
@on-fetch="(data) => (provincesOptions = data)"
|
|
||||||
:filter="provincesFilter"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
url="Towns/location"
|
|
||||||
@on-fetch="(data) => (townsOptions = data)"
|
|
||||||
:filter="townsFilter"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
<FetchData
|
||||||
url="Companies"
|
url="Companies"
|
||||||
@on-fetch="(data) => (companiesOptions = data)"
|
@on-fetch="(data) => (companiesOptions = data)"
|
||||||
|
@ -178,77 +156,19 @@ onMounted(async () => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectCreate
|
<VnLocation
|
||||||
v-model="data.postcode"
|
|
||||||
:label="t('worker.create.postcode')"
|
|
||||||
:rules="validate('Worker.postcode')"
|
:rules="validate('Worker.postcode')"
|
||||||
:roles-allowed-to-create="['deliveryAssistant']"
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
:options="postcodesOptions"
|
:options="postcodesOptions"
|
||||||
option-label="code"
|
v-model="data.location"
|
||||||
option-value="code"
|
@update:model-value="
|
||||||
hide-selected
|
(location) => handleLocation(data, location)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #form>
|
</VnLocation>
|
||||||
<CreateNewPostcode
|
|
||||||
@on-data-saved="onPostcodeCreated($event)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection v-if="scope.opt">
|
|
||||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
|
||||||
<QItemLabel caption
|
|
||||||
>{{ scope.opt.code }} -
|
|
||||||
{{ scope.opt.town.name }} ({{
|
|
||||||
scope.opt.town.province.name
|
|
||||||
}},
|
|
||||||
{{
|
|
||||||
scope.opt.town.province.country.country
|
|
||||||
}})</QItemLabel
|
|
||||||
>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectCreate>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('worker.create.province')"
|
|
||||||
v-model="data.provinceFk"
|
|
||||||
:options="provincesOptions"
|
|
||||||
option-value="id"
|
|
||||||
option-label="name"
|
|
||||||
hide-selected
|
|
||||||
:rules="validate('Worker.provinceFk')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('worker.create.city')"
|
|
||||||
v-model="data.city"
|
|
||||||
:options="townsOptions"
|
|
||||||
option-value="name"
|
|
||||||
option-label="name"
|
|
||||||
hide-selected
|
|
||||||
:rules="validate('Worker.city')"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
|
||||||
<QItemLabel caption
|
|
||||||
>{{ scope.opt.name }},
|
|
||||||
{{ scope.opt.province.name }} ({{
|
|
||||||
scope.opt.province.country.country
|
|
||||||
}})</QItemLabel
|
|
||||||
>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnInput
|
<VnInput
|
||||||
:label="t('worker.create.street')"
|
:label="t('worker.create.street')"
|
||||||
|
|
|
@ -134,6 +134,12 @@ export default {
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/Supplier/Card/SupplierAddresses.vue'),
|
import('src/pages/Supplier/Card/SupplierAddresses.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'address/create',
|
||||||
|
name: 'SupplierAddressesCreate',
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Supplier/Card/SupplierAddressesCreate.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'consumption',
|
path: 'consumption',
|
||||||
name: 'SupplierConsumption',
|
name: 'SupplierConsumption',
|
||||||
|
@ -154,6 +160,12 @@ export default {
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/Supplier/Card/SupplierAgencyTerm.vue'),
|
import('src/pages/Supplier/Card/SupplierAgencyTerm.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'agency-term/create',
|
||||||
|
name: 'SupplierAgencyTermCreate',
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Supplier/Card/SupplierAgencyTermCreate.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -152,25 +152,85 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'consignees',
|
path: 'consignees',
|
||||||
|
name: 'ConsigneesCard',
|
||||||
|
redirect: { name: 'CustomerConsignees' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
name: 'CustomerConsignees',
|
name: 'CustomerConsignees',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'consignees',
|
|
||||||
icon: 'vn:delivery',
|
icon: 'vn:delivery',
|
||||||
|
title: 'consignees',
|
||||||
},
|
},
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/Customer/Card/CustomerConsignees.vue'),
|
import('src/pages/Customer/Card/CustomerConsignees.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'CustomerConsigneeCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'consignee-create',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
'src/pages/Customer/components/CustomerConsigneeCreate.vue'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':consigneeId',
|
||||||
|
name: 'CustomerConsigneeEditCard',
|
||||||
|
redirect: { name: 'CustomerConsigneeEdit' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'edit',
|
||||||
|
name: 'CustomerConsigneeEdit',
|
||||||
|
meta: {
|
||||||
|
title: 'consignee-edit',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
'src/pages/Customer/components/CustomerConsigneeEdit.vue'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'notes',
|
path: 'notes',
|
||||||
|
name: 'NotesCard',
|
||||||
|
redirect: { name: 'CustomerNotes' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
name: 'CustomerNotes',
|
name: 'CustomerNotes',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'notes',
|
title: 'notes',
|
||||||
icon: 'vn:notes',
|
icon: 'vn:notes',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Customer/Card/CustomerNotes.vue'),
|
component: () =>
|
||||||
|
import('src/pages/Customer/Card/CustomerNotes.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'CustomerNoteCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'note-create',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
'src/pages/Customer/components/CustomerNoteCreate.vue'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'credits',
|
path: 'credits',
|
||||||
|
name: 'CreditsCard',
|
||||||
|
redirect: { name: 'CustomerCredits' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
name: 'CustomerCredits',
|
name: 'CustomerCredits',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'credits',
|
title: 'credits',
|
||||||
|
@ -179,6 +239,19 @@ export default {
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/Customer/Card/CustomerCredits.vue'),
|
import('src/pages/Customer/Card/CustomerCredits.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'CustomerCreditCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'credit-create',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
'src/pages/Customer/components/CustomerCreditCreate.vue'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'greuges',
|
path: 'greuges',
|
||||||
name: 'CustomerGreuges',
|
name: 'CustomerGreuges',
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
redirect: { name: 'EntryMain' },
|
redirect: { name: 'EntryMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['EntryList'],
|
main: ['EntryList'],
|
||||||
card: [],
|
card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryLog'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -39,23 +39,63 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// name: 'EntryCard',
|
name: 'EntryCard',
|
||||||
// path: ':id',
|
path: ':id',
|
||||||
// component: () => import('src/pages/Entry/Card/EntryCard.vue'),
|
component: () => import('src/pages/Entry/Card/EntryCard.vue'),
|
||||||
// redirect: { name: 'EntrySummary' },
|
redirect: { name: 'EntrySummary' },
|
||||||
// children: [
|
children: [
|
||||||
// {
|
{
|
||||||
// name: 'EntrySummary',
|
name: 'EntrySummary',
|
||||||
// path: 'summary',
|
path: 'summary',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: 'summary',
|
title: 'summary',
|
||||||
// icon: 'launch',
|
icon: 'launch',
|
||||||
// },
|
},
|
||||||
// component: () =>
|
component: () => import('src/pages/Entry/Card/EntrySummary.vue'),
|
||||||
// import('src/pages/Entry/Card/EntrySummary.vue'),
|
},
|
||||||
// },
|
{
|
||||||
// ],
|
path: 'basic-data',
|
||||||
// },
|
name: 'EntryBasicData',
|
||||||
|
meta: {
|
||||||
|
title: 'basicData',
|
||||||
|
icon: 'vn:settings',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Entry/Card/EntryBasicData.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'buys',
|
||||||
|
name: 'EntryBuys',
|
||||||
|
meta: {
|
||||||
|
title: 'buys',
|
||||||
|
icon: 'vn:lines',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Entry/Card/EntryBuys.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'buys/import',
|
||||||
|
name: 'EntryBuysImport',
|
||||||
|
component: () => import('src/pages/Entry/Card/EntryBuysImport.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'notes',
|
||||||
|
name: 'EntryNotes',
|
||||||
|
meta: {
|
||||||
|
title: 'notes',
|
||||||
|
icon: 'vn:notes',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Entry/Card/EntryNotes.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'log',
|
||||||
|
name: 'EntryLog',
|
||||||
|
meta: {
|
||||||
|
title: 'log',
|
||||||
|
icon: 'vn:History',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Entry/Card/EntryLog.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,15 +10,15 @@ export default {
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'RouteMain' },
|
redirect: { name: 'RouteMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['CmrList'],
|
main: ['RouteList', 'CmrList'],
|
||||||
card: [],
|
card: ['RouteBasicData'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/route',
|
path: '/route',
|
||||||
name: 'RouteMain',
|
name: 'RouteMain',
|
||||||
component: () => import('src/pages/Route/RouteMain.vue'),
|
component: () => import('src/pages/Route/RouteMain.vue'),
|
||||||
redirect: { name: 'CmrList' },
|
redirect: { name: 'RouteList' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'cmr',
|
path: 'cmr',
|
||||||
|
@ -29,6 +29,49 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'list',
|
||||||
|
name: 'RouteList',
|
||||||
|
meta: {
|
||||||
|
title: 'RouteList',
|
||||||
|
icon: 'view_list',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/RouteList.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'RouteCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'create',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/Card/RouteForm.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RouteCard',
|
||||||
|
path: ':id',
|
||||||
|
component: () => import('src/pages/Route/Card/RouteCard.vue'),
|
||||||
|
redirect: { name: 'RouteSummary' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'RouteBasicData',
|
||||||
|
path: 'basic-data',
|
||||||
|
meta: {
|
||||||
|
title: 'basicData',
|
||||||
|
icon: 'vn:settings',
|
||||||
|
},
|
||||||
|
component: () => import('pages/Route/Card/RouteForm.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RouteSummary',
|
||||||
|
path: 'summary',
|
||||||
|
meta: {
|
||||||
|
title: 'summary',
|
||||||
|
icon: 'open_in_new',
|
||||||
|
},
|
||||||
|
component: () => import('pages/Route/Card/RouteSummary.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
const inputLocation = ':nth-child(3) > :nth-child(1) > .q-field > .q-field__inner > .q-field__control';
|
||||||
|
const locationOptions ='[role="listbox"] > div.q-virtual-scroll__content > .q-item'
|
||||||
|
describe('VnLocation', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1280, 720);
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit('/#/worker/create');
|
||||||
|
cy.waitForElement('.q-card');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Show all options', function() {
|
||||||
|
cy.get(inputLocation).click();
|
||||||
|
cy.get(locationOptions).should('have.length',5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('input filter location as "al"', function() {
|
||||||
|
cy.get(inputLocation).click();
|
||||||
|
cy.get(inputLocation).clear();
|
||||||
|
cy.get(inputLocation).type('al');
|
||||||
|
cy.get(locationOptions).should('have.length',3);
|
||||||
|
});
|
||||||
|
it('input filter location as "ecuador"', function() {
|
||||||
|
cy.get(inputLocation).click();
|
||||||
|
cy.get(inputLocation).clear();
|
||||||
|
cy.get(inputLocation).type('ecuador');
|
||||||
|
cy.get(locationOptions).should('have.length',1);
|
||||||
|
cy.get(`${locationOptions}:nth-child(1)`).click();
|
||||||
|
cy.get(':nth-child(3) > :nth-child(1) > .q-field > .q-field__inner > .q-field__control > :nth-child(2) > .q-icon').click();
|
||||||
|
|
||||||
|
});
|
||||||
|
})
|
|
@ -31,7 +31,6 @@ describe('ClaimAction', () => {
|
||||||
|
|
||||||
it('should regularize', () => {
|
it('should regularize', () => {
|
||||||
cy.get('[title="Regularize"]').click();
|
cy.get('[title="Regularize"]').click();
|
||||||
cy.clickConfirm();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the line', () => {
|
it('should remove the line', () => {
|
||||||
|
|
Loading…
Reference in New Issue