441 lines
16 KiB
Vue
441 lines
16 KiB
Vue
<script setup>
|
|
import axios from 'axios';
|
|
import { ref, computed } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useQuasar } from 'quasar';
|
|
import { useRoute } from 'vue-router';
|
|
import { useArrayData } from 'composables/useArrayData';
|
|
import { useStateStore } from 'stores/useStateStore';
|
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
|
import FetchData from 'components/FetchData.vue';
|
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
|
|
|
import { toDate, toCurrency, toPercentage } from 'filters/index';
|
|
import VnDiscount from 'components/common/vnDiscount.vue';
|
|
import ClaimLinesImport from './ClaimLinesImport.vue';
|
|
|
|
const quasar = useQuasar();
|
|
const route = useRoute();
|
|
const { t } = useI18n();
|
|
const stateStore = useStateStore();
|
|
const arrayData = useArrayData('ClaimLines');
|
|
const store = arrayData.store;
|
|
|
|
const claimFilter = {
|
|
fields: ['ticketFk'],
|
|
};
|
|
const linesFilter = {
|
|
include: {
|
|
relation: 'sale',
|
|
scope: {
|
|
fields: ['concept', 'ticketFk', 'price', 'quantity', 'discount', 'itemFk'],
|
|
include: {
|
|
relation: 'ticket',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const claim = ref(null);
|
|
async function onFetchClaim(data) {
|
|
claim.value = data;
|
|
|
|
fetchMana();
|
|
}
|
|
|
|
const amount = ref(0);
|
|
const amountClaimed = ref(0);
|
|
async function onFetch(rows) {
|
|
amount.value = rows.reduce(
|
|
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
|
|
0
|
|
);
|
|
|
|
amountClaimed.value = rows.reduce(
|
|
(acumulator, line) => acumulator + line.sale.price * line.quantity,
|
|
0
|
|
);
|
|
}
|
|
|
|
const columns = computed(() => [
|
|
{
|
|
name: 'dated',
|
|
label: t('Delivered'),
|
|
field: ({ sale: { ticket } }) => toDate(ticket.landed),
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'quantity',
|
|
label: t('Quantity'),
|
|
field: ({ sale }) => sale.quantity,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'claimed',
|
|
label: t('Claimed'),
|
|
field: (row) => row.quantity,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'description',
|
|
label: t('Description'),
|
|
field: ({ sale }) => sale.concept,
|
|
},
|
|
{
|
|
name: 'price',
|
|
label: t('Price'),
|
|
field: ({ sale }) => sale.price,
|
|
format: (value) => toCurrency(value),
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'discount',
|
|
label: t('Discount'),
|
|
field: ({ sale }) => sale.discount,
|
|
format: (value) => toPercentage(value / 100),
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'total',
|
|
label: t('Total'),
|
|
field: ({ sale }) => {
|
|
const amount = sale.price * sale.quantity;
|
|
const appliedDiscount = (sale.discount * amount) / 100;
|
|
|
|
return amount - appliedDiscount;
|
|
},
|
|
format: (value) => toCurrency(value),
|
|
sortable: true,
|
|
},
|
|
]);
|
|
|
|
const selected = ref([]);
|
|
const mana = ref(0);
|
|
async function fetchMana() {
|
|
const ticketId = claim.value.ticketFk;
|
|
const response = await axios.get(`Tickets/${ticketId}/getSalesPersonMana`);
|
|
mana.value = response.data;
|
|
}
|
|
|
|
async function updateQuantity({ id, quantity }) {
|
|
if (!id) return;
|
|
await axios.patch(`ClaimBeginnings/${id}`, { quantity });
|
|
}
|
|
|
|
async function updateDiscount({ saleFk, discount, canceller }) {
|
|
const body = { salesIds: [saleFk], newDiscount: discount };
|
|
const claimId = claim.value.ticketFk;
|
|
const query = `Tickets/${claimId}/updateDiscount`;
|
|
|
|
await axios.post(query, body, {
|
|
signal: canceller.signal,
|
|
});
|
|
}
|
|
|
|
function onUpdateDiscount(response) {
|
|
const row = store.data[response.rowIndex];
|
|
row.sale.discount = response.discount;
|
|
quasar.notify({
|
|
message: t('Discount updated'),
|
|
type: 'positive',
|
|
});
|
|
}
|
|
|
|
async function confirmRemove() {
|
|
const rows = selected.value;
|
|
const count = rows.length;
|
|
|
|
if (count === 0) {
|
|
return quasar.notify({
|
|
message: 'You must select at least one row',
|
|
type: 'warning',
|
|
});
|
|
}
|
|
|
|
quasar
|
|
.dialog({
|
|
component: VnConfirm,
|
|
componentProps: {
|
|
title: t('Delete claimed sales'),
|
|
message: t('You are about to remove {count} rows', count, { count }),
|
|
data: { rows },
|
|
promise: remove,
|
|
},
|
|
})
|
|
.onOk(() => {
|
|
for (const row of rows) {
|
|
const orgData = store.data;
|
|
const index = orgData.findIndex((item) => item.id === row.id);
|
|
store.data.splice(index, 1);
|
|
selected.value = [];
|
|
}
|
|
});
|
|
}
|
|
|
|
async function remove({ rows }) {
|
|
if (!rows.length) return;
|
|
const body = { deletes: rows.map((row) => row.id) };
|
|
await axios.post(`ClaimBeginnings/crud`, body);
|
|
quasar.notify({
|
|
type: 'positive',
|
|
message: t('globals.rowRemoved'),
|
|
});
|
|
}
|
|
|
|
function showImportDialog() {
|
|
quasar
|
|
.dialog({
|
|
component: ClaimLinesImport,
|
|
})
|
|
.onOk(() => arrayData.refresh());
|
|
}
|
|
</script>
|
|
<template>
|
|
<QPageSticky position="top" :offset="[0, 0]" expand>
|
|
<QToolbar class="bg-dark text-white">
|
|
<QToolbarTitle> {{ t('Claimed lines') }} </QToolbarTitle>
|
|
<QSpace />
|
|
<div class="row q-gutter-md">
|
|
<div>
|
|
{{ t('Amount') }}
|
|
<QChip :dense="$q.screen.lt.sm">
|
|
{{ toCurrency(amount) }}
|
|
</QChip>
|
|
</div>
|
|
<QSeparator dark vertical />
|
|
<div>
|
|
{{ t('Amount Claimed') }}
|
|
<QChip color="positive" :dense="$q.screen.lt.sm">
|
|
{{ toCurrency(amountClaimed) }}
|
|
</QChip>
|
|
</div>
|
|
</div>
|
|
</QToolbar>
|
|
</QPageSticky>
|
|
|
|
<FetchData
|
|
:url="`Claims/${route.params.id}`"
|
|
:filter="claimFilter"
|
|
@on-fetch="onFetchClaim"
|
|
auto-load
|
|
/>
|
|
<div class="column items-center">
|
|
<div class="list">
|
|
<VnPaginate
|
|
data-key="ClaimLines"
|
|
:url="`Claims/${route.params.id}/lines`"
|
|
:filter="linesFilter"
|
|
@on-fetch="onFetch"
|
|
auto-load
|
|
>
|
|
<template #body="{ rows }">
|
|
<QTable
|
|
:columns="columns"
|
|
:rows="rows"
|
|
:dense="$q.screen.lt.md"
|
|
:pagination="{ rowsPerPage: 0 }"
|
|
row-key="id"
|
|
selection="multiple"
|
|
v-model:selected="selected"
|
|
hide-pagination
|
|
:grid="$q.screen.lt.md"
|
|
>
|
|
<template #body-cell-claimed="{ row, value }">
|
|
<QTd auto-width align="right" class="text-primary">
|
|
<span>{{ value }}</span>
|
|
|
|
<QPopupEdit
|
|
v-model="row.quantity"
|
|
v-slot="scope"
|
|
:title="t('Claimed quantity')"
|
|
@update:model-value="updateQuantity(row)"
|
|
buttons
|
|
>
|
|
<QInput
|
|
v-model="scope.value"
|
|
type="number"
|
|
dense
|
|
autofocus
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
|
|
<template #body-cell-discount="{ row, value, rowIndex }">
|
|
<QTd auto-width align="right" class="text-primary">
|
|
{{ value }}
|
|
<VnDiscount
|
|
:quantity="row.quantity"
|
|
:price="row.sale.price"
|
|
:discount="row.sale.discount"
|
|
:mana="mana"
|
|
:promise="updateDiscount"
|
|
:data="{ saleFk: row.sale.id, rowIndex: rowIndex }"
|
|
@on-update="onUpdateDiscount"
|
|
/>
|
|
</QTd>
|
|
</template>
|
|
<!-- View for grid mode -->
|
|
<template #item="props">
|
|
<div
|
|
class="q-mb-md col-12 grid-style-transition"
|
|
:style="props.selected ? 'transform: scale(0.95);' : ''"
|
|
>
|
|
<QCard>
|
|
<QCardSection>
|
|
<QCheckbox v-model="props.selected" />
|
|
</QCardSection>
|
|
<QSeparator inset />
|
|
<QList dense>
|
|
<QItem
|
|
v-for="column of props.cols"
|
|
:key="column.name"
|
|
>
|
|
<QItemSection>
|
|
<QItemLabel caption>
|
|
{{ column.label }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
<QItemSection side>
|
|
<template
|
|
v-if="column.name === 'claimed'"
|
|
>
|
|
<QItemLabel class="text-primary">
|
|
{{ column.value }}
|
|
<QPopupEdit
|
|
v-model="props.row.quantity"
|
|
v-slot="scope"
|
|
:title="t('Claimed quantity')"
|
|
@update:model-value="
|
|
updateQuantity(props.row)
|
|
"
|
|
buttons
|
|
>
|
|
<QInput
|
|
v-model="scope.value"
|
|
type="number"
|
|
dense
|
|
autofocus
|
|
@keyup.enter="scope.set"
|
|
@focus="
|
|
($event) =>
|
|
$event.target.select()
|
|
"
|
|
/>
|
|
</QPopupEdit>
|
|
</QItemLabel>
|
|
</template>
|
|
<template
|
|
v-else-if="column.name === 'discount'"
|
|
>
|
|
<QItemLabel class="text-primary">
|
|
{{ column.value }}
|
|
<VnDiscount
|
|
:quantity="props.row.quantity"
|
|
:price="props.row.sale.price"
|
|
:discount="
|
|
props.row.sale.discount
|
|
"
|
|
:mana="mana"
|
|
:promise="updateDiscount"
|
|
:data="{
|
|
saleFk: props.row.sale.id,
|
|
rowIndex: props.rowIndex,
|
|
}"
|
|
@on-update="onUpdateDiscount"
|
|
/>
|
|
</QItemLabel>
|
|
</template>
|
|
<template v-else>
|
|
<QItemLabel>
|
|
{{ column.value }}
|
|
</QItemLabel>
|
|
</template>
|
|
</QItemSection>
|
|
</QItem>
|
|
</QList>
|
|
</QCard>
|
|
</div>
|
|
</template>
|
|
</QTable>
|
|
</template>
|
|
</VnPaginate>
|
|
</div>
|
|
</div>
|
|
|
|
<Teleport
|
|
v-if="stateStore.isHeaderMounted() && !$q.screen.lt.sm"
|
|
to="#actions-prepend"
|
|
>
|
|
<div class="row q-gutter-x-sm">
|
|
<QBtn
|
|
v-if="selected.length > 0"
|
|
@click="confirmRemove"
|
|
icon="delete"
|
|
color="primary"
|
|
flat
|
|
dense
|
|
rounded
|
|
>
|
|
<QTooltip bottom> {{ t('globals.remove') }} </QTooltip>
|
|
</QBtn>
|
|
<QBtn @click="showImportDialog" icon="add" color="primary" flat dense rounded>
|
|
<QTooltip bottom> {{ t('globals.add') }} </QTooltip>
|
|
</QBtn>
|
|
<QSeparator vertical />
|
|
</div>
|
|
</Teleport>
|
|
<!-- v-if="quasar.platform.is.mobile" -->
|
|
<QPageSticky v-if="$q.screen.lt.sm" position="bottom" :offset="[0, 0]" expand>
|
|
<QToolbar class="bg-primary text-white q-pa-none">
|
|
<QTabs class="full-width" align="justify" inline-label narrow-indicator>
|
|
<QTab @click="showImportDialog" icon="add" :label="t('globals.add')" />
|
|
<QSeparator vertical inset />
|
|
<QTab
|
|
@click="confirmRemove"
|
|
icon="delete"
|
|
:label="t('globals.remove')"
|
|
:disable="selected.length === 0"
|
|
/>
|
|
</QTabs>
|
|
</QToolbar>
|
|
</QPageSticky>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.list {
|
|
padding-top: 50px;
|
|
max-width: 900px;
|
|
width: 100%;
|
|
}
|
|
.grid-style-transition {
|
|
transition: transform 0.28s, background-color 0.28s;
|
|
}
|
|
</style>
|
|
|
|
<i18n>
|
|
en:
|
|
You are about to remove {count} rows: '
|
|
You are about to remove <strong>{count}</strong> row |
|
|
You are about to remove <strong>{count}</strong> rows'
|
|
es:
|
|
Claimed lines: Líneas reclamadas
|
|
Delivered: Entregado
|
|
Quantity: Cantidad
|
|
Claimed: Reclamada
|
|
Description: Descripción
|
|
Price: Precio
|
|
Discount: Descuento
|
|
Actions: Acciones
|
|
Amount: Total
|
|
Amount Claimed: Cantidad reclamada
|
|
Delete claimed sales: Eliminar ventas reclamadas
|
|
Discount updated: Descuento actualizado
|
|
Claimed quantity: Cantidad reclamada
|
|
You are about to remove {count} rows: '
|
|
Vas a eliminar <strong>{count}</strong> línea |
|
|
Vas a eliminar <strong>{count}</strong> líneas'
|
|
</i18n>
|