feat: refs #8602 update localization for purchased spaces and enhance Entry components with new labels
gitea/salix-front/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
Pablo Natek 2025-03-17 08:55:55 +01:00
parent cee2bb5111
commit 8e5cfe9fd8
10 changed files with 104 additions and 195 deletions

View File

@ -181,8 +181,12 @@ async function saveChanges(data) {
return; return;
} }
let changes = data || getChanges(); let changes = data || getChanges();
if ($props.beforeSaveFn) { console.log('$props.beforeSaveFn: ', $props.beforeSaveFn);
if ($props.beforeSaveFn && typeof $props.beforeSaveFn === 'function') {
console.log('Ejecutando beforeSaveFn');
changes = await $props.beforeSaveFn(changes, getChanges); changes = await $props.beforeSaveFn(changes, getChanges);
} else {
console.log('beforeSaveFn no es una función válida o no está definida');
} }
try { try {
if (changes?.creates?.length === 0 && changes?.updates?.length === 0) { if (changes?.creates?.length === 0 && changes?.updates?.length === 0) {
@ -194,7 +198,7 @@ async function saveChanges(data) {
isLoading.value = false; isLoading.value = false;
} }
originalData.value = JSON.parse(JSON.stringify(formData.value)); originalData.value = JSON.parse(JSON.stringify(formData.value));
if (changes.creates?.length) await vnPaginateRef.value.fetch(); if (changes?.creates?.length) await vnPaginateRef.value.fetch();
hasChanges.value = false; hasChanges.value = false;
emit('saveChanges', data); emit('saveChanges', data);

View File

@ -595,19 +595,20 @@ function cardClick(_, row) {
function removeTextValue(data, getChanges) { function removeTextValue(data, getChanges) {
let changes = data.updates; let changes = data.updates;
if (!changes) return data; if (changes) {
for (const change of changes) {
for (const change of changes) { for (const key in change.data) {
for (const key in change.data) { if (key.endsWith('VnTableTextValue')) {
if (key.endsWith('VnTableTextValue')) { delete change.data[key];
delete change.data[key]; }
} }
} }
data.updates = changes.filter((change) => Object.keys(change.data).length > 0);
}
if ($attrs?.beforeSaveFn) {
data = $attrs.beforeSaveFn(data, getChanges);
} }
data.updates = changes.filter((change) => Object.keys(change.data).length > 0);
if ($attrs?.beforeSaveFn) data = $attrs.beforeSaveFn(data, getChanges);
return data; return data;
} }

View File

@ -146,12 +146,15 @@ onMounted(() => {
<VnRow class="q-py-sm"> <VnRow class="q-py-sm">
<VnCheckbox <VnCheckbox
v-model="data.isOrdered" v-model="data.isOrdered"
:label="t('entry.summary.ordered')" :label="t('entry.list.tableVisibleColumns.isOrdered')"
/>
<VnCheckbox
v-model="data.isConfirmed"
:label="t('entry.list.tableVisibleColumns.isConfirmed')"
/> />
<VnCheckbox v-model="data.isConfirmed" :label="t('globals.confirmed')" />
<VnCheckbox <VnCheckbox
v-model="data.isExcludedFromAvailable" v-model="data.isExcludedFromAvailable"
:label="t('entry.summary.excludedFromAvailable')" :label="t('entry.list.tableVisibleColumns.isExcludedFromAvailable')"
/> />
<VnCheckbox <VnCheckbox
:disable="!isAdministrative()" :disable="!isAdministrative()"

View File

@ -92,13 +92,13 @@ onMounted(async () => {
</div> </div>
<div class="card-content"> <div class="card-content">
<VnCheckbox <VnCheckbox
:label="t('entry.summary.ordered')" :label="t('entry.list.tableVisibleColumns.isOrdered')"
v-model="entry.isOrdered" v-model="entry.isOrdered"
:disable="true" :disable="true"
size="xs" size="xs"
/> />
<VnCheckbox <VnCheckbox
:label="t('globals.confirmed')" :label="t('entry.list.tableVisibleColumns.isConfirmed')"
v-model="entry.isConfirmed" v-model="entry.isConfirmed"
:disable="true" :disable="true"
size="xs" size="xs"
@ -110,7 +110,11 @@ onMounted(async () => {
size="xs" size="xs"
/> />
<VnCheckbox <VnCheckbox
:label="t('entry.summary.excludedFromAvailable')" :label="
t(
'entry.list.tableVisibleColumns.isExcludedFromAvailable',
)
"
v-model="entry.isExcludedFromAvailable" v-model="entry.isExcludedFromAvailable"
:disable="true" :disable="true"
size="xs" size="xs"

View File

@ -1,24 +1,23 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useState } from 'src/composables/useState'; import { useQuasar, date } from 'quasar';
import { useQuasar } from 'quasar';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import FormModelPopup from 'components/FormModelPopup.vue'; import FormModelPopup from 'components/FormModelPopup.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import EntryStockBoughtFilter from './EntryStockBoughtFilter.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue'; import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
import TravelDescriptorProxy from '../Travel/Card/TravelDescriptorProxy.vue';
import { useFilterParams } from 'src/composables/useFilterParams';
import axios from 'axios';
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const state = useState(); const filterDate = ref(useFilterParams('StockBoughts').params);
const user = state.getUser();
const footer = ref({ bought: 0, reserve: 0 }); const footer = ref({ bought: 0, reserve: 0 });
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -46,7 +45,7 @@ const columns = computed(() => [
optionValue: 'id', optionValue: 'id',
}, },
columnFilter: false, columnFilter: false,
width: '50px', width: '60%',
}, },
{ {
align: 'center', align: 'center',
@ -56,20 +55,20 @@ const columns = computed(() => [
create: true, create: true,
component: 'number', component: 'number',
summation: true, summation: true,
width: '50px',
format: ({ reserve }, dashIfEmpty) => dashIfEmpty(round(reserve)), format: ({ reserve }, dashIfEmpty) => dashIfEmpty(round(reserve)),
width: '20%',
}, },
{ {
align: 'center', align: 'right',
label: t('entryStockBought.bought'), label: t('entryStockBought.bought'),
name: 'bought', name: 'bought',
summation: true, summation: true,
cardVisible: true, cardVisible: true,
style: ({ reserve, bought }) => boughtStyle(bought, reserve), style: ({ reserve, bought }) => boughtStyle(bought, reserve),
columnFilter: false, columnFilter: false,
width: '20%',
}, },
{ {
align: 'left',
label: t('entryStockBought.date'), label: t('entryStockBought.date'),
name: 'dated', name: 'dated',
component: 'date', component: 'date',
@ -77,20 +76,20 @@ const columns = computed(() => [
create: true, create: true,
}, },
{ {
align: 'left', align: 'center',
name: 'tableActions', name: 'tableActions',
actions: [ actions: [
{ {
title: t('entryStockBought.viewMoreDetails'), title: t('entryStockBought.viewMoreDetails'),
name: 'searchBtn', name: 'searchBtn',
icon: 'add', icon: 'search',
isPrimary: true, isPrimary: true,
action: (row) => { action: (row) => {
quasar.dialog({ quasar.dialog({
component: EntryStockBoughtDetail, component: EntryStockBoughtDetail,
componentProps: { componentProps: {
workerFk: row.workerFk, workerFk: row.workerFk,
dated: userParams.value.dated, dated: filterDate.value.dated,
}, },
}); });
}, },
@ -98,39 +97,29 @@ const columns = computed(() => [
], ],
}, },
]); ]);
const fetchDataRef = ref(); const fetchDataRef = ref();
const travelDialogRef = ref(false); const travelDialogRef = ref(false);
const tableRef = ref(); const tableRef = ref();
const travel = ref(null); const travel = ref(null);
const userParams = ref({ const filter = computed(() => ({
dated: Date.vnNew().toJSON(), fields: ['id', 'm3', 'ref', 'warehouseInFk'],
});
const filter = ref({
fields: ['id', 'm3', 'warehouseInFk'],
include: [ include: [
{ {
relation: 'warehouseIn', relation: 'warehouseIn',
scope: { scope: {
fields: ['code'], fields: ['code', 'name'],
}, },
}, },
], ],
where: { where: {
shipped: (userParams.value.dated shipped: date.adjustDate(filterDate.value.dated, {
? new Date(userParams.value.dated) hour: 0,
: Date.vnNew() minute: 0,
).setHours(0, 0, 0, 0), second: 0,
}),
m3: { neq: null }, m3: { neq: null },
}, },
}); }));
const setUserParams = async ({ dated }) => {
const shipped = (dated ? new Date(dated) : Date.vnNew()).setHours(0, 0, 0, 0);
filter.value.where.shipped = shipped;
fetchDataRef.value?.fetch();
};
function openDialog() { function openDialog() {
travelDialogRef.value = true; travelDialogRef.value = true;
@ -151,6 +140,31 @@ function round(value) {
function boughtStyle(bought, reserve) { function boughtStyle(bought, reserve) {
return reserve < bought ? { color: 'var(--q-negative)' } : ''; return reserve < bought ? { color: 'var(--q-negative)' } : '';
} }
async function beforeSave(data, getChanges) {
const changes = data.creates;
if (!changes) return data;
const patchPromises = [];
for (const change of changes) {
if (change?.isReal === false && change?.reserve > 0) {
const postData = {
workerFk: change.workerFk,
reserve: change.reserve,
dated: filterDate.value.dated,
};
const promise = axios.post('StockBoughts', postData).catch((error) => {
console.error('Error processing change: ', change, error);
});
patchPromises.push(promise);
}
}
await Promise.all(patchPromises);
const filteredChanges = changes.filter((change) => change?.isReal !== false);
data.creates = filteredChanges;
}
</script> </script>
<template> <template>
<VnSubToolbar> <VnSubToolbar>
@ -158,18 +172,17 @@ function boughtStyle(bought, reserve) {
<FetchData <FetchData
ref="fetchDataRef" ref="fetchDataRef"
url="Travels" url="Travels"
auto-load
:filter="filter" :filter="filter"
@on-fetch=" @on-fetch="
(data) => { (data) => {
travel = data.find( travel = data.find(
(data) => data.warehouseIn?.code.toLowerCase() === 'vnh', (data) => data.warehouseIn?.code?.toLowerCase() === 'vnh',
); );
} }
" "
/> />
<VnRow class="travel"> <VnRow class="travel">
<div v-if="travel"> <div v-show="travel">
<span style="color: var(--vn-label-color)"> <span style="color: var(--vn-label-color)">
{{ t('entryStockBought.purchaseSpaces') }}: {{ t('entryStockBought.purchaseSpaces') }}:
</span> </span>
@ -180,7 +193,7 @@ function boughtStyle(bought, reserve) {
v-if="travel?.m3" v-if="travel?.m3"
style="max-width: 20%" style="max-width: 20%"
flat flat
icon="edit" icon="search"
@click="openDialog()" @click="openDialog()"
:title="t('entryStockBought.editTravel')" :title="t('entryStockBought.editTravel')"
color="primary" color="primary"
@ -195,57 +208,42 @@ function boughtStyle(bought, reserve) {
:url-update="`Travels/${travel?.id}`" :url-update="`Travels/${travel?.id}`"
model="travel" model="travel"
:title="t('Travel m3')" :title="t('Travel m3')"
:form-initial-data="{ id: travel?.id, m3: travel?.m3 }" :form-initial-data="travel"
@on-data-saved="fetchDataRef.fetch()" @on-data-saved="fetchDataRef.fetch()"
> >
<template #form-inputs="{ data }"> <template #form-inputs="{ data }">
<VnInput <span class="link">
v-model="data.id" {{ data.ref }}
:label="t('id')" <TravelDescriptorProxy :id="data.id" />
type="number" </span>
disable
readonly
/>
<VnInput v-model="data.m3" :label="t('m3')" type="number" /> <VnInput v-model="data.m3" :label="t('m3')" type="number" />
</template> </template>
</FormModelPopup> </FormModelPopup>
</QDialog> </QDialog>
<RightMenu>
<template #right-panel>
<EntryStockBoughtFilter
data-key="StockBoughts"
@set-user-params="setUserParams"
/>
</template>
</RightMenu>
<div class="table-container"> <div class="table-container">
<div class="column items-center"> <div class="column items-center">
<VnTable <VnTable
ref="tableRef" ref="tableRef"
data-key="StockBoughts" data-key="StockBoughts"
url="StockBoughts/getStockBought" url="StockBoughts/getStockBought"
:beforeSaveFn="beforeSave"
save-url="StockBoughts/crud" save-url="StockBoughts/crud"
search-url="StockBoughts" search-url="StockBoughts"
order="reserve DESC" order="bought DESC"
:right-search="false"
:is-editable="true" :is-editable="true"
@on-fetch="(data) => setFooter(data)" @on-fetch="
:create="{ async (data) => {
urlCreate: 'StockBoughts', setFooter(data);
title: t('entryStockBought.reserveSomeSpace'), await fetchDataRef.fetch();
onDataSaved: () => tableRef.reload(), }
formInitialData: { "
workerFk: user.id,
dated: Date.vnNow(),
},
}"
:columns="columns" :columns="columns"
:user-params="userParams"
:footer="true" :footer="true"
table-height="80vh" table-height="80vh"
auto-load
:column-search="false" :column-search="false"
:without-header="true" :without-header="true"
:user-params="{ dated: Date.vnNew() }"
auto-load
> >
<template #column-workerFk="{ row }"> <template #column-workerFk="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
@ -278,9 +276,6 @@ function boughtStyle(bought, reserve) {
.column { .column {
min-width: 35%; min-width: 35%;
margin-top: 5%; margin-top: 5%;
display: flex;
flex-direction: column;
align-items: center;
} }
.text-negative { .text-negative {
color: $negative !important; color: $negative !important;

View File

@ -1,70 +0,0 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue';
import { useStateStore } from 'stores/useStateStore';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const stateStore = useStateStore();
const emit = defineEmits(['set-user-params']);
const setUserParams = (params) => {
emit('set-user-params', params);
};
onMounted(async () => {
stateStore.rightDrawer = true;
});
</script>
<template>
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
search-url="StockBoughts"
@set-user-params="setUserParams"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<QItem class="q-my-sm">
<QItemSection>
<VnInputDate
id="date"
v-model="params.dated"
@update:model-value="
(value) => {
params.dated = value;
setUserParams(params);
searchFn();
}
"
:label="t('Date')"
is-outlined
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
dated: Date
workerFk: Worker
es:
Date: Fecha
params:
dated: Fecha
workerFk: Trabajador
</i18n>

View File

@ -155,7 +155,7 @@ entrySupplier:
entryStockBought: entryStockBought:
travel: Envío travel: Envío
editTravel: Editar envío editTravel: Editar envío
purchaseSpaces: Espacios de compra purchaseSpaces: Camiones reservados
buyer: Comprador buyer: Comprador
reserve: Reservado reserve: Reservado
bought: Comprado bought: Comprado

View File

@ -63,5 +63,6 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
clear, clear,
reset, reset,
resetPagination, resetPagination,
state,
}; };
}); });

View File

@ -1,6 +1,6 @@
import './commands'; import './commands';
describe('Entry', () => { describe('EntryList', () => {
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080); cy.viewport(1920, 1080);
cy.login('buyer'); cy.login('buyer');

View File

@ -4,49 +4,20 @@ describe('EntryStockBought', () => {
cy.login('buyer'); cy.login('buyer');
cy.visit(`/#/entry/stock-Bought`); cy.visit(`/#/entry/stock-Bought`);
}); });
it('Should edit the reserved space', () => {
it('Should edit the reserved space adjust the purchased spaces and check detail', () => {
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');
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('[data-col-field="reserve"][data-row-index="0"]').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.checkNotification('Data saved'); cy.checkNotification('Data saved');
});
it('Should add a new reserved space for buyerBoss', () => {
cy.addBtnClick();
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('itNick');
cy.get('div[role="listbox"] > div > div[role="option"]')
.eq(1)
.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('[data-cy="searchBtn"]').eq(0).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 edit travel m3 and refresh', () => {
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');
});
}); });