474 lines
15 KiB
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>
|