0
0
Fork 0

Merge remote-tracking branch 'origin/dev' into feature/ItemLog

This commit is contained in:
Javier Segarra 2024-04-26 08:34:05 +02:00
commit aa87d54f57
169 changed files with 13110 additions and 7689 deletions

View File

@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2420.01]
## [2418.01] ## [2418.01]
## [2416.01] - 2024-04-18 ## [2416.01] - 2024-04-18

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ import { reactive, ref, onMounted, nextTick } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from './FormModelPopup.vue'; import FormModelPopup from './FormModelPopup.vue';
@ -78,7 +78,7 @@ onMounted(async () => {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('country')" :label="t('country')"
v-model="data.countryFk" v-model="data.countryFk"
:options="countriesOptions" :options="countriesOptions"

View File

@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FormModelPopup from './FormModelPopup.vue'; import FormModelPopup from './FormModelPopup.vue';
import VnInputDate from './common/VnInputDate.vue'; import VnInputDate from './common/VnInputDate.vue';
@ -73,7 +73,7 @@ const onDataSaved = async (formData, requestResponse) => {
</span> </span>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Ticket')" :label="t('Ticket')"
:options="ticketsOptions" :options="ticketsOptions"
hide-selected hide-selected
@ -92,13 +92,13 @@ const onDataSaved = async (formData, requestResponse) => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
<span class="row items-center" style="max-width: max-content">{{ <span class="row items-center" style="max-width: max-content">{{
t('Or') t('Or')
}}</span> }}</span>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Client')" :label="t('Client')"
:options="clientsOptions" :options="clientsOptions"
hide-selected hide-selected
@ -114,7 +114,7 @@ const onDataSaved = async (formData, requestResponse) => {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Serial')" :label="t('Serial')"
:options="invoiceOutSerialsOptions" :options="invoiceOutSerialsOptions"
hide-selected hide-selected
@ -125,7 +125,7 @@ const onDataSaved = async (formData, requestResponse) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Area')" :label="t('Area')"
:options="taxAreasOptions" :options="taxAreasOptions"
hide-selected hide-selected

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FormModelPopup from './FormModelPopup.vue'; import FormModelPopup from './FormModelPopup.vue';
@ -48,7 +48,7 @@ const onDataSaved = (dataSaved) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Province')" :label="t('Province')"
:options="provincesOptions" :options="provincesOptions"
hide-selected hide-selected

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import CreateNewCityForm from './CreateNewCityForm.vue'; import CreateNewCityForm from './CreateNewCityForm.vue';
import CreateNewProvinceForm from './CreateNewProvinceForm.vue'; import CreateNewProvinceForm from './CreateNewProvinceForm.vue';
@ -138,7 +138,7 @@ const onProvinceCreated = async ({ name }, formData) => {
</VnSelectDialog> </VnSelectDialog>
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Country')" :label="t('Country')"
:options="countriesOptions" :options="countriesOptions"
hide-selected hide-selected

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FormModelPopup from './FormModelPopup.vue'; import FormModelPopup from './FormModelPopup.vue';
@ -48,7 +48,7 @@ const onDataSaved = (dataSaved) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Autonomy')" :label="t('Autonomy')"
:options="autonomiesOptions" :options="autonomiesOptions"
hide-selected hide-selected

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FormModelPopup from './FormModelPopup.vue'; import FormModelPopup from './FormModelPopup.vue';
@ -64,7 +64,7 @@ const onDataSaved = (dataSaved) => {
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Model')" :label="t('Model')"
:options="thermographsModels" :options="thermographsModels"
hide-selected hide-selected
@ -78,7 +78,7 @@ const onDataSaved = (dataSaved) => {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-xl"> <VnRow class="row q-gutter-md q-mb-xl">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Warehouse')" :label="t('Warehouse')"
:options="warehousesOptions" :options="warehousesOptions"
hide-selected hide-selected
@ -89,7 +89,7 @@ const onDataSaved = (dataSaved) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Temperature')" :label="t('Temperature')"
:options="temperaturesOptions" :options="temperaturesOptions"
hide-selected hide-selected

View File

@ -2,7 +2,7 @@
import { reactive, computed, ref } from 'vue'; import { reactive, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
@ -293,7 +293,7 @@ const makeRequest = async () => {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Orientation')" :label="t('Orientation')"
:options="viewportTypes" :options="viewportTypes"
hide-selected hide-selected

View File

@ -1,9 +1,12 @@
<script setup> <script setup>
import { ref, reactive } from 'vue'; import { ref, markRaw } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnRow from 'components/ui/VnRow.vue';
import { QCheckbox } from 'quasar';
import axios from 'axios'; import axios from 'axios';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
@ -28,11 +31,16 @@ const $props = defineProps({
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify(); const { notify } = useNotify();
const formData = reactive({ const inputs = {
field: null, input: markRaw(VnInput),
newValue: null, number: markRaw(VnInput),
}); date: markRaw(VnInputDate),
checkbox: markRaw(QCheckbox),
select: markRaw(VnSelect),
};
const newValue = ref(null);
const selectedField = ref(null);
const closeButton = ref(null); const closeButton = ref(null);
const isLoading = ref(false); const isLoading = ref(false);
@ -47,8 +55,8 @@ const submitData = async () => {
isLoading.value = true; isLoading.value = true;
const rowsToEdit = $props.rows.map((row) => ({ id: row.id, itemFk: row.itemFk })); const rowsToEdit = $props.rows.map((row) => ({ id: row.id, itemFk: row.itemFk }));
const payload = { const payload = {
field: formData.field, field: selectedField.value.field,
newValue: formData.newValue, newValue: newValue.value,
lines: rowsToEdit, lines: rowsToEdit,
}; };
@ -75,19 +83,20 @@ const closeForm = () => {
<span class="countLines">{{ ` ${rows.length} ` }}</span> <span class="countLines">{{ ` ${rows.length} ` }}</span>
<span class="title">{{ t('buy(s)') }}</span> <span class="title">{{ t('buy(s)') }}</span>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <VnSelect
<VnSelectFilter :label="t('Field to edit')"
:label="t('Field to edit')" :options="fieldsOptions"
:options="fieldsOptions" hide-selected
hide-selected option-label="label"
option-label="label" v-model="selectedField"
option-value="field" />
v-model="formData.field" <component
/> :is="inputs[selectedField?.component || 'input']"
</div> v-bind="selectedField?.attrs || {}"
<div class="col"> v-model="newValue"
<VnInput :label="t('Value')" v-model="formData.newValue" /> :label="t('Value')"
</div> style="width: 200px"
/>
</VnRow> </VnRow>
<div class="q-mt-lg row justify-end"> <div class="q-mt-lg row justify-end">
<QBtn <QBtn

View File

@ -6,7 +6,7 @@ import { useRoute } from 'vue-router';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import axios from 'axios'; import axios from 'axios';
@ -160,7 +160,7 @@ const selectItem = ({ id }) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.buys.producer')" :label="t('entry.buys.producer')"
:options="producersOptions" :options="producersOptions"
hide-selected hide-selected
@ -170,7 +170,7 @@ const selectItem = ({ id }) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.buys.type')" :label="t('entry.buys.type')"
:options="ItemTypesOptions" :options="ItemTypesOptions"
hide-selected hide-selected
@ -180,7 +180,7 @@ const selectItem = ({ id }) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.buys.color')" :label="t('entry.buys.color')"
:options="InksOptions" :options="InksOptions"
hide-selected hide-selected

View File

@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue'; import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
import axios from 'axios'; import axios from 'axios';
@ -146,7 +146,7 @@ const selectTravel = ({ id }) => {
<h1 class="title">{{ t('Filter travels') }}</h1> <h1 class="title">{{ t('Filter travels') }}</h1>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.basicData.agency')" :label="t('entry.basicData.agency')"
:options="agenciesOptions" :options="agenciesOptions"
hide-selected hide-selected
@ -156,7 +156,7 @@ const selectTravel = ({ id }) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.basicData.warehouseOut')" :label="t('entry.basicData.warehouseOut')"
:options="warehousesOptions" :options="warehousesOptions"
hide-selected hide-selected
@ -166,7 +166,7 @@ const selectTravel = ({ id }) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.basicData.warehouseIn')" :label="t('entry.basicData.warehouseIn')"
:options="warehousesOptions" :options="warehousesOptions"
hide-selected hide-selected

View File

@ -102,7 +102,7 @@ onMounted(async () => {
}); });
onBeforeRouteLeave((to, from, next) => { onBeforeRouteLeave((to, from, next) => {
if (hasChanges.value) if (hasChanges.value && $props.observeFormChanges)
quasar.dialog({ quasar.dialog({
component: VnConfirm, component: VnConfirm,
componentProps: { componentProps: {

View File

@ -76,25 +76,25 @@ defineExpose({
<p>{{ subtitle }}</p> <p>{{ subtitle }}</p>
<slot name="form-inputs" :data="data" :validate="validate" /> <slot name="form-inputs" :data="data" :validate="validate" />
<div class="q-mt-lg row justify-end"> <div class="q-mt-lg row justify-end">
<QBtn
:label="t('globals.save')"
:title="t('globals.save')"
type="submit"
color="primary"
:disabled="isLoading"
:loading="isLoading"
/>
<QBtn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
:title="t('globals.cancel')" :title="t('globals.cancel')"
type="reset" type="reset"
color="primary" color="primary"
flat flat
class="q-ml-sm"
:disabled="isLoading" :disabled="isLoading"
:loading="isLoading" :loading="isLoading"
v-close-popup v-close-popup
/> />
<QBtn
:label="t('globals.save')"
:title="t('globals.save')"
type="submit"
color="primary"
class="q-ml-sm"
:disabled="isLoading"
:loading="isLoading"
/>
</div> </div>
</template> </template>
</FormModel> </FormModel>

View File

@ -56,14 +56,6 @@ const closeForm = () => {
<p>{{ subtitle }}</p> <p>{{ subtitle }}</p>
<slot name="form-inputs" /> <slot name="form-inputs" />
<div class="q-mt-lg row justify-end"> <div class="q-mt-lg row justify-end">
<QBtn
v-if="defaultSubmitButton"
:label="customSubmitButtonLabel || t('globals.save')"
type="submit"
color="primary"
:disabled="isLoading"
:loading="isLoading"
/>
<QBtn <QBtn
v-if="defaultCancelButton" v-if="defaultCancelButton"
:label="t('globals.cancel')" :label="t('globals.cancel')"
@ -74,6 +66,14 @@ const closeForm = () => {
:loading="isLoading" :loading="isLoading"
v-close-popup v-close-popup
/> />
<QBtn
v-if="defaultSubmitButton"
:label="customSubmitButtonLabel || t('globals.save')"
type="submit"
color="primary"
:disabled="isLoading"
:loading="isLoading"
/>
<slot name="customButtons" /> <slot name="customButtons" />
</div> </div>
</QCard> </QCard>

View File

@ -0,0 +1,359 @@
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnInput from 'components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
import axios from 'axios';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
customTags: {
type: Array,
default: () => [],
},
exprBuilder: {
type: Function,
default: null,
},
});
const itemCategories = ref([]);
const selectedCategoryFk = ref(null);
const selectedTypeFk = ref(null);
const itemTypesOptions = ref([]);
const suppliersOptions = ref([]);
const tagOptions = ref([]);
const tagValues = ref([]);
const categoryList = computed(() => {
return (itemCategories.value || [])
.filter((category) => category.display)
.map((category) => ({
...category,
icon: `vn:${(category.icon || '').split('-')[1]}`,
}));
});
const selectedCategory = computed(() =>
(itemCategories.value || []).find(
(category) => category?.id === selectedCategoryFk.value
)
);
const selectedType = computed(() => {
return (itemTypesOptions.value || []).find(
(type) => type?.id === selectedTypeFk.value
);
});
const selectCategory = async (params, categoryId, search) => {
if (params.categoryFk === categoryId) {
resetCategory(params);
search();
return;
}
selectedCategoryFk.value = categoryId;
params.categoryFk = categoryId;
await fetchItemTypes(categoryId);
search();
};
const resetCategory = (params) => {
selectedCategoryFk.value = null;
itemTypesOptions.value = null;
if (params) {
params.categoryFk = null;
params.typeFk = null;
}
};
const applyTags = (params, search) => {
params.tags = tagValues.value
.filter((tag) => tag.selectedTag && tag.value)
.map((tag) => ({
tagFk: tag.selectedTag.id,
tagName: tag.selectedTag.name,
value: tag.value,
}));
search();
};
const fetchItemTypes = async (id) => {
try {
const filter = {
fields: ['id', 'name', 'categoryFk'],
where: { categoryFk: id },
include: 'category',
order: 'name ASC',
};
const { data } = await axios.get('ItemTypes', {
params: { filter: JSON.stringify(filter) },
});
itemTypesOptions.value = data;
} catch (err) {
console.error('Error fetching item types', err);
}
};
const getCategoryClass = (category, params) => {
if (category.id === params?.categoryFk) {
return 'active';
}
};
const getSelectedTagValues = async (tag) => {
try {
tag.value = null;
const filter = {
fields: ['value'],
order: 'value ASC',
limit: 30,
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get(`Tags/${tag.selectedTag.id}/filterValue`, {
params,
});
tag.valueOptions = data;
} catch (err) {
console.error('Error getting selected tag values');
}
};
const removeTag = (index, params, search) => {
(tagValues.value || []).splice(index, 1);
applyTags(params, search);
};
</script>
<template>
<FetchData
url="ItemCategories"
limit="30"
auto-load
@on-fetch="(data) => (itemCategories = data)"
/>
<FetchData
url="Suppliers"
limit="30"
auto-load
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (suppliersOptions = data)"
/>
<FetchData
url="Tags"
:filter="{ fields: ['id', 'name', 'isFree'] }"
auto-load
limit="30"
@on-fetch="(data) => (tagOptions = data)"
/>
<VnFilterPanel
:data-key="props.dataKey"
:expr-builder="exprBuilder"
:custom-tags="customTags"
>
<template #tags="{ tag, formatFn }">
<strong v-if="tag.label === 'categoryFk'">
{{ t(selectedCategory?.name || '') }}
</strong>
<strong v-else-if="tag.label === 'typeFk'">
{{ t(selectedType?.name || '') }}
</strong>
<div v-else class="q-gutter-x-xs">
<strong>{{ t(`components.itemsFilterPanel.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #customTags="{ tags, params }">
<template v-for="tag in tags" :key="tag.label">
<VnFilterPanelChip
v-for="chip in tag.value"
:key="chip"
removable
@remove="removeTagChip(chip, params, searchFn)"
>
<div class="q-gutter-x-xs">
<strong>{{ chip.tagName }}: </strong>
<span>"{{ chip.value }}"</span>
</div>
</VnFilterPanelChip>
</template>
</template>
<template #body="{ params, searchFn }">
<QItem class="category-filter q-mt-md">
<QBtn
dense
flat
round
v-for="category in categoryList"
:key="category.name"
:class="['category', getCategoryClass(category, params)]"
:icon="category.icon"
@click="selectCategory(params, category.id, searchFn)"
>
<QTooltip>
{{ t(category.name) }}
</QTooltip>
</QBtn>
</QItem>
<QItem class="q-my-md">
<QItemSection>
<VnSelectFilter
:label="t('components.itemsFilterPanel.typeFk')"
v-model="params.typeFk"
:options="itemTypesOptions"
option-value="id"
option-label="name"
dense
outlined
rounded
use-input
:disable="!selectedCategoryFk"
@update:model-value="
(value) => {
selectedTypeFk = value;
searchFn();
}
"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel>{{ opt.name }}</QItemLabel>
<QItemLabel caption>
{{ opt.categoryName }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</QItemSection>
</QItem>
<QSeparator />
<slot name="body" :params="params" :search-fn="searchFn" />
<QItem
v-for="(value, index) in tagValues"
:key="value"
class="q-mt-md filter-value"
>
<QItemSection class="col">
<VnSelectFilter
:label="t('components.itemsFilterPanel.tag')"
v-model="value.selectedTag"
:options="tagOptions"
option-label="name"
dense
outlined
rounded
:emit-value="false"
use-input
:is-clearable="false"
@update:model-value="getSelectedTagValues(value)"
/>
</QItemSection>
<QItemSection class="col">
<VnSelectFilter
v-if="!value?.selectedTag?.isFree && value.valueOptions"
:label="t('components.itemsFilterPanel.value')"
v-model="value.value"
:options="value.valueOptions || []"
option-value="value"
option-label="value"
dense
outlined
rounded
emit-value
use-input
:disable="!value"
:is-clearable="false"
@update:model-value="applyTags(params, searchFn)"
/>
<VnInput
v-else
v-model="value.value"
:label="t('components.itemsFilterPanel.value')"
:disable="!value"
is-outlined
:is-clearable="false"
@keyup.enter="applyTags(params, searchFn)"
/>
</QItemSection>
<QIcon
name="delete"
class="fill-icon-on-hover q-px-xs"
color="primary"
size="sm"
@click="removeTag(index, params, searchFn)"
/>
</QItem>
<QItem class="q-mt-lg">
<QIcon
name="add_circle"
class="fill-icon-on-hover q-px-xs"
color="primary"
size="sm"
@click="tagValues.push({})"
/>
</QItem>
</template>
</VnFilterPanel>
</template>
<style lang="scss" scoped>
.category-filter {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
.category {
padding: 8px;
width: 60px;
height: 60px;
font-size: 1.4rem;
background-color: var(--vn-accent-color);
&.active {
background-color: $primary;
}
}
}
.filter-value {
display: flex;
align-items: center;
}
</style>
<i18n>
en:
params:
supplier: Supplier
from: From
to: To
active: Is active
visible: Is visible
floramondo: Is floramondo
salesPersonFk: Buyer
categoryFk: Category
es:
params:
supplier: Proveedor
from: Desde
to: Hasta
active: Activo
visible: Visible
floramondo: Floramondo
salesPersonFk: Comprador
categoryFk: Categoría
</i18n>

View File

@ -2,7 +2,7 @@
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from './FormModelPopup.vue'; import FormModelPopup from './FormModelPopup.vue';
@ -54,12 +54,13 @@ const onDataSaved = (data) => {
<QInput <QInput
:label="t('Type the visible quantity')" :label="t('Type the visible quantity')"
v-model.number="data.quantity" v-model.number="data.quantity"
autofocus
/> />
</div> </div>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Warehouse')" :label="t('Warehouse')"
v-model="data.warehouseFk" v-model="data.warehouseFk"
:options="warehousesOptions" :options="warehousesOptions"

View File

@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import FormPopup from './FormPopup.vue'; import FormPopup from './FormPopup.vue';
import axios from 'axios'; import axios from 'axios';
@ -84,7 +84,7 @@ const transferInvoice = async () => {
<template #form-inputs> <template #form-inputs>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Client')" :label="t('Client')"
:options="clientsOptions" :options="clientsOptions"
hide-selected hide-selected
@ -103,10 +103,10 @@ const transferInvoice = async () => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Rectificative type')" :label="t('Rectificative type')"
:options="rectificativeTypeOptions" :options="rectificativeTypeOptions"
hide-selected hide-selected
@ -119,7 +119,7 @@ const transferInvoice = async () => {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Class')" :label="t('Class')"
:options="siiTypeInvoiceOutsOptions" :options="siiTypeInvoiceOutsOptions"
hide-selected hide-selected
@ -138,10 +138,10 @@ const transferInvoice = async () => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Type')" :label="t('Type')"
:options="invoiceCorrectionTypesOptions" :options="invoiceCorrectionTypesOptions"
hide-selected hide-selected

View File

@ -7,7 +7,7 @@ import axios from 'axios';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { localeEquivalence } from 'src/i18n/index'; import { localeEquivalence } from 'src/i18n/index';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
@ -172,24 +172,24 @@ function copyUserToken() {
<QSeparator inset class="q-mx-lg" /> <QSeparator inset class="q-mx-lg" />
<div class="col q-gutter-xs q-pa-md"> <div class="col q-gutter-xs q-pa-md">
<VnRow> <VnRow>
<VnSelectFilter <VnSelect
:label="t('components.userPanel.localWarehouse')" :label="t('components.userPanel.localWarehouse')"
v-model="user.localWarehouseFk" v-model="user.localWarehouseFk"
:options="warehousesData" :options="warehousesData"
option-label="name" option-label="name"
option-value="id" option-value="id"
/> />
<VnSelectFilter <VnSelect
:label="t('components.userPanel.localBank')" :label="t('components.userPanel.localBank')"
hide-selected hide-selected
v-model="user.localBankFk" v-model="user.localBankFk"
:options="accountBankData" :options="accountBankData"
option-label="bank" option-label="bank"
option-value="id" option-value="id"
></VnSelectFilter> ></VnSelect>
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelectFilter <VnSelect
:label="t('components.userPanel.localCompany')" :label="t('components.userPanel.localCompany')"
hide-selected hide-selected
v-model="user.companyFk" v-model="user.companyFk"
@ -197,7 +197,7 @@ function copyUserToken() {
option-label="code" option-label="code"
option-value="id" option-value="id"
/> />
<VnSelectFilter <VnSelect
:label="t('components.userPanel.userWarehouse')" :label="t('components.userPanel.userWarehouse')"
hide-selected hide-selected
v-model="user.warehouseFk" v-model="user.warehouseFk"
@ -207,7 +207,7 @@ function copyUserToken() {
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelectFilter <VnSelect
:label="t('components.userPanel.userCompany')" :label="t('components.userPanel.userCompany')"
hide-selected hide-selected
v-model="user.companyFk" v-model="user.companyFk"

View File

@ -0,0 +1,78 @@
<script setup>
import { onBeforeMount, computed } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize';
import VnSubToolbar from '../ui/VnSubToolbar.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import LeftMenu from 'components/LeftMenu.vue';
const props = defineProps({
dataKey: { type: String, required: true },
baseUrl: { type: String, default: undefined },
customUrl: { type: String, default: undefined },
filter: { type: Object, default: () => {} },
descriptor: { type: Object, required: true },
searchbarDataKey: { type: String, default: undefined },
searchbarUrl: { type: String, default: undefined },
searchbarLabel: { type: String, default: '' },
searchbarInfo: { type: String, default: '' },
});
const { t } = useI18n();
const stateStore = useStateStore();
const route = useRoute();
const url = computed(() => {
if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`;
return props.customUrl;
});
const arrayData = useArrayData(props.dataKey, {
url: url.value,
filter: props.filter,
});
onBeforeMount(async () => {
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
await arrayData.fetch({ append: false });
});
if (props.baseUrl) {
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
arrayData.store.url = `${props.baseUrl}/${route.params.id}`;
await arrayData.fetch({ append: false });
}
});
}
</script>
<template>
<Teleport
to="#searchbar"
v-if="stateStore.isHeaderMounted() && props.searchbarDataKey"
>
<VnSearchbar
:data-key="props.searchbarDataKey"
:url="props.searchbarUrl"
:label="t(props.searchbarLabel)"
:info="t(props.searchbarInfo)"
/>
</Teleport>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<component :is="descriptor" />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="[useCardSize(), $attrs.class]">
<RouterView />
</div>
</QPage>
</QPageContainer>
</template>

View File

@ -6,7 +6,7 @@ import axios from 'axios';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FormModelPopup from 'components/FormModelPopup.vue'; import FormModelPopup from 'components/FormModelPopup.vue';
@ -123,7 +123,7 @@ function addDefaultData(data) {
<div class="q-gutter-y-ms"> <div class="q-gutter-y-ms">
<VnRow> <VnRow>
<VnInput :label="t('globals.reference')" v-model="dms.reference" /> <VnInput :label="t('globals.reference')" v-model="dms.reference" />
<VnSelectFilter <VnSelect
:label="t('globals.company')" :label="t('globals.company')"
v-model="dms.companyFk" v-model="dms.companyFk"
:options="companies" :options="companies"
@ -133,7 +133,7 @@ function addDefaultData(data) {
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelectFilter <VnSelect
:label="t('globals.warehouse')" :label="t('globals.warehouse')"
v-model="dms.warehouseFk" v-model="dms.warehouseFk"
:options="warehouses" :options="warehouses"
@ -141,7 +141,7 @@ function addDefaultData(data) {
option-label="name" option-label="name"
input-debounce="0" input-debounce="0"
/> />
<VnSelectFilter <VnSelect
:label="t('globals.type')" :label="t('globals.type')"
v-model="dms.dmsTypeFk" v-model="dms.dmsTypeFk"
:options="dmsTypes" :options="dmsTypes"

View File

@ -187,7 +187,7 @@ const columns = computed(() => [
downloadFile( downloadFile(
prop.row.id, prop.row.id,
$props.downloadModel, $props.downloadModel,
null, undefined,
prop.row.download prop.row.download
), ),
}, },

View File

@ -12,7 +12,7 @@ import { useValidator } from 'src/composables/useValidator';
import VnAvatar from '../ui/VnAvatar.vue'; import VnAvatar from '../ui/VnAvatar.vue';
import VnJsonValue from '../common/VnJsonValue.vue'; import VnJsonValue from '../common/VnJsonValue.vue';
import FetchData from '../FetchData.vue'; import FetchData from '../FetchData.vue';
import VnSelectFilter from './VnSelectFilter.vue'; import VnSelect from './VnSelect.vue';
import VnUserLink from '../ui/VnUserLink.vue'; import VnUserLink from '../ui/VnUserLink.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -659,7 +659,7 @@ setLogTree();
</QInput> </QInput>
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
class="full-width" class="full-width"
:label="t('globals.entity')" :label="t('globals.entity')"
v-model="selectedFilters.changedModel" v-model="selectedFilters.changedModel"
@ -689,7 +689,7 @@ setLogTree();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="workers && userRadio !== null"> <QItemSection v-if="workers && userRadio !== null">
<VnSelectFilter <VnSelect
class="full-width" class="full-width"
:label="t('globals.user')" :label="t('globals.user')"
v-model="userSelect" v-model="userSelect"
@ -713,7 +713,7 @@ setLogTree();
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem class="q-mt-sm"> <QItem class="q-mt-sm">

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import { useRole } from 'src/composables/useRole'; import { useRole } from 'src/composables/useRole';
@ -52,7 +52,7 @@ const toggleForm = () => {
</script> </script>
<template> <template>
<VnSelectFilter v-model="value" :options="options" v-bind="$attrs"> <VnSelect v-model="value" :options="options" v-bind="$attrs">
<template v-if="isAllowedToCreate" #append> <template v-if="isAllowedToCreate" #append>
<QIcon <QIcon
@click.stop.prevent="toggleForm()" @click.stop.prevent="toggleForm()"
@ -72,7 +72,7 @@ const toggleForm = () => {
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName"> <template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
<slot :name="slotName" v-bind="slotData" :key="slotName" /> <slot :name="slotName" v-bind="slotData" :key="slotName" />
</template> </template>
</VnSelectFilter> </VnSelect>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -200,8 +200,8 @@ es:
pendingPayment: 'Su pedido está pendiente de pago. pendingPayment: 'Su pedido está pendiente de pago.
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.' Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
minAmount: 'Es necesario un importe mínimo de 50 (Sin IVA) en su pedido minAmount: 'Es necesario un importe mínimo de 50 (Sin IVA) en su pedido
{ orderId } del día { shipped } para recibirlo sin portes adicionales.' { orderId } con llegada { landing } para recibirlo sin portes adicionales.'
orderChanges: 'Pedido {orderId} día { shipped }: { changes }' orderChanges: 'Pedido {orderId} con llegada estimada día { landing }: { changes }'
en: Inglés en: Inglés
es: Español es: Español
fr: Francés fr: Francés
@ -215,11 +215,12 @@ fr:
Message: Message Message: Message
messageTooltip: Les caractères spéciaux comme les accents comptent comme plusieurs messageTooltip: Les caractères spéciaux comme les accents comptent comme plusieurs
templates: templates:
pendingPayment: 'Votre commande est en attente de paiement. pendingPayment: 'Verdnatura : Commande en attente de règlement. Veuillez régler votre commande avant 9h.
Veuillez vous connecter sur le site web et effectuer le paiement par carte. Merci beaucoup.' Sinon elle sera décalée en fonction de vos jours de livraison . Merci'
minAmount: 'Un montant minimum de 50 (TVA non incluse) est requis pour votre commande minAmount: 'Verdnatura vous rappelle :
{ orderId } du { shipped } afin de la recevoir sans frais de port supplémentaires.' Montant minimum nécessaire de 50 euros pour recevoir la commande { orderId } livraison { landing }.
orderChanges: 'Commande { orderId } du { shipped }: { changes }' Merci.'
orderChanges: 'Commande {orderId} livraison {landing} indisponible/s. Désolés pour le dérangement.'
en: Anglais en: Anglais
es: Espagnol es: Espagnol
fr: Français fr: Français
@ -236,8 +237,8 @@ pt:
pendingPayment: 'Seu pedido está pendente de pagamento. pendingPayment: 'Seu pedido está pendente de pagamento.
Por favor, acesse o site e faça o pagamento com cartão. Muito obrigado.' Por favor, acesse o site e faça o pagamento com cartão. Muito obrigado.'
minAmount: 'É necessário um valor mínimo de 50 (sem IVA) em seu pedido minAmount: 'É necessário um valor mínimo de 50 (sem IVA) em seu pedido
{ orderId } do dia { shipped } para recebê-lo sem custos de envio adicionais.' { orderId } do dia { landing } para recebê-lo sem custos de envio adicionais.'
orderChanges: 'Pedido { orderId } dia { shipped }: { changes }' orderChanges: 'Pedido { orderId } com chegada dia { landing }: { changes }'
en: Inglês en: Inglês
es: Espanhol es: Espanhol
fr: Francês fr: Francês

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { onBeforeMount, useSlots, watch, computed, ref } from 'vue'; import { onBeforeMount, watch, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue'; import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
@ -38,7 +38,6 @@ const $props = defineProps({
}); });
const state = useState(); const state = useState();
const slots = useSlots();
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const arrayData = useArrayData($props.dataKey || $props.module, { const arrayData = useArrayData($props.dataKey || $props.module, {
@ -47,7 +46,7 @@ const arrayData = useArrayData($props.dataKey || $props.module, {
skip: 0, skip: 0,
}); });
const { store } = arrayData; const { store } = arrayData;
const entity = computed(() =>Array.isArray( store.data) ? store.data[0] : store.data); const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
const isLoading = ref(false); const isLoading = ref(false);
defineExpose({ defineExpose({
@ -55,14 +54,12 @@ defineExpose({
}); });
onBeforeMount(async () => { onBeforeMount(async () => {
await getData(); await getData();
watch( watch($props, async () => await getData());
() => $props.url,
async () => await getData()
);
}); });
async function getData() { async function getData() {
store.url = $props.url; store.url = $props.url;
store.filter = $props.filter ?? {};
isLoading.value = true; isLoading.value = true;
try { try {
const { data } = await arrayData.fetch({ append: false, updateRouter: false }); const { data } = await arrayData.fetch({ append: false, updateRouter: false });
@ -117,7 +114,7 @@ const emit = defineEmits(['onFetch']);
icon="more_vert" icon="more_vert"
round round
size="md" size="md"
:class="{ invisible: !slots.menu }" :class="{ invisible: !$slots.menu }"
> >
<QTooltip> <QTooltip>
{{ t('components.cardDescriptor.moreOptions') }} {{ t('components.cardDescriptor.moreOptions') }}

View File

@ -15,7 +15,7 @@ const props = defineProps({
default: null, default: null,
}, },
entityId: { entityId: {
type: Number, type: [Number, String],
default: null, default: null,
}, },
dataKey: { dataKey: {
@ -32,7 +32,7 @@ const arrayData = useArrayData(props.dataKey || route.meta.moduleName, {
skip: 0, skip: 0,
}); });
const { store } = arrayData; const { store } = arrayData;
const entity = computed(() => Array.isArray(store.data) ? store.data[0] : store.data); const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
const isLoading = ref(false); const isLoading = ref(false);
defineExpose({ defineExpose({
@ -48,9 +48,10 @@ onBeforeMount(async () => {
async function fetch() { async function fetch() {
store.url = props.url; store.url = props.url;
store.filter = props.filter ?? {};
isLoading.value = true; isLoading.value = true;
const { data } = await arrayData.fetch({ append: false, updateRouter: false }); const { data } = await arrayData.fetch({ append: false, updateRouter: false });
emit('onFetch', data); emit('onFetch', Array.isArray(data) ? data[0] : data);
isLoading.value = false; isLoading.value = false;
} }
</script> </script>

View File

@ -12,12 +12,23 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
viewCustomization: {
type: String,
default: '',
},
}); });
const $q = useQuasar(); const $q = useQuasar();
// El objetivo de asignar las clases de personalización desde el wrapper es no tener conflictos entre vistas que usen el mismo componente
const viewCustomizationClasses = {
workerCalendar: 'worker-calendar-customizations',
};
const containerClasses = computed(() => { const containerClasses = computed(() => {
const classes = ['main-container-background']; const classes = ['main-container-background'];
if (viewCustomizationClasses[$props.viewCustomization])
classes.push(viewCustomizationClasses[$props.viewCustomization]);
if ($props.bordered) classes.push('--bordered'); if ($props.bordered) classes.push('--bordered');
if ($props.transparentBackground) classes.push('transparent-background'); if ($props.transparentBackground) classes.push('transparent-background');
else classes.push($q.dark.isActive ? '--dark' : '--light'); else classes.push($q.dark.isActive ? '--dark' : '--light');
@ -33,6 +44,47 @@ const containerClasses = computed(() => {
</template> </template>
<style lang="scss"> <style lang="scss">
@import '../../css/quasar.variables.scss';
:root {
// Cambia los colores del día actual del calendario por los de salix
--calendar-border-current-dark: #84d0e2 2px solid;
--calendar-border-current: #84d0e2 2px solid;
--calendar-current-color-dark: #84d0e2;
// Colores de fondo del calendario en dark mode
--calendar-outside-background-dark: #222;
--calendar-background-dark: #222;
}
// Clases para modificar el color de fecha seleccionada en componente QCalendarMonth
.q-dark div .q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button {
background-color: $primary !important;
color: white !important;
}
.q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button {
background-color: $primary !important;
color: white !important;
}
.q-calendar-month__head--weekday {
// Transforma los nombres de los días de la semana a mayúsculas
text-transform: capitalize;
}
.transparent-background {
--calendar-background-dark: transparent;
--calendar-background: transparent;
--calendar-outside-background-dark: transparent;
}
.q-calendar__button {
&:hover {
background-color: var(--vn-accent-color);
cursor: pointer;
}
}
.main-container-background { .main-container-background {
--calendar-current-background-dark: transparent; --calendar-current-background-dark: transparent;
@ -45,14 +97,64 @@ const containerClasses = computed(() => {
} }
&.--bordered { &.--bordered {
border: 1px solid black; border: 1px solid #222;
} }
} }
.transparent-background { .worker-calendar-customizations {
--calendar-background-dark: transparent; .q-calendar__button {
--calendar-background: transparent; width: 32px;
--calendar-outside-background-dark: transparent; height: 32px;
font-size: 13px;
&:hover {
background-color: var(--vn-accent-color);
cursor: pointer;
}
}
.q-calendar-month__week--days > div:nth-child(6),
.q-calendar-month__week--days > div:nth-child(7) {
// Cambia el color de los días sábado y domingo
color: #777777;
}
.q-calendar-month__week--wrapper {
margin-bottom: 4px;
}
.q-calendar-month__workweek {
height: 32px;
display: flex;
justify-content: center;
}
.q-calendar__button--bordered {
color: $info !important;
}
.q-calendar-month__day--content {
position: absolute;
top: 1;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.q-outside .calendar-event {
display: none;
}
.q-calendar-month__workweek,
.q-calendar-month__head--workweek,
.q-calendar-month__head--weekday.q-calendar__center.q-calendar__ellipsis {
text-transform: capitalize;
color: #777;
font-weight: bold;
font-size: 0.8rem;
text-align: center;
}
} }
.nav-container { .nav-container {

View File

@ -32,7 +32,7 @@ async function insert() {
<template> <template>
<QCard class="q-pa-xs q-mb-xl full-width" v-if="$props.addNote"> <QCard class="q-pa-xs q-mb-xl full-width" v-if="$props.addNote">
<QCardSection horizontal> <QCardSection horizontal>
<VnAvatar :descriptor="false" :worker-id="1" size="md" /> <VnAvatar :worker-id="currentUser.id" size="md" />
<div class="full-width row justify-between q-pa-xs"> <div class="full-width row justify-between q-pa-xs">
<VnUserLink :name="t('New note')" :worker-id="currentUser.id" /> <VnUserLink :name="t('New note')" :worker-id="currentUser.id" />
{{ t('globals.now') }} {{ t('globals.now') }}

View File

@ -77,7 +77,6 @@ const arrayData = useArrayData(props.dataKey, {
userParams: props.userParams, userParams: props.userParams,
exprBuilder: props.exprBuilder, exprBuilder: props.exprBuilder,
}); });
const hasMoreData = ref();
const store = arrayData.store; const store = arrayData.store;
onMounted(() => { onMounted(() => {
@ -97,7 +96,7 @@ const addFilter = async (filter, params) => {
async function fetch() { async function fetch() {
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
if (!arrayData.hasMoreData.value) { if (!store.hasMoreData) {
isLoading.value = false; isLoading.value = false;
} }
emit('onFetch', store.data); emit('onFetch', store.data);
@ -110,8 +109,8 @@ async function paginate() {
isLoading.value = true; isLoading.value = true;
await arrayData.loadMore(); await arrayData.loadMore();
if (!arrayData.hasMoreData.value) { if (!store.hasMoreData) {
if (store.userParamsChanged) arrayData.hasMoreData.value = true; if (store.userParamsChanged) store.hasMoreData = true;
store.userParamsChanged = false; store.userParamsChanged = false;
endPagination(); endPagination();
return; return;
@ -132,9 +131,7 @@ function endPagination() {
emit('onPaginate'); emit('onPaginate');
} }
async function onLoad(index, done) { async function onLoad(index, done) {
if (!store.data) { if (!store.data) return done();
return done();
}
if (store.data.length === 0 || !props.url) return done(false); if (store.data.length === 0 || !props.url) return done(false);
@ -142,7 +139,7 @@ async function onLoad(index, done) {
await paginate(); await paginate();
let isDone = false; let isDone = false;
if (store.userParamsChanged) isDone = !arrayData.hasMoreData.value; if (store.userParamsChanged) isDone = !store.hasMoreData;
done(isDone); done(isDone);
} }
@ -182,13 +179,12 @@ defineExpose({ fetch, addFilter });
</QCard> </QCard>
</div> </div>
</div> </div>
<QInfiniteScroll <QInfiniteScroll
v-if="store.data" v-if="store.data"
@load="onLoad" @load="onLoad"
:offset="offset" :offset="offset"
:disable="disableInfiniteScroll || !arrayData.hasMoreData"
class="full-width" class="full-width"
:disable="disableInfiniteScroll || !store.hasMoreData"
v-bind="$attrs" v-bind="$attrs"
> >
<slot name="body" :rows="store.data"></slot> <slot name="body" :rows="store.data"></slot>
@ -196,7 +192,10 @@ defineExpose({ fetch, addFilter });
<QSpinner color="orange" size="md" /> <QSpinner color="orange" size="md" />
</div> </div>
</QInfiniteScroll> </QInfiniteScroll>
<div v-if="!isLoading && hasMoreData" class="w-full flex justify-center q-mt-md"> <div
v-if="!isLoading && store.hasMoreData"
class="w-full flex justify-center q-mt-md"
>
<QBtn color="primary" :label="t('Load more data')" @click="paginate()" /> <QBtn color="primary" :label="t('Load more data')" @click="paginate()" />
</div> </div>
</template> </template>

View File

@ -131,13 +131,6 @@ async function search() {
/> />
</template> </template>
<template #append> <template #append>
<QIcon
v-if="searchText !== ''"
name="close"
@click="searchText = ''"
class="cursor-pointer"
/>
<QIcon <QIcon
v-if="props.info && $q.screen.gt.xs" v-if="props.info && $q.screen.gt.xs"
name="info" name="info"

View File

@ -1,11 +1,27 @@
<script setup> <script setup>
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore(); const stateStore = useStateStore();
const actions = ref(null);
const data = ref(null);
const opts = { subtree: true, childList: true, attributes: true };
const hasContent = ref(false);
onMounted(() => { onMounted(() => {
stateStore.toggleSubToolbar(); stateStore.toggleSubToolbar();
actions.value = document.querySelector('#st-actions');
data.value = document.querySelector('#st-data');
if (!actions.value && !data.value) return;
// Check if there's content to display
const observer = new MutationObserver(
() =>
(hasContent.value =
actions.value.childNodes.length + data.value.childNodes.length)
);
if (actions.value) observer.observe(actions.value, opts);
if (data.value) observer.observe(data.value, opts);
}); });
onUnmounted(() => { onUnmounted(() => {
@ -14,7 +30,10 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<QToolbar class="bg-vn-section-color justify-end sticky"> <QToolbar
class="justify-end sticky"
v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
>
<slot name="st-data"> <slot name="st-data">
<div id="st-data"></div> <div id="st-data"></div>
</slot> </slot>

View File

@ -0,0 +1,11 @@
export function getDateQBadgeColor(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 'warning';
if (comparation < 0) return 'negative';
}

View File

@ -9,12 +9,9 @@ const arrayDataStore = useArrayDataStore();
export function useArrayData(key, userOptions) { export function useArrayData(key, userOptions) {
if (!key) throw new Error('ArrayData: A key is required to use this composable'); if (!key) throw new Error('ArrayData: A key is required to use this composable');
if (!arrayDataStore.get(key)) { if (!arrayDataStore.get(key)) arrayDataStore.set(key);
arrayDataStore.set(key);
}
const store = arrayDataStore.get(key); const store = arrayDataStore.get(key);
const hasMoreData = ref(false);
const route = useRoute(); const route = useRoute();
let canceller = null; let canceller = null;
@ -22,6 +19,7 @@ export function useArrayData(key, userOptions) {
onMounted(() => { onMounted(() => {
setOptions(); setOptions();
store.skip = 0;
const query = route.query; const query = route.query;
if (query.params) { if (query.params) {
@ -29,9 +27,7 @@ export function useArrayData(key, userOptions) {
} }
}); });
if (key && userOptions) { if (key && userOptions) setOptions();
setOptions();
}
function setOptions() { function setOptions() {
const allowedOptions = [ const allowedOptions = [
@ -96,8 +92,7 @@ export function useArrayData(key, userOptions) {
}); });
const { limit } = filter; const { limit } = filter;
hasMoreData.value = limit && response.data.length >= limit; store.hasMoreData = limit && response.data.length >= limit;
store.hasMoreData = hasMoreData.value;
if (append) { if (append) {
if (!store.data) store.data = []; if (!store.data) store.data = [];
@ -156,9 +151,10 @@ export function useArrayData(key, userOptions) {
delete store.userParams[param]; delete store.userParams[param];
delete params[param]; delete params[param];
if (store.filter?.where) { if (store.filter?.where) {
delete store.filter.where[ const key = Object.keys(
Object.keys(exprBuilder ? exprBuilder(param) : param)[0] exprBuilder && exprBuilder(param) ? exprBuilder(param) : param
]; );
if (key[0]) delete store.filter.where[key[0]];
if (Object.keys(store.filter.where).length === 0) { if (Object.keys(store.filter.where).length === 0) {
delete store.filter.where; delete store.filter.where;
} }
@ -169,7 +165,7 @@ export function useArrayData(key, userOptions) {
} }
async function loadMore() { async function loadMore() {
if (!hasMoreData.value && !store.hasMoreData) return; if (!store.hasMoreData) return;
store.skip = store.limit * page.value; store.skip = store.limit * page.value;
page.value += 1; page.value += 1;
@ -211,7 +207,6 @@ export function useArrayData(key, userOptions) {
destroy, destroy,
loadMore, loadMore,
store, store,
hasMoreData,
totalRows, totalRows,
updateStateParams, updateStateParams,
isLoading, isLoading,

View File

@ -119,6 +119,11 @@ select:-webkit-autofill {
font-variation-settings: 'FILL' 1; font-variation-settings: 'FILL' 1;
} }
.fill-icon-on-hover:hover {
font-variation-settings: 'FILL' 1;
cursor: pointer;
}
.vn-table-separation-row { .vn-table-separation-row {
height: 16px !important; height: 16px !important;
background-color: var(--vn-section-color) !important; background-color: var(--vn-section-color) !important;
@ -136,6 +141,19 @@ select:-webkit-autofill {
background-color: var(--vn-section-color); background-color: var(--vn-section-color);
} }
.q-checkbox {
& .q-checkbox__label {
color: var(--vn-text-color);
}
& .q-checkbox__inner {
color: var(--vn-label-color);
}
}
.tr-header {
color: var(--vn-label-color);
}
.q-chip, .q-chip,
.q-notification__message, .q-notification__message,
.q-notification__icon { .q-notification__icon {
@ -170,13 +188,6 @@ input::-webkit-inner-spin-button {
-moz-appearance: none; -moz-appearance: none;
} }
// Clases para modificar el color de fecha seleccionada en componente QCalendarMonth .q-scrollarea__content {
.q-dark div .q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button { max-width: 100%;
background-color: $primary !important;
color: white !important;
}
.q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button {
background-color: $primary !important;
color: white !important;
} }

View File

@ -91,3 +91,42 @@ export function toDateTimeFormat(date, showSeconds = false) {
second: showSeconds ? '2-digit' : undefined, second: showSeconds ? '2-digit' : undefined,
}); });
} }
/**
* Converts seconds to a formatted string representing hours and minutes (hh:mm).
* @param {number} seconds - The number of seconds to convert.
* @param {boolean} includeHSuffix - Optional parameter indicating whether to include "h." after the hour.
* @returns {string} A string representing the time in the format "hh:mm" with optional "h." suffix.
*/
export function secondsToHoursMinutes(seconds, includeHSuffix = true) {
if (!seconds) return includeHSuffix ? '00:00 h.' : '00:00';
const hours = Math.floor(seconds / 3600);
const remainingMinutes = seconds % 3600;
const minutes = Math.floor(remainingMinutes / 60);
const formattedHours = hours < 10 ? '0' + hours : hours;
const formattedMinutes = minutes < 10 ? '0' + minutes : minutes;
// Append "h." if includeHSuffix is true
const suffix = includeHSuffix ? ' h.' : '';
// Return formatted string
return formattedHours + ':' + formattedMinutes + suffix;
}
export function getTimeDifferenceWithToday(date) {
let today = Date.vnNew();
today.setHours(0, 0, 0, 0);
date = new Date(date);
date.setHours(0, 0, 0, 0);
return today - date;
}
export function isLower(date) {
return getTimeDifferenceWithToday(date) > 0;
}
export function isBigger(date) {
return getTimeDifferenceWithToday(date) < 0;
}

View File

@ -28,6 +28,7 @@ globals:
reset: Reset reset: Reset
close: Close close: Close
cancel: Cancel cancel: Cancel
clone: Clone
confirm: Confirm confirm: Confirm
assign: Assign assign: Assign
back: Back back: Back
@ -83,6 +84,7 @@ globals:
selectFile: Select a file selectFile: Select a file
copyClipboard: Copy on clipboard copyClipboard: Copy on clipboard
salesPerson: SalesPerson salesPerson: SalesPerson
send: Send
code: Code code: Code
pageTitles: pageTitles:
summary: Summary summary: Summary
@ -811,6 +813,7 @@ worker:
pbx: Private Branch Exchange pbx: Private Branch Exchange
log: Log log: Log
calendar: Calendar calendar: Calendar
timeControl: Time control
list: list:
name: Name name: Name
email: Email email: Email
@ -1117,6 +1120,9 @@ item:
list: List list: List
diary: Diary diary: Diary
tags: Tags tags: Tags
create: Create
buyRequest: Buy requests
fixedPrice: Fixed prices
wasteBreakdown: Waste breakdown wasteBreakdown: Waste breakdown
itemCreate: New item itemCreate: New item
log: Log log: Log
@ -1147,6 +1153,15 @@ item:
stemMultiplier: Multiplier stemMultiplier: Multiplier
producer: Producer producer: Producer
landed: Landed landed: Landed
fixedPrice:
itemId: Item ID
groupingPrice: Grouping price
packingPrice: Packing price
hasMinPrice: Has min price
minPrice: Min price
started: Started
ended: Ended
warehouse: Warehouse
create: create:
name: Name name: Name
tag: Tag tag: Tag
@ -1154,8 +1169,68 @@ item:
type: Type type: Type
intrastat: Intrastat intrastat: Intrastat
origin: Origin origin: Origin
buyRequest:
ticketId: 'Ticket ID'
shipped: 'Shipped'
requester: 'Requester'
requested: 'Requested'
price: 'Price'
attender: 'Atender'
item: 'Item'
achieved: 'Achieved'
concept: 'Concept'
state: 'State'
summary:
basicData: 'Basic data'
otherData: 'Other data'
description: 'Description'
tax: 'Tax'
tags: 'Tags'
botanical: 'Botanical'
barcode: 'Barcode'
name: 'Nombre'
completeName: 'Nombre completo'
family: 'Familia'
size: 'Medida'
origin: 'Origen'
stems: 'Tallos'
multiplier: 'Multiplicador'
buyer: 'Comprador'
doPhoto: 'Do photo'
intrastatCode: 'Código intrastat'
intrastat: 'Intrastat'
ref: 'Referencia'
relevance: 'Relevancia'
weight: 'Peso (gramos)/tallo'
units: 'Unidades/caja'
expense: 'Gasto'
generic: 'Genérico'
recycledPlastic: 'Plástico reciclado'
nonRecycledPlastic: 'Plástico no reciclado'
minSalesQuantity: 'Cantidad mínima de venta'
genus: 'Genus'
specie: 'Specie'
components: components:
topbar: {} topbar: {}
itemsFilterPanel:
typeFk: Type
tag: Tag
value: Value
# ItemFixedPriceFilter
buyerFk: Buyer
warehouseFk: Warehouse
started: From
ended: To
mine: For me
hasMinPrice: Minimum price
# LatestBuysFilter
salesPersonFk: Buyer
supplierFk: Supplier
from: From
to: To
active: Is active
visible: Is visible
floramondo: Is floramondo
userPanel: userPanel:
copyToken: Token copied to clipboard copyToken: Token copied to clipboard
settings: Settings settings: Settings

View File

@ -28,6 +28,7 @@ globals:
reset: Restaurar reset: Restaurar
close: Cerrar close: Cerrar
cancel: Cancelar cancel: Cancelar
clone: Clonar
confirm: Confirmar confirm: Confirmar
assign: Asignar assign: Asignar
back: Volver back: Volver
@ -83,6 +84,7 @@ globals:
selectFile: Seleccione un fichero selectFile: Seleccione un fichero
copyClipboard: Copiar en portapapeles copyClipboard: Copiar en portapapeles
salesPerson: Comercial salesPerson: Comercial
send: Enviar
code: Código code: Código
pageTitles: pageTitles:
summary: Resumen summary: Resumen
@ -809,6 +811,7 @@ worker:
pbx: Centralita pbx: Centralita
log: Historial log: Historial
calendar: Calendario calendar: Calendario
timeControl: Control de horario
list: list:
name: Nombre name: Nombre
email: Email email: Email
@ -1116,8 +1119,14 @@ item:
list: Listado list: Listado
diary: Histórico diary: Histórico
tags: Etiquetas tags: Etiquetas
fixedPrice: Precios fijados
buyRequest: Peticiones de compra
wasteBreakdown: Deglose de mermas wasteBreakdown: Deglose de mermas
itemCreate: Nuevo artículo itemCreate: Nuevo artículo
basicData: 'Datos básicos'
tax: 'IVA'
botanical: 'Botánico'
barcode: 'Código de barras'
log: Historial log: Historial
descriptor: descriptor:
item: Artículo item: Artículo
@ -1146,6 +1155,15 @@ item:
stemMultiplier: Multiplicador stemMultiplier: Multiplicador
producer: Productor producer: Productor
landed: F. entrega landed: F. entrega
fixedPrice:
itemId: ID Artículo
groupingPrice: Precio grouping
packingPrice: Precio packing
hasMinPrice: Tiene precio mínimo
minPrice: Precio min
started: Inicio
ended: Fin
warehouse: Almacén
create: create:
name: Nombre name: Nombre
tag: Etiqueta tag: Etiqueta
@ -1153,8 +1171,68 @@ item:
type: Tipo type: Tipo
intrastat: Intrastat intrastat: Intrastat
origin: Origen origin: Origen
summary:
basicData: 'Datos básicos'
otherData: 'Otros datos'
description: 'Descripción'
tax: 'IVA'
tags: 'Etiquetas'
botanical: 'Botánico'
barcode: 'Código de barras'
name: 'Nombre'
completeName: 'Nombre completo'
family: 'Familia'
size: 'Medida'
origin: 'Origen'
stems: 'Tallos'
multiplier: 'Multiplicador'
buyer: 'Comprador'
doPhoto: 'Hacer foto'
intrastatCode: 'Código intrastat'
intrastat: 'Intrastat'
ref: 'Referencia'
relevance: 'Relevancia'
weight: 'Peso (gramos)/tallo'
units: 'Unidades/caja'
expense: 'Gasto'
generic: 'Genérico'
recycledPlastic: 'Plástico reciclado'
nonRecycledPlastic: 'Plástico no reciclado'
minSalesQuantity: 'Cantidad mínima de venta'
genus: 'Genus'
specie: 'Specie'
buyRequest:
ticketId: 'ID Ticket'
shipped: 'F. envío'
requester: 'Solicitante'
requested: 'Solicitado'
price: 'Precio'
attender: 'Comprador'
item: 'Artículo'
achieved: 'Conseguido'
concept: 'Concepto'
state: 'Estado'
components: components:
topbar: {} topbar: {}
itemsFilterPanel:
typeFk: Tipo
tag: Etiqueta
value: Valor
# ItemFixedPriceFilter
buyerFk: Comprador
warehouseFk: Almacén
started: Desde
ended: Hasta
mine: Para mi
hasMinPrice: Precio mínimo
# LatestBuysFilter
salesPersonFk: Comprador
supplierFk: Proveedor
from: Desde
to: Hasta
active: Activo
visible: Visible
floramondo: Floramondo
userPanel: userPanel:
copyToken: Token copiado al portapapeles copyToken: Token copiado al portapapeles
settings: Configuración settings: Configuración

View File

@ -9,7 +9,7 @@ import { toDate, toPercentage, toCurrency } from 'filters/index';
import { tMobile } from 'src/composables/tMobile'; import { tMobile } from 'src/composables/tMobile';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
@ -302,7 +302,7 @@ async function importToNewRefundTicket() {
</template> </template>
<template #body-cell-destination="{ row }"> <template #body-cell-destination="{ row }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row.claimDestinationFk" v-model="row.claimDestinationFk"
:options="destinationTypes" :options="destinationTypes"
option-label="description" option-label="description"
@ -344,7 +344,7 @@ async function importToNewRefundTicket() {
</QItemSection> </QItemSection>
<QItemSection side> <QItemSection side>
<QItemLabel v-if="column.name === 'destination'"> <QItemLabel v-if="column.name === 'destination'">
<VnSelectFilter <VnSelect
v-model="props.row.claimDestinationFk" v-model="props.row.claimDestinationFk"
:options="destinationTypes" :options="destinationTypes"
option-label="description" option-label="description"
@ -418,7 +418,7 @@ async function importToNewRefundTicket() {
</QItem> </QItem>
</QCardSection> </QCardSection>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
class="q-pa-sm" class="q-pa-sm"
v-model="claimDestinationFk" v-model="claimDestinationFk"
:options="destinationTypes" :options="destinationTypes"

View File

@ -1,46 +1,15 @@
<script setup> <script setup>
import LeftMenu from 'components/LeftMenu.vue'; import VnCard from 'components/common/VnCard.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
import { useI18n } from 'vue-i18n';
import ClaimDescriptor from './ClaimDescriptor.vue'; import ClaimDescriptor from './ClaimDescriptor.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import useCardSize from 'src/composables/useCardSize';
const stateStore = useStateStore();
const { t } = useI18n();
</script> </script>
<template> <template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()"> <VnCard
<VnSearchbar data-key="Claim"
data-key="ClaimList" base-url="Claims"
url="Claims/filter" :descriptor="ClaimDescriptor"
:label="t('Search claim')" searchbar-data-key="ClaimList"
:info="t('You can search by claim id or customer name')" searchbar-url="Claims/filter"
/> searchbar-label="Search claim"
</Teleport> searchbar-info="You can search by claim id or customer name"
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> />
<QScrollArea class="fit">
<ClaimDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="useCardSize()">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>
<i18n>
es:
Search claim: Buscar reclamación
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
Details: Detalles
Notes: Notas
Action: Acción
</i18n>

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import CrudModel from 'components/CrudModel.vue'; import CrudModel from 'components/CrudModel.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import { tMobile } from 'composables/tMobile'; import { tMobile } from 'composables/tMobile';
const route = useRoute(); const route = useRoute();
@ -161,7 +161,7 @@ const columns = computed(() => [
auto-width auto-width
@keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()" @keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
> >
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
@ -181,7 +181,7 @@ const columns = computed(() => [
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QTd> </QTd>
</template> </template>
<template #item="props"> <template #item="props">
@ -198,7 +198,7 @@ const columns = computed(() => [
<QList dense> <QList dense>
<QItem v-for="col in props.cols" :key="col.name"> <QItem v-for="col in props.cols" :key="col.name">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="col.label" :label="col.label"
v-model="props.row[col.model]" v-model="props.row[col.model]"
:options="col.options" :options="col.options"

View File

@ -35,6 +35,7 @@ const columns = computed(() => [
label: t('Quantity'), label: t('Quantity'),
field: (row) => row.quantity, field: (row) => row.quantity,
sortable: true, sortable: true,
default: 0,
}, },
{ {
name: 'description', name: 'description',
@ -75,7 +76,6 @@ async function importLines() {
const body = sales.map((row) => ({ const body = sales.map((row) => ({
claimFk: route.params.id, claimFk: route.params.id,
saleFk: row.saleFk, saleFk: row.saleFk,
quantity: row.quantity,
})); }));
canceller = new AbortController(); canceller = new AbortController();

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
@ -64,7 +64,7 @@ const states = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="workers"> <QItemSection v-if="workers">
<VnSelectFilter <VnSelect
:label="t('Salesperson')" :label="t('Salesperson')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -87,7 +87,7 @@ const states = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="workers"> <QItemSection v-if="workers">
<VnSelectFilter <VnSelect
:label="t('Attender')" :label="t('Attender')"
v-model="params.attenderFk" v-model="params.attenderFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -110,7 +110,7 @@ const states = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="workers"> <QItemSection v-if="workers">
<VnSelectFilter <VnSelect
:label="t('Responsible')" :label="t('Responsible')"
v-model="params.claimResponsibleFk" v-model="params.claimResponsibleFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -133,7 +133,7 @@ const states = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="states"> <QItemSection v-if="states">
<VnSelectFilter <VnSelect
:label="t('State')" :label="t('State')"
v-model="params.claimStateFk" v-model="params.claimStateFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"

View File

@ -0,0 +1,2 @@
Search claim: Buscar reclamación
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente

View File

@ -15,7 +15,7 @@ import { usePrintService } from 'src/composables/usePrintService';
import VnPaginate from 'src/components/ui/VnPaginate.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue'; import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
@ -316,7 +316,7 @@ const sendEmailAction = () => {
<QDrawer :width="256" show-if-above side="right" v-model="stateStore.rightDrawer"> <QDrawer :width="256" show-if-above side="right" v-model="stateStore.rightDrawer">
<div class="q-mt-xl q-px-md"> <div class="q-mt-xl q-px-md">
<VnSelectFilter <VnSelect
:label="t('Company')" :label="t('Company')"
:options="companiesOptions" :options="companiesOptions"
@update:model-value="updateCompanyId($event)" @update:model-value="updateCompanyId($event)"

View File

@ -7,7 +7,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue'; import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
@ -49,7 +49,7 @@ const getBankEntities = (data, formData) => {
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Billing data')" :label="t('Billing data')"
:options="payMethods" :options="payMethods"
hide-selected hide-selected

View File

@ -1,43 +1,15 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import VnCard from 'components/common/VnCard.vue';
import { useStateStore } from 'stores/useStateStore';
import CustomerDescriptor from './CustomerDescriptor.vue'; import CustomerDescriptor from './CustomerDescriptor.vue';
import LeftMenu from 'components/LeftMenu.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import useCardSize from 'src/composables/useCardSize';
const stateStore = useStateStore();
const { t } = useI18n();
</script> </script>
<template> <template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()"> <VnCard
<VnSearchbar data-key="Client"
data-key="CustomerList" base-url="Clients"
url="Clients/filter" :descriptor="CustomerDescriptor"
:label="t('Search customer')" searchbar-data-key="CustomerList"
:info="t('You can search by customer id or name')" searchbar-url="Clients/filter"
/> searchbar-label="Search customer"
</Teleport> searchbar-info="You can search by customer id or name"
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> />
<QScrollArea class="fit">
<CustomerDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="useCardSize()">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>
<i18n>
es:
Search customer: Buscar cliente
You can search by customer id or name: Puedes buscar por id o nombre del cliente
</i18n>

View File

@ -7,7 +7,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnLocation from 'src/components/common/VnLocation.vue'; import VnLocation from 'src/components/common/VnLocation.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -69,7 +69,7 @@ function handleLocation(data, location) {
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Sage tax type')" :label="t('Sage tax type')"
:options="typesTaxes" :options="typesTaxes"
hide-selected hide-selected
@ -79,7 +79,7 @@ function handleLocation(data, location) {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Sage transaction type')" :label="t('Sage transaction type')"
:options="typesTransactions" :options="typesTransactions"
hide-selected hide-selected
@ -97,7 +97,7 @@ function handleLocation(data, location) {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
</VnRow> </VnRow>

View File

@ -8,7 +8,7 @@ import { useStateStore } from 'stores/useStateStore';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
@ -139,7 +139,7 @@ const setInq = (value, status) => {
</QIcon> </QIcon>
</template> </template>
</VnInput> </VnInput>
<VnSelectFilter <VnSelect
:label="t('Entity')" :label="t('Entity')"
:options="[]" :options="[]"
class="q-mt-md" class="q-mt-md"
@ -179,7 +179,7 @@ const setInq = (value, status) => {
/> />
</div> </div>
<VnSelectFilter <VnSelect
:label="t('User')" :label="t('User')"
:options="[]" :options="[]"
class="q-mt-sm" class="q-mt-sm"

View File

@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnLocation from 'src/components/common/VnLocation.vue'; import VnLocation from 'src/components/common/VnLocation.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
@ -53,7 +53,7 @@ function handleLocation(data, location) {
<QInput :label="t('Comercial name')" v-model="data.name" /> <QInput :label="t('Comercial name')" v-model="data.name" />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Salesperson')" :label="t('Salesperson')"
:options="workersOptions" :options="workersOptions"
hide-selected hide-selected
@ -65,7 +65,7 @@ function handleLocation(data, location) {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Business type')" :label="t('Business type')"
:options="businessTypesOptions" :options="businessTypesOptions"
hide-selected hide-selected

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -69,7 +69,7 @@ const zones = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="workers"> <QItemSection v-if="workers">
<VnSelectFilter <VnSelect
:label="t('Salesperson')" :label="t('Salesperson')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -92,7 +92,7 @@ const zones = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="provinces"> <QItemSection v-if="provinces">
<VnSelectFilter <VnSelect
:label="t('Province')" :label="t('Province')"
v-model="params.provinceFk" v-model="params.provinceFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -139,7 +139,7 @@ const zones = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="zones"> <QItemSection v-if="zones">
<VnSelectFilter <VnSelect
:label="t('Zone')" :label="t('Zone')"
v-model="params.zoneFk" v-model="params.zoneFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"

View File

@ -6,7 +6,7 @@ import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -48,7 +48,7 @@ const authors = ref();
<template #body="{ params }"> <template #body="{ params }">
<QItem class="q-mb-sm q-mt-sm"> <QItem class="q-mb-sm q-mt-sm">
<QItemSection v-if="clients"> <QItemSection v-if="clients">
<VnSelectFilter <VnSelect
:input-debounce="0" :input-debounce="0"
:label="t('Client')" :label="t('Client')"
:options="clients" :options="clients"
@ -71,7 +71,7 @@ const authors = ref();
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">
<QItemSection v-if="salespersons"> <QItemSection v-if="salespersons">
<VnSelectFilter <VnSelect
:input-debounce="0" :input-debounce="0"
:label="t('Salesperson')" :label="t('Salesperson')"
:options="salespersons" :options="salespersons"
@ -94,7 +94,7 @@ const authors = ref();
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">
<QItemSection v-if="countries"> <QItemSection v-if="countries">
<VnSelectFilter <VnSelect
:input-debounce="0" :input-debounce="0"
:label="t('Country')" :label="t('Country')"
:options="countries" :options="countries"
@ -139,7 +139,7 @@ const authors = ref();
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">
<QItemSection v-if="authors"> <QItemSection v-if="authors">
<VnSelectFilter <VnSelect
:input-debounce="0" :input-debounce="0"
:label="t('Author')" :label="t('Author')"
:options="authors" :options="authors"

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import { dateRange } from 'src/filters'; import { dateRange } from 'src/filters';
@ -169,7 +169,7 @@ const shouldRenderColumn = (colName) => {
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="clients"> <QItemSection v-if="clients">
<VnSelectFilter <VnSelect
:label="t('Social name')" :label="t('Social name')"
v-model="params.socialName" v-model="params.socialName"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -201,7 +201,7 @@ const shouldRenderColumn = (colName) => {
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="workers"> <QItemSection v-if="workers">
<VnSelectFilter <VnSelect
:label=" :label="
t('customer.extendedList.tableVisibleColumns.salesPersonFk') t('customer.extendedList.tableVisibleColumns.salesPersonFk')
" "
@ -270,7 +270,7 @@ const shouldRenderColumn = (colName) => {
</QItem> </QItem>
<QItem v-if="shouldRenderColumn('countryFk')"> <QItem v-if="shouldRenderColumn('countryFk')">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('customer.extendedList.tableVisibleColumns.countryFk')" :label="t('customer.extendedList.tableVisibleColumns.countryFk')"
v-model="params.countryFk" v-model="params.countryFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -287,7 +287,7 @@ const shouldRenderColumn = (colName) => {
</QItem> </QItem>
<QItem v-if="shouldRenderColumn('provinceFk')"> <QItem v-if="shouldRenderColumn('provinceFk')">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('customer.extendedList.tableVisibleColumns.provinceFk')" :label="t('customer.extendedList.tableVisibleColumns.provinceFk')"
v-model="params.provinceFk" v-model="params.provinceFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -342,7 +342,7 @@ const shouldRenderColumn = (colName) => {
</QItem> </QItem>
<QItem v-if="shouldRenderColumn('businessTypeFk')"> <QItem v-if="shouldRenderColumn('businessTypeFk')">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label=" :label="
t('customer.extendedList.tableVisibleColumns.businessTypeFk') t('customer.extendedList.tableVisibleColumns.businessTypeFk')
" "
@ -361,7 +361,7 @@ const shouldRenderColumn = (colName) => {
</QItem> </QItem>
<QItem v-if="shouldRenderColumn('payMethodFk')"> <QItem v-if="shouldRenderColumn('payMethodFk')">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label=" :label="
t('customer.extendedList.tableVisibleColumns.payMethodFk') t('customer.extendedList.tableVisibleColumns.payMethodFk')
" "
@ -380,7 +380,7 @@ const shouldRenderColumn = (colName) => {
</QItem> </QItem>
<QItem v-if="shouldRenderColumn('sageTaxTypeFk')"> <QItem v-if="shouldRenderColumn('sageTaxTypeFk')">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label=" :label="
t('customer.extendedList.tableVisibleColumns.sageTaxTypeFk') t('customer.extendedList.tableVisibleColumns.sageTaxTypeFk')
" "
@ -399,7 +399,7 @@ const shouldRenderColumn = (colName) => {
</QItem> </QItem>
<QItem v-if="shouldRenderColumn('sageTransactionTypeFk')"> <QItem v-if="shouldRenderColumn('sageTransactionTypeFk')">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label=" :label="
t( t(
'customer.extendedList.tableVisibleColumns.sageTransactionTypeFk' 'customer.extendedList.tableVisibleColumns.sageTransactionTypeFk'

View File

@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -52,7 +52,7 @@ const clients = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="clients"> <QItemSection v-if="clients">
<VnSelectFilter <VnSelect
:input-debounce="0" :input-debounce="0"
:label="t('Social name')" :label="t('Social name')"
:options="clients" :options="clients"
@ -76,7 +76,7 @@ const clients = ref();
<QSkeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</QItemSection> </QItemSection>
<QItemSection v-if="cities"> <QItemSection v-if="cities">
<VnSelectFilter <VnSelect
:input-debounce="0" :input-debounce="0"
:label="t('City')" :label="t('City')"
:options="cities" :options="cities"

View File

@ -9,7 +9,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue'; import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
import CustomerNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue'; import CustomerNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue';
@ -101,7 +101,7 @@ function handleLocation(data, location) {
/> />
<div class="row justify-between q-gutter-md q-mb-md"> <div class="row justify-between q-gutter-md q-mb-md">
<VnSelectFilter <VnSelect
:label="t('Agency')" :label="t('Agency')"
:options="agencyModes" :options="agencyModes"
:rules="validate('route.agencyFk')" :rules="validate('route.agencyFk')"
@ -121,7 +121,7 @@ function handleLocation(data, location) {
</div> </div>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<VnSelectFilter <VnSelect
:label="t('Incoterms')" :label="t('Incoterms')"
:options="incoterms" :options="incoterms"
hide-selected hide-selected

View File

@ -9,7 +9,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue'; import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue'; import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue';
@ -185,7 +185,7 @@ function handleLocation(data, location) {
></VnLocation> ></VnLocation>
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Agency')" :label="t('Agency')"
:options="agencyModes" :options="agencyModes"
:rules="validate('route.agencyFk')" :rules="validate('route.agencyFk')"
@ -205,7 +205,7 @@ function handleLocation(data, location) {
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Incoterms')" :label="t('Incoterms')"
:options="incoterms" :options="incoterms"
hide-selected hide-selected
@ -237,7 +237,7 @@ function handleLocation(data, location) {
v-for="(note, index) in notes" v-for="(note, index) in notes"
> >
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Observation type')" :label="t('Observation type')"
:options="observationTypes" :options="observationTypes"
hide-selected hide-selected

View File

@ -11,7 +11,7 @@ import useNotify from 'src/composables/useNotify';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
const { notify } = useNotify(); const { notify } = useNotify();
@ -152,7 +152,7 @@ const toCustomerFileManagement = () => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Company')" :label="t('Company')"
:options="optionsCompanies" :options="optionsCompanies"
:rules="validate('entry.companyFk')" :rules="validate('entry.companyFk')"
@ -165,7 +165,7 @@ const toCustomerFileManagement = () => {
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Warehouse')" :label="t('Warehouse')"
:options="optionsWarehouses" :options="optionsWarehouses"
option-label="name" option-label="name"
@ -174,7 +174,7 @@ const toCustomerFileManagement = () => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Type')" :label="t('Type')"
:options="optionsDmsTypes" :options="optionsDmsTypes"
option-label="name" option-label="name"

View File

@ -10,7 +10,7 @@ import useNotify from 'src/composables/useNotify';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
const { notify } = useNotify(); const { notify } = useNotify();
@ -128,7 +128,7 @@ const toCustomerFileManagement = () => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Company')" :label="t('Company')"
:options="optionsCompanies" :options="optionsCompanies"
:rules="validate('entry.companyFk')" :rules="validate('entry.companyFk')"
@ -141,7 +141,7 @@ const toCustomerFileManagement = () => {
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Warehouse')" :label="t('Warehouse')"
:options="optionsWarehouses" :options="optionsWarehouses"
option-label="name" option-label="name"
@ -150,7 +150,7 @@ const toCustomerFileManagement = () => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Type')" :label="t('Type')"
:options="optionsDmsTypes" :options="optionsDmsTypes"
option-label="name" option-label="name"

View File

@ -8,7 +8,7 @@ import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
@ -74,7 +74,7 @@ const toCustomerGreuges = () => {
<VnInput :label="t('Comment')" clearable v-model="data.description" /> <VnInput :label="t('Comment')" clearable v-model="data.description" />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Type')" :label="t('Type')"
:options="greugeTypes" :options="greugeTypes"
hide-selected hide-selected

View File

@ -9,7 +9,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -143,7 +143,7 @@ const onDataSaved = async () => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Company')" :label="t('Company')"
:options="companyOptions" :options="companyOptions"
:required="true" :required="true"
@ -158,7 +158,7 @@ const onDataSaved = async () => {
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Bank')" :label="t('Bank')"
:options="bankOptions" :options="bankOptions"
:required="true" :required="true"
@ -177,7 +177,7 @@ const onDataSaved = async () => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
<div class="col"> <div class="col">
<VnInput <VnInput

View File

@ -12,7 +12,7 @@ import useNotify from 'src/composables/useNotify';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import CustomerSamplesPreview from 'src/pages/Customer/components/CustomerSamplesPreview.vue'; import CustomerSamplesPreview from 'src/pages/Customer/components/CustomerSamplesPreview.vue';
@ -253,7 +253,7 @@ const toCustomerSamples = () => {
<QCard class="card-width q-pa-lg"> <QCard class="card-width q-pa-lg">
<QForm> <QForm>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Sample')" :label="t('Sample')"
:options="optionsSamplesVisible" :options="optionsSamplesVisible"
@update:model-value="setSampleType" @update:model-value="setSampleType"
@ -306,7 +306,7 @@ const toCustomerSamples = () => {
v-if="sampleType?.hasCompany || sampleType?.datepickerEnabled" v-if="sampleType?.hasCompany || sampleType?.datepickerEnabled"
> >
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Company')" :label="t('Company')"
:options="optionsCompanies" :options="optionsCompanies"
:rules="validate('entry.companyFk')" :rules="validate('entry.companyFk')"
@ -319,7 +319,7 @@ const toCustomerSamples = () => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Address')" :label="t('Address')"
:options="optionsClientsAddressess" :options="optionsClientsAddressess"
hide-selected hide-selected
@ -345,7 +345,7 @@ const toCustomerSamples = () => {
<QTooltip>{{ t('Edit address') }}</QTooltip> <QTooltip>{{ t('Edit address') }}</QTooltip>
</QIcon> </QIcon>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
</VnRow> </VnRow>

View File

@ -1,3 +1,5 @@
Search customer: Buscar cliente
You can search by customer id or name: Puedes buscar por id o nombre del cliente
customerFilter: customerFilter:
filter: filter:
name: Nombre name: Nombre

View File

@ -7,7 +7,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -68,7 +68,7 @@ const clientsOptions = ref([]);
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('department.bossDepartment')" :label="t('department.bossDepartment')"
v-model="data.workerFk" v-model="data.workerFk"
:options="workersOptions" :options="workersOptions"
@ -80,7 +80,7 @@ const clientsOptions = ref([]);
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('department.selfConsumptionCustomer')" :label="t('department.selfConsumptionCustomer')"
v-model="data.clientFk" v-model="data.clientFk"
:options="clientsOptions" :options="clientsOptions"

View File

@ -1,26 +1,13 @@
<script setup> <script setup>
import { useStateStore } from 'stores/useStateStore'; import VnCard from 'components/common/VnCard.vue';
import DepartmentDescriptor from 'pages/Department/Card/DepartmentDescriptor.vue'; import DepartmentDescriptor from 'pages/Department/Card/DepartmentDescriptor.vue';
import LeftMenu from 'components/LeftMenu.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
const stateStore = useStateStore();
</script> </script>
<template> <template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <VnCard
<QScrollArea class="fit"> class="q-pa-md column items-center"
<DepartmentDescriptor /> v-bind="{ ...$attrs }"
<QSeparator /> data-key="Department"
<LeftMenu source="card" /> base-url="Departments"
</QScrollArea> :descriptor="DepartmentDescriptor"
</QDrawer> />
<QPageContainer>
<QPage>
<VnSubToolbar />
<div class="q-pa-md column items-center">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>

View File

@ -27,6 +27,7 @@ onMounted(async () => {
<template> <template>
<CardSummary <CardSummary
data-key="DepartmentSummary"
ref="summary" ref="summary"
:url="`Departments/${entityId}`" :url="`Departments/${entityId}`"
class="full-width" class="full-width"

View File

@ -8,7 +8,7 @@ import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import FilterTravelForm from 'src/components/FilterTravelForm.vue'; import FilterTravelForm from 'src/components/FilterTravelForm.vue';
@ -69,7 +69,7 @@ const onFilterTravelSelected = (formData, id) => {
<template #form="{ data }"> <template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.basicData.supplier')" :label="t('entry.basicData.supplier')"
v-model="data.supplierFk" v-model="data.supplierFk"
:options="suppliersOptions" :options="suppliersOptions"
@ -89,7 +89,7 @@ const onFilterTravelSelected = (formData, id) => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
<div class="col"> <div class="col">
<VnSelectDialog <VnSelectDialog
@ -141,7 +141,7 @@ const onFilterTravelSelected = (formData, id) => {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.basicData.company')" :label="t('entry.basicData.company')"
v-model="data.companyFk" v-model="data.companyFk"
:options="companiesOptions" :options="companiesOptions"
@ -155,7 +155,7 @@ const onFilterTravelSelected = (formData, id) => {
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('entry.basicData.currency')" :label="t('entry.basicData.currency')"
v-model="data.currencyFk" v-model="data.currencyFk"
:options="currenciesOptions" :options="currenciesOptions"

View File

@ -6,7 +6,7 @@ import { QBtn } from 'quasar';
import VnPaginate from 'src/components/ui/VnPaginate.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FetchedTags from 'components/ui/FetchedTags.vue'; import FetchedTags from 'components/ui/FetchedTags.vue';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
@ -59,7 +59,7 @@ const tableColumnComponents = computed(() => ({
event: getInputEvents, event: getInputEvents,
}, },
packagingFk: { packagingFk: {
component: VnSelectFilter, component: VnSelect,
props: { props: {
'option-value': 'id', 'option-value': 'id',
'option-label': 'id', 'option-label': 'id',

View File

@ -6,7 +6,7 @@ import { useI18n } from 'vue-i18n';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import FilterItemForm from 'src/components/FilterItemForm.vue'; import FilterItemForm from 'src/components/FilterItemForm.vue';
@ -269,7 +269,7 @@ const redirectToBuysView = () => {
</template> </template>
<template #body-cell-packagingFk="{ row, col }"> <template #body-cell-packagingFk="{ row, col }">
<QTd auto-width> <QTd auto-width>
<VnSelectFilter <VnSelect
v-model="row[col.field]" v-model="row[col.field]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"

View File

@ -1,48 +1,15 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import VnCard from 'components/common/VnCard.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import LeftMenu from 'components/LeftMenu.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import EntryDescriptor from './EntryDescriptor.vue'; import EntryDescriptor from './EntryDescriptor.vue';
import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize';
const { t } = useI18n();
const stateStore = useStateStore();
</script> </script>
<template> <template>
<template v-if="stateStore.isHeaderMounted()"> <VnCard
<Teleport to="#searchbar"> data-key="Entry"
<VnSearchbar base-url="Entries"
data-key="EntryList" :descriptor="EntryDescriptor"
url="Entries/filter" searchbar-data-key="EntryList"
:label="t('Search entries')" searchbar-url="Entries/filter"
:info="t('You can search by entry reference')" searchbar-label="Search entries"
/> searchbar-info="You can search by entry reference"
</Teleport> />
</template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<EntryDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="useCardSize()">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>
<i18n>
es:
Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada
</i18n>

View File

@ -6,7 +6,7 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import CrudModel from 'components/CrudModel.vue'; import CrudModel from 'components/CrudModel.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const { params } = useRoute(); const { params } = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -80,7 +80,7 @@ const columns = computed(() => [
> >
<template #body-cell-observationType="{ row, col }"> <template #body-cell-observationType="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
@ -111,7 +111,7 @@ const columns = computed(() => [
<QList dense> <QList dense>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
v-model="props.row.observationTypeFk" v-model="props.row.observationTypeFk"
:options="entryObservationsOptions" :options="entryObservationsOptions"
option-value="id" option-value="id"

View File

@ -5,7 +5,7 @@ import { useRoute, useRouter } from 'vue-router';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
@ -80,7 +80,7 @@ const redirectToEntryBasicData = (_, { id }) => {
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Supplier')" :label="t('Supplier')"
class="full-width" class="full-width"
v-model="data.supplierFk" v-model="data.supplierFk"
@ -101,12 +101,12 @@ const redirectToEntryBasicData = (_, { id }) => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Travel')" :label="t('Travel')"
class="full-width" class="full-width"
v-model="data.travelFk" v-model="data.travelFk"
@ -133,12 +133,12 @@ const redirectToEntryBasicData = (_, { id }) => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Company')" :label="t('Company')"
class="full-width" class="full-width"
v-model="data.companyFk" v-model="data.companyFk"

View File

@ -5,7 +5,7 @@ import { onMounted } from 'vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
@ -99,7 +99,7 @@ onMounted(async () => {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('params.companyFk')" :label="t('params.companyFk')"
v-model="params.companyFk" v-model="params.companyFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -115,7 +115,7 @@ onMounted(async () => {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('params.currencyFk')" :label="t('params.currencyFk')"
v-model="params.currencyFk" v-model="params.currencyFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -131,7 +131,7 @@ onMounted(async () => {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('params.supplierFk')" :label="t('params.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -152,7 +152,7 @@ onMounted(async () => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>

View File

@ -9,7 +9,7 @@ import EntryDescriptorProxy from './Card/EntryDescriptorProxy.vue';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue'; import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue'; import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import EntryLatestBuysFilter from './EntryLatestBuysFilter.vue'; import EntryLatestBuysFilter from './EntryLatestBuysFilter.vue';
import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue';
@ -218,7 +218,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelectFilter, component: VnSelect,
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -237,7 +237,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelectFilter, component: VnSelect,
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -256,7 +256,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelectFilter, component: VnSelect,
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -309,7 +309,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelectFilter, component: VnSelect,
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -513,7 +513,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
sortable: true, sortable: true,
columnFilter: { columnFilter: {
component: VnSelectFilter, component: VnSelect,
type: 'select', type: 'select',
filterValue: null, filterValue: null,
event: getInputEvents, event: getInputEvents,
@ -650,7 +650,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</QToolbar> </QToolbar>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<EntryLatestBuysFilter data-key="EntryLatestBuys" :tags="tags" /> <EntryLatestBuysFilter data-key="EntryLatestBuys" />
</QScrollArea> </QScrollArea>
</QDrawer> </QDrawer>
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">

View File

@ -1,141 +1,26 @@
<script setup> <script setup>
import { computed, ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnInput from 'components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
import axios from 'axios';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({
defineProps({
dataKey: { dataKey: {
type: String, type: String,
required: true, required: true,
}, },
}); });
const itemCategories = ref([]);
const selectedCategoryFk = ref(null);
const selectedTypeFk = ref(null);
const itemTypesOptions = ref([]);
const itemTypeWorkersOptions = ref([]); const itemTypeWorkersOptions = ref([]);
const suppliersOptions = ref([]); const suppliersOptions = ref([]);
const tagOptions = ref([]);
const tagValues = ref([]);
const categoryList = computed(() => {
return (itemCategories.value || [])
.filter((category) => category.display)
.map((category) => ({
...category,
icon: `vn:${(category.icon || '').split('-')[1]}`,
}));
});
const selectedCategory = computed(() =>
(itemCategories.value || []).find(
(category) => category?.id === selectedCategoryFk.value
)
);
const selectedType = computed(() => {
return (itemTypesOptions.value || []).find(
(type) => type?.id === selectedTypeFk.value
);
});
const selectCategory = async (params, categoryId, search) => {
if (params.categoryFk === categoryId) {
resetCategory(params);
search();
return;
}
selectedCategoryFk.value = categoryId;
params.categoryFk = categoryId;
await fetchItemTypes(categoryId);
search();
};
const resetCategory = (params) => {
selectedCategoryFk.value = null;
itemTypesOptions.value = null;
if (params) {
params.categoryFk = null;
params.typeFk = null;
}
};
const applyTags = (params, search) => {
params.tags = tagValues.value
.filter((tag) => tag.selectedTag && tag.value)
.map((tag) => ({
tagFk: tag.selectedTag.id,
tagName: tag.selectedTag.name,
value: tag.value,
}));
search();
};
const fetchItemTypes = async (id) => {
try {
const filter = {
fields: ['id', 'name', 'categoryFk'],
where: { categoryFk: id },
include: 'category',
order: 'name ASC',
};
const { data } = await axios.get('ItemTypes', {
params: { filter: JSON.stringify(filter) },
});
itemTypesOptions.value = data;
} catch (err) {
console.error('Error fetching item types', err);
}
};
const getCategoryClass = (category, params) => {
if (category.id === params?.categoryFk) {
return 'active';
}
};
const getSelectedTagValues = async (tag) => {
try {
tag.value = null;
const filter = {
fields: ['value'],
order: 'value ASC',
limit: 30,
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get(`Tags/${tag.selectedTag.id}/filterValue`, {
params,
});
tag.valueOptions = data;
} catch (err) {
console.error('Error getting selected tag values');
}
};
const removeTag = (index, params, search) => {
(tagValues.value || []).splice(index, 1);
applyTags(params, search);
};
</script> </script>
<template> <template>
<FetchData
url="ItemCategories"
limit="30"
auto-load
@on-fetch="(data) => (itemCategories = data)"
/>
<FetchData <FetchData
url="TicketRequests/getItemTypeWorker" url="TicketRequests/getItemTypeWorker"
limit="30" limit="30"
@ -150,102 +35,12 @@ const removeTag = (index, params, search) => {
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (suppliersOptions = data)" @on-fetch="(data) => (suppliersOptions = data)"
/> />
<FetchData <ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
url="Tags"
:filter="{ fields: ['id', 'name', 'isFree'] }"
auto-load
limit="30"
@on-fetch="(data) => (tagOptions = data)"
/>
<VnFilterPanel
:data-key="props.dataKey"
:expr-builder="exprBuilder"
:custom-tags="['tags']"
@init="onFilterInit"
@remove="clearFilter"
>
<template #tags="{ tag, formatFn }">
<strong v-if="tag.label === 'categoryFk'">
{{ t(selectedCategory?.name || '') }}
</strong>
<strong v-else-if="tag.label === 'typeFk'">
{{ t(selectedType?.name || '') }}
</strong>
<div v-else class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #customTags="{ tags: customTags, params }">
<template v-for="tag in customTags" :key="tag.label">
<VnFilterPanelChip
v-for="chip in tag.value"
:key="chip"
removable
@remove="removeTagChip(chip, params, searchFn)"
>
<div class="q-gutter-x-xs">
<strong>{{ chip.tagName }}: </strong>
<span>"{{ chip.value }}"</span>
</div>
</VnFilterPanelChip>
</template>
</template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem class="category-filter q-mt-md">
<QBtn
dense
flat
round
v-for="category in categoryList"
:key="category.name"
:class="['category', getCategoryClass(category, params)]"
:icon="category.icon"
@click="selectCategory(params, category.id, searchFn)"
>
<QTooltip>
{{ t(category.name) }}
</QTooltip>
</QBtn>
</QItem>
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('params.typeFk')" :label="t('components.itemsFilterPanel.salesPersonFk')"
v-model="params.typeFk"
:options="itemTypesOptions"
option-value="id"
option-label="name"
dense
outlined
rounded
use-input
:disable="!selectedCategoryFk"
@update:model-value="
(value) => {
selectedTypeFk = value;
searchFn();
}
"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel>{{ opt.name }}</QItemLabel>
<QItemLabel caption>
{{ opt.categoryName }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</QItemSection>
</QItem>
<QSeparator />
<QItem class="q-my-md">
<QItemSection>
<VnSelectFilter
:label="t('params.salesPersonFk')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
:options="itemTypeWorkersOptions" :options="itemTypeWorkersOptions"
option-value="id" option-value="id"
@ -260,8 +55,8 @@ const removeTag = (index, params, search) => {
</QItem> </QItem>
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('params.supplier')" :label="t('components.itemsFilterPanel.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
:options="suppliersOptions" :options="suppliersOptions"
option-value="id" option-value="id"
@ -282,13 +77,13 @@ const removeTag = (index, params, search) => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnInputDate <VnInputDate
:label="t('params.from')" :label="t('components.itemsFilterPanel.from')"
v-model="params.from" v-model="params.from"
is-outlined is-outlined
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -298,7 +93,7 @@ const removeTag = (index, params, search) => {
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnInputDate <VnInputDate
:label="t('params.to')" :label="t('components.itemsFilterPanel.to')"
v-model="params.to" v-model="params.to"
is-outlined is-outlined
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -308,7 +103,7 @@ const removeTag = (index, params, search) => {
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('params.active')" :label="t('components.itemsFilterPanel.active')"
v-model="params.active" v-model="params.active"
toggle-indeterminate toggle-indeterminate
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -316,7 +111,7 @@ const removeTag = (index, params, search) => {
</QItemSection> </QItemSection>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('params.visible')" :label="t('components.itemsFilterPanel.visible')"
v-model="params.visible" v-model="params.visible"
toggle-indeterminate toggle-indeterminate
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -326,7 +121,7 @@ const removeTag = (index, params, search) => {
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('params.floramondo')" :label="t('components.itemsFilterPanel.floramondo')"
v-model="params.floramondo" v-model="params.floramondo"
toggle-indeterminate toggle-indeterminate
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -340,7 +135,7 @@ const removeTag = (index, params, search) => {
class="q-mt-md filter-value" class="q-mt-md filter-value"
> >
<QItemSection class="col"> <QItemSection class="col">
<VnSelectFilter <VnSelect
:label="t('params.tag')" :label="t('params.tag')"
v-model="value.selectedTag" v-model="value.selectedTag"
:options="tagOptions" :options="tagOptions"
@ -355,7 +150,7 @@ const removeTag = (index, params, search) => {
/> />
</QItemSection> </QItemSection>
<QItemSection class="col"> <QItemSection class="col">
<VnSelectFilter <VnSelect
v-if="!value?.selectedTag?.isFree && value.valueOptions" v-if="!value?.selectedTag?.isFree && value.valueOptions"
:label="t('params.value')" :label="t('params.value')"
v-model="value.value" v-model="value.value"
@ -397,78 +192,5 @@ const removeTag = (index, params, search) => {
/> />
</QItem> </QItem>
</template> </template>
</VnFilterPanel> </ItemsFilterPanel>
</template> </template>
<style lang="scss" scoped>
.category-filter {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
.category {
padding: 8px;
width: 60px;
height: 60px;
font-size: 1.4rem;
background-color: var(--vn-accent-color);
&.active {
background-color: $primary;
}
}
}
.filter-icon {
font-size: 24px;
color: $primary;
padding: 0 4px;
cursor: pointer;
}
.filter-input {
flex-shrink: 1;
min-width: 0;
}
.filter-value {
display: flex;
align-items: center;
}
</style>
<i18n>
en:
params:
supplier: Supplier
from: From
to: To
active: Is active
visible: Is visible
floramondo: Is floramondo
salesPersonFk: Buyer
categoryFk: Category
typeFk: Type
tag: Tag
value: Value
es:
params:
supplier: Proveedor
from: Desde
to: Hasta
active: Activo
visible: Visible
floramondo: Floramondo
salesPersonFk: Comprador
categoryFk: Categoría
typeFk: Tipo
tag: Etiqueta
value: Valor
Plant: Planta
Flower: Flor
Handmade: Confección
Green: Verde
Accessories: Complemento
Fruit: Fruta
</i18n>

View File

@ -1,3 +1,5 @@
Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada
entryList: entryList:
list: list:
inventoryEntry: Es inventario inventoryEntry: Es inventario

View File

@ -7,7 +7,7 @@ import { useArrayData } from 'src/composables/useArrayData';
import { downloadFile } from 'src/composables/downloadFile'; import { downloadFile } from 'src/composables/downloadFile';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import axios from 'axios'; import axios from 'axios';
@ -183,7 +183,7 @@ async function upsert() {
<template #form="{ data }"> <template #form="{ data }">
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('supplierFk')" :label="t('supplierFk')"
v-model="data.supplierFk" v-model="data.supplierFk"
option-value="id" option-value="id"
@ -202,7 +202,7 @@ async function upsert() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</div> </div>
<div class="col"> <div class="col">
<QInput <QInput
@ -401,7 +401,7 @@ async function upsert() {
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
:label="t('Currency')" :label="t('Currency')"
v-model="data.currencyFk" v-model="data.currencyFk"
:options="currencies" :options="currencies"
@ -410,7 +410,7 @@ async function upsert() {
/> />
</div> </div>
<div class="col"> <div class="col">
<VnSelectFilter <VnSelect
v-if="companiesRef" v-if="companiesRef"
:label="t('Company')" :label="t('Company')"
v-model="data.companyFk" v-model="data.companyFk"
@ -451,7 +451,7 @@ async function upsert() {
clearable clearable
clear-icon="close" clear-icon="close"
/> />
<VnSelectFilter <VnSelect
class="full-width q-pa-xs" class="full-width q-pa-xs"
:label="`${t('Company')}*`" :label="`${t('Company')}*`"
v-model="dms.companyId" v-model="dms.companyId"
@ -462,7 +462,7 @@ async function upsert() {
/> />
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
class="full-width q-pa-xs" class="full-width q-pa-xs"
:label="`${t('Warehouse')}*`" :label="`${t('Warehouse')}*`"
v-model="dms.warehouseId" v-model="dms.warehouseId"
@ -471,7 +471,7 @@ async function upsert() {
option-label="name" option-label="name"
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
/> />
<VnSelectFilter <VnSelect
class="full-width q-pa-xs" class="full-width q-pa-xs"
:label="`${t('Type')}*`" :label="`${t('Type')}*`"
v-model="dms.dmsTypeId" v-model="dms.dmsTypeId"
@ -560,7 +560,7 @@ async function upsert() {
:label="t('Reference')" :label="t('Reference')"
v-model="dms.reference" v-model="dms.reference"
/> />
<VnSelectFilter <VnSelect
class="full-width q-pa-xs" class="full-width q-pa-xs"
:label="`${t('Company')}*`" :label="`${t('Company')}*`"
v-model="dms.companyId" v-model="dms.companyId"
@ -571,7 +571,7 @@ async function upsert() {
/> />
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
class="full-width q-pa-xs" class="full-width q-pa-xs"
:label="`${t('Warehouse')}*`" :label="`${t('Warehouse')}*`"
v-model="dms.warehouseId" v-model="dms.warehouseId"
@ -580,7 +580,7 @@ async function upsert() {
option-label="name" option-label="name"
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
/> />
<VnSelectFilter <VnSelect
class="full-width q-pa-xs" class="full-width q-pa-xs"
:label="`${t('Type')}*`" :label="`${t('Type')}*`"
v-model="dms.dmsTypeId" v-model="dms.dmsTypeId"

View File

@ -1,18 +1,6 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import VnCard from 'components/common/VnCard.vue';
import { useStateStore } from 'stores/useStateStore';
import InvoiceInDescriptor from './InvoiceInDescriptor.vue'; import InvoiceInDescriptor from './InvoiceInDescriptor.vue';
import LeftMenu from 'components/LeftMenu.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import { useArrayData } from 'src/composables/useArrayData';
import { onMounted, watch } from 'vue';
import { useRoute } from 'vue-router';
import useCardSize from 'src/composables/useCardSize';
const stateStore = useStateStore();
const { t } = useI18n();
const route = useRoute();
const filter = { const filter = {
include: [ include: [
@ -21,69 +9,25 @@ const filter = {
scope: { scope: {
include: { include: {
relation: 'contacts', relation: 'contacts',
scope: { scope: { where: { email: { neq: null } } },
where: {
email: { neq: null },
},
},
}, },
}, },
}, },
{ { relation: 'invoiceInDueDay' },
relation: 'invoiceInDueDay', { relation: 'company' },
}, { relation: 'currency' },
{
relation: 'company',
},
{
relation: 'currency',
},
], ],
}; };
const arrayData = useArrayData('InvoiceIn', {
url: `InvoiceIns/${route.params.id}`,
filter,
});
onMounted(async () => await arrayData.fetch({ append: false }));
watch(
() => route.params.id,
async (newId, oldId) => {
if (newId) {
arrayData.store.url = `InvoiceIns/${newId}`;
await arrayData.fetch({ append: false });
}
}
);
</script> </script>
<template> <template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()"> <VnCard
<VnSearchbar data-key="InvoiceIn"
data-key="InvoiceInList" base-url="InvoiceIns"
url="InvoiceIns/filter" :filter="filter"
:label="t('Search invoice')" :descriptor="InvoiceInDescriptor"
:info="t('You can search by invoice reference')" searchbar-data-key="InvoiceInList"
/> searchbar-url="InvoiceIns/filter"
</Teleport> searchbar-label="Search invoice"
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> searchbar-info="You can search by invoice reference"
<QScrollArea class="fit"> />
<InvoiceInDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="useCardSize()">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>
<i18n>
es:
Search invoice: Buscar factura recibida
You can search by invoice reference: Puedes buscar por referencia de la factura
</i18n>

View File

@ -6,7 +6,7 @@ import { useArrayData } from 'src/composables/useArrayData';
import { useCapitalize } from 'src/composables/useCapitalize'; import { useCapitalize } from 'src/composables/useCapitalize';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -116,7 +116,7 @@ const onSave = (data) => data.deletes && router.push(`/invoice-in/${invoiceId}/s
> >
<template #body-cell-type="{ row, col }"> <template #body-cell-type="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
class="q-pb-md" class="q-pb-md"
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
@ -128,7 +128,7 @@ const onSave = (data) => data.deletes && router.push(`/invoice-in/${invoiceId}/s
</template> </template>
<template #body-cell-class="{ row, col }"> <template #body-cell-class="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
class="q-pb-md" class="q-pb-md"
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
@ -141,7 +141,7 @@ const onSave = (data) => data.deletes && router.push(`/invoice-in/${invoiceId}/s
</template> </template>
<template #body-cell-reason="{ row, col }"> <template #body-cell-reason="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
class="q-pb-md" class="q-pb-md"
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"

View File

@ -15,7 +15,7 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue'; import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import { useCapitalize } from 'src/composables/useCapitalize'; import { useCapitalize } from 'src/composables/useCapitalize';
const $props = defineProps({ const $props = defineProps({
@ -480,7 +480,7 @@ const createInvoiceInCorrection = async () => {
v-model="entityId" v-model="entityId"
readonly readonly
/> />
<VnSelectFilter <VnSelect
:label="`${useCapitalize(t('globals.class'))}*`" :label="`${useCapitalize(t('globals.class'))}*`"
v-model="correctionFormData.invoiceClass" v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceOuts" :options="siiTypeInvoiceOuts"
@ -490,7 +490,7 @@ const createInvoiceInCorrection = async () => {
/> />
</QItemSection> </QItemSection>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="`${useCapitalize(t('globals.type'))}*`" :label="`${useCapitalize(t('globals.type'))}*`"
v-model="correctionFormData.invoiceType" v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes" :options="cplusRectificationTypes"
@ -498,7 +498,7 @@ const createInvoiceInCorrection = async () => {
option-label="description" option-label="description"
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
/> />
<VnSelectFilter <VnSelect
:label="`${useCapitalize(t('globals.reason'))}*`" :label="`${useCapitalize(t('globals.reason'))}*`"
v-model="correctionFormData.invoiceReason" v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes" :options="invoiceCorrectionTypes"

View File

@ -7,7 +7,7 @@ import { toDate } from 'src/filters';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnCurrency from 'src/components/common/VnCurrency.vue'; import VnCurrency from 'src/components/common/VnCurrency.vue';
const route = useRoute(); const route = useRoute();
@ -143,7 +143,7 @@ async function insert() {
</template> </template>
<template #body-cell-bank="{ row, col }"> <template #body-cell-bank="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
@ -158,7 +158,7 @@ async function insert() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-amount="{ row }"> <template #body-cell-amount="{ row }">
@ -240,7 +240,7 @@ async function insert() {
</QInput> </QInput>
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
:label="t('Bank')" :label="t('Bank')"
class="full-width" class="full-width"
v-model="props.row['bankFk']" v-model="props.row['bankFk']"
@ -257,7 +257,7 @@ async function insert() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItem> </QItem>
<QItem> <QItem>
<QInput <QInput

View File

@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -147,7 +147,7 @@ function getTotal(type) {
</template> </template>
<template #body-cell-code="{ row, col }"> <template #body-cell-code="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
option-value="id" option-value="id"
@ -159,12 +159,12 @@ function getTotal(type) {
{{ `${scope.opt.id}: ${scope.opt.description}` }} {{ `${scope.opt.id}: ${scope.opt.description}` }}
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-country="{ row, col }"> <template #body-cell-country="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
option-value="id" option-value="id"
@ -181,7 +181,7 @@ function getTotal(type) {
<QSeparator /> <QSeparator />
<QList> <QList>
<QItem> <QItem>
<VnSelectFilter <VnSelect
:label="t('code')" :label="t('code')"
class="full-width" class="full-width"
v-model="props.row['intrastatFk']" v-model="props.row['intrastatFk']"
@ -197,7 +197,7 @@ function getTotal(type) {
}} }}
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItem> </QItem>
<QItem <QItem
v-for="(value, index) of [ v-for="(value, index) of [
@ -216,7 +216,7 @@ function getTotal(type) {
/> />
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
:label="t('country')" :label="t('country')"
class="full-width" class="full-width"
v-model="props.row['countryFk']" v-model="props.row['countryFk']"

View File

@ -7,7 +7,7 @@ import axios from 'axios';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import VnCurrency from 'src/components/common/VnCurrency.vue'; import VnCurrency from 'src/components/common/VnCurrency.vue';
@ -196,7 +196,7 @@ async function addExpense() {
> >
<template #body-cell-expense="{ row, col }"> <template #body-cell-expense="{ row, col }">
<QTd auto-width> <QTd auto-width>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
@ -225,7 +225,7 @@ async function addExpense() {
</QTooltip> </QTooltip>
</QIcon> </QIcon>
</template> </template>
</VnSelectFilter> </VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-taxablebase="{ row }"> <template #body-cell-taxablebase="{ row }">
@ -244,7 +244,7 @@ async function addExpense() {
</template> </template>
<template #body-cell-sageiva="{ row, col }"> <template #body-cell-sageiva="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
@ -263,12 +263,12 @@ async function addExpense() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-sagetransaction="{ row, col }"> <template #body-cell-sagetransaction="{ row, col }">
<QTd> <QTd>
<VnSelectFilter <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
@ -289,7 +289,7 @@ async function addExpense() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-foreignvalue="{ row }"> <template #body-cell-foreignvalue="{ row }">
@ -312,7 +312,7 @@ async function addExpense() {
<QSeparator /> <QSeparator />
<QList> <QList>
<QItem> <QItem>
<VnSelectFilter <VnSelect
:label="t('Expense')" :label="t('Expense')"
class="full-width" class="full-width"
v-model="props.row['expenseFk']" v-model="props.row['expenseFk']"
@ -326,7 +326,7 @@ async function addExpense() {
{{ `${scope.opt.id}: ${scope.opt.name}` }} {{ `${scope.opt.id}: ${scope.opt.name}` }}
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItem> </QItem>
<QItem> <QItem>
<VnCurrency <VnCurrency
@ -344,7 +344,7 @@ async function addExpense() {
/> />
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
:label="t('Sage iva')" :label="t('Sage iva')"
class="full-width" class="full-width"
v-model="props.row['taxTypeSageFk']" v-model="props.row['taxTypeSageFk']"
@ -365,10 +365,10 @@ async function addExpense() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItem> </QItem>
<QItem> <QItem>
<VnSelectFilter <VnSelect
class="full-width" class="full-width"
v-model="props.row['transactionTypeSageFk']" v-model="props.row['transactionTypeSageFk']"
:options="sageTransactionTypes" :options="sageTransactionTypes"
@ -388,7 +388,7 @@ async function addExpense() {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</QItem> </QItem>
<QItem> <QItem>
{{ toCurrency(taxRate(props.row)) }} {{ toCurrency(taxRate(props.row)) }}

View File

@ -2,12 +2,11 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import { useCapitalize } from 'src/composables/useCapitalize';
import VnCurrency from 'src/components/common/VnCurrency.vue'; import VnCurrency from 'src/components/common/VnCurrency.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -41,7 +40,7 @@ const suppliersRef = ref();
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectFilter <VnSelect
:label="t('params.supplierFk')" :label="t('params.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
:options="suppliers" :options="suppliers"
@ -52,7 +51,7 @@ const suppliersRef = ref();
outlined outlined
rounded rounded
> >
</VnSelectFilter> </VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
@ -70,19 +69,15 @@ const suppliersRef = ref();
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputDate <VnInputDate :label="t('From')" v-model="params.from" is-outlined />
:label="t('From')" </QItemSection>
v-model="params.from" </QItem>
is-outlined <QItem>
/> <QItemSection>
</QItemSection> <VnInputDate :label="t('To')" v-model="params.to" is-outlined />
</QItem> </QItemSection>
<QItem> </QItem>
<QItemSection>
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
</QItemSection>
</QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
@ -114,33 +109,33 @@ const suppliersRef = ref();
</QItem> </QItem>
<QExpansionItem :label="t('More options')" expand-separator> <QExpansionItem :label="t('More options')" expand-separator>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.fi')" :label="t('params.fi')"
v-model="params.fi" v-model="params.fi"
is-outlined is-outlined
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<QIcon name="badge" size="sm"></QIcon> <QIcon name="badge" size="sm"></QIcon>
</template> </template>
</VnInput> </VnInput>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.serialNumber')" :label="t('params.serialNumber')"
v-model="params.serialNumber" v-model="params.serialNumber"
is-outlined is-outlined
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<QIcon name="badge" size="sm"></QIcon> <QIcon name="badge" size="sm"></QIcon>
</template> </template>
</VnInput> </VnInput>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput

View File

@ -0,0 +1,2 @@
Search invoice: Buscar factura recibida
You can search by invoice reference: Puedes buscar por referencia de la factura

View File

@ -1,43 +1,15 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue'; import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue';
import LeftMenu from 'components/LeftMenu.vue'; import VnCard from 'components/common/VnCard.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import useCardSize from 'src/composables/useCardSize';
const stateStore = useStateStore();
const { t } = useI18n();
</script> </script>
<template> <template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()"> <VnCard
<VnSearchbar data-key="InvoiceOut"
data-key="InvoiceOutList" base-url="InvoiceOuts"
url="InvoiceOuts/filter" :descriptor="InvoiceOutDescriptor"
:label="t('Search invoice')" searchbar-data-key="InvoiceOutList"
:info="t('You can search by invoice reference')" searchbar-url="InvoiceOuts/filter"
/> searchbar-label="Search invoice"
</Teleport> searchbar-info="You can search by invoice reference"
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> />
<QScrollArea class="fit">
<InvoiceOutDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="useCardSize()">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>
<i18n>
es:
Search invoice: Buscar factura emitida
You can search by invoice reference: Puedes buscar por referencia de la factura
</i18n>

View File

@ -138,7 +138,7 @@ const ticketsColumns = ref([
<VnTitle :text="t('invoiceOut.summary.taxBreakdown')" /> <VnTitle :text="t('invoiceOut.summary.taxBreakdown')" />
<QTable :columns="taxColumns" :rows="invoiceOut.taxesBreakdown" flat> <QTable :columns="taxColumns" :rows="invoiceOut.taxesBreakdown" flat>
<template #header="props"> <template #header="props">
<QTr :props="props"> <QTr class="tr-header" :props="props">
<QTh v-for="col in props.cols" :key="col.name" :props="props"> <QTh v-for="col in props.cols" :key="col.name" :props="props">
{{ t(col.label) }} {{ t(col.label) }}
</QTh> </QTh>
@ -149,6 +149,18 @@ const ticketsColumns = ref([
<QCard class="vn-three"> <QCard class="vn-three">
<VnTitle :text="t('invoiceOut.summary.tickets')" /> <VnTitle :text="t('invoiceOut.summary.tickets')" />
<QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat> <QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
<template #header="props">
<QTr :props="props">
<QTh
class="tr-header"
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ t(col.label) }}
</QTh>
</QTr>
</template>
<template #body-cell-item="{ value }"> <template #body-cell-item="{ value }">
<QTd> <QTd>
<QBtn flat color="primary"> <QBtn flat color="primary">
@ -159,7 +171,7 @@ const ticketsColumns = ref([
</template> </template>
<template #body-cell-quantity="{ value, row }"> <template #body-cell-quantity="{ value, row }">
<QTd> <QTd>
<QBtn flat color="primary" dense> <QBtn class="no-uppercase" flat color="primary" dense>
{{ value }} {{ value }}
<CustomerDescriptorProxy :id="row.id" /> <CustomerDescriptorProxy :id="row.id" />
</QBtn> </QBtn>
@ -170,3 +182,8 @@ const ticketsColumns = ref([
</template> </template>
</CardSummary> </CardSummary>
</template> </template>
<style lang="scss" scoped>
.no-uppercase {
text-transform: none;
}
</style>

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js'; import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
@ -83,7 +83,7 @@ onMounted(async () => {
:dark="true" :dark="true"
class="q-mb-sm" class="q-mb-sm"
/> />
<VnSelectFilter <VnSelect
v-if="clientsToInvoice === 'one'" v-if="clientsToInvoice === 'one'"
:label="t('client')" :label="t('client')"
v-model="formData.clientId" v-model="formData.clientId"
@ -105,7 +105,7 @@ onMounted(async () => {
:label="t('maxShipped')" :label="t('maxShipped')"
is-outlined is-outlined
/> />
<VnSelectFilter <VnSelect
:label="t('company')" :label="t('company')"
v-model="formData.companyFk" v-model="formData.companyFk"
:options="companiesOptions" :options="companiesOptions"
@ -116,7 +116,7 @@ onMounted(async () => {
outlined outlined
rounded rounded
/> />
<VnSelectFilter <VnSelect
:label="t('printer')" :label="t('printer')"
v-model="formData.printer" v-model="formData.printer"
:options="printersOptions" :options="printersOptions"

View File

@ -119,8 +119,8 @@ const columns = computed(() => [
}, },
{ {
label: t('invoiceOut.negativeBases.comercial'), label: t('invoiceOut.negativeBases.comercial'),
field: 'workerSocialName', field: 'workerName',
name: 'comercial', name: 'worker',
align: 'left', align: 'left',
}, },
]); ]);
@ -181,9 +181,11 @@ const downloadCSV = async () => {
<TicketDescriptorProxy :id="row.ticketFk" /> <TicketDescriptorProxy :id="row.ticketFk" />
</QTd> </QTd>
</template> </template>
<template #body-cell-comercial="{ row }"> <template #body-cell-worker="{ row }">
<QTd> <QTd>
<QBtn flat dense color="blue">{{ row.comercialName }}</QBtn> <QBtn class="no-uppercase" flat dense color="blue">{{
row.workerName
}}</QBtn>
<WorkerDescriptorProxy :id="row.comercialId" /> <WorkerDescriptorProxy :id="row.comercialId" />
</QTd> </QTd>
</template> </template>
@ -211,6 +213,9 @@ const downloadCSV = async () => {
border-radius: 4px; border-radius: 4px;
padding: 6px; padding: 6px;
} }
.no-uppercase {
text-transform: none;
}
</style> </style>
<i18n> <i18n>

View File

@ -0,0 +1,2 @@
Search invoice: Buscar factura emitida
You can search by invoice reference: Puedes buscar por referencia de la factura

View File

@ -0,0 +1 @@
<template>Item barcode</template>

View File

@ -0,0 +1 @@
<template>Item Botanical</template>

View File

@ -1,28 +1,7 @@
<script setup> <script setup>
import LeftMenu from 'components/LeftMenu.vue'; import VnCard from 'components/common/VnCard.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import ItemDescriptor from './ItemDescriptor.vue'; import ItemDescriptor from './ItemDescriptor.vue';
import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize';
const stateStore = useStateStore();
</script> </script>
<template> <template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <VnCard data-key="Item" base-url="Items" :descriptor="ItemDescriptor" />
<QScrollArea class="fit">
<ItemDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div :class="useCardSize()">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template> </template>

View File

@ -9,7 +9,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import RegularizeStockForm from 'components/RegularizeStockForm.vue'; import RegularizeStockForm from 'components/RegularizeStockForm.vue';
import EditPictureForm from 'components/EditPictureForm.vue'; import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import useCardDescription from 'src/composables/useCardDescription'; import useCardDescription from 'src/composables/useCardDescription';
@ -50,12 +50,10 @@ const entityId = computed(() => {
}); });
const image = ref(null); const image = ref(null);
const regularizeStockFormDialog = ref(null); const regularizeStockFormDialog = ref(null);
const editPhotoFormDialog = ref(null);
const item = ref(null); const item = ref(null);
const available = ref(null); const available = ref(null);
const visible = ref(null); const visible = ref(null);
const _warehouseFk = ref(null); const _warehouseFk = ref(null);
const warehouseText = ref(null);
const salixUrl = ref(); const salixUrl = ref();
const warehouseFk = computed({ const warehouseFk = computed({
get() { get() {
@ -63,14 +61,9 @@ const warehouseFk = computed({
}, },
set(val) { set(val) {
_warehouseFk.value = val; _warehouseFk.value = val;
if (val) { if (val) updateStock();
updateStock();
getWarehouseName(val);
}
}, },
}); });
const showWarehouseIconTooltip = ref(true);
const showEditPhotoForm = ref(false);
onMounted(async () => { onMounted(async () => {
await getItemAvatar(); await getItemAvatar();
@ -90,26 +83,6 @@ const setData = (entity) => {
data.value = useCardDescription(entity.name, entity.id); data.value = useCardDescription(entity.name, entity.id);
}; };
const getWarehouseName = async (warehouseFk) => {
try {
showWarehouseIconTooltip.value = false;
const filter = {
where: { id: warehouseFk },
};
const { data } = await axios.get('Warehouses/findOne', { filter });
warehouseText.value = t('item.descriptor.warehouseText', {
warehouseName: data.name,
});
showWarehouseIconTooltip.value = true;
} catch (err) {
console.error('Error finding warehouse');
}
};
const updateStock = async () => { const updateStock = async () => {
try { try {
available.value = null; available.value = null;
@ -135,10 +108,6 @@ const openRegularizeStockForm = () => {
regularizeStockFormDialog.value.show(); regularizeStockFormDialog.value.show();
}; };
const toggleEditPictureForm = () => {
showEditPhotoForm.value = !showEditPhotoForm.value;
};
const cloneItem = async () => { const cloneItem = async () => {
try { try {
const { data } = await axios.post(`Items/${entityId.value}/clone`); const { data } = await axios.post(`Items/${entityId.value}/clone`);
@ -193,79 +162,16 @@ const openCloneDialog = async () => {
</QItem> </QItem>
<QItem v-ripple clickable @click="openCloneDialog()"> <QItem v-ripple clickable @click="openCloneDialog()">
<QItemSection> <QItemSection>
{{ t('Clone') }} {{ t('globals.clone') }}
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
<template #before> <template #before>
<div class="relative-position"> <ItemDescriptorImage
<QImg :src="image" spinner-color="primary" class="photo"> :entity-id="entityId"
<template #error> :visible="visible"
<div :available="available"
class="absolute-full picture text-center q-pa-md flex flex-center" />
>
<div>
<div
class="text-grey-5"
style="opacity: 0.4; font-size: 5vh"
>
<QIcon name="vn:item" />
</div>
<div class="text-grey-5" style="opacity: 0.4">
{{ t('item.descriptor.item') }}
</div>
</div>
</div>
</template>
</QImg>
<QBtn
color="primary"
size="lg"
round
class="edit-photo-btn"
@click="toggleEditPictureForm()"
>
<QIcon name="edit" size="sm" />
<QDialog ref="editPhotoFormDialog" v-model="showEditPhotoForm">
<EditPictureForm
collection="catalog"
:id="entityId"
@close-form="toggleEditPictureForm()"
@on-photo-uploaded="getItemAvatar()"
/>
</QDialog>
</QBtn>
</div>
<div
class="row justify-between items-center full-width bg-primary"
style="height: 54px"
>
<div class="col column items-center">
<span class="text-uppercase color-vn-white" style="font-size: 11px">
{{ t('item.descriptor.visible') }}
</span>
<span class="text-weight-bold text-h5 color-vn-white">{{
visible
}}</span>
</div>
<div
class="col column items-center separation-borders"
style="font-size: 11px"
>
<span class="text-uppercase color-vn-white">
{{ t('item.descriptor.available') }}
</span>
<span class="text-weight-bold text-h5 color-vn-white">{{
available
}}</span>
</div>
<div class="col column items-center justify-center">
<QIcon name="info" class="cursor-pointer color-vn-white" size="md">
<QTooltip>{{ warehouseText }}</QTooltip>
</QIcon>
</div>
</div>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<VnLv :label="t('item.descriptor.buyer')"> <VnLv :label="t('item.descriptor.buyer')">
@ -307,7 +213,6 @@ const openCloneDialog = async () => {
<i18n> <i18n>
es: es:
Regularize stock: Regularizar stock Regularize stock: Regularizar stock
Clone: Clonar
All it's properties will be copied: Todas sus propiedades serán copiadas All it's properties will be copied: Todas sus propiedades serán copiadas
Do you want to clone this item?: ¿Desea clonar este artículo? Do you want to clone this item?: ¿Desea clonar este artículo?
</i18n> </i18n>

View File

@ -0,0 +1,158 @@
<script setup>
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import EditPictureForm from 'components/EditPictureForm.vue';
import { useSession } from 'src/composables/useSession';
import axios from 'axios';
const $props = defineProps({
visible: {
type: Number,
default: null,
},
available: {
type: Number,
default: null,
},
entityId: {
type: String,
default: null,
},
showEditButton: {
type: Boolean,
default: true,
},
});
const { t } = useI18n();
const { getTokenMultimedia } = useSession();
const image = ref(null);
const editPhotoFormDialog = ref(null);
const showEditPhotoForm = ref(false);
const warehouseName = ref(null);
const getItemAvatar = async () => {
const token = getTokenMultimedia();
const timeStamp = `timestamp=${Date.now()}`;
image.value = `/api/Images/catalog/200x200/${$props.entityId}/download?access_token=${token}&${timeStamp}`;
};
const toggleEditPictureForm = () => {
showEditPhotoForm.value = !showEditPhotoForm.value;
};
const getItemConfigs = async () => {
const { data } = await axios.get('ItemConfigs/findOne');
if (!data) return;
await getWarehouseName(data.warehouseFk);
return data;
};
const getWarehouseName = async (warehouseFk) => {
const filter = {
where: { id: warehouseFk },
};
const { data } = await axios.get('Warehouses/findOne', { filter });
if (!data) return;
warehouseName.value = data.name;
};
onMounted(async () => {
getItemAvatar();
getItemConfigs();
});
</script>
<template>
<div class="relative-position">
<QImg :src="image" spinner-color="primary" style="min-height: 256px">
<template #error>
<div class="absolute-full picture text-center q-pa-md flex flex-center">
<div>
<div class="text-grey-5" style="opacity: 0.4; font-size: 5vh">
<QIcon name="vn:item" />
</div>
<div class="text-grey-5" style="opacity: 0.4">
{{ t('item.descriptor.item') }}
</div>
</div>
</div>
</template>
</QImg>
<QBtn
v-if="showEditButton"
color="primary"
size="lg"
round
class="edit-photo-btn"
@click="toggleEditPictureForm()"
>
<QIcon name="edit" size="sm" />
<QDialog ref="editPhotoFormDialog" v-model="showEditPhotoForm">
<EditPictureForm
collection="catalog"
:id="entityId"
@close-form="toggleEditPictureForm()"
@on-photo-uploaded="getItemAvatar()"
/>
</QDialog>
</QBtn>
</div>
<div
class="row justify-between items-center full-width bg-primary"
style="height: 54px"
>
<div class="col column items-center">
<span class="text-uppercase color-vn-white" style="font-size: 11px">
{{ t('item.descriptor.visible') }}
</span>
<span class="text-weight-bold text-h5 color-vn-white">{{ visible }}</span>
</div>
<div class="col column items-center separation-borders" style="font-size: 11px">
<span class="text-uppercase color-vn-white">
{{ t('item.descriptor.available') }}
</span>
<span class="text-weight-bold text-h5 color-vn-white">{{ available }}</span>
</div>
<div class="col column items-center justify-center">
<QIcon name="info" class="cursor-pointer color-vn-white" size="md">
<QTooltip>{{
t('warehouseText', {
warehouseName: warehouseName,
})
}}</QTooltip>
</QIcon>
</div>
</div>
</template>
<i18n>
es:
Regularize stock: Regularizar stock
All it's properties will be copied: Todas sus propiedades serán copiadas
Do you want to clone this item?: ¿Desea clonar este artículo?
warehouseText: Calculated on the warehouse of { warehouseName }
en:
warehouseText: Calculado sobre el almacén de { warehouseName }
</i18n>
<style lang="scss" scoped>
.edit-photo-btn {
position: absolute;
right: 12px;
bottom: 12px;
z-index: 1;
cursor: pointer;
}
.separation-borders {
border-left: 1px solid $white;
border-right: 1px solid $white;
}
</style>

View File

@ -1 +1,228 @@
<template>Item summary</template> <script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { useRole } from 'src/composables/useRole';
const $props = defineProps({
id: {
type: Number,
default: 0,
},
});
const route = useRoute();
const { t } = useI18n();
const roleState = useRole();
const entityId = computed(() => $props.id || route.params.id);
const isBuyer = computed(() => {
return roleState.hasAny(['buyer']);
});
const isReplenisher = computed(() => {
return roleState.hasAny(['replenisher']);
});
const isAdministrative = computed(() => {
return roleState.hasAny(['administrative']);
});
</script>
<template>
<CardSummary
ref="summary"
:url="`Items/${entityId}/getSummary`"
:entity-id="entityId"
data-key="ItemSummary"
>
<template #header-left>
<router-link
v-if="route.name !== 'ItemSummary'"
:to="{ name: 'ItemSummary', params: { id: entityId } }"
class="header link"
>
<QIcon name="open_in_new" color="white" size="sm" />
</router-link>
</template>
<template #header="{ entity: { item } }">
{{ item.id }} - {{ item.name }}
</template>
<template #body="{ entity: { item, tags, visible, available, botanical } }">
<QCard class="vn-one photo">
<ItemDescriptorImage
:entity-id="entityId"
:visible="visible"
:available="available"
:show-edit-button="false"
/>
</QCard>
<QCard class="vn-one">
<component
:is="isBuyer ? 'router-link' : 'span'"
:to="{ name: 'ItemBasicData', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer }"
>
{{ t('item.summary.basicData') }}
<QIcon v-if="isBuyer" name="open_in_new" />
</component>
<VnLv :label="t('item.summary.name')" :value="item.name" />
<VnLv :label="t('item.summary.completeName')" :value="item.longName" />
<VnLv :label="t('item.summary.family')" :value="item.itemType.name" />
<VnLv :label="t('item.summary.size')" :value="item.size" />
<VnLv :label="t('item.summary.origin')" :value="item.origin.name" />
<VnLv :label="t('item.summary.stems')" :value="item.stems" />
<VnLv
:label="t('item.summary.multiplier')"
:value="item.stemMultiplier"
/>
<VnLv :label="t('item.summary.buyer')">
<template #value>
<VnUserLink
:name="item.itemType.worker.user.name"
:worker-id="item.itemType.worker.id"
/>
</template>
</VnLv>
<VnLv :info="t('Este artículo necesita una foto')">
<template #value>
<QCheckbox
:label="t('item.summary.doPhoto')"
v-model="item.isPhotoRequested"
:disable="true"
/>
</template>
</VnLv>
</QCard>
<QCard class="vn-one">
<component
:is="isBuyer ? 'router-link' : 'span'"
:to="{ name: 'ItemBasicData', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer }"
>
{{ t('item.summary.otherData') }}
<QIcon v-if="isBuyer" name="open_in_new" />
</component>
<VnLv
:label="t('item.summary.intrastatCode')"
:value="item.intrastat.id"
/>
<VnLv
:label="t('item.summary.intrastat')"
:value="item.intrastat.description"
/>
<VnLv :label="t('item.summary.ref')" :value="item.comment" />
<VnLv :label="t('item.summary.relevance')" :value="item.relevancy" />
<VnLv :label="t('item.summary.weight')" :value="item.weightByPiece" />
<VnLv :label="t('item.summary.units')" :value="item.packingOut" />
<VnLv :label="t('item.summary.expense')" :value="item.expense.name" />
<VnLv :label="t('item.summary.generic')" :value="item.genericFk" />
<VnLv
:label="t('item.summary.recycledPlastic')"
:value="item.recycledPlastic"
/>
<VnLv
:label="t('item.summary.nonRecycledPlastic')"
:value="item.nonRecycledPlastic"
/>
<VnLv
:label="t('item.summary.minSalesQuantity')"
:value="item.minQuantity"
/>
</QCard>
<QCard class="vn-one">
<component
:is="isBuyer || isReplenisher ? 'router-link' : 'span'"
:to="{ name: 'ItemTags', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer || isReplenisher }"
>
{{ t('item.summary.tags') }}
<QIcon v-if="isBuyer || isReplenisher" name="open_in_new" />
</component>
<VnLv
v-for="(tag, index) in tags"
:key="index"
:label="`${tag.priority} ${tag.tag.name}`"
:value="tag.value"
/>
</QCard>
<QCard class="vn-one" v-if="item.description">
<component
:is="isBuyer ? 'router-link' : 'span'"
:to="{ name: 'ItemBasicData', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer }"
>
{{ t('item.summary.description') }}
<QIcon v-if="isBuyer" name="open_in_new" />
</component>
<p>
{{ item.description }}
</p>
</QCard>
<QCard class="vn-one">
<component
:is="isBuyer || isAdministrative ? 'router-link' : 'span'"
:to="{ name: 'ItemTax', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer || isAdministrative }"
>
{{ t('item.summary.tax') }}
<QIcon v-if="isBuyer || isAdministrative" name="open_in_new" />
</component>
<VnLv
v-for="(tax, index) in item.taxes"
:key="index"
:label="tax.country.country"
:value="tax.taxClass.description"
/>
</QCard>
<QCard class="vn-one">
<component
:is="isBuyer ? 'router-link' : 'span'"
:to="{ name: 'ItemBotanical', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer }"
>
{{ t('item.summary.botanical') }}
<QIcon v-if="isBuyer" name="open_in_new" />
</component>
<VnLv :label="t('item.summary.genus')" :value="botanical?.genus?.name" />
<VnLv
:label="t('item.summary.specie')"
:value="botanical?.specie?.name"
/>
</QCard>
<QCard class="vn-one">
<component
:is="isBuyer || isReplenisher ? 'router-link' : 'span'"
:to="{ name: 'ItemBarcode', params: { id: entityId } }"
class="header"
:class="{ 'header-link': isBuyer || isReplenisher }"
>
{{ t('item.summary.barcode') }}
<QIcon v-if="isBuyer || isReplenisher" name="open_in_new" />
</component>
<p v-for="(barcode, index) in item.itemBarcode" :key="index">
{{ barcode.code }}
</p>
</QCard>
</template>
</CardSummary>
</template>
<i18n>
en:
Este artículo necesita una foto: Este artículo necesita una foto
</i18n>

View File

@ -0,0 +1 @@
<template>Item tax</template>

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
@ -90,7 +90,7 @@ onBeforeMount(async () => {
v-model="data.provisionalName" v-model="data.provisionalName"
:label="t('item.create.name')" :label="t('item.create.name')"
/> />
<VnSelectFilter <VnSelect
:label="t('item.create.tag')" :label="t('item.create.tag')"
v-model="data.tag" v-model="data.tag"
:options="tagsOptions" :options="tagsOptions"
@ -98,7 +98,7 @@ onBeforeMount(async () => {
option-label="name" option-label="name"
hide-selected hide-selected
/> />
<VnSelectFilter <VnSelect
:label="t('item.create.priority')" :label="t('item.create.priority')"
v-model="data.priority" v-model="data.priority"
:options="validPriorities" :options="validPriorities"
@ -108,7 +108,7 @@ onBeforeMount(async () => {
/> />
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<VnSelectFilter <VnSelect
:label="t('item.create.type')" :label="t('item.create.type')"
v-model="data.typeFk" v-model="data.typeFk"
:options="itemTypesOptions" :options="itemTypesOptions"
@ -131,8 +131,8 @@ onBeforeMount(async () => {
</QItemLabel> </QItemLabel>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
<VnSelectFilter <VnSelect
:label="t('item.create.intrastat')" :label="t('item.create.intrastat')"
v-model="data.intrastatFk" v-model="data.intrastatFk"
:options="intrastatsOptions" :options="intrastatsOptions"
@ -152,10 +152,10 @@ onBeforeMount(async () => {
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelectFilter> </VnSelect>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<VnSelectFilter <VnSelect
:label="t('item.create.origin')" :label="t('item.create.origin')"
v-model="data.originFk" v-model="data.originFk"
:options="originsOptions" :options="originsOptions"

View File

@ -0,0 +1,605 @@
<script setup>
import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue';
import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue';
import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
import { useStateStore } from 'stores/useStateStore';
import { dashIfEmpty } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm';
import { useState } from 'src/composables/useState';
import { toCurrency } from 'filters/index';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
import { useArrayData } from 'composables/useArrayData';
import { isLower, isBigger } from 'src/filters/date.js';
const stateStore = useStateStore();
const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm();
const state = useState();
const { notify } = useNotify();
const editTableCellDialogRef = ref(null);
const user = state.getUser();
const fixedPrices = ref([]);
const fixedPricesOriginalData = ref([]);
const warehousesOptions = ref([]);
const itemsWithNameOptions = ref([]);
const rowsSelected = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
case 'name':
return { 'i.name': { like: `%${value}%` } };
case 'itemFk':
case 'warehouseFk':
case 'rate2':
case 'rate3':
param = `fp.${param}`;
return { [param]: value };
case 'minPrice':
param = `i.${param}`;
return { [param]: value };
}
};
const params = reactive({});
const arrayData = useArrayData('ItemFixedPrices', {
url: 'FixedPrices/filter',
userParams: params,
order: ['itemFk'],
exprBuilder: exprBuilder,
});
const store = arrayData.store;
const fetchFixedPrices = async () => {
await arrayData.fetch({ append: false });
};
const onFixedPricesFetched = (data) => {
fixedPrices.value = data;
// el objetivo de guardar una copia de las rows es evitar guardar cambios si la data no cambió al disparar los eventos
fixedPricesOriginalData.value = JSON.parse(JSON.stringify(data));
};
watch(
() => store.data,
(data) => onFixedPricesFetched(data)
);
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await arrayData.addFilter({ params });
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getColumnInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const defaultColumnFilter = {
component: VnInput,
type: 'text',
filterValue: null,
event: getColumnInputEvents,
attrs: {
dense: true,
},
};
const defaultColumnAttrs = {
align: 'left',
sortable: true,
};
const columns = computed(() => [
{
label: t('item.fixedPrice.itemId'),
name: 'itemId',
field: 'itemFk',
...defaultColumnAttrs,
columnFilter: {
...defaultColumnFilter,
},
},
{
label: t('globals.description'),
field: 'name',
name: 'description',
...defaultColumnAttrs,
columnFilter: {
...defaultColumnFilter,
},
},
{
label: t('item.fixedPrice.groupingPrice'),
field: 'rate2',
name: 'groupingPrice',
...defaultColumnAttrs,
columnFilter: {
...defaultColumnFilter,
},
format: (val) => toCurrency(val),
},
{
label: t('item.fixedPrice.packingPrice'),
field: 'rate3',
name: 'packingPrice',
...defaultColumnAttrs,
columnFilter: {
...defaultColumnFilter,
},
format: (val) => dashIfEmpty(val),
},
{
label: t('item.fixedPrice.minPrice'),
field: 'minPrice',
name: 'minPrice',
...defaultColumnAttrs,
columnFilter: {
...defaultColumnFilter,
},
},
{
label: t('item.fixedPrice.started'),
field: 'started',
name: 'started',
...defaultColumnAttrs,
columnFilter: null,
},
{
label: t('item.fixedPrice.ended'),
field: 'ended',
name: 'ended',
...defaultColumnAttrs,
columnFilter: null,
},
{
label: t('item.fixedPrice.warehouse'),
field: 'warehouseFk',
name: 'warehouse',
...defaultColumnAttrs,
columnFilter: {
component: VnSelectFilter,
type: 'select',
filterValue: null,
event: getColumnInputEvents,
attrs: {
options: warehousesOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{ name: 'deleteAction', align: 'center' },
]);
const editTableFieldsOptions = [
{
field: 'rate2',
label: t('item.fixedPrice.groupingPrice'),
component: 'input',
attrs: {
type: 'number',
},
},
{
field: 'rate3',
label: t('item.fixedPrice.packingPrice'),
component: 'input',
attrs: {
type: 'number',
},
},
{
field: 'minPrice',
label: t('item.fixedPrice.minPrice'),
component: 'input',
attrs: {
type: 'number',
},
},
{
field: 'hasMinPrice',
label: t('item.fixedPrice.hasMinPrice'),
component: 'checkbox',
attrs: {
'false-value': 0,
'true-value': 1,
},
},
{
field: 'started',
label: t('item.fixedPrice.started'),
component: 'date',
},
{
field: 'ended',
label: t('item.fixedPrice.ended'),
component: 'date',
},
{
field: 'warehouseFk',
label: t('item.fixedPrice.warehouse'),
component: 'select',
attrs: {
options: [],
'option-label': 'name',
'option-value': 'id',
},
},
];
const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
return inputType === 'text'
? {
'keyup.enter': () => upsertPrice(props, resetMinPrice),
blur: () => upsertPrice(props, resetMinPrice),
}
: { 'update:modelValue': () => upsertPrice(props, resetMinPrice) };
};
const validations = (row, rowIndex, col) => {
const isNew = !row.id;
// Si la row no tiene id significa que fue agregada con addRow y no se ha guardado en la base de datos
// Si isNew es falso no se checkea si el valor es igual a la original
if (!isNew)
if (fixedPricesOriginalData.value[rowIndex][col.field] == row[col.field])
return false;
const requiredFields = ['itemFk', 'started', 'ended', 'rate2', 'rate3'];
return requiredFields.every(
(field) => row[field] !== null && row[field] !== undefined
);
};
const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
if (!validations(row, rowIndex, col)) return;
try {
if (resetMinPrice) row.hasMinPrice = 0;
const { data } = await axios.patch('FixedPrices/upsertFixedPrice', row);
row = data;
fixedPricesOriginalData.value[rowIndex][col.field] = row[col.field];
} catch (err) {
console.error('Error editing price', err);
}
};
const addRow = () => {
if (!fixedPrices.value || fixedPrices.value.length === 0) {
fixedPrices.value = [];
const today = Date.vnNew();
const millisecsInDay = 86400000;
const daysInWeek = 7;
const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay);
const newPrice = {
started: today,
ended: nextWeek,
hasMinPrice: 0,
};
fixedPricesOriginalData.value.push({ ...newPrice });
fixedPrices.value.push({ ...newPrice });
return;
}
const lastItemCopy = JSON.parse(
JSON.stringify(fixedPrices.value[fixedPrices.value.length - 1])
);
const { id, ...restOfItem } = lastItemCopy;
fixedPricesOriginalData.value.push(restOfItem);
fixedPrices.value.push(restOfItem);
};
const openEditTableCellDialog = () => {
editTableCellDialogRef.value.show();
};
const onEditCellDataSaved = async () => {
rowsSelected.value = [];
await fetchFixedPrices();
};
const onWarehousesFetched = (data) => {
warehousesOptions.value = data;
// Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'.
const warehouseField = editTableFieldsOptions.find(
(field) => field.field === 'warehouseFk'
);
warehouseField.attrs.options = data;
};
const removePrice = async (id, rowIndex) => {
try {
await axios.delete(`FixedPrices/${id}`);
fixedPrices.value.splice(rowIndex, 1);
fixedPricesOriginalData.value.splice(rowIndex, 1);
notify(t('globals.dataSaved'), 'positive');
} catch (err) {
console.error('Error removing price', err);
}
};
const updateMinPrice = async (value, props) => {
// El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice
// Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado
props.row.hasMinPrice = value;
await upsertPrice({
row: props.row,
col: { field: 'hasMinPrice' },
rowIndex: props.rowIndex,
});
};
onMounted(async () => {
stateStore.rightDrawer = true;
params.warehouseFk = user.value.warehouseFk;
await fetchFixedPrices();
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<FetchData
url="Warehouses"
:filter="{ order: ['name'] }"
auto-load
@on-fetch="(data) => onWarehousesFetched(data)"
/>
<FetchData
url="Items/withName"
:filter="{ fields: ['id', 'name'], order: 'id DESC' }"
auto-load
@on-fetch="(data) => (itemsWithNameOptions = data)"
/>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#actions-append">
<div class="row q-gutter-x-sm">
<QBtn
flat
@click="stateStore.toggleRightDrawer()"
round
dense
icon="menu"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }}
</QTooltip>
</QBtn>
</div>
</Teleport>
</template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<ItemFixedPriceFilter
data-key="ItemFixedPrices"
:warehouses-options="warehousesOptions"
/>
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<QTable
:rows="fixedPrices"
:columns="columns"
row-key="id"
selection="multiple"
v-model:selected="rowsSelected"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
: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-itemId="props">
<QTd>
<VnSelectFilter
:options="itemsWithNameOptions"
hide-selected
option-label="id"
option-value="id"
v-model="props.row.itemFk"
v-on="getRowUpdateInputEvents(props, true, 'select')"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</QTd>
</template>
<template #body-cell-description="{ row }">
<QTd class="col">
<span class="link">
{{ row.name }}
</span>
<ItemDescriptorProxy :id="row.itemFk" />
<fetched-tags :item="row" :max-length="6" />
</QTd>
</template>
<template #body-cell-groupingPrice="props">
<QTd class="col">
<VnInput
v-model.number="props.row.rate2"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</QTd>
</template>
<template #body-cell-packingPrice="props">
<QTd class="col">
<VnInput
v-model.number="props.row.rate3"
v-on="getRowUpdateInputEvents(props)"
>
<template #append></template>
</VnInput>
</QTd>
</template>
<template #body-cell-minPrice="props">
<QTd class="col">
<div class="row">
<QCheckbox
class="col"
:model-value="props.row.hasMinPrice"
@update:model-value="updateMinPrice($event, props)"
:false-value="0"
:true-value="1"
:toggle-indeterminate="false"
/>
<VnInput
class="col"
:disable="!props.row.hasMinPrice"
v-model.number="props.row.minPrice"
v-on="getRowUpdateInputEvents(props)"
type="number"
/>
</div>
</QTd>
</template>
<template #body-cell-started="props">
<QTd class="col" style="min-width: 160px">
<VnInputDate
v-model="props.row.started"
v-on="getRowUpdateInputEvents(props, false, 'date')"
v-bind="
isBigger(props.row.started)
? { 'bg-color': 'warning', 'is-outlined': true }
: {}
"
/>
</QTd>
</template>
<template #body-cell-ended="props">
<QTd class="col" style="min-width: 150px">
<VnInputDate
v-model="props.row.ended"
v-on="getRowUpdateInputEvents(props, false, 'date')"
v-bind="
isLower(props.row.ended)
? { 'bg-color': 'warning', 'is-outlined': true }
: {}
"
/>
</QTd>
</template>
<template #body-cell-warehouse="props">
<QTd class="col">
<VnSelectFilter
:options="warehousesOptions"
hide-selected
option-label="name"
option-value="id"
v-model="props.row.warehouseFk"
v-on="getRowUpdateInputEvents(props, false, 'select')"
/>
</QTd>
</template>
<template #body-cell-deleteAction="{ row, rowIndex }">
<QTd class="col">
<QIcon
name="delete"
size="sm"
class="cursor-pointer fill-icon-on-hover"
color="primary"
@click.stop="
openConfirmationModal(
t('This row will be removed'),
t('Do you want to clone this item?'),
() => removePrice(row.id, rowIndex)
)
"
>
<QTooltip class="text-no-wrap">
{{ t('Delete') }}
</QTooltip>
</QIcon>
</QTd>
</template>
<template #bottom-row>
<QTd align="center">
<QIcon
@click.stop="addRow()"
class="fill-icon-on-hover"
color="primary"
name="add_circle"
size="sm"
>
<QTooltip>
{{ t('Add fixed price') }}
</QTooltip>
</QIcon>
</QTd>
</template>
</QTable>
<QPageSticky v-if="rowsSelected.length > 0" :offset="[20, 20]">
<QBtn @click="openEditTableCellDialog()" color="primary" fab icon="edit" />
<QTooltip>
{{ t('Edit fixed price(s)') }}
</QTooltip>
</QPageSticky>
<QDialog ref="editTableCellDialogRef">
<EditTableCellValueForm
edit-url="FixedPrices/editFixedPrice"
:rows="rowsSelected"
:fields-options="editTableFieldsOptions"
@on-data-saved="onEditCellDataSaved()"
/>
</QDialog>
</QPage>
</template>
<i18n>
es:
Add fixed price: Añadir precio fijado
Edit fixed price(s): Editar precio(s) fijado(s)
This row will be removed: Esta linea se eliminará
Are you sure you want to continue?: ¿Seguro que quieres continuar?
Delete: Eliminar
</i18n>

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