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) {
const rowIndex = selectedRows[0].$index;
const selectedIndexes = new Set(selected.value.map((row) => row.$index));
for (const row of rows) {
if (row.$index == rowIndex) break;
const minIndex = selectedIndexes.size
? 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)) {
selected.value.push(row);
selectedIndexes.add(row.$index);

View File

@ -27,30 +27,58 @@ describe('VnTable', () => {
beforeEach(() => (vm.selected = []));
describe('handleSelection()', () => {
const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }];
const selectedRows = [{ $index: 1 }];
it('should add rows to selected when shift key is pressed and rows are added except last one', () => {
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 }];
vm.handleSelection(
{ evt: { shiftKey: true }, added: true, rows: selectedRows },
rows
rows,
);
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', () => {
const selectedRows = [{ $index: 1 }];
vm.handleSelection(
{ evt: { shiftKey: false }, added: true, rows: selectedRows },
rows
rows,
);
expect(vm.selected).toEqual([]);
});
it('should not add rows to selected when rows are not added', () => {
const selectedRows = [{ $index: 1 }];
vm.handleSelection(
{ evt: { shiftKey: true }, added: false, rows: selectedRows },
rows
rows,
);
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>
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
@ -148,6 +147,7 @@ const exprBuilder = (param, value) => {
outlined
rounded
auto-load
sortBy="name ASC"
/></QItemSection>
</QItem>
<QItem class="q-mb-sm">

View File

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

View File

@ -77,24 +77,23 @@ onBeforeMount(() => {
function setPaymentType(accounting) {
if (!accounting) return;
accountingType.value = accounting.accountingType;
initialData.description = [];
initialData.payed = Date.vnNew();
isCash.value = accountingType.value.code == 'cash';
viewReceipt.value = isCash.value;
if (accountingType.value.daysInFuture)
initialData.payed.setDate(
initialData.payed.getDate() + accountingType.value.daysInFuture
initialData.payed.getDate() + accountingType.value.daysInFuture,
);
maxAmount.value = accountingType.value && accountingType.value.maxAmount;
if (accountingType.value.code == 'compensation')
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) => {

View File

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

View File

@ -1,32 +1,26 @@
<script setup>
import { ref } from 'vue';
import axios from 'axios';
import { useI18n } from 'vue-i18n';
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { toCurrency } from 'src/filters';
import VnUsesMana from 'components/ui/VnUsesMana.vue';
const $props = defineProps({
mana: {
type: Number,
default: null,
},
newPrice: {
type: Number,
default: 0,
},
usesMana: {
type: Boolean,
default: false,
},
manaCode: {
type: String,
default: 'mana',
},
sale: {
type: Object,
default: null,
},
});
const route = useRoute();
const mana = ref(null);
const usesMana = ref(false);
const emit = defineEmits(['save', 'cancel']);
const { t } = useI18n();
@ -38,32 +32,47 @@ const save = (sale = $props.sale) => {
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 = () => {
emit('cancel');
QPopupProxyRef.value.hide();
};
const hasMana = computed(() => typeof mana.value === 'number');
defineExpose({ save });
</script>
<template>
<QPopupProxy ref="QPopupProxyRef" data-cy="ticketEditManaProxy">
<QPopupProxy
ref="QPopupProxyRef"
@before-show="getMana"
data-cy="ticketEditManaProxy"
>
<div class="container">
<QSpinner v-if="!mana" color="primary" size="md" />
<div v-else>
<div class="header">Mana: {{ toCurrency(mana) }}</div>
<div class="q-pa-md">
<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">
{{ toCurrency($props.newPrice) }}
</span>
</div>
<div class="header">Mana: {{ toCurrency(mana) }}</div>
<QSpinner v-if="!hasMana" color="primary" size="md" />
<div class="q-pa-md" v-else>
<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">
{{ toCurrency($props.newPrice) }}
</span>
</div>
</div>
<div class="row">
<QBtn
color="primary"

View File

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

View File

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

View File

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