refs #5835 rate fixed & options descriptor created
gitea/salix-front/pipeline/head This commit looks good Details

This commit is contained in:
Jorge Penadés 2023-11-28 12:05:39 +01:00
parent 7595c01293
commit 0282f8dde8
8 changed files with 293 additions and 84 deletions

View File

@ -24,12 +24,13 @@ const address = ref(props.data.address);
const isLoading = ref(false);
async function confirm() {
const response = { address };
const response = { address: address.value };
if (props.promise) {
isLoading.value = true;
const { address: _address, ...restData } = props.data;
try {
Object.assign(response, props.data);
Object.assign(response, restData);
await props.promise(response);
} finally {
isLoading.value = false;

View File

@ -89,6 +89,7 @@ const value = computed({
map-options
use-input
@filter="filterHandler"
hide-selected
fill-input
ref="vnSelectRef"
>

View File

@ -35,6 +35,10 @@ const $props = defineProps({
const slots = useSlots();
const { t } = useI18n();
const entity = computed(() => useArrayData($props.dataKey).store.data);
defineExpose({
getData,
});
onMounted(async () => {
await getData();
watch(

View File

@ -427,8 +427,8 @@ export default {
dueDay: 'Fecha de vencimiento',
},
summary: {
supplier: 'Supplier',
supplierRef: 'Supplier ref.',
supplier: 'Proveedor',
supplierRef: 'Ref. proveedor',
currency: 'Divisa',
docNumber: 'Número documento',
issued: 'Fecha de expedición',

View File

@ -37,7 +37,7 @@ function confirmPickupOrder() {
data: {
address: customer.email,
},
send: sendPickupOrder,
promise: sendPickupOrder,
},
});
}

View File

@ -1,12 +1,20 @@
<script setup>
import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
import { toCurrency, toDate } from 'src/filters';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { useRole } from 'src/composables/useRole';
import useCardDescription from 'src/composables/useCardDescription';
import { downloadFile } from 'src/composables/downloadFile';
import { useArrayData } from 'src/composables/useArrayData';
import { usePrintService } from 'composables/usePrintService';
import VnLv from 'src/components/ui/VnLv.vue';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import FetchData from 'src/components/FetchData.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
const $props = defineProps({
id: {
@ -17,13 +25,41 @@ const $props = defineProps({
});
const route = useRoute();
const router = useRouter();
const quasar = useQuasar();
const { hasAny } = useRole();
const { t } = useI18n();
const { openReport, sendEmail } = usePrintService();
const arrayData = useArrayData('InvoiceIn');
const entityId = computed(() => {
return $props.id || route.params.id;
});
const totalAmount = ref([]);
const invoiceIn = computed(() => arrayData.store.data);
const cardDescriptorRef = ref();
const entityId = computed(() => $props.id || route.params.id);
const totalAmount = ref();
const currentAction = ref();
const config = ref();
const actions = {
book: {
title: 'Are you sure you want to book this invoice?',
cb: checkToBook,
action: toBook,
},
delete: {
title: 'Are you sure you want to delete this invoice?',
action: deleteInvoice,
},
clone: {
title: 'Are you sure you want to clone this invoice?',
action: cloneInvoice,
},
showPdf: {
cb: showPdfInvoice,
},
sendPdf: {
cb: sendPdfInvoiceConfirmation,
},
};
const filter = {
include: [
{
@ -52,23 +88,130 @@ const filter = {
};
const data = ref(useCardDescription());
function setData(entity) {
async function setData(entity) {
data.value = useCardDescription(entity.supplierRef, entity.id);
const { totalDueDay } = await getTotals();
totalAmount.value = totalDueDay;
}
async function getTotals() {
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
return data;
}
function openDialog() {
quasar.dialog({
component: VnConfirm,
componentProps: {
title: currentAction.value.title,
promise: currentAction.value.action,
},
});
}
async function checkToBook() {
let directBooking = true;
const totals = await getTotals();
const taxableBaseNotEqualDueDay = totals.totalDueDay != totals.totalTaxableBase;
const vatNotEqualDueDay = totals.totalDueDay != totals.totalVat;
if (taxableBaseNotEqualDueDay && vatNotEqualDueDay) directBooking = false;
const { data: dueDaysCount } = await axios.get('InvoiceInDueDays/count', {
where: {
invoiceInFk: entityId.value,
dueDated: { gte: Date.vnNew() },
},
});
if (dueDaysCount) directBooking = false;
if (!directBooking) openDialog();
else toBook();
}
async function toBook() {
await axios.post(`InvoiceIns/${entityId.value}/toBook`);
// Pendiente de sincronizar todo con arrayData
quasar.notify({
type: 'positive',
message: t('globals.dataSaved'),
});
await cardDescriptorRef.value.getData();
setTimeout(() => location.reload(), 500);
}
async function deleteInvoice() {
await axios.delete(`InvoiceIns/${entityId.value}`);
quasar.notify({
type: 'positive',
message: t('Invoice deleted'),
});
router.push({ path: '/invoice-in' });
}
async function cloneInvoice() {
const { data } = await axios.post(`InvoiceIns/${entityId.value}/clone`);
quasar.notify({
type: 'positive',
message: t('Invoice cloned'),
});
router.push({ path: `/invoice-in/${data.id}/summary` });
}
const isAgricultural = () =>
invoiceIn.value.supplier.sageWithholdingFk == config.value[0].sageWithholdingFk;
function showPdfInvoice() {
if (isAgricultural()) openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`);
}
function sendPdfInvoiceConfirmation() {
quasar.dialog({
component: SendEmailDialog,
componentProps: {
data: {
address: invoiceIn.value.supplier.contacts[0].email,
},
promise: sendPdfInvoice,
},
});
}
function sendPdfInvoice({ address }) {
if (!address)
quasar.notify({
type: 'negative',
message: t(`The email can't be empty`),
});
else
return sendEmail(`InvoiceIns/${entityId.value}/invoice-in-email`, {
recipientId: invoiceIn.value.supplier.id,
recipient: address,
});
}
function triggerMenu(type) {
currentAction.value = actions[type];
if (currentAction.value.cb) currentAction.value.cb();
else openDialog(type);
}
</script>
<template>
<!--Refactor para añadir en el arrayData-->
<FetchData
:url="`InvoiceIns/${entityId}/getTotals`"
@on-fetch="
(data) => {
totalAmount = data.totalDueDay;
}
"
url="InvoiceInConfigs"
:where="{ fields: ['sageWithholdingFk'] }"
auto-load
@on-fetch="(data) => (config = data)"
/>
<!--Refactor para añadir en el arrayData-->
<CardDescriptor
ref="cardDescriptorRef"
module="InvoiceIn"
:url="`InvoiceIns/${entityId}`"
:filter="filter"
@ -77,6 +220,79 @@ function setData(entity) {
@on-fetch="setData"
data-key="invoiceInData"
>
<template #menu="{ entity }">
<QItem
v-if="!entity.isBooked && hasAny(['administrative'])"
v-ripple
clickable
@click="triggerMenu('book')"
>
<QItemSection avatar>
<QIcon name="summarize" />
</QItemSection>
<QItemSection>{{ t('To book') }}</QItemSection>
</QItem>
<QSeparator />
<QItem
v-if="hasAny(['administrative'])"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection avatar>
<QIcon name="delete" />
</QItemSection>
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
</QItem>
<QSeparator />
<QItem
v-if="hasAny(['administrative'])"
v-ripple
clickable
@click="triggerMenu('clone')"
>
<QItemSection avatar>
<QIcon name="file_copy" />
</QItemSection>
<QItemSection>{{ t('Clone invoice') }}</QItemSection>
</QItem>
<QSeparator />
<QItem
v-if="isAgricultural()"
v-ripple
clickable
@click="triggerMenu('showPdf')"
>
<QItemSection avatar>
<QIcon name="picture_as_pdf" />
</QItemSection>
<QItemSection>{{ t('Show agricultural receipt as PDF') }}</QItemSection>
</QItem>
<QSeparator />
<QItem
v-if="isAgricultural()"
v-ripple
clickable
@click="triggerMenu('sendPdf')"
>
<QItemSection avatar>
<QIcon name="mail" />
</QItemSection>
<QItemSection>{{ t('Send agricultural receipt as PDF') }}</QItemSection>
</QItem>
<QSeparator />
<QItem
v-if="entity.dmsFk"
v-ripple
clickable
@click="downloadFile(entity.dmsFk)"
>
<QItemSection avatar>
<QIcon name="cloud_download" />
</QItemSection>
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
</QItem>
</template>
<template #body="{ entity }">
<VnLv :label="t('invoiceIn.card.issued')" :value="toDate(entity.issued)" />
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
@ -107,3 +323,25 @@ function setData(entity) {
</template>
</CardDescriptor>
</template>
<style lang="scss" scoped>
.q-dialog {
.q-card {
width: 35em;
max-width: 35em;
}
}
</style>
<i18n>
es:
To book: Contabilizar
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
Delete invoice: Eliminar factura
Are you sure you want to delete this invoice?: Estas seguro de querer eliminar esta factura?
Invoice deleted: Factura eliminada
Clone invoice: Clonar factura
Invoice cloned: Factura clonada
Show agricultural receipt as PDF: Ver recibo agrícola como PDF
Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
Are you sure you want to send it?: Estás seguro que quieres enviarlo?
Send PDF invoice: Enviar factura a PDF
</i18n>

View File

@ -4,8 +4,6 @@ import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toCurrency, toDate } from 'src/filters';
import { getUrl } from 'src/composables/getUrl';
import { downloadFile } from 'src/composables/downloadFile';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
@ -66,8 +64,8 @@ const vatColumns = ref([
{
name: 'rate',
label: 'invoiceIn.summary.rate',
field: (row) => row.taxRate,
format: (value) => value,
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
format: (value) => toCurrency(value),
sortable: true,
align: 'left',
},
@ -173,6 +171,13 @@ function getIntrastatTotals(intrastat) {
return totals;
}
function getTaxTotal(tax) {
return tax.reduce(
(acc, cur) => acc + taxRate(cur.taxableBase, cur.taxTypeSage?.rate),
0
);
}
function setData(entity) {
if (!entity) return false;
@ -181,6 +186,13 @@ function setData(entity) {
if (entity.invoiceInIntrastat.length)
intrastatTotals.value = { ...getIntrastatTotals(entity.invoiceInIntrastat) };
}
function taxRate(taxableBase, rate) {
if (rate && taxableBase) {
return (rate / 100) * taxableBase;
}
return 0;
}
</script>
<template>
@ -200,19 +212,6 @@ function setData(entity) {
{{ t('invoiceIn.pageTitles.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QBtn
class="q-ml-sm"
padding="xs"
flat
color="primary"
round
icon="cloud_download"
@click.stop="downloadFile(invoiceIn.dmsFk)"
>
<QTooltip>
{{ t('components.smartCard.downloadFile') }}
</QTooltip>
</QBtn>
</QCardSection>
<VnLv
:label="t('invoiceIn.summary.supplier')"
@ -237,19 +236,6 @@ function setData(entity) {
{{ t('invoiceIn.pageTitles.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QBtn
class="q-ml-sm"
padding="xs"
flat
color="primary"
round
icon="cloud_download"
@click.stop="downloadFile(invoiceIn.dmsFk)"
>
<QTooltip>
{{ t('components.smartCard.downloadFile') }}
</QTooltip>
</QBtn>
</QCardSection>
<VnLv
:label="t('invoiceIn.summary.issued')"
@ -274,19 +260,6 @@ function setData(entity) {
{{ t('invoiceIn.pageTitles.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QBtn
class="q-ml-sm"
padding="xs"
flat
color="primary"
round
icon="cloud_download"
@click.stop="downloadFile(invoiceIn.dmsFk)"
>
<QTooltip>
{{ t('components.smartCard.downloadFile') }}
</QTooltip>
</QBtn>
</QCardSection>
<QCardSection class="q-pa-none">
<div class="bordered q-px-sm q-mx-auto">
@ -323,19 +296,6 @@ function setData(entity) {
{{ t('invoiceIn.pageTitles.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QBtn
class="q-ml-sm"
padding="xs"
flat
color="primary"
round
icon="cloud_download"
@click.stop="downloadFile(invoiceIn.dmsFk)"
>
<QTooltip>
{{ t('components.smartCard.downloadFile') }}
</QTooltip>
</QBtn>
</QCardSection>
<VnLv
:label="t('invoiceIn.summary.sage')"
@ -356,8 +316,9 @@ function setData(entity) {
</QCard>
<!--Vat-->
<QCard v-if="invoiceIn.invoiceInTax.length" class="vn-three">
<a class="header">
<a class="header" :href="`#/invoice-in/${entityId}/vat`">
{{ t('invoiceIn.card.vat') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QTable
:columns="vatColumns"
@ -378,7 +339,9 @@ function setData(entity) {
<QTd>{{ toCurrency(invoiceIn.totals.totalTaxableBase) }}</QTd>
<QTd></QTd>
<QTd></QTd>
<QTd></QTd>
<QTd>{{
toCurrency(getTaxTotal(invoiceIn.invoiceInTax))
}}</QTd>
<QTd></QTd>
</QTr>
</template>
@ -386,9 +349,10 @@ function setData(entity) {
</QCard>
<!--Due Day-->
<QCard v-if="invoiceIn.invoiceInDueDay.length" class="vn-two">
<div class="header">
<a class="header" :href="`#/invoice-in/${entityId}/due-day`">
{{ t('invoiceIn.card.dueDay') }}
</div>
<QIcon name="open_in_new" color="primary" />
</a>
<QTable
class="full-width"
:columns="dueDayColumns"
@ -415,9 +379,10 @@ function setData(entity) {
</QCard>
<!--Intrastat-->
<QCard v-if="invoiceIn.invoiceInIntrastat.length">
<div class="header">
<a class="header" :href="`#/invoice-in/${entityId}/intrastat`">
{{ t('invoiceIn.card.intrastat') }}
</div>
<QIcon name="open_in_new" color="primary" />
</a>
<QTable
:columns="intrastatColumns"
:rows="invoiceIn.invoiceInIntrastat"
@ -451,7 +416,7 @@ function setData(entity) {
}
.bordered {
border: 1px solid var(--vn-text);
width: 18em;
max-width: 18em;
}
</style>
<i18n>