diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/placeholderlogo.png | bin | 0 -> 10419 bytes | |||
-rw-r--r-- | src/assets/placeholderqr.png | bin | 0 -> 6999 bytes | |||
-rw-r--r-- | src/classes/user.ts | 21 | ||||
-rw-r--r-- | src/components/InvoiceHeaderEditor.vue (renamed from src/components/invoice_header_editor.vue) | 28 | ||||
-rw-r--r-- | src/components/PrintPreview.vue | 31 | ||||
-rw-r--r-- | src/components/PrintPreviewHeader.vue | 99 | ||||
-rw-r--r-- | src/components/PrintPreviewItemsList.vue | 59 | ||||
-rw-r--r-- | src/components/PrintPreviewRecipientDetails.vue | 146 | ||||
-rw-r--r-- | src/components/invoice_header.vue | 1 | ||||
-rw-r--r-- | src/views/NewInvoice.vue | 4 | ||||
-rw-r--r-- | src/views/ViewInvoice.vue | 40 |
11 files changed, 394 insertions, 35 deletions
diff --git a/src/assets/placeholderlogo.png b/src/assets/placeholderlogo.png Binary files differnew file mode 100644 index 0000000..6675ddd --- /dev/null +++ b/src/assets/placeholderlogo.png diff --git a/src/assets/placeholderqr.png b/src/assets/placeholderqr.png Binary files differnew file mode 100644 index 0000000..634fe46 --- /dev/null +++ b/src/assets/placeholderqr.png diff --git a/src/classes/user.ts b/src/classes/user.ts new file mode 100644 index 0000000..dc18717 --- /dev/null +++ b/src/classes/user.ts @@ -0,0 +1,21 @@ +export default class User { + FullName: string + FirmName: string + Gstin: string + Phone: string + Email: string + Website: string + Username: string + IsVerified: bool + + constructor() { + this.FullName = '' + this.FirmName = '' + this.Gstin = '' + this.Phone = '' + this.Email = '' + this.Website = '' + this.Username = '' + this.IsVerified = false + } +} diff --git a/src/components/invoice_header_editor.vue b/src/components/InvoiceHeaderEditor.vue index 83c5281..6fe8a25 100644 --- a/src/components/invoice_header_editor.vue +++ b/src/components/InvoiceHeaderEditor.vue @@ -4,6 +4,7 @@ import { useRouter } from 'vue-router' import axios from 'axios' import { useToast } from 'vue-toast-notification' import Customer from "./../classes/customer" +import User from "./../classes/user" const toast = useToast({ position: 'top-right' @@ -16,6 +17,7 @@ const submitting = ref(false) // shows spinner on continue button const allCustomers = ref([]) const customer = ref(new Customer()) +const user = ref(new User()) const customerSelection = ref(null) const invoiceDate = ref(new Date().toISOString().substr(0, 10)) @@ -36,12 +38,31 @@ const getAllCustomers = async () => { gettingData.value = false } +const getUser = async () => { + user.value = new User() + gettingData.value = true + + try { + const r = await axios.get('/user') + if (r.status === 200) { + user.value = r.data.data + } + } catch (err) { + toast.error('An unhandled exception occoured. Please check logs') + console.error(err) + } + + gettingData.value = false + console.log(user) +} + const submit = async (e: Event) => { e.preventDefault() submitting.value = true try { const c = toRaw(customer.value) + const u = toRaw(user.value) const res = await axios.post('/invoice', { "invoicedate": new Date(toRaw(invoiceDate.value)).toISOString(), @@ -68,6 +89,12 @@ const submit = async (e: Event) => { "customerphone": c.Phone, "customeremail": c.Email, "customerwebsite": c.Website, + "issuerfirmname": u.FirmName, + "issuerfirmaddress": u.AddressText, + "issuerfirmgstin": u.Gstin, + "issuerfirmphone": u.Phone, + "issuerfirmemail": u.Email, + "issuerfirmwebsite": u.Website, }) route.push({ name: "edit-invoice", params: { id: res.data.data.ID }}) @@ -110,6 +137,7 @@ const refreshCustomer = async () => { } onMounted(() => { + getUser() getAllCustomers() }) </script> diff --git a/src/components/PrintPreview.vue b/src/components/PrintPreview.vue new file mode 100644 index 0000000..ecbda88 --- /dev/null +++ b/src/components/PrintPreview.vue @@ -0,0 +1,31 @@ +<script setup lang="ts"> + import PrintPreviewHeader from './PrintPreviewHeader.vue' + import PrintPreviewRecipientDetails from './PrintPreviewRecipientDetails.vue' + import PrintPreviewItemsList from './PrintPreviewItemsList.vue' + + const props = defineProps(["invoice"]) + + setTimeout(() => { + console.log(props.invoice) + }, 1000) +</script> + +<template> + <div class="print-preview"> + <PrintPreviewHeader + :invoice="props.invoice" /> + <PrintPreviewRecipientDetails + :invoice="props.invoice"/> + <PrintPreviewItemsList + :items="props.invoice.Items"/> + </div> +</template> + +<style> +.print-preview * { + font-size: 9pt; +} +.print-preview p { + margin: 0; +} +</style> diff --git a/src/components/PrintPreviewHeader.vue b/src/components/PrintPreviewHeader.vue new file mode 100644 index 0000000..33f82f2 --- /dev/null +++ b/src/components/PrintPreviewHeader.vue @@ -0,0 +1,99 @@ +<script setup lang="ts"> + const props = defineProps(["invoice"]) +</script> + +<template> + <div class="print-preview-header"> + <div class="logo-container"> + <img src="../assets/placeholderlogo.png"/> + </div> + + <div> + <div class="firm-name"> + <p>{{ props.invoice.IssuerFirmName }}</p> + </div> + <div class="firm-address"> + {{ props.invoice.IssuerFirmAddress }} + </div> + </div> + + <div class="header-col3"> + <div class="firm-contact"> + <div class="label"> + <span><strong>Phone:</strong></span> + <span>{{ props.invoice.IssuerFirmPhone }}</span> + </div> + <div class="label"> + <span><strong>Email:</strong></span> + <span>{{ props.invoice.IssuerFirmEmail }}</span> + </div> + <div class="label"> + <span><strong>Website:</strong></span> + <span>{{ props.invoice.IssuerFirmWebsite }}</span> + </div> + <div class="label"> + <span><strong>GSTIN:</strong></span> + <span>{{ props.invoice.IssuerFirmGstin }}</span> + </div> + </div> + <div class="qr-container"> + <img src="../assets/placeholderqr.png"/> + </div> + </div> + </div> +</template> + +<style> +.print-preview-header { + display: grid; + grid-template-columns: 1.7in 2.4in auto; + grid-column-gap: 1em; + width: 100%; + margin-bottom: 1em; +} +.logo-container { + display: flex; + justify-content: center; + align-items: flex-start; + max-width: 1.7in; +} +.logo-container img { + max-width: 100%; + max-height: 100%; +} +.firm-name { + height: 2em; +} +.firm-name p { + line-height: 0.8em; + font-size: 1.6em; + font-weight: bold; +} +.firm-address { + font-size: 0.9em; + line-height: 1.2em; + white-space: pre-wrap; +} +.header-col3 { + display: grid; + grid-template-columns: auto 0.8in; +} +.firm-contact { + display: flex; + flex-direction: column; + font-size: 0.9em; +} +.firm-contact .label { + display: grid; + grid-template-columns: 4.5em auto; +} +.qr-container { + display: flex; + justify-content: flex-end; + align-items: flex-start; + width: 100%; +} +.qr-container img { + max-width: 100%; +} +</style> diff --git a/src/components/PrintPreviewItemsList.vue b/src/components/PrintPreviewItemsList.vue new file mode 100644 index 0000000..72f0fc1 --- /dev/null +++ b/src/components/PrintPreviewItemsList.vue @@ -0,0 +1,59 @@ +<script setup lang="ts"> + const props = defineProps(["items"]) +</script> + +<template> + <div class="items-list-wrapper"> + <div class="items-list-header"> + <div class="cell">#</div> + <div class="cell">Item Name</div> + <div class="cell">HSN</div> + <div class="cell">Qty</div> + <div class="cell">Unit Price</div> + <div class="cell">Discount</div> + <div class="cell">Taxable Value</div> + <div class="cell">GST</div> + <div class="cell">Total</div> + </div> + <div class="item-list"> + <div v-for="(item, index) in props.items" :key="item['id']" class="item-list-row"> + <div class="cell">{{ index + 1 }}</div> + <div class="cell">{{ item.Name }}</div> + <div class="cell">{{ item.HSN }}</div> + <div class="cell">1</div> + <div class="cell">100</div> + <div class="cell">0</div> + <div class="cell">10</div> + <div class="cell">18</div> + <div class="cell">1000</div> + </div> + </div> + </div> +</template> + +<style> +.items-list-wrapper { + margin-top: 1em; + border-top: 1px solid gray; + border-left: 1px solid gray; +} +.cell { + border-right: 1px solid gray; + border-bottom: 1px solid gray; +} +.items-list-header, .item-list-row { + display: grid; + grid-template-columns: 0.3in 2in 0.5in 0.5in 0.5in 0.6in 0.77in 1.5in 0.6in; +} +.items-list-header .cell { + font-weight: bold; + text-align: center; + display: flex; + align-items: center; + justify-content: center; +} +.item-list { + display: flex; + flex-direction: column; +} +</style> diff --git a/src/components/PrintPreviewRecipientDetails.vue b/src/components/PrintPreviewRecipientDetails.vue new file mode 100644 index 0000000..6f1f5b7 --- /dev/null +++ b/src/components/PrintPreviewRecipientDetails.vue @@ -0,0 +1,146 @@ +<script setup lang="ts"> + const props = defineProps(["invoice"]) +</script> + +<template> + <div class="print-preview-recipient-info"> + <div class="recipient-info--row1"> + <div class="recipient-info--col1"> + </div> + <div class="recipient-info--col2"> + <span class="invoice-label"> + <span><strong>INVOICE</strong></span> + <span>Original for Recipient</span> + </span> + </div> + </div> + <div class="recipient-info--row2"> + <div class="recipient-info--col1 recipient-contact"> + <div class="label"> + <span><strong>Bill To</strong></span> + <span>{{ props.invoice.CustomerName }}</span> + </div> + <div class="label"> + <span><strong>Address</strong></span> + <span> + {{ props.invoice.BillingAddress.AddressText }}<br> + {{ props.invoice.BillingAddress.City }} + ({{ props.invoice.BillingAddress.PostalCode }}), + {{ props.invoice.BillingAddress.State }}, + {{ props.invoice.BillingAddress.Country }} + </span> + </div> + <div class="label"> + <span><strong>Contact</strong></span> + <span>{{ props.invoice.CustomerContactName }}</span> + </div> + <div class="label"> + <span><strong>Phone</strong></span> + <span>{{ props.invoice.CustomerPhone }}</span> + </div> + <div class="label"> + <span><strong>Email</strong></span> + <span>{{ props.invoice.CustomerEmail }}</span> + </div> + <div class="label"> + <span><strong>Website</strong></span> + <span>{{ props.invoice.CustomerWebsite }}</span> + </div> + <div class="label"> + <span><strong>GSTIN</strong></span> + <span>{{ props.invoice.CustomerGstin }}</span> + </div> + </div> + <div class="recipient-info--col2"> + <div class="invoice-meta"> + <div class="label"> + <span><strong>Invoice Number: </strong></span> + <span>{{ props.invoice.InvoiceNumber }}</span> + </div> + <div class="label"> + <span><strong>Invoice Date: </strong></span> + <span>{{ new Date(props.invoice.InvoiceDate).toLocaleDateString() }}</span> + </div> + </div> + <div class="shipping-address"> + <div class="label"> + <span><strong>Shipping Address</strong></span> + <span> + {{ props.invoice.ShippingAddress.AddressText }}<br> + {{ props.invoice.ShippingAddress.City }} + ({{ props.invoice.ShippingAddress.PostalCode }}), + {{ props.invoice.ShippingAddress.State }}, + {{ props.invoice.ShippingAddress.Country }} + </span> + + <span><strong>City</strong></span> + <span>{{ props.invoice.ShippingAddress.City }}</span> + + <span><strong>State</strong></span> + <span>{{ props.invoice.ShippingAddress.State }}</span> + + <span><strong>ZIP Code</strong></span> + <span>{{ props.invoice.ShippingAddress.PostalCode }}</span> + + <span><strong>Country</strong></span> + <span>{{ props.invoice.ShippingAddress.Country }}</span> + </div> + </div> + </div> + </div> + </div> +</template> + +<style> +.print-preview-recipient-info { + border: 1px solid gray; + display: grid; + grid-template-rows: 2em auto; + width: 100%; +} +.recipient-info--row1, +.recipient-info--row2 { + display: grid; + grid-template-columns: 3fr 4fr; +} +.recipient-info--row1 { + border-bottom: 1px solid gray; +} +.recipient-info--col1 { + border-right: 1px solid gray; +} +.recipient-info--col1 { + padding: 0.1em 0.3em; +} +.invoice-label { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 0.3em; +} +.recipient-contact { + display: flex; + flex-direction: column; +} +.recipient-contact .label { + display: grid; + grid-template-columns: 5em auto; +} +.invoice-meta { + width: 100%; + border-bottom: 1px solid gray; + padding: 0.3em 0.3em; + display: grid; + grid-template-columns: 1fr 1fr; +} +.invoice-meta .label, .shipping-address .label { + display: grid; + grid-template-columns: 9em auto; +} +.shipping-address { + width: 100%; + padding: 0.1em 0.3em; +} +</style> diff --git a/src/components/invoice_header.vue b/src/components/invoice_header.vue index 5a57fc8..fe35980 100644 --- a/src/components/invoice_header.vue +++ b/src/components/invoice_header.vue @@ -1,5 +1,4 @@ <script setup lang="ts"> -import { defineProps } from 'vue' const props = defineProps(["invoice"]) </script> diff --git a/src/views/NewInvoice.vue b/src/views/NewInvoice.vue index 06d3ec7..2e2230c 100644 --- a/src/views/NewInvoice.vue +++ b/src/views/NewInvoice.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> -import invoiceHeaderEditor from './../components/invoice_header_editor.vue' +import InvoiceHeaderEditor from './../components/InvoiceHeaderEditor.vue' </script> <template> - <invoiceHeaderEditor /> + <InvoiceHeaderEditor /> </template> diff --git a/src/views/ViewInvoice.vue b/src/views/ViewInvoice.vue index 5f2a783..b8b7f2d 100644 --- a/src/views/ViewInvoice.vue +++ b/src/views/ViewInvoice.vue @@ -11,6 +11,8 @@ import invoiceHeader from './../components/invoice_header.vue' import invoiceItemsTable from './../components/invoice_items_table.vue' import invoiceSummary from './../components/invoice_summary.vue' +import PrintPreview from './../components/PrintPreview.vue' + const toast = useToast({ position: 'top-right' }) @@ -52,50 +54,24 @@ onMounted(() => { <template> <div id="print-preview" class="bg-white text-black"> - <invoiceHeader - :invoice="invoice" /> - <invoiceItemsTable - preview=true - :items="items" - :isLoading="itemsTableIsLoading" /> - <invoiceSummary - :items="items" - :isLoading="itemsTableIsLoading" - /> + <PrintPreview :invoice="invoice"/> </div> <button id="print-button" class="btn btn-primary" @click="handlePrint">Print</button> </template> <style> #print-preview { - width: 670px; -} -#print-preview * { - font-size: 12pt !important; -} -#print-preview .sup { -} -#print-preview table { - width: 100%; -} -#print-preview table * { - font-size: 10pt !important; -} -#print-preview .invoice-items-table { - margin-bottom: auto !important; -} -#print-preview .invoice-summary { - margin-top: auto !important; -} -#print-preview .item-name-cell span { - max-width: 150pt !important; + max-height: 90vh; + /*display: none;*/ + aspect-ratio: 1 / 1.414; } @media print { body { background-color: white; } #print-preview { - width: auto !important; + display: block; + width: 100% !important; height: 100% !important; } #sidebar, #navbar, #print-button, .btn { |