diff options
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/classes/invoice_item.ts | 21 | ||||
-rw-r--r-- | src/components/invoice_header_editor.vue | 2 | ||||
-rw-r--r-- | src/components/invoice_summary.vue | 9 | ||||
-rw-r--r-- | src/components/sidebar.vue | 4 | ||||
-rw-r--r-- | src/router/index.ts | 11 | ||||
-rw-r--r-- | src/views/EditInvoice.vue | 31 | ||||
-rw-r--r-- | src/views/HomeView.vue | 2 | ||||
-rw-r--r-- | src/views/ViewInvoice.vue | 72 |
9 files changed, 120 insertions, 34 deletions
diff --git a/package.json b/package.json index 9daaba8..2f5c216 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openbills-web", - "version": "0.2.4", + "version": "0.3.0", "private": false, "scripts": { "dev": "vite", diff --git a/src/classes/invoice_item.ts b/src/classes/invoice_item.ts index a7a0a85..dcb917a 100644 --- a/src/classes/invoice_item.ts +++ b/src/classes/invoice_item.ts @@ -1,3 +1,5 @@ +import currency from "currency.js" + export default class InvoiceItem { UnitOfMeasure: string Quantity: string @@ -19,3 +21,22 @@ export default class InvoiceItem { this.BrandName = "" } } + +export const calculate = (items: InvoiceItem[]) => items.map((x: InvoiceItem) => { + const quantity = currency(x.Quantity) + const unitPrice = currency(x.UnitPrice) + const gstPercentage = currency(x.GSTPercentage) + const gstValue = unitPrice.multiply(gstPercentage).divide(100) + const totalGSTValue = gstValue.multiply(quantity) + const amountWithoutGST = unitPrice.multiply(quantity) + + return({ + ...x + , Quantity: quantity + , UnitPrice: unitPrice + , GSTValue: gstValue + , TotalGSTValue: totalGSTValue + , AmountWithoutGST: amountWithoutGST + , TotalAmount: amountWithoutGST.add(totalGSTValue) + }) +}) diff --git a/src/components/invoice_header_editor.vue b/src/components/invoice_header_editor.vue index 0a39ce5..6bd62b8 100644 --- a/src/components/invoice_header_editor.vue +++ b/src/components/invoice_header_editor.vue @@ -58,7 +58,7 @@ const submit = async (e: Event) => { "customerwebsite": c.Website, }) - route.push({ name: "edit-draft", params: { id: res.data.data.ID }}) + route.push({ name: "edit-invoice", params: { id: res.data.data.ID }}) } catch (err: any) { const statusCode: any = err.request.status const res: any = JSON.parse(err.request.response) diff --git a/src/components/invoice_summary.vue b/src/components/invoice_summary.vue new file mode 100644 index 0000000..6ca7907 --- /dev/null +++ b/src/components/invoice_summary.vue @@ -0,0 +1,9 @@ +<script setup lang="js"> +import currency from "currency.js" + +const props = defineProps(["items", "isLoading"]) +</script> + +<template> + Total: {{ props.items.reduce((t, c) => t.add(c.TotalAmount), currency(0)) }} +</template> diff --git a/src/components/sidebar.vue b/src/components/sidebar.vue index 4c4623f..ada509e 100644 --- a/src/components/sidebar.vue +++ b/src/components/sidebar.vue @@ -1,5 +1,5 @@ <script setup lang="js"> -import { watch, ref, toRaw } from 'vue' +import { watch, ref } from 'vue' import { RouterLink, useRoute } from 'vue-router' import { BIconHouseDoor, BIconPlusSquare, BIconPersonCircle, BIconStack, BIconBuildings } from 'bootstrap-icons-vue'; @@ -36,7 +36,7 @@ watch( <li class="nav-item"> <RouterLink to="/invoice/new" - :class="`nav-link ${(route.name === 'new-invoice' || route.name === 'edit-draft') ? 'active' : ''} py-3 border-secondary border-bottom text-white`" + :class="`nav-link ${(route.name === 'new-invoice' || route.name === 'edit-invoice') ? 'active' : ''} py-3 border-secondary border-bottom text-white`" aria-current="page" title="New Invoice" data-bs-toggle="tooltip" diff --git a/src/router/index.ts b/src/router/index.ts index 9817a1e..496f5d0 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -10,6 +10,7 @@ import AllItems from '../views/AllItems.vue' import NewItem from '../views/NewItem.vue' import NewInvoice from '../views/NewInvoice.vue' import EditInvoice from '../views/EditInvoice.vue' +import ViewInvoice from '../views/ViewInvoice.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -69,11 +70,17 @@ const router = createRouter({ meta: { isAuth: true } }, { - path: '/invoice/edit-draft/:id', - name: 'edit-draft', + path: '/invoice/edit/:id', + name: 'edit-invoice', component: EditInvoice, meta: { isAuth: true } }, + { + path: '/invoice/view/:id', + name: 'view-invoice', + component: ViewInvoice, + meta: { isAuth: true } + }, ] }) diff --git a/src/views/EditInvoice.vue b/src/views/EditInvoice.vue index adc172b..69afc7e 100644 --- a/src/views/EditInvoice.vue +++ b/src/views/EditInvoice.vue @@ -1,12 +1,11 @@ <script setup lang="ts"> -import { ref, onMounted, toRaw } from 'vue' +import { ref, onMounted } from 'vue' import { useRoute } from "vue-router" import { useToast } from 'vue-toast-notification' import axios from 'axios' -import currency from "currency.js" import Invoice from "./../classes/invoice" -import InvoiceItem from "./../classes/invoice_item" +import { calculate } from "./../classes/invoice_item" import invoiceHeader from './../components/invoice_header.vue' import itemSelector from './../components/item_selector.vue' @@ -25,28 +24,6 @@ const items = ref<any[]>([]) const invoiceIsLoading = ref(true) const itemsTableIsLoading = ref(true) -const processItems = (items: InvoiceItem[]) => items.map((x: InvoiceItem) => { - const quantity = currency(x.Quantity) - const unitPrice = currency(x.UnitPrice) - const gstPercentage = currency(x.GSTPercentage) - const gstValue = unitPrice.multiply(gstPercentage).divide(100) - const totalGSTValue = gstValue.multiply(quantity) - const amountWithoutGST = unitPrice.multiply(quantity) - - return({ - ...x - , Quantity: quantity - , UnitPrice: unitPrice - , GSTValue: gstValue - , TotalGSTValue: totalGSTValue - , AmountWithoutGST: amountWithoutGST - , TotalAmount: amountWithoutGST.add(totalGSTValue) - }) -}) - //{{ currency(item.UnitPrice).add(currency(item.UnitPrice).multiply(currency(item.GSTPercentage)).divide(100)).multiply(currency(item.Quantity)) }} - // {{ currency(item.UnitPrice).multiply(currency(item.Quantity)) }} - // {{ currency(item.UnitPrice).multiply(currency(item.GSTPercentage)).divide(100).multiply(item.Quantity) }} - const getInvoice = async () => { invoiceIsLoading.value = true itemsTableIsLoading.value = true @@ -54,7 +31,7 @@ const getInvoice = async () => { try { const r = await axios.get(`/invoice/${invoiceId}`) invoice.value = r.data.data - items.value = processItems(r.data.data.Items) + items.value = calculate(r.data.data.Items) } catch (err) { toast.error('An unhandled exception occoured. Please check logs') console.error(err) @@ -71,7 +48,7 @@ const refreshItems = async () => { try { const res = await axios.get(`/invoice/${invoiceId}/item`) if (res.status === 200) { - items.value = processItems(res.data.data) + items.value = calculate(res.data.data) } } catch (err) { toast.error('An unhandled exception occoured. Please check logs') diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 6ef6440..2092e6b 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -33,7 +33,7 @@ onMounted(() => { <h2>Draft Invoices:</h2> <ul> <li v-for="draft in allDrafts" :key="draft['ID']"> - <RouterLink :to="{path: `/invoice/edit-draft/${draft['ID']}`}">Invoice Number {{ draft["InvoiceNumber"] }}</RouterLink> + <RouterLink :to="{path: `/invoice/edit/${draft['ID']}`}">Invoice Number {{ draft["InvoiceNumber"] }}</RouterLink> </li> </ul> </template> diff --git a/src/views/ViewInvoice.vue b/src/views/ViewInvoice.vue new file mode 100644 index 0000000..c2a6558 --- /dev/null +++ b/src/views/ViewInvoice.vue @@ -0,0 +1,72 @@ +<script setup lang="ts"> +import { ref, onMounted } from 'vue' +import { useRoute } from "vue-router" +import { useToast } from 'vue-toast-notification' +import axios from 'axios' + +import Invoice from "./../classes/invoice" +import { calculate } from "./../classes/invoice_item" + +import invoiceHeader from './../components/invoice_header.vue' +import invoiceItemsTable from './../components/invoice_items_table.vue' +import invoiceSummary from './../components/invoice_summary.vue' + +const toast = useToast({ + position: 'top-right' +}) + +const route = useRoute() + +const invoiceId = route.params.id +const invoice = ref(new Invoice()) +const items = ref<any[]>([]) + +const invoiceIsLoading = ref(true) +const itemsTableIsLoading = ref(true) + +const getInvoice = async () => { + invoiceIsLoading.value = true + itemsTableIsLoading.value = true + + try { + const r = await axios.get(`/invoice/${invoiceId}`) + invoice.value = r.data.data + items.value = calculate(r.data.data.Items) + } catch (err) { + toast.error('An unhandled exception occoured. Please check logs') + console.error(err) + } + + invoiceIsLoading.value = false + itemsTableIsLoading.value = false +} + +const handlePrint = () => { + print() +} + +onMounted(() => { + getInvoice() +}) +</script> + +<template> + <invoiceHeader + :invoice="invoice" /> + <invoiceItemsTable + :items="items" + :isLoading="itemsTableIsLoading" /> + <invoiceSummary + :items="items" + :isLoading="itemsTableIsLoading" + /> + <button id="print-button" class="btn btn-primary" @click="handlePrint">Print</button> +</template> + +<style> +@media print { + #sidebar, #navbar, #print-button, .btn { + display: none !important; + } +} +</style> |