<script setup> import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; import TicketSummary from 'src/pages/Ticket/Card/TicketSummary.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { toDateFormat } from 'src/filters/date.js'; import { toCurrency, dateRange, dashIfEmpty } from 'src/filters'; import RightMenu from 'src/components/common/RightMenu.vue'; import MonitorTicketSearchbar from './MonitorTicketSearchbar.vue'; import MonitorTicketFilter from './MonitorTicketFilter.vue'; const DEFAULT_AUTO_REFRESH = 2 * 60 * 1000; // 2min in ms const { t } = useI18n(); const autoRefresh = ref(false); const tableRef = ref(null); const provinceOpts = ref([]); const stateOpts = ref([]); const zoneOpts = ref([]); const visibleColumns = ref([]); const { viewSummary } = useSummaryDialog(); const from = Date.vnNew(); from.setHours(0, 0, 0, 0); const to = new Date(from.getTime()); to.setDate(to.getDate() + 1); to.setHours(23, 59, 59, 999); function exprBuilder(param, value) { switch (param) { case 'stateFk': return { 'ts.stateFk': value }; case 'salesPersonFk': return { 'c.salesPersonFk': !value ? null : value }; case 'provinceFk': return { 'a.provinceFk': value }; case 'theoreticalHour': return { 'z.hour': value }; case 'practicalHour': return { 'zed.etc': value }; case 'shippedDate': return { 't.shipped': { between: dateRange(value) } }; case 'nickname': return { [`t.nickname`]: { like: `%${value}%` } }; case 'zoneFk': case 'totalWithVat': return { [`t.${param}`]: value }; } } const columns = computed(() => [ { label: t('salesTicketsTable.problems'), name: 'totalProblems', align: 'left', columnFilter: false, attrs: { dense: true, }, }, { label: t('salesTicketsTable.identifier'), name: 'id', field: 'id', align: 'left', columnFilter: { component: 'number', name: 'id', attrs: { dense: true, }, }, }, { label: t('salesTicketsTable.client'), name: 'clientFk', align: 'left', field: 'nickname', columnFilter: { component: 'select', attrs: { url: 'Clients', fields: ['id', 'name', 'nickname'], sortBy: 'name ASC', }, }, }, { label: t('salesTicketsTable.salesPerson'), name: 'salesPersonFk', field: 'userName', align: 'left', optionFilter: 'firstName', columnFilter: { component: 'select', attrs: { url: 'Workers/activeWithInheritedRole', fields: ['id', 'name'], sortBy: 'nickname ASC', where: { role: 'salesPerson' }, useLike: false, }, }, }, { label: t('salesTicketsTable.date'), name: 'shippedDate', align: 'left', columnFilter: { component: 'date', name: 'shippedDate', attrs: { dense: true, }, }, }, { label: t('salesTicketsTable.theoretical'), name: 'theoreticalhour', field: 'zoneLanding', align: 'left', format: (row) => row.theoreticalhour, columnFilter: false, }, { label: t('salesTicketsTable.practical'), name: 'practicalHour', field: 'practicalHour', align: 'left', format: (row) => row.practicalHour, columnFilter: false, }, { label: t('salesTicketsTable.preparation'), name: 'preparationHour', field: 'shipped', align: 'left', format: (row) => row.preparationHour, columnFilter: false, }, { label: t('salesTicketsTable.province'), name: 'provinceFk', field: 'province', align: 'left', format: (row) => row.province, columnFilter: { component: 'select', name: 'provinceFk', attrs: { options: provinceOpts.value, 'option-value': 'id', 'option-label': 'name', dense: true, }, }, }, { label: t('salesTicketsTable.state'), name: 'state', align: 'left', columnFilter: { component: 'select', name: 'stateFk', attrs: { options: stateOpts.value, 'option-value': 'id', 'option-label': 'name', dense: true, }, }, }, { label: t('salesTicketsTable.isFragile'), name: 'isFragile', field: 'isFragile', align: 'left', columnFilter: false, attrs: { 'checked-icon': 'local_bar', 'unchecked-icon': 'local_bar', 'false-value': 0, 'true-value': 1, }, }, { label: t('salesTicketsTable.zone'), name: 'zoneFk', align: 'left', columnFilter: { component: 'select', name: 'zoneFk', attrs: { options: zoneOpts.value, 'option-value': 'id', 'option-label': 'name', dense: true, }, }, }, { label: t('salesTicketsTable.total'), name: 'totalWithVat', field: 'totalWithVat', align: 'left', columnFilter: { component: 'number', name: 'totalWithVat', attrs: { dense: true, }, }, }, { align: 'right', name: 'tableActions', label: '', actions: [ { title: t('salesTicketsTable.goToLines'), icon: 'vn:lines', color: 'priamry', action: (row) => openTab(row.id), isPrimary: true, attrs: { flat: true, dense: true, }, }, { title: t('salesTicketsTable.preview'), icon: 'preview', color: 'priamry', action: (row) => viewSummary(row.id, TicketSummary), isPrimary: true, attrs: { flat: true, dense: true, }, }, ], }, ]); const getBadgeAttrs = (date) => { let today = Date.vnNew(); today.setHours(0, 0, 0, 0); let timeTicket = new Date(date); timeTicket.setHours(0, 0, 0, 0); let comparation = today - timeTicket; if (comparation == 0) return { color: 'warning', 'text-color': 'black' }; if (comparation < 0) return { color: 'success', 'text-color': 'black' }; return { color: 'transparent', 'text-color': 'white' }; }; let refreshTimer = null; const autoRefreshHandler = (value) => { if (value) refreshTimer = setInterval(() => tableRef.value.reload(), DEFAULT_AUTO_REFRESH); else { clearInterval(refreshTimer); refreshTimer = null; } }; const stateColors = { notice: 'info', success: 'positive', warning: 'warning', alert: 'negative', }; const totalPriceColor = (ticket) => { const total = parseInt(ticket.totalWithVat); if (total > 0 && total < 50) return 'warning'; }; const formatShippedDate = (date) => { if (!date) return '-'; const split1 = date.split('T'); const [year, month, day] = split1[0].split('-'); const _date = new Date(year, month - 1, day); return toDateFormat(_date); }; const openTab = (id) => window.open(`#/ticket/${id}/sale`, '_blank', 'noopener, noreferrer'); </script> <template> <FetchData url="Provinces" :filter="{ fields: ['id', 'name'], order: 'name ASC', }" auto-load @on-fetch="(data) => (provinceOpts = data)" /> <FetchData url="States" :filter="{ fields: ['id', 'name'], order: 'name ASC', }" auto-load @on-fetch="(data) => (stateOpts = data)" /> <FetchData url="Zones" :filter="{ fields: ['id', 'name'], order: 'name ASC', }" auto-load @on-fetch="(data) => (zoneOpts = data)" /> <MonitorTicketSearchbar /> <RightMenu> <template #right-panel> <MonitorTicketFilter data-key="saleMonitorTickets" /> </template> </RightMenu> <VnTable ref="tableRef" data-key="saleMonitorTickets" url="SalesMonitors/salesFilter" search-url="saleMonitorTickets" :expr-builder="exprBuilder" :offset="50" :columns="columns" :visible-columns="visibleColumns" :right-search="false" default-mode="table" auto-load :row-click="({ id }) => openTab(id)" :disable-option="{ card: true }" :user-params="{ from, to, scopeDays: 1 }" > <template #top-left> <QBtn icon="refresh" size="md" color="primary" class="q-mr-sm" dense flat @click="$refs.tableRef.reload()" > <QTooltip>{{ $t('globals.refresh') }}</QTooltip> </QBtn> <QCheckbox v-model="autoRefresh" :label="$t('salesTicketsTable.autoRefresh')" @update:model-value="autoRefreshHandler" dense > <QTooltip>{{ $t('refreshInfo') }}</QTooltip> </QCheckbox> </template> <template #column-totalProblems="{ row }"> <span> <QIcon v-if="row.isTaxDataChecked === 0" name="vn:no036" color="primary" size="xs" > <QTooltip>{{ $t('salesTicketsTable.noVerifiedData') }}</QTooltip> </QIcon> <QIcon v-if="row.hasTicketRequest" name="vn:buyrequest" color="primary" size="xs" > <QTooltip>{{ $t('salesTicketsTable.purchaseRequest') }}</QTooltip> </QIcon> <QIcon v-if="row.itemShortage" name="vn:unavailable" color="primary" size="xs" > <QTooltip>{{ $t('salesTicketsTable.notVisible') }}</QTooltip> </QIcon> <QIcon v-if="row.isFreezed" name="vn:frozen" color="primary" size="xs"> <QTooltip>{{ $t('salesTicketsTable.clientFrozen') }}</QTooltip> </QIcon> <QIcon v-if="row.risk" name="vn:risk" :color="row.hasHighRisk ? 'negative' : 'primary'" size="xs" > <QTooltip >{{ $t('salesTicketsTable.risk') }}: {{ row.risk }}</QTooltip > </QIcon> <QIcon v-if="row.hasComponentLack" name="vn:components" color="primary" size="xs" > <QTooltip>{{ $t('salesTicketsTable.componentLack') }}</QTooltip> </QIcon> <QIcon v-if="row.isTooLittle" name="vn:isTooLittle" color="primary" size="xs" > <QTooltip>{{ $t('salesTicketsTable.tooLittle') }}</QTooltip> </QIcon> </span> </template> <template #column-id="{ row }"> <span class="link" @click.stop.prevent> {{ row.id }} <TicketDescriptorProxy :id="row.id" /> </span> </template> <template #column-clientFk="{ row }"> <div @click.stop :title="row.nickname"> <span class="link" v-text="row.nickname" /> <CustomerDescriptorProxy :id="row.clientFk" /> </div> </template> <template #column-salesPersonFk="{ row }"> <div @click.stop :title="row.userName"> <span class="link" v-text="dashIfEmpty(row.userName)" /> <WorkerDescriptorProxy :id="row.salesPersonFk" /> </div> </template> <template #column-shippedDate="{ row }"> <QBadge v-bind="getBadgeAttrs(row.shippedDate)" class="q-pa-sm" style="font-size: 14px" > {{ formatShippedDate(row.shippedDate) }} </QBadge> </template> <template #column-provinceFk="{ row }"> <span :title="row.province" v-text="row.province" /> </template> <template #column-state="{ row }"> <div @click.stop.prevent> <div v-if="row.refFk"> <span class="link">{{ row.refFk }}</span> <InvoiceOutDescriptorProxy :id="row.invoiceOutId" /> </div> <QBadge v-else :color="stateColors[row.classColor] || 'transparent'" :text-color="stateColors[row.classColor] ? 'black' : 'white'" class="q-pa-sm" style="font-size: 14px" > {{ row.state }} </QBadge> </div> </template> <template #column-isFragile="{ row }"> <QIcon v-if="row.isFragile" name="local_bar" color="primary" size="sm"> <QTooltip>{{ $t('salesTicketsTable.isFragile') }}</QTooltip> </QIcon> </template> <template #column-zoneFk="{ row }"> <div @click.stop.prevent :title="row.zoneName"> <span class="link">{{ row.zoneName }}</span> <ZoneDescriptorProxy :id="row.zoneFk" /> </div> </template> <template #column-totalWithVat="{ row }"> <QBadge :color="totalPriceColor(row) || 'transparent'" :text-color="totalPriceColor(row) ? 'black' : 'white'" class="q-pa-sm" style="font-size: 14px" > {{ toCurrency(row.totalWithVat) }} </QBadge> </template> </VnTable> </template> <style lang="scss" scoped> td .q-icon { margin: 0 2px; } </style>