Merge pull request 'HOTFIX: refactor canProceed' (!1449) from hotfix_ticketSale_other into master
gitea/salix-front/pipeline/head This commit looks good Details

Reviewed-on: #1449
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
Reviewed-by: Jon Elias <jon@verdnatura.es>
This commit is contained in:
Javier Segarra 2025-02-20 07:27:26 +00:00
commit f9c2d9e641
5 changed files with 279 additions and 155 deletions

View File

@ -48,6 +48,7 @@ globals:
rowRemoved: Row removed rowRemoved: Row removed
pleaseWait: Please wait... pleaseWait: Please wait...
noPinnedModules: You don't have any pinned modules noPinnedModules: You don't have any pinned modules
enterToConfirm: Press Enter to confirm
summary: summary:
basicData: Basic data basicData: Basic data
daysOnward: Days onward daysOnward: Days onward

View File

@ -48,6 +48,7 @@ globals:
rowRemoved: Fila eliminada rowRemoved: Fila eliminada
pleaseWait: Por favor espera... pleaseWait: Por favor espera...
noPinnedModules: No has fijado ningún módulo noPinnedModules: No has fijado ningún módulo
enterToConfirm: Pulsa Enter para confirmar
summary: summary:
basicData: Datos básicos basicData: Datos básicos
daysOnward: Días adelante daysOnward: Días adelante

View File

@ -46,7 +46,7 @@ defineExpose({ save });
</script> </script>
<template> <template>
<QPopupProxy ref="QPopupProxyRef"> <QPopupProxy ref="QPopupProxyRef" data-cy="ticketEditManaProxy">
<div class="container"> <div class="container">
<QSpinner v-if="!mana" color="primary" size="md" /> <QSpinner v-if="!mana" color="primary" size="md" />
<div v-else> <div v-else>

View File

