540 lines
18 KiB
Vue
540 lines
18 KiB
Vue
<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 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();
|
|
|
|
const token = session.getTokenMultimedia();
|
|
|
|
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(() => $props.item.itemFk);
|
|
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,
|
|
},
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<FetchData
|
|
url="States/editableStates"
|
|
@on-fetch="(data) => (editableStates = data)"
|
|
auto-load
|
|
/>
|
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
|
<QBtnGroup push style="column-gap: 1px"
|
|
><QBtn
|
|
:label="t('globals.cancel')"
|
|
@click="emit('close')"
|
|
color="primary"
|
|
flat
|
|
icon="close"
|
|
>
|
|
<QTooltip>{{ t('globals.cancel') }}</QTooltip>
|
|
</QBtn></QBtnGroup
|
|
>
|
|
</Teleport>
|
|
<Teleport to="#st-data" v-if="stateStore?.isSubToolbarShown()">
|
|
<QSpace />
|
|
<QBtnGroup push style="column-gap: 1px">
|
|
<QBtn
|
|
color="primary"
|
|
:label="t('Change state')"
|
|
:disable="selectedRows.length < 2"
|
|
@click="showChangeStateDialog = true"
|
|
>
|
|
<QTooltip bottom anchor="bottom right">
|
|
{{ t('Change state') }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
<QBtn
|
|
color="primary"
|
|
:label="t('Change quantity')"
|
|
@click="showChangeQuantityDialog = true"
|
|
:disable="selectedRows.length < 2"
|
|
>
|
|
<QTooltip bottom anchor="bottom right">
|
|
{{ t('Change quantity') }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
<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
|
|
icon="vn:splitline"
|
|
color="primary"
|
|
:disable="selectedRows.length < 1"
|
|
@click="showProposalDialog = true"
|
|
>
|
|
<QTooltip bottom anchor="bottom right">
|
|
{{ t('Item proposal') }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
</QBtnGroup>
|
|
<QCheckbox v-model="showFree" :label="t('negative.detail.showFree')" />
|
|
</Teleport>
|
|
<VnPaginate
|
|
:data-key="URL_KEY"
|
|
:url="`${URL_KEY}/${entityId}/detail`"
|
|
ref="itemLackForm"
|
|
@on-fetch="copyOriginalRowsData($event)"
|
|
auto-load
|
|
>
|
|
<!-- :rows="rows" -->
|
|
<template #body="{ rows }">
|
|
<QImg
|
|
:src="`/api/Images/catalog/50x50/${item.itemFk}/download?access_token=${token}`"
|
|
spinner-color="primary"
|
|
:ratio="1"
|
|
height="50px"
|
|
width="50px"
|
|
class="image remove-bg"
|
|
:alt="'asdads'"
|
|
/>
|
|
|
|
<span class="text-h6">{{ item.longName }}</span>
|
|
|
|
<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>
|
|
|
|
<ChangeStateDialog
|
|
ref="changeStateDialogRef"
|
|
@hide="onDetailDialogHide"
|
|
v-model="showChangeStateDialog"
|
|
:selected-rows="selectedRows"
|
|
></ChangeStateDialog>
|
|
<ChangeQuantityDialog
|
|
ref="changeQuantityDialogRef"
|
|
@hide="onDetailDialogHide"
|
|
v-model="showChangeQuantityDialog"
|
|
:selected-rows="selectedRows"
|
|
>
|
|
</ChangeQuantityDialog>
|
|
<!--<HandleSplited
|
|
ref="splitDialogRef"
|
|
@hide="onDialogHide"
|
|
v-model="showSplitDialog"
|
|
:tickets="resultSplit"
|
|
></HandleSplited>-->
|
|
<ItemProposal
|
|
ref="proposalDialogRef"
|
|
@hide="onDialogHide"
|
|
v-model="showProposalDialog"
|
|
:item="item"
|
|
:tickets="selectedRows"
|
|
></ItemProposal>
|
|
</template>
|