357 lines
11 KiB
Vue
357 lines
11 KiB
Vue
<script setup>
|
|
import FetchedTags from 'components/ui/FetchedTags.vue';
|
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
|
import { computed, ref, watch } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import axios from 'axios';
|
|
import FetchData from 'src/components/FetchData.vue';
|
|
import { toDate, toHour } from 'src/filters';
|
|
import useNotify from 'src/composables/useNotify.js';
|
|
import ZoneDescriptorProxy from 'pages/Zone/Card/ZoneDescriptorProxy.vue';
|
|
import { useRoute } from 'vue-router';
|
|
import VnTable from 'src/components/VnTable/VnTable.vue';
|
|
import TicketDescriptorProxy from '../Card/TicketDescriptorProxy.vue';
|
|
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
|
|
|
const $props = defineProps({
|
|
filter: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
});
|
|
|
|
watch(
|
|
() => $props.filter,
|
|
(v) => {
|
|
filterLack.value.where = v;
|
|
tableRef.value.reload(filterLack);
|
|
},
|
|
);
|
|
|
|
const filterLack = ref({
|
|
include: [
|
|
{
|
|
relation: 'workers',
|
|
scope: {
|
|
fields: ['id', 'firstName'],
|
|
},
|
|
},
|
|
],
|
|
where: { ...$props.filter },
|
|
order: 'ts.alertLevelCode ASC',
|
|
});
|
|
|
|
const selectedRows = ref([]);
|
|
const { t } = useI18n();
|
|
const { notify } = useNotify();
|
|
const entityId = computed(() => route.params.id);
|
|
const item = ref({});
|
|
const route = useRoute();
|
|
const columns = computed(() => [
|
|
{
|
|
name: 'status',
|
|
align: 'center',
|
|
sortable: false,
|
|
columnClass: 'shrink',
|
|
columnFilter: false,
|
|
},
|
|
{
|
|
name: 'ticketFk',
|
|
label: t('negative.detail.ticketFk'),
|
|
align: 'center',
|
|
sortable: true,
|
|
columnFilter: {
|
|
component: 'input',
|
|
type: 'number',
|
|
},
|
|
},
|
|
{
|
|
name: 'shipped',
|
|
label: t('negative.detail.shipped'),
|
|
field: 'shipped',
|
|
align: 'center',
|
|
format: ({ shipped }) => toDate(shipped),
|
|
sortable: true,
|
|
columnFilter: {
|
|
component: 'date',
|
|
columnClass: 'shrink',
|
|
},
|
|
},
|
|
{
|
|
name: 'minTimed',
|
|
label: t('negative.detail.theoreticalhour'),
|
|
field: 'minTimed',
|
|
align: 'center',
|
|
sortable: true,
|
|
component: 'time',
|
|
columnFilter: {},
|
|
},
|
|
{
|
|
name: 'alertLevelCode',
|
|
label: t('negative.detail.state'),
|
|
columnFilter: {
|
|
name: 'alertLevelCode',
|
|
component: 'select',
|
|
attrs: {
|
|
url: 'AlertLevels',
|
|
fields: ['name', 'code'],
|
|
optionLabel: 'code',
|
|
optionValue: 'code',
|
|
},
|
|
},
|
|
align: 'center',
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'zoneName',
|
|
label: t('negative.detail.zoneName'),
|
|
field: 'zoneName',
|
|
align: 'center',
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'nickname',
|
|
label: t('negative.detail.nickname'),
|
|
field: 'nickname',
|
|
align: 'center',
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'quantity',
|
|
label: t('negative.detail.quantity'),
|
|
field: 'quantity',
|
|
sortable: true,
|
|
component: 'input',
|
|
type: 'number',
|
|
},
|
|
]);
|
|
|
|
const emit = defineEmits(['update:selection']);
|
|
const itemLack = ref(null);
|
|
const fetchItemLack = ref(null);
|
|
const tableRef = ref(null);
|
|
defineExpose({ tableRef, itemLack });
|
|
watch(selectedRows, () => emit('update:selection', selectedRows));
|
|
const getInputEvents = ({ col, ...rows }) => ({
|
|
'update:modelValue': () => saveChange(col.name, rows),
|
|
'keyup.enter': () => saveChange(col.name, rows),
|
|
});
|
|
const saveChange = async (field, { row }) => {
|
|
try {
|
|
switch (field) {
|
|
case 'alertLevelCode':
|
|
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;
|
|
}
|
|
notify('globals.dataSaved', 'positive');
|
|
fetchItemLack.value.fetch();
|
|
} catch (err) {
|
|
console.error('Error saving changes', err);
|
|
f;
|
|
}
|
|
};
|
|
|
|
function onBuysFetched(data) {
|
|
Object.assign(item.value, data[0]);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<FetchData
|
|
ref="fetchItemLack"
|
|
:url="`Tickets/itemLack`"
|
|
:params="{ id: entityId }"
|
|
@on-fetch="(data) => (itemLack = data[0])"
|
|
auto-load
|
|
/>
|
|
<FetchData
|
|
:url="`Items/${entityId}/getCard`"
|
|
:fields="['longName']"
|
|
@on-fetch="(data) => (item = data)"
|
|
auto-load
|
|
/>
|
|
<FetchData
|
|
:url="`Buys/latestBuysFilter`"
|
|
:fields="['longName']"
|
|
:filter="{ where: { 'i.id': entityId } }"
|
|
@on-fetch="onBuysFetched"
|
|
auto-load
|
|
/>
|
|
<VnTable
|
|
ref="tableRef"
|
|
data-key="NegativeItem"
|
|
:map-key="false"
|
|
:url="`Tickets/itemLack/${entityId}`"
|
|
:columns="columns"
|
|
auto-load
|
|
:create="false"
|
|
:create-as-dialog="false"
|
|
:use-model="true"
|
|
:filter="filterLack"
|
|
:order="['ts.alertLevelCode ASC']"
|
|
:table="{
|
|
'row-key': 'id',
|
|
selection: 'multiple',
|
|
}"
|
|
dense
|
|
:is-editable="true"
|
|
:row-click="false"
|
|
:right-search="false"
|
|
:right-search-icon="false"
|
|
v-model:selected="selectedRows"
|
|
:disable-option="{ card: true }"
|
|
>
|
|
<template #top-left>
|
|
<div style="display: flex; align-items: center" v-if="itemLack">
|
|
<!-- <VnImg :id="itemLack.itemFk" class="rounded image-wrapper"></VnImg> -->
|
|
<div class="flex column" style="align-items: center">
|
|
<QBadge
|
|
ref="badgeLackRef"
|
|
class="q-ml-xs"
|
|
text-color="white"
|
|
:color="itemLack.lack === 0 ? 'positive' : 'negative'"
|
|
:label="itemLack.lack"
|
|
/>
|
|
</div>
|
|
<div class="flex column left" style="align-items: flex-start">
|
|
<QBtn flat class="link">
|
|
{{ item?.longName ?? item.name }}
|
|
<ItemDescriptorProxy :id="entityId" />
|
|
<FetchedTags class="q-ml-md" :item="item" :columns="7" />
|
|
</QBtn>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #top-right>
|
|
<slot name="top-right" />
|
|
</template>
|
|
|
|
<template #column-status="{ row }">
|
|
<QTd style="min-width: 150px">
|
|
<div class="icon-container">
|
|
<QIcon
|
|
v-if="row.isBasket"
|
|
name="vn:basket"
|
|
color="primary"
|
|
class="cursor-pointer"
|
|
size="xs"
|
|
>
|
|
<QTooltip>{{ t('negative.detail.isBasket') }}</QTooltip>
|
|
</QIcon>
|
|
<QIcon
|
|
v-if="row.hasToIgnore"
|
|
name="star"
|
|
color="primary"
|
|
class="cursor-pointer fill-icon"
|
|
size="xs"
|
|
>
|
|
<QTooltip>{{ t('negative.detail.hasToIgnore') }}</QTooltip>
|
|
</QIcon>
|
|
<QIcon
|
|
v-if="row.hasObservation"
|
|
name="change_circle"
|
|
color="primary"
|
|
class="cursor-pointer"
|
|
size="xs"
|
|
>
|
|
<QTooltip>{{
|
|
t('negative.detail.hasObservation')
|
|
}}</QTooltip> </QIcon
|
|
><QIcon
|
|
v-if="row.isRookie"
|
|
name="vn:Person"
|
|
size="xs"
|
|
color="primary"
|
|
class="cursor-pointer"
|
|
>
|
|
<QTooltip>{{ t('negative.detail.isRookie') }}</QTooltip>
|
|
</QIcon>
|
|
<QIcon
|
|
v-if="row.peticionCompra"
|
|
name="vn:buyrequest"
|
|
size="xs"
|
|
color="primary"
|
|
class="cursor-pointer"
|
|
>
|
|
<QTooltip>{{ t('negative.detail.peticionCompra') }}</QTooltip>
|
|
</QIcon>
|
|
<QIcon
|
|
v-if="row.turno"
|
|
name="vn:calendar"
|
|
size="xs"
|
|
color="primary"
|
|
class="cursor-pointer"
|
|
>
|
|
<QTooltip>{{ t('negative.detail.turno') }}</QTooltip>
|
|
</QIcon>
|
|
</div></QTd
|
|
>
|
|
</template>
|
|
<template #column-nickname="{ row }">
|
|
<span class="link" @click.stop>
|
|
{{ row.nickname }}
|
|
<CustomerDescriptorProxy :id="row.customerId" />
|
|
</span>
|
|
</template>
|
|
<template #column-ticketFk="{ row }">
|
|
<span class="q-pa-sm link">
|
|
{{ row.id }}
|
|
<TicketDescriptorProxy :id="row.id" />
|
|
</span>
|
|
</template>
|
|
<template #column-alertLevelCode="props">
|
|
<VnSelect
|
|
url="States/editableStates"
|
|
auto-load
|
|
hide-selected
|
|
option-value="id"
|
|
option-label="name"
|
|
v-model="props.row.alertLevelCode"
|
|
v-on="getInputEvents(props)"
|
|
/>
|
|
</template>
|
|
|
|
<template #column-zoneName="{ row }">
|
|
<span class="link">{{ row.zoneName }}</span>
|
|
<ZoneDescriptorProxy :id="row.zoneFk" />
|
|
</template>
|
|
<template #column-quantity="props">
|
|
<VnInputNumber
|
|
v-model.number="props.row.quantity"
|
|
v-on="getInputEvents(props)"
|
|
></VnInputNumber>
|
|
</template>
|
|
</VnTable>
|
|
</template>
|
|
<style lang="scss" scoped>
|
|
.icon-container {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 0.2fr);
|
|
row-gap: 5px; /* Ajusta el espacio entre los iconos según sea necesario */
|
|
}
|
|
.icon-container > * {
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
.list-enter-active,
|
|
.list-leave-active {
|
|
transition: all 1s ease;
|
|
}
|
|
.list-enter-from,
|
|
.list-leave-to {
|
|
opacity: 0;
|
|
background-color: $primary;
|
|
}
|
|
</style>
|