Skip to content

Commit

Permalink
feat: working on post invoice refactor ui
Browse files Browse the repository at this point in the history
  • Loading branch information
JustSamuel committed Sep 12, 2024
1 parent 03a9599 commit 234b052
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<template>
<FormCard :header="t('c_invoiceInfo.Amount')" v-if="invoice" @cancel="form.context.resetForm"
@update:modelValue="edit = $event" @save="formSubmit" :enableEdit="!deleted">
<div class="flex flex-column justify-content-between gap-2">
<InvoiceAmountForm :invoice="invoice" :form="form" :edit="edit" @update:edit="edit = $event"/>
</div>
</FormCard>
</template>

<script setup lang="ts">
import { computed, onBeforeMount, ref, watch } from "vue";
import type { InvoiceResponse } from "@sudosos/sudosos-client";
import { updateInvoiceAmountObject } from "@/utils/validation-schema";
import { schemaToForm } from "@/utils/formUtils";
import { InvoiceStatusResponseStateEnum } from "@sudosos/sudosos-client/src/api";
import { useInvoiceStore } from "@/stores/invoice.store";
import { useI18n } from "vue-i18n";
import InvoiceAmountForm from "@/modules/financial/components/invoice/forms/InvoiceAmountForm.vue";
import FormCard from "@/components/FormCard.vue";
const { t } = useI18n();
const edit = ref(false);
const invoiceStore = useInvoiceStore();
const deleted = computed(() => invoice.value.currentState.state === InvoiceStatusResponseStateEnum.Deleted);
const invoice = computed(() => invoiceStore.getInvoice(props.invoiceId) as InvoiceResponse);
const props = defineProps({
invoiceId: {
type: Number,
required: true
}
});
const form = schemaToForm(updateInvoiceAmountObject);
const formSubmit = () => {
form.submit();
};
const updateFieldValues = (p: InvoiceResponse) => {
if (!p) return;
const values = {
amount: p.transfer.amountInclVat.amount / 100,
};
form.context.resetForm({ values });
};
watch(() => invoice.value, (newValue) => {
updateFieldValues(newValue);
});
onBeforeMount(() => {
if (invoice.value) {
updateFieldValues(invoice.value);
}
});
</script>

