#8448 - devToTest #1254

Merged
alexm merged 365 commits from 8448-devToTest into test 2025-01-21 10:44:46 +00:00
4 changed files with 221 additions and 229 deletions
Showing only changes of commit 69bcab0ec4 - Show all commits

View File

@ -1,60 +1,30 @@
<script setup>
import { ref, reactive, computed, onBeforeMount, capitalize } from 'vue';
import { ref, reactive, computed, onBeforeMount } from 'vue';
import { useRouter, onBeforeRouteUpdate } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
import { toCurrency, toDate } from 'src/filters';
import { useAcl } from 'src/composables/useAcl';
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';
import VnSelect from 'src/components/common/VnSelect.vue';
import { useCapitalize } from 'src/composables/useCapitalize';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import InvoiceInToBook from '../InvoiceInToBook.vue';
import InvoiceInDescriptorMenu from './InvoiceInDescriptorMenu.vue';
const $props = defineProps({ id: { type: Number, default: null } });
const { push, currentRoute } = useRouter();
const quasar = useQuasar();
const { hasAny } = useAcl();
const { t } = useI18n();
const { openReport, sendEmail } = usePrintService();
const arrayData = useArrayData();
const invoiceIn = computed(() => arrayData.store.data);
const cardDescriptorRef = ref();
const correctionDialogRef = ref();
const entityId = computed(() => $props.id || +currentRoute.value.params.id);
const totalAmount = ref();
const currentAction = ref();
const config = ref();
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceIns = ref([]);
const siiTypeInvoiceOuts = ref([]);
const invoiceCorrectionTypes = ref([]);
const actions = {
unbook: {
title: t('assertAction', { action: t('unbook') }),
action: toUnbook,
},
delete: {
title: t('assertAction', { action: t('delete') }),
action: deleteInvoice,
},
clone: {
title: t('assertAction', { action: t('clone') }),
action: cloneInvoice,
},
showPdf: { cb: showPdfInvoice },
sendPdf: { cb: sendPdfInvoiceConfirmation },
correct: { cb: () => correctionDialogRef.value.show() },
};
const filter = {
include: [
{
@ -90,7 +60,7 @@ const routes = reactive({
return {
name: 'InvoiceInList',
query: {
table: JSON.stringify({ supplierFk: id }),
params: JSON.stringify({ supplierFk: id }),
},
};
},
@ -99,7 +69,7 @@ const routes = reactive({
return {
name: 'InvoiceInList',
query: {
table: JSON.stringify({ correctedFk: entityId.value }),
params: JSON.stringify({ correctedFk: entityId.value }),
},
};
}
@ -118,21 +88,21 @@ const routes = reactive({
const correctionFormData = reactive({
invoiceReason: 2,
invoiceType: 2,
invoiceClass: 8,
invoiceClass: 6,
});
const isNotFilled = computed(() => Object.values(correctionFormData).includes(null));
onBeforeMount(async () => {
await setInvoiceCorrection(entityId.value);
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
totalAmount.value = data.totalTaxableBase;
totalAmount.value = data.totalDueDay;
});
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
await setInvoiceCorrection(to.params.id);
const { data } = await axios.get(`InvoiceIns/${to.params.id}/getTotals`);
totalAmount.value = data.totalTaxableBase;
totalAmount.value = data.totalDueDay;
}
});
@ -153,94 +123,6 @@ async function setInvoiceCorrection(id) {
);
}
function openDialog() {
quasar.dialog({
component: VnConfirm,
componentProps: {
title: t(currentAction.value.title),
promise: currentAction.value.action,
},
});
}
async function toUnbook() {
const { data } = await axios.post(`InvoiceIns/${entityId.value}/toUnbook`);
const { isLinked, bookEntry, accountingEntries } = data;
const type = isLinked ? 'warning' : 'positive';
const message = isLinked
? t('isLinked', { bookEntry, accountingEntries })
: t('isNotLinked', { bookEntry });
quasar.notify({ type, message });
if (!isLinked) arrayData.store.data.isBooked = false;
}
async function deleteInvoice() {
await axios.delete(`InvoiceIns/${entityId.value}`);
quasar.notify({
type: 'positive',
message: t('Invoice deleted'),
});
push({ path: '/invoice-in' });
}
async function cloneInvoice() {
const { data } = await axios.post(`InvoiceIns/${entityId.value}/clone`);
quasar.notify({
type: 'positive',
message: t('Invoice cloned'),
});
push({ path: `/invoice-in/${data.id}/summary` });
}
const canEditProp = (props) =>
hasAny([{ model: 'InvoiceIn', props, accessType: 'WRITE' }]);
const isAgricultural = () => {
if (!config.value) return false;
return (
invoiceIn.value?.supplier?.sageFarmerWithholdingFk ===
config?.value[0]?.sageWithholdingFk
);
};
function showPdfInvoice() {
if (isAgricultural())
openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`, null, '_blank');
}
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);
}
const createInvoiceInCorrection = async () => {
const { data: correctingId } = await axios.post(
'InvoiceIns/corrective',
@ -262,9 +144,9 @@ const createInvoiceInCorrection = async () => {
auto-load
/>
<FetchData
url="siiTypeInvoiceIns"
url="SiiTypeInvoiceOuts"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceIns = data)"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)"
auto-load
/>
<FetchData
@ -281,87 +163,13 @@ const createInvoiceInCorrection = async () => {
title="supplierRef"
>
<template #menu="{ entity }">
<InvoiceInToBook>
<template #content="{ book }">
<QItem
v-if="!entity?.isBooked && canEditProp('toBook')"
v-ripple
clickable
@click="book(entityId)"
>
<QItemSection>{{ t('To book') }}</QItemSection>
</QItem>
</template>
</InvoiceInToBook>
<QItem
v-if="entity?.isBooked && canEditProp('toUnbook')"
v-ripple
clickable
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('To unbook') }}
</QItemSection>
</QItem>
<QItem
v-if="canEditProp('deleteById')"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
</QItem>
<QItem
v-if="canEditProp('clone')"
v-ripple
clickable
@click="triggerMenu('clone')"
>
<QItemSection>{{ t('Clone invoice') }}</QItemSection>
</QItem>
<QItem
v-if="isAgricultural()"
v-ripple
clickable
@click="triggerMenu('showPdf')"
>
<QItemSection>{{ t('Show agricultural receipt as PDF') }}</QItemSection>
</QItem>
<QItem
v-if="isAgricultural()"
v-ripple
clickable
@click="triggerMenu('sendPdf')"
>
<QItemSection
>{{ t('Send agricultural receipt as PDF') }}...</QItemSection
>
</QItem>
<QItem
v-if="!invoiceInCorrection.corrected"
v-ripple
clickable
@click="triggerMenu('correct')"
>
<QItemSection>{{ t('Create rectificative invoice') }}...</QItemSection>
</QItem>
<QItem
v-if="entity.dmsFk"
v-ripple
clickable
@click="downloadFile(entity.dmsFk)"
>
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
</QItem>
<InvoiceInDescriptorMenu :invoice="entity" />
</template>
<template #body="{ entity }">
<VnLv :label="t('InvoiceIn.list.issued')" :value="toDate(entity.issued)" />
<VnLv
:label="t('InvoiceIn.summary.bookedDate')"
:value="toDate(entity.booked)"
/>
<VnLv :label="t('InvoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('InvoiceIn.list.supplier')">
<VnLv :label="t('invoiceIn.list.issued')" :value="toDate(entity.issued)" />
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
<VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoiceIn.list.supplier')">
<template #value>
<span class="link">
{{ entity?.supplier?.nickname }}
@ -378,7 +186,7 @@ const createInvoiceInCorrection = async () => {
color="primary"
:to="routes.getSupplier(entity.supplierFk)"
>
<QTooltip>{{ t('InvoiceIn.list.supplier') }}</QTooltip>
<QTooltip>{{ t('invoiceIn.list.supplier') }}</QTooltip>
</QBtn>
<QBtn
size="md"
@ -394,7 +202,7 @@ const createInvoiceInCorrection = async () => {
color="primary"
:to="routes.getTickets(entity.supplierFk)"
>
<QTooltip>{{ t('InvoiceIn.descriptor.ticketList') }}</QTooltip>
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
</QBtn>
<QBtn
v-if="
@ -438,9 +246,9 @@ const createInvoiceInCorrection = async () => {
readonly
/>
<VnSelect
:label="`${capitalize(t('globals.class'))}`"
:label="`${useCapitalize(t('globals.class'))}`"
v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceIns"
:options="siiTypeInvoiceOuts"
option-value="id"
option-label="code"
:required="true"
@ -448,27 +256,15 @@ const createInvoiceInCorrection = async () => {
</QItemSection>
<QItemSection>
<VnSelect
:label="`${capitalize(t('globals.type'))}`"
:label="`${useCapitalize(t('globals.type'))}`"
v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes"
option-value="id"
option-label="description"
:required="true"
>
<template #option="{ opt }">
<QItem>
<QItemSection>
<QItemLabel
>{{ opt.code }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
<div></div>
</template>
</VnSelect>
/>
<VnSelect
:label="`${capitalize(t('globals.reason'))}`"
:label="`${useCapitalize(t('globals.reason'))}`"
v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes"
option-value="id"

View File

@ -0,0 +1,189 @@
<script setup>
import { ref, computed, toRefs, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
import { useAcl } from 'src/composables/useAcl';
import { downloadFile } from 'src/composables/downloadFile';
import { useArrayData } from 'src/composables/useArrayData';
import { usePrintService } from 'composables/usePrintService';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import InvoiceInToBook from '../InvoiceInToBook.vue';
const { hasAny } = useAcl();
const { t } = useI18n();
const { openReport, sendEmail } = usePrintService();
const { push, currentRoute } = useRouter();
const $props = defineProps({
invoice: {
type: Object,
required: true,
},
});
const { invoice } = toRefs($props);
const quasar = useQuasar();
const arrayData = useArrayData();
const currentAction = ref();
const config = ref();
const correctionDialogRef = ref();
const invoiceInCorrection = reactive({ correcting: [], corrected: null });
const entityId = computed(() => $props.invoice.id || +currentRoute.value.params.id);
const invoiceIn = computed(() => arrayData.store.data);
const actions = {
unbook: {
title: t('assertAction', { action: t('unbook') }),
action: toUnbook,
},
delete: {
title: t('assertAction', { action: t('delete') }),
action: deleteInvoice,
},
clone: {
title: t('assertAction', { action: t('clone') }),
action: cloneInvoice,
},
showPdf: { cb: showPdfInvoice },
sendPdf: { cb: sendPdfInvoiceConfirmation },
correct: { cb: () => correctionDialogRef.value.show() },
};
const canEditProp = (props) =>
hasAny([{ model: 'InvoiceIn', props, accessType: 'WRITE' }]);
function triggerMenu(type) {
currentAction.value = actions[type];
if (currentAction.value.cb) currentAction.value.cb();
else openDialog(type);
}
function openDialog() {
quasar.dialog({
component: VnConfirm,
componentProps: {
title: t(currentAction.value.title),
promise: currentAction.value.action,
},
});
}
async function toUnbook() {
const { data } = await axios.post(`InvoiceIns/${entityId.value}/toUnbook`);
const { isLinked, bookEntry, accountingEntries } = data;
const type = isLinked ? 'warning' : 'positive';
const message = isLinked
? t('isLinked', { bookEntry, accountingEntries })
: t('isNotLinked', { bookEntry });
quasar.notify({ type, message });
if (!isLinked) arrayData.store.data.isBooked = false;
}
async function deleteInvoice() {
await axios.delete(`InvoiceIns/${entityId.value}`);
quasar.notify({
type: 'positive',
message: t('Invoice deleted'),
});
push({ path: '/invoice-in' });
}
async function cloneInvoice() {
const { data } = await axios.post(`InvoiceIns/${entityId.value}/clone`);
quasar.notify({
type: 'positive',
message: t('Invoice cloned'),
});
push({ path: `/invoice-in/${data.id}/summary` });
}
const isAgricultural = () => {
if (!config.value) return false;
return (
invoiceIn.value?.supplier?.sageFarmerWithholdingFk ===
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,
});
}
</script>
<template>
<InvoiceInToBook>
<template #content="{ book }">
<QItem
v-if="!invoice?.isBooked && canEditProp('toBook')"
v-ripple
clickable
@click="book(entityId)"
>
<QItemSection>{{ t('To book') }}</QItemSection>
</QItem>
</template>
</InvoiceInToBook>
<QItem
v-if="invoice?.isBooked && canEditProp('toUnbook')"
v-ripple
clickable
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('To unbook') }}
</QItemSection>
</QItem>
<QItem
v-if="canEditProp('deleteById')"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
</QItem>
<QItem v-if="canEditProp('clone')" v-ripple clickable @click="triggerMenu('clone')">
<QItemSection>{{ t('Clone invoice') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
<QItemSection>{{ t('Show agricultural receipt as PDF') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
<QItemSection>{{ t('Send agricultural receipt as PDF') }}...</QItemSection>
</QItem>
<QItem
v-if="!invoiceInCorrection.corrected"
v-ripple
clickable
@click="triggerMenu('correct')"
>
<QItemSection>{{ t('Create rectificative invoice') }}...</QItemSection>
</QItem>
<QItem v-if="invoice.dmsFk" v-ripple clickable @click="downloadFile(invoice.dmsFk)">
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
</QItem>
</template>

View File

@ -10,6 +10,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import InvoiceIntoBook from '../InvoiceInToBook.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import InvoiceInDescriptorMenu from './InvoiceInDescriptorMenu.vue';
const props = defineProps({ id: { type: [Number, String], default: 0 } });
const { t } = useI18n();
@ -200,6 +201,9 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</template>
</InvoiceIntoBook>
</template>
<template #menu="{ entity }">
<InvoiceInDescriptorMenu :invoice="entity" />
</template>
<template #body="{ entity }">
<!--Basic Data-->
<QCard class="vn-one">

View File

@ -11,6 +11,7 @@ import FetchData from 'src/components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import { toDate, toCurrency } from 'src/filters';
import axios from 'axios';
import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
const $props = defineProps({
id: {
@ -242,7 +243,9 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<template #header>
<span>{{ travel.id }} - {{ travel.ref }}</span>
</template>
<template #menu="{ entity }">
<TravelDescriptorMenuItems :travel="entity" />
</template>
<template #body>
<QCard class="vn-one">
<QCardSection class="q-pa-none">