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

This commit is contained in:
Robert Ferrús 2024-05-16 07:29:07 +02:00
commit b7fce0917e
122 changed files with 6572 additions and 3038 deletions

View File

@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- (Worker) => Se crea la sección Taquilla
### Fixed
- (General) => Se vuelven a mostrar los parámetros en la url al aplicar un filtro

View File

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

View File

@ -29,7 +29,7 @@ module.exports = configure(function (/* ctx */) {
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli/boot-files
boot: ['i18n', 'axios', 'vnDate', 'validations', 'quasar.defaults'],
boot: ['i18n', 'axios', 'vnDate', 'validations', 'quasar', 'quasar.defaults'],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: ['app.scss'],

View File

@ -58,15 +58,12 @@ onMounted(async () => {
>
<template #form-inputs="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('name')"
v-model="data.name"
:required="true"
:rules="validate('bankEntity.name')"
/>
</div>
<div class="col">
<VnInput
ref="bicInputRef"
:label="t('swift')"
@ -74,7 +71,6 @@ onMounted(async () => {
:required="true"
:rules="validate('bankEntity.bic')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">

View File

@ -52,9 +52,7 @@ onMounted(() => {
</span>
<h1 class="title">{{ t('New department') }}</h1>
<VnRow class="row q-gutter-md q-mb-md" style="min-width: 250px">
<div class="col">
<VnInput :label="t('Name')" v-model="data.name" />
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn

View File

@ -72,7 +72,6 @@ const onDataSaved = async (formData, requestResponse) => {
{{ t('Invoicing in progress...') }}
</span>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Ticket')"
:options="ticketsOptions"
@ -86,18 +85,14 @@ const onDataSaved = async (formData, requestResponse) => {
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
<QItemLabel caption>{{
scope.opt?.nickname
}}</QItemLabel>
<QItemLabel caption>{{ scope.opt?.nickname }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</div>
<span class="row items-center" style="max-width: max-content">{{
t('Or')
}}</span>
<div class="col">
<VnSelect
:label="t('Client')"
:options="clientsOptions"
@ -107,13 +102,9 @@ const onDataSaved = async (formData, requestResponse) => {
v-model="data.clientFk"
@update:model-value="data.ticketFk = null"
/>
</div>
<div class="col">
<VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Serial')"
:options="invoiceOutSerialsOptions"
@ -123,8 +114,6 @@ const onDataSaved = async (formData, requestResponse) => {
v-model="data.serial"
:required="true"
/>
</div>
<div class="col">
<VnSelect
:label="t('Area')"
:options="taxAreasOptions"
@ -134,7 +123,6 @@ const onDataSaved = async (formData, requestResponse) => {
v-model="data.taxArea"
:required="true"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput

View File

@ -40,14 +40,11 @@ const onDataSaved = (dataSaved) => {
>
<template #form-inputs="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Name')"
v-model="data.name"
:rules="validate('city.name')"
/>
</div>
<div class="col">
<VnSelect
:label="t('Province')"
:options="provincesOptions"
@ -57,7 +54,6 @@ const onDataSaved = (dataSaved) => {
v-model="data.provinceFk"
:rules="validate('city.provinceFk')"
/>
</div>
</VnRow>
</template>
</FormModelPopup>

View File

@ -30,18 +30,18 @@ const townsLocationOptions = ref([]);
const onDataSaved = (formData) => {
const newPostcode = {
...formData
...formData,
};
const townObject = townsLocationOptions.value.find(
({id}) => id === formData.townFk
({ id }) => id === formData.townFk
);
newPostcode.town = townObject?.name;
const provinceObject = provincesOptions.value.find(
({id}) => id === formData.provinceFk
({ id }) => id === formData.provinceFk
);
newPostcode.province = provinceObject?.name;
const countryObject = countriesOptions.value.find(
({id}) => id === formData.countryFk
({ id }) => id === formData.countryFk
);
newPostcode.country = countryObject?.country;
emit('onDataSaved', newPostcode);
@ -92,14 +92,11 @@ const onProvinceCreated = async ({ name }, formData) => {
>
<template #form-inputs="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Postcode')"
v-model="data.code"
:rules="validate('postcode.code')"
/>
</div>
<div class="col">
<VnSelectDialog
:label="t('City')"
:options="townsLocationOptions"
@ -111,15 +108,11 @@ const onProvinceCreated = async ({ name }, formData) => {
:roles-allowed-to-create="['deliveryAssistant']"
>
<template #form>
<CreateNewCityForm
@on-data-saved="onCityCreated($event, data)"
/>
<CreateNewCityForm @on-data-saved="onCityCreated($event, data)" />
</template>
</VnSelectDialog>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-xl">
<div class="col">
<VnSelectDialog
:label="t('Province')"
:options="provincesOptions"
@ -134,11 +127,10 @@ const onProvinceCreated = async ({ name }, formData) => {
<CreateNewProvinceForm
@on-data-saved="onProvinceCreated($event, data)"
/>
</template>
</VnSelectDialog>
</div>
<div class="col">
<VnSelect
</template> </VnSelectDialog
></VnRow>
<VnRow class="row q-gutter-md q-mb-xl"
><VnSelect
:label="t('Country')"
:options="countriesOptions"
hide-selected
@ -147,8 +139,8 @@ const onProvinceCreated = async ({ name }, formData) => {
v-model="data.countryFk"
:rules="validate('postcode.countryFk')"
/>
</div> </VnRow
></template>
</VnRow>
</template>
</FormModelPopup>
</template>

View File

@ -40,14 +40,11 @@ const onDataSaved = (dataSaved) => {
>
<template #form-inputs="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Name')"
v-model="data.name"
:rules="validate('province.name')"
/>
</div>
<div class="col">
<VnSelect
:label="t('Autonomy')"
:options="autonomiesOptions"
@ -57,7 +54,6 @@ const onDataSaved = (dataSaved) => {
v-model="data.autonomyFk"
:rules="validate('province.autonomyFk')"
/>
</div>
</VnRow>
</template>
</FormModelPopup>

View File

@ -54,16 +54,12 @@ const onDataSaved = (dataSaved) => {
>
<template #form-inputs="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Identifier')"
v-model="data.thermographId"
:required="true"
:rules="validate('thermograph.id')"
/>
</div>
<div class="col">
<VnSelect
:label="t('Model')"
:options="thermographsModels"
@ -74,10 +70,8 @@ const onDataSaved = (dataSaved) => {
:required="true"
:rules="validate('thermograph.model')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-xl">
<div class="col">
<VnSelect
:label="t('Warehouse')"
:options="warehousesOptions"
@ -87,8 +81,6 @@ const onDataSaved = (dataSaved) => {
v-model="data.warehouseId"
:required="true"
/>
</div>
<div class="col">
<VnSelect
:label="t('Temperature')"
:options="temperaturesOptions"
@ -98,7 +90,6 @@ const onDataSaved = (dataSaved) => {
v-model="data.temperatureFk"
:required="true"
/>
</div>
</VnRow>
</template>
</FormModelPopup>

View File

@ -81,6 +81,7 @@ defineExpose({
hasChanges,
saveChanges,
getChanges,
formData,
});
async function fetch(data) {

View File

@ -246,16 +246,13 @@ const makeRequest = async () => {
<div class="column">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QOptionGroup
:options="uploadMethodsOptions"
type="radio"
v-model="uploadMethodSelected"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
v-if="uploadMethodSelected === 'computer'"
ref="inputFileRef"
@ -289,10 +286,8 @@ const makeRequest = async () => {
@update:model-value="updatePhotoPreview($event)"
placeholder="https://"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Orientation')"
:options="viewportTypes"
@ -300,7 +295,6 @@ const makeRequest = async () => {
option-label="description"
v-model="viewportSelection"
/>
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn

View File

@ -1,7 +1,6 @@
<script setup>
import { ref, reactive, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue';
@ -12,10 +11,16 @@ import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import axios from 'axios';
import { dashIfEmpty } from 'src/filters';
const props = defineProps({
url: {
type: String,
required: true,
},
});
const emit = defineEmits(['itemSelected']);
const { t } = useI18n();
const route = useRoute();
const itemFilter = {
include: [
@ -73,7 +78,7 @@ const tableColumns = computed(() => [
{
label: t('entry.buys.color'),
name: 'ink',
field: 'inkName',
field: (row) => row?.ink?.name,
align: 'left',
},
]);
@ -100,7 +105,7 @@ const fetchResults = async () => {
}
filter.where = where;
const { data } = await axios.get(`Entries/${route.params.id}/lastItemBuys`, {
const { data } = await axios.get(props.url, {
params: { filter: JSON.stringify(filter) },
});
tableRows.value = data;
@ -147,19 +152,8 @@ const selectItem = ({ id }) => {
</span>
<h1 class="title">{{ t('Filter item') }}</h1>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('entry.buys.name')"
v-model="itemFilterParams.name"
/>
</div>
<div class="col">
<VnInput
:label="t('entry.buys.size')"
v-model="itemFilterParams.size"
/>
</div>
<div class="col">
<VnInput :label="t('entry.buys.name')" v-model="itemFilterParams.name" />
<VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" />
<VnSelect
:label="t('entry.buys.producer')"
:options="producersOptions"
@ -168,8 +162,6 @@ const selectItem = ({ id }) => {
option-value="id"
v-model="itemFilterParams.producerFk"
/>
</div>
<div class="col">
<VnSelect
:label="t('entry.buys.type')"
:options="ItemTypesOptions"
@ -178,8 +170,6 @@ const selectItem = ({ id }) => {
option-value="id"
v-model="itemFilterParams.typeFk"
/>
</div>
<div class="col">
<VnSelect
:label="t('entry.buys.color')"
:options="InksOptions"
@ -188,7 +178,6 @@ const selectItem = ({ id }) => {
option-value="id"
v-model="itemFilterParams.inkFk"
/>
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn

View File

@ -145,7 +145,6 @@ const selectTravel = ({ id }) => {
</span>
<h1 class="title">{{ t('Filter travels') }}</h1>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('entry.basicData.agency')"
:options="agenciesOptions"
@ -154,8 +153,6 @@ const selectTravel = ({ id }) => {
option-value="id"
v-model="travelFilterParams.agencyModeFk"
/>
</div>
<div class="col">
<VnSelect
:label="t('entry.basicData.warehouseOut')"
:options="warehousesOptions"
@ -164,8 +161,6 @@ const selectTravel = ({ id }) => {
option-value="id"
v-model="travelFilterParams.warehouseOutFk"
/>
</div>
<div class="col">
<VnSelect
:label="t('entry.basicData.warehouseIn')"
:options="warehousesOptions"
@ -174,19 +169,14 @@ const selectTravel = ({ id }) => {
option-value="id"
v-model="travelFilterParams.warehouseInFk"
/>
</div>
<div class="col">
<VnInputDate
:label="t('entry.basicData.shipped')"
v-model="travelFilterParams.shipped"
/>
</div>
<div class="col">
<VnInputDate
:label="t('entry.basicData.landed')"
v-model="travelFilterParams.landed"
/>
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn

View File

@ -156,9 +156,12 @@ const startFormWatcher = () => {
async function fetch() {
try {
const { data } = await axios.get($props.url, {
let { data } = await axios.get($props.url, {
params: { filter: JSON.stringify($props.filter) },
});
if (Array.isArray(data)) data = data[0] ?? {};
state.set($props.model, data);
originalData.value = data && JSON.parse(JSON.stringify(data));

View File

@ -50,13 +50,11 @@ const onDataSaved = (data) => {
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Type the visible quantity')"
v-model.number="data.quantity"
autofocus
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">

View File

@ -83,7 +83,6 @@ const transferInvoice = async () => {
>
<template #form-inputs>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Client')"
:options="clientsOptions"
@ -104,8 +103,6 @@ const transferInvoice = async () => {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnSelect
:label="t('Rectificative type')"
:options="rectificativeTypeOptions"
@ -115,10 +112,8 @@ const transferInvoice = async () => {
v-model="transferInvoiceParams.cplusRectificationTypeFk"
:required="true"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Class')"
:options="siiTypeInvoiceOutsOptions"
@ -139,8 +134,6 @@ const transferInvoice = async () => {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnSelect
:label="t('Type')"
:options="invoiceCorrectionTypesOptions"
@ -150,7 +143,6 @@ const transferInvoice = async () => {
v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
:required="true"
/>
</div>
</VnRow>
</template>
</FormPopup>

View File

@ -1,5 +1,5 @@
<script setup>
import { onBeforeMount, computed } from 'vue';
import { onBeforeMount, computed, watchEffect } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
@ -47,6 +47,11 @@ if (props.baseUrl) {
}
});
}
watchEffect(() => {
if (Array.isArray(arrayData.store.data))
arrayData.store.data = arrayData.store.data[0];
});
</script>
<template>
<Teleport

View File

@ -52,6 +52,13 @@ const focus = () => {
defineExpose({
focus,
});
const inputRules = [
(val) => {
const { min } = vnInputRef.value.$attrs;
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
},
];
</script>
<template>
@ -68,6 +75,9 @@ defineExpose({
:class="{ required: $attrs.required }"
@keyup.enter="onEnterPress()"
:clearable="false"
:rules="inputRules"
:lazy-rules="true"
hide-bottom-space
>
<template v-if="$slots.prepend" #prepend>
<slot name="prepend" />
@ -85,3 +95,9 @@ defineExpose({
</QInput>
</div>
</template>
<i18n>
en:
inputMin: Must be more than {value}
es:
inputMin: Debe ser mayor a {value}
</i18n>

View File

@ -57,7 +57,7 @@ const $props = defineProps({
});
const { t } = useI18n();
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
const requiredFieldRule = (val) => val ?? t('globals.fieldRequired');
const { optionLabel, optionValue, options, modelValue } = toRefs($props);
const myOptions = ref([]);
@ -167,6 +167,7 @@ watch(modelValue, (newValue) => {
hide-selected
fill-input
ref="vnSelectRef"
lazy-rules
:class="{ required: $attrs.required }"
:rules="$attrs.required ? [requiredFieldRule] : null"
virtual-scroll-slice-size="options.length"

View File

@ -54,6 +54,13 @@ async function fetch() {
emit('onFetch', Array.isArray(data) ? data[0] : data);
isLoading.value = false;
}
const showRedirectToSummaryIcon = computed(() => {
const routeExists = route.matched.some(
(route) => route.name === `${route.meta.moduleName}Summary`
);
return !isSummary.value && route.meta.moduleName && routeExists;
});
</script>
<template>
@ -64,7 +71,7 @@ async function fetch() {
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
<slot name="header-left">
<router-link
v-if="!isSummary && route.meta.moduleName"
v-if="showRedirectToSummaryIcon"
class="header link"
:to="{
name: `${route.meta.moduleName}Summary`,

View File

@ -1,29 +1,17 @@
<template>
<div class="q-pa-md">
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
</div>
<div class="row q-gutter-md">
<QSkeleton type="QBtn" />
<QSkeleton type="QBtn" />

View File

@ -3,39 +3,30 @@
<QSkeleton type="rect" square />
</div>
<div class="row q-pa-md q-col-gutter-md q-mb-md">
<div class="col">
<QSkeleton type="rect" class="q-mb-md" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
</div>
<div class="col">
<QSkeleton type="rect" class="q-mb-md" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
</div>
<div class="col">
<QSkeleton type="rect" class="q-mb-md" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
</div>
<div class="col">
<QSkeleton type="rect" class="q-mb-md" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
</div>
<div class="col">
<QSkeleton type="rect" class="q-mb-md" square />
<QSkeleton type="text" square />
<QSkeleton type="text" square />
@ -43,7 +34,6 @@
<QSkeleton type="text" square />
<QSkeleton type="text" square />
</div>
</div>
</template>
<style lang="scss" scoped>

View File

@ -1,47 +1,22 @@
<template>
<div class="q-pa-md w">
<div class="row q-gutter-md q-mb-md">
<div class="col-1">
<QSkeleton type="rect" square />
<QSkeleton type="rect" square />
<QSkeleton type="rect" square />
<QSkeleton type="rect" square />
<QSkeleton type="rect" square />
<QSkeleton type="rect" square />
</div>
<div class="col">
<QSkeleton type="rect" square />
</div>
<div class="col">
<QSkeleton type="rect" square />
</div>
<div class="col">
<QSkeleton type="rect" square />
</div>
<div class="col">
<QSkeleton type="rect" square />
</div>
<div class="col">
<QSkeleton type="rect" square />
</div>
</div>
<div class="row q-gutter-md q-mb-md" v-for="n in 5" :key="n">
<div class="col-1">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
<div class="col">
<QSkeleton type="QInput" square />
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.w {

View File

@ -147,7 +147,7 @@ const customTags = computed(() =>
async function remove(key) {
userParams.value[key] = null;
await search();
await arrayData.applyFilter({ params: userParams.value });
emit('remove', key);
}

View File

@ -81,14 +81,14 @@ async function search() {
const staticParams = Object.entries(store.userParams).filter(
([key, value]) => value && (props.staticParams || []).includes(key)
);
// const filter =props?.where? { where: JSON.parse(props.where) }: {}
store.skip = 0;
await arrayData.applyFilter({
params: {
// filter ,
...Object.fromEntries(staticParams),
search: searchText.value,
},
});
if (!props.redirect) return;
if (props.customRouteRedirectName)

View File

@ -95,6 +95,10 @@ globals:
agency: Agency
workCenters: Work centers
modes: Modes
zones: Zones
zonesList: Zones
deliveryList: Delivery days
upcomingList: Upcoming deliveries
created: Created
worker: Worker
now: Now
@ -426,6 +430,7 @@ ticket:
boxing: Boxing
sms: Sms
notes: Notes
sale: Sale
list:
nickname: Nickname
state: State
@ -822,6 +827,7 @@ worker:
log: Log
calendar: Calendar
timeControl: Time control
locker: Locker
list:
name: Name
email: Email
@ -1137,6 +1143,7 @@ item:
tax: Tax
log: Log
botanical: Botanical
shelving: Shelving
itemTypeCreate: New item type
family: Item Type
lastEntries: Last entries
@ -1229,12 +1236,10 @@ item/itemType:
itemType: Item type
basicData: Basic data
summary: Summary
zone:
monitor:
pageTitles:
zones: Zone
zonesList: Zones
deliveryList: Delivery days
upcomingList: Upcoming deliveries
monitors: Monitors
list: List
components:
topbar: {}
itemsFilterPanel:

View File

@ -95,6 +95,10 @@ globals:
agency: Agencia
workCenters: Centros de trabajo
modes: Modos
zones: Zonas
zonesList: Zonas
deliveryList: Días de entrega
upcomingList: Próximos repartos
created: Fecha creación
worker: Trabajador
now: Ahora
@ -424,6 +428,7 @@ ticket:
boxing: Encajado
sms: Sms
notes: Notas
sale: Lineas del pedido
list:
nickname: Alias
state: Estado
@ -820,6 +825,7 @@ worker:
log: Historial
calendar: Calendario
timeControl: Control de horario
locker: Taquilla
list:
name: Nombre
email: Email
@ -1136,6 +1142,7 @@ item:
botanical: 'Botánico'
barcode: 'Código de barras'
log: Historial
shelving: Carros
itemTypeCreate: Nueva familia
family: Familia
lastEntries: Últimas entradas
@ -1234,6 +1241,10 @@ zone:
zonesList: Zonas
deliveryList: Días de entrega
upcomingList: Próximos repartos
monitor:
pageTitles:
monitors: Monitores
list: Listado
components:
topbar: {}
itemsFilterPanel:

View File

@ -95,22 +95,17 @@ const statesFilter = {
>
<template #form="{ data, validate, filter }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.client.name"
:label="t('claim.basicData.customer')"
disable
/>
</div>
<div class="col">
<VnInputDate
v-model="data.created"
:label="t('claim.basicData.created')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('claim.basicData.assignedTo')"
v-model="data.workerFk"
@ -131,8 +126,6 @@ const statesFilter = {
</QAvatar>
</template>
</VnSelect>
</div>
<div class="col">
<QSelect
v-model="data.claimStateFk"
:options="claimStates"
@ -147,18 +140,14 @@ const statesFilter = {
:input-debounce="0"
>
</QSelect>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
v-model.number="data.packages"
:label="t('globals.packages')"
:rules="validate('claim.packages')"
type="number"
/>
</div>
<div class="col">
<QSelect
v-model="data.pickup"
:options="optionsList"
@ -171,7 +160,6 @@ const statesFilter = {
:input-debounce="0"
>
</QSelect>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -70,7 +70,6 @@ const filterOptions = {
<FormModel :url="`Clients/${route.params.id}`" auto-load model="customer">
<template #form="{ data, validate, filter }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Comercial name')"
:rules="validate('client.socialName')"
@ -78,8 +77,7 @@ const filterOptions = {
clearable
v-model="data.name"
/>
</div>
<div class="col">
<QSelect
:input-debounce="0"
:label="t('customer.basicData.businessType')"
@ -91,18 +89,14 @@ const filterOptions = {
option-value="code"
v-model="data.businessTypeFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('customer.basicData.contact')"
:rules="validate('client.contact')"
clearable
v-model="data.contact"
/>
</div>
<div class="col">
<VnInput
:label="t('customer.basicData.email')"
:rules="validate('client.email')"
@ -118,28 +112,22 @@ const filterOptions = {
</QIcon>
</template>
</VnInput>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('customer.basicData.phone')"
:rules="validate('client.phone')"
clearable
v-model="data.phone"
/>
</div>
<div class="col">
<VnInput
:label="t('customer.basicData.mobile')"
:rules="validate('client.mobile')"
clearable
v-model="data.mobile"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QSelect
:input-debounce="0"
:label="t('customer.basicData.salesPerson')"
@ -163,23 +151,19 @@ const filterOptions = {
</QAvatar>
</template>
</QSelect>
</div>
<div class="col">
<QSelect
:input-debounce="0"
:label="t('customer.basicData.contactChannel')"
:options="contactChannels"
:rules="validate('client.contactChannelFk')"
emit-value
map-options
option-label="name"
option-value="id"
v-model="data.contactChannelFk"
:options="contactChannels"
option-value="id"
option-label="name"
emit-value
:label="t('customer.basicData.contactChannel')"
map-options
:rules="validate('client.contactChannelFk')"
:input-debounce="0"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QSelect
:input-debounce="0"
:label="t('customer.basicData.previousClient')"
@ -201,7 +185,6 @@ const filterOptions = {
</QIcon>
</template>
</QSelect>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -48,7 +48,6 @@ const getBankEntities = (data, formData) => {
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Billing data')"
:options="payMethods"
@ -57,14 +56,10 @@ const getBankEntities = (data, formData) => {
option-value="id"
v-model="data.payMethod"
/>
</div>
<div class="col">
<VnInput :label="t('Due day')" clearable v-model="data.dueDay" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('IBAN')" clearable v-model="data.iban">
<template #append>
<QIcon name="info" class="cursor-info">
@ -72,8 +67,6 @@ const getBankEntities = (data, formData) => {
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnSelectDialog
:label="t('Swift / BIC')"
:options="bankEntitiesOptions"
@ -93,29 +86,18 @@ const getBankEntities = (data, formData) => {
<QItem v-bind="scope.itemProps">
<QItemSection v-if="scope.opt">
<QItemLabel
>{{ scope.opt.bic }}
{{ scope.opt.name }}</QItemLabel
>{{ scope.opt.bic }} {{ scope.opt.name }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelectDialog>
</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 core received')" v-model="data.hasCoreVnl" />
<QCheckbox :label="t('VNL B2B received')" v-model="data.hasSepaVnl" />
</div>
</VnRow>
</template>
</FormModel>

View File

@ -41,7 +41,6 @@ function handleLocation(data, location) {
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Social name')"
:required="true"
@ -55,20 +54,14 @@ function handleLocation(data, location) {
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnInput :label="t('Tax number')" clearable v-model="data.fi" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Street')" clearable v-model="data.street" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Sage tax type')"
:options="typesTaxes"
@ -77,8 +70,6 @@ function handleLocation(data, location) {
option-value="id"
v-model="data.sageTaxTypeFk"
/>
</div>
<div class="col">
<VnSelect
:label="t('Sage transaction type')"
:options="typesTransactions"
@ -98,11 +89,9 @@ function handleLocation(data, location) {
</QItem>
</template>
</VnSelect>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
@ -111,80 +100,50 @@ function handleLocation(data, location) {
@update:model-value="(location) => handleLocation(data, location)"
>
</VnLocation>
</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" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip>
{{ t('whenActivatingIt') }}
</QTooltip>
</QIcon>
</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('Notify by email')" v-model="data.isToBeMailed" />
<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"
/>
<QCheckbox :label="t('Is equalizated')" v-model="data.isEqualizated" />
<QIcon class="cursor-info q-ml-sm" name="info" size="sm">
<QTooltip>
{{ t('inOrderToInvoice') }}
</QTooltip>
</QIcon>
</div>
<div class="col">
<QCheckbox
:label="t('Verified data')"
v-model="data.isTaxDataChecked"
/>
</div>
<QCheckbox :label="t('Verified data')" v-model="data.isTaxDataChecked" />
</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>

View File

@ -49,10 +49,7 @@ function handleLocation(data, location) {
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput :label="t('Comercial name')" v-model="data.name" />
</div>
<div class="col">
<VnSelect
:label="t('Salesperson')"
:options="workersOptions"
@ -61,10 +58,8 @@ function handleLocation(data, location) {
option-value="id"
v-model="data.salesPersonFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Business type')"
:options="businessTypesOptions"
@ -73,49 +68,35 @@ function handleLocation(data, location) {
option-value="code"
v-model="data.businessTypeFk"
/>
</div>
<div class="col">
<QInput v-model="data.fi" :label="t('Tax number')" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Business name')"
:rules="validate('client.socialName')"
v-model="data.socialName"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Street')"
:rules="validate('client.street')"
v-model="data.street"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
v-model="data.location"
@update:model-value="
(location) => handleLocation(data, location)
"
@update:model-value="(location) => handleLocation(data, location)"
>
</VnLocation>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput v-model="data.userName" :label="t('Web user')" />
</div>
<div class="col">
<QInput
:label="t('Email')"
:rules="validate('client.email')"
@ -131,7 +112,6 @@ function handleLocation(data, location) {
</QIcon>
</template>
</QInput>
</div>
</VnRow>
<QCheckbox
:label="t('Is equalizated')"

View File

@ -15,10 +15,3 @@ const stateStore = useStateStore();
<RouterView></RouterView>
</QPageContainer>
</template>
<style lang="scss">
#searchbar,
.search-panel {
width: 400px;
}
</style>

View File

@ -61,13 +61,11 @@ const onSubmit = async () => {
}}
</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

View File

@ -56,24 +56,17 @@ const toCustomerGreuges = () => {
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Amount')"
clearable
type="number"
v-model="data.amount"
/>
</div>
<div class="col">
<VnInputDate :label="t('Date')" v-model="data.shipped" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Comment')" clearable v-model="data.description" />
</div>
<div class="col">
<VnSelect
:label="t('Type')"
:options="greugeTypes"
@ -82,7 +75,6 @@ const toCustomerGreuges = () => {
option-value="id"
v-model="data.greugeTypeFk"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -24,30 +24,22 @@ const onDataSaved = (dataSaved) => {
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('NIF')"
:required="true"
clearable
v-model="data.nif"
/>
</div>
<div class="col">
<VnInput
:label="t('Fiscal name')"
:required="true"
clearable
v-model="data.fiscalName"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Street')" clearable v-model="data.street" />
</div>
<div class="col">
<VnInput :label="t('Phone')" clearable v-model="data.phone" />
</div>
</VnRow>
</template>
</FormModelPopup>

View File

@ -135,14 +135,11 @@ const onDataSaved = async () => {
<h5 class="q-mt-none">{{ t('New payment') }}</h5>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate
:label="t('Date')"
:required="true"
v-model="data.payed"
/>
</div>
<div class="col">
<VnSelect
:label="t('Company')"
:options="companyOptions"
@ -153,11 +150,9 @@ const onDataSaved = async () => {
option-value="id"
v-model="data.companyFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Bank')"
:options="bankOptions"
@ -178,8 +173,6 @@ const onDataSaved = async () => {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnInput
:label="t('Amount')"
:required="true"
@ -188,7 +181,6 @@ const onDataSaved = async () => {
type="number"
v-model.number="data.amountPaid"
/>
</div>
</VnRow>
<div class="text-h6" v-if="data.bankFk === 3 || data.bankFk === 3117">
@ -203,21 +195,18 @@ const onDataSaved = async () => {
v-model="data.compensationAccount"
/>
</div>
<div class="col">
<VnInput
:label="t('Reference')"
:required="true"
clearable
v-model="data.description"
/>
</div>
</VnRow>
<div class="q-mt-lg" v-if="data.bankFk === 2">
<div class="text-h6">{{ t('Cash') }}</div>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Delivered amount')"
@update:model-value="calculateFromDeliveredAmount($event)"
@ -225,8 +214,6 @@ const onDataSaved = async () => {
type="number"
v-model="deliveredAmount"
/>
</div>
<div class="col">
<VnInput
:label="t('Amount to return')"
clearable
@ -234,16 +221,11 @@ const onDataSaved = async () => {
type="number"
v-model="amountToReturn"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox v-model="viewRecipt" />
</div>
<div class="col">
<QCheckbox v-model="sendEmail" />
</div>
</VnRow>
</div>

View File

@ -45,9 +45,7 @@ const 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>

View File

@ -50,31 +50,23 @@ const toCustomerRecoveries = () => {
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate :label="t('Since')" v-model="data.started" />
</div>
<div class="col">
<VnInputDate :label="t('To')" v-model="data.finished" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Amount')"
clearable
type="number"
v-model="data.amount"
/>
</div>
<div class="col">
<VnInput
:label="t('Period')"
clearable
type="number"
v-model="data.period"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -30,7 +30,6 @@ const clientsOptions = ref([]);
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('department.name')"
v-model="data.name"
@ -38,36 +37,28 @@ const clientsOptions = ref([]);
clearable
autofocus
/>
</div>
<div class="col">
<VnInput
v-model="data.code"
:label="t('department.code')"
:rules="validate('department.code')"
clearable
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('department.chat')"
v-model="data.chatName"
:rules="validate('department.chat')"
clearable
/>
</div>
<div class="col">
<VnInput
v-model="data.notificationEmail"
:label="t('department.email')"
:rules="validate('department.email')"
clearable
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('department.bossDepartment')"
v-model="data.workerFk"
@ -78,8 +69,6 @@ const clientsOptions = ref([]);
map-options
:rules="validate('department.workerFk')"
/>
</div>
<div class="col">
<VnSelect
:label="t('department.selfConsumptionCustomer')"
v-model="data.clientFk"
@ -90,45 +79,34 @@ const clientsOptions = ref([]);
map-options
:rules="validate('department.clientFk')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox
:label="t('department.telework')"
v-model="data.isTeleworking"
/>
</div>
<div class="col">
<QCheckbox
:label="t('department.notifyOnErrors')"
v-model="data.hasToMistake"
:false-value="0"
:true-value="1"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox
:label="t('department.worksInProduction')"
v-model="data.isProduction"
/>
</div>
<div class="col">
<QCheckbox
:label="t('department.hasToRefill')"
v-model="data.hasToRefill"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox
:label="t('department.hasToSendMail')"
v-model="data.hasToSendMail"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -68,7 +68,6 @@ const onFilterTravelSelected = (formData, id) => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('entry.basicData.supplier')"
v-model="data.supplierFk"
@ -90,8 +89,6 @@ const onFilterTravelSelected = (formData, id) => {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnSelectDialog
:label="t('entry.basicData.travel')"
v-model="data.travelFk"
@ -123,24 +120,18 @@ const onFilterTravelSelected = (formData, id) => {
</QItem>
</template>
</VnSelectDialog>
</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">
<VnSelect
:label="t('entry.basicData.company')"
v-model="data.companyFk"
@ -151,10 +142,8 @@ const onFilterTravelSelected = (formData, id) => {
hide-selected
:required="true"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('entry.basicData.currency')"
v-model="data.currencyFk"
@ -162,8 +151,6 @@ const onFilterTravelSelected = (formData, id) => {
option-value="id"
option-label="code"
/>
</div>
<div class="col">
<QInput
:label="t('entry.basicData.commission')"
v-model="data.commission"
@ -171,10 +158,8 @@ const onFilterTravelSelected = (formData, id) => {
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"
@ -183,37 +168,26 @@ const onFilterTravelSelected = (formData, id) => {
counter
fill-input
/>
</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-if="isAdministrative()"
v-model="data.isBooked"
:label="t('entry.basicData.booked')"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -198,7 +198,6 @@ const redirectToBuysView = () => {
</Teleport>
<QCard class="q-pa-lg">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
ref="inputFileRef"
:label="t('entry.buys.file')"
@ -218,24 +217,19 @@ const redirectToBuysView = () => {
</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">
@ -251,6 +245,7 @@ const redirectToBuysView = () => {
>
<template #form>
<FilterItemForm
:url="`Entries/${route.params.id}/lastItemBuys`"
@item-selected="row[col.field] = $event"
/>
</template>

View File

@ -79,7 +79,6 @@ const redirectToEntryBasicData = (_, { id }) => {
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Supplier')"
class="full-width"
@ -102,10 +101,8 @@ const redirectToEntryBasicData = (_, { id }) => {
</QItem>
</template>
</VnSelect>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Travel')"
class="full-width"
@ -125,8 +122,7 @@ const redirectToEntryBasicData = (_, { id }) => {
>{{ scope.opt?.agencyModeName }} -
{{ scope.opt?.warehouseInName }} ({{
toDate(scope.opt?.shipped)
}}) &#x2192;
{{ scope.opt?.warehouseOutName }} ({{
}}) &#x2192; {{ scope.opt?.warehouseOutName }} ({{
toDate(scope.opt?.landed)
}})</QItemLabel
>
@ -134,10 +130,8 @@ const redirectToEntryBasicData = (_, { id }) => {
</QItem>
</template>
</VnSelect>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Company')"
class="full-width"
@ -150,7 +144,6 @@ const redirectToEntryBasicData = (_, { id }) => {
:required="true"
:rules="validate('entry.companyFk')"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -182,7 +182,6 @@ async function upsert() {
>
<template #form="{ data }">
<div class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('supplierFk')"
v-model="data.supplierFk"
@ -203,8 +202,6 @@ async function upsert() {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<QInput
clearable
clear-icon="close"
@ -212,20 +209,14 @@ async function upsert() {
v-model="data.supplierRef"
/>
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Expedition date')"
v-model="data.issued"
:mask="dateMask"
>
<template #append>
<QIcon
name="event"
class="cursor-pointer"
:fill-mask="fillMask"
>
<QIcon name="event" class="cursor-pointer" :fill-mask="fillMask">
<QPopupProxy
cover
transition-show="scale"
@ -245,8 +236,6 @@ async function upsert() {
</QIcon>
</template>
</QInput>
</div>
<div class="col">
<QInput
:label="t('Operation date')"
v-model="data.operated"
@ -276,17 +265,13 @@ async function upsert() {
</template>
</QInput>
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Undeductible VAT')"
v-model="data.deductibleExpenseFk"
clearable
clear-icon="close"
/>
</div>
<div class="col">
<QInput
:label="t('Document')"
v-model="data.dmsFk"
@ -334,9 +319,7 @@ async function upsert() {
</template>
</QInput>
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Entry date')"
v-model="data.bookEntried"
@ -366,8 +349,6 @@ async function upsert() {
</QIcon>
</template>
</QInput>
</div>
<div class="col">
<QInput
:label="t('Accounted date')"
v-model="data.booked"
@ -398,9 +379,7 @@ async function upsert() {
</template>
</QInput>
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Currency')"
v-model="data.currencyFk"
@ -408,8 +387,6 @@ async function upsert() {
option-value="id"
option-label="code"
/>
</div>
<div class="col">
<VnSelect
v-if="companiesRef"
:label="t('Company')"
@ -419,16 +396,12 @@ async function upsert() {
option-label="code"
/>
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox
:label="t('invoiceIn.summary.booked')"
v-model="data.isBooked"
/>
</div>
<div class="col"></div>
</div>
</template>
</FormModel>
<QDialog ref="editDmsRef">

View File

@ -0,0 +1,52 @@
<script setup>
import { reactive, ref, onMounted, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue';
import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
const { t } = useI18n();
const emit = defineEmits(['onDataSaved']);
const route = useRoute();
const identifierInputRef = ref(null);
const intrastatFormData = reactive({});
const onDataSaved = (formData, requestResponse) => {
emit('onDataSaved', formData, requestResponse);
};
onMounted(async () => {
await nextTick();
identifierInputRef.value.focus();
});
</script>
<template>
<FormModelPopup
:url-update="`Items/${route.params.id}/createIntrastat`"
model="itemGenus"
:title="t('createIntrastatForm.title')"
:form-initial-data="intrastatFormData"
@on-data-saved="onDataSaved"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
ref="identifierInputRef"
:label="t('createIntrastatForm.identifier')"
type="number"
v-model.number="data.intrastatId"
:required="true"
/>
<VnInput
:label="t('createIntrastatForm.description')"
v-model="data.description"
:required="true"
/>
</VnRow>
</template>
</FormModelPopup>
</template>

View File

@ -1 +1,233 @@
<template>Item 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 VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import FilterItemForm from 'src/components/FilterItemForm.vue';
import CreateIntrastatForm from './CreateIntrastatForm.vue';
const route = useRoute();
const { t } = useI18n();
const itemTypesOptions = ref([]);
const itemsWithNameOptions = ref([]);
const intrastatsOptions = ref([]);
const expensesOptions = ref([]);
const onIntrastatCreated = (response, formData) => {
intrastatsOptions.value = [...intrastatsOptions.value, response];
formData.intrastatFk = response.id;
};
</script>
<template>
<FetchData
url="ItemTypes"
:filter="{
fields: ['id', 'name', 'categoryFk'],
include: 'category',
order: 'name ASC',
}"
@on-fetch="(data) => (itemTypesOptions = data)"
auto-load
/>
<FetchData
url="Items/withName"
:filter="{
fields: ['id', 'name'],
order: 'id DESC',
}"
@on-fetch="(data) => (itemsWithNameOptions = data)"
auto-load
/>
<FetchData
url="Intrastats"
:filter="{
fields: ['id', 'description'],
order: 'description ASC',
}"
@on-fetch="(data) => (intrastatsOptions = data)"
auto-load
/>
<FetchData
url="Expenses"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
@on-fetch="(data) => (expensesOptions = data)"
auto-load
/>
<FormModel
:url="`Items/${route.params.id}`"
:url-update="`Items/${route.params.id}`"
model="item"
auto-load
:clear-store-on-unmount="false"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
:label="t('basicData.type')"
v-model="data.typeFk"
:options="itemTypesOptions"
option-value="id"
option-label="name"
hide-selected
map-options
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>
{{ scope.opt?.category?.name }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInput :label="t('basicData.reference')" v-model="data.comment" />
<VnInput :label="t('basicData.relevancy')" v-model="data.relevancy" />
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput :label="t('basicData.stems')" v-model="data.stems" />
<VnInput
:label="t('basicData.multiplier')"
v-model="data.stemMultiplier"
/>
<VnSelectDialog
:label="t('basicData.generic')"
v-model="data.genericFk"
:options="itemsWithNameOptions"
option-value="id"
option-label="name"
map-options
hide-selected
action-icon="filter_alt"
>
<template #form>
<FilterItemForm
url="Items/withName"
@item-selected="data.genericFk = $event"
/>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption> #{{ scope.opt?.id }} </QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectDialog>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelectDialog
:label="t('basicData.intrastat')"
v-model="data.intrastatFk"
:options="intrastatsOptions"
option-value="id"
option-label="description"
map-options
hide-selected
>
<template #form>
<CreateIntrastatForm
@on-data-saved="
(_, requestResponse) =>
onIntrastatCreated(requestResponse, data)
"
/>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.description }}</QItemLabel>
<QItemLabel caption> #{{ scope.opt?.id }} </QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectDialog>
<div class="col">
<VnSelect
:label="t('basicData.expense')"
v-model="data.expenseFk"
:options="expensesOptions"
option-value="id"
option-label="name"
hide-selected
map-options
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
:label="t('basicData.weightByPiece')"
v-model.number="data.weightByPiece"
:min="0"
type="number"
/>
<VnInput
:label="t('basicData.boxUnits')"
v-model.number="data.packingOut"
:min="0"
type="number"
/>
<VnInput
:label="t('basicData.recycledPlastic')"
v-model.number="data.recycledPlastic"
:min="0"
type="number"
/>
<VnInput
:label="t('basicData.nonRecycledPlastic')"
v-model.number="data.nonRecycledPlastic"
:min="0"
type="number"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<QCheckbox v-model="data.isActive" :label="t('basicData.isActive')" />
<QCheckbox v-model="data.hasKgPrice" :label="t('basicData.hasKgPrice')" />
<div>
<QCheckbox
v-model="data.isFragile"
:label="t('basicData.isFragile')"
class="q-mr-sm"
/>
<QIcon name="info" class="cursor-pointer" size="xs">
<QTooltip max-width="300px">
{{ t('basicData.isFragileTooltip') }}
</QTooltip>
</QIcon>
</div>
<div>
<QCheckbox
v-model="data.isPhotoRequested"
:label="t('basicData.isPhotoRequested')"
class="q-mr-sm"
/>
<QIcon name="info" class="cursor-pointer" size="xs">
<QTooltip>
{{ t('basicData.isPhotoRequestedTooltip') }}
</QTooltip>
</QIcon>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<QInput
:label="t('basicData.description')"
type="textarea"
v-model="data.description"
fill-input
/>
</VnRow>
</template>
</FormModel>
</template>

View File

@ -2,7 +2,7 @@
import { onMounted, computed, onUnmounted, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { dateRange } from 'src/filters';
import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
@ -29,28 +29,19 @@ const exprBuilder = (param, value) => {
case 'landed':
return {
'tr.landed': {
between: getDateRange(value),
between: dateRange(value),
},
};
}
};
const dateRange = reactive({
const datedRange = reactive({
from: null,
to: null,
});
const getDateRange = (val) => {
const minHour = new Date(val);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(val);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
};
const from = computed({
get: () => dateRange.from,
get: () => datedRange.from,
set: (val) => {
updateFrom(val);
updateFilter();
@ -58,7 +49,7 @@ const from = computed({
});
const to = computed({
get: () => dateRange.to,
get: () => datedRange.to,
set: (val) => {
updateTo(val);
updateFilter();
@ -173,17 +164,17 @@ const fetchItemLastEntries = async () => {
const updateFrom = async (date) => {
date.setHours(0, 0, 0, 0);
dateRange.from = date.toISOString();
datedRange.from = date.toISOString();
};
const updateTo = async (date) => {
date.setHours(23, 59, 59, 59);
dateRange.to = date.toISOString();
datedRange.to = date.toISOString();
};
const updateFilter = async () => {
arrayData.store.userFilter.where.landed = {
between: [dateRange.from, dateRange.to],
between: [datedRange.from, datedRange.to],
};
await fetchItemLastEntries();
};

View File

@ -0,0 +1,279 @@
<script setup>
import { onMounted, ref, computed, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import { toDateFormat } from 'src/filters/date.js';
import { dashIfEmpty } from 'src/filters';
import { useArrayData } from 'src/composables/useArrayData';
import useNotify from 'src/composables/useNotify.js';
import { useVnConfirm } from 'composables/useVnConfirm';
import axios from 'axios';
import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore();
const route = useRoute();
const { t } = useI18n();
const { notify } = useNotify();
const { openConfirmationModal } = useVnConfirm();
const rowsSelected = ref([]);
const parkingsOptions = ref([]);
const shelvingsOptions = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
case 'parking':
case 'shelving':
case 'label':
case 'packing':
case 'itemFk':
return { [param]: value };
}
};
const params = reactive({ itemFk: route.params.id });
const arrayData = useArrayData('ItemShelvings', {
url: 'ItemShelvingPlacementSupplyStocks',
userParams: params,
exprBuilder: exprBuilder,
});
const rows = computed(() => arrayData.store.data || []);
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await arrayData.addFilter({ filter: null, params });
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const columns = computed(() => [
{
label: t('shelvings.created'),
name: 'created',
field: 'created',
align: 'left',
sortable: true,
columnFilter: null,
format: (val) => toDateFormat(val),
},
{
label: t('shelvings.item'),
name: 'item',
field: 'itemFk',
align: 'left',
sortable: true,
columnFilter: null,
},
{
label: t('shelvings.concept'),
name: 'concept',
align: 'left',
sortable: true,
columnFilter: null,
},
{
label: t('shelvings.parking'),
name: 'parking',
field: 'parking',
align: 'left',
sortable: true,
format: (val) => dashIfEmpty(val),
columnFilter: {
component: VnSelect,
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: parkingsOptions.value,
'option-value': 'code',
'option-label': 'code',
dense: true,
},
},
},
{
label: t('shelvings.shelving'),
name: 'shelving',
field: 'shelving',
align: 'left',
sortable: true,
format: (val) => dashIfEmpty(val),
columnFilter: {
component: VnSelect,
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: shelvingsOptions.value,
'option-value': 'code',
'option-label': 'code',
dense: true,
},
},
},
{
label: t('shelvings.label'),
name: 'label',
align: 'left',
sortable: true,
format: (_, row) => (row.stock / row.packing).toFixed(2),
columnFilter: {
component: VnInput,
type: 'text',
filterParamKey: 'label',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('shelvings.packing'),
field: 'packing',
name: 'packing',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
},
]);
const totalLabels = computed(() =>
rows.value.reduce((acc, row) => acc + row.stock / row.packing, 0).toFixed(2)
);
const removeLines = async () => {
try {
const itemShelvingIds = rowsSelected.value.map((row) => row.itemShelvingFk);
await axios.post('ItemShelvings/deleteItemShelvings', { itemShelvingIds });
rowsSelected.value = [];
notify('shelvings.shelvingsRemoved', 'positive');
await arrayData.fetch({ append: false });
} catch (err) {
console.error('Error removing lines', err);
}
};
onMounted(async () => {
await arrayData.fetch({ append: false });
});
</script>
<template>
<FetchData
url="parkings"
:filter="{ fields: ['code'], order: 'code ASC' }"
auto-load
@on-fetch="(data) => (parkingsOptions = data)"
/>
<FetchData
url="shelvings"
:filter="{ fields: ['code'], order: 'code ASC' }"
auto-load
@on-fetch="(data) => (shelvingsOptions = data)"
/>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#st-data">
<div class="q-pa-md q-mr-lg q-ma-xs" style="border: 2px solid #222">
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('shelvings.total') }}
</span>
</QCardSection>
<QCardSection class="column items-center" horizontal>
<div>
<span class="details-label"
>{{ t('shelvings.totalLabels') }}
</span>
<span>: {{ totalLabels }}</span>
</div></QCardSection
>
</div>
</Teleport>
<Teleport to="#st-actions">
<QBtn
color="primary"
icon="delete"
:disabled="!rowsSelected.length"
@click="
openConfirmationModal(
t('shelvings.removeConfirmTitle'),
t('shelvings.removeConfirmSubtitle'),
removeLines
)
"
>
<QTooltip>
{{ t('shelvings.removeLines') }}
</QTooltip>
</QBtn>
</Teleport>
</template>
<QPage class="column items-center q-pa-md">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
selection="multiple"
v-model:selected="rowsSelected"
:no-data-label="t('globals.noResults')"
>
<template #top-row="{ cols }">
<QTr>
<QTd />
<QTd
v-for="(col, index) in cols"
:key="index"
style="max-width: 100px"
>
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #body-cell-concept="{ row }">
<QTd @click.stop>
<span class="link">{{ row.longName }}</span>
<ItemDescriptorProxy :id="row.itemFk" />
</QTd>
</template>
</QTable>
</QPage>
</template>

View File

@ -1 +1,191 @@
<template>Item tags (CREAR CUANDO SE DESARROLLE EL MODULO DE ITEMS)</template>
<script setup>
import { ref } 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';
import FetchData from 'components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import axios from 'axios';
const route = useRoute();
const { t } = useI18n();
const itemTagsRef = ref(null);
const tagOptions = ref([]);
const valueOptionsMap = ref(new Map());
const getSelectedTagValues = async (tag) => {
try {
if (!tag.tagFk && tag.tag.isFree) return;
const filter = {
fields: ['value'],
order: 'value ASC',
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get(`Tags/${tag.tagFk}/filterValue`, {
params,
});
valueOptionsMap.value.set(tag.tagFk, data);
} catch (err) {
console.error('Error getting selected tag values');
}
};
const onItemTagsFetched = async (itemTags) => {
(itemTags || []).forEach((tag) => {
getSelectedTagValues(tag);
});
};
const handleTagSelected = (rows, index, tag) => {
rows[index].tag = tag;
rows[index].tagFk = tag.id;
rows[index].value = null;
getSelectedTagValues(rows[index]);
};
const getHighestPriority = (rows) => {
let max = 0;
rows.forEach((tag) => {
if (tag.priority > max) max = tag.priority;
});
return max + 1;
};
const insertTag = (rows) => {
itemTagsRef.value.insert();
itemTagsRef.value.formData[itemTagsRef.value.formData.length - 1].priority =
getHighestPriority(rows);
};
</script>
<template>
<FetchData
url="Tags"
:filter="{ fields: ['id', 'name', 'isFree', 'sourceTable'] }"
@on-fetch="(data) => (tagOptions = data)"
auto-load
/>
<div class="full-width flex justify-center">
<QPage class="card-width q-pa-lg">
<CrudModel
ref="itemTagsRef"
data-key="ItemTags"
model="ItemTags"
url="ItemTags"
update-url="Tags/onSubmit"
:data-required="{
$index: undefined,
itemFk: route.params.id,
priority: undefined,
tag: {
isFree: undefined,
value: undefined,
name: undefined,
},
tagFk: undefined,
}"
:default-remove="false"
:filter="{
fields: ['id', 'itemFk', 'tagFk', 'value', 'priority'],
where: { itemFk: route.params.id },
order: 'priority ASC',
include: {
relation: 'tag',
scope: {
fields: ['id', 'name', 'isFree', 'sourceTable'],
},
},
}"
auto-load
@on-fetch="onItemTagsFetched"
>
<template #body="{ rows, validate }">
<QCard class="q-pl-lg q-py-md">
<VnRow
v-for="(row, index) in rows"
:key="index"
class="row q-gutter-md q-mb-md"
>
<VnSelect
:label="t('itemTags.tag')"
:options="tagOptions"
:model-value="row.tag"
option-label="name"
hide-selected
@update:model-value="
($event) => handleTagSelected(rows, index, $event)
"
:required="true"
:rules="validate('itemTag.tagFk')"
/>
<VnSelect
v-if="row.tag?.isFree === false"
:key="row.tagFk"
:label="t('Value')"
v-model="row.value"
:options="valueOptionsMap.get(row.tagFk)"
option-label="value"
option-value="value"
emit-value
use-input
class="col"
:is-clearable="false"
:required="false"
:rules="validate('itemTag.tagFk')"
/>
<VnInput
v-else-if="
row.tag?.isFree || row.tag?.isFree == undefined
"
v-model="row.value"
:label="t('itemTags.value')"
:is-clearable="false"
style="width: 100%"
/>
<VnInput
:label="t('itemTags.relevancy')"
type="number"
v-model="row.priority"
:required="true"
:rules="validate('itemTag.priority')"
/>
<div class="col-1 row justify-center items-center">
<QIcon
@click="itemTagsRef.remove([row])"
class="fill-icon-on-hover"
color="primary"
name="delete"
size="sm"
>
<QTooltip>
{{ t('itemTags.removeTag') }}
</QTooltip>
</QIcon>
</div>
</VnRow>
<VnRow>
<QIcon
@click="insertTag(rows)"
class="cursor-pointer"
:disable="!validRow"
color="primary"
name="add"
size="sm"
>
<QTooltip>
{{ t('itemTags.addTag') }}
</QTooltip>
</QIcon>
</VnRow>
</QCard>
</template>
</CrudModel>
</QPage>
</div>
</template>

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, computed, onMounted, onBeforeMount, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
@ -9,7 +9,7 @@ import VnInput from 'src/components/common/VnInput.vue';
import ItemRequestDenyForm from './ItemRequestDenyForm.vue';
import ItemRequestFilter from './ItemRequestFilter.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnSelect from 'components/common/VnSelect.vue';
import { useStateStore } from 'stores/useStateStore';
import { useArrayData } from 'composables/useArrayData';
import { toDateFormat } from 'src/filters/date';
@ -21,7 +21,7 @@ import axios from 'axios';
const { t } = useI18n();
const { notify } = useNotify();
const stateStore = useStateStore();
const workersOptions = ref([]);
let filterParams = ref({});
const denyFormRef = ref(null);
const denyRequestId = ref(null);
@ -147,6 +147,7 @@ const confirmRequest = async (request) => {
const params = {
itemFk: request.itemFk,
quantity: request.saleQuantity,
attenderFk: request.attenderFk,
};
const { data } = await axios.post(
@ -206,6 +207,13 @@ onBeforeMount(() => {
</script>
<template>
<FetchData
url="Workers"
:filter="{ where: { role: 'buyer' } }"
order="id"
@on-fetch="(data) => (workersOptions = data)"
auto-load
/>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#searchbar">
<VnSearchbar
@ -278,8 +286,14 @@ onBeforeMount(() => {
</template>
<template #body-cell-attender="{ row }">
<QTd>
<QBtn flat dense color="primary"> {{ row.attenderName }}</QBtn>
<WorkerDescriptorProxy :id="row.attenderFk" />
<VnSelect
v-model="row.attenderFk"
:options="workersOptions"
hide-selected
option-label="firstName"
option-value="id"
dense
/>
</QTd>
</template>
<template #body-cell-item="{ row }">

View File

@ -1,7 +1,7 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { dateRange } from 'src/filters';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue';
@ -44,15 +44,6 @@ const exprBuilder = (param, value) => {
}
};
const dateRange = (value) => {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
};
const add = (paramsObj, key) => {
if (paramsObj[key] === undefined) {
paramsObj[key] = 1;
@ -201,6 +192,7 @@ const decrement = (paramsObj, key) => {
v-model="params.from"
@update:model-value="searchFn()"
is-outlined
emit-date-format
/>
</QItemSection>
</QItem>
@ -211,6 +203,7 @@ const decrement = (paramsObj, key) => {
v-model="params.to"
@update:model-value="searchFn()"
is-outlined
emit-date-format
/>
</QItemSection>
</QItem>

View File

@ -1,3 +1,17 @@
shelvings:
created: Created
item: Item
concept: Concept
parking: Parking
shelving: Shelving
label: Label
packing: Packing
total: Total
totalLabels: Total labels
removeLines: Remove selected lines
shelvingsRemoved: ItemShelvings removed
removeConfirmTitle: Selected lines will be deleted
removeConfirmSubtitle: Are you sure you want to continue?
itemDiary:
date: Date
id: Id
@ -11,6 +25,30 @@ itemDiary:
showBefore: Show what's before the inventory
since: Since
warehouse: Warehouse
basicData:
type: Type
reference: Reference
relevancy: Relevancy
stems: Stems
multiplier: Multiplier
generic: Generic
intrastat: Intrastat
expense: Expense
weightByPiece: Weight/Piece
boxUnits: Units/Box
recycledPlastic: Recycled plastic
nonRecycledPlastic: Non recycled plastic
description: Description
isActive: Active
hasKgPrice: Price in kg
isFragile: Fragile
isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...)
isPhotoRequested: Do photo
isPhotoRequestedTooltip: This item does need a photo
createIntrastatForm:
title: New intrastat
identifier: Identifier
description: Description
tax:
country: Country
class: Class
@ -34,3 +72,9 @@ lastEntries:
package: Package
freight: Freight
comission: Comission
itemTags:
removeTag: Remove tag
addTag: Add tag
tag: Tag
value: Value
relevancy: Relevancy

View File

@ -1,3 +1,17 @@
shelvings:
created: Creado
item: Artículo
concept: Concepto
parking: Parking
shelving: Matrícula
label: Etiqueta
packing: Packing
total: Total
totalLabels: Total etiquetas
removeLines: Eliminar líneas seleccionadas
shelvingsRemoved: Carros eliminados
removeConfirmTitle: Las líneas seleccionadas serán eliminadas
removeConfirmSubtitle: ¿Seguro que quieres continuar?
itemDiary:
date: Fecha
id: Id
@ -11,6 +25,30 @@ itemDiary:
showBefore: Mostrar lo anterior al inventario
since: Desde
warehouse: Almacén
basicData:
type: Tipo
reference: Referencia
relevancy: Relevancia
stems: Tallos
multiplier: Multiplicador
generic: Genérico
intrastat: Intrastat
expense: Gasto
weightByPiece: Peso (gramos)/tallo
boxUnits: Unidades/caja
recycledPlastic: Plástico reciclado
nonRecycledPlastic: Plástico no reciclado
description: Descripción
isActive: Activo
hasKgPrice: Precio en kg
isFragile: Frágil
isFragileTooltip: Se muestra en la web app, que este artículo no puede viajar (coronas, palmas, ...)
isPhotoRequested: Hacer foto
isPhotoRequestedTooltip: Este artículo necesita una foto
createIntrastatForm:
title: Nuevo intrastat
identifier: Identificador
description: Descripción
tax:
country: País
class: Clase
@ -34,3 +72,9 @@ lastEntries:
package: Embalaje
freight: Porte
comission: Comisión
itemTags:
removeTag: Quitar etiqueta
addTag: Añadir etiqueta
tag: Etiqueta
value: Valor
relevancy: Relevancia

View File

@ -0,0 +1,88 @@
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import SalesClientTable from './SalesClientsTable.vue';
import SalesOrdersTable from './SalesOrdersTable.vue';
import SalesTicketsTable from './SalesTicketsTable.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
const { t } = useI18n();
const stateStore = useStateStore();
const expanded = ref(true);
onMounted(async () => {
stateStore.rightDrawer = true;
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#searchbar">
<VnSearchbar
data-key="SalesMonitorTickets"
url="SalesMonitors/salesFilter"
:redirect="false"
:label="t('searchBar.label')"
:info="t('searchBar.info')"
/>
</Teleport>
</template>
<QPage class="column items-center q-pa-md">
<QCard class="full-width q-mb-lg">
<QExpansionItem v-model="expanded" dense :duration="150">
<template v-if="!expanded" #header>
<div class="row full-width">
<span class="flex col text-body1">
{{ t('salesMonitor.clientsOnWebsite') }}
</span>
<span class="flex col q-ml-xl text-body1">
{{ t('salesMonitor.recentOrderActions') }}
</span>
</div>
</template>
<template #default>
<div class="expansion-tables-container">
<QCardSection class="col">
<span class="flex col q-mb-sm text-body1">
{{ t('salesMonitor.clientsOnWebsite') }}
</span>
<SalesClientTable />
</QCardSection>
<QCardSection class="col">
<span class="flex col q-mb-sm text-body1">
{{ t('salesMonitor.recentOrderActions') }}
</span>
<SalesOrdersTable />
</QCardSection>
</div>
</template>
</QExpansionItem>
</QCard>
<QCard class="full-width q-mb-lg">
<QItem class="justify-between">
<QItemLabel class="slider-container">
<span class="text-body1"
>{{ t('salesMonitor.ticketsMonitor') }}
</span>
<QCardSection class="col"><SalesTicketsTable /> </QCardSection>
</QItemLabel>
</QItem>
</QCard>
</QPage>
</template>
<style lang="scss" scoped>
.expansion-tables-container {
display: flex;
border-top: 1px solid $color-spacer;
@media (max-width: $breakpoint-md-max) {
flex-direction: column;
}
}
</style>

View File

@ -0,0 +1,18 @@
<script setup>
import LeftMenu from 'src/components/LeftMenu.vue';
import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit text-grey-8">
<LeftMenu />
</QScrollArea>
</QDrawer>
<QPageContainer>
<RouterView></RouterView>
</QPageContainer>
</template>

View File

@ -0,0 +1,219 @@
<script setup>
import { ref, computed, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import { toDateFormat } from 'src/filters/date.js';
import { dashIfEmpty } from 'src/filters';
const { t } = useI18n();
const paginateRef = ref(null);
const workersActiveOptions = ref([]);
const clientsOptions = ref([]);
const from = ref(Date.vnNew());
const to = ref(Date.vnNew());
const dateRange = computed(() => {
const minHour = new Date(from.value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(to.value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
});
const filter = reactive({
where: {
'v.stamp': {
between: dateRange.value,
},
},
});
watch(dateRange, (val) => {
filter.where['v.stamp'].between = val;
paginateRef.value.fetch();
});
function exprBuilder(param, value) {
switch (param) {
case 'clientFk':
return { [`c.id`]: value };
case 'salesPersonFk':
return { [`c.${param}`]: value };
}
}
const params = reactive({});
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await paginateRef.value.addFilter(null, params);
} catch (err) {
console.error('Error applying column filter', err);
}
};
const columns = computed(() => [
{
label: t('salesClientsTable.date'),
name: 'date',
field: 'dated',
align: 'left',
columnFilter: null,
sortable: true,
format: (val) => toDateFormat(val),
},
{
label: t('salesClientsTable.hour'),
name: 'hour',
field: 'hour',
align: 'left',
sortable: true,
},
{
label: t('salesClientsTable.salesPerson'),
name: 'salesPerson',
field: 'salesPerson',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'salesPersonFk',
type: 'select',
filterValue: null,
attrs: {
options: workersActiveOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
format: (val) => dashIfEmpty(val),
},
{
label: t('salesClientsTable.client'),
field: 'clientName',
name: 'client',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'clientFk',
type: 'select',
filterValue: null,
attrs: {
options: clientsOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
format: (val) => dashIfEmpty(val),
},
]);
</script>
<template>
<FetchData
url="Workers/activeWithInheritedRole"
:filter="{
fields: ['id', 'nickname'],
order: 'nickname ASC',
where: { role: 'salesPerson' },
}"
auto-load
@on-fetch="(data) => (workersActiveOptions = data)"
/>
<FetchData
url="Clients"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
auto-load
@on-fetch="(data) => (clientsOptions = data)"
/>
<QCard style="max-height: 380px; overflow-y: scroll">
<VnPaginate
ref="paginateRef"
data-key="SalesMonitorClients"
url="SalesMonitors/clientsFilter"
:order="['dated DESC', 'hour DESC']"
:limit="6"
:expr-builder="exprBuilder"
:user-params="params"
:filter="filter"
:offset="50"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width"
:no-data-label="t('globals.noResults')"
>
<template #top>
<VnInputDate
:label="t('salesClientsTable.from')"
dense
emit-date-format
v-model="from"
class="q-mr-lg"
style="width: 150px"
/>
<VnInputDate
:label="t('salesClientsTable.to')"
dense
emit-date-format
v-model="to"
style="width: 150px"
/>
</template>
<template #top-row="{ cols }">
<QTr>
<QTd
v-for="(col, index) in cols"
:key="index"
style="max-width: 100px"
>
<VnSelect
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
@update:model-value="applyColumnFilter(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #body-cell-salesPerson="{ row }">
<QTd>
<span class="link">{{ row.salesPerson }}</span>
<WorkerDescriptorProxy :id="row.salesPersonFk" dense />
</QTd>
</template>
<template #body-cell-client="{ row }">
<QTd>
<span class="link">{{ row.clientName }}</span>
<CustomerDescriptorProxy :id="row.clientFk" />
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
</QCard>
</template>

View File

@ -0,0 +1,203 @@
<script setup>
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import { toDateFormat, toDateTimeFormat } from 'src/filters/date.js';
import { toCurrency } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm';
import axios from 'axios';
const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm();
const router = useRouter();
const paginateRef = ref(null);
const workersActiveOptions = ref([]);
const clientsOptions = ref([]);
const selectedRows = ref([]);
const dateRange = (value) => {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
};
function exprBuilder(param, value) {
switch (param) {
case 'date_send':
return {
[`o.date_send`]: {
between: dateRange(value),
},
};
case 'clientFk':
return { [`c.id`]: value };
case 'salesPersonFk':
return { [`c.${param}`]: value };
}
}
const columns = computed(() => [
{
label: t('salesOrdersTable.date'),
name: 'date',
field: 'dated',
align: 'left',
columnFilter: null,
},
{
label: t('salesOrdersTable.client'),
name: 'client',
align: 'left',
sortable: true,
},
{
label: t('salesOrdersTable.salesPerson'),
name: 'salesPerson',
align: 'left',
sortable: true,
},
]);
const getBadgeColor = (date) => {
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const orderLanded = new Date(date);
orderLanded.setHours(0, 0, 0, 0);
const difference = today - orderLanded;
if (difference == 0) return 'warning';
if (difference < 0) return 'success';
if (difference > 0) return 'alert';
};
const removeOrders = async () => {
try {
const selectedIds = selectedRows.value.map((row) => row.id);
const params = { deletes: selectedIds };
await axios.post('SalesMonitors/deleteOrders', params);
selectedRows.value = [];
await paginateRef.value.fetch();
} catch (err) {
console.error('Error deleting orders', err);
}
};
const redirectToOrderSummary = (orderId) => {
const url = `#/order/${orderId}/summary`;
window.open(url, '_blank');
};
</script>
<template>
<FetchData
url="Workers/activeWithInheritedRole"
:filter="{
fields: ['id', 'nickname'],
order: 'nickname ASC',
where: { role: 'salesPerson' },
}"
auto-load
@on-fetch="(data) => (workersActiveOptions = data)"
/>
<FetchData
url="Clients"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
auto-load
@on-fetch="(data) => (clientsOptions = data)"
/>
<QCard style="max-height: 380px; overflow-y: scroll">
<VnPaginate
ref="paginateRef"
data-key="SalesMonitorOrders"
url="SalesMonitors/ordersFilter"
order="date_make DESC"
:limit="6"
:expr-builder="exprBuilder"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
selection="multiple"
v-model:selected="selectedRows"
:pagination="{ rowsPerPage: 0 }"
:no-data-label="t('globals.noResults')"
@row-click="(_, row) => redirectToOrderSummary(row.id)"
>
<template #top>
<QBtn
v-if="selectedRows.length > 0"
icon="delete"
size="md"
color="primary"
@click="
openConfirmationModal(
t('salesOrdersTable.deleteConfirmTitle'),
t('salesOrdersTable.deleteConfirmMessage'),
removeOrders
)
"
>
<QTooltip>{{ t('salesOrdersTable.delete') }}</QTooltip>
</QBtn>
</template>
<template #body-cell-date="{ row }">
<QTd>
<QBadge
:color="getBadgeColor(row.date_send)"
text-color="black"
class="q-ma-none q-mb-md"
dense
style="font-size: 14px"
>
{{ toDateFormat(row.date_send) }}
</QBadge>
<div>{{ toDateTimeFormat(row.date_make) }}</div>
</QTd>
</template>
<template #body-cell-client="{ row }">
<QTd>
<div class="q-mb-md">
<span class="link">{{ row.clientName }}</span>
<CustomerDescriptorProxy :id="row.clientFk" />
</div>
<span> {{ row.agencyName }}</span>
</QTd>
</template>
<template #body-cell-salesPerson="{ row }">
<QTd>
<div class="q-mb-md">
<span class="link">{{ row.salesPerson }}</span>
<WorkerDescriptorProxy :id="row.salesPersonFk" dense />
</div>
<span>{{ toCurrency(row.import) }}</span>
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
</QCard>
</template>
<style lang="scss" scoped>
.q-td {
color: gray;
}
</style>

View File

@ -0,0 +1,636 @@
<script setup>
import { ref, computed, onMounted, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import TicketSummary from 'src/pages/Ticket/Card/TicketSummary.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { toDateFormat, toTimeFormat } from 'src/filters/date.js';
import { toCurrency } from 'src/filters';
const DEFAULT_AUTO_REFRESH = 1000;
const { t } = useI18n();
const autoRefresh = ref(false);
const router = useRouter();
const paginateRef = ref(null);
const workersActiveOptions = ref([]);
const provincesOptions = ref([]);
const statesOptions = ref([]);
const zonesOptions = ref([]);
const visibleColumns = ref([]);
const allColumnNames = ref([]);
const { viewSummary } = useSummaryDialog();
const dateRange = (value) => {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
};
function exprBuilder(param, value) {
switch (param) {
case 'stateFk':
return { 'ts.stateFk': value };
case 'salesPersonFk':
return { 'c.salesPersonFk': value };
case 'provinceFk':
return { 'a.provinceFk': value };
case 'theoreticalHour':
return { 'z.hour': value };
case 'practicalHour':
return { 'zed.etc': value };
case 'shippedDate':
return {
't.shipped': {
between: dateRange(value),
},
};
case 'nickname':
return { [`t.nickname`]: { like: `%${value}%` } };
case 'zoneFk':
case 'totalWithVat':
return { [`t.${param}`]: value };
}
}
const filter = { order: ['totalProblems DESC'] };
let params = reactive({});
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await paginateRef.value.addFilter(null, params);
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select' || col.columnFilter.type === 'date'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const fetchParams = ($params = {}) => {
const excludedParams = ['search', 'clientFk', 'orderFk', 'refFk', 'scopeDays'];
const hasExcludedParams = excludedParams.some((param) => {
return $params && $params[param] != undefined;
});
const hasParams = Object.entries($params).length;
if (!hasParams || !hasExcludedParams) $params.scopeDays = 1;
if (typeof $params.scopeDays === 'number') {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = new Date(from.getTime());
to.setDate(to.getDate() + $params.scopeDays);
to.setHours(23, 59, 59, 999);
Object.assign($params, { from, to });
}
return { tableOrder: 'totalProblems DESC', ...$params };
};
const columns = computed(() => [
{
label: t('salesTicketsTable.problems'),
name: 'problems',
align: 'left',
sortable: true,
columnFilter: null,
},
{
label: t('salesTicketsTable.identifier'),
name: 'identifier',
field: 'id',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('salesTicketsTable.client'),
name: 'client',
align: 'left',
field: 'nickname',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('salesTicketsTable.salesPerson'),
name: 'salesPerson',
field: 'userName',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'salesPersonFk',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: workersActiveOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{
label: t('salesTicketsTable.date'),
name: 'date',
align: 'left',
sortable: true,
columnFilter: {
component: VnInputDate,
filterParamKey: 'shippedDate',
type: 'date',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('salesTicketsTable.theoretical'),
name: 'theoretical',
field: 'zoneLanding',
align: 'left',
sortable: true,
format: (val) => toTimeFormat(val),
},
{
label: t('salesTicketsTable.practical'),
name: 'practical',
field: 'practicalHour',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('salesTicketsTable.preparation'),
name: 'preparation',
field: 'shipped',
align: 'left',
sortable: true,
format: (val) => toTimeFormat(val),
},
{
label: t('salesTicketsTable.province'),
name: 'province',
field: 'province',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'provinceFk',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: provincesOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{
label: t('salesTicketsTable.state'),
name: 'state',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'stateFk',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: statesOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{
label: t('salesTicketsTable.isFragile'),
name: 'isFragile',
field: 'isFragile',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('salesTicketsTable.zone'),
name: 'zone',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'zoneFk',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: zonesOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{
label: t('salesTicketsTable.total'),
name: 'total',
field: 'totalWithVat',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
name: 'rowActions',
align: 'left',
sortable: true,
},
]);
const getBadgeAttrs = (date) => {
let today = Date.vnNew();
today.setHours(0, 0, 0, 0);
let timeTicket = new Date(date);
timeTicket.setHours(0, 0, 0, 0);
let comparation = today - timeTicket;
if (comparation == 0) return { color: 'warning', 'text-color': 'black' };
if (comparation < 0) return { color: 'success', 'text-color': 'black' };
return { color: 'transparent', 'text-color': 'white' };
};
let refreshTimer = null;
const autoRefreshHandler = (value) => {
if (value)
refreshTimer = setInterval(() => paginateRef.value.fetch(), DEFAULT_AUTO_REFRESH);
else {
clearInterval(refreshTimer);
refreshTimer = null;
}
};
const redirectToTicketSummary = (id) => {
router.push({ name: 'TicketSummary', params: { id } });
};
const stateColors = {
notice: 'info',
success: 'positive',
warning: 'warning',
alert: 'negative',
};
const totalPriceColor = (ticket) => {
const total = parseInt(ticket.totalWithVat);
if (total > 0 && total < 50) return 'warning';
};
const formatShippedDate = (date) => {
if (!date) return '-';
const split1 = date.split('T');
const [year, month, day] = split1[0].split('-');
const _date = new Date(year, month - 1, day);
return toDateFormat(_date);
};
onMounted(async () => {
const filteredColumns = columns.value.filter((col) => col.name !== 'rowActions');
allColumnNames.value = filteredColumns.map((col) => col.name);
params = fetchParams();
await paginateRef.value.addFilter(null, params);
});
</script>
<template>
<FetchData
url="Workers/activeWithInheritedRole"
:filter="{
fields: ['id', 'nickname'],
order: 'nickname ASC',
where: { role: 'salesPerson' },
}"
auto-load
@on-fetch="(data) => (workersActiveOptions = data)"
/>
<FetchData
url="Provinces"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
auto-load
@on-fetch="(data) => (provincesOptions = data)"
/>
<FetchData
url="States"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
auto-load
@on-fetch="(data) => (statesOptions = data)"
/>
<FetchData
url="Zones"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
auto-load
@on-fetch="(data) => (zonesOptions = data)"
/>
<VnPaginate
ref="paginateRef"
data-key="SalesMonitorTickets"
url="SalesMonitors/salesFilter"
:filter="filter"
:limit="20"
:expr-builder="exprBuilder"
:user-params="params"
:offset="50"
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
:visible-columns="visibleColumns"
:no-data-label="t('globals.noResults')"
@row-click="(_, row) => redirectToTicketSummary(row.id)"
>
<template #top>
<TableVisibleColumns
:all-columns="allColumnNames"
table-code="ticketsMonitor"
labels-traductions-path="salesTicketsTable"
@on-config-saved="visibleColumns = [...$event, 'rowActions']"
/>
<QCheckbox
v-model="autoRefresh"
:label="t('salesTicketsTable.autoRefresh')"
@update:model-value="autoRefreshHandler"
/>
</template>
<template #top-row="{ cols }">
<QTr>
<QTd
v-for="(col, index) in cols"
:key="index"
style="max-width: 100px"
>
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
style="padding-bottom: 0"
/>
</QTd>
</QTr>
</template>
<template #body-cell-problems="{ row }">
<QTd class="q-gutter-x-sm">
<QIcon
v-if="row.isTaxDataChecked === 0"
name="vn:no036"
color="primary"
size="xs"
>
<QTooltip>{{
t('salesTicketsTable.noVerifiedData')
}}</QTooltip>
</QIcon>
<QIcon
v-if="row.hasTicketRequest"
name="vn:buyrequest"
color="primary"
size="xs"
>
<QTooltip>{{
t('salesTicketsTable.purchaseRequest')
}}</QTooltip>
</QIcon>
<QIcon
v-if="row.itemShortage"
name="vn:unavailable"
color="primary"
size="xs"
>
<QTooltip>{{ t('salesTicketsTable.notVisible') }}</QTooltip>
</QIcon>
<QIcon
v-if="row.isFreezed"
name="vn:frozen"
color="primary"
size="xs"
>
<QTooltip>{{ t('salesTicketsTable.clientFrozen') }}</QTooltip>
</QIcon>
<QIcon
v-if="row.risk"
name="vn:risk"
:color="row.hasHighRisk ? 'negative' : 'primary'"
size="xs"
>
<QTooltip
>{{ t('salesTicketsTable.risk') }}:
{{ row.risk }}</QTooltip
>
</QIcon>
<QIcon
v-if="row.hasComponentLack"
name="vn:components"
color="primary"
size="xs"
>
<QTooltip>{{
t('salesTicketsTable.componentLack')
}}</QTooltip>
</QIcon>
<QIcon
v-if="row.isTooLittle"
name="vn:isTooLittle"
color="primary"
size="xs"
>
<QTooltip>{{ t('salesTicketsTable.tooLittle') }}</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-identifier="{ row }">
<QTd>
<span class="link" @click.stop.prevent>{{ row.id }}</span>
<TicketDescriptorProxy :id="row.id" />
</QTd>
</template>
<template #body-cell-client="{ row }">
<QTd @click.stop.prevent>
<span class="link">{{ row.nickname }}</span>
<CustomerDescriptorProxy :id="row.clientFk" />
</QTd>
</template>
<template #body-cell-salesPerson="{ row }">
<QTd @click.stop.prevent>
<span class="link">{{ row.userName }}</span>
<WorkerDescriptorProxy :id="row.salesPersonFk" />
</QTd>
</template>
<template #body-cell-date="{ row }">
<QTd>
<QBadge
v-bind="getBadgeAttrs(row.shippedDate)"
class="q-ma-none"
dense
style="font-size: 14px"
>
{{ formatShippedDate(row.shippedDate) }}
</QBadge>
</QTd>
</template>
<template #body-cell-state="{ row }">
<QTd @click.stop.prevent>
<div v-if="row.refFk">
<span class="link">{{ row.refFk }}</span>
<InvoiceOutDescriptorProxy :id="row.invoiceOutId" />
</div>
<QBadge
v-else
:color="stateColors[row.classColor] || 'transparent'"
:text-color="stateColors[row.classColor] ? 'black' : 'white'"
dense
style="font-size: 14px"
>
{{ row.state }}
</QBadge>
</QTd>
</template>
<template #body-cell-isFragile="{ row }">
<QTd>
<QIcon
v-if="row.isFragile"
name="local_bar"
color="primary"
size="sm"
>
<QTooltip>{{ t('salesTicketsTable.isFragile') }}</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-zone="{ row }">
<QTd @click.stop.prevent>
<span class="link">{{ row.zoneName }}</span>
<ZoneDescriptorProxy :id="row.zoneFk" />
</QTd>
</template>
<template #body-cell-total="{ row }">
<QTd>
<QBadge
:color="totalPriceColor(row) || 'transparent'"
:text-color="totalPriceColor(row) ? 'black' : 'white'"
dense
style="font-size: 14px"
>
{{ toCurrency(row.totalWithVat) }}
</QBadge>
</QTd>
</template>
<template #body-cell-rowActions="{ row }">
<QTd @click.stop.prevent>
<QBtn
icon="vn:lines"
color="primary"
size="md"
class="q-mr-sm"
flat
dense
:to="{ name: 'TicketSale', params: { id: row.id } }"
>
<QTooltip>{{ t('salesTicketsTable.goToLines') }}</QTooltip>
</QBtn>
<QBtn
icon="preview"
color="primary"
size="md"
dense
flat
@click="viewSummary(row.id, TicketSummary)"
>
<QTooltip>{{ t('salesTicketsTable.preview') }}</QTooltip>
</QBtn>
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
</template>

View File

@ -0,0 +1,45 @@
salesMonitor:
clientsOnWebsite: Clients on website
recentOrderActions: Recent order actions
ticketsMonitor: Tickets monitor
salesClientsTable:
from: From
to: To
date: Date
hour: Hour
salesPerson: Salesperson
client: Client
salesOrdersTable:
delete: Delete
date: Date
client: Client
salesPerson: Salesperson
deleteConfirmTitle: Delete selected elements
deleteConfirmMessage: All the selected elements will be deleted. Are you sure you want to continue?
salesTicketsTable:
autoRefresh: Auto-refresh
problems: Problems
noVerifiedData: No verified data
notVisible: Not visible
purchaseRequest: Purchase request
clientFrozen: Client frozen
risk: Risk
componentLack: Component lack
tooLittle: Ticket too little
identifier: Identifier
client: Client
salesPerson: Salesperson
date: Date
theoretical: Theoretical
practical: Practical
province: Province
state: State
isFragile: Is fragile
zone: Zone
goToLines: Go to lines
preview: Preview
total: Total
preparation: Preparation
searchBar:
label: Search tickets
info: Search tickets by id or alias

View File

@ -0,0 +1,45 @@
salesMonitor:
clientsOnWebsite: Clientes activos en la web
recentOrderActions: Acciones recientes en pedidos
ticketsMonitor: Monitor de tickets
salesClientsTable:
from: Desde
to: Hasta
date: Fecha
hour: Hora
salesPerson: Comercial
client: Cliente
salesOrdersTable:
delete: Eliminar
date: Fecha
client: Cliente
salesPerson: Comercial
deleteConfirmTitle: Eliminar los elementos seleccionados
deleteConfirmMessage: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar?
salesTicketsTable:
autoRefresh: Auto-refresco
problems: Problemas
noVerifiedData: Sin datos comprobados
notVisible: No visible
purchaseRequest: Petición de compra
clientFrozen: Cliente congelado
risk: Riesgo
componentLack: Faltan componentes
tooLittle: Ticket demasiado pequeño
identifier: Identificador
client: Cliente
salesPerson: Comercial
date: Fecha
theoretical: Teórica
practical: Práctica
province: Provincia
state: Estado
isFragile: Es frágil
zone: Zona
goToLines: Ir a líneas
preview: Vista previa
total: Total
preparation: Preparación
searchBar:
label: Buscar tickets
info: Buscar tickets por identificador o alias

View File

@ -144,7 +144,6 @@ const orderFilter = {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('order.form.clientFk')"
v-model="data.clientFk"
@ -166,8 +165,6 @@ const orderFilter = {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnSelect
:label="t('order.form.addressFk')"
v-model="data.addressFk"
@ -192,10 +189,8 @@ const orderFilter = {
</QItem>
</template>
</VnSelect>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate
placeholder="dd-mm-aaa"
:label="t('order.form.landed')"
@ -204,10 +199,8 @@ const orderFilter = {
() => fetchAgencyList(data.landed, data.addressFk)
"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('order.form.agencyModeFk')"
v-model="data.agencyModeFk"
@ -218,7 +211,6 @@ const orderFilter = {
:disable="!agencyList?.length"
>
</VnSelect>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -123,7 +123,6 @@ const onSave = (data, response) => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Worker')"
v-model="data.workerFk"
@ -146,8 +145,6 @@ const onSave = (data, response) => {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnSelect
:label="t('Vehicle')"
v-model="data.vehicleFk"
@ -159,10 +156,8 @@ const onSave = (data, response) => {
use-input
:input-debounce="0"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Agency')"
v-model="data.agencyModeFk"
@ -174,39 +169,24 @@ const onSave = (data, response) => {
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.kmStart" :label="t('Km Start')" clearable />
<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>
<div class="col flex items-center">
<QCheckbox
size="sm"
@ -218,7 +198,6 @@ const onSave = (data, response) => {
</VnRow>
</template>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.description"
:label="t('Description')"
@ -226,7 +205,6 @@ const onSave = (data, response) => {
:rows="3"
clearable
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -41,34 +41,23 @@ const onSave = (data, response) => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput v-model="data.name" :label="t('Roadmap')" clearable />
</div>
<div class="col">
<VnInputDate v-model="data.etd" :label="t('ETD date')" />
</div>
<div class="col">
<VnInputTime v-model="data.etd" :label="t('ETD hour')" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.tractorPlate"
:label="t('Tractor plate')"
clearable
/>
</div>
<div class="col">
<VnInput
v-model="data.trailerPlate"
:label="t('Trailer plate')"
clearable
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('Carrier')"
v-model="data.supplierFk"
@ -90,36 +79,23 @@ const onSave = (data, response) => {
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnInput
v-model="data.price"
:label="t('Price')"
type="number"
clearable
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.driverName"
:label="t('Driver name')"
clearable
/>
</div>
<div class="col">
<VnInput v-model="data.driverName" :label="t('Driver name')" clearable />
<VnInput v-model="data.phone" :label="t('Phone')" clearable />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.observations"
:label="t('Observations')"
clearable
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -31,19 +31,13 @@ const onSave = (data, response) => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.name"
:label="t('Roadmap')"
clearable
/>
</div>
<div class="col">
<VnInputDate v-model="data.etd" :label="t('ETD date')" />
</div>
<div class="col">
<VnInputTime v-model="data.etd" :label="t('ETD hour')" />
</div>
</VnRow>
</template>
</FormModel>

View File

@ -280,7 +280,7 @@ function navigateToRouteSummary(event, row) {
<div class="table-actions">
<QIcon
name="preview"
size="xs"
size="sm"
color="primary"
@click.stop="
viewSummary(

View File

@ -422,7 +422,7 @@ const openTicketsDialog = (id) => {
<div class="flex items-center no-wrap table-actions">
<QIcon
name="vn:ticketAdd"
size="xs"
size="sm"
color="primary"
class="cursor-pointer"
@click="openTicketsDialog(props?.row?.id)"
@ -431,7 +431,7 @@ const openTicketsDialog = (id) => {
</QIcon>
<QIcon
name="preview"
size="xs"
size="sm"
color="primary"
@click="
viewSummary(props?.row?.id, RouteSummary)

View File

@ -242,7 +242,7 @@ function navigateToRoadmapSummary(event, row) {
<div class="flex items-center table-actions">
<QIcon
name="preview"
size="xs"
size="sm"
color="primary"
@click.stop="
viewSummary(

View File

@ -86,14 +86,11 @@ const onSave = (shelving, newShelving) => {
>
<template #form="{ data, validate, filter }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.code"
:label="t('shelving.basicData.code')"
:rules="validate('Shelving.code')"
/>
</div>
<div class="col">
<QSelect
v-model="data.parkingFk"
:options="parkingList"
@ -109,24 +106,19 @@ const onSave = (shelving, newShelving) => {
:rules="validate('Shelving.parkingFk')"
:input-debounce="0"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.priority"
type="number"
:label="t('shelving.basicData.priority')"
:rules="validate('Shelving.priority')"
/>
</div>
<div class="col">
<QCheckbox
v-model="data.isRecyclable"
:label="t('shelving.basicData.recyclable')"
:rules="validate('Shelving.isRecyclable')"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -99,7 +99,6 @@ const setWireTransfer = async () => {
:key="index"
class="row q-gutter-md q-mb-md"
>
<div class="col">
<VnInput :label="t('supplier.accounts.iban')" v-model="row.iban">
<template #append>
<QIcon name="info" class="cursor-info">
@ -109,8 +108,6 @@ const setWireTransfer = async () => {
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnSelectDialog
:label="t('worker.create.bankEntity')"
v-model="row.bankEntityFk"
@ -139,8 +136,6 @@ const setWireTransfer = async () => {
</QItem>
</template>
</VnSelectDialog>
</div>
<div class="col">
<VnInput
:label="t('supplier.accounts.beneficiary')"
v-model="row.beneficiary"
@ -155,7 +150,6 @@ const setWireTransfer = async () => {
</QIcon>
</template>
</VnInput>
</div>
<div class="col-1 row justify-center items-center">
<QIcon
name="delete"

View File

@ -79,21 +79,16 @@ function handleLocation(data, location) {
>
<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">
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
@ -102,22 +97,18 @@ function handleLocation(data, location) {
@update:model-value="
(location) => handleLocation(data, location)
"
></VnLocation>
</div>
>
</VnLocation>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.phone"
:label="t('supplier.addresses.phone')"
/>
</div>
<div class="col">
<VnInput
v-model="data.mobile"
:label="t('supplier.addresses.mobile')"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -54,7 +54,6 @@ const redirectToCreateView = () => {
: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">
@ -62,49 +61,36 @@ const redirectToCreateView = () => {
</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"

View File

@ -46,7 +46,6 @@ const onDataSaved = () => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('supplier.agencyTerms.agencyFk')"
v-model="data.agencyFk"
@ -56,53 +55,40 @@ const onDataSaved = () => {
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>
<div class="col">
<QInput
:label="t('supplier.agencyTerms.m3Price')"
v-model.number="data.m3Price"
type="number"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('supplier.agencyTerms.routePrice')"
v-model.number="data.routePrice"
type="number"
/>
</div>
<div class="col">
<QInput
:label="t('supplier.agencyTerms.minimumKm')"
v-model.number="data.minimumKm"
type="number"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -30,15 +30,12 @@ const workersOptions = ref([]);
>
<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">
<VnSelect
:label="t('supplier.basicData.workerFk')"
v-model="data.workerFk"
@ -67,37 +64,28 @@ const workersOptions = ref([]);
</QItem>
</template>
</VnSelect>
</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
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -37,7 +37,6 @@ const formatPayDems = (data) => {
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('supplier.billingData.payMethodFk')"
v-model="data.payMethodFk"
@ -47,8 +46,6 @@ const formatPayDems = (data) => {
hide-selected
:rules="validate('supplier.payMethodFk')"
/>
</div>
<div class="col">
<VnSelect
:label="t('supplier.billingData.payDemFk')"
v-model="data.payDemFk"
@ -58,16 +55,13 @@ const formatPayDems = (data) => {
hide-selected
: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>

View File

@ -42,34 +42,25 @@ const insertRow = () => {
class="border q-pa-md q-mb-md"
>
<VnRow class="row q-gutter-md">
<div class="col">
<VnInput
input-name-focusable
: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"
@ -77,7 +68,6 @@ const insertRow = () => {
fill-input
autogrow
/>
</div>
<div class="col-1 row justify-center items-center">
<QIcon
name="delete"

View File

@ -57,30 +57,23 @@ function handleLocation(data, location) {
>
<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">
<VnSelect
:label="t('supplier.fiscalData.sageTaxTypeFk')"
v-model="data.sageTaxTypeFk"
@ -90,10 +83,8 @@ function handleLocation(data, location) {
hide-selected
map-options
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('supplier.fiscalData.sageWithholdingFk')"
v-model="data.sageWithholdingFk"
@ -103,8 +94,6 @@ function handleLocation(data, location) {
hide-selected
map-options
/>
</div>
<div class="col">
<VnSelect
:label="t('supplier.fiscalData.sageTransactionTypeFk')"
v-model="data.sageTransactionTypeFk"
@ -114,10 +103,8 @@ function handleLocation(data, location) {
hide-selected
map-options
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('supplier.fiscalData.supplierActivityFk')"
v-model="data.supplierActivityFk"
@ -127,26 +114,20 @@ function handleLocation(data, location) {
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>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
@ -155,7 +136,6 @@ function handleLocation(data, location) {
@update:model-value="(location) => handleLocation(data, location)"
>
</VnLocation>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col flex justify-around">

View File

@ -45,7 +45,6 @@ const redirectToSupplierFiscalData = (_, responseData) => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.name"
:label="t('supplier.create.supplierName')"
@ -53,7 +52,6 @@ const redirectToSupplierFiscalData = (_, responseData) => {
newSupplierForm.name = newSupplierForm.name.toUpperCase()
"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -0,0 +1 @@
<template>Ticket sale</template>

View File

@ -29,13 +29,7 @@ const agenciesOptions = ref([]);
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.ref"
:label="t('travel.basicData.reference')"
/>
</div>
<div class="col">
<VnInput v-model="data.ref" :label="t('travel.basicData.reference')" />
<VnSelect
:label="t('travel.basicData.agency')"
v-model="data.agencyModeFk"
@ -45,24 +39,18 @@ const agenciesOptions = ref([]);
map-options
hide-selected
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate
v-model="data.shipped"
:label="t('travel.basicData.shipped')"
/>
</div>
<div class="col">
<VnInputDate
v-model="data.landed"
:label="t('travel.basicData.landed')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('travel.basicData.warehouseOut')"
v-model="data.warehouseOutFk"
@ -72,8 +60,6 @@ const agenciesOptions = ref([]);
map-options
hide-selected
/>
</div>
<div class="col">
<VnSelect
:label="t('travel.basicData.warehouseIn')"
v-model="data.warehouseInFk"
@ -83,21 +69,16 @@ const agenciesOptions = ref([]);
map-options
hide-selected
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox
:label="t('travel.basicData.delivered')"
v-model="data.isDelivered"
/>
</div>
<div class="col">
<QCheckbox
:label="t('travel.basicData.received')"
v-model="data.isReceived"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -236,7 +236,6 @@ const onThermographCreated = async (data) => {
</Teleport>
<QCard class="q-pa-lg">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectDialog
:label="t('travel.thermographs.thermograph')"
v-model="thermographForm.thermographId"
@ -252,22 +251,16 @@ const onThermographCreated = async (data) => {
/>
</template>
</VnSelectDialog>
</div>
<div class="col">
<VnInput
v-model="thermographForm.state"
:label="t('travel.thermographs.state')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="thermographForm.reference"
:label="t('travel.thermographs.reference')"
/>
</div>
<div class="col">
<VnSelect
:label="t('travel.thermographs.type')"
v-model="thermographForm.dmsTypeId"
@ -275,10 +268,8 @@ const onThermographCreated = async (data) => {
option-value="id"
option-label="name"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('travel.thermographs.company')"
v-model="thermographForm.companyId"
@ -286,8 +277,6 @@ const onThermographCreated = async (data) => {
option-value="id"
option-label="code"
/>
</div>
<div class="col">
<VnSelect
:label="t('travel.thermographs.warehouse')"
v-model="thermographForm.warehouseId"
@ -295,20 +284,16 @@ const onThermographCreated = async (data) => {
option-value="id"
option-label="name"
/>
</div>
</VnRow>
<VnRow v-if="viewAction === 'edit'" class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('globals.description')"
type="textarea"
v-model="thermographForm.description"
fill-input
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
ref="inputFileRef"
:label="t('travel.thermographs.file')"
@ -333,7 +318,6 @@ const onThermographCreated = async (data) => {
</QIcon>
</template>
</QFile>
</div>
</VnRow>
</QCard>
</QForm>

View File

@ -68,10 +68,7 @@ const redirectToTravelBasicData = (_, { id }) => {
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput v-model="data.ref" :label="t('globals.reference')" />
</div>
<div class="col">
<VnSelect
:label="t('globals.agency')"
v-model="data.agencyModeFk"
@ -80,21 +77,12 @@ const redirectToTravelBasicData = (_, { id }) => {
option-label="name"
hide-selected
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate
v-model="data.shipped"
:label="t('globals.shipped')"
/>
</div>
<div class="col">
<VnInputDate v-model="data.shipped" :label="t('globals.shipped')" />
<VnInputDate :label="t('globals.landed')" v-model="data.landed" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('globals.wareHouseOut')"
v-model="data.warehouseOutFk"
@ -103,8 +91,6 @@ const redirectToTravelBasicData = (_, { id }) => {
option-label="name"
hide-selected
/>
</div>
<div class="col">
<VnSelect
:label="t('globals.wareHouseIn')"
v-model="data.warehouseInFk"
@ -113,7 +99,6 @@ const redirectToTravelBasicData = (_, { id }) => {
option-label="name"
hide-selected
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -92,7 +92,6 @@ function filterType(val, update) {
<QForm @submit="onSubmit()" @reset="onReset()" class="q-pa-sm">
<QCard class="q-pa-md">
<div class="row q-col-gutter-md">
<div class="col">
<QInput
filled
v-model="wagon.label"
@ -101,8 +100,6 @@ function filterType(val, update) {
min="0"
:rules="[(val) => !!val || t('wagon.warnings.labelNotEmpty')]"
/>
</div>
<div class="col">
<VnInput
filled
v-model="wagon.plate"
@ -110,9 +107,7 @@ function filterType(val, update) {
:rules="[(val) => !!val || t('wagon.warnings.plateNotEmpty')]"
/>
</div>
</div>
<div class="row q-col-gutter-md">
<div class="col">
<QInput
filled
v-model="wagon.volume"
@ -123,8 +118,6 @@ function filterType(val, update) {
(val) => !!val || t('wagon.warnings.volumeNotEmpty'),
]"
/>
</div>
<div class="col">
<QSelect
filled
v-model="wagon.typeFk"
@ -157,7 +150,6 @@ function filterType(val, update) {
</template>
</QSelect>
</div>
</div>
</QCard>
<div class="q-mt-md">
<QBtn :label="t('wagon.type.submit')" type="submit" color="primary" />

View File

@ -27,7 +27,6 @@ const workerFilter = {
},
{ relation: 'sip', scope: { fields: ['extension', 'secret'] } },
{ relation: 'department', scope: { include: { relation: 'department' } } },
{ relation: 'client', scope: { fields: ['phone'] } },
],
};
const workersFilter = {
@ -87,11 +86,6 @@ const maritalStatus = [
:label="t('Mobile extension')"
clearable
/>
<VnInput
v-model="data.client.phone"
:label="t('Personal phone')"
clearable
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
@ -163,7 +157,6 @@ es:
Last name: Apellidos
Business phone: Teléfono de empresa
Mobile extension: Extensión móvil
Personal phone: Teléfono personal
Boss: Jefe
Marital status: Estado civil
Married: Casado/a

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, watch } from 'vue';
import { nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
@ -10,10 +10,13 @@ import WorkerCalendarItem from 'pages/Worker/Card/WorkerCalendarItem.vue';
import { useStateStore } from 'stores/useStateStore';
import axios from 'axios';
import { useRouter } from 'vue-router';
const router = useRouter();
const stateStore = useStateStore();
const route = useRoute();
const { t } = useI18n();
const workerIsFreelance = ref();
const WorkerFreelanceRef = ref();
const workerCalendarFilterRef = ref(null);
const workerCalendarRef = ref(null);
const absenceType = ref(null);
@ -128,6 +131,7 @@ const refreshData = () => {
updateYearHolidays();
updateContractHolidays();
getAbsences();
WorkerFreelanceRef.value.fetch();
};
const onDeletedEvent = (timestamp) => {
@ -136,12 +140,21 @@ const onDeletedEvent = (timestamp) => {
if (festiveEventsMap.value[timestamp])
eventsMap.value[timestamp] = festiveEventsMap.value[timestamp];
};
const activeContractRef = ref(null);
watch(
() => router.currentRoute.value.params.id,
async () => {
await nextTick();
await activeContractRef.value.fetch();
}
);
watch([year, businessFk], () => refreshData());
</script>
<template>
<FetchData
ref="activeContractRef"
:url="`Workers/${route.params.id}/activeContract`"
@on-fetch="onFetchActiveContract"
auto-load
@ -151,6 +164,12 @@ watch([year, businessFk], () => refreshData());
@on-fetch="(data) => (isSubordinate = data)"
auto-load
/>
<FetchData
:url="`Workers/${route.params.id}`"
@on-fetch="(data) => (workerIsFreelance = data.isFreelance)"
ref="WorkerFreelanceRef"
auto-load
/>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#actions-append">
<div class="row q-gutter-x-sm">
@ -181,7 +200,7 @@ watch([year, businessFk], () => refreshData());
</QScrollArea>
</QDrawer>
<QPage class="column items-center">
<QCard v-if="!hasWorkCenter">
<QCard v-if="workerIsFreelance">
<QCardSection class="text-center">
{{ t('Autonomous worker') }}
</QCardSection>

View File

@ -1,15 +1,12 @@
<script setup>
import VnCard from 'components/common/VnCard.vue';
import WorkerDescriptor from './WorkerDescriptor.vue';
const filter = { where: {} };
</script>
<template>
<VnCard
data-key="Worker"
custom-url="Workers/Summary"
base-url="Workers"
:descriptor="WorkerDescriptor"
:filter="filter"
searchbar-data-key="WorkerList"
searchbar-url="Workers/filter"
searchbar-label="Search worker"

View File

@ -31,12 +31,31 @@ const entityId = computed(() => {
});
const worker = ref();
const filter = computed(() => {
return { where: { id: entityId.value } };
});
const filter = {
include: [
{
relation: 'user',
scope: {
fields: ['email', 'name', 'nickname'],
},
},
{
relation: 'department',
scope: {
include: [
{
relation: 'department',
},
],
},
},
{
relation: 'sip',
},
],
};
const sip = ref(null);
watch(
() => [worker.value?.sip?.extension, state.get('extension')],
([newWorkerSip, newStateExtension], [oldWorkerSip, oldStateExtension]) => {
@ -62,7 +81,7 @@ const setData = (entity) => {
<CardDescriptor
module="Worker"
data-key="workerData"
url="Workers/summary"
:url="`Workers/${entityId}`"
:filter="filter"
:title="data.title"
:subtitle="data.subtitle"

View File

@ -0,0 +1,64 @@
<script setup>
import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useRole } from 'src/composables/useRole';
import FormModel from 'components/FormModel.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import { useArrayData } from 'src/composables/useArrayData';
import FetchData from 'components/FetchData.vue';
const { hasAny } = useRole();
const { t } = useI18n();
const fetchData = ref();
const originaLockerId = ref();
const lockers = ref([]);
const { store } = useArrayData('Worker');
const entityId = computed(() => useRoute().params.id);
const filter = computed(() => ({
where: {
gender: store.data?.sex,
or: [{ workerFk: null }, { workerFk: entityId.value }],
},
}));
const save = async (data) => {
const lockerId = data.id ?? originaLockerId.value;
const workerFk = lockerId == originaLockerId.value ? null : entityId.value;
await axios.patch(`Lockers/${lockerId}`, { workerFk });
};
const init = async (data) => {
await fetchData.value.fetch();
originaLockerId.value = data.id;
};
</script>
<template>
<FetchData
ref="fetchData"
url="Lockers/codes"
:filter="filter"
@on-fetch="(data) => (lockers = data)"
/>
<FormModel
:url="`Workers/${entityId}/locker`"
model="worker"
auto-load
:save-fn="save"
@on-fetch="init"
>
<template #form="{ data }">
<VnSelect
:label="t('Locker')"
v-model="data.id"
:options="lockers"
option-label="code"
option-value="id"
hide-selected
:readonly="!hasAny(['productionBoss', 'hr'])"
/>
</template>
</FormModel>
</template>

View File

@ -57,13 +57,11 @@ const updateModelValue = (data) => {
>
<template #form="{}">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('worker.summary.sipExtension')"
v-model="extension"
@update:model-value="updateModelValue"
/>
</div>
</VnRow>
</template>
</FormModel>

View File

@ -27,18 +27,46 @@ onMounted(async () => {
workerUrl.value = (await getUrl('')) + `worker/${entityId.value}/`;
});
const filter = computed(() => {
return { where: { id: entityId.value } };
});
const filter = {
include: [
{
relation: 'user',
scope: {
fields: ['email', 'name', 'nickname', 'roleFk'],
include: {
relation: 'role',
scope: {
fields: ['name'],
},
},
},
},
{
relation: 'department',
scope: {
include: {
relation: 'department',
scope: {
fields: ['name'],
},
},
},
},
{
relation: 'boss',
},
{
relation: 'client',
},
{
relation: 'sip',
},
],
};
</script>
<template>
<CardSummary
data-key="workerData"
ref="summary"
:url="`Workers/summary`"
:filter="filter"
>
<CardSummary ref="summary" :url="`Workers/${entityId}`" :filter="filter">
<template #header="{ entity }">
<div>{{ entity.id }} - {{ entity.firstName }} {{ entity.lastName }}</div>
</template>
@ -75,6 +103,12 @@ const filter = computed(() => {
<VnLinkPhone :phone-number="worker.phone" />
</template>
</VnLv>
<VnLv :value="worker.client?.phone">
<template #label>
{{ t('worker.summary.personalPhone') }}
<VnLinkPhone :phone-number="worker.client?.phone" />
</template>
</VnLv>
<VnLv :label="t('worker.summary.locker')" :value="worker.locker" />
</QCard>
<QCard class="vn-one">

View File

@ -108,92 +108,69 @@ onMounted(async () => {
>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.firstName"
:label="t('worker.create.name')"
:rules="validate('Worker.firstName')"
/>
</div>
<div class="col">
<VnInput
v-model="data.lastNames"
:label="t('worker.create.lastName')"
:rules="validate('Worker.lastNames')"
/>
</div>
<div class="col">
<VnInputDate
v-model="data.birth"
:label="t('worker.create.birth')"
:rules="validate('Worker.birth')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.fi"
:label="t('worker.create.fi')"
:rules="validate('Worker.fi')"
/>
</div>
<div class="col">
<VnInput
v-model="data.code"
:label="t('worker.create.code')"
:rules="validate('Worker.code')"
/>
</div>
<div class="col">
<VnInput
v-model="data.phone"
:label="t('worker.create.phone')"
:rules="validate('Worker.phone')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
v-model="data.location"
@update:model-value="
(location) => handleLocation(data, location)
"
@update:model-value="(location) => handleLocation(data, location)"
>
</VnLocation>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('worker.create.street')"
v-model="data.street"
:rules="validate('Worker.street')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.name"
:label="t('worker.create.webUser')"
:rules="validate('Worker.name')"
/>
</div>
<div class="col">
<VnInput
v-model="data.email"
:label="t('worker.create.personalEmail')"
:rules="validate('Worker.email')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('worker.create.company')"
v-model="data.companyFk"
@ -203,8 +180,6 @@ onMounted(async () => {
hide-selected
:rules="validate('Worker.company')"
/>
</div>
<div class="col">
<VnSelect
:label="t('worker.create.boss')"
v-model="data.bossFk"
@ -226,10 +201,8 @@ onMounted(async () => {
</QItem>
</template>
</VnSelect>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelect
:label="t('worker.create.payMethods')"
v-model="data.payMethodFk"
@ -240,9 +213,6 @@ onMounted(async () => {
hide-selected
:rules="validate('Worker.payMethodFk')"
/>
</div>
<div class="col">
<VnInput
v-model="data.iban"
:label="t('worker.create.iban')"
@ -250,13 +220,10 @@ onMounted(async () => {
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip>{{
t('components.iban_tooltip')
}}</QTooltip>
<QTooltip>{{ t('components.iban_tooltip') }}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
<VnSelectDialog
:label="t('worker.create.bankEntity')"
v-model="data.bankEntityFk"

View File

@ -1,2 +1,3 @@
Search worker: Buscar trabajador
You can search by worker id or name: Puedes buscar por id o nombre del trabajador
Locker: Taquilla

View File

@ -0,0 +1,125 @@
<script setup>
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { ref } from 'vue';
import FetchData from 'components/FetchData.vue';
import FormModel from 'src/components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import { QCheckbox } from 'quasar';
import VnInputTime from 'src/components/common/VnInputTime.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const route = useRoute();
const { t } = useI18n();
const agencyFilter = {
fields: ['id', 'name'],
order: 'name ASC',
limit: 30,
};
const agencyOptions = ref([]);
</script>
<template>
<FetchData
:filter="agencyFilter"
@on-fetch="(data) => (agencyOptions = data)"
auto-load
url="AgencyModes/isActive"
/>
<FormModel :url="`Zones/${route.params.id}`" auto-load model="zone">
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInput :label="t('Name')" clearable v-model="data.name" />
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
option-label="name"
option-value="id"
v-model="data.agencyModeFk"
:rules="validate('zone.agencyModeFk')"
:options="agencyOptions"
:label="t('Agency')"
emit-value
map-options
use-input
hide-bottom-space
/>
<VnInput
class="mw-10"
v-model="data.itemMaxSize"
:label="t('Max m³')"
clearable
type="number"
min="0"
/>
<VnInput
class="mw-10"
v-model="data.m3Max"
:label="t('Maximum m³')"
clearable
type="number"
min="0"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
v-model="data.travelingDays"
:label="t('Traveling days')"
clearable
type="number"
min="0"
/>
<VnInputTime v-model="data.hour" :label="t('Closing')" clearable />
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
v-model="data.price"
:label="t('Price')"
type="number"
min="0"
clearable
/>
<VnInput
v-model="data.bonus"
:label="t('Bonus')"
type="number"
min="0"
clearable
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput v-model="data.inflation" :label="t('Inflation')" clearable />
<QCheckbox
v-model="data.isVolumetric"
:label="t('Volumetric')"
:toggle-indeterminate="false"
/>
</VnRow>
</template>
</FormModel>
</template>
<style lang="scss" scoped>
.mw-10 {
max-width: 10vw;
}
</style>
<i18n>
es:
Name: Nombre
Agency: Agencia
Max : Medida máxima
Maximum : maximo
Traveling days: Dias de viaje
Closing: Cierre
Price: Precio
Bonus: Bonificación
Inflation: Inflación
Volumetric: Volumétrico
</i18n>

View File

View File

@ -1,6 +1,15 @@
<script setup>
import VnCard from 'components/common/VnCard.vue';
import ZoneDescriptor from './ZoneDescriptor.vue';
</script>
<template>
<VnCard data-key="Zone" base-url="Zones" />
<VnCard
data-key="Zone"
base-url="Zones"
:descriptor="ZoneDescriptor"
searchbar-data-key="ZoneList"
searchbar-url="Zones/filter"
searchbar-label="Search zones"
searchbar-info="You can search by zone reference"
/>
</template>

View File

@ -5,9 +5,11 @@ import { useI18n } from 'vue-i18n';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import ZoneDescriptorMenuItems from './ZoneDescriptorMenuItems.vue';
import { toTimeFormat } from 'src/filters/date';
import { toCurrency } from 'filters/index';
import useCardDescription from 'src/composables/useCardDescription';
import ZoneDescriptorMenuItems from './ZoneDescriptorMenuItems.vue';
const $props = defineProps({
id: {
@ -40,13 +42,6 @@ const data = ref(useCardDescription());
const setData = (entity) => {
data.value = useCardDescription(entity.ref, entity.id);
};
function extractHour(dateTime) {
const date = new Date(dateTime);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
</script>
<template>
@ -65,7 +60,7 @@ function extractHour(dateTime) {
flat
dense
size="md"
icon="preview"
icon="vn:zone"
color="white"
class="link"
:to="{ name: 'ZoneList' }"
@ -75,29 +70,15 @@ function extractHour(dateTime) {
</QTooltip>
</QBtn>
</template>
<!-- <template #menu="{ entity }">
<template #menu="{ entity }">
<ZoneDescriptorMenuItems :zone="entity" />
</template> -->
</template>
<template #body="{ entity }">
{{ console.log('entity', entity) }}
<VnLv :label="t('Agency')" :value="entity.agencyMode.name" />
<VnLv :label="t('Closing hour')" :value="extractHour(entity.hour)" />
<VnLv :label="t('zoneing days')" :value="entity.zoneingDays" />
<VnLv :label="t('Price')" :value="entity.price" />
<VnLv :label="t('Bonus')" :value="entity.bonus" />
<VnLv :label="t('summary.agency')" :value="entity.agencyMode.name" />
<VnLv :label="t('summary.closeHour')" :value="toTimeFormat(entity.hour)" />
<VnLv :label="t('summary.travelingDays')" :value="entity.travelingDays" />
<VnLv :label="t('summary.price')" :value="toCurrency(entity.price)" />
<VnLv :label="t('summary.bonus')" :value="toCurrency(entity.bonus)" />
</template>
</CardDescriptor>
</template>
<i18n>
es:
Summary: Detalles
The zone will be deleted: El envío será eliminado
Do you want to delete this zone?: ¿Quieres eliminar este envío?
All zones with current agency: Todos los envíos con la agencia actual
Agency: Agencia
Closing hour: Hora de cierre
zoneing days: Días de viaje
Price: Precio
Bonus: Bonificación
</i18n>

View File

@ -0,0 +1 @@
<template>Zone Locations</template>

View File

@ -0,0 +1,6 @@
<script setup>
import VnLog from 'src/components/common/VnLog.vue';
</script>
<template>
<VnLog model="Zone" url="/ZoneLogs"></VnLog>
</template>

View File

@ -2,14 +2,16 @@
import { ref, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { dashIfEmpty } from 'src/filters';
import { getUrl } from 'src/composables/getUrl';
import VnLv from 'src/components/ui/VnLv.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import CardSummary from 'components/ui/CardSummary.vue';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import { getUrl } from 'src/composables/getUrl';
import { toCurrency } from 'filters/index';
import { toTimeFormat } from 'src/filters/date';
import axios from 'axios';
const route = useRoute();
const { t } = useI18n();
@ -23,71 +25,84 @@ const $props = defineProps({
const entityId = computed(() => $props.id || route.params.id);
const zoneUrl = ref();
onMounted(async () => {
zoneUrl.value = (await getUrl('')) + `zone/${entityId.value}/`;
const filter = computed(() => {
const filter = {
include: {
relation: 'agencyMode',
fields: ['name'],
},
where: {
id: entityId,
},
};
return filter;
});
const filter = computed(() => {
return { where: { id: entityId.value } };
const columns = computed(() => [
{
label: t('summary.name'),
name: 'name',
field: 'warehouse',
align: 'left',
format: (val) => val.name,
},
]);
const warehouses = ref([]);
const getWarehouses = async () => {
const params = {
filter: {
include: {
relation: 'warehouse',
fields: ['name'],
},
},
};
const { data } = await axios.get(`Zones/${entityId.value}/warehouses`, { params });
warehouses.value = data;
};
onMounted(async () => {
zoneUrl.value = (await getUrl('')) + `zone/${entityId.value}/`;
await getWarehouses();
});
</script>
<template>
<CardSummary
data-key="zoneData"
data-key="ZoneSummary"
ref="summary"
:url="`Zones/summary`"
url="Zones/findOne"
:filter="filter"
>
<template #header="{ entity }">
<div>{{ entity.id }} - {{ entity.firstName }} {{ entity.lastName }}</div>
<div>#{{ entity.id }} - {{ entity.name }}</div>
</template>
<template #body="{ entity: zone }">
<QCard class="vn-one">
<VnTitle
:url="zoneUrl + `basic-data`"
:text="t('zone.summary.basicData')"
/>
<VnLv :label="t('zone.card.name')" :value="zone.user?.nickname" />
<VnLv
:label="t('zone.list.department')"
:value="zone.department?.department?.name"
/>
<VnLv :label="t('zone.list.email')" :value="zone.user.email" copy />
<VnLv :label="t('zone.summary.boss')" link>
<template #value>
<VnUserLink
v-if="zone.boss"
:name="dashIfEmpty(zone.boss?.name)"
:zone-id="zone.bossFk"
/>
</template>
</VnLv>
<VnLv :value="zone.mobileExtension">
<template #label>
{{ t('zone.summary.phoneExtension') }}
<VnLinkPhone :phone-number="zone.mobileExtension" />
</template>
</VnLv>
<VnLv :value="zone.phone">
<template #label>
{{ t('zone.summary.entPhone') }}
<VnLinkPhone :phone-number="zone.phone" />
</template>
</VnLv>
<VnLv :label="t('zone.summary.locker')" :value="zone.locker" />
<VnTitle :url="zoneUrl + `basic-data`" :text="t('summary.basicData')" />
<VnLv :label="t('summary.agency')" :value="zone.agencyMode?.name" />
<VnLv :label="t('summary.price')" :value="toCurrency(zone.price)" />
<VnLv :label="t('summary.bonus')" :value="toCurrency(zone.bonus)" />
</QCard>
<QCard class="vn-one">
<VnTitle :text="t('zone.summary.userData')" />
<VnLv :label="t('zone.summary.userId')" :value="zone.user.id" />
<VnLv :label="t('zone.card.name')" :value="zone.user.nickname" />
<VnLv :label="t('zone.summary.role')" :value="zone.user.role.name" />
<VnLv :value="zone?.sip?.extension">
<template #label>
{{ t('zone.summary.sipExtension') }}
<VnLinkPhone :phone-number="zone?.sip?.extension" />
</template>
</VnLv>
<VnTitle :url="zoneUrl + `basic-data`" :text="t('summary.basicData')" />
<VnLv :label="t('summary.closeHour')" :value="toTimeFormat(zone.hour)" />
<VnLv :label="t('summary.travelingDays')" :value="zone.travelingDays" />
<QCheckbox
:label="t('summary.volumetric')"
v-model="zone.isVolumetric"
:disable="true"
/>
</QCard>
<QCard class="full-width">
<VnTitle :url="zoneUrl + `warehouses`" :text="t('summary.warehouse')" />
<QTable
:columns="columns"
:rows="warehouses"
:no-data-label="t('globals.noResults')"
/>
</QCard>
</template>
</CardSummary>

View File

@ -0,0 +1,53 @@
<script setup>
import { useRoute } from 'vue-router';
import VnPaginate from 'components/ui/VnPaginate.vue';
import CardList from 'components/CardList.vue';
import VnLv from 'components/ui/VnLv.vue';
const route = useRoute();
const deleteWarehouse = () => {
return true;
};
</script>
<template>
<QPage class="column items-center q-pa-md">
<div class="vn-card-list">
<VnPaginate
data-key="ZoneWarehouses"
:url="`Zones/${route.params.id}/warehouses`"
auto-load
>
<template #body="{ rows }">
<CardList
v-for="row of rows"
:key="row.id"
:title="row.name"
:id="row.id"
>
<template #list-items>
<VnLv :value="row.name" />
<QIcon
name="delete"
size="sm"
class="cursor-pointer"
color="primary"
@click="deleteWarehouse()"
>
<QTooltip>
{{ t('Remove row') }}
</QTooltip>
</QIcon>
</template>
</CardList>
</template>
</VnPaginate>
</div>
</QPage>
</template>
<i18n>
es:
Remove row: Eliminar fila
</i18n>

Some files were not shown because too many files have changed in this diff Show More