0
0
Fork 0

Btn dropdown more actions

This commit is contained in:
William Buezas 2024-06-14 15:04:02 -03:00
parent 8c3262bcdf
commit 0666aeb166
6 changed files with 322 additions and 21 deletions

View File

@ -184,6 +184,7 @@ en:
minAmount: 'A minimum amount of 50 (VAT excluded) is required for your order minAmount: 'A minimum amount of 50 (VAT excluded) is required for your order
{ orderId } of { shipped } to receive it without additional shipping costs.' { orderId } of { shipped } to receive it without additional shipping costs.'
orderChanges: 'Order {orderId} of { shipped }: { changes }' orderChanges: 'Order {orderId} of { shipped }: { changes }'
productNotAvailable: 'Verdnatura communicates: Your order {ticketFk} with reception date on {landed}. {notAvailables} not available. Sorry for the inconvenience.'
en: English en: English
es: Spanish es: Spanish
fr: French fr: French
@ -203,6 +204,7 @@ es:
Te recomendamos amplíes para no generar costes extra, provocarán un incremento de tu tarifa. Te recomendamos amplíes para no generar costes extra, provocarán un incremento de tu tarifa.
¡Un saludo!' ¡Un saludo!'
orderChanges: 'Pedido {orderId} con llegada estimada día { landing }: { changes }' orderChanges: 'Pedido {orderId} con llegada estimada día { landing }: { changes }'
productNotAvailable: 'Verdnatura le comunica: Pedido {ticketFk} con fecha de recepción {landed}. {notAvailables} no disponible/s. Disculpe las molestias.'
en: Inglés en: Inglés
es: Español es: Español
fr: Francés fr: Francés
@ -222,6 +224,7 @@ fr:
Montant minimum nécessaire de 50 euros pour recevoir la commande { orderId } livraison { landing }. Montant minimum nécessaire de 50 euros pour recevoir la commande { orderId } livraison { landing }.
Merci.' Merci.'
orderChanges: 'Commande {orderId} livraison {landing} indisponible/s. Désolés pour le dérangement.' orderChanges: 'Commande {orderId} livraison {landing} indisponible/s. Désolés pour le dérangement.'
productNotAvailable: 'Verdnatura communique : Votre commande {ticketFk} avec date de réception le {landed}. {notAvailables} non disponible. Nous sommes désolés pour les inconvénients.'
en: Anglais en: Anglais
es: Espagnol es: Espagnol
fr: Français fr: Français
@ -240,7 +243,7 @@ pt:
minAmount: 'É necessário um valor mínimo de 50 (sem IVA) em seu pedido minAmount: 'É necessário um valor mínimo de 50 (sem IVA) em seu pedido
{ orderId } do dia { landing } para recebê-lo sem custos de envio adicionais.' { orderId } do dia { landing } para recebê-lo sem custos de envio adicionais.'
orderChanges: 'Pedido { orderId } com chegada dia { landing }: { changes }' orderChanges: 'Pedido { orderId } com chegada dia { landing }: { changes }'
en: Inglês productNotAvailable: 'Verdnatura comunica: Seu pedido {ticketFk} com data de recepção em {landed}. {notAvailables} não disponível/eis. Desculpe pelo transtorno.'
es: Espanhol es: Espanhol
fr: Francês fr: Francês
pt: Português pt: Português

View File

