8179-testToMaster #909

Merged
alexm merged 448 commits from 8179-testToMaster into master 2024-11-12 07:01:12 +00:00
15 changed files with 730 additions and 346 deletions
Showing only changes of commit 4a57c53c3d - Show all commits

View File

@ -49,6 +49,10 @@ const $props = defineProps({
type: Object, type: Object,
default: null, default: null,
}, },
bottom: {
type: Object,
default: null,
},
cardClass: { cardClass: {
type: String, type: String,
default: 'flex-one', default: 'flex-one',
@ -388,6 +392,11 @@ defineExpose({
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
/> />
</template> </template>
<template #top>
<div class="full-width-slot">
<slot name="before-header" />
</div>
</template>
<template #header-cell="{ col }"> <template #header-cell="{ col }">
<QTh v-if="col.visible ?? true"> <QTh v-if="col.visible ?? true">
<div <div
@ -482,6 +491,9 @@ defineExpose({
/> />
</QTd> </QTd>
</template> </template>
<template #bottom v-if="bottom">
<slot name="bottom-table" />
</template>
<template #item="{ row, colsMap }"> <template #item="{ row, colsMap }">
<component <component
:is="$props.redirect ? 'router-link' : 'span'" :is="$props.redirect ? 'router-link' : 'span'"
@ -780,4 +792,13 @@ es:
cursor: text; cursor: text;
user-select: all; user-select: all;
} }
.full-width-slot {
width: 100%;
display: flex;
text-align: center;
color: var(--vn-text-color);
margin-bottom: -1%;
background-color: var(--vn-header-color);
}
</style> </style>

View File

@ -28,7 +28,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
delete params.filter; delete params.filter;
store.userParams = { ...params, ...store.userParams }; store.userParams = { ...params, ...store.userParams };
store.userFilter = { ...filter, ...store.userFilter }; store.userFilter = { ...filter, ...store.userFilter };
if (filter.order) store.order = filter.order; if (filter?.order) store.order = filter.order;
} }
}); });

View File

@ -578,6 +578,8 @@ ticket:
warehouse: Warehouse warehouse: Warehouse
customerCard: Customer card customerCard: Customer card
alias: Alias alias: Alias
ticketList: Ticket List
newOrder: New Order
boxing: boxing:
expedition: Expedition expedition: Expedition
item: Item item: Item
@ -628,6 +630,11 @@ ticket:
weight: Weight weight: Weight
goTo: Go to goTo: Go to
summaryAmount: Summary summaryAmount: Summary
purchaseRequest: Purchase request
service: Service
description: Description
attender: Attender
ok: Ok
create: create:
client: Client client: Client
address: Address address: Address
@ -651,7 +658,6 @@ invoiceOut:
client: Client client: Client
company: Company company: Company
customerCard: Customer card customerCard: Customer card
ticketList: Ticket List
summary: summary:
issued: Issued issued: Issued
created: Created created: Created

View File

@ -584,6 +584,8 @@ ticket:
warehouse: Almacén warehouse: Almacén
customerCard: Ficha del cliente customerCard: Ficha del cliente
alias: Alias alias: Alias
ticketList: Listado de tickets
newOrder: Nuevo pedido
boxing: boxing:
expedition: Expedición expedition: Expedición
item: Artículo item: Artículo
@ -634,6 +636,10 @@ ticket:
weight: Peso weight: Peso
goTo: Ir a goTo: Ir a
summaryAmount: Resumen summaryAmount: Resumen
purchaseRequest: Petición de compra
service: Servicio
description: Descripción
attender: Comprador
create: create:
client: Cliente client: Cliente
address: Dirección address: Dirección

View File

@ -87,6 +87,11 @@ const filter = {
}; };
const data = ref(useCardDescription()); const data = ref(useCardDescription());
function ticketFilter(ticket) {
console.log('ticket: ', ticket);
return JSON.stringify({ clientFk: ticket.clientFk });
}
</script> </script>
<template> <template>
@ -159,6 +164,22 @@ const data = ref(useCardDescription());
> >
<QTooltip>{{ t('ticket.card.customerCard') }}</QTooltip> <QTooltip>{{ t('ticket.card.customerCard') }}</QTooltip>
</QBtn> </QBtn>
<QBtn
size="md"
icon="vn:ticket"
color="primary"
:to="{ name: 'TicketList', query: { table: ticketFilter(entity) } }"
>
<QTooltip>{{ t('ticket.card.ticketList') }}</QTooltip>
</QBtn>
<QBtn
size="md"
icon="vn:basketadd"
color="primary"
:to="{ name: 'OrderList' }"
>
<QTooltip>{{ t('ticket.card.newOrder') }}</QTooltip>
</QBtn>
</QCardActions> </QCardActions>
</template> </template>
</CardDescriptor> </CardDescriptor>

View File

