Merge pull request 'Ticket purchase request' (!479) from hyervoni/salix-front-mindshore:feature/TicketPurchaseRequest into dev
gitea/salix-front/pipeline/head This commit looks good Details

Reviewed-on: #479
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
Reviewed-by: Javier Segarra <jsegarra@verdnatura.es>
This commit is contained in:
Javier Segarra 2024-06-25 20:36:52 +00:00
commit 22ccd4adfd
8 changed files with 385 additions and 7 deletions

View File

@ -446,6 +446,7 @@ ticket:
sale: Sale
ticketAdvance: Advance tickets
futureTickets: Future tickets
purchaseRequest: Purchase request
list:
nickname: Nickname
state: State

View File

@ -445,6 +445,7 @@ ticket:
sale: Lineas del pedido
ticketAdvance: Adelantar tickets
futureTickets: Tickets a futuro
purchaseRequest: Petición de compra
list:
nickname: Alias
state: Estado

View File

@ -14,6 +14,7 @@ const routeName = computed(() => route.name);
const searchBarDataKeys = {
TicketSummary: 'TicketSummary',
TicketSale: 'TicketSale',
TicketPurchaseRequest: 'TicketPurchaseRequest',
};
</script>
<template>

View File

@ -0,0 +1,69 @@
<script setup>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
const emit = defineEmits(['onRequestCreated']);
const route = useRoute();
const { t } = useI18n();
const attendersOptions = ref([]);
</script>
<template>
<FetchData
url="TicketRequests/getItemTypeWorker"
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }"
auto-load
@on-fetch="(data) => (attendersOptions = data)"
/>
<FormModelPopup
:title="t('Create request')"
url-create="TicketRequests"
model="CreateTicketRequest"
:form-initial-data="{ ticketFk: route.params.id }"
@on-data-saved="() => emit('onRequestCreated')"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
v-model="data.description"
:label="t('purchaseRequest.description')"
/>
<VnSelect
:label="t('purchaseRequest.atender')"
v-model="data.attenderFk"
:options="attendersOptions"
hide-selected
option-label="nickname"
option-value="id"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnInput
v-model="data.quantity"
:label="t('purchaseRequest.quantity')"
type="number"
min="1"
/>
<VnInput
v-model="data.price"
:label="t('purchaseRequest.price')"
type="number"
min="0"
/>
</VnRow>
</template>
</FormModelPopup>
</template>
<i18n>
es:
Create request: Crear petición de compra
</i18n>

View File

