Merge branch 'master' of https://gitea.verdnatura.es/verdnatura/salix-front into test
gitea/salix-front/pipeline/head This commit looks good Details

This commit is contained in:
Alex Moreno 2025-03-05 11:17:10 +01:00
commit 966d59e52d
8 changed files with 85 additions and 113 deletions

View File

@ -1,12 +1,9 @@
<script setup> <script setup>
import { nextTick, ref, watch } from 'vue'; import { nextTick, ref } from 'vue';
import { QInput } from 'quasar'; import VnInput from './VnInput.vue';
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
const $props = defineProps({ const $props = defineProps({
modelValue: {
type: String,
default: '',
},
insertable: { insertable: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -14,70 +11,25 @@ const $props = defineProps({
}); });
const emit = defineEmits(['update:modelValue', 'accountShortToStandard']); const emit = defineEmits(['update:modelValue', 'accountShortToStandard']);
const model = defineModel({ prop: 'modelValue' });
const inputRef = ref(false);
let internalValue = ref($props.modelValue); function setCursorPosition(pos) {
const input = inputRef.value.vnInputRef.$el.querySelector('input');
watch( input.focus();
() => $props.modelValue, input.setSelectionRange(pos, pos);
(newVal) => {
internalValue.value = newVal;
}
);
watch(
() => internalValue.value,
(newVal) => {
emit('update:modelValue', newVal);
accountShortToStandard();
}
);
const handleKeydown = (e) => {
if (e.key === 'Backspace') return;
if (e.key === '.') {
accountShortToStandard();
// TODO: Fix this setTimeout, with nextTick doesn't work
setTimeout(() => {
setCursorPosition(0, e.target);
}, 1);
return;
} }
if ($props.insertable && e.key.match(/[0-9]/)) { async function handleUpdateModel(val) {
handleInsertMode(e); model.value = val?.at(-1) === '.' ? useAccountShortToStandard(val) : val;
} await nextTick(() => setCursorPosition(0));
};
function setCursorPosition(pos, el = vnInputRef.value) {
el.focus();
el.setSelectionRange(pos, pos);
}
const vnInputRef = ref(false);
const handleInsertMode = (e) => {
e.preventDefault();
const input = e.target;
const cursorPos = input.selectionStart;
const { maxlength } = vnInputRef.value;
let currentValue = internalValue.value;
if (!currentValue) currentValue = e.key;
const newValue = e.key;
if (newValue && !isNaN(newValue) && cursorPos < maxlength) {
internalValue.value =
currentValue.substring(0, cursorPos) +
newValue +
currentValue.substring(cursorPos + 1);
}
nextTick(() => {
input.setSelectionRange(cursorPos + 1, cursorPos + 1);
});
};
function accountShortToStandard() {
internalValue.value = internalValue.value?.replace(
'.',
'0'.repeat(11 - internalValue.value.length)
);
} }
</script> </script>
<template> <template>
<QInput @keydown="handleKeydown" ref="vnInputRef" v-model="internalValue" /> <VnInput
v-model="model"
ref="inputRef"
:insertable
@update:model-value="handleUpdateModel"
/>
</template> </template>

View File

@ -83,7 +83,7 @@ const mixinRules = [
requiredFieldRule, requiredFieldRule,
...($attrs.rules ?? []), ...($attrs.rules ?? []),
(val) => { (val) => {
const { maxlength } = vnInputRef.value; const maxlength = $props.maxlength;
if (maxlength && +val.length > maxlength) if (maxlength && +val.length > maxlength)
return t(`maxLength`, { value: maxlength }); return t(`maxLength`, { value: maxlength });
const { min, max } = vnInputRef.value.$attrs; const { min, max } = vnInputRef.value.$attrs;
@ -108,7 +108,7 @@ const handleInsertMode = (e) => {
e.preventDefault(); e.preventDefault();
const input = e.target; const input = e.target;
const cursorPos = input.selectionStart; const cursorPos = input.selectionStart;
const { maxlength } = vnInputRef.value; const maxlength = $props.maxlength;
let currentValue = value.value; let currentValue = value.value;
if (!currentValue) currentValue = e.key; if (!currentValue) currentValue = e.key;
const newValue = e.key; const newValue = e.key;
@ -143,7 +143,7 @@ const handleUppercase = () => {
:rules="mixinRules" :rules="mixinRules"
:lazy-rules="true" :lazy-rules="true"
hide-bottom-space hide-bottom-space
:data-cy="$attrs.dataCy ?? $attrs.label + '_input'" :data-cy="$attrs['data-cy'] ?? $attrs.label + '_input'"
> >
<template #prepend v-if="$slots.prepend"> <template #prepend v-if="$slots.prepend">
<slot name="prepend" /> <slot name="prepend" />

View File

@ -132,7 +132,8 @@ const card = toRef(props, 'item');
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: 4px;
white-space: nowrap;
width: 192px;
p { p {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -369,6 +369,7 @@ globals:
countryFk: Country countryFk: Country
countryCodeFk: Country countryCodeFk: Country
companyFk: Company companyFk: Company
nickname: Alias
model: Model model: Model
fuel: Fuel fuel: Fuel
active: Active active: Active

View File

@ -370,6 +370,7 @@ globals:
countryFk: País countryFk: País
countryCodeFk: País countryCodeFk: País
companyFk: Empresa companyFk: Empresa
nickname: Alias
errors: errors:
statusUnauthorized: Acceso denegado statusUnauthorized: Acceso denegado
statusInternalServerError: Ha ocurrido un error interno del servidor statusInternalServerError: Ha ocurrido un error interno del servidor

View File

@ -108,7 +108,6 @@ function handleLocation(data, location) {
<VnAccountNumber <VnAccountNumber
v-model="data.account" v-model="data.account"
:label="t('supplier.fiscalData.account')" :label="t('supplier.fiscalData.account')"
clearable
data-cy="supplierFiscalDataAccount" data-cy="supplierFiscalDataAccount"
insertable insertable
:maxlength="10" :maxlength="10"

View File

@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import SupplierSummary from './Card/SupplierSummary.vue'; import SupplierSummary from './Card/SupplierSummary.vue';
@ -53,7 +52,7 @@ const columns = computed(() => [
label: t('globals.alias'), label: t('globals.alias'),
name: 'alias', name: 'alias',
columnFilter: { columnFilter: {
name: 'search', name: 'nickname',
}, },
cardVisible: true, cardVisible: true,
}, },
@ -120,6 +119,21 @@ const columns = computed(() => [
], ],
}, },
]); ]);
const filterColumns = computed(() => {
const copy = [...columns.value];
copy.splice(copy.length - 1, 0, {
align: 'left',
label: t('globals.params.provinceFk'),
name: 'provinceFk',
options: provincesOptions.value,
columnFilter: {
component: 'select',
},
});
return copy;
});
</script> </script>
<template> <template>
<FetchData <FetchData
@ -130,7 +144,7 @@ const columns = computed(() => [
/> />
<VnSection <VnSection
:data-key="dataKey" :data-key="dataKey"
:columns="columns" :columns="filterColumns"
prefix="supplier" prefix="supplier"
:array-data-props="{ :array-data-props="{
url: 'Suppliers/filter', url: 'Suppliers/filter',
@ -165,17 +179,6 @@ const columns = computed(() => [
</template> </template>
</VnTable> </VnTable>
</template> </template>
<template #moreFilterPanel="{ params, searchFn }">
<VnSelect
:label="t('globals.params.provinceFk')"
v-model="params.provinceFk"
@update:model-value="searchFn()"
:options="provincesOptions"
filled
dense
class="q-px-sm q-pr-lg"
/>
</template>
</VnSection> </VnSection>
</template> </template>

View File

@ -1,35 +1,50 @@
describe('VnInput Component', () => { describe('VnAccountNumber', () => {
beforeEach(() => { beforeEach(() => {
cy.login('developer'); cy.login('developer');
cy.viewport(1920, 1080); cy.viewport(1920, 1080);
cy.visit('/#/supplier/1/fiscal-data'); cy.visit('/#/supplier/1/fiscal-data');
}); });
describe('VnInput handleInsertMode()', () => {
it('should replace character at cursor position in insert mode', () => { it('should replace character at cursor position in insert mode', () => {
// Simula escribir en el input cy.get('input[data-cy="supplierFiscalDataAccount"]').type(
cy.dataCy('supplierFiscalDataAccount').clear(); '{selectall}4100000001',
cy.dataCy('supplierFiscalDataAccount').type('4100000001'); );
// Coloca el cursor en la posición 0 cy.get('input[data-cy="supplierFiscalDataAccount"]').type('{movetostart}');
cy.dataCy('supplierFiscalDataAccount').type('{movetostart}'); cy.get('input[data-cy="supplierFiscalDataAccount"]').type('999');
// Escribe un número y verifica que se reemplace correctamente cy.get('input[data-cy="supplierFiscalDataAccount"]').should(
cy.dataCy('supplierFiscalDataAccount').type('999'); 'have.value',
cy.dataCy('supplierFiscalDataAccount').should('have.value', '9990000001'); '9990000001',
);
}); });
it('should replace character at cursor position in insert mode', () => { it('should replace character at cursor position in insert mode', () => {
// Simula escribir en el input cy.get('input[data-cy="supplierFiscalDataAccount"]').clear();
cy.dataCy('supplierFiscalDataAccount').clear(); cy.get('input[data-cy="supplierFiscalDataAccount"]').type('4100000001');
cy.dataCy('supplierFiscalDataAccount').type('4100000001'); cy.get('input[data-cy="supplierFiscalDataAccount"]').type('{movetostart}');
// Coloca el cursor en la posición 0 cy.get('input[data-cy="supplierFiscalDataAccount"]').type('999');
cy.dataCy('supplierFiscalDataAccount').type('{movetostart}'); cy.get('input[data-cy="supplierFiscalDataAccount"]').should(
// Escribe un número y verifica que se reemplace correctamente en la posicion incial 'have.value',
cy.dataCy('supplierFiscalDataAccount').type('999'); '9990000001',
cy.dataCy('supplierFiscalDataAccount').should('have.value', '9990000001'); );
}); });
it('should respect maxlength prop', () => { it('should respect maxlength prop', () => {
cy.dataCy('supplierFiscalDataAccount').clear(); cy.get('input[data-cy="supplierFiscalDataAccount"]').clear();
cy.dataCy('supplierFiscalDataAccount').type('123456789012345'); cy.get('input[data-cy="supplierFiscalDataAccount"]').type('123456789012345');
cy.dataCy('supplierFiscalDataAccount').should('have.value', '1234567890'); // asumiendo que maxlength es 10 cy.get('input[data-cy="supplierFiscalDataAccount"]').should(
'have.value',
'1234567890',
);
});
});
it('should convert short account number to standard format', () => {
cy.get('input[data-cy="supplierFiscalDataAccount"]').clear();
cy.get('input[data-cy="supplierFiscalDataAccount"]').type('123.');
cy.get('input[data-cy="supplierFiscalDataAccount"]').should(
'have.value',
'1230000000',
);
}); });
}); });