@ -24,6 +24,7 @@ const { openReport, sendEmail } = usePrintService();
const ticket = ref(props.ticket); const ticket = ref(props.ticket);
const ticketId = currentRoute.value.params.id; const ticketId = currentRoute.value.params.id;
const showWeightDialog = ref(false);
const actions = { const actions = {
clone: async () => { clone: async () => {
const opts = { message: t('Ticket cloned'), type: 'positive' }; const opts = { message: t('Ticket cloned'), type: 'positive' };
@ -153,30 +154,66 @@ function openConfirmDialog(callback) {
}, },
}); });
} }
function createPdfDialog() {
dialog({
component: VnConfirm,
componentProps: {
promise: createPdfInvoice,
},
});
}
async function createPdfInvoice() {
const { data } = await axios.get('invoiceOuts', {
params: {
filter: JSON.stringify({
where: { ref: ticket.value.refFk },
}),
},
});
const invoiceId = data[0].id;
const { response } = await axios.post(`InvoiceOuts/${invoiceId}/createPdf`);
if (!response) {
notify({
message: 'The invoice PDF document has been regenerated',
type: 'positive',
});
}
}
function setWeightToTicket(weight) {
console.log('weight: ', weight);
const { data } = axios.patch(`Tickets/${ticketId}`, {
weight: weight,
});
console.log('data: ', data);
if (data) window.location.reload();
}
</script> </script>
<template> <template>
<QItem v-ripple clickable> <QItem v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
<QIcon name="picture_as_pdf" /> <QIcon name="picture_as_pdf" />
</QItemSection> </QItemSection>
<QItemSection>{{ t('Open Delivery Note...') }}</QItemSection> <QItemSection>{{ t('Show Delivery Note...') }}</QItemSection>
<QItemSection side> <QItemSection side>
<QIcon name="keyboard_arrow_right" /> <QIcon name="keyboard_arrow_right" />
</QItemSection> </QItemSection>
<QMenu anchor="top end" self="top start" auto-close bordered> <QMenu anchor="top end" self="top start" auto-close bordered>
<QList> <QList>
<QItem @click="openDeliveryNote('deliveryNote')" v-ripple clickable> <QItem @click="openDeliveryNote('deliveryNote')" v-ripple clickable>
<QItemSection>{{ t('With prices') }}</QItemSection> <QItemSection>{{ t('as PDF') }}</QItemSection>
</QItem> </QItem>
<QItem @click="openDeliveryNote('withoutPrices')" v-ripple clickable> <QItem @click="openDeliveryNote('withoutPrices')" v-ripple clickable>
<QItemSection>{{ t('Without prices') }}</QItemSection> <QItemSection>{{ t('as PDF without prices') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="openDeliveryNote('deliveryNote', 'csv')" @click="openDeliveryNote('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('As CSV') }}</QItemSection> <QItemSection>{{ t('as CSV') }}</QItemSection>
</QItem> </QItem>
</QList> </QList>
</QMenu> </QMenu>
@ -196,21 +233,21 @@ function openConfirmDialog(callback) {
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('With prices') }}</QItemSection> <QItemSection>{{ t('Send PDF') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="sendDeliveryNoteConfirmation('withoutPrices')" @click="sendDeliveryNoteConfirmation('withoutPrices')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('Without prices') }}</QItemSection> <QItemSection>{{ t('Send PDF to tablet') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')" @click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('As CSV') }}</QItemSection> <QItemSection>{{ t('Send CSV') }}</QItemSection>
</QItem> </QItem>
</QList> </QList>
</QMenu> </QMenu>
@ -219,7 +256,7 @@ function openConfirmDialog(callback) {
<QItemSection avatar> <QItemSection avatar>
<QIcon name="receipt" /> <QIcon name="receipt" />
</QItemSection> </QItemSection>
<QItemSection>{{ t('Open Proforma Invoice') }}</QItemSection> <QItemSection>{{ t('Show Proforma') }}</QItemSection>
</QItem> </QItem>
<QItem v-ripple clickable> <QItem v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
@ -235,24 +272,54 @@ function openConfirmDialog(callback) {
<QItemSection>{{ t('Pending payment') }}</QItemSection> <QItemSection>{{ t('Pending payment') }}</QItemSection>
</QItem> </QItem>
<QItem @click="showSmsDialog('minAmount')" v-ripple clickable> <QItem @click="showSmsDialog('minAmount')" v-ripple clickable>
<QItemSection>{{ t('Minimum amount') }}</QItemSection> <QItemSection>{{ t('Minimum import') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="showSmsDialogWithChanges('orderChanges')" @click="showSmsDialogWithChanges('orderChanges')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('Order changes') }}</QItemSection> <QItemSection>{{ t('Notify changes') }}</QItemSection>
</QItem> </QItem>
</QList> </QList>
</QMenu> </QMenu>
</QItem> </QItem>
<QItem @click="createPdfDialog()" v-ripple clickable>
<QItemSection avatar>
<QIcon name="picture_as_pdf" />
</QItemSection>
<QItemSection>{{ t('Regenerate PDF invoice') }}</QItemSection>
</QItem>
<QItem @click="openConfirmDialog('clone')" v-ripple clickable> <QItem @click="openConfirmDialog('clone')" v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
<QIcon name="content_copy" /> <QIcon name="content_copy" />
</QItemSection> </QItemSection>
<QItemSection>{{ t('To clone ticket') }}</QItemSection> <QItemSection>{{ t('To clone ticket') }}</QItemSection>
</QItem> </QItem>
<QItem @click="showWeightDialog = !showWeightDialog" v-ripple clickable>
<QItemSection avatar>
<QIcon name="content_paste" />
</QItemSection>
<QItemSection>{{ t('Set ticket weight') }}</QItemSection>
</QItem>
<QDialog v-model="showWeightDialog" transition-show="scale" transition-hide="scale">
<QCard>
<QCardSection>
<QInput
v-model="weight"
type="number"
:label="t('Weight')"
@update:model-value="setWeightToTicket"
/>
<QBtn flat v-close-popup class="text-primary">
{{ t('Close') }}
</QBtn>
<QBTn flat @click="setWeightToTicket" class="text-primary">
{{ t('Save') }}
</QBTn>
</QCardSection>
</QCard>
</QDialog>
<template v-if="!ticket.isDeleted"> <template v-if="!ticket.isDeleted">
<QSeparator /> <QSeparator />
<QItem @click="openConfirmDialog('remove')" v-ripple clickable> <QItem @click="openConfirmDialog('remove')" v-ripple clickable>
@ -266,20 +333,26 @@ function openConfirmDialog(callback) {
<i18n> <i18n>
es: es:
Open Delivery Note...: Abrir albarán... Show Delivery Note...: Ver albarán...
Send Delivery Note...: Enviar albarán... Send Delivery Note...: Enviar albarán...
With prices: Con precios as PDF: como PDF
Without prices: Sin precios as PDF without prices: como PDF sin precios
As CSV: Como CSV as CSV: Como CSV
Open Proforma Invoice: Abrir factura proforma Send PDF: Enviar PDF
Send PDF to tablet: Enviar PDF a tablet
Send CSV: Enviar CSV
Show Proforma: Ver proforma
Delete ticket: Eliminar ticket Delete ticket: Eliminar ticket
Send SMS...: Enviar SMS Send SMS...: Enviar SMS
Pending payment: Pago pendiente Pending payment: Pago pendiente
Minimum amount: Importe mínimo Minimum import: Importe mínimo
Order changes: Cambios del pedido Notify changes: Notificar cambios
Ticket deleted: Ticket eliminado Ticket deleted: Ticket eliminado
You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora
To clone ticket: Clonar ticket To clone ticket: Clonar ticket
Ticket cloned: Ticked clonado Ticket cloned: Ticked clonado
It was not able to clone the ticket: No se pudo clonar el ticket It was not able to clone the ticket: No se pudo clonar el ticket
Regenerate PDF invoice: Regenerar PDF factura
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
Set ticket weight: Establecer peso al ticket
</i18n> </i18n>

View File

@ -22,6 +22,7 @@ import { useVnConfirm } from 'composables/useVnConfirm';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import axios from 'axios'; import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import FormModelPopup from 'src/components/FormModelPopup.vue';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -50,6 +51,9 @@ const transfer = ref({
lastActiveTickets: [], lastActiveTickets: [],
sales: [], sales: [],
}); });
const tableRef = ref([]);
const formModelPopupRef = ref();
const showForm = ref(false);
watch( watch(
() => route.params.id, () => route.params.id,
@ -395,7 +399,7 @@ const changeTicketState = async (val) => {
}; };
const removeSelectedSales = () => { const removeSelectedSales = () => {
selectedSales.value.forEach((sale) => { selectedRows.value.forEach((sale) => {
const index = sales.value.indexOf(sale); const index = sales.value.indexOf(sale);
sales.value.splice(index, 1); sales.value.splice(index, 1);
}); });
@ -403,17 +407,16 @@ const removeSelectedSales = () => {
const removeSales = async () => { const removeSales = async () => {
try { try {
const params = { sales: selectedValidSales.value, ticketId: store.data.id }; const params = { sales: selectedRows.value, 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');
window.location.reload();
} catch (err) { } catch (err) {
console.error('Error deleting sales', err); console.error('Error deleting sales', err);
} }
}; };
const insertRow = () => sales.value.push({ ...DEFAULT_EDIT });
const setTransferParams = async () => { const setTransferParams = async () => {
try { try {
const checkedSales = JSON.parse(JSON.stringify(selectedSales.value)); const checkedSales = JSON.parse(JSON.stringify(selectedSales.value));
@ -442,6 +445,15 @@ onMounted(async () => {
}); });
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
async function save(item) {
const { data } = await axios.post(`Tickets/${route.params.id}/addSale`, {
id: route.params.id,
barcode: item.itemFk,
quantity: parseInt(item.quantity),
});
if (data) window.location.reload();
}
</script> </script>
<template> <template>
@ -455,12 +467,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
auto-load auto-load
@on-fetch="(data) => (isLocked = data)" @on-fetch="(data) => (isLocked = data)"
/> />
<FetchData
url="Items/withName"
:filter="{ fields: ['id', 'name'], order: 'id DESC' }"
auto-load
@on-fetch="(data) => (itemsWithNameOptions = data)"
/>
<FetchData <FetchData
url="States/editableStates" url="States/editableStates"
:filter="{ fields: ['code', 'name', 'id', 'alertLevel'], order: 'name ASC' }" :filter="{ fields: ['code', 'name', 'id', 'alertLevel'], order: 'name ASC' }"
@ -560,13 +566,13 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</QCardSection> </QCardSection>
</div></QDrawer </div></QDrawer
> >
<!-- TODO: el botón de añadir filas como está en salix-->
<VnTable <VnTable
ref="tableRef" ref="tableRef"
data-key="TicketSales" data-key="TicketSales"
:url="`Tickets/${route.params.id}/getSales`" :url="`Tickets/${route.params.id}/getSales`"
:columns="columns" :columns="columns"
v-model:selected="selectedRows" v-model:selected="selectedRows"
:bottom="true"
:table="{ :table="{
'row-key': 'id', 'row-key': 'id',
selection: 'multiple', selection: 'multiple',
@ -625,7 +631,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</div> </div>
</template> </template>
<template #column-visible="{ row }"> <template #column-visible="{ row }">
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense> <QBadge :color="row.visible < 0 ? 'alert' : 'transparent'" dense>
{{ row.visible }} {{ row.visible }}
</QBadge> </QBadge>
</template> </template>
@ -667,7 +673,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<template #column-price="{ row }"> <template #column-price="{ row }">
<QTd> <QTd>
<template v-if="isTicketEditable && row.id"> <template v-if="isTicketEditable && row.id">
<QBtn flat color="primary" dense @click="onOpenEditPricePopover(row)"> <QBtn flat class="link" dense @click="onOpenEditPricePopover(row)">
{{ toCurrency(row.price) }} {{ toCurrency(row.price) }}
</QBtn> </QBtn>
<TicketEditManaProxy <TicketEditManaProxy
@ -689,12 +695,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<template #column-discount="{ row }"> <template #column-discount="{ row }">
<QTd> <QTd>
<template v-if="!isLocked && row.id"> <template v-if="!isLocked && row.id">
<QBtn <QBtn flat class="link" dense @click="onOpenEditDiscountPopover(row)">
flat
color="primary"
dense
@click="onOpenEditDiscountPopover(row)"
>
{{ toPercentage(row.discount / 100) }} {{ toPercentage(row.discount / 100) }}
</QBtn> </QBtn>
<TicketEditManaProxy <TicketEditManaProxy
@ -715,21 +716,54 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<template #column-amount="{ row }"> <template #column-amount="{ row }">
{{ toCurrency(row.quantity * row.price) }} {{ toCurrency(row.quantity * row.price) }}
</template> </template>
<template #bottom-row> <template #bottom-table v-if="isTicketEditable">
<QBtn <QIcon
class="cursor-pointer fill-icon q-ml-md q-my-lg" @click="showForm = !showForm"
class="fill-icon-on-hover"
color="primary" color="primary"
icon="add_circle" name="add_circle"
size="md" size="sm"
round
flat
:disable="!isTicketEditable"
@click="insertRow()"
> >
<QTooltip> <QTooltip>
{{ t('Add item') }} {{ t('Add item') }}
</QTooltip> </QTooltip>
</QBtn> </QIcon>
<QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
<FormModelPopup
ref="formModelPopupRef"
:url-create="`Tickets/${route.params.id}/addSale`"
:title="t('New item')"
:form-initial-data="{
price: 0,
discount: 0,
visible: 0,
available: 0,
packaging: null,
}"
:save-fn="save"
>
<template #form-inputs="{ data }">
<VnSelect
url="Items/withName"
:option-filter="{
order: 'id DESC',
}"
:fields="['id', 'name']"
:label="t('Items')"
option-label="name"
option-value="id"
:options="itemsWithNameOptions"
hide-selected
v-model="data.itemFk"
/>
<VnInput
v-model="data.quantity"
:label="t('Quantity')"
type="number"
/>
</template>
</FormModelPopup>
</QDialog>
</template> </template>
</VnTable> </VnTable>

View File

@ -50,7 +50,7 @@ const createRefund = async () => {
if (!selected.value.length) return; if (!selected.value.length) return;
const params = { const params = {
servicesIds: selected.value.map((s) => +s.ticketFk), servicesIds: selected.value.map((s) => +s.id),
withWarehouse: false, withWarehouse: false,
negative: true, negative: true,
}; };

View File

@ -425,7 +425,7 @@ async function changeState(value) {
<VnTitle :url="ticketUrl + 'package'" :text="t('globals.packages')" /> <VnTitle :url="ticketUrl + 'package'" :text="t('globals.packages')" />
<QTable :rows="ticket.packagings" flat> <QTable :rows="ticket.packagings" flat>
<template #header="props"> <template #header="props">
<QTr :props="props"> <QTr class="tr-header" :props="props">
<QTh auto-width>{{ t('ticket.summary.created') }}</QTh> <QTh auto-width>{{ t('ticket.summary.created') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.package') }}</QTh> <QTh auto-width>{{ t('ticket.summary.package') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh> <QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
@ -445,7 +445,7 @@ async function changeState(value) {
/> />
<QTable :rows="ticket.services" flat> <QTable :rows="ticket.services" flat>
<template #header="props"> <template #header="props">
<QTr :props="props"> <QTr class="tr-header" :props="props">
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh> <QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
<QTh auto-width>{{ t('globals.description') }}</QTh> <QTh auto-width>{{ t('globals.description') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh> <QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
@ -466,6 +466,49 @@ async function changeState(value) {
</template> </template>
</QTable> </QTable>
</QCard> </QCard>
<QCard class="vn-max" v-if="ticket.requests.length > 0">
<VnTitle
:url="ticketUrl + 'request'"
:text="t('ticket.summary.purchaseRequest')"
/>
<QTable :rows="ticket.requests" flat>
<template #header="props">
<QTr class="tr-header" :props="props">
<QTh auto-width>{{ t('ticket.summary.description') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.created') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.requester') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.attender') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.item') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.ok') }}</QTh>
</QTr>
</template>
<template #body="props">
<QTr :props="props">
<QTd>{{ toDate(props.row.created) }}</QTd>
<QTd>{{ props.row.description }}</QTd>
<QTd>{{ props.row.requester?.user?.username }}</QTd>
<QTd>{{ props.row.atender?.user?.username }}</QTd>
<QTd>{{ props.row.quantity }}</QTd>
<QTd>{{ toCurrency(props.row.price) }}</QTd>
<QTd>
<span class="link" v-if="props.row.isOk">
{{ props.row.itemFk }}
<ItemDescriptorProxy :id="props.row.itemFk" />
</span>
</QTd>
<QTd>
<QCheckbox
v-model="props.row.isOk"
disable
:toggle-indeterminate="false"
/>
</QTd>
</QTr>
</template>
</QTable>
</QCard>
</template> </template>
</CardSummary> </CardSummary>
</template> </template>

View File

@ -94,13 +94,11 @@ const openCreateModal = () => createTrackingDialogRef.value.show();
:no-data-label="t('globals.noResults')" :no-data-label="t('globals.noResults')"
> >
<template #body-cell-worker="{ row }"> <template #body-cell-worker="{ row }">
<QTd @click.stop> <QTd>
<span class="link"> <QBtn flat class="link" @click.stop>
<QBtn flat>
{{ row.user?.name }} {{ row.user?.name }}
<WorkerDescriptorProxy :id="row.user?.worker?.id" /> <WorkerDescriptorProxy :id="row.user?.worker?.id" />
</QBtn> </QBtn>
</span>
</QTd> </QTd>
</template> </template>
</QTable> </QTable>

View File

@ -134,12 +134,10 @@ onUnmounted(() => (stateStore.rightDrawer = false));
> >
<template #body-cell-item="{ row }"> <template #body-cell-item="{ row }">
<QTd> <QTd>
<span class="link"> <QBtn flat class="link" @click.stop>
<QBtn flat>
{{ row.itemFk }} {{ row.itemFk }}
<ItemDescriptorProxy :id="row.itemFk" /> <ItemDescriptorProxy :id="row.itemFk" />
</QBtn> </QBtn>
</span>
</QTd> </QTd>
</template> </template>
<template #body-cell-description="{ row }"> <template #body-cell-description="{ row }">

View File

@ -3,8 +3,8 @@ import { onMounted, ref, computed, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue'; // import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; // import VnSelect from 'src/components/common/VnSelect.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
@ -19,13 +19,14 @@ import useNotify from 'src/composables/useNotify.js';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import { toDateFormat } from 'src/filters/date.js'; import { toDateFormat } from 'src/filters/date.js';
import axios from 'axios'; import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue';
const state = useState(); const state = useState();
const { t } = useI18n(); const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm(); const { openConfirmationModal } = useVnConfirm();
const { notify } = useNotify(); const { notify } = useNotify();
const user = state.getUser(); const user = state.getUser();
const tableRef = ref();
const itemPackingTypesOptions = ref([]); const itemPackingTypesOptions = ref([]);
const zonesOptions = ref([]); const zonesOptions = ref([]);
const selectedTickets = ref([]); const selectedTickets = ref([]);
@ -44,15 +45,14 @@ const exprBuilder = (param, value) => {
case 'notMovableLines': case 'notMovableLines':
case 'futureZoneFk': case 'futureZoneFk':
return { [param]: value }; return { [param]: value };
case 'iptColFilter': case 'ipt':
return { ipt: { like: `%${value}%` } }; return { [param]: { like: `%${value}%` } };
case 'futureIptColFilter': case 'futureIpt':
return { futureIpt: { like: `%${value}%` } }; return { [param]: { like: `%${value}%` } };
} }
}; };
const userParams = reactive({}); const userParams = reactive({});
const arrayData = useArrayData('AdvanceTickets', { const arrayData = useArrayData('AdvanceTickets', {
url: 'Tickets/getTicketsAdvance', url: 'Tickets/getTicketsAdvance',
userParams: userParams, userParams: userParams,
@ -82,220 +82,123 @@ const getInputEvents = (col) => {
}; };
}; };
const ticketColumns = computed(() => [ const columns = computed(() => [
{ {
label: '', label: '',
name: 'icons', name: 'icons',
align: 'left', hidden: true,
columnFilter: null,
}, },
{ {
label: t('advanceTickets.ticketId'),
name: 'ticketId',
align: 'center', align: 'center',
sortable: true, label: t('advanceTickets.ticketId'),
columnFilter: { name: 'id',
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'id',
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
align: 'left',
label: t('advanceTickets.ipt'), label: t('advanceTickets.ipt'),
name: 'ipt', name: 'ipt',
field: 'ipt',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
filterParamKey: 'iptColFilter',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: { attrs: {
options: itemPackingTypesOptions.value, url: 'itemPackingTypes',
'option-value': 'code', fields: ['code', 'description'],
'option-label': 'description', where: { isActive: true },
dense: true, optionValue: 'code',
optionLabel: 'description',
}, },
}, },
format: (val) => dashIfEmpty(val), format: (row, dashIfEmpty) => dashIfEmpty(row.ipt),
}, },
{ {
align: 'left',
label: t('advanceTickets.state'), label: t('advanceTickets.state'),
name: 'state', name: 'state',
align: 'left', hidden: true,
sortable: true,
columnFilter: null,
}, },
{ {
align: 'left',
label: t('advanceTickets.liters'), label: t('advanceTickets.liters'),
name: 'liters', name: 'liters',
field: 'liters',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
align: 'left',
label: t('advanceTickets.lines'), label: t('advanceTickets.lines'),
name: 'lines', name: 'lines',
field: 'lines', format: (row, dashIfEmpty) => dashIfEmpty(row.lines),
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
}, },
{ {
align: 'left',
label: t('advanceTickets.import'), label: t('advanceTickets.import'),
field: 'import', name: 'totalWithVat',
name: 'import', hidden: true,
align: 'left', format: (row) => toCurrency(row.totalWithVat),
sortable: true,
}, },
{ {
align: 'left',
label: t('advanceTickets.futureId'), label: t('advanceTickets.futureId'),
name: 'futureId', name: 'futureId',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'futureId',
event: getInputEvents,
attrs: {
dense: true,
},
},
}, },
{ {
align: 'left',
label: t('advanceTickets.futureIpt'), label: t('advanceTickets.futureIpt'),
name: 'futureIpt', name: 'futureIpt',
field: 'futureIpt',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
filterParamKey: 'futureIptColFilter',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: { attrs: {
options: itemPackingTypesOptions.value, url: 'itemPackingTypes',
'option-value': 'code', fields: ['code', 'description'],
'option-label': 'description', where: { isActive: true },
dense: true, optionValue: 'code',
optionLabel: 'description',
}, },
}, },
format: (val) => dashIfEmpty(val), format: (row, dashIfEmpty) => dashIfEmpty(row.futureIpt),
}, },
{ {
align: 'left',
label: t('advanceTickets.futureState'), label: t('advanceTickets.futureState'),
name: 'futureState', name: 'futureState',
align: 'left', hidden: true,
sortable: true,
columnFilter: null,
format: (val) => dashIfEmpty(val),
}, },
{ {
align: 'left',
label: t('advanceTickets.futureLiters'), label: t('advanceTickets.futureLiters'),
name: 'futureLiters', name: 'futureLiters',
field: 'futureLiters',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
}, },
{ {
align: 'left',
label: t('advanceTickets.futureZone'), label: t('advanceTickets.futureZone'),
name: 'futureZoneName', name: 'futureZoneFk',
field: 'futureZoneName', columnClass: 'expand',
align: 'left',
sortable: true,
columnFilter: { columnFilter: {
component: VnSelect, component: 'select',
type: 'select', inWhere: true,
filterValue: null,
filterParamKey: 'futureZoneFk',
event: getInputEvents,
attrs: { attrs: {
options: zonesOptions.value, url: 'Zones',
'option-value': 'id', fields: ['id', 'name'],
'option-label': 'name',
dense: true,
}, },
}, },
format: (val) => dashIfEmpty(val), columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.futureZoneName),
}, },
{ {
align: 'left',
label: t('advanceTickets.notMovableLines'), label: t('advanceTickets.notMovableLines'),
name: 'notMovableLines', name: 'notMovableLines',
field: 'notMovableLines',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
}, },
{ {
align: 'left',
label: t('advanceTickets.futureLines'), label: t('advanceTickets.futureLines'),
name: 'futureLines', name: 'futureLines',
field: 'futureLines',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
}, },
{ {
label: t('advanceTickets.futureImport'),
name: 'futureImport',
align: 'left', align: 'left',
sortable: true, label: t('advanceTickets.futureImport'),
columnFilter: null, name: 'futureTotalWithVat',
hidden: true,
format: (row) => toCurrency(row.futureTotalWithVat),
}, },
]); ]);
@ -457,6 +360,13 @@ onMounted(async () => {
const filter = { limit: 0 }; const filter = { limit: 0 };
await arrayData.addFilter({ filter, userParams }); await arrayData.addFilter({ filter, userParams });
}); });
const getColor = (row) => {
return row?.classColor ? `bg-${row.classColor}` : 'bg-orange';
};
const getFutureColor = (row) => {
return row?.futureClassColor ? `bg-${row.futureClassColor}` : 'bg-orange';
};
</script> </script>
<template> <template>
@ -480,9 +390,9 @@ onMounted(async () => {
@on-fetch="(data) => (zonesOptions = data)" @on-fetch="(data) => (zonesOptions = data)"
/> />
<VnSearchbar <VnSearchbar
data-key="WeeklyTickets" data-key="AdvanceTickets"
:label="t('weeklyTickets.search')" :label="t('advanceTickets.search')"
:info="t('weeklyTickets.searchInfo')" :info="t('advanceTickets.searchInfo')"
/> />
<VnSubToolbar> <VnSubToolbar>
<template #st-data> <template #st-data>
@ -531,73 +441,32 @@ onMounted(async () => {
</template> </template>
</RightMenu> </RightMenu>
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<QTable <VnTable
:rows="tickets" ref="tableRef"
:columns="ticketColumns" data-key="AdvanceTickets"
row-key="index" url="TicketWeeklies/filter"
selection="multiple" :columns="columns"
auto-load
:right-search="false"
:disable-option="{ card: true }"
v-model:selected="selectedTickets" v-model:selected="selectedTickets"
:pagination="{ rowsPerPage: 0 }" :table="{
:no-data-label="t('globals.noResults')" 'row-key': 'id',
style="max-width: 99%" selection: 'multiple',
> }"
<template #header="props">
<QTr :props="props">
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="7"
translate
> >
<!--TODO: arreglar estilos-->
<template #before-header>
<span class="horizontal-separator" translate>
{{ t('advanceTickets.destination') }} {{ t('advanceTickets.destination') }}
{{ toDateFormat(userParams.dateToAdvance) }} {{ toDateFormat(userParams.dateToAdvance) }}
</QTh> </span>
<QTh <span class="horizontal-separator" translate>
class="horizontal-separator text-uppercase color-vn-label"
colspan="9"
translate
>
{{ t('advanceTickets.origin') }} {{ t('advanceTickets.origin') }}
{{ toDateFormat(userParams.dateFuture) }} {{ toDateFormat(userParams.dateFuture) }}
</QTh> </span>
</QTr>
<QTr>
<QTh>
<QCheckbox v-model="props.selected" />
</QTh>
<QTh
v-for="(col, index) in ticketColumns"
:key="index"
:class="{ 'vertical-separator': col.name === 'futureId' }"
>
{{ col.label }}
</QTh>
</QTr>
</template> </template>
<template #top-row="{ cols }"> <template #column-icons="{ row }">
<QTr>
<QTd />
<QTd
v-for="(col, index) in cols"
:key="index"
style="max-width: 100px"
>
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #header-cell-availableLines="{ col }">
<QTh class="vertical-separator">
{{ col.label }}
</QTh>
</template>
<template #body-cell-icons="{ row }">
<QTd class="q-gutter-x-xs"> <QTd class="q-gutter-x-xs">
<QIcon <QIcon
v-if="row.futureAgency !== row.agency && row.agency" v-if="row.futureAgency !== row.agency && row.agency"
@ -624,28 +493,18 @@ onMounted(async () => {
</QIcon> </QIcon>
</QTd> </QTd>
</template> </template>
<template #body-cell-ticketId="{ row }"> <template #column-id="{ row }">
<QTd> <span class="link" @click.stop>
<QBtn flat class="link">
{{ row.id }} {{ row.id }}
<TicketDescriptorProxy :id="row.id" /> <TicketDescriptorProxy :id="row.id" />
</QBtn> </span>
</QTd>
</template> </template>
<template #body-cell-state="{ row }"> <template #column-state="{ row }">
<QTd> <QChip :class="getColor(row)" dense square>
<QBadge
text-color="black"
:color="row.classColor"
class="q-ma-none"
dense
>
{{ row.state }} {{ row.state }}
</QBadge> </QChip>
</QTd>
</template> </template>
<template #body-cell-import="{ row }"> <template #column-totalWithVat="{ row }">
<QTd>
<QBadge <QBadge
:text-color="isLessThan50(row.totalWithVat) ? 'black' : 'white'" :text-color="isLessThan50(row.totalWithVat) ? 'black' : 'white'"
:color="totalPriceColor(row.totalWithVat)" :color="totalPriceColor(row.totalWithVat)"
@ -654,43 +513,32 @@ onMounted(async () => {
> >
{{ toCurrency(row.totalWithVat || 0) }} {{ toCurrency(row.totalWithVat || 0) }}
</QBadge> </QBadge>
</QTd>
</template> </template>
<template #body-cell-futureId="{ row }"> <template #column-futureId="{ row }">
<QTd class="vertical-separator"> <!--TODO: arreglar estilos para que se muestre como antes, slot?-->
<QBtn flat class="link" dense> <div class="vertical-separator">
<span class="link" @click.stop>
{{ row.futureId }} {{ row.futureId }}
<TicketDescriptorProxy :id="row.futureId" /> <TicketDescriptorProxy :id="row.futureId" />
</QBtn> </span>
</QTd> </div>
</template> </template>
<template #body-cell-futureState="{ row }"> <template #column-futureState="{ row }">
<QTd> <QChip :class="getFutureColor(row)" dense square>
<QBadge
text-color="black"
:color="row.futureClassColor"
class="q-ma-none"
dense
>
{{ row.futureState }} {{ row.futureState }}
</QBadge> </QChip>
</QTd>
</template> </template>
<template #body-cell-futureImport="{ row }"> <template #column-futureTotalWithVat="{ row }">
<QTd>
<QBadge <QBadge
:text-color=" :text-color="isLessThan50(row.futureTotalWithVat) ? 'black' : 'white'"
isLessThan50(row.futureTotalWithVat) ? 'black' : 'white'
"
:color="totalPriceColor(row.futureTotalWithVat)" :color="totalPriceColor(row.futureTotalWithVat)"
class="q-ma-none" class="q-ma-none"
dense dense
> >
{{ toCurrency(row.futureTotalWithVat || 0) }} {{ toCurrency(row.futureTotalWithVat || 0) }}
</QBadge> </QBadge>
</QTd>
</template> </template>
</QTable> </VnTable>
<VnProgress <VnProgress
:progress="progressPercentage" :progress="progressPercentage"
:cancelled="cancelProgress" :cancelled="cancelProgress"
@ -714,6 +562,8 @@ onMounted(async () => {
} }
.horizontal-separator { .horizontal-separator {
border-bottom: 4px solid white !important; width: 100%;
margin-top: 1%;
border-bottom: 5px solid white !important;
} }
</style> </style>

View File

@ -2,7 +2,9 @@
import axios from 'axios'; import axios from 'axios';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { toDate, toCurrency } from 'src/filters/index'; import { toDate, toCurrency } from 'src/filters/index';
import useNotify from 'src/composables/useNotify';
import TicketSummary from './Card/TicketSummary.vue'; import TicketSummary from './Card/TicketSummary.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -10,14 +12,25 @@ import VnTable from 'src/components/VnTable/VnTable.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnRow from 'src/components/ui/VnRow.vue'; import VnRow from 'src/components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'src/components/FetchData.vue';
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const tableRef = ref(); const tableRef = ref();
const quasar = useQuasar();
const { notify } = useNotify();
const clientsOptions = ref([]); const clientsOptions = ref([]);
const addressesOptions = ref([]); const addressesOptions = ref([]);
const agenciesOptions = ref([]); const agenciesOptions = ref([]);
const selectedClient = ref(); const selectedClient = ref();
const selectedRows = ref([]);
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
const showForm = ref(false);
const dialogData = ref();
const companiesOptions = ref([]);
const accountingOptions = ref([]);
const amountToReturn = ref();
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -187,9 +200,158 @@ const fetchAddresses = async (formData) => {
const getColor = (row) => { const getColor = (row) => {
return row?.classColor ? `bg-${row.classColor}` : 'bg-orange'; return row?.classColor ? `bg-${row.classColor}` : 'bg-orange';
}; };
async function makeInvoice(ticket) {
const ticketsIds = ticket.map((item) => item.id);
const { data } = await axios.post(`Tickets/invoiceTicketsAndPdf`, { ticketsIds });
const response = data;
if (response)
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
}
async function sendDocuware(ticket) {
try {
let ticketIds = ticket.map((item) => item.id);
const { data } = await axios.post(`Docuwares/upload`, {
fileCabinet: 'deliveryNote',
ticketIds,
});
for (let ticket of ticketIds) {
ticket.stateFk = data.id;
ticket.state = data.name;
ticket.alertLevel = data.alertLevel;
ticket.alertLevelCode = data.code;
}
notify('globals.dataSaved', 'positive');
} catch (err) {
console.log('err: ', err);
}
}
function openBalanceDialog(ticket) {
const checkedTickets = ticket;
const amountPaid = ref(0);
const clientFk = ref(null);
const description = ref([]);
const firstTicketClientId = checkedTickets[0].clientFk;
const isSameClient = checkedTickets.every(
(ticket) => ticket.clientFk === firstTicketClientId
);
if (!isSameClient) {
throw new Error('You cannot make a payment on account from multiple clients');
}
for (let ticketData of checkedTickets) {
console.log('ticketData: ', ticketData);
amountPaid.value += ticketData.totalWithVat;
clientFk.value = ticketData.clientFk;
description.value.push(ticketData.id);
}
const balanceCreateDialog = ref({
amountPaid: amountPaid.value,
clientFk: clientFk.value,
description: `Albaran: ${description.value.join(', ')}`,
});
dialogData.value = balanceCreateDialog;
showForm.value = true;
}
async function onSubmit() {
const { data: email } = await axios.get('Clients', {
params: {
filter: JSON.stringify({ where: { id: dialogData.value.value.clientFk } }),
},
});
const { data } = await axios.post(
`Clients/${dialogData.value.value.clientFk}/createReceipt`,
{
payed: dialogData.value.payed,
companyFk: dialogData.value.companyFk,
bankFk: dialogData.value.bankFk,
amountPaid: dialogData.value.value.amountPaid,
description: dialogData.value.value.description,
clientFk: dialogData.value.value.clientFk,
email: email[0].email,
}
);
if (data) notify('globals.dataSaved', 'positive');
showForm.value = false;
}
const setAmountToReturn = (newAmountGiven) => {
const amountPaid = dialogData.value.value.amountPaid;
amountToReturn.value = newAmountGiven - amountPaid;
};
function setReference(data) {
console.log('entity: ', data);
let newDescription = '';
switch (data) {
case 1:
newDescription = `${t(
'ticketList.creditCard'
)}, ${dialogData.value.value.description.replace(
/^(Credit Card, |Cash, |Transfers, )/,
''
)}`;
break;
case 2:
newDescription = `${t(
'ticketList.cash'
)}, ${dialogData.value.value.description.replace(
/^(Credit Card, |Cash, |Transfers, )/,
''
)}`;
break;
case 3:
newDescription = `${newDescription.replace(
/^(Credit Card, |Cash, |Transfers, )/,
''
)}`;
break;
case 4:
newDescription = `${t(
'ticketList.transfers'
)}, ${dialogData.value.value.description.replace(
/^(Credit Card, |Cash, |Transfers, )/,
''
)}`;
break;
case 3317:
newDescription = '';
break;
default:
break;
}
dialogData.value.value.description = newDescription;
console.log('Updated description:', dialogData.value.value.description);
}
</script> </script>
<template> <template>
<FetchData
url="Companies"
@on-fetch="(data) => (companiesOptions = data)"
auto-load
/>
<FetchData
url="Accountings"
@on-fetch="(data) => (accountingOptions = data)"
auto-load
/>
<VnSearchbar <VnSearchbar
data-key="Tickets" data-key="Tickets"
:label="t('Search ticket')" :label="t('Search ticket')"
@ -209,6 +371,11 @@ const getColor = (row) => {
:columns="columns" :columns="columns"
redirect="ticket" redirect="ticket"
auto-load auto-load
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
> >
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnRow> <VnRow>
@ -306,6 +473,135 @@ const getColor = (row) => {
</QChip> </QChip>
</template> </template>
</VnTable> </VnTable>
<QPageSticky :offset="[20, 80]" style="z-index: 2">
<QBtn
v-if="hasSelectedRows"
@click="makeInvoice(selectedRows)"
color="primary"
fab
icon="vn:invoice-in"
/>
<QTooltip>
{{ t('ticketList.createInvoice') }}
</QTooltip>
</QPageSticky>
<QPageSticky v-if="hasSelectedRows" :offset="[20, 140]" style="z-index: 2">
<QBtn
@click.stop="openBalanceDialog(selectedRows)"
color="primary"
fab
icon="vn:recovery"
/>
<QTooltip>
{{ t('ticketList.accountPayment') }}
</QTooltip>
</QPageSticky>
<QDialog ref="dialogRef" v-model="showForm">
<QCard class="q-pa-md q-mb-md">
<QForm @submit="onSubmit()" class="q-pa-sm">
{{ t('ticketList.addPayment') }}
<VnRow>
<VnInputDate
:label="t('ticketList.date')"
v-model="dialogData.payed"
/>
<VnSelect
:label="t('ticketList.company')"
v-model="dialogData.companyFk"
:options="companiesOptions"
option-value="id"
option-label="code"
hide-selected
>
</VnSelect>
</VnRow>
<VnRow>
<VnSelect
:label="t('ticketList.bank')"
v-model="dialogData.bankFk"
:options="accountingOptions"
option-value="id"
option-label="bank"
hide-selected
@update:model-value="setReference"
/>
<VnInput
:label="t('ticketList.amount')"
v-model="dialogData.value.amountPaid"
/>
</VnRow>
<VnRow v-if="dialogData.bankFk === 2">
<span>
{{ t('ticketList.cash') }}
</span>
</VnRow>
<VnRow v-if="dialogData.bankFk === 2">
<VnInput
:label="t('ticketList.deliveredAmount')"
v-model="dialogData.value.amountGiven"
@update:model-value="setAmountToReturn"
type="number"
/>
<VnInput
:label="t('ticketList.amountToReturn')"
:model-value="amountToReturn"
type="number"
readonly
/>
</VnRow>
<VnRow v-if="dialogData.bankFk === 3 || dialogData.bankFk === 3117">
<VnInput
:label="t('ticketList.compensation')"
v-model="dialogData.value.compensation"
type="text"
/>
</VnRow>
<VnRow>
<VnInput
:label="t('ticketList.reference')"
v-model="dialogData.value.description"
type="text"
/>
</VnRow>
<VnRow v-if="dialogData.bankFk === 2">
<QCheckbox
:label="t('ticketList.viewReceipt')"
v-model="dialogData.value.viewReceipt"
:toggle-indeterminate="false"
/>
<QCheckbox
:label="t('ticketList.sendEmail')"
v-model="dialogData.value.senEmail"
:toggle-indeterminate="false"
/>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn
:label="t('globals.save')"
color="primary"
@click="onSubmit()"
/>
<QBtn
flat
:label="t('globals.close')"
color="primary"
v-close-popup
/>
</div>
</QForm>
</QCard>
</QDialog>
<QPageSticky v-if="hasSelectedRows" :offset="[20, 200]" style="z-index: 2">
<QBtn
@click="sendDocuware(selectedRows)"
color="primary"
fab
icon="install_mobile"
/>
<QTooltip>
{{ t('ticketList.sendDocuware') }}
</QTooltip>
</QPageSticky>
</template> </template>
<i18n> <i18n>

View File

@ -66,6 +66,8 @@ advanceTickets:
advanceWithoutNegativeTitle: Advance tickets (without negatives) advanceWithoutNegativeTitle: Advance tickets (without negatives)
advanceWithoutNegativeSubtitle: Advance {selectedTickets} tickets confirmation advanceWithoutNegativeSubtitle: Advance {selectedTickets} tickets confirmation
errorsList: Errors list errorsList: Errors list
search: Search advance tickets
searchInfo: Search advance tickets by ID or client ID
futureTickets: futureTickets:
problems: Problems problems: Problems
ticketId: ID ticketId: ID
@ -239,3 +241,20 @@ ticketList:
summary: Summary summary: Summary
client: Customer client: Customer
createTicket: Create ticket createTicket: Create ticket
createInvoice: Create invoice
accountPayment: Account payment
sendDocuware: Set delivered and send delivery note(s) to the tablet
addPayment: Add payment
date: Date
company: Company
amount: Amount
reference: Reference
bank: Bank
cash: Cash
deliveredAmount: Delivered amount
amountToReturn: Amount to return
viewReceipt: View receipt
sendEmail: Send email
compensation: Compensation
creditCard: Credit card
transfers: Transfers

View File

@ -113,6 +113,8 @@ advanceTickets:
advanceWithoutNegativeTitle: Adelantar tickets (sin negativos) advanceWithoutNegativeTitle: Adelantar tickets (sin negativos)
advanceWithoutNegativeSubtitle: '¿Desea adelantar {selectedTickets} tickets?' advanceWithoutNegativeSubtitle: '¿Desea adelantar {selectedTickets} tickets?'
errorsList: Lista de errores errorsList: Lista de errores
search: Buscar por tickets adelantados
searchInfo: Buscar tickets adelantados por el identificador o el identificador del cliente
futureTickets: futureTickets:
problems: Problemas problems: Problemas
ticketId: ID ticketId: ID
@ -242,3 +244,20 @@ ticketList:
summary: Resumen summary: Resumen
client: Cliente client: Cliente
createTicket: Crear ticket createTicket: Crear ticket
createInvoice: Crear factura
accountPayment: Pago a cuenta...
sendDocuware: Marcar como servido/s y enviar albarán/es a la tablet
addPayment: Añadir pago
date: Fecha
company: Empresa
amount: Importe
reference: Referencia
bank: Caja
cash: Efectivo
deliveredAmount: Cantidad entregada
amountToReturn: Cantidad a devolver
viewReceipt: Ver recibido
sendEmail: Enviar correo
compensation: Compensación
creditCard: Tarjeta de crédito
transfers: Transferencias