Skip to content

Commit

Permalink
feat: creating invoices
Browse files Browse the repository at this point in the history
  • Loading branch information
JustSamuel committed Nov 2, 2024
1 parent 391cd59 commit 834deda
Show file tree
Hide file tree
Showing 32 changed files with 1,108 additions and 370 deletions.
4 changes: 2 additions & 2 deletions apps/dashboard/src/components/ActionButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { ref, watch } from 'vue';
import Button from 'primevue/button';
const props = defineProps({
Expand All @@ -27,7 +27,7 @@ const props = defineProps({
},
});
const emits = defineEmits(['click']);
defineEmits(['click']);
const buttonIcon = ref('pi pi-check');
const buttonSeverity = ref('primary');
Expand Down
34 changes: 22 additions & 12 deletions apps/dashboard/src/components/FindUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,11 @@ import { onMounted, type PropType, ref, watch } from "vue";
import type { Ref } from "vue";
import apiService from "@/services/ApiService";
import { debounce } from "lodash";
import type { BaseUserResponse, UserResponse } from "@sudosos/sudosos-client";
import { type BaseUserResponse, GetAllUsersTypeEnum, type UserResponse } from "@sudosos/sudosos-client";
const lastQuery = ref("");
const selectedUser = ref(null);
const loading = ref(false);
const users: Ref<(BaseUserResponse & { fullName: string })[]> = ref([]);
const emits = defineEmits(['update:value']);
defineProps({
const props = defineProps({
value: {
type: Object as PropType<UserResponse>,
},
Expand All @@ -40,8 +35,24 @@ defineProps({
required: false,
default: ''
},
type: {
type: String as PropType<GetAllUsersTypeEnum>,
required: false,
default: undefined
},
take: {
type: Number,
required: false,
default: 10,
}
});
const lastQuery = ref("");
const selectedUser = ref(null);
const loading = ref(false);
const users: Ref<(BaseUserResponse & { fullName: string })[]> = ref([]);
const transformUsers = (userData: BaseUserResponse[]) => {
return userData.map((user: BaseUserResponse) => ({
...user,
Expand All @@ -51,8 +62,8 @@ const transformUsers = (userData: BaseUserResponse[]) => {
const debouncedSearch = debounce((e: any) => {
loading.value = true;
apiService.user.getAllUsers(50, 0, e.value).then((res) => {
users.value = transformUsers(res.data.records); // Transform users
apiService.user.getAllUsers(props.take, 0, e.value, undefined, undefined, undefined, props.type).then((res) => {
users.value = transformUsers(res.data.records);
}).finally(() => {
loading.value = false;
});
Expand All @@ -67,15 +78,14 @@ const filterUsers = (e: any) => {
};
onMounted(async () => {
apiService.user.getAllUsers(10, 0).then((res) => {
users.value = transformUsers(res.data.records); // Transform users
apiService.user.getAllUsers(props.take, 0, undefined, undefined, undefined, undefined, props.type).then((res) => {
users.value = transformUsers(res.data.records);
});
});
watch(selectedUser, () => {
emits('update:value', selectedUser.value);
});
</script>

<style scoped lang="scss">
Expand Down
10 changes: 8 additions & 2 deletions apps/dashboard/src/components/InputUserSpan.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<span class="my-0">{{ label }}</span>
<FindUser :placeholder="placeholder"
v-model="internalValue"
:disabled="disabled"/>
:disabled="disabled"
:type="type"/>
</span>
<div class="flex justify-content-end">
<ErrorSpan :error="errors"/>
Expand All @@ -17,7 +18,7 @@
import ErrorSpan from "@/components/ErrorSpan.vue";
import { onMounted, type PropType, ref, watch } from "vue";
import FindUser from "@/components/FindUser.vue";
import type { BaseUserResponse } from "@sudosos/sudosos-client";
import { type BaseUserResponse, GetAllUsersTypeEnum } from "@sudosos/sudosos-client";
const emit = defineEmits(['update:value']);
Expand Down Expand Up @@ -48,6 +49,11 @@ const props = defineProps({
required: false,
default: false
},
type: {
type: String as PropType<GetAllUsersTypeEnum>,
required: false,
default: undefined
}
});
const internalValue = ref();
Expand Down
3 changes: 2 additions & 1 deletion apps/dashboard/src/components/TopNavbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ const getOrgans = async () => {
const getAllOrgans = async () => {
organs.value = [];
const promises: Promise<any>[] = [];
await apiService.user.getAllUsers(100, undefined, undefined, undefined, undefined, undefined, GetAllUsersTypeEnum.Organ).then((res) => {
await apiService.user.getAllUsers(100, undefined, undefined, undefined, undefined,
undefined, GetAllUsersTypeEnum.Organ).then((res) => {
const orgs = res.data.records;
orgs.forEach((organ) => {
promises.push(apiService.balance.getBalanceId(organ.id).then((res) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
{{ formatPrice((mutation.data as FinancialMutation).amount) }}
</div>

<!-- Fines get green -->
<!-- Fines get red -->
<div v-else-if="isFine(mutation.data.type)" style="color: #d40000"
class="font-bold">
{{ formatPrice((mutation.data as FinancialMutation).amount, true) }}
Expand Down
3 changes: 2 additions & 1 deletion apps/dashboard/src/locales/en/modules/financial.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"missingPdf": "PDF is missing or generation failed",
"deleted": "Invoice has been deleted.",
"dirty": "Invoice amount does not match transfer amount.",
"total": "The current total on the invoice is {total}"
"total": "The current total on the invoice is {total}",
"create": "Create invoice"
},
"payout": {
"title": "Payout overview",
Expand Down
3 changes: 2 additions & 1 deletion apps/dashboard/src/locales/nl/modules/financial.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"missingPdf": "PDF ontbreekt of genereren is mislukt",
"deleted": "Factuur is verwijderd.",
"dirty": "De factuurbedrag komt niet overeen met de overboeking.",
"total": "Het huidige totaal op de factuur is {total}"
"total": "Het huidige totaal op de factuur is {total}",
"create": "Factuur aanmaken"
},
"payout": {
"title": "Uitbetalingsoverzicht",
Expand Down
7 changes: 2 additions & 5 deletions apps/dashboard/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ import FileUpload from "primevue/fileupload";
import Tooltip from 'primevue/tooltip';
import SelectButton from "primevue/selectbutton";
import 'primeflex/primeflex.css';
import { clearTokenInStorage, populateStoresFromToken, useAuthStore } from "@sudosos/sudosos-frontend-common";
import ToastService from "primevue/toastservice";
import Toast from "primevue/toast";
import 'primeflex/primeflex.css';
import 'primeicons/primeicons.css'; // Import PrimeIcons

import 'primeicons/primeicons.css';
import 'primeflex/primeflex.css';
import apiService from './services/ApiService';
import Accordion from "primevue/accordion";
import Skeleton from "primevue/skeleton";
import IconField from "primevue/iconfield";
Expand Down Expand Up @@ -68,7 +65,6 @@ app.component('DataTable', DataTable);
app.component('DataView', DataView);
app.component('InputNumber', InputNumber);
app.component('Dialog', Dialog);
app.component('Divider', Divider);
app.component('Dropdown', Dropdown);
app.component('Checkbox', Checkbox);
app.component('TabView', TabView);
Expand All @@ -86,6 +82,7 @@ app.component('ToggleButton', ToggleButton);
app.component('Steps', Steps);
app.component('Calendar', Calendar);
app.component('ConfirmDialog', ConfirmDialog);
app.component('Divider', Divider);

beforeLoad().then(() => {
app.mount('#app');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import FormCard from "@/components/FormCard.vue";
import { computed, onBeforeMount, ref, watch } from "vue";
import type { InvoiceResponse } from "@sudosos/sudosos-client";
import InvoiceAddressingForm from "@/modules/financial/components/invoice/forms/InvoiceAddressingForm.vue";
import InvoiceAddressingForm from "@/modules/financial/components/invoice/forms/InvoiceUpdateAddressingForm.vue";
import { updateInvoiceAddressingObject } from "@/utils/validation-schema";
import { schemaToForm } from "@/utils/formUtils";
import { InvoiceStatusResponseStateEnum } from "@sudosos/sudosos-client/src/api";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ onBeforeMount(() => {
updateFieldValues(invoice.value);
}
});
</script>

<style scoped lang="scss">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
import { formatPrice } from "@/utils/formatterUtils";
import Column from "primevue/column";
import DataTable from "primevue/datatable";
import { computed, onMounted, type PropType, type Ref, ref } from "vue";
import { 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";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<template>
<CardComponent header="Create Invoice">
<div class="flex flex-column justify-content-between gap-2">
<span class="p-error"> <i class="pi pi-exclamation-circle"/>{{ " "+ t('modules.financial.invoice.create.warning') }}</span>
<div class="flex flex-row justify-content-end">
<ActionButton
type="submit"
:disabled="disabled"
:label="t('common.create')"
:submitting="form.context.isSubmitting.value"
:result="form.success?.value != null"
@click="form.submit"
/>
</div>
</div>
</CardComponent>
</template>

<script setup lang="ts">
import CardComponent from "@/components/CardComponent.vue";
import { computed, type PropType } from "vue";
import { type Form, getProperty, setSubmit, setSuccess } from "@/utils/formUtils";
import * as yup from "yup";
import { createInvoiceObject } from "@/utils/validation-schema";
import ActionButton from "@/components/ActionButton.vue";
import { useI18n } from "vue-i18n";
import type { CreateInvoiceRequest } from "@sudosos/sudosos-client";
import { useAuthStore } from "@sudosos/sudosos-frontend-common";
import apiService from "@/services/ApiService";
import { useToast } from "primevue/usetoast";
import { handleError } from "@/utils/errorUtils";
import type { AxiosResponse } from "axios";
import type { InvoiceResponse } from "@sudosos/sudosos-client/src/api";
import { useInvoiceStore } from "@/stores/invoice.store";
import { useRouter } from "vue-router";
const { t } = useI18n();
const toast = useToast();
const authStore = useAuthStore();
const invoiceStore = useInvoiceStore();
const router = useRouter();
const props = defineProps({
form: {
type: Object as PropType<Form<yup.InferType<typeof createInvoiceObject>>>,
required: true,
},
});
const disabled = computed(() => {
return !props.form?.context.meta.value.valid || getProperty(props.form, "forId") === undefined;
});
setSubmit(props.form, props.form.context.handleSubmit(async (values) => {
const byId = authStore.user?.id;
if (byId === undefined) {
setSuccess(props.form, false);
return;
}
const request: CreateInvoiceRequest = {
forId: values.forId,
byId,
addressee: values.addressee,
description: values.description,
reference: values.reference,
transactionIDs: values.transactionIDs,
street: values.street,
postalCode: values.postalCode,
city: values.city,
country: values.country,
date: values.date,
attention: values.attention,
amount: values.transactionTotal,
};
await apiService.invoices.createInvoice(request).then(async (invoiceResult: AxiosResponse<InvoiceResponse>) => {
toast.add({
severity: 'success',
summary: t('common.toast.success.success'),
detail: t('common.toast.success.invoiceCreated'),
life: 3000,
});
const invoice = invoiceResult.data;
console.error(invoice.id);
await invoiceStore.fetchInvoicePdf(invoice.id).finally(async () => {
await router.push({ name: 'invoiceInfo', params: { id: invoice.id } });
setSuccess(props.form, true);
props.form.context.resetForm();
});
}).catch((error) => {
setSuccess(props.form, false);
handleError(error, toast);
});
}));
</script>

<style scoped lang="scss">
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<FormCard :header="t('modules.financial.forms.invoice.addressing')" :enable-edit="false">
<div class="flex flex-column justify-content-between gap-2">
<InvoiceBaseAddressingForm :form="form" :edit="edit"/>
</div>
</FormCard>
</template>

<script setup lang="ts">
import FormCard from "@/components/FormCard.vue";
import { computed, type PropType } from "vue";
import { createInvoiceObject } from "@/utils/validation-schema";
import { type Form, getProperty } from "@/utils/formUtils";
import { useI18n } from "vue-i18n";
import * as yup from "yup";
import InvoiceBaseAddressingForm from "@/modules/financial/components/invoice/forms/InvoiceBaseAddressingForm.vue";
const { t } = useI18n();
const props = defineProps({
form: {
type: Object as PropType<Form<yup.InferType<typeof createInvoiceObject>>>,
required: true,
},
});
const edit = computed(() => {
const forId = getProperty(props.form, "forId");
return forId !== undefined;
});
</script>

<style scoped lang="scss">
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<FormCard :header="t('modules.financial.forms.invoice.settings')" :enable-edit="false">
<div class="flex flex-column justify-content-between gap-2">
<InvoiceBaseSettingsForm :form="form" :edit="edit"/>
</div>
</FormCard>
</template>

<script setup lang="ts">
import FormCard from "@/components/FormCard.vue";
import { computed, type PropType } from "vue";
import { createInvoiceObject } from "@/utils/validation-schema";
import { type Form, getProperty } from "@/utils/formUtils";
import { useI18n } from "vue-i18n";
import * as yup from "yup";
import InvoiceBaseSettingsForm from "@/modules/financial/components/invoice/forms/InvoiceBaseSettingsForm.vue";
const { t } = useI18n();
const props = defineProps({
form: {
type: Object as PropType<Form<yup.InferType<typeof createInvoiceObject>>>,
required: true,
},
});
const edit = computed(() => {
const forId = getProperty(props.form, "forId");
return forId !== undefined;
});
</script>

<style scoped lang="scss">
</style>
Loading

0 comments on commit 834deda

Please sign in to comment.