0
0
Fork 0

Solucion a comentarios 34

This commit is contained in:
carlosfonseca 2024-03-12 16:24:27 -05:00
parent 5ff90d127b
commit 38d3d49ecb
19 changed files with 313 additions and 409 deletions

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -1,3 +0,0 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=C:\Users\cmari\Downloads\Salix-font1.68.zip

View File

@ -176,11 +176,11 @@ export default {
securedCredit: 'Secured credit', securedCredit: 'Secured credit',
payMethod: 'Pay method', payMethod: 'Pay method',
debt: 'Debt', debt: 'Debt',
isDisabled: 'Customer is disabled', isFrozen: 'Customer frozen',
isFrozen: 'Customer is frozen',
webAccountInactive: 'Web account inactive',
hasDebt: 'Customer has debt', hasDebt: 'Customer has debt',
notChecked: 'Customer not checked', isDisabled: 'Customer inactive',
notChecked: 'Customer no checked',
webAccountInactive: 'Web account inactive',
noWebAccess: 'Web access is disabled', noWebAccess: 'Web access is disabled',
businessType: 'Business type', businessType: 'Business type',
passwordRequirements: passwordRequirements:

View File

@ -175,11 +175,11 @@ export default {
securedCredit: 'Crédito asegurado', securedCredit: 'Crédito asegurado',
payMethod: 'Método de pago', payMethod: 'Método de pago',
debt: 'Riesgo', debt: 'Riesgo',
isDisabled: 'El cliente está desactivado', isFrozen: 'Cliente congelado',
isFrozen: 'El cliente está congelado', hasDebt: 'Cliente con riesgo',
isDisabled: 'Cliente inactivo',
notChecked: 'Cliente no comprobado',
webAccountInactive: 'Sin acceso web', webAccountInactive: 'Sin acceso web',
hasDebt: 'El cliente tiene riesgo',
notChecked: 'El cliente no está comprobado',
noWebAccess: 'El acceso web está desactivado', noWebAccess: 'El acceso web está desactivado',
businessType: 'Tipo de negocio', businessType: 'Tipo de negocio',
passwordRequirements: passwordRequirements:

View File

@ -46,62 +46,6 @@ const filter = {
where: { clientFk: route.params.id, companyFk: user.value.companyFk }, where: { clientFk: route.params.id, companyFk: user.value.companyFk },
}; };
const tableColumnComponents = {
date: {
component: 'span',
props: () => {},
event: () => {},
},
creationDate: {
component: 'span',
props: () => {},
event: () => {},
},
employee: {
component: QBtn,
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
reference: {
component: 'div',
props: () => {},
event: () => {},
},
bank: {
component: 'span',
props: () => {},
event: () => {},
},
debit: {
component: 'span',
props: () => {},
event: () => {},
},
havings: {
component: 'span',
props: () => {},
event: () => {},
},
balance: {
component: 'span',
props: () => {},
event: () => {},
},
conciliated: {
component: QCheckbox,
props: (prop) => ({
disable: true,
'model-value': Boolean(prop.value),
}),
event: () => {},
},
actions: {
component: 'div',
props: () => {},
event: () => {},
},
};
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left', align: 'left',
@ -151,8 +95,8 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
field: (value) => value.debit - value.credit, field: 'balance',
format: (value) => toCurrency(value), format: (value) => value && toCurrency(value),
label: t('Balance'), label: t('Balance'),
name: 'balance', name: 'balance',
}, },
@ -190,6 +134,16 @@ const getData = () => {
}; };
const onFetch = (data) => { const onFetch = (data) => {
if (clientRisks.value?.length && data.length) {
data[0].balance = clientRisks.value[0]?.amount || 0;
data.reduce((prev, curr) => {
const netMovement = prev.debit - prev.credit;
curr.balance = prev.balance - netMovement;
return curr;
});
}
receiptsData.value = data; receiptsData.value = data;
}; };
@ -213,10 +167,10 @@ const updateCompanyId = (id) => {
getData(); getData();
}; };
const saveFieldValue = async (event) => { const saveFieldValue = async (row) => {
try { try {
const payload = { description: event.value }; const payload = { description: row.description };
await axios.patch(`Receipts/${event.key}`, payload); await axios.patch(`Receipts/${row.id}`, payload);
} catch (err) { } catch (err) {
return err; return err;
} }
@ -228,13 +182,6 @@ const sendEmailAction = () => {
</script> </script>
<template> <template>
<FetchData
:filter="filterCompanies"
@on-fetch="(data) => (companiesOptions = data)"
auto-load
url="Companies"
/>
<FetchData <FetchData
:filter="filter" :filter="filter"
@on-fetch="(data) => (clientRisks = data)" @on-fetch="(data) => (clientRisks = data)"
@ -242,6 +189,12 @@ const sendEmailAction = () => {
ref="clientRisksRef" ref="clientRisksRef"
url="ClientRisks" url="ClientRisks"
/> />
<FetchData
:filter="filterCompanies"
@on-fetch="(data) => (companiesOptions = data)"
auto-load
url="Companies"
/>
<VnPaginate <VnPaginate
auto-load auto-load
@ -261,116 +214,91 @@ const sendEmailAction = () => {
class="full-width q-mt-md" class="full-width q-mt-md"
row-key="id" row-key="id"
> >
<template #body-cell="props"> <template #body-cell-employee="{ row }">
<QTd :props="props"> <QTd auto-width @click.stop>
<QTr :props="props" class="cursor-pointer"> <QBtn color="blue" flat no-caps>{{ row.userName }}</QBtn>
<component <WorkerDescriptorProxy :id="row.clientFk" />
:is="tableColumnComponents[props.col.name].component" </QTd>
class="col-content" </template>
v-bind="
tableColumnComponents[props.col.name].props(props)
"
@click="
tableColumnComponents[props.col.name].event(props)
"
>
<template
v-if="
props.col.name !== 'conciliated' &&
props.col.name !== 'reference' &&
props.col.name !== 'actions'
"
>
{{ props.value }}
</template>
<template v-if="props.col.name === 'reference'"> <template #body-cell-reference="{ row }">
<div v-if="props.row.isInvoice"> <QTd auto-width @click.stop v-if="row.isInvoice">
<QBtn color="blue" dense flat> <QBtn color="blue" dense flat>
{{ t('bill', { ref: props.value }) }} {{ t('bill', { ref: row.description }) }}
</QBtn> </QBtn>
<InvoiceOutDescriptorProxy <InvoiceOutDescriptorProxy :id="row.id" />
:id="props.row.id" </QTd>
v-if="props.col.name === 'reference'" <QTd v-else>
/> <VnInput
</div> @keyup.enter="saveFieldValue(row)"
<VnInput autofocus
@keyup.enter="saveFieldValue(props)" clearable
autofocus dense
clearable v-model="row.description"
dense />
v-model="props.row.description" </QTd>
v-else </template>
/>
</template>
<template <template #body-cell-conciliated="{ row }">
v-if=" <QTd align="center">
props.col.name === 'actions' && <QCheckbox :model-value="row.isConciliate === 1" />
props.row.isCompensation </QTd>
" </template>
>
<QIcon <template #body-cell-actions="{ row }">
@click.stop="showDialog = true" <QTd align="center">
class="q-ml-md" <QIcon
color="primary" @click.stop="showDialog = true"
name="add" class="q-ml-md"
size="sm" color="primary"
name="add"
size="sm"
v-if="row.isCompensation"
>
<QTooltip>
{{ t('Send compensation') }}
</QTooltip>
</QIcon>
<QDialog v-model="showDialog">
<QCard class="q-pa-sm">
<QCardSection>
<span
ref="closeButton"
class="flex justify-end color-vn-label"
v-close-popup
> >
<QTooltip> <QIcon name="close" size="sm" />
{{ t('Send compensation') }} </span>
</QTooltip> <div class="text-h6">
</QIcon> {{ t('Send compensation') }}
</div>
<QDialog v-model="showDialog"> </QCardSection>
<QCard class="q-pa-sm"> <QCardSection>
<QCardSection> <div>
<span {{
ref="closeButton" t(
class="flex justify-end color-vn-label" 'Do you want to report compensation to the client by mail?'
v-close-popup )
> }}
<QIcon name="close" size="sm" /> </div>
</span> </QCardSection>
<div class="text-h6"> <QCardActions class="flex justify-end q-mb-sm">
{{ t('Send compensation') }} <QBtn
</div> :label="t('globals.cancel')"
</QCardSection> color="primary"
<QCardSection> flat
<div> v-close-popup
{{ />
t( <QBtn
'Do you want to report compensation to the client by mail?' :label="t('globals.save')"
) @click="sendEmailAction"
}} class="q-ml-sm"
</div> color="primary"
</QCardSection> />
<QCardActions </QCardActions>
class="flex justify-end q-mb-sm" </QCard>
> </QDialog>
<QBtn
:label="t('globals.cancel')"
color="primary"
flat
v-close-popup
/>
<QBtn
:label="t('globals.save')"
@click="sendEmailAction"
class="q-ml-sm"
color="primary"
/>
</QCardActions>
</QCard>
</QDialog>
</template>
<WorkerDescriptorProxy
:id="props.row.clientFk"
v-if="props.col.name === 'userName'"
/>
</component>
</QTr>
</QTd> </QTd>
</template> </template>
</QTable> </QTable>

View File

@ -92,87 +92,91 @@ const updateData = () => {
'is-active': !item.finished, 'is-active': !item.finished,
}" }"
> >
<QCardSection class="flex q-py-none"> <QCardSection
<div class="full-width flex justify-between q-py-none"
class="flex items-center q-ml-md cursor-pointer" >
v-if="!item.finished" <div class="width-state flex">
> <div
<QIcon class="flex items-center cursor-pointer q-mr-md"
@click.stop="openDialog(item)" v-if="!item.finished"
color="primary"
name="lock"
size="md"
> >
<QTooltip>{{ t('Close contract') }}</QTooltip> <QIcon
</QIcon> @click.stop="openDialog(item)"
</div> color="primary"
name="lock"
size="md"
style="font-variation-settings: 'FILL' 1"
>
<QTooltip>{{ t('Close contract') }}</QTooltip>
</QIcon>
</div>
<div class="q-ml-lg"> <div>
<div class="flex q-mb-xs"> <div class="flex q-mb-xs">
<div class="q-mr-sm color-vn-label"> <div class="q-mr-sm color-vn-label">
{{ t('Since') }}: {{ t('Since') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.started) }}
</div>
</div> </div>
<div class="text-weight-bold"> <div class="flex">
{{ toDate(item.started) }} <div class="q-mr-sm color-vn-label">
{{ t('To') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.finished) }}
</div>
</div> </div>
</div> </div>
<div class="flex"> </div>
<div class="q-mr-sm color-vn-label">
{{ t('To') }}: <QSeparator vertical />
<div class="width-data flex">
<div
class="full-width flex justify-between items-center"
v-if="item?.insurances.length"
>
<div class="flex">
<div class="color-vn-label q-mr-xs">
{{ t('Credit') }}:
</div>
<div class="text-weight-bold">
{{ item.insurances[0].credit }}
</div>
</div> </div>
<div class="text-weight-bold"> <div class="flex">
{{ toDate(item.finished) }} <div class="color-vn-label q-mr-xs">
{{ t('Grade') }}:
</div>
<div class="text-weight-bold">
{{ item.insurances[0].grade || '-' }}
</div>
</div>
<div class="flex">
<div class="color-vn-label q-mr-xs">
{{ t('Date') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.insurances[0].created) }}
</div>
</div>
<div class="flex items-center cursor-pointer">
<QIcon
@click.stop="openViewCredit(item)"
color="primary"
name="preview"
size="md"
>
<QTooltip>{{
t('View credits')
}}</QTooltip>
</QIcon>
</div> </div>
</div> </div>
</div> </div>
</QCardSection> </QCardSection>
<QSeparator class="q-mx-lg" vertical />
<div class="flex">
<div
class="flex items-center"
v-if="item?.insurances.length"
>
<div class="flex q-mr-xl">
<div class="q-mr-sm color-vn-label">
{{ t('Credit') }}:
</div>
<div class="text-weight-bold">
{{ item.insurances[0].credit }}
</div>
</div>
<div class="flex q-mr-xl">
<div class="q-mr-sm color-vn-label">
{{ t('Grade') }}:
</div>
<div class="text-weight-bold">
{{ item.insurances[0].grade || '-' }}
</div>
</div>
<div class="flex">
<div class="q-mr-sm color-vn-label">
{{ t('Date') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.insurances[0].created) }}
</div>
</div>
</div>
<div
class="flex items-center q-ml-lg q-mr-md cursor-pointer"
>
<QIcon
@click.stop="openViewCredit(item)"
color="primary"
name="preview"
size="md"
>
<QTooltip>{{ t('View credits') }}</QTooltip>
</QIcon>
</div>
</div>
</QCard> </QCard>
</div> </div>
<h5 class="flex justify-center color-vn-label" v-else> <h5 class="flex justify-center color-vn-label" v-else>
@ -207,6 +211,12 @@ const updateData = () => {
.is-active { .is-active {
background-color: var(--vn-light-gray); background-color: var(--vn-light-gray);
} }
.width-state {
width: 30%;
}
.width-data {
width: 65%;
}
</style> </style>
<i18n> <i18n>

View File

@ -95,7 +95,6 @@ const columns = computed(() => [
/> />
<FormModel <FormModel
:form-initial-data="{}"
:observe-form-changes="false" :observe-form-changes="false"
:url-create="`Clients/${route.params.id}/setRating`" :url-create="`Clients/${route.params.id}/setRating`"
> >

View File

@ -139,39 +139,29 @@ const onSubmit = async () => {
<QCard class="card-width q-pa-lg"> <QCard class="card-width q-pa-lg">
<QCardSection> <QCardSection>
<QForm> <QForm>
<VnRow class="row q-gutter-md q-mb-md"> <QCheckbox :label="t('Enable web access')" v-model="active" />
<div class="col">
<QCheckbox :label="t('Enable web access')" v-model="active" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <div class="q-px-sm">
<div class="col"> <VnInput :label="t('User')" clearable v-model="name" />
<VnInput :label="t('User')" clearable v-model="name" /> <VnInput
</div> :label="t('Recovery email')"
</VnRow> :rules="validate('client.email')"
clearable
<VnRow class="row q-gutter-md q-mb-md"> type="email"
<div class="col"> v-model="email"
<VnInput class="q-mt-sm"
:label="t('Recovery email')" >
:rules="validate('client.email')" <template #append>
clearable <QIcon name="info" class="cursor-pointer">
type="email" <QTooltip>{{
v-model="email" t(
> 'This email is used for user to regain access their account'
<template #append> )
<QIcon name="info" class="cursor-pointer"> }}</QTooltip>
<QTooltip>{{ </QIcon>
t( </template>
'This email is used for user to regain access their account' </VnInput>
) </div>
}}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
</VnRow>
</QForm> </QForm>
</QCardSection> </QCardSection>
</QCard> </QCard>

View File

@ -84,79 +84,64 @@ function handleLocation(data, location) {
</template> </template>
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md"> <QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
<div class="col">
<QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <VnInput :label="t('Consignee')" clearable v-model="data.nickname" />
<VnInput :label="t('Consignee')" clearable v-model="data.nickname" />
</div> <VnInput :label="t('Street address')" clearable v-model="data.street" />
<div class="col">
<VnInput
:label="t('Street address')"
clearable
v-model="data.street"
/>
</div>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnLocation
<div class="col"> :rules="validate('Worker.postcode')"
<VnLocation :roles-allowed-to-create="['deliveryAssistant']"
:rules="validate('Worker.postcode')" :options="postcodesOptions"
:roles-allowed-to-create="['deliveryAssistant']" v-model="data.location"
:options="postcodesOptions" @update:model-value="(location) => handleLocation(data, location)"
v-model="data.location" />
@update:model-value="(location) => handleLocation(data, location)"
></VnLocation> <div class="row justify-between q-gutter-md q-mb-md">
</div> <VnSelectFilter
<div class="col"> :label="t('Agency')"
<VnSelectFilter :options="agencyModes"
:label="t('Agency')" :rules="validate('route.agencyFk')"
:options="agencyModes" hide-selected
:rules="validate('route.agencyFk')" option-label="name"
hide-selected option-value="id"
option-label="name" v-model="data.agencyModeFk"
option-value="id" class="col"
v-model="data.agencyModeFk" />
/> <VnInput class="col" :label="t('Phone')" clearable v-model="data.phone" />
</div> <VnInput
<div class="col"> class="col"
<VnInput :label="t('Phone')" clearable v-model="data.phone" /> :label="t('Mobile')"
</div> clearable
<div class="col"> v-model="data.mobile"
<VnInput :label="t('Mobile')" clearable v-model="data.mobile" /> />
</div> </div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <VnSelectFilter
<VnSelectFilter :label="t('Incoterms')"
:label="t('Incoterms')" :options="incoterms"
:options="incoterms" hide-selected
hide-selected option-label="name"
option-label="name" option-value="code"
option-value="code" v-model="data.incotermsFk"
v-model="data.incotermsFk" />
/>
</div> <VnSelectDialog
<div class="col"> :label="t('Customs agent')"
<VnSelectDialog :options="customsAgents"
:label="t('Customs agent')" hide-selected
:options="customsAgents" option-label="fiscalName"
hide-selected option-value="id"
option-label="fiscalName" v-model="data.customsAgentFk"
option-value="id" >
v-model="data.customsAgentFk" <template #form>
> <CustomerNewCustomsAgent @on-data-saved="refreshData()" />
<template #form> </template>
<CustomerNewCustomsAgent @on-data-saved="refreshData()" /> </VnSelectDialog>
</template>
</VnSelectDialog>
</div>
</VnRow> </VnRow>
</template> </template>
</FormModel> </FormModel>

View File

@ -25,7 +25,6 @@ const toCustomerCreditContracts = () => {
<FormModel <FormModel
:form-initial-data="initialData" :form-initial-data="initialData"
:observe-form-changes="false" :observe-form-changes="false"
:default-actions="true"
url-create="creditClassifications/createWithInsurance" url-create="creditClassifications/createWithInsurance"
@on-data-saved="toCustomerCreditContracts()" @on-data-saved="toCustomerCreditContracts()"
> >

View File

@ -1,15 +1,22 @@
<script setup> <script setup>
import { reactive } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const initialData = ref({});
const setClient = (data) => {
console.log(data.credit);
initialData.value.credit = data.credit;
};
const toCustomerCredits = () => { const toCustomerCredits = () => {
router.push({ router.push({
name: 'CustomerCredits', name: 'CustomerCredits',
@ -21,8 +28,15 @@ const toCustomerCredits = () => {
</script> </script>
<template> <template>
<FetchData
:filter="filter"
@on-fetch="setClient"
auto-load
:url="`Clients/${route.params.id}/getCard`"
/>
<FormModel <FormModel
:form-initial-data="{}" :form-initial-data="initialData"
:observe-form-changes="false" :observe-form-changes="false"
:url-update="`/Clients/${route.params.id}`" :url-update="`/Clients/${route.params.id}`"
@on-data-saved="toCustomerCredits()" @on-data-saved="toCustomerCredits()"
@ -38,15 +52,12 @@ const toCustomerCredits = () => {
</template> </template>
<template #form="{ data }"> <template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md"> <QInput
<div class="col"> :label="t('Credit')"
<QInput clearable
:label="t('Credit')" type="number"
type="number" v-model.number="data.credit"
v-model.number="data.credit" />
/>
</div>
</VnRow>
</template> </template>
</FormModel> </FormModel>
</template> </template>

View File

@ -57,18 +57,21 @@ watch([client, findOne], ([newClient, newFindOne]) => {
const saveData = async () => { const saveData = async () => {
try { try {
const formData = new FormData(); const formData = new FormData();
const files = dms.value.files;
if (dms.value.files) { if (files && files.length > 0) {
for (let i = 0; i < dms.value.files.length; i++) for (let file of files) {
formData.append(dms.value.files[i].name, dms.value.files[i]); formData.append(file.name, file);
}
dms.value.hasFileAttached = true; dms.value.hasFileAttached = true;
const url = `clients/${route.params.id}/uploadFile`;
await axios.post(url, formData, {
params: dms.value,
});
notify('globals.dataSaved', 'positive');
toCustomerFileManagement();
} }
const url = `clients/${route.params.id}/uploadFile`;
await axios.post(url, formData, {
params: dms.value,
});
notify('globals.dataSaved', 'positive');
toCustomerFileManagement();
} catch (error) { } catch (error) {
notify(error.message, 'negative'); notify(error.message, 'negative');
} }

View File

@ -43,20 +43,21 @@ const setCurrentDms = (data) => {
const saveData = async () => { const saveData = async () => {
try { try {
const formData = new FormData(); const formData = new FormData();
const files = dms.value.files;
if (dms.value.files) { if (files && files.length > 0) {
for (let i = 0; i < dms.value.files.length; i++) for (let file of files) {
formData.append(dms.value.files[i].name, dms.value.files[i]); formData.append(file.name, file);
}
dms.value.hasFileAttached = true; dms.value.hasFileAttached = true;
const url = `dms/${route.params.dmsId}/updateFile`;
await axios.post(url, formData, {
params: dms.value,
});
notify('globals.dataSaved', 'positive');
toCustomerFileManagement();
} }
const url = `dms/${route.params.dmsId}/updateFile`;
await axios.post(url, formData, {
params: dms.value,
});
notify('globals.dataSaved', 'positive');
toCustomerFileManagement();
} catch (error) { } catch (error) {
notify(error.message, 'negative'); notify(error.message, 'negative');
} }

View File

@ -17,7 +17,6 @@ const onDataSaved = (dataSaved) => {
<template> <template>
<FormModelPopup <FormModelPopup
:form-initial-data="{}"
:title="t('New customs agent')" :title="t('New customs agent')"
@on-data-saved="onDataSaved($event)" @on-data-saved="onDataSaved($event)"
model="customer" model="customer"