Ticket purchase request
gitea/salix-front/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
William Buezas 2024-06-19 08:16:20 -03:00
parent cc0eb77228
commit b3a4dd3f79
8 changed files with 389 additions and 7 deletions

View File

@ -443,6 +443,7 @@ ticket:
sms: Sms
notes: Notes
sale: Sale
purchaseRequest: Purchase request
list:
nickname: Nickname
state: State

View File

@ -441,6 +441,7 @@ ticket:
sms: Sms
notes: Notas
sale: Lineas del pedido
purchaseRequest: Petición de compra
list:
nickname: Alias
state: Estado

View File

@ -1,17 +1,29 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { computed } from 'vue';
import VnCard from 'components/common/VnCard.vue';
import TicketDescriptor from './TicketDescriptor.vue';
import TicketFilter from '../TicketFilter.vue';
const { t } = useI18n();
const route = useRoute();
const routeName = computed(() => route.name);
const searchBarDataKeys = {
TicketSummary: 'TicketSummary',
TicketPurchaseRequest: 'TicketPurchaseRequest',
};
</script>
<template>
<VnCard
data-key="Ticket"
base-url="Tickets"
:descriptor="TicketDescriptor"
:filter-panel="TicketFilter"
search-data-key="TicketList"
search-url="Tickets/filter"
searchbar-label="Search ticket"
searchbar-info="You can search by ticket id or alias"
:descriptor="TicketDescriptor"
:search-data-key="searchBarDataKeys[routeName]"
:search-custom-route-redirect="routeName"
:searchbar-label="t('card.search')"
:searchbar-info="t('card.searchInfo')"
/>
</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,261 @@
<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) => 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.requester?.user?.nickname }}
<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>

View File

@ -0,0 +1,14 @@
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,2 +1,16 @@
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
Search ticket: Buscar ticket
You can search by ticket id or alias: Puedes buscar por id o alias del ticket

View File

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