feat: apply changes for customerModule

This commit is contained in:
Javier Segarra 2024-10-29 02:15:02 +01:00
parent 458c6b0f42
commit 0de4dfd4f8
17 changed files with 251 additions and 105 deletions

View File

@ -0,0 +1,16 @@
<script setup>
defineProps({ email: { type: [String], default: null } });
</script>
<template>
<QBtn
v-if="email"
flat
round
icon="email"
size="sm"
color="primary"
padding="none"
:href="`mailto:${email}`"
@click.stop
/>
</template>

View File

@ -27,7 +27,7 @@ const addressFilter = {
'isLogifloraAllowed', 'isLogifloraAllowed',
'postalCode', 'postalCode',
], ],
order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'], order: ['isDefaultAddress DESC', 'isActive DESC', 'id DESC', 'nickname ASC'],
include: [ include: [
{ {
relation: 'observations', relation: 'observations',

View File

@ -5,7 +5,7 @@ import { useRoute } from 'vue-router';
import { useAcl } from 'src/composables/useAcl'; import { useAcl } from 'src/composables/useAcl';
import axios from 'axios'; import axios from 'axios';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import FetchData from 'components/FetchData.vue'; import { getClientRisk } from '../composables/getClientRisk';
import { toCurrency, toDate, toDateHourMin } from 'src/filters'; import { toCurrency, toDate, toDateHourMin } from 'src/filters';
import { useState } from 'composables/useState'; import { useState } from 'composables/useState';
@ -16,7 +16,7 @@ import { useVnConfirm } from 'composables/useVnConfirm';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnSubToolbar from 'components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'components/ui/VnSubToolbar.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnFilter from 'components/VnTable/VnFilter.vue';
import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue'; import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue';
import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
@ -25,7 +25,7 @@ const { openConfirmationModal } = useVnConfirm();
const { sendEmail, openReport } = usePrintService(); const { sendEmail, openReport } = usePrintService();
const { t } = useI18n(); const { t } = useI18n();
const { hasAny } = useAcl(); const { hasAny } = useAcl();
const currentBalance = ref({});
const quasar = useQuasar(); const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const state = useState(); const state = useState();
@ -33,28 +33,53 @@ const stateStore = useStateStore();
const user = state.getUser(); const user = state.getUser();
const clientRisk = ref([]); const clientRisk = ref([]);
const companies = ref([]);
const tableRef = ref(); const tableRef = ref();
const companyId = ref(user.value.companyFk); const companyId = ref();
const companyUser = ref(user.value.companyFk);
const balances = ref([]); const balances = ref([]);
const vnFilterRef = ref({}); const vnFilterRef = ref({});
const filter = computed(() => { const filter = computed(() => {
return { return {
clientId: route.params.id, clientId: route.params.id,
companyId: companyId.value ?? user.value.companyFk, companyId: companyId.value ?? companyUser.value,
}; };
}); });
const companyFilterColumn = {
align: 'left',
name: 'companyId',
label: t('Company'),
component: 'select',
attrs: {
url: 'Companies',
optionLabel: 'code',
optionValue: 'id',
sortBy: 'code',
},
columnFilter: {
event: {
remove: () => (companyId.value = null),
'update:modelValue': (newCompanyFk) => {
if (!newCompanyFk) return;
vnFilterRef.value.addFilter(newCompanyFk);
companyUser.value = newCompanyFk;
},
blur: () => !companyId.value && (companyId.value = companyUser.value),
},
},
visible: false,
};
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'right', align: 'left',
name: 'payed', name: 'payed',
label: t('Date'), label: t('Date'),
format: ({ payed }) => toDate(payed), format: ({ payed }) => toDate(payed),
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'right', align: 'left',
name: 'created', name: 'created',
label: t('Creation date'), label: t('Creation date'),
format: ({ created }) => toDateHourMin(created), format: ({ created }) => toDateHourMin(created),
@ -65,7 +90,12 @@ const columns = computed(() => [
label: t('Employee'), label: t('Employee'),
columnField: { columnField: {
component: 'userLink', component: 'userLink',
attrs: ({ row }) => ({ workerId: row.workerFk, name: row.userName }), attrs: ({ row }) => {
return {
workerId: row.workerFk,
name: row.userName,
};
},
}, },
cardVisible: true, cardVisible: true,
}, },
@ -77,13 +107,13 @@ const columns = computed(() => [
class: 'extend', class: 'extend',
}, },
{ {
align: 'right', align: 'left',
name: 'bankFk', name: 'bankFk',
label: t('Bank'), label: t('Bank'),
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'right', align: 'left',
name: 'debit', name: 'debit',
label: t('Debit'), label: t('Debit'),
format: ({ debit }) => debit && toCurrency(debit), format: ({ debit }) => debit && toCurrency(debit),
@ -136,20 +166,37 @@ const columns = computed(() => [
onBeforeMount(() => { onBeforeMount(() => {
stateStore.rightDrawer = true; stateStore.rightDrawer = true;
companyId.value = companyUser.value;
}); });
async function getCurrentBalance(data) { async function getClientRisks() {
currentBalance.value[companyId.value] = { const filter = {
amount: 0, where: { clientFk: route.params.id, companyFk: companyUser.value },
code: companies.value.find((c) => c.id === companyId.value)?.code,
};
for (const balance of data) {
currentBalance.value[balance.companyFk] = {
code: balance.company.code,
amount: balance.amount,
}; };
const { data } = await getClientRisk(filter);
clientRisk.value = data;
return clientRisk.value;
} }
async function getCurrentBalance() {
const currentBalance = (await getClientRisks()).find((balance) => {
return balance.companyFk === companyId.value;
});
return currentBalance && currentBalance.amount;
}
async function onFetch(data) {
balances.value = [];
for (const [index, balance] of data.entries()) {
if (index === 0) {
balance.balance = await getCurrentBalance();
continue;
}
const previousBalance = data[index - 1];
balance.balance =
previousBalance?.balance - (previousBalance?.debit - previousBalance?.credit);
}
balances.value = data;
} }
const showNewPaymentDialog = () => { const showNewPaymentDialog = () => {
@ -169,43 +216,25 @@ const showBalancePdf = ({ id }) => {
</script> </script>
<template> <template>
<FetchData
url="Companies"
auto-load
@on-fetch="(data) => (companies = data)"
></FetchData>
<FetchData
v-if="companies.length > 0"
url="clientRisks"
:filter="{
include: { relation: 'company', scope: { fields: ['code'] } },
where: { clientFk: route.params.id },
}"
auto-load
@on-fetch="getCurrentBalance"
></FetchData>
<VnSubToolbar class="q-mb-md"> <VnSubToolbar class="q-mb-md">
<template #st-data> <template #st-data>
<div class="column justify-center q-px-md q-py-sm"> <div class="column justify-center q-px-md q-py-sm">
<span class="text-bold">{{ t('Total by company') }}</span> <span class="text-bold">{{ t('Total by company') }}</span>
<div class="row justify-center"> <div class="row justify-center" v-if="clientRisk?.length">
{{ currentBalance[companyId]?.code }}: {{ clientRisk[0].company.code }}:
{{ toCurrency(currentBalance[companyId]?.amount) }} {{ toCurrency(clientRisk[0].amount) }}
</div> </div>
</div> </div>
</template> </template>
<template #st-actions> <template #st-actions>
<div> <div>
<VnSelect <VnFilter
:label="t('Company')"
ref="vnFilterRef" ref="vnFilterRef"
v-model="companyId" v-model="companyId"
data-key="CustomerBalance" data-key="CustomerBalance"
:options="companies" :column="companyFilterColumn"
option-label="code" search-url="balance"
option-value="id" />
></VnSelect>
</div> </div>
</template> </template>
</VnSubToolbar> </VnSubToolbar>
@ -219,6 +248,7 @@ const showBalancePdf = ({ id }) => {
:right-search="false" :right-search="false"
:is-editable="false" :is-editable="false"
:column-search="false" :column-search="false"
@on-fetch="onFetch"
:disable-option="{ card: true }" :disable-option="{ card: true }"
auto-load auto-load
> >

View File

@ -49,7 +49,9 @@ const columns = computed(() => [
name: 'credit', name: 'credit',
create: true, create: true,
visible: false, visible: false,
attrs: { columnCreate: {
component: 'number',
required: true,
autofocus: true, autofocus: true,
}, },
}, },

View File

@ -150,7 +150,7 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
</QCardActions> </QCardActions>
</template> </template>
<template #actions="{ entity }"> <template #actions="{ entity }">
<QCardActions class="flex justify-center"> <QCardActions class="flex justify-center" style="padding-inline: 0">
<QBtn <QBtn
:to="{ :to="{
name: 'TicketList', name: 'TicketList',
@ -168,6 +168,23 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
> >
<QTooltip>{{ t('Customer ticket list') }}</QTooltip> <QTooltip>{{ t('Customer ticket list') }}</QTooltip>
</QBtn> </QBtn>
<QBtn
:to="{
name: 'TicketList',
query: {
table: JSON.stringify({
clientFk: entity.id,
}),
createForm: JSON.stringify({ clientId: entity.id }),
},
}"
size="md"
color="primary"
target="_blank"
icon="vn:ticketAdd"
>
<QTooltip>{{ t('New ticket') }}</QTooltip>
</QBtn>
<QBtn <QBtn
:to="{ :to="{
name: 'InvoiceOutList', name: 'InvoiceOutList',
@ -179,6 +196,23 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
> >
<QTooltip>{{ t('Customer invoice out list') }}</QTooltip> <QTooltip>{{ t('Customer invoice out list') }}</QTooltip>
</QBtn> </QBtn>
<QBtn
:to="{
name: 'OrderList',
query: {
table: JSON.stringify({
clientFk: entity.id,
}),
createForm: JSON.stringify({ clientFk: entity.id }),
},
}"
size="md"
target="_blank"
icon="vn:basketadd"
color="primary"
>
<QTooltip>{{ t('New order') }}</QTooltip>
</QBtn>
<QBtn <QBtn
:to="{ :to="{
name: 'AccountSummary', name: 'AccountSummary',
@ -215,6 +249,8 @@ es:
Go to module index: Ir al índice del módulo Go to module index: Ir al índice del módulo
Customer ticket list: Listado de tickets del cliente Customer ticket list: Listado de tickets del cliente
Customer invoice out list: Listado de facturas del cliente Customer invoice out list: Listado de facturas del cliente
New order: Nuevo pedido
New ticket: Nuevo ticket
Go to user: Ir al usuario Go to user: Ir al usuario
Go to supplier: Ir al proveedor Go to supplier: Ir al proveedor
Customer unpaid: Cliente impago Customer unpaid: Cliente impago

View File

@ -8,9 +8,6 @@ import { useQuasar } from 'quasar';
import useNotify from 'src/composables/useNotify'; import useNotify from 'src/composables/useNotify';
import VnSmsDialog from 'src/components/common/VnSmsDialog.vue'; import VnSmsDialog from 'src/components/common/VnSmsDialog.vue';
import TicketCreateDialog from 'src/pages/Ticket/TicketCreateDialog.vue';
import OrderCreateDialog from 'src/pages/Order/Card/OrderCreateDialog.vue';
import { ref } from 'vue';
const $props = defineProps({ const $props = defineProps({
customer: { customer: {
@ -43,34 +40,9 @@ const sendSms = async (payload) => {
notify(error.message, 'positive'); notify(error.message, 'positive');
} }
}; };
const ticketCreateFormDialog = ref(null);
const openTicketCreateForm = () => {
ticketCreateFormDialog.value.show();
};
const orderCreateFormDialog = ref(null);
const openOrderCreateForm = () => {
orderCreateFormDialog.value.show();
};
</script> </script>
<template> <template>
<QItem v-ripple clickable @click="openTicketCreateForm()">
<QItemSection>
{{ t('globals.pageTitles.createTicket') }}
<QDialog ref="ticketCreateFormDialog">
<TicketCreateDialog />
</QDialog>
</QItemSection>
</QItem>
<QItem v-ripple clickable @click="openOrderCreateForm()">
<QItemSection>
{{ t('globals.pageTitles.createOrder') }}
<QDialog ref="orderCreateFormDialog">
<OrderCreateDialog :client-fk="customer.id" />
</QDialog>
</QItemSection>
</QItem>
<QItem v-ripple clickable> <QItem v-ripple clickable>
<QItemSection @click="showSmsDialog()">{{ t('Send SMS') }}</QItemSection> <QItemSection @click="showSmsDialog()">{{ t('Send SMS') }}</QItemSection>
</QItem> </QItem>

View File

@ -53,11 +53,11 @@ function handleLocation(data, location) {
</QIcon> </QIcon>
</template> </template>
</VnInput> </VnInput>
<VnInput :label="t('Tax number')" clearable v-model="data.fi" /> <VnInput :label="t('Tax number')" clearable v-model="data.fi" required />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnInput :label="t('Street')" clearable v-model="data.street" /> <VnInput :label="t('Street')" clearable v-model="data.street" required />
</VnRow> </VnRow>
<VnRow> <VnRow>
@ -68,6 +68,7 @@ function handleLocation(data, location) {
option-label="vat" option-label="vat"
option-value="id" option-value="id"
v-model="data.sageTaxTypeFk" v-model="data.sageTaxTypeFk"
:required="data.isTaxDataChecked"
/> />
<VnSelect <VnSelect
:label="t('Sage transaction type')" :label="t('Sage transaction type')"
@ -76,6 +77,7 @@ function handleLocation(data, location) {
option-label="transaction" option-label="transaction"
option-value="id" option-value="id"
v-model="data.sageTransactionTypeFk" v-model="data.sageTransactionTypeFk"
:required="data.isTaxDataChecked"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -96,6 +98,7 @@ function handleLocation(data, location) {
:roles-allowed-to-create="['deliveryAssistant', 'administrative']" :roles-allowed-to-create="['deliveryAssistant', 'administrative']"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
:location="data" :location="data"
:required="true"
@update:model-value="(location) => handleLocation(data, location)" @update:model-value="(location) => handleLocation(data, location)"
/> />
</VnRow> </VnRow>

View File

@ -80,6 +80,11 @@ const columns = computed(() => [
align: 'left', align: 'left',
name: 'amount', name: 'amount',
label: t('Amount'), label: t('Amount'),
columnCreate: {
component: 'number',
autofocus: true,
required: true,
},
format: ({ amount }) => toCurrency(amount), format: ({ amount }) => toCurrency(amount),
create: true, create: true,
}, },

View File

@ -56,6 +56,7 @@ const columns = computed(() => [
label: t('Period'), label: t('Period'),
create: true, create: true,
...componentColumn('number'), ...componentColumn('number'),
required: true,
}, },
{ {
align: 'left', align: 'left',

View File

@ -1,13 +1,15 @@
<script setup> <script setup>
import { computed, ref } from 'vue'; import { computed, ref, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { toCurrency, toPercentage, toDate } from 'src/filters'; import { toCurrency, toPercentage, toDate } from 'src/filters';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
import { getUrl } from 'src/composables/getUrl';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import VnLinkMail from 'src/components/ui/VnLinkMail.vue';
import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryTable.vue'; import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryTable.vue';
import VnTitle from 'src/components/common/VnTitle.vue'; import VnTitle from 'src/components/common/VnTitle.vue';
import VnRow from 'src/components/ui/VnRow.vue'; import VnRow from 'src/components/ui/VnRow.vue';
@ -25,6 +27,11 @@ const $props = defineProps({
const entityId = computed(() => $props.id || route.params.id); const entityId = computed(() => $props.id || route.params.id);
const customer = computed(() => summary.value.entity); const customer = computed(() => summary.value.entity);
const summary = ref(); const summary = ref();
const clientUrl = ref();
onMounted(async () => {
clientUrl.value = (await getUrl('client/')) + entityId.value + '/';
});
const balanceDue = computed(() => { const balanceDue = computed(() => {
return ( return (
@ -67,6 +74,7 @@ const sumRisk = ({ clientRisks }) => {
ref="summary" ref="summary"
:url="`Clients/${entityId}/summary`" :url="`Clients/${entityId}/summary`"
data-key="CustomerSummary" data-key="CustomerSummary"
module-name="Customer"
> >
<template #body="{ entity }"> <template #body="{ entity }">
<QCard class="vn-one"> <QCard class="vn-one">
@ -89,7 +97,11 @@ const sumRisk = ({ clientRisks }) => {
<VnLinkPhone :phone-number="entity.mobile" /> <VnLinkPhone :phone-number="entity.mobile" />
</template> </template>
</VnLv> </VnLv>
<VnLv :label="t('customer.summary.email')" :value="entity.email" copy /> <VnLv :value="entity.email" copy
><template #label>
{{ t('customer.summary.email') }}
<VnLinkMail email="entity.email"></VnLinkMail> </template
></VnLv>
<VnLv <VnLv
:label="t('customer.summary.salesPerson')" :label="t('customer.summary.salesPerson')"
:value="entity?.salesPersonUser?.name" :value="entity?.salesPersonUser?.name"
@ -166,7 +178,7 @@ const sumRisk = ({ clientRisks }) => {
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle <VnTitle
:url="`#/customer/${entityId}/billing-data`" :url="`#/customer/${entityId}/billing-data`"
:text="t('customer.summary.payMethodFk')" :text="t('customer.summary.billingData')"
/> />
<VnLv <VnLv
:label="t('customer.summary.payMethod')" :label="t('customer.summary.payMethod')"
@ -222,6 +234,7 @@ const sumRisk = ({ clientRisks }) => {
</QCard> </QCard>
<QCard class="vn-one" v-if="entity.account"> <QCard class="vn-one" v-if="entity.account">
<VnTitle <VnTitle
target="_blank"
:url="`${grafanaUrl}/d/adjlxzv5yjt34d/analisis-de-clientes-7c-crm?orgId=1&var-clientFk=${entityId}`" :url="`${grafanaUrl}/d/adjlxzv5yjt34d/analisis-de-clientes-7c-crm?orgId=1&var-clientFk=${entityId}`"
:text="t('customer.summary.businessData')" :text="t('customer.summary.businessData')"
icon="vn:grafana" icon="vn:grafana"
@ -235,6 +248,7 @@ const sumRisk = ({ clientRisks }) => {
:value="toCurrency(entity?.mana?.mana)" :value="toCurrency(entity?.mana?.mana)"
/> />
<VnLv <VnLv
v-if="entity.claimsRatio"
:label="t('customer.summary.priceIncreasingRate')" :label="t('customer.summary.priceIncreasingRate')"
:value="toPercentage(priceIncreasingRate)" :value="toPercentage(priceIncreasingRate)"
/> />
@ -243,12 +257,14 @@ const sumRisk = ({ clientRisks }) => {
:value="toCurrency(entity?.averageInvoiced?.invoiced)" :value="toCurrency(entity?.averageInvoiced?.invoiced)"
/> />
<VnLv <VnLv
v-if="entity.claimsRatio"
:label="t('customer.summary.claimRate')" :label="t('customer.summary.claimRate')"
:value="toPercentage(claimRate)" :value="toPercentage(claimRate)"
/> />
</QCard> </QCard>
<QCard class="vn-one" v-if="entity.account"> <QCard class="vn-one" v-if="entity.account">
<VnTitle <VnTitle
target="_blank"
:url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`" :url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`"
:text="t('customer.summary.payMethodFk')" :text="t('customer.summary.payMethodFk')"
icon="vn:grafana" icon="vn:grafana"
@ -268,10 +284,12 @@ const sumRisk = ({ clientRisks }) => {
/> />
<VnLv <VnLv
v-if="entity.creditInsurance"
:label="t('customer.summary.securedCredit')" :label="t('customer.summary.securedCredit')"
:value="toCurrency(entity.creditInsurance)" :value="toCurrency(entity.creditInsurance)"
:info="t('customer.summary.securedCreditInfo')" :info="t('customer.summary.securedCreditInfo')"
/> />
<VnLv <VnLv
:label="t('customer.summary.balance')" :label="t('customer.summary.balance')"
:value="toCurrency(sumRisk(entity)) || toCurrency(0)" :value="toCurrency(sumRisk(entity)) || toCurrency(0)"
@ -301,7 +319,7 @@ const sumRisk = ({ clientRisks }) => {
:value="entity.recommendedCredit" :value="entity.recommendedCredit"
/> />
</QCard> </QCard>
<QCard> <QCard class="vn-one">
<VnTitle :text="t('Latest tickets')" /> <VnTitle :text="t('Latest tickets')" />
<CustomerSummaryTable /> <CustomerSummaryTable />
</QCard> </QCard>

View File

@ -68,7 +68,6 @@ const columns = computed(() => [
fields: ['id', 'name'], fields: ['id', 'name'],
where: { role: 'salesPerson' }, where: { role: 'salesPerson' },
optionFilter: 'firstName', optionFilter: 'firstName',
useLike: false,
}, },
create: false, create: false,
columnField: { columnField: {
@ -429,9 +428,10 @@ function handleLocation(data, location) {
:params="{ :params="{
departmentCodes: ['VT', 'shopping'], departmentCodes: ['VT', 'shopping'],
}" }"
:fields="['id', 'nickname']" :fields="['id', 'nickname', 'code']"
sort-by="nickname ASC" sort-by="nickname ASC"
:use-like="false" option-label="nickname"
option-value="id"
emit-value emit-value
auto-load auto-load
> >

View File

@ -85,15 +85,26 @@ function handleLocation(data, location) {
<QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" /> <QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
<VnRow> <VnRow>
<VnInput :label="t('Consignee')" clearable v-model="data.nickname" /> <VnInput
:label="t('Consignee')"
required
clearable
v-model="data.nickname"
/>
<VnInput :label="t('Street address')" clearable v-model="data.street" /> <VnInput
:label="t('Street address')"
clearable
v-model="data.street"
required
/>
</VnRow> </VnRow>
<VnLocation <VnLocation
:rules="validate('Worker.postcode')" :rules="validate('Worker.postcode')"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
v-model="data.location" v-model="data.location"
:required="true"
@update:model-value="(location) => handleLocation(data, location)" @update:model-value="(location) => handleLocation(data, location)"
/> />

View File

@ -3,7 +3,7 @@ import { onBeforeMount, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import axios from 'axios'; import axios from 'axios';
import { getClientRisk } from '../composables/getClientRisk';
import { useDialogPluginComponent } from 'quasar'; import { useDialogPluginComponent } from 'quasar';
import { usePrintService } from 'composables/usePrintService'; import { usePrintService } from 'composables/usePrintService';
@ -158,9 +158,7 @@ async function getAmountPaid() {
}, },
}; };
const { data } = await axios(`ClientRisks`, { const { data } = await getClientRisk(filter);
params: { filter: JSON.stringify(filter) },
});
initialData.amountPaid = (data?.length && data[0].amount) || undefined; initialData.amountPaid = (data?.length && data[0].amount) || undefined;
} }
</script> </script>
@ -241,7 +239,7 @@ async function getAmountPaid() {
</QItem> </QItem>
</template> </template>
</VnSelect> </VnSelect>
<VnInput <VnInputNumber
:label="t('Amount')" :label="t('Amount')"
:required="true" :required="true"
@update:model-value="calculateFromAmount($event)" @update:model-value="calculateFromAmount($event)"
@ -254,7 +252,7 @@ async function getAmountPaid() {
{{ t('Compensation') }} {{ t('Compensation') }}
</div> </div>
<VnRow> <VnRow>
<VnInput <VnInputNumber
:label="t('Compensation account')" :label="t('Compensation account')"
clearable clearable
v-model="data.compensationAccount" v-model="data.compensationAccount"

View File

@ -63,7 +63,7 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
format: (row) => row.agencyMode.name, format: (row) => dashIfEmpty(row.agencyMode?.name),
columnClass: 'expand', columnClass: 'expand',
label: t('Agency'), label: t('Agency'),
}, },
@ -111,7 +111,11 @@ const columns = computed(() => [
{ {
title: t('customer.summary.goToLines'), title: t('customer.summary.goToLines'),
icon: 'vn:lines', icon: 'vn:lines',
action: ({ id }) => router.push({ params: { id }, name: 'TicketSale' }), action: ({ id }) =>
window.open(
router.resolve({ params: { id }, name: 'TicketSale' }).href,
'_blank'
),
isPrimary: true, isPrimary: true,
}, },
{ {
@ -150,6 +154,8 @@ const setShippedColor = (date) => {
if (difference == 0) return 'warning'; if (difference == 0) return 'warning';
if (difference < 0) return 'success'; if (difference < 0) return 'success';
}; };
const rowClick = ({ id }) =>
window.open(router.resolve({ params: { id }, name: 'TicketSummary' }).href, '_blank');
const getItemPackagingType = (ticketSales) => { const getItemPackagingType = (ticketSales) => {
if (!ticketSales?.length) return '-'; if (!ticketSales?.length) return '-';
@ -177,13 +183,15 @@ const getItemPackagingType = (ticketSales) => {
:column-search="false" :column-search="false"
url="Tickets" url="Tickets"
:columns="columns" :columns="columns"
search-url="tickets" append-params="false"
:without-header="true" :without-header="true"
auto-load auto-load
:row-click="rowClick"
order="shipped DESC, id" order="shipped DESC, id"
:disable-option="{ card: true, table: true }" :disable-option="{ card: true, table: true }"
class="full-width" class="full-width"
:disable-infinite-scroll="true" :disable-infinite-scroll="true"
search-url="tickets"
> >
<template #column-nickname="{ row }"> <template #column-nickname="{ row }">
<span class="link"> <span class="link">

View File

@ -0,0 +1,12 @@
import axios from 'axios';
export async function getClientRisk(_filter) {
const filter = {
..._filter,
include: { relation: 'company', scope: { fields: ['code'] } },
};
return await axios(`ClientRisks`, {
params: { filter: JSON.stringify(filter) },
});
}

View File

@ -28,7 +28,7 @@ describe('Client list', () => {
cy.get('.q-mt-lg > .q-btn--standard').click(); cy.get('.q-mt-lg > .q-btn--standard').click();
cy.checkNotification('Data created'); cy.checkNotification('created');
cy.url().should('include', '/summary'); cy.url().should('include', '/summary');
}); });
it('Client list search client', () => { it('Client list search client', () => {
@ -43,4 +43,21 @@ describe('Client list', () => {
cy.url().should('include', `/customer/${id}/summary`); cy.url().should('include', `/customer/${id}/summary`);
}); });
}); });
it('Client founded create ticket', () => {
const search = 'Jessica Jones';
cy.searchByLabel('Name', search);
cy.clickButtonsDescriptor(2);
cy.waitForElement('#formModel');
cy.waitForElement('.q-form');
cy.checkValueForm(1, search);
});
it('Client founded create order', () => {
const search = 'Jessica Jones';
cy.searchByLabel('Name', search);
cy.clickButtonsDescriptor(4);
cy.waitForElement('#formModel');
cy.waitForElement('.q-form');
cy.checkValueForm(2, search);
});
}); });

View File

@ -264,6 +264,12 @@ Cypress.Commands.add('openActionsDescriptor', () => {
cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click(); cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click();
}); });
Cypress.Commands.add('clickButtonsDescriptor', (id) => {
cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`)
.invoke('removeAttr', 'target')
.click();
});
Cypress.Commands.add('openUserPanel', () => { Cypress.Commands.add('openUserPanel', () => {
cy.get( cy.get(
'.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image' '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image'
@ -274,14 +280,25 @@ Cypress.Commands.add('openActions', (row) => {
cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
}); });
Cypress.Commands.add('checkNotification', (text) => { Cypress.Commands.add('checkNotification', (type) => {
cy.get('.q-notification') const values = {
.should('be.visible') created: 'Data created',
.last() updated: 'Data saved',
.then(($lastNotification) => { deleted: 'Data deleted',
if (!Cypress.$($lastNotification).text().includes(text)) };
throw new Error(`Notification not found: "${text}"`); cy.get('.q-notification__message').should('have.text', values[type]);
}); });
Cypress.Commands.add('checkValueForm', (id, search) => {
cy.get(
`.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input`
).should('have.value', search);
});
Cypress.Commands.add('checkValueSelectForm', (id, search) => {
cy.get(
`.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input`
).should('have.value', search);
}); });
Cypress.Commands.add('searchByLabel', (label, value) => { Cypress.Commands.add('searchByLabel', (label, value) => {