From 460b166a1733ba632578d6bdf5a558f7ef0dc2f5 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 5 Aug 2024 09:04:08 +0200 Subject: [PATCH 1/3] feat(customer_balance): refs #6943 add functionality from salix --- src/components/common/VnSelect.vue | 8 +- src/pages/Customer/Card/CustomerBalance.vue | 95 ++----- .../components/CustomerNewPayment.vue | 231 +++++++++++------- 3 files changed, 168 insertions(+), 166 deletions(-) diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index b9d24267c..63a3f088f 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -49,6 +49,10 @@ const $props = defineProps({ type: Array, default: null, }, + include: { + type: [Object, Array], + default: null, + }, where: { type: Object, default: null, @@ -142,7 +146,7 @@ function filter(val, options) { async function fetchFilter(val) { if (!$props.url || !dataRef.value) return; - const { fields, sortBy, limit } = $props; + const { fields, include, sortBy, limit } = $props; const key = optionFilterValue.value ?? (new RegExp(/\d/g).test(val) @@ -153,7 +157,7 @@ async function fetchFilter(val) { ? { [key]: { like: `%${val}%` } } : { [key]: val }; const where = { ...(val ? defaultWhere : {}), ...$props.where }; - const fetchOptions = { where, limit }; + const fetchOptions = { where, include, limit }; if (fields) fetchOptions.fields = fields; if (sortBy) fetchOptions.order = sortBy; return dataRef.value.fetch(fetchOptions); diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue index 411c44ad3..346e76681 100644 --- a/src/pages/Customer/Card/CustomerBalance.vue +++ b/src/pages/Customer/Card/CustomerBalance.vue @@ -17,7 +17,6 @@ import VnTable from 'components/VnTable/VnTable.vue'; import VnInput from 'components/common/VnInput.vue'; import VnSubToolbar from 'components/ui/VnSubToolbar.vue'; import VnFilter from 'components/VnTable/VnFilter.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; @@ -73,17 +72,6 @@ const companyFilterColumn = { }, }, visible: false, - create: true, -}; -const referenceColumn = { - align: 'left', - name: 'description', - label: t('Reference'), -}; -const onlyCreate = { - visible: false, - columnFilter: false, - create: true, }; const columns = computed(() => [ @@ -93,10 +81,6 @@ const columns = computed(() => [ label: t('Date'), format: ({ payed }) => toDate(payed), cardVisible: true, - create: true, - columnCreate: { - component: 'date', - }, }, { align: 'left', @@ -120,21 +104,18 @@ const columns = computed(() => [ }, cardVisible: true, }, - { ...referenceColumn, isTitle: true, class: 'extend' }, - companyFilterColumn, + { + align: 'left', + name: 'description', + label: t('Reference'), + isTitle: true, + class: 'extend', + }, { align: 'right', name: 'bankFk', label: t('Bank'), cardVisible: true, - create: true, - }, - { - align: 'right', - name: 'amountPaid', - label: t('Amount'), - component: 'number', - ...onlyCreate, }, { align: 'right', @@ -163,7 +144,6 @@ const columns = computed(() => [ label: t('Conciliated'), cardVisible: true, }, - { ...referenceColumn, ...onlyCreate }, { align: 'left', name: 'tableActions', @@ -194,18 +174,16 @@ onBeforeMount(() => { companyId.value = user.value.companyFk; }); -async function getClientRisk(reload = false) { - if (reload || !clientRisk.value?.length) { - const { data } = await axios.get(`clientRisks`, { - params: { - filter: JSON.stringify({ - include: { relation: 'company', scope: { fields: ['code'] } }, - where: { clientFk: route.params.id, companyFk: user.value.companyFk }, - }), - }, - }); - clientRisk.value = data; - } +async function getClientRisk() { + const { data } = await axios.get(`clientRisks`, { + params: { + filter: JSON.stringify({ + include: { relation: 'company', scope: { fields: ['code'] } }, + where: { clientFk: route.params.id, companyFk: user.value.companyFk }, + }), + }, + }); + clientRisk.value = data; return clientRisk.value; } @@ -230,14 +208,13 @@ async function onFetch(data) { balances.value = data; } -// BORRAR COMPONENTE Y HACER CON VNTABLE const showNewPaymentDialog = () => { quasar.dialog({ component: CustomerNewPayment, componentProps: { companyId: companyId.value, totalCredit: clientRisk.value[0]?.amount, - promise: getClientRisk(true), + promise: () => tableRef.value.reload(), }, }); }; @@ -282,17 +259,6 @@ const showBalancePdf = ({ id }) => { :is-editable="false" :column-search="false" @on-fetch="onFetch" - :create="{ - urlCreate: `Clients/${route.params.id}/createReceipt`, - mapper: (data) => { - data.companyFk = data.companyId; - delete data.companyId; - return data; - }, - title: t('New payment'), - onDataSaved: () => tableRef.reload(), - formInitialData: { companyId }, - }" auto-load > - + + + + {{ t('New payment') }} + + diff --git a/src/pages/Customer/components/CustomerNewPayment.vue b/src/pages/Customer/components/CustomerNewPayment.vue index 877a2c353..0e1bc8bd2 100644 --- a/src/pages/Customer/components/CustomerNewPayment.vue +++ b/src/pages/Customer/components/CustomerNewPayment.vue @@ -2,18 +2,24 @@ import { onBeforeMount, reactive, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; +import axios from 'axios'; import { useDialogPluginComponent } from 'quasar'; +import { usePrintService } from 'composables/usePrintService'; +import useNotify from 'src/composables/useNotify.js'; import FetchData from 'components/FetchData.vue'; import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; +import VnInputNumber from 'components/common/VnInputNumber.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import VnInput from 'src/components/common/VnInput.vue'; const { t } = useI18n(); const route = useRoute(); +const { notify } = useNotify(); +const { sendEmail, openReport } = usePrintService(); const { dialogRef } = useDialogPluginComponent(); const $props = defineProps({ @@ -27,7 +33,7 @@ const $props = defineProps({ }, promise: { type: Function, - required: true, + default: null, }, }); @@ -36,11 +42,12 @@ const urlCreate = ref([]); const companyOptions = ref([]); const bankOptions = ref([]); const clientFindOne = ref([]); -const deliveredAmount = ref(null); -const amountToReturn = ref(null); const viewReceipt = ref(); -const sendEmail = ref(false); -const isLoading = ref(false); +const shouldSendEmail = ref(false); +const maxAmount = ref(); +const accountingType = ref({}); +const isCash = ref(false); +const formModelRef = ref(false); const filterBanks = { fields: ['id', 'bank', 'accountingTypeFk'], @@ -67,87 +74,123 @@ onBeforeMount(() => { urlCreate.value = `Clients/${route.params.id}/createReceipt`; }); -const setPaymentType = (value) => { - // if (id === 1) initialData.description = 'Credit card'; - // if (id === 2) initialData.description = 'Cash'; - // if (id === 3 || id === 3117) initialData.description = ''; - // if (id === 4) initialData.description = 'Transfer'; - // const CASH_CODE = 2; - // // const CASH_CODE = 2 - // if (!value) return; - // const accountingType = CASH_CODE; - // initialData.description = ''; - // viewReceipt.value = value == CASH_CODE; - // if (accountingType.code == 'compensation') this.receipt.description = ''; - // else { - // if ( - // accountingType.receiptDescription != null && - // accountingType.receiptDescription != '' - // ) - // this.receipt.description.push(accountingType.receiptDescription); - // if (this.originalDescription) - // this.receipt.description.push(this.originalDescription); - // this.receipt.description = this.receipt.description.join(', '); - // } - // this.maxAmount = accountingType && accountingType.maxAmount; - // this.receipt.payed = Date.vnNew(); - // if (accountingType.daysInFuture) - // this.receipt.payed.setDate( - // this.receipt.payed.getDate() + accountingType.daysInFuture - // ); -}; +function setPaymentType(accounting) { + if (!accounting) return; + accountingType.value = accounting.accountingType; + + initialData.description = []; + initialData.payed = Date.vnNew(); + isCash.value = accountingType.value.code == 'cash'; + viewReceipt.value = isCash.value; + if (accountingType.value.daysInFuture) + initialData.payed.setDate( + initialData.payed.getDate() + accountingType.value.daysInFuture + ); + maxAmount.value = accountingType.value && accountingType.value.maxAmount; + + if (accountingType.value.code == 'compensation') + return (initialData.description = ''); + if (accountingType.value.receiptDescription) + initialData.description.push(accountingType.value.receiptDescription); + if (initialData.description) initialData.description.push(initialData.description); + + initialData.description = initialData.description.join(', '); +} const calculateFromAmount = (event) => { - amountToReturn.value = parseFloat(event) * -1 + parseFloat(deliveredAmount.value); + initialData.amountToReturn = + parseFloat(initialData.deliveredAmount) + parseFloat(event) * -1; }; const calculateFromDeliveredAmount = (event) => { - amountToReturn.value = parseFloat($props.totalCredit) * -1 + parseFloat(event); + initialData.amountToReturn = parseFloat(event) - initialData.amountPaid; }; -const setClientEmail = (data) => { - initialData.email = data.email; -}; +function onBeforeSave(data) { + const exceededAmount = data.amountPaid > maxAmount.value; + if (isCash.value && exceededAmount) + return notify(t('Amount exceeded', { maxAmount: maxAmount.value }), 'negative'); -const onDataSaved = async () => { - isLoading.value = true; - if ($props.promise) { - try { - await $props.promise(); - } finally { - isLoading.value = false; - if (closeButton.value) closeButton.value.click(); - } + if (isCash.value && shouldSendEmail.value && !data.email) + return notify(t('There is no assigned email for this client'), 'negative'); + + data.bankFk = data.bankFk.id; + return data; +} + +async function onDataSaved(formData, { id }) { + try { + if (shouldSendEmail.value && isCash.value) + await sendEmail(`Receipts/${id}/receipt-email`, { + recipient: formData.email, + }); + + if (viewReceipt.value) openReport(`Receipts/${id}/receipt-pdf`); + } finally { + if ($props.promise) $props.promise(); + if (closeButton.value) closeButton.value.click(); } -}; +} + +async function accountShortToStandard({ target: { value } }) { + if (!value) return (initialData.description = ''); + initialData.compensationAccount = value.replace('.', '0'.repeat(11 - value.length)); + const params = { bankAccount: initialData.compensationAccount }; + const { data } = await axios(`Clients/getClientOrSupplierReference`, { params }); + if (!data.clientId) { + initialData.description = t('Supplier Compensation Reference', { + supplierId: data.supplierId, + supplierName: data.supplierName, + }); + return; + } + initialData.description = t('Client Compensation Reference', { + clientId: data.clientId, + clientName: data.clientName, + }); +} + +async function getAmountPaid() { + const filter = { + where: { + clientFk: route.params.id, + companyFk: initialData.companyFk, + }, + }; + + const { data } = await axios(`ClientRisks`, { + params: { filter: JSON.stringify(filter) }, + }); + initialData.amountPaid = (data?.length && data[0].amount) || undefined; +}