@ -5,10 +5,6 @@ import { useI18n } from 'vue-i18n';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
const $props = defineProps({ const $props = defineProps({
id: {
type: String,
required: true,
},
mana: { mana: {
type: Number, type: Number,
default: null, default: null,
@ -38,16 +34,16 @@ const cancel = () => {
<template> <template>
<QPopupProxy ref="QPopupProxyRef"> <QPopupProxy ref="QPopupProxyRef">
<div class="container"> <div class="container">
<QSpinner v-if="!$props.mana" color="orange" size="md" /> <QSpinner v-if="!mana" color="orange" size="md" />
<div v-else> <div v-else>
<div class="header">Mana: {{ toCurrency($props.mana) }}</div> <div class="header">Mana: {{ toCurrency(mana) }}</div>
<div class="q-pa-md"> <div class="q-pa-md">
<slot /> <slot />
<div class="column items-center q-mt-lg"> <div v-if="newPrice" class="column items-center q-mt-lg">
<span class="text-primary">{{ t('New price') }}</span> <span class="text-primary">{{ t('New price') }}</span>
<span class="text-subtitle1">{{ <span class="text-subtitle1">
toCurrency($props.newPrice) {{ toCurrency($props.newPrice) }}
}}</span> </span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -12,6 +12,7 @@ import TicketEditManaProxy from './TicketEditMana.vue';
import ItemPicture from 'src/components/ui/ItemPicture.vue'; import ItemPicture from 'src/components/ui/ItemPicture.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import TicketSaleMoreActions from './TicketSaleMoreActions.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useSession } from 'composables/useSession'; import { useSession } from 'composables/useSession';
@ -232,10 +233,10 @@ const getMana = async () => {
await getUsesMana(); await getUsesMana();
}; };
const selectedValidSales = () => { const selectedValidSales = computed(() => {
if (!sales.value) return; if (!sales.value) return;
return selectedSales.value.filter((sale) => sale.id != undefined); return selectedSales.value.filter((sale) => sale.id != undefined);
}; });
const onOpenEditPricePopover = async (sale) => { const onOpenEditPricePopover = async (sale) => {
await getMana(); await getMana();
@ -256,7 +257,7 @@ const onOpenEditDiscountPopover = async (sale) => {
} else { } else {
edit.value = { edit.value = {
discount: null, discount: null,
sales: selectedValidSales(), sales: selectedValidSales.value,
}; };
} }
}; };
@ -281,16 +282,17 @@ const changeDiscount = (sale) => {
if (newDiscount != null && newDiscount != sale.discount) updateDiscount([sale]); if (newDiscount != null && newDiscount != sale.discount) updateDiscount([sale]);
}; };
const updateDiscount = async (sales) => { const updateDiscount = async (sales, newDiscount = null) => {
const saleIds = sales.map((sale) => sale.id); const saleIds = sales.map((sale) => sale.id);
const _newDiscount = newDiscount || edit.value.discount;
const params = { const params = {
salesIds: saleIds, salesIds: saleIds,
newDiscount: edit.value.discount, newDiscount: _newDiscount,
manaCode: manaCode.value, manaCode: manaCode.value,
}; };
await axios.post(`Tickets/${route.params.id}/updateDiscount`, params); await axios.post(`Tickets/${route.params.id}/updateDiscount`, params);
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
for (let sale of sales) sale.discount = edit.value.discount; for (let sale of sales) sale.discount = _newDiscount;
edit.value = { ...DEFAULT_EDIT }; edit.value = { ...DEFAULT_EDIT };
}; };
@ -357,8 +359,7 @@ const removeSelectedSales = () => {
const removeSales = async () => { const removeSales = async () => {
try { try {
const sales = selectedValidSales(); const params = { sales: selectedValidSales.value, ticketId: store.data.id };
const params = { sales: sales, ticketId: store.data.id };
await axios.post('Sales/deleteSales', params); await axios.post('Sales/deleteSales', params);
removeSelectedSales(); removeSelectedSales();
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
@ -421,6 +422,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
ref="stateBtnDropdownRef" ref="stateBtnDropdownRef"
color="primary" color="primary"
:label="t('ticketSale.state')" :label="t('ticketSale.state')"
:disable="!isTicketEditable"
> >
<VnSelect <VnSelect
:options="editableStatesOptions" :options="editableStatesOptions"
@ -432,6 +434,16 @@ onUnmounted(() => (stateStore.rightDrawer = false));
@update:model-value="changeTicketState" @update:model-value="changeTicketState"
/> />
</QBtnDropdown> </QBtnDropdown>
<TicketSaleMoreActions
:ticket="store.data"
:is-ticket-editable="isTicketEditable"
:sales="selectedValidSales"
:disable="!selectedSales.length"
:mana="mana"
:ticket-config="ticketConfig"
@get-mana="getMana()"
@update-discounts="updateDiscount"
/>
<QBtn <QBtn
color="primary" color="primary"
icon="delete" icon="delete"
@ -620,7 +632,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
:mana="mana" :mana="mana"
:new-price="getNewPrice" :new-price="getNewPrice"
@save="updatePrice(row)" @save="updatePrice(row)"
@cancel="updatePrice(row)"
> >
<VnInput <VnInput
v-model.number="edit.price" v-model.number="edit.price"
@ -647,7 +658,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
:mana="mana" :mana="mana"
:new-price="getNewPrice" :new-price="getNewPrice"
@save="changeDiscount(row)" @save="changeDiscount(row)"
@cancel="changeDiscount(row)"
> >
<VnInput <VnInput
v-model.number="edit.discount" v-model.number="edit.discount"
@ -709,4 +719,5 @@ es:
Continue anyway?: ¿Continuar de todas formas? Continue anyway?: ¿Continuar de todas formas?
You are going to delete lines of the ticket: Vas a eliminar lineas del ticket You are going to delete lines of the ticket: Vas a eliminar lineas del ticket
Add item: Añadir artículo Add item: Añadir artículo
Select lines to see the options: Selecciona líneas para ver las opciones
</i18n> </i18n>

View File

@ -0,0 +1,289 @@
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { useRouter } from 'vue-router';
import VnSmsDialog from 'components/common/VnSmsDialog.vue';
import TicketEditManaProxy from './TicketEditMana.vue';
import VnInput from 'src/components/common/VnInput.vue';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
import { toDateFormat } from 'src/filters/date';
import { useRole } from 'src/composables/useRole';
import { useVnConfirm } from 'composables/useVnConfirm';
const emit = defineEmits(['updateDiscounts', 'getMana']);
const props = defineProps({
disable: {
type: Boolean,
default: false,
},
isTicketEditable: {
type: Boolean,
default: false,
},
ticket: {
type: Object,
required: true,
default: () => {},
},
sales: {
type: Array,
default: () => [],
},
mana: {
type: Number,
default: null,
},
ticketConfig: {
type: Array,
default: () => [],
},
});
const router = useRouter();
const { t } = useI18n();
const { dialog } = useQuasar();
const { notify } = useNotify();
const role = useRole();
const btnDropdownRef = ref(null);
const { openConfirmationModal } = useVnConfirm();
const newDiscount = ref(null);
const ticket = computed(() => props.ticket);
const isClaimable = computed(() => {
if (ticket.value) {
const landedPlusWeek = new Date(ticket.value.landed);
landedPlusWeek.setDate(landedPlusWeek.getDate() + 7);
const hasClaimManagerRole = role.hasAny('claimManager');
return landedPlusWeek >= Date.vnNew() || hasClaimManagerRole;
}
return false;
});
const hasReserves = computed(() => props.sales.some((sale) => sale.reserved == true));
const sendSms = async (params) => {
await axios.post(`Tickets/${ticket.value.id}/sendSms`, params);
notify(t('SMS sent'), 'positive');
};
const showSmsDialog = (template) => {
const address = ticket.value.address;
const client = ticket.value.client;
const phone = address.mobile || address.phone || client.mobile || client.phone;
const items = props.sales.map((sale) => {
return `${sale.quantity} ${sale.concept}`;
});
const notAvailables = items.join(', ');
const data = {
ticketId: ticket.value.id,
destinationFk: ticket.value.clientFk,
destination: phone,
ticketFk: ticket.value.id,
created: ticket.value.updated,
landed: toDateFormat(ticket.value.landed),
notAvailables,
};
dialog({
component: VnSmsDialog,
componentProps: {
phone: phone,
template: template,
locale: client?.user?.lang ?? 'default_locale',
data: data,
promise: sendSms,
},
});
};
const calculateSalePrice = async () => {
if (!props.sales) return;
await axios.post(`Sales/recalculatePrice`, props.sales);
notify(t('globals.dataSaved'), 'positive');
};
const changeMultipleDiscount = () => {
const hasChanges = props.sales.some((sale) => {
return sale.discount != newDiscount.value;
});
if (newDiscount.value != null && hasChanges)
emit('updateDiscounts', props.sales, newDiscount.value);
btnDropdownRef.value.hide();
};
const createClaim = () => {
const today = new Date();
today.setHours(0, 0, 0, 0);
const timeDifference = today.getTime() - new Date(ticket.value.landed).getTime();
const pastDays = Math.floor(timeDifference / 86400000);
if (pastDays >= props.ticketConfig[0].daysForWarningClaim)
openConfirmationModal(
t('Claim out of time'),
t('Do you want to continue?'),
onCreateClaimAccepted
);
else
openConfirmationModal(t('Do you want to create a claim?'), onCreateClaimAccepted);
};
const onCreateClaimAccepted = async () => {
try {
const params = { ticketId: ticket.value.id, sales: props.sales };
const { data } = await axios.post(`Claims/createFromSales`, params);
router.push({ name: 'ClaimBasicData', params: { id: data.id } });
} catch (error) {
console.error('Error creating claim: ', error);
}
};
const setReserved = async (reserved) => {
const params = { ticketId: ticket.value.id, sales: props.sales, reserved: reserved };
await axios.post(`Sales/reserve`, params);
props.sales.forEach((sale) => {
sale.reserved = reserved;
});
};
const createRefund = async (withWarehouse) => {
if (!props.sales) return;
const salesIds = props.sales.map((sale) => sale.id);
const params = { salesIds: salesIds, withWarehouse: withWarehouse, negative: true };
const { data } = await axios.post('Sales/clone', params);
const [refundTicket] = data;
notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive');
router.push({ name: 'TicketSale', params: { id: refundTicket.id } });
};
</script>
<template>
<QBtnDropdown
ref="btnDropdownRef"
color="primary"
:label="t('ticketSale.more')"
:disable="disable"
>
<template #label>
<QTooltip>{{ t('Select lines to see the options') }}</QTooltip>
</template>
<QList>
<QItem
v-if="ticket"
clickable
v-close-popup
v-ripple
@click="showSmsDialog('productNotAvailable')"
>
<QItemSection>
<QItemLabel>{{ t('Send shortage SMS') }}</QItemLabel>
</QItemSection>
</QItem>
<QItem
v-if="isTicketEditable"
clickable
v-close-popup
v-ripple
@click="calculateSalePrice()"
>
<QItemSection>
<QItemLabel>{{ t('Recalculate price') }}</QItemLabel>
</QItemSection>
</QItem>
<QItem clickable v-ripple @click="emit('getMana')">
<QItemSection>
<QItemLabel>{{ t('Update discount') }}</QItemLabel>
</QItemSection>
<TicketEditManaProxy :mana="props.mana" @save="changeMultipleDiscount()">
<VnInput
v-model.number="newDiscount"
:label="t('ticketSale.discount')"
type="number"
/>
</TicketEditManaProxy>
</QItem>
<QItem
v-if="isClaimable"
clickable
v-close-popup
v-ripple
@click="createClaim()"
>
<QItemSection>
<QItemLabel>{{ t('Add claim') }}</QItemLabel>
</QItemSection>
</QItem>
<QItem
v-if="isTicketEditable"
clickable
v-close-popup
v-ripple
@click="setReserved(true)"
>
<QItemSection>
<QItemLabel>{{ t('Mark as reserved') }}</QItemLabel>
</QItemSection>
</QItem>
<QItem
v-if="isTicketEditable && hasReserves"
clickable
v-close-popup
v-ripple
@click="setReserved(false)"
>
<QItemSection>
<QItemLabel>{{ t('Unmark as reserved') }}</QItemLabel>
</QItemSection>
</QItem>
<QItem clickable v-ripple>
<QItemSection>
<QItemLabel>{{ t('Refund...') }}</QItemLabel>
</QItemSection>
<QItemSection side>
<QIcon name="keyboard_arrow_right" />
</QItemSection>
<QMenu anchor="top end" self="top start" auto-close bordered>
<QList>
<QItem v-ripple clickable @click="createRefund(true)">
<QItemSection>
{{ t('with warehouse') }}
</QItemSection>
</QItem>
<QItem v-ripple clickable @click="createRefund(false)">
<QItemSection>
{{ t('without warehouse') }}
</QItemSection>
</QItem>
</QList>
</QMenu>
</QItem>
</QList>
</QBtnDropdown>
</template>
<i18n>
en:
refundTicketCreated: 'The following refund ticket have been created {ticketId}'
es:
SMS sent: SMS enviado
Send shortage SMS: Enviar SMS faltas
Recalculate price: Recalcular precio
Update discount: Actualizar descuento
Add claim: Crear reclamación
Mark as reserved: Marcar como reservado
Unmark as reserved: Desmarcar como reservado
Refund...: Abono...
with warehouse: con almacén
without warehouse: sin almacén
Claim out of time: Reclamación fuera de plazo
Do you want to continue?: ¿Desea continuar?
Do you want to create a claim?: ¿Quieres crear una reclamación?
refundTicketCreated: 'The following refund ticket have been created: {ticketId}'
</i18n>

View File

@ -18,3 +18,4 @@ ticketSale:
hasComponentLack: Component lack hasComponentLack: Component lack
ok: Ok ok: Ok
state: State state: State
more: More

View File

@ -20,3 +20,4 @@ ticketSale:
hasComponentLack: Faltan componentes hasComponentLack: Faltan componentes
ok: Ok ok: Ok
state: Estado state: Estado
more: Más