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

This commit is contained in:
Jon Elias 2025-02-18 10:36:52 +00:00
commit a2bb7c44f3
56 changed files with 2309 additions and 1987 deletions

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectProvince from 'src/components/VnSelectProvince.vue'; import VnSelectProvince from 'src/components/VnSelectProvince.vue';
@ -21,14 +20,11 @@ const postcodeFormData = reactive({
provinceFk: null, provinceFk: null,
townFk: null, townFk: null,
}); });
const townsFetchDataRef = ref(false);
const townFilter = ref({}); const townFilter = ref({});
const countriesRef = ref(false); const countriesRef = ref(false);
const provincesOptions = ref([]); const provincesOptions = ref([]);
const townsOptions = ref([]);
const town = ref({}); const town = ref({});
const countryFilter = ref({});
function onDataSaved(formData) { function onDataSaved(formData) {
const newPostcode = { const newPostcode = {
@ -51,7 +47,6 @@ async function setCountry(countryFk, data) {
data.townFk = null; data.townFk = null;
data.provinceFk = null; data.provinceFk = null;
data.countryFk = countryFk; data.countryFk = countryFk;
await fetchTowns();
} }
// Province // Province
@ -60,22 +55,11 @@ async function setProvince(id, data) {
const newProvince = provincesOptions.value.find((province) => province.id == id); const newProvince = provincesOptions.value.find((province) => province.id == id);
if (newProvince) data.countryFk = newProvince.countryFk; if (newProvince) data.countryFk = newProvince.countryFk;
postcodeFormData.provinceFk = id; postcodeFormData.provinceFk = id;
await fetchTowns();
} }
async function onProvinceCreated(data) { async function onProvinceCreated(data) {
postcodeFormData.provinceFk = data.id; postcodeFormData.provinceFk = data.id;
} }
function provinceByCountry(countryFk = postcodeFormData.countryFk) {
return provincesOptions.value
.filter((province) => province.countryFk === countryFk)
.map(({ id }) => id);
}
// Town
async function handleTowns(data) {
townsOptions.value = data;
}
function setTown(newTown, data) { function setTown(newTown, data) {
town.value = newTown; town.value = newTown;
data.provinceFk = newTown?.provinceFk ?? newTown; data.provinceFk = newTown?.provinceFk ?? newTown;
@ -88,18 +72,6 @@ async function onCityCreated(newTown, formData) {
formData.townFk = newTown; formData.townFk = newTown;
setTown(newTown, formData); setTown(newTown, formData);
} }
async function fetchTowns(countryFk = postcodeFormData.countryFk) {
if (!countryFk) return;
const provinces = postcodeFormData.provinceFk
? [postcodeFormData.provinceFk]
: provinceByCountry();
townFilter.value.where = {
provinceFk: {
inq: provinces,
},
};
await townsFetchDataRef.value?.fetch();
}
async function filterTowns(name) { async function filterTowns(name) {
if (name !== '') { if (name !== '') {
@ -108,22 +80,11 @@ async function filterTowns(name) {
like: `%${name}%`, like: `%${name}%`,
}, },
}; };
await townsFetchDataRef.value?.fetch();
} }
} }
</script> </script>
<template> <template>
<FetchData
ref="townsFetchDataRef"
:sort-by="['name ASC']"
:limit="30"
:filter="townFilter"
@on-fetch="handleTowns"
auto-load
url="Towns/location"
/>
<FormModelPopup <FormModelPopup
url-create="postcodes" url-create="postcodes"
model="postcode" model="postcode"
@ -149,14 +110,13 @@ async function filterTowns(name) {
@filter="filterTowns" @filter="filterTowns"
:tooltip="t('Create city')" :tooltip="t('Create city')"
v-model="data.townFk" v-model="data.townFk"
:options="townsOptions" url="Towns/location"
option-label="name"
option-value="id"
:rules="validate('postcode.city')" :rules="validate('postcode.city')"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
:emit-value="false" :emit-value="false"
required required
data-cy="locationTown" data-cy="locationTown"
sort-by="name ASC"
> >
<template #option="{ itemProps, opt }"> <template #option="{ itemProps, opt }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
@ -197,16 +157,12 @@ async function filterTowns(name) {
/> />
<VnSelect <VnSelect
ref="countriesRef" ref="countriesRef"
:limit="30"
:filter="countryFilter"
:sort-by="['name ASC']" :sort-by="['name ASC']"
auto-load auto-load
url="Countries" url="Countries"
required required
:label="t('Country')" :label="t('Country')"
hide-selected hide-selected
option-label="name"
option-value="id"
v-model="data.countryFk" v-model="data.countryFk"
:rules="validate('postcode.countryFk')" :rules="validate('postcode.countryFk')"
@update:model-value="(value) => setCountry(value, data)" @update:model-value="(value) => setCountry(value, data)"

View File

@ -62,12 +62,9 @@ const where = computed(() => {
auto-load auto-load
:where="where" :where="where"
url="Autonomies/location" url="Autonomies/location"
:sort-by="['name ASC']" sort-by="name ASC"
:limit="30"
:label="t('Autonomy')" :label="t('Autonomy')"
hide-selected hide-selected
option-label="name"
option-value="id"
v-model="data.autonomyFk" v-model="data.autonomyFk"
:rules="validate('province.autonomyFk')" :rules="validate('province.autonomyFk')"
> >

View File

@ -42,7 +42,6 @@ const itemFilter = {
const itemFilterParams = reactive({}); const itemFilterParams = reactive({});
const closeButton = ref(null); const closeButton = ref(null);
const isLoading = ref(false); const isLoading = ref(false);
const producersOptions = ref([]);
const ItemTypesOptions = ref([]); const ItemTypesOptions = ref([]);
const InksOptions = ref([]); const InksOptions = ref([]);
const tableRows = ref([]); const tableRows = ref([]);
@ -121,23 +120,17 @@ const selectItem = ({ id }) => {
</script> </script>
<template> <template>
<FetchData
url="Producers"
@on-fetch="(data) => (producersOptions = data)"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
auto-load
/>
<FetchData <FetchData
url="ItemTypes" url="ItemTypes"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
order="name" order="name ASC"
@on-fetch="(data) => (ItemTypesOptions = data)" @on-fetch="(data) => (ItemTypesOptions = data)"
auto-load auto-load
/> />
<FetchData <FetchData
url="Inks" url="Inks"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
order="name" order="name ASC"
@on-fetch="(data) => (InksOptions = data)" @on-fetch="(data) => (InksOptions = data)"
auto-load auto-load
/> />
@ -152,11 +145,11 @@ const selectItem = ({ id }) => {
<VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" /> <VnInput :label="t('entry.buys.size')" v-model="itemFilterParams.size" />
<VnSelect <VnSelect
:label="t('globals.producer')" :label="t('globals.producer')"
:options="producersOptions"
hide-selected hide-selected
option-label="name"
option-value="id"
v-model="itemFilterParams.producerFk" v-model="itemFilterParams.producerFk"
url="Producers"
:fields="['id', 'name']"
sort-by="name ASC"
/> />
<VnSelect <VnSelect
:label="t('globals.type')" :label="t('globals.type')"

View File

@ -124,7 +124,7 @@ const selectTravel = ({ id }) => {
<FetchData <FetchData
url="AgencyModes" url="AgencyModes"
@on-fetch="(data) => (agenciesOptions = data)" @on-fetch="(data) => (agenciesOptions = data)"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
auto-load auto-load
/> />
<FetchData <FetchData

View File

@ -134,7 +134,8 @@ onMounted(async () => {
if (!$props.formInitialData) { if (!$props.formInitialData) {
if ($props.autoLoad && $props.url) await fetch(); if ($props.autoLoad && $props.url) await fetch();
else if (arrayData.store.data) updateAndEmit('onFetch', arrayData.store.data); else if (arrayData.store.data)
updateAndEmit('onFetch', { val: arrayData.store.data });
} }
if ($props.observeFormChanges) { if ($props.observeFormChanges) {
watch( watch(
@ -154,7 +155,7 @@ onMounted(async () => {
if (!$props.url) if (!$props.url)
watch( watch(
() => arrayData.store.data, () => arrayData.store.data,
(val) => updateAndEmit('onFetch', val), (val) => updateAndEmit('onFetch', { val }),
); );
watch( watch(
@ -200,7 +201,7 @@ async function fetch() {
}); });
if (Array.isArray(data)) data = data[0] ?? {}; if (Array.isArray(data)) data = data[0] ?? {};
updateAndEmit('onFetch', data); updateAndEmit('onFetch', { val: data });
} catch (e) { } catch (e) {
state.set(modelValue, {}); state.set(modelValue, {});
throw e; throw e;
@ -228,7 +229,11 @@ async function save(prevent = false) {
if ($props.urlCreate) notify('globals.dataCreated', 'positive'); if ($props.urlCreate) notify('globals.dataCreated', 'positive');
updateAndEmit('onDataSaved', formData.value, response?.data); updateAndEmit('onDataSaved', {
val: formData.value,
res: response?.data,
old: originalData.value,
});
if ($props.reload) await arrayData.fetch({}); if ($props.reload) await arrayData.fetch({});
hasChanges.value = false; hasChanges.value = false;
} finally { } finally {
@ -242,8 +247,7 @@ async function saveAndGo() {
} }
function reset() { function reset() {
formData.value = JSON.parse(JSON.stringify(originalData.value)); updateAndEmit('onFetch', { val: originalData.value });
updateAndEmit('onFetch', originalData.value);
if ($props.observeFormChanges) { if ($props.observeFormChanges) {
hasChanges.value = false; hasChanges.value = false;
isResetting.value = true; isResetting.value = true;
@ -265,11 +269,11 @@ function filter(value, update, filterOptions) {
); );
} }
function updateAndEmit(evt, val, res) { function updateAndEmit(evt, { val, res, old } = { val: null, res: null, old: null }) {
state.set(modelValue, val); state.set(modelValue, val);
if (!$props.url) arrayData.store.data = val; if (!$props.url) arrayData.store.data = val;
emit(evt, state.get(modelValue), res); emit(evt, state.get(modelValue), res, old);
} }
function trimData(data) { function trimData(data) {

View File

@ -121,23 +121,25 @@ const removeTag = (index, params, search) => {
applyTags(params, search); applyTags(params, search);
}; };
const setCategoryList = (data) => { const setCategoryList = (data) => {
categoryList.value = (data || []) categoryList.value = (data || []).map((category) => ({
.filter((category) => category.display) ...category,
.map((category) => ({ icon: `vn:${(category.icon || '').split('-')[1]}`,
...category, }));
icon: `vn:${(category.icon || '').split('-')[1]}`,
}));
fetchItemTypes(); fetchItemTypes();
}; };
</script> </script>
<template> <template>
<FetchData url="ItemCategories" limit="30" auto-load @on-fetch="setCategoryList" /> <FetchData
url="ItemCategories"
auto-load
@on-fetch="setCategoryList"
:where="{ display: { neq: 0 } }"
/>
<FetchData <FetchData
url="Tags" url="Tags"
:filter="{ fields: ['id', 'name', 'isFree'] }" :filter="{ fields: ['id', 'name', 'isFree'] }"
auto-load auto-load
limit="30"
@on-fetch="(data) => (tagOptions = data)" @on-fetch="(data) => (tagOptions = data)"
/> />
<VnFilterPanel <VnFilterPanel
@ -195,8 +197,6 @@ const setCategoryList = (data) => {
:label="t('components.itemsFilterPanel.typeFk')" :label="t('components.itemsFilterPanel.typeFk')"
v-model="params.typeFk" v-model="params.typeFk"
:options="itemTypesOptions" :options="itemTypesOptions"
option-value="id"
option-label="name"
dense dense
outlined outlined
rounded rounded
@ -234,7 +234,6 @@ const setCategoryList = (data) => {
:label="t('globals.tag')" :label="t('globals.tag')"
v-model="value.selectedTag" v-model="value.selectedTag"
:options="tagOptions" :options="tagOptions"
option-label="name"
dense dense
outlined outlined
rounded rounded

View File

@ -1,4 +1,6 @@
<script setup> <script setup>
import { toCurrency } from 'src/filters';
defineProps({ row: { type: Object, required: true } }); defineProps({ row: { type: Object, required: true } });
</script> </script>
<template> <template>
@ -10,7 +12,8 @@ defineProps({ row: { type: Object, required: true } });
size="xs" size="xs"
> >
<QTooltip> <QTooltip>
{{ $t('salesTicketsTable.risk') }}: {{ row.risk - row.credit }} {{ $t('salesTicketsTable.risk') }}:
{{ toCurrency(row.risk - row.credit) }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
<QIcon <QIcon

View File

@ -353,14 +353,14 @@ const clickHandler = async (event) => {
const column = $props.columns.find((col) => col.name === colField); const column = $props.columns.find((col) => col.name === colField);
if (editingRow.value !== null && editingField.value !== null) { if (editingRow.value !== null && editingField.value !== null) {
if (editingRow.value === rowIndex && editingField.value === colField) { if (editingRow.value == rowIndex && editingField.value == colField) return;
return;
}
destroyInput(editingRow.value, editingField.value); destroyInput(editingRow.value, editingField.value);
} }
if (isEditableColumn(column))
if (isEditableColumn(column)) {
await renderInput(Number(rowIndex), colField, clickedElement); await renderInput(Number(rowIndex), colField, clickedElement);
}
}; };
async function handleTabKey(event, rowIndex, colField) { async function handleTabKey(event, rowIndex, colField) {
@ -492,9 +492,7 @@ async function handleTabNavigation(rowIndex, colName, direction) {
if (isEditableColumn(columns[newColumnIndex])) break; if (isEditableColumn(columns[newColumnIndex])) break;
} while (iterations < totalColumns); } while (iterations < totalColumns);
if (iterations >= totalColumns) { if (iterations >= totalColumns + 1) return;
return;
}
if (direction === 1 && newColumnIndex <= currentColumnIndex) { if (direction === 1 && newColumnIndex <= currentColumnIndex) {
rowIndex++; rowIndex++;
@ -632,6 +630,7 @@ function cardClick(_, row) {
<template #header-cell="{ col }"> <template #header-cell="{ col }">
<QTh <QTh
v-if="col.visible ?? true" v-if="col.visible ?? true"
v-bind:class="col.headerClass"
class="body-cell" class="body-cell"
:style="col?.width ? `max-width: ${col?.width}` : ''" :style="col?.width ? `max-width: ${col?.width}` : ''"
style="padding: inherit" style="padding: inherit"
@ -758,7 +757,7 @@ function cardClick(_, row) {
flat flat
dense dense
:class=" :class="
btn.isPrimary ? 'text-primary-light' : 'color-vn-text ' btn.isPrimary ? 'text-primary-light' : 'color-vn-label'
" "
:style="`visibility: ${ :style="`visibility: ${
((btn.show && btn.show(row)) ?? true) ((btn.show && btn.show(row)) ?? true)
@ -766,6 +765,7 @@ function cardClick(_, row) {
: 'hidden' : 'hidden'
}`" }`"
@click="btn.action(row)" @click="btn.action(row)"
:data-cy="btn?.name ?? `tableAction-${index}`"
/> />
</QTd> </QTd>
</template> </template>
@ -783,7 +783,7 @@ function cardClick(_, row) {
<QCardSection <QCardSection
vertical vertical
class="no-margin no-padding" class="no-margin no-padding"
:class="colsMap.tableActions ? 'w-80' : 'fit'" :class="colsMap.tableActions ? '' : 'fit'"
> >
<!-- Chips --> <!-- Chips -->
<QCardSection <QCardSection
@ -814,7 +814,7 @@ function cardClick(_, row) {
</QCardSection> </QCardSection>
<!-- Fields --> <!-- Fields -->
<QCardSection <QCardSection
class="q-pl-sm q-pr-lg q-py-xs" class="q-pl-sm q-py-xs"
:class="$props.cardClass" :class="$props.cardClass"
> >
<div <div
@ -822,11 +822,11 @@ function cardClick(_, row) {
col, index col, index
) of splittedColumns.cardVisible" ) of splittedColumns.cardVisible"
:key="col.name" :key="col.name"
class="fields"
> >
<VnLv :label="col.label + ':'"> <VnLv :label="col.label + ':'">
<template #value> <template #value>
<span <span
class="q-pl-xs"
@click="stopEventPropagation($event)" @click="stopEventPropagation($event)"
> >
<slot <slot
@ -862,12 +862,12 @@ function cardClick(_, row) {
:title="btn.title" :title="btn.title"
:icon="btn.icon" :icon="btn.icon"
class="q-pa-xs" class="q-pa-xs"
flat
:class=" :class="
btn.isPrimary btn.isPrimary
? 'text-primary-light' ? 'text-primary-light'
: 'color-vn-text ' : 'color-vn-label'
" "
flat
@click="btn.action(row)" @click="btn.action(row)"
/> />
</QCardSection> </QCardSection>
@ -1046,7 +1046,7 @@ es:
.grid-three { .grid-three {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, max-content)); grid-template-columns: repeat(auto-fit, minmax(300px, max-content));
max-width: 100%; width: 100%;
grid-gap: 20px; grid-gap: 20px;
margin: 0 auto; margin: 0 auto;
} }
@ -1120,6 +1120,7 @@ es:
.vn-label-value { .vn-label-value {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center;
color: var(--vn-text-color); color: var(--vn-text-color);
.value { .value {
overflow: hidden; overflow: hidden;

View File

@ -39,6 +39,13 @@ onBeforeMount(async () => {
}); });
onBeforeRouteUpdate(async (to, from) => { onBeforeRouteUpdate(async (to, from) => {
if (hasRouteParam(to.params)) {
const { matched } = router.currentRoute.value;
const { name } = matched.at(-3);
if (name) {
router.push({ name, params: to.params });
}
}
const id = to.params.id; const id = to.params.id;
if (id !== from.params.id) await fetch(id, true); if (id !== from.params.id) await fetch(id, true);
}); });
@ -50,6 +57,9 @@ async function fetch(id, append = false) {
else arrayData.store.url = props.url.replace(regex, `/${id}`); else arrayData.store.url = props.url.replace(regex, `/${id}`);
await arrayData.fetch({ append, updateRouter: false }); await arrayData.fetch({ append, updateRouter: false });
} }
function hasRouteParam(params, valueToCheck = ':addressId') {
return Object.values(params).includes(valueToCheck);
}
</script> </script>
<template> <template>
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()"> <Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">

View File

@ -85,6 +85,7 @@ const handleModelValue = (data) => {
:tooltip="t('Create new location')" :tooltip="t('Create new location')"
:rules="mixinRules" :rules="mixinRules"
:lazy-rules="true" :lazy-rules="true"
required
> >
<template #form> <template #form>
<CreateNewPostcode <CreateNewPostcode

View File

@ -53,3 +53,8 @@ const manaCode = ref(props.manaCode);
/> />
</div> </div>
</template> </template>
<i18n>
es:
Promotion mana: Maná promoción
Claim mana: Maná reclamación
</i18n>

View File

@ -0,0 +1,66 @@
import { describe, it, expect, vi } from 'vitest';
import { useRequired } from '../useRequired';
vi.mock('../useValidator', () => ({
useValidator: () => ({
validations: () => ({
required: vi.fn((isRequired, val) => {
if (!isRequired) return true;
return val !== null && val !== undefined && val !== '';
}),
}),
}),
}));
describe('useRequired', () => {
it('should detect required when attr is boolean true', () => {
const attrs = { required: true };
const { isRequired } = useRequired(attrs);
expect(isRequired).toBe(true);
});
it('should detect required when attr is boolean false', () => {
const attrs = { required: false };
const { isRequired } = useRequired(attrs);
expect(isRequired).toBe(false);
});
it('should detect required when attr exists without value', () => {
const attrs = { required: '' };
const { isRequired } = useRequired(attrs);
expect(isRequired).toBe(true);
});
it('should return false when required attr does not exist', () => {
const attrs = { someOtherAttr: 'value' };
const { isRequired } = useRequired(attrs);
expect(isRequired).toBe(false);
});
describe('requiredFieldRule', () => {
it('should validate required field with value', () => {
const attrs = { required: true };
const { requiredFieldRule } = useRequired(attrs);
expect(requiredFieldRule('some value')).toBe(true);
});
it('should validate required field with empty value', () => {
const attrs = { required: true };
const { requiredFieldRule } = useRequired(attrs);
expect(requiredFieldRule('')).toBe(false);
});
it('should pass validation when field is not required', () => {
const attrs = { required: false };
const { requiredFieldRule } = useRequired(attrs);
expect(requiredFieldRule('')).toBe(true);
});
it('should handle null and undefined values', () => {
const attrs = { required: true };
const { requiredFieldRule } = useRequired(attrs);
expect(requiredFieldRule(null)).toBe(false);
expect(requiredFieldRule(undefined)).toBe(false);
});
});
});

View File

@ -2,14 +2,10 @@ import { useValidator } from 'src/composables/useValidator';
export function useRequired($attrs) { export function useRequired($attrs) {
const { validations } = useValidator(); const { validations } = useValidator();
const hasRequired = Object.keys($attrs).includes('required'); const isRequired =
let isRequired = false; typeof $attrs['required'] === 'boolean'
if (hasRequired) { ? $attrs['required']
const required = $attrs['required']; : Object.keys($attrs).includes('required');
if (typeof required === 'boolean') {
isRequired = required;
}
}
const requiredFieldRule = (val) => validations().required(isRequired, val); const requiredFieldRule = (val) => validations().required(isRequired, val);
return { return {

View File

@ -830,6 +830,8 @@ travel:
CloneTravelAndEntries: Clone travel and his entries CloneTravelAndEntries: Clone travel and his entries
deleteTravel: Delete travel deleteTravel: Delete travel
AddEntry: Add entry AddEntry: Add entry
availabled: Availabled
availabledHour: Availabled hour
thermographs: Thermographs thermographs: Thermographs
hb: HB hb: HB
basicData: basicData:

View File

@ -839,6 +839,7 @@ supplier:
verified: Verificado verified: Verificado
isActive: Está activo isActive: Está activo
billingData: Forma de pago billingData: Forma de pago
financialData: Datos financieros
payDeadline: Plazo de pago payDeadline: Plazo de pago
payDay: Día de pago payDay: Día de pago
account: Cuenta account: Cuenta
@ -916,6 +917,8 @@ travel:
deleteTravel: Eliminar envío deleteTravel: Eliminar envío
AddEntry: Añadir entrada AddEntry: Añadir entrada
thermographs: Termógrafos thermographs: Termógrafos
availabled: F. Disponible
availabledHour: Hora Disponible
hb: HB hb: HB
basicData: basicData:
daysInForward: Desplazamiento automatico (redada) daysInForward: Desplazamiento automatico (redada)

View File

@ -158,7 +158,7 @@ onMounted(() => {
> >
<QItemSection>{{ t('globals.delete') }}</QItemSection> <QItemSection>{{ t('globals.delete') }}</QItemSection>
</QItem> </QItem>
<QItem v-if="hasSysadminAccess" v-ripple clickable> <QItem v-if="hasSysadminAccess || isHimself" v-ripple clickable>
<QItemSection @click="onChangePass(isHimself)"> <QItemSection @click="onChangePass(isHimself)">
{{ isHimself ? t('globals.changePass') : t('globals.setPass') }} {{ isHimself ? t('globals.changePass') : t('globals.setPass') }}
</QItemSection> </QItemSection>

View File

@ -156,7 +156,6 @@ function onDrag() {
url="Claims" url="Claims"
:filter="claimDmsFilter" :filter="claimDmsFilter"
@on-fetch="([data]) => setClaimDms(data)" @on-fetch="([data]) => setClaimDms(data)"
limit="20"
auto-load auto-load
ref="claimDmsRef" ref="claimDmsRef"
/> />

View File

@ -17,8 +17,7 @@ const bankEntitiesRef = ref(null);
const filter = { const filter = {
fields: ['id', 'bic', 'name'], fields: ['id', 'bic', 'name'],
order: 'bic ASC', order: 'bic ASC'
limit: 30,
}; };
const getBankEntities = (data, formData) => { const getBankEntities = (data, formData) => {

View File

@ -232,7 +232,6 @@ const updateDateParams = (value, params) => {
:include="'category'" :include="'category'"
:sortBy="'name ASC'" :sortBy="'name ASC'"
dense dense
@update:model-value="(data) => updateDateParams(data, params)"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -254,7 +253,6 @@ const updateDateParams = (value, params) => {
:fields="['id', 'name']" :fields="['id', 'name']"
:sortBy="'name ASC'" :sortBy="'name ASC'"
dense dense
@update:model-value="(data) => updateDateParams(data, params)"
/> />
<VnSelect <VnSelect
v-model="params.campaign" v-model="params.campaign"
@ -303,12 +301,14 @@ en:
valentinesDay: Valentine's Day valentinesDay: Valentine's Day
mothersDay: Mother's Day mothersDay: Mother's Day
allSaints: All Saints' Day allSaints: All Saints' Day
frenchMothersDay: Mother's Day in France
es: es:
Enter a new search: Introduce una nueva búsqueda Enter a new search: Introduce una nueva búsqueda
Group by items: Agrupar por artículos Group by items: Agrupar por artículos
valentinesDay: Día de San Valentín valentinesDay: Día de San Valentín
mothersDay: Día de la Madre mothersDay: Día de la Madre
allSaints: Día de Todos los Santos allSaints: Día de Todos los Santos
frenchMothersDay: (Francia) Día de la Madre
Campaign consumption: Consumo campaña Campaign consumption: Consumo campaña
Campaign: Campaña Campaign: Campaña
From: Desde From: Desde

View File

@ -2,7 +2,10 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useQuasar } from 'quasar';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
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';
@ -11,9 +14,12 @@ 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 VnCheckbox from 'src/components/common/VnCheckbox.vue'; import VnCheckbox from 'src/components/common/VnCheckbox.vue';
import { getDifferences, getUpdatedValues } from 'src/filters'; import { getDifferences, getUpdatedValues } from 'src/filters';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const { notify } = useNotify();
const typesTaxes = ref([]); const typesTaxes = ref([]);
const typesTransactions = ref([]); const typesTransactions = ref([]);
@ -31,6 +37,31 @@ function onBeforeSave(formData, originalData) {
formData, formData,
); );
} }
async function checkEtChanges(data, _, originalData) {
const equalizatedHasChanged = originalData.isEqualizated != data.isEqualizated;
const hasToInvoiceByAddress =
originalData.hasToInvoiceByAddress || data.hasToInvoiceByAddress;
if (equalizatedHasChanged && hasToInvoiceByAddress) {
quasar.dialog({
component: VnConfirm,
componentProps: {
title: t('You changed the equalization tax'),
message: t('Do you want to spread the change?'),
promise: () => acceptPropagate(data),
},
});
} else if (equalizatedHasChanged) {
await acceptPropagate(data);
}
}
async function acceptPropagate({ isEqualizated }) {
await axios.patch(`Clients/${route.params.id}/addressesPropagateRe`, {
isEqualizated,
});
notify(t('Equivalent tax spreaded'), 'warning');
}
</script> </script>
<template> <template>
@ -45,6 +76,8 @@ function onBeforeSave(formData, originalData) {
auto-load auto-load
model="Customer" model="Customer"
:mapper="onBeforeSave" :mapper="onBeforeSave"
observe-form-changes
@on-data-saved="checkEtChanges"
> >
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow> <VnRow>
@ -180,6 +213,9 @@ es:
whenActivatingIt: Al activarlo, no informar el código del país en el campo nif whenActivatingIt: Al activarlo, no informar el código del país en el campo nif
inOrderToInvoice: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automaticamente el cambio a todos lo consignatarios, en caso contrario preguntará al usuario si quiere o no propagar inOrderToInvoice: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automaticamente el cambio a todos lo consignatarios, en caso contrario preguntará al usuario si quiere o no propagar
Daily invoice: Facturación diaria Daily invoice: Facturación diaria
Equivalent tax spreaded: Recargo de equivalencia propagado
You changed the equalization tax: Has cambiado el recargo de equivalencia
Do you want to spread the change?: ¿Deseas propagar el cambio a sus consignatarios?
en: en:
onlyLetters: Only letters, numbers and spaces can be used onlyLetters: Only letters, numbers and spaces can be used
whenActivatingIt: When activating it, do not enter the country code in the ID field whenActivatingIt: When activating it, do not enter the country code in the ID field

View File

@ -270,7 +270,7 @@ const sumRisk = ({ clientRisks }) => {
<VnTitle <VnTitle
target="_blank" target="_blank"
:url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`" :url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`"
:text="t('customer.summary.payMethodFk')" :text="t('customer.summary.financialData')"
icon="vn:grafana" icon="vn:grafana"
/> />
<VnLv <VnLv

View File

@ -87,7 +87,7 @@ onMounted(async () => {
<FetchData <FetchData
url="Campaigns/latest" url="Campaigns/latest"
@on-fetch="(data) => (campaignsOptions = data)" @on-fetch="(data) => (campaignsOptions = data)"
:filter="{ fields: ['id', 'code', 'dated'], order: 'code ASC', limit: 30 }" :filter="{ fields: ['id', 'code', 'dated'], order: 'code ASC' }"
auto-load auto-load
/> />
<FetchData <FetchData

View File

@ -98,7 +98,6 @@ function onAgentCreated({ id, fiscalName }, data) {
:rules="validate('Worker.postcode')" :rules="validate('Worker.postcode')"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
v-model="data.location" v-model="data.location"
:required="true"
@update:model-value="(location) => handleLocation(data, location)" @update:model-value="(location) => handleLocation(data, location)"
/> />

View File

@ -96,11 +96,11 @@ const updateObservations = async (payload) => {
await axios.post('AddressObservations/crud', payload); await axios.post('AddressObservations/crud', payload);
notes.value = []; notes.value = [];
deletes.value = []; deletes.value = [];
toCustomerAddress();
}; };
async function updateAll({ data, payload }) { async function updateAll({ data, payload }) {
await updateObservations(payload); await updateObservations(payload);
await updateAddress(data); await updateAddress(data);
toCustomerAddress();
} }
function getPayload() { function getPayload() {
return { return {
@ -137,15 +137,12 @@ async function handleDialog(data) {
.onOk(async () => { .onOk(async () => {
await updateAddressTicket(); await updateAddressTicket();
await updateAll(body); await updateAll(body);
toCustomerAddress();
}) })
.onCancel(async () => { .onCancel(async () => {
await updateAll(body); await updateAll(body);
toCustomerAddress();
}); });
} else { } else {
updateAll(body); await updateAll(body);
toCustomerAddress();
} }
} }

View File

@ -209,13 +209,14 @@ const columns = [
row['amount'] = row['quantity'] * row['buyingValue']; row['amount'] = row['quantity'] * row['buyingValue'];
}, },
}, },
width: '20px', width: '30px',
style: (row) => { style: (row) => {
if (row.groupingMode === 'grouping') if (row.groupingMode === 'grouping')
return { color: 'var(--vn-label-color)' }; return { color: 'var(--vn-label-color)' };
}, },
}, },
{ {
align: 'center',
labelAbbreviation: 'GM', labelAbbreviation: 'GM',
label: t('Grouping selector'), label: t('Grouping selector'),
toolTip: t('Grouping selector'), toolTip: t('Grouping selector'),
@ -249,7 +250,7 @@ const columns = [
toolTip: 'Grouping', toolTip: 'Grouping',
name: 'grouping', name: 'grouping',
component: 'number', component: 'number',
width: '20px', width: '30px',
create: true, create: true,
style: (row) => { style: (row) => {
if (row.groupingMode === 'packing') return { color: 'var(--vn-label-color)' }; if (row.groupingMode === 'packing') return { color: 'var(--vn-label-color)' };
@ -508,7 +509,7 @@ async function setBuyUltimate(itemFk, data) {
allowedKeys.forEach((key) => { allowedKeys.forEach((key) => {
if (buyUltimateData.hasOwnProperty(key) && key !== 'entryFk') { if (buyUltimateData.hasOwnProperty(key) && key !== 'entryFk') {
data[key] = buyUltimateData[key]; if (!['stickers', 'quantity'].includes(key)) data[key] = buyUltimateData[key];
} }
}); });
} }
@ -600,7 +601,6 @@ onMounted(() => {
ref="entryBuysRef" ref="entryBuysRef"
data-key="EntryBuys" data-key="EntryBuys"
:url="`Entries/${entityId}/getBuyList`" :url="`Entries/${entityId}/getBuyList`"
order="name DESC"
save-url="Buys/crud" save-url="Buys/crud"
:disable-option="{ card: true }" :disable-option="{ card: true }"
v-model:selected="selectedRows" v-model:selected="selectedRows"
@ -644,7 +644,8 @@ onMounted(() => {
:is-editable="editableMode" :is-editable="editableMode"
:without-header="!editableMode" :without-header="!editableMode"
:with-filters="editableMode" :with-filters="editableMode"
:right-search="editableMode" :right-search="false"
:right-search-icon="false"
:row-click="false" :row-click="false"
:columns="columns" :columns="columns"
:beforeSaveFn="beforeSave" :beforeSaveFn="beforeSave"

View File

@ -1,8 +1,6 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
@ -18,18 +16,10 @@ defineProps({
}, },
}); });
const itemTypeWorkersOptions = ref([]);
const tagValues = ref([]); const tagValues = ref([]);
</script> </script>
<template> <template>
<FetchData
url="TicketRequests/getItemTypeWorker"
limit="30"
auto-load
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
/>
<ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']"> <ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem class="q-my-md"> <QItem class="q-my-md">
@ -37,9 +27,10 @@ const tagValues = ref([]);
<VnSelect <VnSelect
:label="t('components.itemsFilterPanel.salesPersonFk')" :label="t('components.itemsFilterPanel.salesPersonFk')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
:options="itemTypeWorkersOptions" url="TicketRequests/getItemTypeWorker"
option-value="id"
option-label="nickname" option-label="nickname"
:fields="['id', 'nickname']"
sort-by="nickname ASC"
dense dense
outlined outlined
rounded rounded
@ -52,8 +43,9 @@ const tagValues = ref([]);
<QItemSection> <QItemSection>
<VnSelectSupplier <VnSelectSupplier
v-model="params.supplierFk" v-model="params.supplierFk"
@update:model-value="searchFn()" url="Suppliers"
hide-selected :fields="['id', 'name', 'nickname']"
sort-by="name ASC"
dense dense
outlined outlined
rounded rounded

View File

@ -44,28 +44,32 @@ const entryQueryFilter = {
const columns = computed(() => [ const columns = computed(() => [
{ {
label: 'Ex', labelAbbreviation: 'Ex',
label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
toolTip: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'), toolTip: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
name: 'isExcludedFromAvailable', name: 'isExcludedFromAvailable',
component: 'checkbox', component: 'checkbox',
width: '35px', width: '35px',
}, },
{ {
label: 'Pe', labelAbbreviation: 'Pe',
label: t('entry.list.tableVisibleColumns.isOrdered'),
toolTip: t('entry.list.tableVisibleColumns.isOrdered'), toolTip: t('entry.list.tableVisibleColumns.isOrdered'),
name: 'isOrdered', name: 'isOrdered',
component: 'checkbox', component: 'checkbox',
width: '35px', width: '35px',
}, },
{ {
label: 'Le', labelAbbreviation: 'LE',
label: t('entry.list.tableVisibleColumns.isConfirmed'),
toolTip: t('entry.list.tableVisibleColumns.isConfirmed'), toolTip: t('entry.list.tableVisibleColumns.isConfirmed'),
name: 'isConfirmed', name: 'isConfirmed',
component: 'checkbox', component: 'checkbox',
width: '35px', width: '35px',
}, },
{ {
label: 'Re', labelAbbreviation: 'Re',
label: t('entry.list.tableVisibleColumns.isReceived'),
toolTip: t('entry.list.tableVisibleColumns.isReceived'), toolTip: t('entry.list.tableVisibleColumns.isReceived'),
name: 'isReceived', name: 'isReceived',
component: 'checkbox', component: 'checkbox',
@ -89,6 +93,7 @@ const columns = computed(() => [
chip: { chip: {
condition: () => true, condition: () => true,
}, },
width: '50px',
}, },
{ {
label: t('entry.list.tableVisibleColumns.supplierFk'), label: t('entry.list.tableVisibleColumns.supplierFk'),
@ -99,8 +104,10 @@ const columns = computed(() => [
attrs: { attrs: {
url: 'suppliers', url: 'suppliers',
fields: ['id', 'name'], fields: ['id', 'name'],
where: { order: 'name DESC' },
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName), format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName),
width: '110px',
}, },
{ {
align: 'left', align: 'left',
@ -124,6 +131,7 @@ const columns = computed(() => [
label: 'AWB', label: 'AWB',
name: 'awbCode', name: 'awbCode',
component: 'input', component: 'input',
width: '100px',
}, },
{ {
align: 'left', align: 'left',
@ -160,6 +168,7 @@ const columns = computed(() => [
component: null, component: null,
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseOutName), format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseOutName),
width: '65px',
}, },
{ {
align: 'left', align: 'left',
@ -175,12 +184,24 @@ const columns = computed(() => [
component: null, component: null,
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseInName), format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseInName),
width: '65px',
}, },
{ {
align: 'left', align: 'left',
labelAbbreviation: t('Type'),
label: t('entry.list.tableVisibleColumns.entryTypeDescription'), label: t('entry.list.tableVisibleColumns.entryTypeDescription'),
toolTip: t('entry.list.tableVisibleColumns.entryTypeDescription'),
name: 'entryTypeCode', name: 'entryTypeCode',
component: 'select',
attrs: {
url: 'entryTypes',
fields: ['code', 'description'],
optionValue: 'code',
optionLabel: 'description',
},
cardVisible: true, cardVisible: true,
width: '65px',
format: (row, dashIfEmpty) => dashIfEmpty(row.entryTypeDescription),
}, },
{ {
name: 'companyFk', name: 'companyFk',
@ -320,4 +341,5 @@ es:
Search entries: Buscar entradas Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada You can search by entry reference: Puedes buscar por referencia de la entrada
Create entry: Crear entrada Create entry: Crear entrada
Type: Tipo
</i18n> </i18n>

View File

@ -34,18 +34,20 @@ const columns = computed(() => [
label: t('entryStockBought.buyer'), label: t('entryStockBought.buyer'),
isTitle: true, isTitle: true,
component: 'select', component: 'select',
isEditable: false,
cardVisible: true, cardVisible: true,
create: true, create: true,
attrs: { attrs: {
url: 'Workers/activeWithInheritedRole', url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'], fields: ['id', 'name', 'nickname'],
where: { role: 'buyer' }, where: { role: 'buyer' },
optionFilter: 'firstName', optionFilter: 'firstName',
optionLabel: 'name', optionLabel: 'nickname',
optionValue: 'id', optionValue: 'id',
useLike: false, useLike: false,
}, },
columnFilter: false, columnFilter: false,
width: '70px',
}, },
{ {
align: 'center', align: 'center',
@ -55,6 +57,7 @@ const columns = computed(() => [
create: true, create: true,
component: 'number', component: 'number',
summation: true, summation: true,
width: '60px',
}, },
{ {
align: 'center', align: 'center',
@ -78,6 +81,7 @@ const columns = computed(() => [
actions: [ actions: [
{ {
title: t('entryStockBought.viewMoreDetails'), title: t('entryStockBought.viewMoreDetails'),
name: 'searchBtn',
icon: 'search', icon: 'search',
isPrimary: true, isPrimary: true,
action: (row) => { action: (row) => {
@ -91,6 +95,7 @@ const columns = computed(() => [
}, },
}, },
], ],
'data-cy': 'table-actions',
}, },
]); ]);
@ -158,7 +163,7 @@ function round(value) {
@on-fetch=" @on-fetch="
(data) => { (data) => {
travel = data.find( travel = data.find(
(data) => data.warehouseIn?.code.toLowerCase() === 'vnh' (data) => data.warehouseIn?.code.toLowerCase() === 'vnh',
); );
} }
" "
@ -179,6 +184,7 @@ function round(value) {
@click="openDialog()" @click="openDialog()"
:title="t('entryStockBought.editTravel')" :title="t('entryStockBought.editTravel')"
color="primary" color="primary"
data-cy="edit-travel"
/> />
</div> </div>
</VnRow> </VnRow>
@ -239,10 +245,11 @@ function round(value) {
table-height="80vh" table-height="80vh"
auto-load auto-load
:column-search="false" :column-search="false"
:without-header="true"
> >
<template #column-workerFk="{ row }"> <template #column-workerFk="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
{{ row?.worker?.user?.name }} {{ row?.worker?.user?.nickname }}
<WorkerDescriptorProxy :id="row?.workerFk" /> <WorkerDescriptorProxy :id="row?.workerFk" />
</span> </span>
</template> </template>
@ -279,10 +286,11 @@ function round(value) {
justify-content: center; justify-content: center;
} }
.column { .column {
min-width: 30%;
margin-top: 5%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
min-width: 35%;
} }
.text-negative { .text-negative {
color: $negative !important; color: $negative !important;

View File

@ -21,7 +21,7 @@ const $props = defineProps({
const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}&dated=${$props.dated}`; const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}&dated=${$props.dated}`;
const columns = [ const columns = [
{ {
align: 'left', align: 'right',
label: t('Entry'), label: t('Entry'),
name: 'entryFk', name: 'entryFk',
isTitle: true, isTitle: true,
@ -29,7 +29,7 @@ const columns = [
columnFilter: false, columnFilter: false,
}, },
{ {
align: 'left', align: 'right',
name: 'itemFk', name: 'itemFk',
label: t('Item'), label: t('Item'),
columnFilter: false, columnFilter: false,
@ -44,21 +44,21 @@ const columns = [
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'left', align: 'right',
name: 'volume', name: 'volume',
label: t('Volume'), label: t('Volume'),
columnFilter: false, columnFilter: false,
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'left', align: 'right',
label: t('Packaging'), label: t('Packaging'),
name: 'packagingFk', name: 'packagingFk',
columnFilter: false, columnFilter: false,
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'left', align: 'right',
label: 'Packing', label: 'Packing',
name: 'packing', name: 'packing',
columnFilter: false, columnFilter: false,
@ -73,12 +73,14 @@ const columns = [
ref="tableRef" ref="tableRef"
data-key="StockBoughtsDetail" data-key="StockBoughtsDetail"
:url="customUrl" :url="customUrl"
order="itemName DESC" order="volume DESC"
:columns="columns" :columns="columns"
:right-search="false" :right-search="false"
:disable-infinite-scroll="true" :disable-infinite-scroll="true"
:disable-option="{ card: true }" :disable-option="{ card: true }"
:limit="0" :limit="0"
:without-header="true"
:with-filters="false"
auto-load auto-load
> >
<template #column-entryFk="{ row }"> <template #column-entryFk="{ row }">
@ -105,7 +107,7 @@ const columns = [
align-items: center; align-items: center;
margin: auto; margin: auto;
background-color: var(--vn-section-color); background-color: var(--vn-section-color);
padding: 4px; padding: 2%;
} }
.container > div > div > .q-table__top.relative-position.row.items-center { .container > div > div > .q-table__top.relative-position.row.items-center {
background-color: red !important; background-color: red !important;

View File

@ -7,7 +7,6 @@ import { toDate } from 'src/filters';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { getTotal } from 'src/composables/getTotal'; import { getTotal } from 'src/composables/getTotal';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
@ -22,7 +21,6 @@ const invoiceIn = computed(() => arrayData.store.data);
const currency = computed(() => invoiceIn.value?.currency?.code); const currency = computed(() => invoiceIn.value?.currency?.code);
const rowsSelected = ref([]); const rowsSelected = ref([]);
const banks = ref([]);
const invoiceInFormRef = ref(); const invoiceInFormRef = ref();
const invoiceId = +route.params.id; const invoiceId = +route.params.id;
const filter = { where: { invoiceInFk: invoiceId } }; const filter = { where: { invoiceInFk: invoiceId } };
@ -41,10 +39,9 @@ const columns = computed(() => [
name: 'bank', name: 'bank',
label: t('Bank'), label: t('Bank'),
field: (row) => row.bankFk, field: (row) => row.bankFk,
options: banks.value,
model: 'bankFk', model: 'bankFk',
optionValue: 'id',
optionLabel: 'bank', optionLabel: 'bank',
url: 'Accountings',
sortable: true, sortable: true,
tabIndex: 2, tabIndex: 2,
align: 'left', align: 'left',
@ -82,12 +79,6 @@ onBeforeMount(async () => {
}); });
</script> </script>
<template> <template>
<FetchData
url="Accountings"
auto-load
limit="30"
@on-fetch="(data) => (banks = data)"
/>
<CrudModel <CrudModel
v-if="invoiceIn" v-if="invoiceIn"
ref="invoiceInFormRef" ref="invoiceInFormRef"
@ -117,9 +108,9 @@ onBeforeMount(async () => {
<QTd> <QTd>
<VnSelect <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :url="col.url"
:option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:option-value="col.optionValue"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -193,8 +184,7 @@ onBeforeMount(async () => {
:label="t('Bank')" :label="t('Bank')"
class="full-width" class="full-width"
v-model="props.row['bankFk']" v-model="props.row['bankFk']"
:options="banks" url="Accountings"
option-value="id"
option-label="bank" option-label="bank"
> >
<template #option="scope"> <template #option="scope">

View File

@ -125,7 +125,7 @@ const ticketsColumns = ref([
:value="toDate(invoiceOut.issued)" :value="toDate(invoiceOut.issued)"
/> />
<VnLv <VnLv
:label="t('invoiceOut.summary.dued')" :label="t('invoiceOut.summary.expirationDate')"
:value="toDate(invoiceOut.dued)" :value="toDate(invoiceOut.dued)"
/> />
<VnLv :label="t('globals.created')" :value="toDate(invoiceOut.created)" /> <VnLv :label="t('globals.created')" :value="toDate(invoiceOut.created)" />

View File

@ -19,6 +19,7 @@ invoiceOut:
summary: summary:
issued: Issued issued: Issued
dued: Due dued: Due
expirationDate: Expiration date
booked: Booked booked: Booked
taxBreakdown: Tax breakdown taxBreakdown: Tax breakdown
taxableBase: Taxable base taxableBase: Taxable base

View File

@ -19,6 +19,7 @@ invoiceOut:
summary: summary:
issued: Fecha issued: Fecha
dued: Fecha límite dued: Fecha límite
expirationDate: Fecha vencimiento
booked: Contabilizada booked: Contabilizada
taxBreakdown: Desglose impositivo taxBreakdown: Desglose impositivo
taxableBase: Base imp. taxableBase: Base imp.

View File

@ -1,8 +1,6 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue'; import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
@ -16,17 +14,9 @@ const props = defineProps({
}, },
}); });
const itemTypeWorkersOptions = ref([]);
</script> </script>
<template> <template>
<FetchData
url="TicketRequests/getItemTypeWorker"
limit="30"
auto-load
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
/>
<ItemsFilterPanel :data-key="props.dataKey" :custom-tags="['tags']"> <ItemsFilterPanel :data-key="props.dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem class="q-my-md"> <QItem class="q-my-md">
@ -34,14 +24,15 @@ const itemTypeWorkersOptions = ref([]);
<VnSelect <VnSelect
:label="t('params.buyerFk')" :label="t('params.buyerFk')"
v-model="params.buyerFk" v-model="params.buyerFk"
:options="itemTypeWorkersOptions" url="TicketRequests/getItemTypeWorker"
option-value="id" :fields="['id', 'nickname']"
option-label="nickname" option-label="nickname"
dense dense
outlined outlined
rounded rounded
use-input use-input
@update:model-value="searchFn()" @update:model-value="searchFn()"
sort-by="nickname ASC"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -50,11 +41,10 @@ const itemTypeWorkersOptions = ref([]);
<VnSelect <VnSelect
url="Warehouses" url="Warehouses"
auto-load auto-load
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }" :fields="['id', 'name']"
sort-by="name ASC"
:label="t('params.warehouseFk')" :label="t('params.warehouseFk')"
v-model="params.warehouseFk" v-model="params.warehouseFk"
option-label="name"
option-value="id"
dense dense
outlined outlined
rounded rounded

View File

@ -46,7 +46,6 @@ const exprBuilder = (param, value) => {
url="AgencyModes" url="AgencyModes"
:filter="{ fields: ['id', 'name'] }" :filter="{ fields: ['id', 'name'] }"
sort-by="name ASC" sort-by="name ASC"
limit="30"
@on-fetch="(data) => (agencyList = data)" @on-fetch="(data) => (agencyList = data)"
auto-load auto-load
/> />
@ -54,7 +53,6 @@ const exprBuilder = (param, value) => {
url="Agencies" url="Agencies"
:filter="{ fields: ['id', 'name'] }" :filter="{ fields: ['id', 'name'] }"
sort-by="name ASC" sort-by="name ASC"
limit="30"
@on-fetch="(data) => (agencyAgreementList = data)" @on-fetch="(data) => (agencyAgreementList = data)"
auto-load auto-load
/> />
@ -120,7 +118,11 @@ const exprBuilder = (param, value) => {
<VnSelectSupplier <VnSelectSupplier
:label="t('Autonomous')" :label="t('Autonomous')"
v-model="params.supplierFk" v-model="params.supplierFk"
hide-selected url="Suppliers"
:fields="['name']"
sort-by="name ASC"
option-value="name"
option-label="name"
dense dense
outlined outlined
rounded rounded

View File

@ -48,7 +48,6 @@ const onFetch = (data) => {
}, },
], ],
}" }"
limit="30"
@on-fetch="onFetch" @on-fetch="onFetch"
/> />
<div :class="[isDialog ? 'column' : 'form-gap', 'full-width flex']"> <div :class="[isDialog ? 'column' : 'form-gap', 'full-width flex']">

View File

@ -18,6 +18,7 @@ const onSave = (data, response) => {
<template> <template>
<FormModel <FormModel
:update-url="`Roadmaps/${$route.params?.id}`" :update-url="`Roadmaps/${$route.params?.id}`"
:url="`Roadmaps/${$route.params?.id}`"
observe-form-changes observe-form-changes
model="Roadmap" model="Roadmap"
auto-load auto-load

View File

@ -1,7 +1,5 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
@ -65,6 +63,7 @@ const emit = defineEmits(['search']);
<QItemSection> <QItemSection>
<VnSelectSupplier <VnSelectSupplier
:label="t('Carrier')" :label="t('Carrier')"
:fields="['id', 'nickname']"
v-model="params.supplierFk" v-model="params.supplierFk"
dense dense
outlined outlined

View File

@ -47,7 +47,10 @@ const cancel = () => {
<div v-else> <div v-else>
<div class="header">Mana: {{ toCurrency(mana) }}</div> <div class="header">Mana: {{ toCurrency(mana) }}</div>
<div class="q-pa-md"> <div class="q-pa-md">
<slot /> <slot :popup="QPopupProxyRef" />
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm">
<VnUsesMana :mana-code="manaCode" />
</div>
<div v-if="newPrice" class="column items-center q-mt-lg"> <div v-if="newPrice" class="column items-center q-mt-lg">
<span class="text-primary">{{ t('New price') }}</span> <span class="text-primary">{{ t('New price') }}</span>
<span class="text-subtitle1"> <span class="text-subtitle1">
@ -56,9 +59,6 @@ const cancel = () => {
</div> </div>
</div> </div>
</div> </div>
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm">
<VnUsesMana :mana-code="manaCode" />
</div>
<div class="row"> <div class="row">
<QBtn <QBtn
color="primary" color="primary"

View File

@ -49,7 +49,7 @@ watch(
<FetchData <FetchData
@on-fetch="(data) => (listPackagingsOptions = data)" @on-fetch="(data) => (listPackagingsOptions = data)"
auto-load auto-load
:filter="{ fields: ['packagingFk', 'name'], order: 'name ASC', limit: 30 }" :filter="{ fields: ['packagingFk', 'name'], order: 'name ASC' }"
url="Packagings/listPackaging" url="Packagings/listPackaging"
/> />
<div class="flex justify-center"> <div class="flex justify-center">

View File

@ -133,7 +133,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
label: t('globals.amount'), label: t('globals.amount'),
name: 'amount', name: 'amount',
format: (row) => parseInt(row.amount * row.quantity), format: (row) => toCurrency(getSaleTotal(row)),
}, },
{ {
align: 'left', align: 'left',
@ -331,8 +331,7 @@ const updateDiscount = async (sales, newDiscount = null) => {
}; };
await axios.post(`Tickets/${route.params.id}/updateDiscount`, params); await axios.post(`Tickets/${route.params.id}/updateDiscount`, params);
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
for (let sale of sales) sale.discount = _newDiscount; tableRef.value.reload();
edit.value = { ...DEFAULT_EDIT };
}; };
const getNewPrice = computed(() => { const getNewPrice = computed(() => {
@ -789,21 +788,24 @@ watch(
:mana-code="manaCode" :mana-code="manaCode"
@save="changeDiscount(row)" @save="changeDiscount(row)"
> >
<VnInput <template #default="{ popup }">
v-model.number="edit.discount" <VnInput
:label="t('ticketSale.discount')" autofocus
type="number" @keyup.enter="
/> () => {
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm"> changeDiscount(row);
<VnUsesMana :mana-code="manaCode" /> popup.hide();
</div> }
"
v-model.number="edit.discount"
:label="t('ticketSale.discount')"
type="number"
/>
</template>
</TicketEditManaProxy> </TicketEditManaProxy>
</template> </template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span> <span v-else>{{ toPercentage(row.discount / 100) }}</span>
</template> </template>
<template #column-amount="{ row }">
{{ toCurrency(row.quantity * row.price) }}
</template>
</VnTable> </VnTable>
<QPageSticky :offset="[20, 20]" style="z-index: 2"> <QPageSticky :offset="[20, 20]" style="z-index: 2">

View File

@ -1,19 +1,16 @@
<script setup> <script setup>
import { onMounted, ref, computed, reactive } from 'vue'; import { ref, computed, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.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';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import VnTable from 'src/components/VnTable/VnTable.vue';
import TicketFutureFilter from './TicketFutureFilter.vue'; import TicketFutureFilter from './TicketFutureFilter.vue';
import { dashIfEmpty, toCurrency } from 'src/filters'; import { dashIfEmpty, toCurrency } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
import { useArrayData } from 'composables/useArrayData';
import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js'; import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
@ -26,214 +23,126 @@ const { openConfirmationModal } = useVnConfirm();
const { notify } = useNotify(); const { notify } = useNotify();
const user = state.getUser(); const user = state.getUser();
const itemPackingTypesOptions = ref([]);
const selectedTickets = ref([]); const selectedTickets = ref([]);
const vnTableRef = ref({});
const exprBuilder = (param, value) => { const originElRef = ref(null);
switch (param) { const destinationElRef = ref(null);
case 'id':
return { id: value };
case 'futureId':
return { futureId: value };
case 'liters':
return { liters: value };
case 'lines':
return { lines: value };
case 'iptColFilter':
return { ipt: { like: `%${value}%` } };
case 'futureIptColFilter':
return { futureIpt: { like: `%${value}%` } };
case 'totalWithVat':
return { totalWithVat: value };
}
};
const userParams = reactive({ const userParams = reactive({
futureScopeDays: Date.vnNew().toISOString(), futureScopeDays: Date.vnNew().toISOString(),
originScopeDays: Date.vnNew().toISOString(), originScopeDays: Date.vnNew().toISOString(),
warehouseFk: user.value.warehouseFk, warehouseFk: user.value.warehouseFk,
}); });
const arrayData = useArrayData('FutureTickets', {
url: 'Tickets/getTicketsFuture',
userParams: userParams,
exprBuilder: exprBuilder,
});
const { store } = arrayData;
const params = reactive({
futureScopeDays: Date.vnNew(),
originScopeDays: Date.vnNew(),
warehouseFk: user.value.warehouseFk,
});
const applyColumnFilter = async (col) => {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await arrayData.addFilter({ params });
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const tickets = computed(() => store.data);
const ticketColumns = computed(() => [ const ticketColumns = computed(() => [
{ {
label: t('futureTickets.problems'), label: '',
name: 'problems', name: 'problems',
headerClass: 'horizontal-separator',
align: 'left', align: 'left',
columnFilter: null, columnFilter: false,
}, },
{ {
label: t('advanceTickets.ticketId'), label: t('advanceTickets.ticketId'),
name: 'ticketId', name: 'id',
align: 'center', align: 'center',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'id',
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
label: t('futureTickets.shipped'), label: t('futureTickets.shipped'),
name: 'shipped', name: 'shipped',
align: 'left', align: 'left',
sortable: true, columnFilter: false,
columnFilter: null, headerClass: 'horizontal-separator',
}, },
{ {
align: 'center',
class: 'shrink',
label: t('advanceTickets.ipt'), label: t('advanceTickets.ipt'),
name: 'ipt', name: 'ipt',
field: 'ipt',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
filterParamKey: 'iptColFilter',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: { attrs: {
options: itemPackingTypesOptions.value, url: 'itemPackingTypes',
'option-value': 'code', fields: ['code', 'description'],
'option-label': 'description', where: { isActive: true },
dense: true, optionValue: 'code',
optionLabel: 'description',
inWhere: false,
}, },
}, },
format: (val) => dashIfEmpty(val), format: (row, dashIfEmpty) => dashIfEmpty(row.ipt),
headerClass: 'horizontal-separator',
}, },
{ {
label: t('ticketList.state'), label: t('ticketList.state'),
name: 'state', name: 'state',
align: 'left', align: 'left',
sortable: true, columnFilter: false,
columnFilter: null, headerClass: 'horizontal-separator',
}, },
{ {
label: t('advanceTickets.liters'), label: t('advanceTickets.liters'),
name: 'liters', name: 'liters',
field: 'liters',
align: 'left', align: 'left',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
label: t('advanceTickets.import'), label: t('advanceTickets.import'),
field: 'import',
name: 'import', name: 'import',
align: 'left', align: 'left',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: false,
format: (row) => toCurrency(row.totalWithVat),
}, },
{ {
label: t('futureTickets.availableLines'), label: t('futureTickets.availableLines'),
name: 'lines', name: 'lines',
field: 'lines', field: 'lines',
align: 'center', align: 'center',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: { format: (row, dashIfEmpty) => dashIfEmpty(row.lines),
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
}, },
{ {
label: t('advanceTickets.futureId'), label: t('advanceTickets.futureId'),
name: 'futureId', name: 'futureId',
align: 'left', align: 'center',
sortable: true, headerClass: 'horizontal-separator vertical-separator ',
columnFilter: { columnClass: 'vertical-separator',
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'futureId',
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
label: t('futureTickets.futureShipped'), label: t('futureTickets.futureShipped'),
name: 'futureShipped', name: 'futureShipped',
align: 'left', align: 'left',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: null, columnFilter: false,
format: (val) => dashIfEmpty(val), format: (row) => toDateTimeFormat(row.futureShipped),
}, },
{ {
align: 'center',
label: t('advanceTickets.futureIpt'), label: t('advanceTickets.futureIpt'),
class: 'shrink',
name: 'futureIpt', name: 'futureIpt',
field: 'futureIpt',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
filterParamKey: 'futureIptColFilter',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: { attrs: {
options: itemPackingTypesOptions.value, url: 'itemPackingTypes',
'option-value': 'code', fields: ['code', 'description'],
'option-label': 'description', where: { isActive: true },
dense: true, optionValue: 'code',
optionLabel: 'description',
}, },
}, },
format: (val) => dashIfEmpty(val), headerClass: 'horizontal-separator',
format: (row, dashIfEmpty) => dashIfEmpty(row.futureIpt),
}, },
{ {
label: t('advanceTickets.futureState'), label: t('advanceTickets.futureState'),
name: 'futureState', name: 'futureState',
align: 'right', align: 'right',
sortable: true, headerClass: 'horizontal-separator',
columnFilter: null, class: 'expand',
format: (val) => dashIfEmpty(val), columnFilter: false,
format: (row, dashIfEmpty) => dashIfEmpty(row.futureState),
}, },
]); ]);
@ -258,26 +167,51 @@ const moveTicketsFuture = async () => {
await axios.post('Tickets/merge', params); await axios.post('Tickets/merge', params);
notify(t('advanceTickets.moveTicketSuccess'), 'positive'); notify(t('advanceTickets.moveTicketSuccess'), 'positive');
selectedTickets.value = []; selectedTickets.value = [];
arrayData.fetch({ append: false }); vnTableRef.value.reload();
}; };
onMounted(async () => {
await arrayData.fetch({ append: false }); watch(
}); () => vnTableRef.value.tableRef?.$el,
($el) => {
if (!$el) return;
const head = $el.querySelector('thead');
const firstRow = $el.querySelector('thead > tr');
const newRow = document.createElement('tr');
destinationElRef.value = document.createElement('th');
originElRef.value = document.createElement('th');
newRow.classList.add('bg-header');
destinationElRef.value.classList.add('text-uppercase', 'color-vn-label');
originElRef.value.classList.add('text-uppercase', 'color-vn-label');
destinationElRef.value.setAttribute('colspan', '7');
originElRef.value.setAttribute('colspan', '9');
originElRef.value.textContent = `${t('advanceTickets.origin')}`;
destinationElRef.value.textContent = `${t('advanceTickets.destination')}`;
newRow.append(destinationElRef.value, originElRef.value);
head.insertBefore(newRow, firstRow);
},
{ once: true, inmmediate: true },
);
watch(
() => vnTableRef.value.params,
() => {
if (originElRef.value && destinationElRef.value) {
destinationElRef.value.textContent = `${t('advanceTickets.origin')}`;
originElRef.value.textContent = `${t('advanceTickets.destination')}`;
}
},
{ deep: true },
);
</script> </script>
<template> <template>
<FetchData
url="itemPackingTypes"
:filter="{
fields: ['code', 'description'],
order: 'description ASC',
where: { isActive: true },
}"
auto-load
@on-fetch="(data) => (itemPackingTypesOptions = data)"
/>
<VnSearchbar <VnSearchbar
data-key="FutureTickets" data-key="futureTicket"
:label="t('Search ticket')" :label="t('Search ticket')"
:info="t('futureTickets.searchInfo')" :info="t('futureTickets.searchInfo')"
/> />
@ -293,7 +227,7 @@ onMounted(async () => {
t(`futureTickets.moveTicketDialogSubtitle`, { t(`futureTickets.moveTicketDialogSubtitle`, {
selectedTickets: selectedTickets.length, selectedTickets: selectedTickets.length,
}), }),
moveTicketsFuture moveTicketsFuture,
) )
" "
> >
@ -305,77 +239,29 @@ onMounted(async () => {
</VnSubToolbar> </VnSubToolbar>
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
<TicketFutureFilter data-key="FutureTickets" /> <TicketFutureFilter data-key="futureTickets" />
</template> </template>
</RightMenu> </RightMenu>
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<QTable <VnTable
:rows="tickets" data-key="futureTickets"
ref="vnTableRef"
url="Tickets/getTicketsFuture"
search-url="futureTickets"
:user-params="userParams"
:limit="0"
:columns="ticketColumns" :columns="ticketColumns"
row-key="id" :table="{
selection="multiple" 'row-key': '$index',
selection: 'multiple',
}"
v-model:selected="selectedTickets" v-model:selected="selectedTickets"
:pagination="{ rowsPerPage: 0 }" :right-search="false"
:no-data-label="t('globals.noResults')" auto-load
style="max-width: 99%" :disable-option="{ card: true }"
> >
<template #header="props"> <template #column-problems="{ row }">
<QTr> <span class="q-gutter-x-xs">
<QTh class="horizontal-separator" />
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="8"
translate
>
{{ t('advanceTickets.origin') }}
</QTh>
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="4"
translate
>
{{ t('advanceTickets.destination') }}
</QTh>
</QTr>
<QTr>
<QTh>
<QCheckbox v-model="props.selected" />
</QTh>
<QTh
v-for="(col, index) in ticketColumns"
:key="index"
:class="{ 'vertical-separator': col.name === 'futureId' }"
>
{{ col.label }}
</QTh>
</QTr>
</template>
<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 #header-cell-availableLines="{ col }">
<QTh class="vertical-separator">
{{ col.label }}
</QTh>
</template>
<template #body-cell-problems="{ row }">
<QTd class="q-gutter-x-xs">
<QIcon <QIcon
v-if="row.futureAgencyFk !== row.agencyFk && row.agencyFk" v-if="row.futureAgencyFk !== row.agencyFk && row.agencyFk"
color="primary" color="primary"
@ -465,99 +351,87 @@ onMounted(async () => {
{{ t('futureTickets.rounding') }} {{ t('futureTickets.rounding') }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
</QTd> </span>
</template> </template>
<template #body-cell-ticketId="{ row }"> <template #column-id="{ row }">
<QTd> <QBtn flat class="link" @click.stop dense>
<QBtn flat class="link"> {{ row.id }}
{{ row.id }} <TicketDescriptorProxy :id="row.id" />
<TicketDescriptorProxy :id="row.id" /> </QBtn>
</QBtn>
</QTd>
</template> </template>
<template #body-cell-shipped="{ row }"> <template #column-shipped="{ row }">
<QTd class="shipped"> <QBadge
<QBadge text-color="black"
text-color="black" :color="getDateQBadgeColor(row.shipped)"
:color="getDateQBadgeColor(row.shipped)" class="q-ma-none"
class="q-ma-none" >
> {{ toDateTimeFormat(row.shipped) }}
{{ toDateTimeFormat(row.shipped) }} </QBadge>
</QBadge>
</QTd>
</template> </template>
<template #body-cell-state="{ row }"> <template #column-state="{ row }">
<QTd> <QBadge
<QBadge v-if="row.state"
text-color="black" text-color="black"
:color="row.classColor" :color="row.classColor"
class="q-ma-none" class="q-ma-none"
dense dense
> >
{{ row.state }} {{ row.state }}
</QBadge> </QBadge>
</QTd> <span v-else> {{ dashIfEmpty(row.state) }}</span>
</template> </template>
<template #body-cell-import="{ row }"> <template #column-import="{ row }">
<QTd> <QBadge
<QBadge :text-color="
:text-color=" totalPriceColor(row.totalWithVat) === 'warning'
totalPriceColor(row.totalWithVat) === 'warning' ? 'black'
? 'black' : 'white'
: 'white' "
" :color="totalPriceColor(row.totalWithVat)"
:color="totalPriceColor(row.totalWithVat)" class="q-ma-none"
class="q-ma-none" dense
dense >
> {{ toCurrency(row.totalWithVat || 0) }}
{{ toCurrency(row.totalWithVat || 0) }} </QBadge>
</QBadge>
</QTd>
</template> </template>
<template #body-cell-futureId="{ row }"> <template #column-futureId="{ row }">
<QTd class="vertical-separator"> <QBtn flat class="link" @click.stop dense>
<QBtn flat class="link" dense> {{ row.futureId }}
{{ row.futureId }} <TicketDescriptorProxy :id="row.futureId" />
<TicketDescriptorProxy :id="row.futureId" /> </QBtn>
</QBtn>
</QTd>
</template> </template>
<template #body-cell-futureShipped="{ row }"> <template #column-futureShipped="{ row }">
<QTd class="shipped"> <QBadge
<QBadge text-color="black"
text-color="black" :color="getDateQBadgeColor(row.futureShipped)"
:color="getDateQBadgeColor(row.futureShipped)" class="q-ma-none"
class="q-ma-none" >
> {{ toDateTimeFormat(row.futureShipped) }}
{{ toDateTimeFormat(row.futureShipped) }} </QBadge>
</QBadge>
</QTd>
</template> </template>
<template #body-cell-futureState="{ row }"> <template #column-futureState="{ row }">
<QTd> <QBadge
<QBadge text-color="black"
text-color="black" :color="row.futureClassColor"
:color="row.futureClassColor" class="q-mr-xs"
class="q-ma-none" dense
dense >
> {{ row.futureState }}
{{ row.futureState }} </QBadge>
</QBadge>
</QTd>
</template> </template>
</QTable> </VnTable>
</QPage> </QPage>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.shipped { :deep(.vertical-separator) {
min-width: 132px;
}
.vertical-separator {
border-left: 4px solid white !important; border-left: 4px solid white !important;
} }
.horizontal-separator { :deep(.horizontal-separator) {
border-top: 4px solid white !important;
}
:deep(.horizontal-bottom-separator) {
border-bottom: 4px solid white !important; border-bottom: 4px solid white !important;
} }
</style> </style>

View File

@ -12,7 +12,7 @@ import axios from 'axios';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ defineProps({
dataKey: { dataKey: {
type: String, type: String,
required: true, required: true,
@ -58,7 +58,7 @@ onMounted(async () => {
auto-load auto-load
/> />
<VnFilterPanel <VnFilterPanel
:data-key="props.dataKey" :data-key
:un-removable-params="['warehouseFk', 'originScopeDays ', 'futureScopeDays']" :un-removable-params="['warehouseFk', 'originScopeDays ', 'futureScopeDays']"
> >
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">

View File

@ -9,6 +9,7 @@ import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -53,7 +54,16 @@ const warehousesOptionsIn = ref([]);
<VnInputDate v-model="data.shipped" :label="t('globals.shipped')" /> <VnInputDate v-model="data.shipped" :label="t('globals.shipped')" />
<VnInputDate v-model="data.landed" :label="t('globals.landed')" /> <VnInputDate v-model="data.landed" :label="t('globals.landed')" />
</VnRow> </VnRow>
<VnRow>
<VnInputDate
v-model="data.availabled"
:label="t('travel.summary.availabled')"
/>
<VnInputTime
v-model="data.availabled"
:label="t('travel.summary.availabledHour')"
/>
</VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
:label="t('globals.warehouseOut')" :label="t('globals.warehouseOut')"
@ -101,10 +111,3 @@ const warehousesOptionsIn = ref([]);
</template> </template>
</FormModel> </FormModel>
</template> </template>
<i18n>
es:
raidDays: El travel se desplaza automáticamente cada día para estar desde hoy al número de días indicado. Si se deja vacio no se moverá
en:
raidDays: The travel adjusts itself daily to match the number of days set, starting from today. If left blank, it wont move
</i18n>

View File

@ -11,6 +11,7 @@ export default {
'agencyModeFk', 'agencyModeFk',
'isRaid', 'isRaid',
'daysInForward', 'daysInForward',
'availabled',
], ],
include: [ include: [
{ {

View File

@ -10,6 +10,8 @@ import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue'
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import { toDate, toCurrency, toCelsius } from 'src/filters'; import { toDate, toCurrency, toCelsius } from 'src/filters';
import { toDateTimeFormat } from 'src/filters/date.js';
import { dashIfEmpty } from 'src/filters';
import axios from 'axios'; import axios from 'axios';
import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue'; import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
@ -333,6 +335,12 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<VnLv :label="t('globals.reference')" :value="travel.ref" /> <VnLv :label="t('globals.reference')" :value="travel.ref" />
<VnLv label="m³" :value="travel.m3" /> <VnLv label="m³" :value="travel.m3" />
<VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" /> <VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
<VnLv
:label="t('travel.summary.availabled')"
:value="
dashIfEmpty(toDateTimeFormat(travel.availabled))
"
/>
</QCard> </QCard>
<QCard class="full-width"> <QCard class="full-width">
<VnTitle :text="t('travel.summary.entries')" /> <VnTitle :text="t('travel.summary.entries')" />

View File

@ -10,6 +10,9 @@ import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
import TravelFilter from './TravelFilter.vue'; import TravelFilter from './TravelFilter.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
import VnInputTime from 'src/components/common/VnInputTime.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import { toDateTimeFormat } from 'src/filters/date';
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const router = useRouter(); const router = useRouter();
@ -167,6 +170,17 @@ const columns = computed(() => [
cardVisible: true, cardVisible: true,
create: true, create: true,
}, },
{
align: 'left',
name: 'availabled',
label: t('travel.summary.availabled'),
component: 'input',
columnClass: 'expand',
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(toDateTimeFormat(row.availabled)),
},
{ {
align: 'right', align: 'right',
label: '', label: '',
@ -269,6 +283,16 @@ const columns = computed(() => [
:class="{ 'is-active': row.isReceived }" :class="{ 'is-active': row.isReceived }"
/> />
</template> </template>
<template #more-create-dialog="{ data }">
<VnInputDate
v-model="data.availabled"
:label="t('travel.summary.availabled')"
/>
<VnInputTime
v-model="data.availabled"
:label="t('travel.summary.availabledHour')"
/>
</template>
<template #moreFilterPanel="{ params }"> <template #moreFilterPanel="{ params }">
<VnInputNumber <VnInputNumber
:label="t('params.scopeDays')" :label="t('params.scopeDays')"

View File

@ -53,7 +53,7 @@ const showChangePasswordDialog = () => {
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem <QItem
v-if="!worker.user.emailVerified && user.id == worker.id" v-if="!worker.user.emailVerified && user.id != worker.id"
v-ripple v-ripple
clickable clickable
@click="showChangePasswordDialog" @click="showChangePasswordDialog"

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { computed, ref } from 'vue'; import { ref } from 'vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import FormModel from 'src/components/FormModel.vue'; import FormModel from 'src/components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
@ -9,33 +9,23 @@ import VnInputTime from 'src/components/common/VnInputTime.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const agencyFilter = {
fields: ['id', 'name'],
order: 'name ASC',
limit: 30,
};
const agencyOptions = ref([]);
const validAddresses = ref([]); const validAddresses = ref([]);
const addresses = ref([]);
const filterWhere = computed(() => ({ const setFilteredAddresses = (data) => {
id: { inq: validAddresses.value.map((item) => item.addressFk) }, const validIds = new Set(validAddresses.value.map((item) => item.addressFk));
})); addresses.value = data.filter((address) => validIds.has(address.id));
};
</script> </script>
<template> <template>
<FetchData
:filter="agencyFilter"
@on-fetch="(data) => (agencyOptions = data)"
auto-load
url="AgencyModes/isActive"
/>
<FetchData <FetchData
url="RoadmapAddresses" url="RoadmapAddresses"
auto-load auto-load
@on-fetch="(data) => (validAddresses = data)" @on-fetch="(data) => (validAddresses = data)"
/> />
<FormModel :url="`Zones/${$route.params.id}`" auto-load data-key="Zone"> <FetchData url="Addresses" auto-load @on-fetch="setFilteredAddresses" />
<FormModel auto-load model="zone">
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow> <VnRow>
<VnInput <VnInput
@ -45,7 +35,6 @@ const filterWhere = computed(() => ({
v-model="data.name" v-model="data.name"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
v-model="data.agencyModeFk" v-model="data.agencyModeFk"
@ -128,7 +117,7 @@ const filterWhere = computed(() => ({
v-model="data.addressFk" v-model="data.addressFk"
option-value="id" option-value="id"
option-label="nickname" option-label="nickname"
url="Addresses" :options="addresses"
:fields="['id', 'nickname']" :fields="['id', 'nickname']"
sort-by="id" sort-by="id"
hide-selected hide-selected

View File

@ -45,7 +45,6 @@ describe('OrderCatalog', () => {
).type('{enter}'); ).type('{enter}');
cy.get(':nth-child(1) > [data-cy="catalogFilterCategory"]').click(); cy.get(':nth-child(1) > [data-cy="catalogFilterCategory"]').click();
cy.dataCy('catalogFilterValueDialogBtn').last().click(); cy.dataCy('catalogFilterValueDialogBtn').last().click();
cy.get('[data-cy="catalogFilterValueDialogTagSelect"]').click();
cy.selectOption("[data-cy='catalogFilterValueDialogTagSelect']", 'Tallos'); cy.selectOption("[data-cy='catalogFilterValueDialogTagSelect']", 'Tallos');
cy.dataCy('catalogFilterValueDialogValueInput').find('input').focus(); cy.dataCy('catalogFilterValueDialogValueInput').find('input').focus();
cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('2'); cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('2');

View File

@ -3,11 +3,46 @@ describe('Client consignee', () => {
beforeEach(() => { beforeEach(() => {
cy.viewport(1280, 720); cy.viewport(1280, 720);
cy.login('developer'); cy.login('developer');
cy.visit('#/customer/1110/address', { cy.visit('#/customer/1107/address');
timeout: 5000, cy.domContentLoad();
});
}); });
it('Should load layout', () => { it('Should load layout', () => {
cy.get('.q-card').should('be.visible'); cy.get('.q-card').should('be.visible');
}); });
it('check as equalizated', function () {
cy.get('.q-card__section > .address-card').then(($el) => {
let addressCards_before = $el.length;
cy.get('.q-page-sticky > div > .q-btn').click();
const addressName = 'test';
cy.dataCy('Consignee_input').type(addressName);
cy.dataCy('Location_select').click();
cy.get('[role="listbox"] .q-item:nth-child(1)').click();
cy.dataCy('Street address_input').type('TEST ADDRESS');
cy.get('.q-btn-group > .q-btn--standard').click();
cy.location('href').should('contain', '#/customer/1107/address');
cy.get('.q-card__section > .address-card').should(
'have.length',
addressCards_before + 1,
);
cy.get('.q-card__section > .address-card')
.eq(addressCards_before)
.should('be.visible')
.get('.text-weight-bold')
.eq(addressCards_before - 1)
.should('contain', addressName)
.click();
});
cy.get(
'.q-card > :nth-child(1) > :nth-child(2) > .q-checkbox > .q-checkbox__inner',
)
.should('have.class', 'q-checkbox__inner--falsy')
.click();
cy.get('.q-btn-group > .q-btn--standard > .q-btn__content').click();
cy.get(
':nth-child(2) > :nth-child(2) > .flex > .q-mr-lg > .q-checkbox__inner',
).should('have.class', 'q-checkbox__inner--truthy');
});
}); });

View File

@ -3,9 +3,8 @@ describe('Client fiscal data', () => {
beforeEach(() => { beforeEach(() => {
cy.viewport(1280, 720); cy.viewport(1280, 720);
cy.login('developer'); cy.login('developer');
cy.visit('#/customer/1107/fiscal-data', { cy.visit('#/customer/1107/fiscal-data');
timeout: 5000, cy.domContentLoad();
});
}); });
it('Should change required value when change customer', () => { it('Should change required value when change customer', () => {
cy.get('.q-card').should('be.visible'); cy.get('.q-card').should('be.visible');
@ -15,4 +14,25 @@ describe('Client fiscal data', () => {
cy.get('.q-item > .q-item__label').should('have.text', ' #1'); cy.get('.q-item > .q-item__label').should('have.text', ' #1');
cy.dataCy('sageTaxTypeFk').filter('input').should('have.attr', 'required'); cy.dataCy('sageTaxTypeFk').filter('input').should('have.attr', 'required');
}); });
it('check as equalizated', () => {
cy.get(
':nth-child(1) > .q-checkbox > .q-checkbox__inner > .q-checkbox__bg',
).click();
cy.get('.q-btn-group > .q-btn--standard > .q-btn__content').click();
cy.get('.q-card > :nth-child(1) > span').should(
'contain',
'You changed the equalization tax',
);
cy.get('.q-card > :nth-child(2) > span').should(
'have.text',
'Do you want to spread the change?',
);
cy.get('[data-cy="VnConfirm_confirm"] > .q-btn__content > .block').click();
cy.get(
'.bg-warning > .q-notification__wrapper > .q-notification__content > .q-notification__message',
).should('have.text', 'Equivalent tax spreaded');
});
}); });

View File

@ -106,8 +106,9 @@ describe('Entry', () => {
cy.get(`td[data-col-field="${field}"][data-row-index="${row}"]`); cy.get(`td[data-col-field="${field}"][data-row-index="${row}"]`);
const selectSpan = (field, row = 0) => selectCell(field, row).find('div > span'); const selectSpan = (field, row = 0) => selectCell(field, row).find('div > span');
const selectButton = (cySelector) => cy.get(`button[data-cy="${cySelector}"]`); const selectButton = (cySelector) => cy.get(`button[data-cy="${cySelector}"]`);
const clickAndType = (field, value, row = 0) => const clickAndType = (field, value, row = 0) => {
selectCell(field, row).click().type(value); selectCell(field, row).click().type(`${value}{esc}`);
};
const checkText = (field, expectedText, row = 0) => const checkText = (field, expectedText, row = 0) =>
selectCell(field, row).should('have.text', expectedText); selectCell(field, row).should('have.text', expectedText);
const checkColor = (field, expectedColor, row = 0) => const checkColor = (field, expectedColor, row = 0) =>
@ -115,21 +116,18 @@ describe('Entry', () => {
createEntryAndBuy(); createEntryAndBuy();
selectCell('isIgnored') selectCell('isIgnored').click().click().type('{esc}');
.click() checkText('isIgnored', 'close');
.click()
.trigger('keydown', { key: 'Tab', keyCode: 9, which: 9 });
checkText('isIgnored', 'check');
checkColor('quantity', COLORS.negative);
clickAndType('stickers', '1'); clickAndType('stickers', '1');
checkText('quantity', '11'); checkText('stickers', '0/01');
checkText('amount', '550.00'); checkText('quantity', '1');
checkText('amount', '50.00');
clickAndType('packing', '2'); clickAndType('packing', '2');
checkText('packing', '12close'); checkText('packing', '12');
checkText('weight', '12.0'); checkText('weight', '12.0');
checkText('quantity', '132'); checkText('quantity', '12');
checkText('amount', '6600.00'); checkText('amount', '600.00');
checkColor('packing', COLORS.enabled); checkColor('packing', COLORS.enabled);
selectCell('groupingMode').click().click().click(); selectCell('groupingMode').click().click().click();
@ -137,7 +135,7 @@ describe('Entry', () => {
checkColor('grouping', COLORS.enabled); checkColor('grouping', COLORS.enabled);
selectCell('buyingValue').click().clear().type('{backspace}{backspace}1'); selectCell('buyingValue').click().clear().type('{backspace}{backspace}1');
checkText('amount', '132.00'); checkText('amount', '12.00');
checkColor('minPrice', COLORS.disable); checkColor('minPrice', COLORS.disable);
selectCell('hasMinPrice').click().click(); selectCell('hasMinPrice').click().click();
@ -145,7 +143,7 @@ describe('Entry', () => {
selectCell('hasMinPrice').click(); selectCell('hasMinPrice').click();
cy.saveCard(); cy.saveCard();
cy.get('span[data-cy="footer-stickers"]').should('have.text', '11'); cy.get('span[data-cy="footer-stickers"]').should('have.text', '1');
cy.get('.q-notification__message').contains('Data saved'); cy.get('.q-notification__message').contains('Data saved');
selectButton('change-quantity-sign').should('be.disabled'); selectButton('change-quantity-sign').should('be.disabled');
@ -156,9 +154,9 @@ describe('Entry', () => {
selectButton('change-quantity-sign').click(); selectButton('change-quantity-sign').click();
selectButton('set-negative-quantity').click(); selectButton('set-negative-quantity').click();
checkText('quantity', '-132'); checkText('quantity', '-12');
selectButton('set-positive-quantity').click(); selectButton('set-positive-quantity').click();
checkText('quantity', '132'); checkText('quantity', '12');
checkColor('amount', COLORS.disable); checkColor('amount', COLORS.disable);
selectButton('check-buy-amount').click(); selectButton('check-buy-amount').click();

View File

@ -6,7 +6,7 @@ describe('EntryStockBought', () => {
}); });
it('Should edit the reserved space', () => { it('Should edit the reserved space', () => {
cy.get('.q-field__native.q-placeholder').should('have.value', '01/01/2001'); cy.get('.q-field__native.q-placeholder').should('have.value', '01/01/2001');
cy.get('td[data-col-field="reserve"]').click(); cy.get('[data-col-field="reserve"][data-row-index="0"]').click();
cy.get('input[name="reserve"]').type('10{enter}'); cy.get('input[name="reserve"]').type('10{enter}');
cy.get('button[title="Save"]').click(); cy.get('button[title="Save"]').click();
cy.get('.q-notification__message').should('have.text', 'Data saved'); cy.get('.q-notification__message').should('have.text', 'Data saved');
@ -16,25 +16,35 @@ describe('EntryStockBought', () => {
cy.get('input[aria-label="Reserve"]').type('1'); cy.get('input[aria-label="Reserve"]').type('1');
cy.get('input[aria-label="Date"]').eq(1).clear(); cy.get('input[aria-label="Date"]').eq(1).clear();
cy.get('input[aria-label="Date"]').eq(1).type('01-01'); cy.get('input[aria-label="Date"]').eq(1).type('01-01');
cy.get('input[aria-label="Buyer"]').type('buyerboss{downarrow}{enter}'); cy.get('input[aria-label="Buyer"]').type('buyerBossNick');
cy.get('div[role="listbox"] > div > div[role="option"]')
.eq(0)
.should('be.visible')
.click();
cy.get('[data-cy="FormModelPopup_save"]').click();
cy.get('.q-notification__message').should('have.text', 'Data created'); cy.get('.q-notification__message').should('have.text', 'Data created');
cy.get('[data-col-field="reserve"][data-row-index="1"]').click().clear();
cy.get('[data-cy="searchBtn"]').eq(1).click();
cy.get('.q-table__bottom.row.items-center.q-table__bottom--nodata')
.should('have.text', 'warningNo data available')
.type('{esc}');
cy.get('[data-col-field="reserve"][data-row-index="1"]')
.click()
.type('{backspace}{enter}');
cy.get('[data-cy="crudModelDefaultSaveBtn"]').should('be.enabled').click();
cy.get('.q-notification__message').eq(1).should('have.text', 'Data saved');
}); });
it('Should check detail for the buyer', () => { it('Should check detail for the buyer', () => {
cy.get(':nth-child(1) > .sticky > .q-btn > .q-btn__content > .q-icon').click(); cy.get('[data-cy="searchBtn"]').eq(0).click();
cy.get('tBody > tr').eq(1).its('length').should('eq', 1); cy.get('tBody > tr').eq(1).its('length').should('eq', 1);
}); });
it('Should check detail for the buyerBoss and had no content', () => {
cy.get(':nth-child(2) > .sticky > .q-btn > .q-btn__content > .q-icon').click();
cy.get('.q-table__bottom.row.items-center.q-table__bottom--nodata').should(
'have.text',
'warningNo data available',
);
});
it('Should edit travel m3 and refresh', () => { it('Should edit travel m3 and refresh', () => {
cy.get('.vn-row > div > .q-btn > .q-btn__content > .q-icon').click(); cy.get('[data-cy="edit-travel"]').should('be.visible').click();
cy.get('input[aria-label="m3"]').clear(); cy.get('input[aria-label="m3"]').clear().type('60');
cy.get('input[aria-label="m3"]').type('60'); cy.get('[data-cy="FormModelPopup_save"]').click();
cy.get('.q-mt-lg > .q-btn--standard > .q-btn__content > .block').click();
cy.get('.vn-row > div > :nth-child(2)').should('have.text', '60'); cy.get('.vn-row > div > :nth-child(2)').should('have.text', '60');
}); });
}); });

View File

@ -36,7 +36,7 @@ describe('InvoiceInVat', () => {
cy.get(dialogInputs).eq(0).type(randomInt); cy.get(dialogInputs).eq(0).type(randomInt);
cy.get(dialogInputs).eq(1).type('This is a dummy expense'); cy.get(dialogInputs).eq(1).type('This is a dummy expense');
cy.get('button[type="submit"]').click(); cy.get('[data-cy="FormModelPopup_save"]').click();
cy.get('.q-notification__message').should('have.text', 'Data created'); cy.get('.q-notification__message').should('have.text', 'Data created');
}); });
}); });