salix-front/src/pages/Ticket/Card/TicketExpedition.vue

474 lines
15 KiB
Vue

<script setup>
import { onMounted, ref, computed, onUnmounted, reactive, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import TicketEditManaProxy from './TicketEditMana.vue';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import ExpeditionNewTicket from './ExpeditionNewTicket.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency, toPercentage } from 'src/filters';
import { useArrayData } from 'composables/useArrayData';
import { useVnConfirm } from 'composables/useVnConfirm';
import useNotify from 'src/composables/useNotify.js';
import { toDateTimeFormat } from 'src/filters/date';
import axios from 'axios';
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
const { notify } = useNotify();
const { openConfirmationModal } = useVnConfirm();
const editPriceProxyRef = ref(null);
const newTicketDialogRef = ref(null);
const logsTableDialogRef = ref(null);
const expeditionsLogsData = ref([]);
const selectedExpeditions = ref([]);
const allColumnNames = ref([]);
const visibleColumns = ref([]);
const newTicketWithRoute = ref(false);
const itemsOptions = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
case 'expeditionFk':
return { id: value };
case 'packageItemName':
return { packagingItemFk: value };
}
};
const expeditionsFilter = computed(() => ({
where: { ticketFk: route.params.id },
order: ['created DESC'],
}));
const expeditionsArrayData = useArrayData('ticketExpeditions', {
url: 'Expeditions/filter',
filter: expeditionsFilter.value,
exprBuilder: exprBuilder,
});
const expeditionsStore = expeditionsArrayData.store;
const ticketExpeditions = computed(() => expeditionsStore.data);
const ticketArrayData = useArrayData('ticketData');
const ticketStore = ticketArrayData.store;
const ticketData = computed(() => ticketStore.data);
const refetchExpeditions = async () => {
await expeditionsArrayData.applyFilter({
filter: expeditionsFilter.value,
});
};
watch(
() => route.params.id,
async () => await refetchExpeditions(),
{ immediate: true }
);
const params = reactive({});
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await expeditionsArrayData.addFilter({ filter: expeditionsFilter.value, params });
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const columns = computed(() => [
{
label: t('expedition.id'),
name: 'id',
field: 'id',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterParamKey: 'expeditionFk',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('expedition.item'),
name: 'item',
align: 'left',
columnFilter: {
component: VnInput,
type: 'text',
filterParamKey: 'packageItemName',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('expedition.name'),
name: 'name',
field: 'packageItemName',
align: 'left',
columnFilter: {
component: VnSelect,
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: itemsOptions.value,
'option-value': 'id',
'option-label': 'name',
dense: true,
},
},
},
{
label: t('expedition.packageType'),
name: 'packageType',
field: 'freightItemName',
align: 'left',
columnFilter: {
component: VnInput,
type: 'text',
// filterParamKey: 'expeditionFk',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('expedition.counter'),
name: 'counter',
field: 'counter',
align: 'left',
columnFilter: null,
},
{
label: t('expedition.externalId'),
name: 'externalId',
field: 'externalId',
align: 'left',
columnFilter: null,
},
{
label: t('expedition.created'),
name: 'created',
field: 'created',
align: 'left',
columnFilter: null,
format: (value) => toDateTimeFormat(value),
},
{
label: t('expedition.state'),
name: 'state',
field: 'state',
align: 'left',
columnFilter: null,
},
{
label: '',
name: 'history',
align: 'left',
columnFilter: null,
},
]);
const logTableColumns = computed(() => [
{
label: t('expedition.state'),
name: 'state',
field: 'state',
align: 'left',
sortable: true,
},
{
label: t('expedition.name'),
name: 'name',
align: 'name',
columnFilter: null,
},
{
label: t('expedition.created'),
name: 'created',
field: 'created',
align: 'left',
columnFilter: null,
format: (value) => toDateTimeFormat(value),
},
]);
const showNewTicketDialog = (withRoute = false) => {
newTicketWithRoute.value = withRoute;
newTicketDialogRef.value.show();
};
const deleteExpedition = async () => {
try {
const expeditionIds = selectedExpeditions.value.map(
(expedition) => expedition.id
);
const params = { expeditionIds };
await axios.post('Expeditions/deleteExpeditions', params);
await refetchExpeditions();
selectedExpeditions.value = [];
notify(t('expedition.expeditionRemoved'), 'positive');
} catch (error) {
console.error(error);
}
};
const showLog = async (expedition) => {
await getExpeditionState(expedition);
logsTableDialogRef.value.show();
};
const getExpeditionState = async (expedition) => {
try {
const filter = {
where: { expeditionFk: expedition.id },
order: ['created DESC'],
};
const { data } = await axios.get(`ExpeditionStates/filter`, {
params: { filter: JSON.stringify(filter) },
});
expeditionsLogsData.value = data;
} catch (error) {
console.error(error);
}
};
onMounted(async () => {
stateStore.rightDrawer = true;
const filteredColumns = columns.value.filter((col) => col.name !== 'history');
allColumnNames.value = filteredColumns.map((col) => col.name);
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<FetchData
url="Items"
auto-load
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
@on-fetch="(data) => (itemsOptions = data)"
/>
<VnSubToolbar>
<template #st-data>
<TableVisibleColumns
:all-columns="allColumnNames"
table-code="expeditionIndex"
labels-traductions-path="expedition"
@on-config-saved="visibleColumns = [...$event, 'history']"
/>
</template>
<template #st-actions>
<QBtnGroup push class="q-gutter-x-sm" flat>
<QBtnDropdown
ref="btnDropdownRef"
color="primary"
:label="t('expedition.move')"
:disable="!selectedExpeditions.length"
>
<template #label>
<QTooltip>{{ t('Select lines to see the options') }}</QTooltip>
</template>
<QList>
<QItem
clickable
v-close-popup
v-ripple
@click="showNewTicketDialog(false)"
>
<QItemSection>
<QItemLabel>{{
t('expedition.newTicketWithoutRoute')
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem
clickable
v-close-popup
v-ripple
@click="showNewTicketDialog(true)"
>
<QItemSection>
<QItemLabel>{{
t('expedition.newTicketWithRoute')
}}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QBtnDropdown>
<QBtn
:disable="!selectedExpeditions.length"
icon="delete"
color="primary"
@click="
openConfirmationModal(
'',
t('expedition.removeExpeditionSubtitle'),
deleteExpedition
)
"
/>
</QBtnGroup>
</template>
</VnSubToolbar>
<QTable
:rows="ticketExpeditions"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
selection="multiple"
v-model:selected="selectedExpeditions"
:visible-columns="visibleColumns"
:no-data-label="t('globals.noResults')"
>
<template #top-row="{ cols }">
<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 #body-cell-item="{ row }">
<QTd auto-width @click.stop>
<QBtn flat color="primary">{{ row.packagingItemFk }}</QBtn>
<ItemDescriptorProxy :id="row.packagingItemFk" />
</QTd>
</template>
<template #body-cell-available="{ row }">
<QTd @click.stop>
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
{{ row.available }}
</QBadge>
</QTd>
</template>
<template #body-cell-price="{ row }">
<QTd>
<template v-if="isTicketEditable && row.id">
<QBtn flat color="primary" dense @click="onOpenEditPricePopover(row)">
{{ toCurrency(row.price) }}
</QBtn>
<TicketEditManaProxy
ref="editPriceProxyRef"
:mana="mana"
:new-price="getNewPrice"
@save="updatePrice(row)"
>
<VnInput
v-model.number="edit.price"
:label="t('ticketSale.price')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toCurrency(row.price) }}</span>
</QTd>
</template>
<template #body-cell-discount="{ row }">
<QTd>
<template v-if="!isLocked && row.id">
<QBtn
flat
color="primary"
dense
@click="onOpenEditDiscountPopover(row)"
>
{{ toPercentage(row.discount / 100) }}
</QBtn>
<TicketEditManaProxy
:mana="mana"
:new-price="getNewPrice"
@save="changeDiscount(row)"
>
<VnInput
v-model.number="edit.discount"
:label="t('ticketSale.discount')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span>
</QTd>
</template>
<template #body-cell-history="{ row }">
<QTd>
<QBtn
@click.stop="showLog(row)"
color="primary"
icon="history"
size="md"
flat
>
<QTooltip class="text-no-wrap">
{{ t('expedition.historyAction') }}
</QTooltip>
</QBtn>
</QTd>
</template>
</QTable>
<QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale">
<ExpeditionNewTicket
:ticket="ticketData"
:with-route="newTicketWithRoute"
:selected-expeditions="selectedExpeditions"
/>
</QDialog>
<QDialog ref="logsTableDialogRef" transition-show="scale" transition-hide="scale">
<QTable
ref="tableRef"
data-key="TicketExpeditionLog"
:rows="expeditionsLogsData"
:columns="logTableColumns"
class="q-pa-sm"
>
<template #body-cell-name="{ row }">
<QTd auto-width>
<QBtn flat dense color="primary">{{ row.name }}</QBtn>
<WorkerDescriptorProxy :id="row.workerFk" />
</QTd>
</template>
</QTable>
</QDialog>
</template>