@ -0,0 +1,267 @@
<script setup>
import { ref, computed, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import CrudModel from 'src/components/CrudModel.vue';
import TicketCreateRequest from './TicketCreateRequest.vue';
import { dashIfEmpty } from 'src/filters';
import { toDateFormat } from 'src/filters/date.js';
const route = useRoute();
const { t } = useI18n();
const createTicketRequestDialogRef = ref(null);
const crudModelRef = ref(null);
watch(
() => route.params.id,
async (val) => {
crudModelFilter.where.ticketFk = val;
crudModelRef.value.reload();
}
);
const crudModelFilter = reactive({
include: [
{
relation: 'atender',
scope: {
include: {
relation: 'user',
scope: {
fields: ['nickname'],
},
},
},
},
{
relation: 'requester',
scope: {
include: {
relation: 'user',
scope: {
fields: ['nickname'],
},
},
},
},
{
relation: 'sale',
},
],
fields: [
'id',
'description',
'created',
'requesterFk',
'attenderFk',
'quantity',
'price',
'saleFk',
'isOk',
],
order: ['created ASC'],
where: {
ticketFk: route.params.id,
},
});
const columns = computed(() => [
{
label: t('purchaseRequest.id'),
name: 'id',
field: 'id',
align: 'left',
columnFilter: null,
},
{
label: t('purchaseRequest.description'),
name: 'description',
field: 'description',
align: 'left',
format: (val) => dashIfEmpty(val),
},
{
label: t('purchaseRequest.created'),
name: 'created',
field: 'created',
align: 'left',
format: (val) => toDateFormat(val),
},
{
label: t('purchaseRequest.requester'),
name: 'requester',
align: 'left',
sortable: true,
},
{
label: t('purchaseRequest.atender'),
name: 'atender',
align: 'left',
},
{
label: t('purchaseRequest.quantity'),
name: 'quantity',
align: 'left',
},
{
label: t('purchaseRequest.price'),
name: 'price',
align: 'left',
},
{
label: t('purchaseRequest.saleFk'),
name: 'saleFk',
align: 'left',
},
{
label: t('purchaseRequest.state'),
name: 'state',
field: 'isOk',
align: 'left',
format: (val) => t(getRequestState(val)),
},
{
label: '',
name: 'actions',
align: 'left',
columnFilter: null,
},
]);
const getRequestState = (state) => {
switch (state) {
case null:
return 'New';
case false:
return 'Denied';
case true:
return 'Acepted';
}
};
const isEditable = (isOk) => isOk !== null;
const removeLine = async (row) => crudModelRef.value.remove([row]);
const openCreateModal = () => createTicketRequestDialogRef.value.show();
</script>
<template>
<QPage class="column items-center q-pa-md">
<CrudModel
data-key="PurchaseRequests"
url="TicketRequests"
ref="crudModelRef"
:filter="crudModelFilter"
:order="['created ASC']"
:default-remove="false"
:default-save="false"
:default-reset="false"
:limit="0"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
@row-click="(_, row) => redirectToTicketSummary(row.ticketFk)"
>
<template #body-cell-description="{ row }">
<QTd @click.stop>
<VnInput
v-model="row.description"
@blur="crudModelRef.saveChanges()"
:disable="isEditable(row.isOk)"
/>
</QTd>
</template>
<template #body-cell-requester="{ row }">
<QTd @click.stop>
<QBtn flat color="primary">
{{ row.requester?.user?.nickname }}
<WorkerDescriptorProxy :id="row.requesterFk" />
</QBtn>
</QTd>
</template>
<template #body-cell-atender="{ row }">
<QTd @click.stop>
<QBtn flat color="primary">
{{ row.atender?.user?.nickname }}
<WorkerDescriptorProxy :id="row.attenderFk" />
</QBtn>
</QTd>
</template>
<template #body-cell-quantity="{ row }">
<QTd @click.stop>
<VnInput
v-model="row.quantity"
@blur="crudModelRef.saveChanges()"
:disable="isEditable(row.isOk)"
/>
</QTd>
</template>
<template #body-cell-price="{ row }">
<QTd @click.stop>
<VnInput
v-model="row.price"
@blur="crudModelRef.saveChanges()"
:disable="isEditable(row.isOk)"
/>
</QTd>
</template>
<template #body-cell-saleFk="{ row }">
<QTd @click.stop>
<QBtn v-if="row.saleFk" flat color="primary">
{{ row.sale.itemFk }}
<ItemDescriptorProxy :id="row.sale.itemFk" />
</QBtn>
</QTd>
</template>
<template #body-cell-actions="{ row }">
<QTd>
<QIcon
@click.stop="removeLine(row)"
class="q-ml-sm cursor-pointer"
color="primary"
name="delete"
size="sm"
>
<QTooltip>
{{ t('globals.delete') }}
</QTooltip>
</QIcon>
</QTd>
</template>
</QTable>
</template>
</CrudModel>
<QDialog
ref="createTicketRequestDialogRef"
transition-show="scale"
transition-hide="scale"
>
<TicketCreateRequest @on-request-created="crudModelRef.reload()" />
</QDialog>
<QPageSticky :offset="[20, 20]">
<QBtn @click="openCreateModal()" color="primary" fab icon="add" />
<QTooltip class="text-no-wrap">
{{ t('purchaseRequest.newRequest') }}
</QTooltip>
</QPageSticky>
</QPage>
</template>
<i18n>
es:
New: Nueva
Denied: Denegada
Accepted: Aceptada
</i18n>

View File

@ -22,9 +22,6 @@ ticketSale:
shipped: Shipped
agency: Agency
address: Address
card:
search: Search tickets
searchInfo: You can search by ticket id or alias
advanceTickets:
origin: Origin
destination: Destination
@ -116,3 +113,17 @@ basicData:
negativesConfirmMessage: Negatives are going to be generated, are you sure you want to advance all the lines?
chooseAnOption: Choose an option
unroutedTicket: The ticket has been unrouted
card:
search: Search tickets
searchInfo: You can search by ticket id or alias
purchaseRequest:
id: Id
description: Description
created: Created
requester: Requester
atender: Atender
quantity: Quantity
price: Price
saleFk: Item id
state: State
newRequest: New request

View File

@ -1,3 +1,17 @@
card:
search: Buscar tickets
searchInfo: Buscar tickets por identificador o alias
purchaseRequest:
Id: Id
description: Descripción
created: Fecha creación
requester: Solicitante
atender: Comprador
quantity: Cantidad
price: Precio
saleFk: Id artículo
state: Estado
newRequest: Crear petición
basicData:
next: Siguiente
back: Anterior
@ -115,6 +129,3 @@ ticketSale:
shipped: F. Envío
agency: Agencia
address: Consignatario
card:
search: Buscar tickets
searchInfo: Buscar tickets por identificador o alias

View File

@ -12,7 +12,14 @@ export default {
redirect: { name: 'TicketMain' },
menus: {
main: ['TicketList', 'TicketAdvance', 'TicketFuture'],
card: ['TicketBasicData', 'TicketBoxing', 'TicketSms', 'TicketSale', 'TicketLog'],
card: [
'TicketBasicData',
'TicketBoxing',
'TicketSms',
'TicketSale',
'TicketLog',
'TicketPurchaseRequest',
],
},
children: [
{
@ -121,6 +128,16 @@ export default {
},
component: () => import('src/pages/Ticket/Card/TicketLog.vue'),
},
{
path: 'request',
name: 'TicketPurchaseRequest',
meta: {
title: 'purchaseRequest',
icon: 'vn:buyrequest',
},
component: () =>
import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'),
},
],
},
],