aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--src/classes/invoice_item.ts21
-rw-r--r--src/components/invoice_header_editor.vue2
-rw-r--r--src/components/invoice_summary.vue9
-rw-r--r--src/components/sidebar.vue4
-rw-r--r--src/router/index.ts11
-rw-r--r--src/views/EditInvoice.vue31
-rw-r--r--src/views/HomeView.vue2
-rw-r--r--src/views/ViewInvoice.vue72
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>