<style scoped lang="scss">
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<span v-if="entry.index < totalRowCutoff">
{{ entry.data.vatPercentage + '%' }}
</span>
<span v-else class="font-bold">
<span v-else class="font-bold" :class="{ ...entry.data.class }">
{{ entry.data.description }}
</span>
</template>
Expand All @@ -46,7 +46,7 @@
class="p-1"
>
<template #body="entry">
<span :class="{ 'font-bold': entry.index >= totalRowCutoff }">
<span :class="{ 'font-bold': entry.index >= totalRowCutoff, ...entry.data.class }">
{{ formatPrice(rowTotal(entry.data)) }}
</span>
</template>
Expand All @@ -62,6 +62,7 @@ import { computed, onMounted, type PropType, type Ref, ref } from "vue";
import type { InvoiceEntryResponse, InvoiceResponse } from "@sudosos/sudosos-client";
import type { DineroObject } from "dinero.js";
import { useI18n } from "vue-i18n";
import { isDirty } from "@/utils/invoiceUtil";
const { t } = useI18n();
Expand All @@ -73,13 +74,13 @@ const props = defineProps({
});
const exclVat: Ref<DineroObject> = ref({ amount: 0, precision: 2, currency: 'EUR' });
const totalEntries: Ref<DineroObject> = ref({ amount: 0, precision: 2, currency: 'EUR' });
const vat: Ref<Record<number, DineroObject>> = ref({});
const inclVat: Ref<DineroObject> = ref({ amount: 0, precision: 2, currency: 'EUR' });
const totalRows: InvoiceEntryResponse[] = [];
const totalRowCutoff = computed(() => {
return props.invoice.invoiceEntries.length - totalRows.length;
});
const allRows = ref(props.invoice.invoiceEntries);
const allRows = ref([]);
const totalRowCutoff = ref(0);
const rowTotal = (row: any): DineroObject => {
return {
Expand All @@ -90,12 +91,20 @@ const rowTotal = (row: any): DineroObject => {
onMounted(() => {
props.invoice.invoiceEntries.forEach((entry) => {
allRows.value.push({
description: entry.description,
amount: entry.amount,
vatPercentage: entry.vatPercentage,
priceInclVat: entry.priceInclVat,
custom: false,
});
const price = entry.priceInclVat.amount * entry.amount;
inclVat.value.amount += price;
const excl = Math.round(price / (1 + (entry.vatPercentage / 100)));
const vatAmount = price - excl;
exclVat.value.amount += excl;
totalEntries.value.amount += price;
if (entry.vatPercentage in vat.value) {
vat.value[entry.vatPercentage].amount += vatAmount;
Expand Down Expand Up @@ -123,16 +132,26 @@ onMounted(() => {
});
}
if (props.invoice.transfer) {
totalRows.push({
description: t('common.incl'),
amount: 1,
vatPercentage: 0,
priceInclVat: totalEntries.value,
custom: false,
});
if (props.invoice.transfer && isDirty(props.invoice)) {
totalRows.push({
description: t('common.incl'),
description: t('common.transfer'),
amount: 1,
vatPercentage: 0,
class: { 'text-red-500': true },
priceInclVat: props.invoice.transfer.amountInclVat,
custom: false,
});
}
totalRowCutoff.value = allRows.value.length;
allRows.value.push(...totalRows);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import { useToast } from "primevue/usetoast";
import type { InvoiceResponse } from "@sudosos/sudosos-client";
import CardComponent from "@/components/CardComponent.vue";
import VuePdfApp from "vue3-pdf-app";
import InvoiceEntriesTable from "@/modules/financial/components/invoice/forms/InvoiceEntriesTable.vue";
import InvoiceEntriesTable from "@/modules/financial/components/invoice/InvoiceEntriesTable.vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<div class="flex flex-column justify-content-between gap-2">
<InputSpan :label="t('c_invoiceInfo.Amount')"
:value="form.model.amount.value.value"
:attributes="form.model.amount.attr.value"
@update:value="form.context.setFieldValue('amount', $event)"
:errors="form.context.errors.value.amount"
id="name" placeholder="0" type="currency" :disabled="!edit"/>
The current total on the invoice is {{ formatPrice(entryTotal) }}
<!-- {{ entryTotal }}-->
</div>
</template>

<script setup lang="ts">
import InputSpan from "@/components/InputSpan.vue";
import { useI18n } from "vue-i18n";
import { useToast } from "primevue/usetoast";
import { useInvoiceStore } from "@/stores/invoice.store";
import { computed, type PropType } from "vue";
import type { InvoiceResponse } from "@sudosos/sudosos-client";
import { type Form, setSubmit } from "@/utils/formUtils";
import * as yup from "yup";
import { updateInvoiceAddressingObject, updateInvoiceAmountObject } from "@/utils/validation-schema";
import { handleError } from "@/utils/errorUtils";
import { formatPrice } from "@/utils/formatterUtils";
import type { DineroObject } from "dinero.js";
const { t } = useI18n();
const toast = useToast();
const emit = defineEmits(['update:edit']);
const invoiceStore = useInvoiceStore();
const entryTotal = computed(() => {
const total: DineroObject = { amount: 0, precision: 2, currency: 'EUR' };
props.invoice.invoiceEntries.forEach((entry) => {
console.error(entry.amount, entry.priceInclVat.amount, entry.amount * entry.priceInclVat.amount);
total.amount += ((entry.amount * entry.priceInclVat.amount));
});
return total;
});
const props = defineProps({
invoice: {
type: Object as PropType<InvoiceResponse>,
required: true
},
form: {
type: Object as PropType<Form<yup.InferType<typeof updateInvoiceAmountObject>>>,
required: true,
},
edit: {
type: Boolean,
required: false,
default: false,
},
});
setSubmit(props.form, props.form.context.handleSubmit(async (values) => {
await invoiceStore.updateInvoice(props.invoice.id, { amount: {
amount: Math.round(values.amount * 100),
currency: 'EUR',
precision: 2
} }).then(() => {
toast.add({
severity: 'success',
summary: t('successMessages.success'),
detail: t('c_invoiceInfo.AmountUpdated'),
life: 3000,
});
emit('update:edit', false);
}).catch((error) => {
handleError(error, toast);
});
}));
</script>

<style scoped lang="scss">
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
<div>
<div class="page-title flex flex-row">
<div class="flex flex-column">
<span>{{ isCredit ? t("modules.financial.invoice.credit") : t("modules.financial.invoice.invoice") }}</span>
<span>{{t("modules.financial.invoice.invoice") }}</span>
<small class="text-base">
{{ invoice.reference + ": " }} <i>{{ invoice.description }}</i>
</small>
<span v-if="dirty" class="text-red-500">
<i class="pi pi-exclamation-triangle text-4xl"></i>
{{ t("modules.financial.invoice.dirty") }}
</span>
</div>
</div>
<div class="flex flex-row gap-5 flex-wrap flex-grow justify-content-center">
<div class="flex flex-column gap-5">
<InvoiceStepsCard :invoiceId="invoice.id"/>
<InvoiceAmountCard v-if="dirty" :invoiceId="invoice.id"/>
<InvoiceStepsCard v-else :invoiceId="invoice.id"/>
<InvoiceSettingsCard :invoiceId="invoice.id"/>
<InvoiceAddressingCard :invoiceId="invoice.id"/>
<InvoiceInfo :invoiceId="invoice.id"/>
Expand Down Expand Up @@ -39,6 +44,8 @@ import InvoiceStepsCard from "@/modules/financial/components/invoice/InvoiceStep
import InvoicePdf from "@/modules/financial/components/invoice/InvoicePdf.vue";
import InvoiceInfo from "@/modules/financial/components/invoice/InvoiceInfo.vue";
import { useI18n } from "vue-i18n";
import InvoiceAmountCard from "@/modules/financial/components/invoice/InvoiceAmountCard.vue";
import { isDirty } from "@/utils/invoiceUtil";
const { t } = useI18n();
Expand All @@ -48,10 +55,8 @@ const route = useRoute();
const invoice: Ref<InvoiceResponse | undefined> = ref(undefined);
const invoiceStore = useInvoiceStore();
const isCredit = computed(() => {
if (!invoice.value) return false;
return invoice.value?.transfer?.to === undefined;
});
// Invoice is condired dirty if entry total does not match transfer total
const dirty = computed(() => isDirty(invoice.value as InvoiceResponse));
onBeforeMount(async () => {
const id = Number(route.params.id);
Expand Down
6 changes: 6 additions & 0 deletions apps/dashboard/src/utils/invoiceUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { InvoiceResponse } from "@sudosos/sudosos-client";

export function isDirty(invoice: InvoiceResponse): boolean {
const entryTotal = invoice.invoiceEntries.reduce((sum, entry) => sum + entry.priceInclVat.amount * entry.amount, 0);
return entryTotal !== invoice.transfer.amountInclVat.amount;
}
4 changes: 4 additions & 0 deletions apps/dashboard/src/utils/validation-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export const updateInvoiceAddressingObject = yup.object({
country: yup.string().required(),
});

export const updateInvoiceAmountObject = yup.object({
amount: yup.number().required().default(0),
});

export const updateUserDetailsObject = yup.object({
firstName: yup.string().required(),
lastName: yup.string(),
Expand Down

0 comments on commit 234b052

Please sign in to comment.