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
{ orderId } of { shipped } to receive it without additional shipping costs.'
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
es: Spanish
fr: French
@ -203,6 +204,7 @@ es:
Te recomendamos amplíes para no generar costes extra, provocarán un incremento de tu tarifa.
¡Un saludo!'
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
es: Español
fr: Francés
@ -222,6 +224,7 @@ fr:
Montant minimum nécessaire de 50 euros pour recevoir la commande { orderId } livraison { landing }.
Merci.'
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
es: Espagnol
fr: Français
@ -240,7 +243,7 @@ pt:
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.'
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
fr: Francês
pt: Português

View File

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

View File

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

View File

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