forked from verdnatura/salix-front
Merge pull request 'Item requests' (!297) from hyervoni/salix-front-mindshore:feature/ItemRequests into dev
Reviewed-on: verdnatura/salix-front#297 Reviewed-by: Javier Segarra <jsegarra@verdnatura.es> Reviewed-by: Alex Moreno <alexm@verdnatura.es>
This commit is contained in:
commit
22478bed48
|
@ -91,6 +91,7 @@ defineExpose({
|
||||||
:title="t('globals.save')"
|
:title="t('globals.save')"
|
||||||
type="submit"
|
type="submit"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
class="q-ml-sm"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
export function getDateQBadgeColor(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 'warning';
|
||||||
|
if (comparation < 0) return 'negative';
|
||||||
|
}
|
|
@ -1120,6 +1120,8 @@ item:
|
||||||
list: List
|
list: List
|
||||||
diary: Diary
|
diary: Diary
|
||||||
tags: Tags
|
tags: Tags
|
||||||
|
create: Create
|
||||||
|
buyRequest: Buy requests
|
||||||
fixedPrice: Fixed prices
|
fixedPrice: Fixed prices
|
||||||
wasteBreakdown: Waste breakdown
|
wasteBreakdown: Waste breakdown
|
||||||
itemCreate: New item
|
itemCreate: New item
|
||||||
|
@ -1166,6 +1168,17 @@ item:
|
||||||
type: Type
|
type: Type
|
||||||
intrastat: Intrastat
|
intrastat: Intrastat
|
||||||
origin: Origin
|
origin: Origin
|
||||||
|
buyRequest:
|
||||||
|
ticketId: 'Ticket ID'
|
||||||
|
shipped: 'Shipped'
|
||||||
|
requester: 'Requester'
|
||||||
|
requested: 'Requested'
|
||||||
|
price: 'Price'
|
||||||
|
attender: 'Atender'
|
||||||
|
item: 'Item'
|
||||||
|
achieved: 'Achieved'
|
||||||
|
concept: 'Concept'
|
||||||
|
state: 'State'
|
||||||
summary:
|
summary:
|
||||||
basicData: 'Basic data'
|
basicData: 'Basic data'
|
||||||
otherData: 'Other data'
|
otherData: 'Other data'
|
||||||
|
|
|
@ -1120,6 +1120,7 @@ item:
|
||||||
diary: Histórico
|
diary: Histórico
|
||||||
tags: Etiquetas
|
tags: Etiquetas
|
||||||
fixedPrice: Precios fijados
|
fixedPrice: Precios fijados
|
||||||
|
buyRequest: Peticiones de compra
|
||||||
wasteBreakdown: Deglose de mermas
|
wasteBreakdown: Deglose de mermas
|
||||||
itemCreate: Nuevo artículo
|
itemCreate: Nuevo artículo
|
||||||
basicData: 'Datos básicos'
|
basicData: 'Datos básicos'
|
||||||
|
@ -1199,6 +1200,17 @@ item:
|
||||||
minSalesQuantity: 'Cantidad mínima de venta'
|
minSalesQuantity: 'Cantidad mínima de venta'
|
||||||
genus: 'Genus'
|
genus: 'Genus'
|
||||||
specie: 'Specie'
|
specie: 'Specie'
|
||||||
|
buyRequest:
|
||||||
|
ticketId: 'ID Ticket'
|
||||||
|
shipped: 'F. envío'
|
||||||
|
requester: 'Solicitante'
|
||||||
|
requested: 'Solicitado'
|
||||||
|
price: 'Precio'
|
||||||
|
attender: 'Comprador'
|
||||||
|
item: 'Artículo'
|
||||||
|
achieved: 'Conseguido'
|
||||||
|
concept: 'Concepto'
|
||||||
|
state: 'Estado'
|
||||||
components:
|
components:
|
||||||
topbar: {}
|
topbar: {}
|
||||||
itemsFilterPanel:
|
itemsFilterPanel:
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, onBeforeMount, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import ItemRequestDenyForm from './ItemRequestDenyForm.vue';
|
||||||
|
import ItemRequestFilter from './ItemRequestFilter.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import { toDateFormat } from 'src/filters/date';
|
||||||
|
import { toCurrency } from 'filters/index';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
|
let filterParams = ref({});
|
||||||
|
const denyFormRef = ref(null);
|
||||||
|
const denyRequestId = ref(null);
|
||||||
|
const denyRequestIndex = ref(null);
|
||||||
|
const itemRequestsOptions = ref([]);
|
||||||
|
const arrayData = useArrayData('ItemRequests', {
|
||||||
|
url: 'TicketRequests/filter',
|
||||||
|
userParams: filterParams,
|
||||||
|
order: ['shippedDate ASC', 'isOk ASC'],
|
||||||
|
});
|
||||||
|
const store = arrayData.store;
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => store.data,
|
||||||
|
(value) => (itemRequestsOptions.value = value)
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.ticketId'),
|
||||||
|
name: 'id',
|
||||||
|
field: 'id',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.shipped'),
|
||||||
|
field: 'shipped',
|
||||||
|
name: 'shipped',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('globals.description'),
|
||||||
|
field: 'description',
|
||||||
|
name: 'description',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.requester'),
|
||||||
|
name: 'requester',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.requested'),
|
||||||
|
field: 'quantity',
|
||||||
|
name: 'requested',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.price'),
|
||||||
|
field: 'price',
|
||||||
|
name: 'price',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
format: (val) => toCurrency(val),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.attender'),
|
||||||
|
field: 'attender',
|
||||||
|
name: 'attender',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.item'),
|
||||||
|
field: 'item',
|
||||||
|
name: 'item',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.achieved'),
|
||||||
|
field: 'achieved',
|
||||||
|
name: 'achieved',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.concept'),
|
||||||
|
field: 'concept',
|
||||||
|
name: 'concept',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('item.buyRequest.state'),
|
||||||
|
field: 'state',
|
||||||
|
name: 'state',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '',
|
||||||
|
name: 'action',
|
||||||
|
align: 'left',
|
||||||
|
columnFilter: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const changeQuantity = async (request) => {
|
||||||
|
try {
|
||||||
|
if (request.saleFk) {
|
||||||
|
const params = {
|
||||||
|
quantity: request.saleQuantity,
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.patch(`Sales/${request.saleFk}`, params);
|
||||||
|
notify(t('globals.dataSaved'), 'positive');
|
||||||
|
confirmRequest(request);
|
||||||
|
} else confirmRequest(request);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error changing quantity:: ', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmRequest = async (request) => {
|
||||||
|
try {
|
||||||
|
if (request.itemFk && request.saleQuantity) {
|
||||||
|
const params = {
|
||||||
|
itemFk: request.itemFk,
|
||||||
|
quantity: request.saleQuantity,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await axios.post(
|
||||||
|
`TicketRequests/${request.id}/confirm`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
request.itemDescription = data.concept;
|
||||||
|
request.isOk = true;
|
||||||
|
notify(t('globals.dataSaved'), 'positive');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error confirming request:: ', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getState = (isOk) => {
|
||||||
|
if (isOk === null) return t('Pending');
|
||||||
|
else if (isOk) return t('Accepted');
|
||||||
|
else return t('Denied');
|
||||||
|
};
|
||||||
|
|
||||||
|
const showDenyRequestForm = (requestId, rowIndex) => {
|
||||||
|
denyRequestId.value = requestId;
|
||||||
|
denyRequestIndex.value = rowIndex;
|
||||||
|
denyFormRef.value.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDenyAccept = (_, responseData) => {
|
||||||
|
itemRequestsOptions.value[denyRequestIndex.value].isOk = responseData.isOk;
|
||||||
|
itemRequestsOptions.value[denyRequestIndex.value].attenderFk =
|
||||||
|
responseData.attenderFk;
|
||||||
|
itemRequestsOptions.value[denyRequestIndex.value].response = responseData.response;
|
||||||
|
denyRequestId.value = null;
|
||||||
|
denyRequestIndex.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
|
stateStore.rightDrawer = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
const today = Date.vnNew();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const nextWeek = Date.vnNew();
|
||||||
|
nextWeek.setHours(23, 59, 59, 59);
|
||||||
|
nextWeek.setDate(nextWeek.getDate() + 7);
|
||||||
|
|
||||||
|
filterParams.value = {
|
||||||
|
from: today,
|
||||||
|
to: nextWeek,
|
||||||
|
state: 'pending',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="ItemRequests"
|
||||||
|
url="TicketRequests/filter"
|
||||||
|
:label="t('globals.search')"
|
||||||
|
:info="t('You can search by Id or alias')"
|
||||||
|
:redirect="false"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#actions-append">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
@click="stateStore.toggleRightDrawer()"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="menu"
|
||||||
|
>
|
||||||
|
<QTooltip bottom anchor="bottom right">
|
||||||
|
{{ t('globals.collapseMenu') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit text-grey-8">
|
||||||
|
<ItemRequestFilter data-key="ItemRequests" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<QTable
|
||||||
|
:rows="itemRequestsOptions"
|
||||||
|
:columns="columns"
|
||||||
|
row-key="id"
|
||||||
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
:no-data-label="t('globals.noResults')"
|
||||||
|
>
|
||||||
|
<template #body-cell-id="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QBtn flat color="primary"> {{ row.ticketFk }}</QBtn>
|
||||||
|
<TicketDescriptorProxy :id="row.ticketFk" />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-shipped="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QBadge
|
||||||
|
v-if="getDateQBadgeColor(row.shipped)"
|
||||||
|
:color="getDateQBadgeColor(row.shipped)"
|
||||||
|
text-color="black"
|
||||||
|
class="q-ma-none"
|
||||||
|
dense
|
||||||
|
style="font-size: 14px"
|
||||||
|
>
|
||||||
|
{{ toDateFormat(row.shipped) }}
|
||||||
|
</QBadge>
|
||||||
|
<span v-else>{{ toDateFormat(row.shipped) }}</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-requester="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QBtn flat dense color="primary"> {{ row.requesterName }}</QBtn>
|
||||||
|
<WorkerDescriptorProxy :id="row.requesterFk" />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-attender="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QBtn flat dense color="primary"> {{ row.attenderName }}</QBtn>
|
||||||
|
<WorkerDescriptorProxy :id="row.attenderFk" />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-item="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<VnInput
|
||||||
|
v-model.number="row.itemFk"
|
||||||
|
type="number"
|
||||||
|
:disable="row.isOk != null"
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-achieved="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<VnInput
|
||||||
|
v-model.number="row.saleQuantity"
|
||||||
|
@blur="changeQuantity(row)"
|
||||||
|
type="number"
|
||||||
|
:disable="!row.itemFk || row.isOk != null"
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-concept="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QBtn flat dense color="primary"> {{ row.itemDescription }}</QBtn>
|
||||||
|
<ItemDescriptorProxy :id="row.itemFk" />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-state="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<span>{{ getState(row.isOk) }}</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-action="{ row, rowIndex }">
|
||||||
|
<QTd>
|
||||||
|
<QIcon
|
||||||
|
v-if="row.response?.length"
|
||||||
|
name="insert_drive_file"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ row.response }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon
|
||||||
|
v-if="row.isOk == null"
|
||||||
|
name="thumb_down"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
class="fill-icon"
|
||||||
|
@click="showDenyRequestForm(row.id, rowIndex)"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Discard') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
<QDialog ref="denyFormRef" transition-show="scale" transition-hide="scale">
|
||||||
|
<ItemRequestDenyForm
|
||||||
|
:request-id="denyRequestId"
|
||||||
|
@on-data-saved="onDenyAccept"
|
||||||
|
/>
|
||||||
|
</QDialog>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Discard: Descartar
|
||||||
|
You can search by Id or alias: Buscar peticiones por identificador o alias
|
||||||
|
Denied: Denegada
|
||||||
|
Accepted: Aceptada
|
||||||
|
Pending: Pendiente
|
||||||
|
</i18n>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, onMounted, nextTick } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FormModelPopup from 'src/components/FormModelPopup.vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
requestId: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
const { t } = useI18n();
|
||||||
|
const textAreaRef = ref(null);
|
||||||
|
const bankEntityFormData = reactive({});
|
||||||
|
|
||||||
|
const onDataSaved = (formData, requestResponse) => {
|
||||||
|
emit('onDataSaved', formData, requestResponse);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
textAreaRef.value.focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormModelPopup
|
||||||
|
:url-create="`TicketRequests/${$props.requestId}/deny`"
|
||||||
|
:title="t('Specify the reasons to deny this request')"
|
||||||
|
:form-initial-data="bankEntityFormData"
|
||||||
|
@on-data-saved="onDataSaved"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
ref="textAreaRef"
|
||||||
|
type="textarea"
|
||||||
|
v-model="data.observation"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Specify the reasons to deny this request: Especifica las razones para descartar la petición
|
||||||
|
</i18n>
|
|
@ -0,0 +1,318 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const stateOptions = [
|
||||||
|
{ code: 'pending', name: t('pending') },
|
||||||
|
{ code: 'accepted', name: t('accepted') },
|
||||||
|
{ code: 'denied', name: t('denied') },
|
||||||
|
];
|
||||||
|
|
||||||
|
const itemTypesOptions = ref([]);
|
||||||
|
const warehousesOptions = ref([]);
|
||||||
|
const workersOptions = ref([]);
|
||||||
|
|
||||||
|
const exprBuilder = (param, value) => {
|
||||||
|
switch (param) {
|
||||||
|
case 'ticketFk':
|
||||||
|
case 'quantity':
|
||||||
|
case 'price':
|
||||||
|
case 'isOk':
|
||||||
|
return { [`tr.${param}`]: value };
|
||||||
|
case 'attenderName':
|
||||||
|
return { [`ua.name`]: value };
|
||||||
|
case 'shipped':
|
||||||
|
return {
|
||||||
|
't.shipped': {
|
||||||
|
between: dateRange(value),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateRange = (value) => {
|
||||||
|
const minHour = new Date(value);
|
||||||
|
minHour.setHours(0, 0, 0, 0);
|
||||||
|
const maxHour = new Date(value);
|
||||||
|
maxHour.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
return [minHour, maxHour];
|
||||||
|
};
|
||||||
|
|
||||||
|
const add = (paramsObj, key) => {
|
||||||
|
if (paramsObj[key] === undefined) {
|
||||||
|
paramsObj[key] = 1;
|
||||||
|
} else {
|
||||||
|
paramsObj[key]++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const decrement = (paramsObj, key) => {
|
||||||
|
if (paramsObj[key] === 0) return;
|
||||||
|
|
||||||
|
paramsObj[key]--;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="TicketRequests/getItemTypeWorker"
|
||||||
|
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }"
|
||||||
|
@on-fetch="(data) => (itemTypesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="warehouses"
|
||||||
|
:filter="{ order: 'name ASC' }"
|
||||||
|
@on-fetch="(data) => (warehousesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:filter="{
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
order: 'name ASC',
|
||||||
|
}"
|
||||||
|
:params="{
|
||||||
|
departmentCodes: ['VT'],
|
||||||
|
}"
|
||||||
|
@on-fetch="(data) => (workersOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:search-button="true"
|
||||||
|
:expr-builder="exprBuilder"
|
||||||
|
>
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span v-if="tag.label !== 'state'">{{ formatFn(tag.value) }}</span>
|
||||||
|
<span v-else>{{ t(`${tag.value}`) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.search"
|
||||||
|
:label="t('params.search')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.ticketFk"
|
||||||
|
:label="t('params.ticketFk')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
v-model="params.attenderFk"
|
||||||
|
:label="t('params.attenderFk')"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="itemTypesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.clientFk"
|
||||||
|
:label="t('params.clientFk')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('params.warehouseFk')"
|
||||||
|
v-model="params.warehouseFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="warehousesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('params.requesterFk')"
|
||||||
|
v-model="params.requesterFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="workersOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption
|
||||||
|
>{{ scope.opt?.nickname }},
|
||||||
|
{{ scope.opt?.code }}</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QCard bordered>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('params.from')"
|
||||||
|
v-model="params.from"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('params.to')"
|
||||||
|
v-model="params.to"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.scopeDays"
|
||||||
|
:label="t('params.scopeDays')"
|
||||||
|
type="number"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
:min="0"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QBtn
|
||||||
|
icon="add"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="12px"
|
||||||
|
@click="add(params, 'scopeDays')"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
icon="remove"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="12px"
|
||||||
|
@click="decrement(params, 'scopeDays')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnInput>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QIcon name="info" style="position: absolute; top: 4px; right: 4px">
|
||||||
|
<QTooltip max-width="300px">
|
||||||
|
{{ t('dateFiltersTooltip') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QCard>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('params.mine')"
|
||||||
|
v-model="params.mine"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('params.state')"
|
||||||
|
v-model="params.state"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="stateOptions"
|
||||||
|
option-value="code"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
search: General search
|
||||||
|
ticketFk: Ticket id
|
||||||
|
attenderFk: Atender
|
||||||
|
clientFk: Client id
|
||||||
|
warehouseFk: Warehouse
|
||||||
|
requesterFk: Salesperson
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
scopeDays: Days onward
|
||||||
|
mine: For me
|
||||||
|
state: State
|
||||||
|
dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time
|
||||||
|
denied: Denied
|
||||||
|
accepted: Accepted
|
||||||
|
pending: Pending
|
||||||
|
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
search: Búsqueda general
|
||||||
|
ticketFk: Id ticket
|
||||||
|
attenderFk: Comprador
|
||||||
|
clientFk: Id cliente
|
||||||
|
warehouseFk: Almacén
|
||||||
|
requesterFk: Comercial
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
scopeDays: Días adelante
|
||||||
|
mine: Para mi
|
||||||
|
state: Estado
|
||||||
|
dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez
|
||||||
|
denied: Denegada
|
||||||
|
accepted: Aceptada
|
||||||
|
pending: Pendiente
|
||||||
|
</i18n>
|
|
@ -13,6 +13,7 @@ import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { toDate } from 'src/filters/index';
|
import { toDate } from 'src/filters/index';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -42,18 +43,6 @@ const getWarehouseName = (id) => {
|
||||||
return warehouses.value.find((warehouse) => warehouse.id === id).name;
|
return warehouses.value.find((warehouse) => warehouse.id === id).name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDateQBadgeColor = (date) => {
|
|
||||||
let today = Date.vnNew();
|
|
||||||
today.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
date = new Date(date);
|
|
||||||
date.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
const timeDifference = today - date;
|
|
||||||
if (timeDifference == 0) return 'warning';
|
|
||||||
if (timeDifference < 0) return 'success';
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
stateStore.rightDrawer = true;
|
stateStore.rightDrawer = true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default {
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'ItemMain' },
|
redirect: { name: 'ItemMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['ItemList', 'WasteBreakdown', 'ItemFixedPrice'],
|
main: ['ItemList', 'WasteBreakdown', 'ItemFixedPrice', 'ItemRequest'],
|
||||||
card: [
|
card: [
|
||||||
'ItemBasicData',
|
'ItemBasicData',
|
||||||
'ItemDiary',
|
'ItemDiary',
|
||||||
|
@ -66,6 +66,15 @@ export default {
|
||||||
'https://grafana.verdnatura.es/d/TTNXQAxVk';
|
'https://grafana.verdnatura.es/d/TTNXQAxVk';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'request',
|
||||||
|
name: 'ItemRequest',
|
||||||
|
meta: {
|
||||||
|
title: 'buyRequest',
|
||||||
|
icon: 'vn:buyrequest',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Item/ItemRequest.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue