<script setup> import { computed, nextTick, onMounted, onUnmounted, ref, toRefs } from 'vue'; import { useI18n } from 'vue-i18n'; import { QBtn, QCheckbox, useQuasar } from 'quasar'; import axios from 'axios'; import HandleSplited from 'pages/Ticket/Negative/components/HandleSplited.vue'; import ChangeQuantityDialog from 'pages/Ticket/Negative/components/ChangeQuantityDialog.vue'; import ChangeStateDialog from 'pages/Ticket/Negative/components/ChangeStateDialog.vue'; import ItemProposal from 'pages/Item/components/ItemProposal.vue'; import { useVnConfirm } from 'composables/useVnConfirm'; import VnLv from 'src/components/ui/VnLv.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import TickerSplit from '../Card/TicketSplit.vue'; import TicketMassiveUpdate from '../Card/TicketMassiveUpdate.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue'; import FetchData from 'src/components/FetchData.vue'; import VnSelect from 'components/common/VnSelect.vue'; import VnInput from 'src/components/common/VnInput.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import { toDate, toHour } from 'src/filters'; import useNotify from 'src/composables/useNotify.js'; import { useStateStore } from 'stores/useStateStore'; import { useDialogPluginComponent } from 'quasar'; import { useSession } from 'src/composables/useSession'; import ZoneDescriptorProxy from 'pages/Zone/Card/ZoneDescriptorProxy.vue'; const { openConfirmationModal } = useVnConfirm(); const { t } = useI18n(); const URL_KEY = 'Tickets/ItemLack'; const editableStates = ref([]); const { notify } = useNotify(); const stateStore = useStateStore(); const proposalDialogRef = ref(); const splitDialogRef = ref(); const changeStateDialogRef = ref(); const changeQuantityDialogRef = ref(); const showSplitDialog = ref(false); const showProposalDialog = ref(false); const showChangeQuantityDialog = ref(false); const showChangeStateDialog = ref(false); const componentIsRendered = ref(false); const showFree = ref(true); const resultSplit = ref([]); const selectedRows = ref([]); const session = useSession(); import { useRoute } from 'vue-router'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnRow from 'src/components/ui/VnRow.vue'; const route = useRoute(); const token = session.getTokenMultimedia(); const itemLack = ref(null); const originalRowDataCopy = ref(null); // const $props = defineProps({ // item: { // type: Number, // required: true, // }, // filter: { // type: Object, // required: false, // default: () => { // true; // }, // }, // }); onMounted(() => { stateStore.rightDrawer = false; nextTick(() => { componentIsRendered.value = true; }); }); onUnmounted(() => { stateStore.rightDrawer = true; }); const copyOriginalRowsData = (rows) => { originalRowDataCopy.value = JSON.parse(JSON.stringify(rows)); }; const getInputEvents = (colField, props) => ({ 'update:modelValue': () => saveChange(colField, props), 'keyup.enter': () => saveChange(colField, props), }); const saveChange = async (field, { rowIndex, row }) => { try { switch (field) { // case 'split': // showSplitDialog.value =true // // await split({ simple: true }, [row]); // break; case 'code': await axios.post(`Tickets/state`, { ticketFk: row.ticketFk, code: row[field], }); break; case 'quantity': await axios.post(`Sales/${row.saleFk}/updateQuantity`, { quantity: +row.quantity, }); break; default: console.error(field, { rowIndex, row }); break; } notify('globals.dataSaved', 'positive'); } catch (err) { console.error('Error saving changes', err); } }; const entityId = computed(() => route.params.id); const item = ref({}); function isComponentVn(col) { return tableColumnComponents?.value[col.name]?.component === 'span' ?? false; } // const onDetailDialogHide = (evt) => { // if (evt?.type === 'refresh') ticketDetailRef.value.reload(); // }; const tableColumnComponents = computed(() => ({ status: { component: 'span', props: {}, event: () => ({}), sortable: false, }, ticketFk: { component: QBtn, props: { color: 'blue', sortable: true, flat: true }, event: () => ({}), sortable: true, }, shipped: { component: 'span', props: {}, event: () => ({}), }, theoreticalhour: { component: 'span', props: {}, event: () => ({}), }, state: { style: 'width: 160px', component: VnSelect, type: 'select', filterValue: null, props: { 'option-value': 'code', 'option-label': 'name', 'emit-value': true, 'map-options': true, 'use-input': true, 'hide-selected': true, dense: true, options: editableStates.value, }, event: getInputEvents, }, zoneName: { component: QBtn, props: { color: 'blue', sortable: true, flat: true }, event: () => ({}), }, nickname: { component: 'span', props: {}, event: () => ({}), }, quantity: { component: VnInput, props: { type: 'number', min: 0, class: 'input-number', }, event: getInputEvents, style: 'width: 100px', }, alertLevelCode: { component: 'span', props: {}, event: () => ({}), }, })); const columns = computed(() => [ { name: 'status', align: 'center', sortable: false, }, { name: 'ticketFk', label: t('negative.detail.ticketFk'), field: 'ticketFk', align: 'left', sortable: true, }, { name: 'shipped', label: t('negative.detail.shipped'), field: 'shipped', align: 'left', format: (val) => toDate(val), sortable: true, }, { name: 'theoreticalhour', label: t('negative.detail.theoreticalhour'), field: 'theoreticalhour', align: 'left', sortable: true, format: (val) => toHour(val), }, { name: 'state', label: t('negative.detail.state'), field: 'code', align: 'left', sortable: true, }, { name: 'zoneName', label: t('negative.detail.zoneName'), field: 'zoneName', align: 'left', sortable: true, }, { name: 'nickname', label: t('negative.detail.nickname'), field: 'nickname', align: 'left', sortable: true, }, { name: 'quantity', label: t('negative.detail.quantity'), field: 'quantity', align: 'left', sortable: true, style: 'width: 100px', }, ]); const { dialogRef, onDialogHide } = useDialogPluginComponent(); // const { filter } = toRefs($props); const emit = defineEmits([...useDialogPluginComponent.emits, 'selection', 'close']); function rowsHasSelected(selection) { emit( 'selection', selection //.map(({ ticketFk }) => ticketFk) ); } const itemLackForm = ref(); // const split = async ({ simple }, data = []) => { // openConfirmationModal( // t('negative.detail.split.confirmSplitSelected'), // t('negative.detail.split.splitQuestion'), // null, // () => { // showSplitDialog.value = true; // resultSplit.value = [{}]; // // const body = simple ? data : selectedRows.value; // // axios.post(`Tickets/split`, body).then((data) => { // // resultSplit.value = data; // // }); // } // ); // }; // const split = async ({ simple }, data = []) => { // openConfirmationModal( // t('negative.modalSplit.title'), // t('splitQuestion'), // () => { // const body = simple ? data : selectedRows.value; // axios.post(`Tickets/split`, body).then((data) => { // resultSplit.value = data; // }); // } // ); // }; const reload = async () => { itemLackForm.value.fetch(); }; defineExpose({ reload }); // FunciĆ³n de comparaciĆ³n function freeFirst({ alertLevel: a }, { alertLevel: b }) { const DEFAULT = 0; // Si el estado de 'a' es 'free' y el de 'b' no lo es, 'a' viene primero if (a === DEFAULT && b !== DEFAULT) { return -1; } // Si el estado de 'b' es 'free' y el de 'a' no lo es, 'b' viene primero if (b === DEFAULT && a !== DEFAULT) { return 1; } // En cualquier otro caso, no se cambia el orden return 0; } const handleRows = (rows) => { if (showFree.value) return rows.filter(({ alertLevel }) => alertLevel === 0); return rows.sort(freeFirst); }; const quasar = useQuasar(); const split = async () => { const body = selectedRows.value; // const {data} = await axios.post(`Tickets/split`, body); // resultSplit.value = data; resultSplit.value = [ { ticket: 32, newTicket: 1000005, status: 'split' }, { ticket: 32, newTicket: 1000006, status: 'noSplit' }, { ticket: 32, newTicket: 1000007, status: 'error' }, ]; quasar.dialog({ component: HandleSplited, componentProps: { tickets: resultSplit.value, }, }); }; const itemProposalEvt = ({ itemProposal }) => { itemProposalSelected.value = itemProposal; replaceItem(); }; const itemProposalSelected = ref(null); const replaceItem = () => { const rows = handleRows(originalRowDataCopy.value).sort((row) => row.quantity); for (const ticket of rows) { if (ticket.quantity > itemProposalSelected.value.available) continue; ticket.itemFk = itemProposalSelected.value.id; ticket.quantity *= 2; selectedRows.value.push({ ticketFk: ticket.ticketFk }); itemProposalSelected.value.available -= ticket.quantity; itemLack.value.lack += ticket.quantity; } }; </script> <template> <FetchData url="States/editableStates" @on-fetch="(data) => (editableStates = data)" auto-load /> <FetchData :url="`Items/${entityId}/getCard`" :fields="['longName']" @on-fetch="(data) => (item = data)" auto-load /> <FetchData :url="`Tickets/itemLack`" :filter="{ id: entityId }" @on-fetch="(data) => (itemLack = data[0])" auto-load /> <!-- <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()"> <QBtnGroup push style="column-gap: 1px" ><QBtn :label="t('proposal.replace')" @click="emit('close')" color="primary" icon="save" > <QTooltip>{{ t('globals.cancel') }}</QTooltip> </QBtn></QBtnGroup > </Teleport> --> <VnSubToolbar> <template #st-data> <QBtnGroup push style="column-gap: 1px"> <TicketMassiveUpdate :disable="selectedRows.length < 2" label="negative.buttonsUpdate.state" tooltip="negative.detail.modal.changeState.title" > <ChangeStateDialog ref="changeStateDialogRef" :selected-rows="selectedRows" ></ChangeStateDialog> </TicketMassiveUpdate> <!-- <QBtn > <QTooltip bottom anchor="bottom right"> {{ t() }} </QTooltip> </QBtn> --> <TicketMassiveUpdate label="negative.buttonsUpdate.quantity" @click="showChangeQuantityDialog = true" :disable="selectedRows.length < 2" tooltip="negative.detail.modal.changeQuantity.title" > <ChangeQuantityDialog ref="changeQuantityDialogRef" :selected-rows="selectedRows" > </ChangeQuantityDialog> </TicketMassiveUpdate> <!-- <TicketMassiveUpdate icon="refresh" color="primary" label="negative.buttonsUpdate.itemProposal" @click="showChangeQuantityDialog = true" :disable="selectedRows.length < 2" tooltip="negative.buttonsUpdate.itemProposal" > </TicketMassiveUpdate> --> <QBtn color="primary" @click=" openConfirmationModal( t('negative.detail.modal.split.title'), t('negative.detail.modal.split.subTitle'), split, () => (showSplitDialog = true) ) " :disable="selectedRows.length < 1" icon="call_split" > <QTooltip bottom anchor="bottom right"> {{ t('globals.split') }} </QTooltip> </QBtn> <QBtn color="primary" @click="showProposalDialog = true"> <QIcon name="import_export" class="rotate-90"></QIcon> <ItemProposal ref="proposalDialogRef" :item="item" :tickets="selectedRows" ></ItemProposal> <QTooltip bottom anchor="bottom right"> {{ t('itemProposal') }} </QTooltip> </QBtn> </QBtnGroup> <QCheckbox v-model="showFree" :label="t('negative.detail.showFree')" /> </template> </VnSubToolbar> <QPage> <div class="full-width q-pa-md"> {{ itemLack }} {{ selectedRows }} <VnPaginate :data-key="URL_KEY" :url="`${URL_KEY}/${entityId}`" ref="itemLackForm" @on-fetch="copyOriginalRowsData($event)" auto-load > <!-- :rows="rows" --> <template #body="{ rows }"> <VnLv class="q-mb-lg image"> <template #label> <QImg :src="`/api/Images/catalog/50x50/${entityId}/download?access_token=${token}`" spinner-color="primary" :ratio="1" height="50px" width="50px" class="image remove-bg" :alt="'asdads'" /> </template> <template #value> <QBtn flat class="link text-blue"> {{ item.longName }} <ItemDescriptorProxy :id="entityId" /> </QBtn> <QBadge text-color="white" color="red" :label="itemLack.lack" /> <FetchedTags class="q-ml-md" :item="item" :max-length="5" /> </template> </VnLv> <!-- <ItemDescriptorProxy :id="entityId" /> <span class="text-h6">{{ item.longName }}</span> <span class="text-h6" ><sub>{{ item.longName }}</sub></span > --> <!-- <VnRow style="align-items: center"> <div> </div> <QIcon name="arrow_right" size="lg" /> <VnSelectDialog action-icon="call_split"></VnSelectDialog ></VnRow> --> <QTable ref="tableRef" :columns="columns" :rows="handleRows(rows)" row-key="ticketFk" selection="multiple" v-model:selected="selectedRows" @update:selected="rowsHasSelected" :grid="$q.screen.lt.md" hide-bottom > <template #body="props"> <QTr> <QTd> <!-- <QIcon v-if="resultSplit.length > 0" :name="getIcon(props.key, 'name')" :color="getIcon(props.key, 'color')" class="fill-icon q-mr-sm" size="xs" style="font-weight: bold" /> --> <QCheckbox v-model="props.selected" /> </QTd> <QTd v-for="col in props.cols" :key="col.name"> <template v-if="tableColumnComponents[col.name]?.component" > <component :is=" tableColumnComponents[col.name].component " v-bind="tableColumnComponents[col.name].props" v-model="props.row[col.field]" v-on=" tableColumnComponents[col.name].event( col.field, props ) " :style="tableColumnComponents[col.name].style" > <template v-if="isComponentVn(col)">{{ col.value }}</template> <template v-if="col.name === 'status'"> <QIcon v-if="props.row.isRookie" name="vn:person" size="xs" color="primary" class="cursor-pointer" > <QTooltip>{{ t('negative.detail.isRookie') }}</QTooltip> </QIcon> <QIcon v-if="props.row.peticionCompra" name="vn:buyrequest" size="xs" color="primary" class="cursor-pointer" > <QTooltip>{{ t( 'negative.detail.peticionCompra' ) }}</QTooltip> </QIcon> <QIcon v-if="props.row.turno" name="vn:calendar" size="xs" color="primary" class="cursor-pointer" > <QTooltip>{{ t('negative.detail.turno') }}</QTooltip> </QIcon> </template> <template v-if="col.name === 'ticketFk'" >{{ col.value }} <ItemDescriptorProxy :id="$props.entityId" /></template> <template v-if="col.name === 'zoneName'"> {{ col.value }} <ZoneDescriptorProxy :id="props.row.zoneFk" /> </template> </component> </template> </QTd> </QTr> </template> </QTable> </template> </VnPaginate></div ></QPage> <!--<HandleSplited ref="splitDialogRef" @hide="onDialogHide" v-model="showSplitDialog" :tickets="resultSplit" ></HandleSplited>--> </template>