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;
}
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);
} else {
console.log('beforeSaveFn no es una función válida o no está definida');
}
try {
if (changes?.creates?.length === 0 && changes?.updates?.length === 0) {
@ -194,7 +198,7 @@ async function saveChanges(data) {
isLoading.value = false;
}
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;
emit('saveChanges', data);

View File

@ -595,19 +595,20 @@ function cardClick(_, row) {
function removeTextValue(data, getChanges) {
let changes = data.updates;
if (!changes) return data;
for (const change of changes) {
for (const key in change.data) {
if (key.endsWith('VnTableTextValue')) {
delete change.data[key];
if (changes) {
for (const change of changes) {
for (const key in change.data) {
if (key.endsWith('VnTableTextValue')) {
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;
}

View File

@ -146,12 +146,15 @@ onMounted(() => {
<VnRow class="q-py-sm">
<VnCheckbox
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
v-model="data.isExcludedFromAvailable"
:label="t('entry.summary.excludedFromAvailable')"
:label="t('entry.list.tableVisibleColumns.isExcludedFromAvailable')"
/>
<VnCheckbox
:disable="!isAdministrative()"

View File

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

View File

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

View File

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

View File

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

View File

@ -4,49 +4,20 @@ describe('EntryStockBought', () => {
cy.login('buyer');
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('[data-col-field="reserve"][data-row-index="0"]').click();
cy.get('input[name="reserve"]').type('10{enter}');
cy.get('button[title="Save"]').click();
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('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');
});
});