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

View File

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

View File

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

View File

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

View File

@ -134,7 +134,8 @@ onMounted(async () => {
if (!$props.formInitialData) {
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) {
watch(
@ -154,7 +155,7 @@ onMounted(async () => {
if (!$props.url)
watch(
() => arrayData.store.data,
(val) => updateAndEmit('onFetch', val),
(val) => updateAndEmit('onFetch', { val }),
);
watch(
@ -200,7 +201,7 @@ async function fetch() {
});
if (Array.isArray(data)) data = data[0] ?? {};
updateAndEmit('onFetch', data);
updateAndEmit('onFetch', { val: data });
} catch (e) {
state.set(modelValue, {});
throw e;
@ -228,7 +229,11 @@ async function save(prevent = false) {
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({});
hasChanges.value = false;
} finally {
@ -242,8 +247,7 @@ async function saveAndGo() {
}
function reset() {
formData.value = JSON.parse(JSON.stringify(originalData.value));
updateAndEmit('onFetch', originalData.value);
updateAndEmit('onFetch', { val: originalData.value });
if ($props.observeFormChanges) {
hasChanges.value = false;
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);
if (!$props.url) arrayData.store.data = val;
emit(evt, state.get(modelValue), res);
emit(evt, state.get(modelValue), res, old);
}
function trimData(data) {

View File

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

View File

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

View File

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

View File

@ -39,6 +39,13 @@ onBeforeMount(async () => {
});
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;
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}`);
await arrayData.fetch({ append, updateRouter: false });
}
function hasRouteParam(params, valueToCheck = ':addressId') {
return Object.values(params).includes(valueToCheck);
}
</script>
<template>
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">

View File

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

View File

@ -53,3 +53,8 @@ const manaCode = ref(props.manaCode);
/>
</div>
</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) {
const { validations } = useValidator();
const hasRequired = Object.keys($attrs).includes('required');
let isRequired = false;
if (hasRequired) {
const required = $attrs['required'];
if (typeof required === 'boolean') {
isRequired = required;
}
}
const isRequired =
typeof $attrs['required'] === 'boolean'
? $attrs['required']
: Object.keys($attrs).includes('required');
const requiredFieldRule = (val) => validations().required(isRequired, val);
return {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,10 @@
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
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 FormModel from 'components/FormModel.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 VnCheckbox from 'src/components/common/VnCheckbox.vue';
import { getDifferences, getUpdatedValues } from 'src/filters';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
const quasar = useQuasar();
const { t } = useI18n();
const route = useRoute();
const { notify } = useNotify();
const typesTaxes = ref([]);
const typesTransactions = ref([]);
@ -31,6 +37,31 @@ function onBeforeSave(formData, originalData) {
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>
<template>
@ -45,6 +76,8 @@ function onBeforeSave(formData, originalData) {
auto-load
model="Customer"
:mapper="onBeforeSave"
observe-form-changes
@on-data-saved="checkEtChanges"
>
<template #form="{ data, validate }">
<VnRow>
@ -180,6 +213,9 @@ es:
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
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:
onlyLetters: Only letters, numbers and spaces can be used
whenActivatingIt: When activating it, do not enter the country code in the ID field

View File

@ -270,7 +270,7 @@ const sumRisk = ({ clientRisks }) => {
<VnTitle
target="_blank"
: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"
/>
<VnLv

View File

@ -87,7 +87,7 @@ onMounted(async () => {
<FetchData
url="Campaigns/latest"
@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
/>
<FetchData

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ import axios from 'axios';
import { onMounted } from 'vue';
const { t } = useI18n();
const props = defineProps({
defineProps({
dataKey: {
type: String,
required: true,
@ -58,7 +58,7 @@ onMounted(async () => {
auto-load
/>
<VnFilterPanel
:data-key="props.dataKey"
:data-key
:un-removable-params="['warehouseFk', 'originScopeDays ', 'futureScopeDays']"
>
<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 VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
const route = useRoute();
const { t } = useI18n();
@ -53,7 +54,16 @@ const warehousesOptionsIn = ref([]);
<VnInputDate v-model="data.shipped" :label="t('globals.shipped')" />
<VnInputDate v-model="data.landed" :label="t('globals.landed')" />
</VnRow>
<VnRow>
<VnInputDate
v-model="data.availabled"
:label="t('travel.summary.availabled')"
/>
<VnInputTime
v-model="data.availabled"
:label="t('travel.summary.availabledHour')"
/>
</VnRow>
<VnRow>
<VnSelect
:label="t('globals.warehouseOut')"
@ -101,10 +111,3 @@ const warehousesOptionsIn = ref([]);
</template>
</FormModel>
</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',
'isRaid',
'daysInForward',
'availabled',
],
include: [
{

View File

@ -10,6 +10,8 @@ import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue'
import FetchData from 'src/components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import { toDate, toCurrency, toCelsius } from 'src/filters';
import { toDateTimeFormat } from 'src/filters/date.js';
import { dashIfEmpty } from 'src/filters';
import axios from 'axios';
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="m³" :value="travel.m3" />
<VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
<VnLv
:label="t('travel.summary.availabled')"
:value="
dashIfEmpty(toDateTimeFormat(travel.availabled))
"
/>
</QCard>
<QCard class="full-width">
<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 VnInputNumber from 'src/components/common/VnInputNumber.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 router = useRouter();
@ -167,6 +170,17 @@ const columns = computed(() => [
cardVisible: 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',
label: '',
@ -269,6 +283,16 @@ const columns = computed(() => [
:class="{ 'is-active': row.isReceived }"
/>
</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 }">
<VnInputNumber
:label="t('params.scopeDays')"

View File

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

View File

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

View File

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

View File

@ -3,11 +3,46 @@ describe('Client consignee', () => {
beforeEach(() => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('#/customer/1110/address', {
timeout: 5000,
});
cy.visit('#/customer/1107/address');
cy.domContentLoad();
});
it('Should load layout', () => {
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(() => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('#/customer/1107/fiscal-data', {
timeout: 5000,
});
cy.visit('#/customer/1107/fiscal-data');
cy.domContentLoad();
});
it('Should change required value when change customer', () => {
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.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}"]`);
const selectSpan = (field, row = 0) => selectCell(field, row).find('div > span');
const selectButton = (cySelector) => cy.get(`button[data-cy="${cySelector}"]`);
const clickAndType = (field, value, row = 0) =>
selectCell(field, row).click().type(value);
const clickAndType = (field, value, row = 0) => {
selectCell(field, row).click().type(`${value}{esc}`);
};
const checkText = (field, expectedText, row = 0) =>
selectCell(field, row).should('have.text', expectedText);
const checkColor = (field, expectedColor, row = 0) =>
@ -115,21 +116,18 @@ describe('Entry', () => {
createEntryAndBuy();
selectCell('isIgnored')
.click()
.click()
.trigger('keydown', { key: 'Tab', keyCode: 9, which: 9 });
checkText('isIgnored', 'check');
checkColor('quantity', COLORS.negative);
selectCell('isIgnored').click().click().type('{esc}');
checkText('isIgnored', 'close');
clickAndType('stickers', '1');
checkText('quantity', '11');
checkText('amount', '550.00');
checkText('stickers', '0/01');
checkText('quantity', '1');
checkText('amount', '50.00');
clickAndType('packing', '2');
checkText('packing', '12close');
checkText('packing', '12');
checkText('weight', '12.0');
checkText('quantity', '132');
checkText('amount', '6600.00');
checkText('quantity', '12');
checkText('amount', '600.00');
checkColor('packing', COLORS.enabled);
selectCell('groupingMode').click().click().click();
@ -137,7 +135,7 @@ describe('Entry', () => {
checkColor('grouping', COLORS.enabled);
selectCell('buyingValue').click().clear().type('{backspace}{backspace}1');
checkText('amount', '132.00');
checkText('amount', '12.00');
checkColor('minPrice', COLORS.disable);
selectCell('hasMinPrice').click().click();
@ -145,7 +143,7 @@ describe('Entry', () => {
selectCell('hasMinPrice').click();
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');
selectButton('change-quantity-sign').should('be.disabled');
@ -156,9 +154,9 @@ describe('Entry', () => {
selectButton('change-quantity-sign').click();
selectButton('set-negative-quantity').click();
checkText('quantity', '-132');
checkText('quantity', '-12');
selectButton('set-positive-quantity').click();
checkText('quantity', '132');
checkText('quantity', '12');
checkColor('amount', COLORS.disable);
selectButton('check-buy-amount').click();

View File

@ -6,7 +6,7 @@ describe('EntryStockBought', () => {
});
it('Should edit the reserved space', () => {
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('button[title="Save"]').click();
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="Date"]').eq(1).clear();
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('[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', () => {
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);
});
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', () => {
cy.get('.vn-row > div > .q-btn > .q-btn__content > .q-icon').click();
cy.get('input[aria-label="m3"]').clear();
cy.get('input[aria-label="m3"]').type('60');
cy.get('.q-mt-lg > .q-btn--standard > .q-btn__content > .block').click();
cy.get('[data-cy="edit-travel"]').should('be.visible').click();
cy.get('input[aria-label="m3"]').clear().type('60');
cy.get('[data-cy="FormModelPopup_save"]').click();
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(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');
});
});