Customer description corrections and creation of tickets create view

This commit is contained in:
William Buezas 2024-02-22 10:38:15 -03:00
parent d06271b91d
commit 1b67b7ab36
9 changed files with 348 additions and 48 deletions

View File

@ -170,6 +170,7 @@ export default {
hasDebt: 'Customer has debt',
notChecked: 'Customer not checked',
noWebAccess: 'Web access is disabled',
businessType: 'Business type',
passwordRequirements:
'The password must have at least { length } length characters, {nAlpha} alphabetic characters, {nUpper} capital letters, {nDigits} digits and {nPunct} symbols (Ex: $%&.)\n',
},
@ -492,6 +493,13 @@ export default {
weight: 'Weight',
goTo: 'Go to',
},
create: {
client: 'Client',
address: 'Address',
landed: 'Landed',
warehouse: 'Warehouse',
agency: 'Agency',
},
},
claim: {
pageTitles: {

View File

@ -169,6 +169,7 @@ export default {
hasDebt: 'El cliente tiene riesgo',
notChecked: 'El cliente no está comprobado',
noWebAccess: 'El acceso web está desactivado',
businessType: 'Tipo de negocio',
passwordRequirements:
'La contraseña debe tener al menos { length } caracteres de longitud, {nAlpha} caracteres alfabéticos, {nUpper} letras mayúsculas, {nDigits} dígitos y {nPunct} símbolos (Ej: $%&.)',
},
@ -492,6 +493,13 @@ export default {
weight: 'Peso',
goTo: 'Ir a',
},
create: {
client: 'Cliente',
address: 'Dirección',
landed: 'F. entrega',
warehouse: 'Almacén',
agency: 'Agencia',
},
},
claim: {
pageTitles: {

View File

@ -3,15 +3,14 @@ import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toCurrency } from 'src/filters';
import useCardDescription from 'src/composables/useCardDescription';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
import useCardDescription from 'src/composables/useCardDescription';
import { toCurrency } from 'src/filters';
const $props = defineProps({
id: {
type: Number,
@ -23,8 +22,10 @@ const $props = defineProps({
default: null,
},
});
const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => {
return $props.id || route.params.id;
});
@ -43,54 +44,68 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
:summary="$props.summary"
data-key="customerData"
>
<template #header-extra-action>
<QBtn
round
flat
size="sm"
icon="vn:Person"
color="white"
:to="{ name: 'CustomerList' }"
>
<QTooltip>
{{ t('Go to module index') }}
</QTooltip>
</QBtn>
</template>
<template #menu="{ entity }">
<CustomerDescriptorMenu :customer="entity" />
</template>
<template #body="{ entity }">
<VnLv v-if="entity.salesPersonUser" :label="t('customer.card.salesPerson')">
<VnLv :label="t('customer.card.payMethod')" :value="entity.payMethod.name" />
<VnLv :label="t('customer.card.credit')" :value="toCurrency(entity.credit)" />
<VnLv
:label="t('customer.card.securedCredit')"
:value="entity.creditInsurance ? toCurrency(entity.creditInsurance) : '-'"
/>
<VnLv
:label="t('customer.card.risk')"
:value="entity.debt ? toCurrency(entity.debt) : '-'"
:info="t('customer.summary.descriptorInfo')"
/>
<VnLv :label="t('customer.card.salesPerson')">
<template #value>
<VnUserLink
v-if="entity.salesPersonUser"
:name="entity.salesPersonUser?.name"
:worker-id="entity.salesPersonFk"
/>
<span v-else>-</span>
</template>
</VnLv>
<VnLv :label="t('customer.card.credit')" :value="toCurrency(entity.credit)" />
<VnLv
:label="t('customer.card.risk')"
:value="toCurrency(entity.debt)"
:info="t('customer.summary.descriptorInfo')"
:label="t('customer.card.businessType')"
:value="entity.businessType.description"
/>
<VnLv
:label="t('customer.card.securedCredit')"
:value="toCurrency(entity.creditInsurance)"
/>
<VnLv :label="t('customer.card.payMethod')" :value="entity.payMethod.name" />
<VnLv :label="t('customer.card.debt')" :value="toCurrency(entity.debt)" />
</template>
<template #icons="{ entity }">
<QCardActions>
<QCardActions class="q-gutter-x-md">
<QIcon
v-if="entity.isActive == false"
v-if="!entity.isActive"
name="vn:disabled"
size="xs"
color="primary"
>
<QTooltip>{{ t('customer.card.isDisabled') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.isFreezed == true"
name="vn:frozen"
size="xs"
color="primary"
>
<QIcon v-if="entity.isFreezed" name="vn:frozen" size="xs" color="primary">
<QTooltip>{{ t('customer.card.isFrozen') }}</QTooltip>
</QIcon>
<QIcon
v-if="!entity.account.active"
color="primary"
name="vn:noweb"
size="sm"
v-if="entity.account.active == false"
size="xs"
>
<QTooltip>{{ t('customer.card.webAccountInactive') }}</QTooltip>
</QIcon>
@ -103,13 +118,25 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
<QTooltip>{{ t('customer.card.hasDebt') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.isTaxDataChecked == false"
v-if="!entity.isTaxDataChecked"
name="vn:no036"
size="xs"
color="primary"
>
<QTooltip>{{ t('customer.card.notChecked') }}</QTooltip>
</QIcon>
<QBtn
v-if="entity.unpaid"
flat
size="sm"
icon="vn:noPayMethod"
color="primary"
:to="{ name: 'CustomerUnpaid' }"
>
<QTooltip>
{{ t('Customer unpaid') }}
</QTooltip>
</QBtn>
</QCardActions>
</template>
<template #actions="{ entity }">
@ -123,7 +150,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
icon="vn:ticket"
color="primary"
>
<QTooltip>{{ t('ticketList') }}</QTooltip>
<QTooltip>{{ t('Customer ticket list') }}</QTooltip>
</QBtn>
<QBtn
:to="{
@ -134,35 +161,33 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
icon="vn:invoice-out"
color="primary"
>
<QTooltip>{{ t('invoiceOutList') }}</QTooltip>
<QTooltip>{{ t('Customer invoice out list') }}</QTooltip>
</QBtn>
<QBtn
:to="{
name: 'OrderCreate',
params: { id: entity.id },
query: { clientFk: entity.id },
}"
size="md"
icon="vn:basketadd"
color="primary"
>
<QTooltip>{{ t('invoiceOutList') }}</QTooltip>
<QTooltip>{{ t('New order') }}</QTooltip>
</QBtn>
<QBtn size="md" icon="vn:Person" color="primary">
<QTooltip>{{ t('invoiceOutList') }}</QTooltip>
<QBtn size="md" icon="face" color="primary">
<!-- TODO:: Redirigir a la vista de usuario cuando exista -->
<QTooltip>{{ t('Go to user') }}</QTooltip>
</QBtn>
</QCardActions>
</template>
</CardDescriptor>
</template>
<i18n>
{
"en": {
"ticketList": "Customer ticket list",
"invoiceOutList": "Customer invoice out list"
},
"es": {
"ticketList": "Listado de tickets del cliente",
"invoiceOutList": "Listado de facturas del cliente"
}
}
es:
Go to module index: Ir al índice del módulo
Customer ticket list: Listado de tickets del cliente
Customer invoice out list: Listado de facturas del cliente
New order: Nuevo pedido
Go to user: Ir al usuario
Customer unpaid: Cliente impago
</i18n>

View File

@ -25,7 +25,7 @@ const showSmsDialog = () => {
quasar.dialog({
component: VnSmsDialog,
componentProps: {
phone: $props.customer.phone,
phone: $props.customer.phone || $props.customer.mobile,
promise: sendSms,
},
});
@ -45,7 +45,13 @@ const sendSms = async (payload) => {
<template>
<QItem v-ripple clickable>
<QItemSection>
<RouterLink :to="{ name: 'TicketCreate' }" class="color-vn-text">
<RouterLink
:to="{
name: 'TicketCreate',
query: { clientFk: customer.id },
}"
class="color-vn-text"
>
{{ t('Simple ticket') }}
</RouterLink>
</QItemSection>

View File

@ -1,6 +1,6 @@
<script setup>
import { useRoute } from 'vue-router';
import { reactive, ref } from 'vue';
import { reactive, ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useState } from 'composables/useState';
@ -27,6 +27,23 @@ const clientList = ref([]);
const agencyList = ref([]);
const addressList = ref([]);
const onClientsFetched = async (data) => {
try {
clientList.value = data;
initialFormState.clientFk = Number(route.query?.clientFk) || null;
if (initialFormState.clientFk) {
const { defaultAddressFk } = clientList.value.find(
(client) => client.id === initialFormState.clientFk
);
if (defaultAddressFk) await fetchAddressList(defaultAddressFk);
}
} catch (err) {
console.error('Error fetching clients', err);
}
};
const fetchAddressList = async (addressId) => {
try {
const { data } = await axios.get('addresses', {
@ -47,6 +64,7 @@ const fetchAddressList = async (addressId) => {
return err.response;
}
};
const fetchAgencyList = async (landed, addressFk) => {
if (!landed || !addressFk) {
return;
@ -108,7 +126,7 @@ const orderFilter = {
<template>
<FetchData
url="Clients"
@on-fetch="(data) => (clientList = data)"
@on-fetch="(data) => onClientsFetched(data)"
:filter="{ fields: ['id', 'name', 'defaultAddressFk'] }"
auto-load
/>

View File

@ -168,7 +168,7 @@ async function changeState(value) {
</VnLv>
<VnLv
:label="t('ticket.summary.agency')"
:value="ticket.agencyMode.name"
:value="ticket.agencyMode?.name"
/>
<VnLv :label="t('ticket.summary.zone')" :value="ticket?.zone?.name" />
<VnLv

View File

@ -0,0 +1,227 @@
<script setup>
import { useRoute, useRouter } from 'vue-router';
import { onBeforeMount, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FormModel from 'components/FormModel.vue';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import { useState } from 'composables/useState';
import axios from 'axios';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const state = useState();
const user = state.getUser();
const initialFormState = reactive({
clientId: Number(route.query?.clientFk) || null,
addressId: null,
agencyModeId: null,
warehouseId: user.value.warehouseFk,
landed: null,
});
const clientOptions = ref([]);
const agenciesOptions = ref([]);
const addressesOptions = ref([]);
const warehousesOptions = ref([]);
const selectedClient = ref(null);
onBeforeMount(async () => {
await onClientSelected(initialFormState);
});
const fetchClient = async (formData) => {
try {
const filter = {
include: {
relation: 'defaultAddress',
scope: {
fields: ['id', 'agencyModeFk'],
},
},
where: { id: formData.clientId },
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get('Clients', { params });
const [client] = data;
selectedClient.value = client;
} catch (err) {
console.error('Error fetching client');
}
};
const fetchAddresses = async (formData) => {
try {
if (!formData.clientId) return;
const filter = {
fields: ['nickname', 'street', 'city', 'id'],
where: { isActive: true },
order: 'nickname ASC',
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get(`Clients/${formData.clientId}/addresses`, {
params,
});
addressesOptions.value = data;
const { defaultAddress } = selectedClient.value;
formData.addressId = defaultAddress.id;
console.log();
} catch (err) {
console.error(`Error fetching addresses`, err);
return err.response;
}
};
const onClientSelected = async (formData) => {
await fetchClient(formData);
await fetchAddresses(formData);
};
const fetchAvailableAgencies = async (formData) => {
if (!formData.warehouseId || !formData.addressId || !formData.landed) return;
let params = {
warehouseFk: formData.warehouseId,
addressFk: formData.addressId,
landed: formData.landed,
};
const { data } = await axios.get('Agencies/getAgenciesWithWarehouse', { params });
agenciesOptions.value = data;
const defaultAgency = agenciesOptions.value.find(
(agency) =>
agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk
);
if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk;
};
const redirectToTicketList = (_, { id }) => {
router.push({ name: 'TicketSummary', params: { id } });
};
</script>
<template>
<FetchData
url="Clients"
@on-fetch="(data) => (clientOptions = data)"
:filter="{ fields: ['id', 'name', 'defaultAddressFk'], order: 'id' }"
auto-load
/>
<FetchData
url="Warehouses"
@on-fetch="(data) => (warehousesOptions = data)"
order="name"
auto-load
/>
<VnSubToolbar />
<div class="q-pa-md">
<FormModel
url-create="Tickets/new"
model="ticket"
:form-initial-data="initialFormState"
@on-data-saved="redirectToTicketList"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.client')"
v-model="data.clientId"
:options="clientOptions"
option-value="id"
option-label="name"
hide-selected
@update:model-value="(client) => onClientSelected(data)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt.name }}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt.id}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.address')"
v-model="data.addressId"
:options="addressesOptions"
option-value="id"
option-label="nickname"
hide-selected
:disable="!data.clientId"
@update:model-value="() => fetchAvailableAgencies(data)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt.nickname }}
</QItemLabel>
<QItemLabel caption>
{{ `${scope.opt.street}, ${scope.opt.city}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate
placeholder="dd-mm-aaa"
:label="t('ticket.create.landed')"
v-model="data.landed"
@update:model-value="() => fetchAvailableAgencies(data)"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.warehouse')"
v-model="data.warehouseId"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
@update:model-value="() => fetchAvailableAgencies(data)"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.agency')"
v-model="data.agencyModeId"
:options="agenciesOptions"
option-value="agencyModeFk"
option-label="agencyMode"
hide-selected
:disable="!data.clientId || !data.landed || !data.warehouseId"
/>
</div>
</VnRow>
</template>
</FormModel>
</div>
</template>

View File

@ -122,6 +122,13 @@ function navigate(id) {
</template>
</VnPaginate>
</div>
<QPageSticky :offset="[20, 20]">
<QBtn :to="{ name: 'TicketCreate' }" fab icon="add" color="primary">
<QTooltip>
{{ t('New ticket') }}
</QTooltip>
</QBtn>
</QPageSticky>
</QPage>
</template>
@ -130,4 +137,5 @@ es:
Search ticket: Buscar ticket
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
Zone: Zona
New ticket: Nuevo ticket
</i18n>

View File

@ -37,7 +37,7 @@ export default {
icon: 'vn:ticketAdd',
roles: ['developer'],
},
component: () => import('src/pages/Ticket/TicketList.vue'),
component: () => import('src/pages/Ticket/TicketCreate.vue'),
},
],
},