refactor: update ItemDescriptor to use dynamic labels for values #1505

Merged
pablone merged 4 commits from hotFixItemDescriptor into master 2025-02-27 08:25:58 +00:00
10 changed files with 124 additions and 91 deletions
Showing only changes of commit c362282772 - Show all commits

View File

@ -295,8 +295,14 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
if (evt?.shiftKey && added) { if (evt?.shiftKey && added) {
const rowIndex = selectedRows[0].$index; const rowIndex = selectedRows[0].$index;
const selectedIndexes = new Set(selected.value.map((row) => row.$index)); const selectedIndexes = new Set(selected.value.map((row) => row.$index));
for (const row of rows) { const minIndex = selectedIndexes.size
if (row.$index == rowIndex) break; ? Math.min(...selectedIndexes, rowIndex)
: 0;
const maxIndex = Math.max(...selectedIndexes, rowIndex);
for (let i = minIndex; i <= maxIndex; i++) {
const row = rows[i];
if (row.$index == rowIndex) continue;
if (!selectedIndexes.has(row.$index)) { if (!selectedIndexes.has(row.$index)) {
selected.value.push(row); selected.value.push(row);
selectedIndexes.add(row.$index); selectedIndexes.add(row.$index);

View File

@ -27,30 +27,58 @@ describe('VnTable', () => {
beforeEach(() => (vm.selected = [])); beforeEach(() => (vm.selected = []));
describe('handleSelection()', () => { describe('handleSelection()', () => {
const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }]; const rows = [
{ $index: 0 },
{ $index: 1 },
{ $index: 2 },
{ $index: 3 },
{ $index: 4 },
];
it('should add rows to selected when shift key is pressed and rows are added in ascending order', () => {
const selectedRows = [{ $index: 1 }]; const selectedRows = [{ $index: 1 }];
it('should add rows to selected when shift key is pressed and rows are added except last one', () => {
vm.handleSelection( vm.handleSelection(
{ evt: { shiftKey: true }, added: true, rows: selectedRows }, { evt: { shiftKey: true }, added: true, rows: selectedRows },
rows rows,
); );
expect(vm.selected).toEqual([{ $index: 0 }]); expect(vm.selected).toEqual([{ $index: 0 }]);
}); });
it('should add rows to selected when shift key is pressed and rows are added in descending order', () => {
const selectedRows = [{ $index: 3 }];
vm.handleSelection(
{ evt: { shiftKey: true }, added: true, rows: selectedRows },
rows,
);
expect(vm.selected).toEqual([{ $index: 0 }, { $index: 1 }, { $index: 2 }]);
});
it('should not add rows to selected when shift key is not pressed', () => { it('should not add rows to selected when shift key is not pressed', () => {
const selectedRows = [{ $index: 1 }];
vm.handleSelection( vm.handleSelection(
{ evt: { shiftKey: false }, added: true, rows: selectedRows }, { evt: { shiftKey: false }, added: true, rows: selectedRows },
rows rows,
); );
expect(vm.selected).toEqual([]); expect(vm.selected).toEqual([]);
}); });
it('should not add rows to selected when rows are not added', () => { it('should not add rows to selected when rows are not added', () => {
const selectedRows = [{ $index: 1 }];
vm.handleSelection( vm.handleSelection(
{ evt: { shiftKey: true }, added: false, rows: selectedRows }, { evt: { shiftKey: true }, added: false, rows: selectedRows },
rows rows,
); );
expect(vm.selected).toEqual([]); expect(vm.selected).toEqual([]);
}); });
it('should add all rows between the smallest and largest selected indexes', () => {
vm.selected = [{ $index: 1 }, { $index: 3 }];
const selectedRows = [{ $index: 4 }];
vm.handleSelection(
{ evt: { shiftKey: true }, added: true, rows: selectedRows },
rows,
);
expect(vm.selected).toEqual([{ $index: 1 }, { $index: 3 }, { $index: 2 }]);
});
}); });
}); });

View File

@ -1,4 +1,3 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
@ -148,6 +147,7 @@ const exprBuilder = (param, value) => {
outlined outlined
rounded rounded
auto-load auto-load
sortBy="name ASC"
/></QItemSection> /></QItemSection>
</QItem> </QItem>
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">

View File

@ -78,10 +78,20 @@ const columns = computed(() => [
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/activeWithInheritedRole', url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'], fields: ['id', 'name', 'firstName'],
where: { role: 'salesPerson' }, where: { role: 'salesPerson' },
optionFilter: 'firstName', optionFilter: 'firstName',
}, },
columnFilter: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name', 'firstName'],
where: { role: 'salesPerson' },
optionLabel: 'firstName',
optionValue: 'id',
},
},
create: false, create: false,
columnField: { columnField: {
component: null, component: null,

View File

@ -77,24 +77,23 @@ onBeforeMount(() => {
function setPaymentType(accounting) { function setPaymentType(accounting) {
if (!accounting) return; if (!accounting) return;
accountingType.value = accounting.accountingType; accountingType.value = accounting.accountingType;
initialData.description = []; initialData.description = [];
initialData.payed = Date.vnNew(); initialData.payed = Date.vnNew();
isCash.value = accountingType.value.code == 'cash'; isCash.value = accountingType.value.code == 'cash';
viewReceipt.value = isCash.value; viewReceipt.value = isCash.value;
if (accountingType.value.daysInFuture) if (accountingType.value.daysInFuture)
initialData.payed.setDate( initialData.payed.setDate(
initialData.payed.getDate() + accountingType.value.daysInFuture initialData.payed.getDate() + accountingType.value.daysInFuture,
); );
maxAmount.value = accountingType.value && accountingType.value.maxAmount; maxAmount.value = accountingType.value && accountingType.value.maxAmount;
if (accountingType.value.code == 'compensation') if (accountingType.value.code == 'compensation')
return (initialData.description = ''); return (initialData.description = '');
if (accountingType.value.receiptDescription)
initialData.description.push(accountingType.value.receiptDescription);
if (initialData.description) initialData.description.push(initialData.description);
initialData.description = initialData.description.join(', '); let descriptions = [];
if (accountingType.value.receiptDescription)
descriptions.push(accountingType.value.receiptDescription);
if (initialData.description) descriptions.push(initialData.description);
initialData.description = descriptions.join(', ');
} }
const calculateFromAmount = (event) => { const calculateFromAmount = (event) => {

View File

@ -87,7 +87,7 @@ const insertTag = (rows) => {
tagFk: undefined, tagFk: undefined,
}" }"
:default-remove="false" :default-remove="false"
:filter="{ :user-filter="{
fields: ['id', 'itemFk', 'tagFk', 'value', 'priority'], fields: ['id', 'itemFk', 'tagFk', 'value', 'priority'],
where: { itemFk: route.params.id }, where: { itemFk: route.params.id },
include: { include: {
@ -119,6 +119,7 @@ const insertTag = (rows) => {
" "
:required="true" :required="true"
:rules="validate('itemTag.tagFk')" :rules="validate('itemTag.tagFk')"
:data-cy="`tag${row?.tag?.name}`"
/> />
<VnSelect <VnSelect
v-if="row.tag?.isFree === false" v-if="row.tag?.isFree === false"
@ -145,6 +146,7 @@ const insertTag = (rows) => {
:label="t('itemTags.value')" :label="t('itemTags.value')"
:is-clearable="false" :is-clearable="false"
@keyup.enter.stop="(data) => itemTagsRef.onSubmit(data)" @keyup.enter.stop="(data) => itemTagsRef.onSubmit(data)"
:data-cy="`tag${row?.tag?.name}Value`"
/> />
<VnInput <VnInput
:label="t('itemBasicData.relevancy')" :label="t('itemBasicData.relevancy')"
@ -162,6 +164,7 @@ const insertTag = (rows) => {
name="delete" name="delete"
size="sm" size="sm"
dense dense
:data-cy="`deleteTag${row?.tag?.name}`"
> >
<QTooltip> <QTooltip>
{{ t('itemTags.removeTag') }} {{ t('itemTags.removeTag') }}
@ -177,6 +180,7 @@ const insertTag = (rows) => {
icon="add" icon="add"
shortcut="+" shortcut="+"
fab fab
data-cy="createNewTag"
> >
<QTooltip> <QTooltip>
{{ t('itemTags.addTag') }} {{ t('itemTags.addTag') }}

View File

@ -1,32 +1,26 @@
<script setup> <script setup>
import { ref } from 'vue'; import axios from 'axios';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
import VnUsesMana from 'components/ui/VnUsesMana.vue'; import VnUsesMana from 'components/ui/VnUsesMana.vue';
const $props = defineProps({ const $props = defineProps({
mana: {
type: Number,
default: null,
},
newPrice: { newPrice: {
type: Number, type: Number,
default: 0, default: 0,
}, },
usesMana: {
type: Boolean,
default: false,
},
manaCode: {
type: String,
default: 'mana',
},
sale: { sale: {
type: Object, type: Object,
default: null, default: null,
}, },
}); });
const route = useRoute();
const mana = ref(null);
const usesMana = ref(false);
const emit = defineEmits(['save', 'cancel']); const emit = defineEmits(['save', 'cancel']);
const { t } = useI18n(); const { t } = useI18n();
@ -38,20 +32,35 @@ const save = (sale = $props.sale) => {
QPopupProxyRef.value.hide(); QPopupProxyRef.value.hide();
}; };
const getMana = async () => {
const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`);
mana.value = data;
await getUsesMana();
};
const getUsesMana = async () => {
const { data } = await axios.get('Sales/usesMana');
usesMana.value = data;
};
const cancel = () => { const cancel = () => {
emit('cancel'); emit('cancel');
QPopupProxyRef.value.hide(); QPopupProxyRef.value.hide();
}; };
const hasMana = computed(() => typeof mana.value === 'number');
defineExpose({ save }); defineExpose({ save });
</script> </script>
<template> <template>
<QPopupProxy ref="QPopupProxyRef" data-cy="ticketEditManaProxy"> <QPopupProxy
ref="QPopupProxyRef"
@before-show="getMana"
data-cy="ticketEditManaProxy"
>
<div class="container"> <div class="container">
<QSpinner v-if="!mana" color="primary" size="md" />
<div v-else>
<div class="header">Mana: {{ toCurrency(mana) }}</div> <div class="header">Mana: {{ toCurrency(mana) }}</div>
<div class="q-pa-md"> <QSpinner v-if="!hasMana" color="primary" size="md" />
<div class="q-pa-md" v-else>
<slot :popup="QPopupProxyRef" /> <slot :popup="QPopupProxyRef" />
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm"> <div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm">
<VnUsesMana :mana-code="manaCode" /> <VnUsesMana :mana-code="manaCode" />
@ -63,7 +72,7 @@ defineExpose({ save });
</span> </span>
</div> </div>
</div> </div>
</div>
<div class="row"> <div class="row">
<QBtn <QBtn
color="primary" color="primary"

View File

@ -44,7 +44,6 @@ const isTicketEditable = ref(false);
const sales = ref([]); const sales = ref([]);
const editableStatesOptions = ref([]); const editableStatesOptions = ref([]);
const selectedSales = ref([]); const selectedSales = ref([]);
const mana = ref(null);
const manaCode = ref('mana'); const manaCode = ref('mana');
const ticketState = computed(() => store.data?.ticketState?.state?.code); const ticketState = computed(() => store.data?.ticketState?.state?.code);
const transfer = ref({ const transfer = ref({
@ -258,18 +257,6 @@ const DEFAULT_EDIT = {
oldQuantity: null, oldQuantity: null,
}; };
const edit = ref({ ...DEFAULT_EDIT }); const edit = ref({ ...DEFAULT_EDIT });
const usesMana = ref(null);
const getUsesMana = async () => {
const { data } = await axios.get('Sales/usesMana');
usesMana.value = data;
};
const getMana = async () => {
const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`);
mana.value = data;
await getUsesMana();
};
const selectedValidSales = computed(() => { const selectedValidSales = computed(() => {
if (!sales.value) return; if (!sales.value) return;
@ -277,7 +264,6 @@ const selectedValidSales = computed(() => {
}); });
const onOpenEditPricePopover = async (sale) => { const onOpenEditPricePopover = async (sale) => {
await getMana();
edit.value = { edit.value = {
sale: JSON.parse(JSON.stringify(sale)), sale: JSON.parse(JSON.stringify(sale)),
price: sale.price, price: sale.price,
@ -285,7 +271,6 @@ const onOpenEditPricePopover = async (sale) => {
}; };
const onOpenEditDiscountPopover = async (sale) => { const onOpenEditDiscountPopover = async (sale) => {
await getMana();
if (isLocked.value) return; if (isLocked.value) return;
if (sale) { if (sale) {
edit.value = { edit.value = {
@ -306,7 +291,6 @@ const changePrice = async (sale) => {
await confirmUpdate(() => updatePrice(sale, newPrice)); await confirmUpdate(() => updatePrice(sale, newPrice));
} else updatePrice(sale, newPrice); } else updatePrice(sale, newPrice);
} }
await getMana();
}; };
const updatePrice = async (sale, newPrice) => { const updatePrice = async (sale, newPrice) => {
await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice }); await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice });
@ -599,9 +583,7 @@ watch(
:is-ticket-editable="isTicketEditable" :is-ticket-editable="isTicketEditable"
:sales="selectedValidSales" :sales="selectedValidSales"
:disable="!hasSelectedRows" :disable="!hasSelectedRows"
:mana="mana"
:ticket-config="ticketConfig" :ticket-config="ticketConfig"
@get-mana="getMana()"
@update-discounts="updateDiscounts" @update-discounts="updateDiscounts"
@refresh-table="resetChanges" @refresh-table="resetChanges"
/> />
@ -829,7 +811,6 @@ watch(
</QBtn> </QBtn>
<TicketEditManaProxy <TicketEditManaProxy
ref="editPriceProxyRef" ref="editPriceProxyRef"
:mana="mana"
:sale="row" :sale="row"
:new-price="getNewPrice" :new-price="getNewPrice"
@save="changePrice" @save="changePrice"
@ -852,10 +833,8 @@ watch(
<TicketEditManaProxy <TicketEditManaProxy
ref="editManaProxyRef" ref="editManaProxyRef"
:mana="mana"
:sale="row" :sale="row"
:new-price="getNewPrice" :new-price="getNewPrice"
:uses-mana="usesMana"
:mana-code="manaCode" :mana-code="manaCode"
@save="changeDiscount" @save="changeDiscount"
> >

View File

@ -34,10 +34,6 @@ const props = defineProps({
type: Array, type: Array,
default: () => [], default: () => [],
}, },
mana: {
type: Number,
default: null,
},
ticketConfig: { ticketConfig: {
type: Array, type: Array,
default: () => [], default: () => [],
@ -220,7 +216,6 @@ const createRefund = async (withWarehouse) => {
<TicketEditManaProxy <TicketEditManaProxy
ref="editManaProxyRef" ref="editManaProxyRef"
:sale="row" :sale="row"
:mana="props.mana"
@save="changeMultipleDiscount" @save="changeMultipleDiscount"
> >
<VnInput <VnInput

View File

@ -1,33 +1,36 @@
/// <reference types="cypress" />
describe('Item tag', () => { describe('Item tag', () => {
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080); cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit(`/#/item/1/tags`); cy.visit(`/#/item/1/tags`);
cy.get('.q-page').should('be.visible');
cy.waitForElement('[data-cy="itemTags"]');
}); });
const createNewTag = 'createNewTag';
const saveBtn = 'crudModelDefaultSaveBtn';
const newTag = 'tagundefined';
it('should throw an error adding an existent tag', () => { it('should throw an error adding an existent tag', () => {
cy.get('.q-page').should('be.visible'); cy.dataCy(createNewTag).click();
cy.get('.q-page-sticky > div').click(); cy.dataCy(newTag).should('be.visible').click().type('Genero{enter}');
cy.get('.q-page-sticky > div').click(); cy.dataCy('tagGeneroValue').eq(1).should('be.visible');
cy.dataCy('Tag_select').eq(7).type('Tallos'); cy.dataCy(saveBtn).click();
cy.get('.q-menu .q-item').contains('Tallos').click(); cy.get('.q-notification__message').should(
cy.get(':nth-child(8) > [label="Value"]').type('1'); 'have.text',
+cy.dataCy('crudModelDefaultSaveBtn').click(); "The tag or priority can't be repeated for an item",
cy.checkNotification("The tag or priority can't be repeated for an item"); );
}); });
it('should add a new tag', () => { it('should add a new tag', () => {
cy.get('.q-page').should('be.visible'); cy.dataCy(createNewTag).click();
cy.get('.q-page-sticky > div').click(); cy.dataCy(newTag).should('be.visible').click().type('Forma{enter}');
cy.get('.q-page-sticky > div').click(); cy.dataCy('tagFormaValue').should('be.visible').type('50');
cy.dataCy('Tag_select').eq(7).click(); cy.dataCy(saveBtn).click();
cy.get('.q-menu .q-item').contains('Ancho de la base').type('{enter}');
cy.get(':nth-child(8) > [label="Value"]').type('50');
cy.dataCy('crudModelDefaultSaveBtn').click();
cy.checkNotification('Data saved'); cy.checkNotification('Data saved');
cy.dataCy('itemTags').children(':nth-child(8)').find('.justify-center > .q-icon').click(); cy.dataCy('deleteTagForma').should('be.visible').click();
cy.dataCy('VnConfirm_confirm').click(); cy.dataCy('VnConfirm_confirm').should('be.visible').click();
cy.checkNotification('Data saved'); cy.checkNotification('Data saved');
}); });
}); });