@ -52,7 +52,6 @@ const transfer = ref({
sales: [], sales: [],
}); });
const tableRef = ref([]); const tableRef = ref([]);
const canProceed = ref();
watch( watch(
() => route.params.id, () => route.params.id,
@ -132,7 +131,6 @@ const columns = computed(() => [
align: 'left', align: 'left',
label: t('globals.amount'), label: t('globals.amount'),
name: 'amount', name: 'amount',
format: (row) => toCurrency(getSaleTotal(row)),
}, },
{ {
align: 'left', align: 'left',
@ -182,8 +180,6 @@ const resetChanges = async () => {
}; };
const rowToUpdate = ref(null); const rowToUpdate = ref(null);
const changeQuantity = async (sale) => { const changeQuantity = async (sale) => {
canProceed.value = await isSalePrepared(sale);
if (!canProceed.value) return;
if ( if (
!sale.itemFk || !sale.itemFk ||
sale.quantity == null || sale.quantity == null ||
@ -192,11 +188,21 @@ const changeQuantity = async (sale) => {
return; return;
if (!sale.id) return addSale(sale); if (!sale.id) return addSale(sale);
if (await isSalePrepared(sale)) {
await confirmUpdate(() => updateQuantity(sale));
} else await updateQuantity(sale);
};
const updateQuantity = async (sale) => {
try { try {
let { quantity, id } = sale;
if (!rowToUpdate.value) return; if (!rowToUpdate.value) return;
rowToUpdate.value = null; rowToUpdate.value = null;
sale.isNew = false; sale.isNew = false;
await updateQuantity(sale); const params = { quantity: quantity };
await axios.post(`Sales/${id}/updateQuantity`, params);
notify('globals.dataSaved', 'positive');
tableRef.value.reload();
} catch (e) { } catch (e) {
const { quantity } = tableRef.value.CrudModelRef.originalData.find( const { quantity } = tableRef.value.CrudModelRef.originalData.find(
(s) => s.id === sale.id, (s) => s.id === sale.id,
@ -206,12 +212,6 @@ const changeQuantity = async (sale) => {
} }
}; };
const updateQuantity = async ({ quantity, id }) => {
const params = { quantity: quantity };
await axios.post(`Sales/${id}/updateQuantity`, params);
notify('globals.dataSaved', 'positive');
};
const addSale = async (sale) => { const addSale = async (sale) => {
const params = { const params = {
barcode: sale.itemFk, barcode: sale.itemFk,
@ -236,13 +236,17 @@ const addSale = async (sale) => {
sale.isNew = false; sale.isNew = false;
arrayData.fetch({}); arrayData.fetch({});
}; };
const changeConcept = async (sale) => {
if (await isSalePrepared(sale)) {
await confirmUpdate(() => updateConcept(sale));
} else await updateConcept(sale);
};
const updateConcept = async (sale) => { const updateConcept = async (sale) => {
canProceed.value = await isSalePrepared(sale);
if (!canProceed.value) return;
const data = { newConcept: sale.concept }; const data = { newConcept: sale.concept };
await axios.post(`Sales/${sale.id}/updateConcept`, data); await axios.post(`Sales/${sale.id}/updateConcept`, data);
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
tableRef.value.reload();
}; };
const DEFAULT_EDIT = { const DEFAULT_EDIT = {
@ -294,33 +298,40 @@ const onOpenEditDiscountPopover = async (sale) => {
}; };
} }
}; };
const changePrice = async (sale) => {
const updatePrice = async (sale) => {
canProceed.value = await isSalePrepared(sale);
if (!canProceed.value) return;
const newPrice = edit.value.price; const newPrice = edit.value.price;
if (newPrice != null && newPrice != sale.price) { if (newPrice != null && newPrice != sale.price) {
if (await isSalePrepared(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 }); await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice });
sale.price = newPrice; sale.price = newPrice;
edit.value = { ...DEFAULT_EDIT }; edit.value = { ...DEFAULT_EDIT };
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
} tableRef.value.reload();
await getMana();
}; };
const changeDiscount = async (sale) => { const changeDiscount = async (sale) => {
const newDiscount = edit.value.discount; const newDiscount = edit.value.discount;
if (newDiscount != null && newDiscount != sale.discount) { if (newDiscount != null && newDiscount != sale.discount) {
if (isSalePrepared(sale)) if (await isSalePrepared(sale))
await confirmUpdate(() => updateDiscount([sale], newDiscount)); await confirmUpdate(() => updateDiscount([sale], newDiscount));
else await updateDiscount([sale], newDiscount);
} }
}; };
const updateDiscounts = async (sales, newDiscount = null) => { const updateDiscounts = async (sales, newDiscount = null) => {
const someSaleIsPrepared = sales.some(isSalePrepared); const salesTracking = await fetchSalesTracking();
if (someSaleIsPrepared);
await confirmUpdate(() => updateDiscount(sales, newDiscount)); const someSaleIsPrepared = salesTracking.some((sale) =>
matchSale(salesTracking, sale),
);
if (someSaleIsPrepared) await confirmUpdate(() => updateDiscount(sales, newDiscount));
else updateDiscount(sales, newDiscount);
}; };
const updateDiscount = async (sales, newDiscount = null) => { const updateDiscount = async (sales, newDiscount = null) => {
@ -426,9 +437,13 @@ onMounted(async () => {
const items = ref([]); const items = ref([]);
const newRow = ref({}); const newRow = ref({});
const changeItem = async (sale) => {
if (await isSalePrepared(sale)) {
await confirmUpdate(() => updateItem(sale));
} else await updateItem(sale);
};
const updateItem = async (row) => { const updateItem = async (row) => {
canProceed.value = await isSalePrepared(row);
if (!canProceed.value) return;
const selectedItem = items.value.find((item) => item.id === row.itemFk); const selectedItem = items.value.find((item) => item.id === row.itemFk);
if (selectedItem) { if (selectedItem) {
row.item = selectedItem; row.item = selectedItem;
@ -473,7 +488,7 @@ const endNewRow = (row) => {
}; };
async function confirmUpdate(cb) { async function confirmUpdate(cb) {
quasar await quasar
.dialog({ .dialog({
component: VnConfirm, component: VnConfirm,
componentProps: { componentProps: {
@ -483,8 +498,7 @@ async function confirmUpdate(cb) {
}) })
.onOk(cb); .onOk(cb);
} }
async function fetchSalesTracking() {
async function isSalePrepared(sale) {
const filter = { const filter = {
params: { params: {
where: { ticketFk: route.params.id }, where: { ticketFk: route.params.id },
@ -496,25 +510,37 @@ async function isSalePrepared(sale) {
filter: JSON.stringify(filter), filter: JSON.stringify(filter),
}, },
}); });
return data;
}
async function isSalePrepared(sale) {
const data = await fetchSalesTracking();
return matchSale(data, sale);
}
function matchSale(data, sale) {
const matchingSale = data.find(({ itemFk }) => itemFk === sale.itemFk); const matchingSale = data.find(({ itemFk }) => itemFk === sale.itemFk);
if (!matchingSale) { if (!matchingSale) {
return true; return false;
}
return (
matchingSale.hasSaleGroupDetail ||
matchingSale.isControled ||
matchingSale.isPrepared ||
matchingSale.isPrevious ||
matchingSale.isPreviousSelected
);
} }
return isPrepared(matchingSale);
}
function isPrepared(sale) {
const flagsToCheck = [
'hasSaleGroupDetail',
'isControled',
'isPrepared',
'isPrevious',
'isPreviousSelected',
];
return flagsToCheck.some((flag) => sale[flag] === 1);
}
watch( watch(
() => newRow.value.itemFk, () => newRow.value.itemFk,
(newItemFk) => { (newItemFk) => {
if (newItemFk) { if (newItemFk) {
updateItem(newRow.value); changeItem(newRow.value);
} }
}, },
); );
@ -751,7 +777,7 @@ watch(
option-value="id" option-value="id"
v-model="row.itemFk" v-model="row.itemFk"
:use-like="false" :use-like="false"
@update:model-value="updateItem(row)" @update:model-value="changeItem(row)"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -777,16 +803,21 @@ watch(
</div> </div>
<FetchedTags :item="row" :max-length="6" /> <FetchedTags :item="row" :max-length="6" />
<QPopupProxy v-if="row.id && isTicketEditable"> <QPopupProxy v-if="row.id && isTicketEditable">
<VnInput v-model="row.concept" @change="updateConcept(row)" /> <VnInput
v-model="row.concept"
@keyup.enter.stop="changeConcept(row)"
:hint="t('globals.enterToConfirm')"
/>
</QPopupProxy> </QPopupProxy>
</template> </template>
<template #column-quantity="{ row }"> <template #column-quantity="{ row }">
<VnInput <VnInput
data-cy="ticketSaleQuantityInput"
v-if="row.isNew || isTicketEditable" v-if="row.isNew || isTicketEditable"
type="number" type="number"
v-model.number="row.quantity" v-model.number="row.quantity"
@blur="changeQuantity(row)" @blur="changeQuantity(row)"
@keyup.enter="changeQuantity(row)" @keyup.enter.stop="changeQuantity(row)"
@update:model-value="() => (rowToUpdate = row)" @update:model-value="() => (rowToUpdate = row)"
@focus="edit.oldQuantity = row.quantity" @focus="edit.oldQuantity = row.quantity"
/> />
@ -800,10 +831,12 @@ watch(
<TicketEditManaProxy <TicketEditManaProxy
ref="editPriceProxyRef" ref="editPriceProxyRef"
:mana="mana" :mana="mana"
:sale="row"
:new-price="getNewPrice" :new-price="getNewPrice"
@save="updatePrice(row)" @save="changePrice"
> >
<VnInput <VnInput
@keyup.enter.stop="() => editManaProxyRef.save(row)"
v-model.number="edit.price" v-model.number="edit.price"
:label="t('basicData.price')" :label="t('basicData.price')"
type="number" type="number"
@ -838,6 +871,9 @@ watch(
</template> </template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span> <span v-else>{{ toPercentage(row.discount / 100) }}</span>
</template> </template>
<template #column-amount="{ row }">
{{ toCurrency(getSaleTotal(row)) }}
</template>
</VnTable> </VnTable>
<QPageSticky :offset="[20, 20]" style="z-index: 2"> <QPageSticky :offset="[20, 20]" style="z-index: 2">

View File

@ -1,6 +1,7 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
describe('TicketSale', () => { describe('TicketSale', () => {
describe('Free ticket #31', () => {
beforeEach(() => { beforeEach(() => {
cy.login('developer'); cy.login('developer');
cy.viewport(1920, 1080); cy.viewport(1920, 1080);
@ -120,3 +121,88 @@ describe('TicketSale', () => {
cy.url().should('match', /\/ticket\/31\/log/); cy.url().should('match', /\/ticket\/31\/log/);
}); });
}); });
describe('Ticket prepared #23', () => {
beforeEach(() => {
cy.login('developer');
cy.viewport(1920, 1080);
cy.visit('/#/ticket/23/sale');
});
const firstRow = 'tbody > :nth-child(1)';
const selectFirstRow = () => {
cy.waitForElement(firstRow);
cy.get(firstRow).find('.q-checkbox__inner').click();
};
it('update price', () => {
const price = Number((Math.random() * 99 + 1).toFixed(2));
cy.waitForElement(firstRow);
cy.get(':nth-child(10) > .q-btn').click();
cy.waitForElement('[data-cy="ticketEditManaProxy"]');
cy.dataCy('ticketEditManaProxy').should('exist');
cy.waitForElement('[data-cy="Price_input"]');
cy.dataCy('Price_input').clear();
cy.dataCy('Price_input').type(price);
cy.dataCy('saveManaBtn').click();
handleVnConfirm();
cy.get(':nth-child(10) > .q-btn > .q-btn__content').should(
'have.text',
`${price}`,
);
});
it('update dicount', () => {
const discount = Math.floor(Math.random() * 100) + 1;
selectFirstRow();
cy.get(':nth-child(11) > .q-btn').click();
cy.waitForElement('[data-cy="ticketEditManaProxy"]');
cy.dataCy('ticketEditManaProxy').should('exist');
cy.waitForElement('[data-cy="Disc_input"]');
cy.dataCy('Disc_input').clear();
cy.dataCy('Disc_input').type(discount);
cy.dataCy('saveManaBtn').click();
handleVnConfirm();
cy.get(':nth-child(11) > .q-btn > .q-btn__content').should(
'have.text',
`${discount}.00%`,
);
});
it('change concept', () => {
const quantity = Math.floor(Math.random() * 100) + 1;
cy.waitForElement(firstRow);
cy.get(':nth-child(8) > .row').click();
cy.get(
'.q-menu > [data-v-ca3f07a4=""] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="undefined_input"]',
)
.type(quantity)
.type('{enter}');
handleVnConfirm();
cy.get(':nth-child(8) >.row').should('contain.text', `${quantity}`);
});
it('changequantity ', () => {
const quantity = Math.floor(Math.random() * 100) + 1;
cy.waitForElement(firstRow);
cy.dataCy('ticketSaleQuantityInput').clear();
cy.dataCy('ticketSaleQuantityInput').type(quantity).trigger('tab');
cy.get('.q-page > :nth-child(6)').click();
handleVnConfirm();
cy.get('[data-cy="ticketSaleQuantityInput"]')
.find('[data-cy="undefined_input"]')
.should('have.value', `${quantity}`);
});
});
});
function handleVnConfirm() {
cy.get('[data-cy="VnConfirm_confirm"] > .q-btn__content > .block').click();
cy.waitForElement('.q-notification__message');
cy.get('.q-notification__message').should('be.visible');
cy.checkNotification('Data saved');
}