Untitled
unknown
plain_text
2 years ago
15 kB
5
Indexable
<template> <tr> <td v-if="!$vuetify.breakpoint.mobile" width="40%" class="d-block d-sm-table-cell" > <div> <v-autocomplete outlined :items="items" :disabled="disabled" item-value="id" item-text="name" cache-items eager dense v-model="currentItem" @change="applyItem" return-object :no-data-text="$t('main.empty_product_service')" > </v-autocomplete> <label>Description</label> <v-textarea class="text-md" :disabled="disabled" v-model="description" @change="itemDescriptionChange" filled outlined auto-grow rows="2" dense ></v-textarea> </div> </td> <td v-if="!$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell" width="25%" > <div> <v-text-field type="number" dense outlined :disabled="disabled" @keyup="computeAmount" v-model="rawUnit_price" :prefix="invoiceSymbol" @change="itemPriceChange" > </v-text-field> <label>Tax</label> <v-select :disabled="disabled" :items="taxes" dense item-value="id" multiple item-text="display_name" :hint="tax_amount | toMoney | invoiceCurrencySymbol" v-model="applied_taxes" @change="calculateTax" persistent-hint > <template v-slot:prepend-item> <v-btn style="text-decoration: none" to="/settings?open=taxes" block rounded text color="blue" >Manage Taxes <v-icon small>mdi-arrow-right</v-icon></v-btn > </template> <template v-slot:selection="{ item, index }"> <div v-if="index === 0" class="chip"> <span class="text-sm">{{ item.name }}</span> </div> <span v-if="index === 1" class="grey--text text-caption"> (+{{ applied_taxes.length - 1 }} others) </span> </template> <template v-slot:item="{ item }"> <v-checkbox color="blue" :value="applied_taxes.includes(item.id)" ></v-checkbox> <span> {{ item.display_name }}({{ item.rate }}%) <small class="text--disabled d-block" v-if="item.type === 'Compound'" > Includes: <span v-for="st in item.sub_tax" >{{ st.name }}({{ st.rate }}%), </span> </small> </span> </template> </v-select> </div> </td> <td v-if="!$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell" width="10%" > <v-text-field type="number" dense outlined :disabled="disabled" @keyup="computeAmount" @change="itemQuantityChange" v-model="invoice_quantity" :rules="requiredRules" > </v-text-field> <v-menu max-width="500px" min-width="400px" :close-on-content-click="false" transition="slide-y-transition" > <template v-slot:activator="{ on, attr }"> <v-btn v-on="on" v-bind="attr" color="blue" text :disabled="!Boolean(currentItem)" > Discount:{{ discount_amount | toMoney | invoiceCurrencySymbol }} <v-icon>mdi-chevron-down</v-icon> </v-btn> </template> <v-list> <v-list-item> <v-list-item-content> <v-list-item-title >Amount:{{ discount_amount | toMoney | invoiceCurrencySymbol }}</v-list-item-title > <v-list-item-subtitle >{{ Number(discount_percent).toFixed(2) }}%</v-list-item-subtitle > </v-list-item-content> </v-list-item> <v-list-item> <v-switch color="blue" inset label="Enter discount % " v-model="percent" @click="switchPercent" > </v-switch> </v-list-item> <v-list-item v-if="percent"> <v-text-field outlined label="Discount percent" v-model="discount_percent" @input="cumputeDiscountAmount()" @blur="computeAmount()" ref="discountPinput" filled > </v-text-field> </v-list-item> <v-list-item v-else> <v-text-field outlined label="Absolute discount amount" v-model="discount_amount" @input="cumputePercentage()" @blur="computeAmount()" ref="discount_amount_input" filled > </v-text-field> </v-list-item> </v-list> </v-menu> </td> <td v-if="!$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell" width="40%" > <div class="d-flex justify-start"> <p class="m-0"> {{ invoice_amount.toFixed(2) | toMoney | invoiceCurrencySymbol }} </p> <v-btn color="red" icon small class="ml-3 m-0 up" @click="removeItem"> <v-icon>mdi-trash-can</v-icon> </v-btn> </div> <br /> <br /> <strong >Amount due:{{ (Number(amount_due) + Number(tax_amount)) | toMoney | invoiceCurrencySymbol }}</strong > </td> <!-- only way to make responsive is to have separate td for mobile --> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell mb-5"> <v-autocomplete outlined label="Item" :items="items" :disabled="disabled" item-value="id" item-text="name" cache-items eager dense v-model="currentItem" @change="applyItem" return-object :no-data-text="$t('main.empty_product_service')" > </v-autocomplete> </td> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell mb-5"> <v-textarea class="text-md" :disabled="disabled" v-model="description" label="Description" @change="itemDescriptionChange" filled outlined auto-grow rows="2" dense ></v-textarea> </td> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell mb-5"> <v-text-field type="number" dense label="Price" outlined :disabled="disabled" @keyup="computeAmount" v-model="rawUnit_price" :prefix="invoiceSymbol" @change="itemPriceChange" > </v-text-field> </td> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell mb-5"> <v-select :disabled="disabled" :items="taxes" dense item-value="id" multiple label="Tax Amount" item-text="display_name" :hint="tax_amount | toMoney | invoiceCurrencySymbol" v-model="applied_taxes" @change="calculateTax" persistent-hint > <template v-slot:selection="{ item, index }"> <div v-if="index === 0" small class="chip"> <span class="text-sm">{{ item.name }}</span> </div> <span v-if="index === 1" class="grey--text caption"> (+1) </span> </template> </v-select> </td> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell mb-5"> <v-text-field type="number" label="Quantity" dense outlined :disabled="disabled" @keyup="computeAmount" @change="itemQuantityChange" v-model="invoice_quantity" :rules="requiredRules" > </v-text-field> </td> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell"> <p class="m-0"> Amount: {{ invoice_amount.toFixed(4) | invoiceCurrencySymbol }} </p> </td> <td v-if="$vuetify.breakpoint.mobile" class="d-block d-sm-table-cell d-flex justify-center" > <v-btn color="red" icon large @click="removeItem"> <v-icon>mdi-trash-can</v-icon> </v-btn> </td> </tr> </template> <script> export default { name: "invoiceItem", props: ["items", "id", "defaultItem", "disabled", "index"], data() { return { discount_percent_input: 0, discount_percent: 0, discount_amount_input: 0, discount_amount: 0, percent: true, currentItem: null, description: "", rawUnit_price: "", applied_taxes: [], invoice_quantity: 0, tax_amount: 0, invoice_amount: 0, quantity_error: false, requiredRules: [(v) => !!v || this.$t("main.required")], }; }, watch: { discount_percent_input() { this.cumputeDiscountAmount(); this.cumputePercentage(); }, discount_amount_input() { this.cumputeDiscountAmount(); this.cumputePercentage(); }, defaultItem() { if (this.defaultItem) { this.currentItem = this.defaultItem; //this.applyItem(); //this.computeAmount(); } }, invoice_amount() { this.computeAmount(); }, }, computed: { amount_due() { return ( Number(this.invoice_amount) - Number(this.discount_amount) ).toFixed(2); }, taxes() { return this.$store.state.user.user_infor.current_business.taxes; }, invoiceSymbol() { return this.$store.state.invoiceCurrencySymbol; }, }, methods: { cumputePercentage() { const amount = Number(this.invoice_amount); const d = Number(this.discount_amount); const p = d > 0 ? (d / amount) * 100 : 0; if (this.percent) { this.discount_percent = Number(p).toFixed(2); } else { this.discount_percent = p } return p; }, cumputeDiscountAmount() { const amount = Number(this.invoice_amount); const p = Number(this.discount_percent); this.discount_amount = Number(p) > 0 ? ((Number(p) / 100) * Number(amount)).toFixed(2) : 0; return this.discount_amount; }, switchPercent() { if (this.percent) { this.discount_percent = Number(this.discount_percent).toFixed(2); console.log(this.discount_percent); } else { console.log(this.discount_percent); this.discount_percent = Number(this.discount_percent); } }, getUpdatedItem() { return { ...this.currentItem, description: this.description, unit_price: this.rawUnit_price, taxes: this.taxes, applied_taxes: this.applied_taxes, invoice_quantity: this.invoice_quantity, tax_amount: this.tax_amount, invoice_amount: this.invoice_amount, quantity_error: this.quantity_error, amount_due: Number(this.amount_due) + Number(this.tax_amount), discount_amount: this.discount_amount, discount_percent: this.discount_percent, }; }, applyItem() { if (this.currentItem && this.currentItem.id) { this.description = this.currentItem.description; this.rawUnit_price = this.currentItem.rawUnit_price; this.applied_taxes = this.currentItem.applied_taxes; this.invoice_quantity = this.currentItem.invoice_quantity; this.invoice_amount = this.currentItem.invoice_amount; this.discount_amount = this.currentItem.discount_amount; this.discount_percent = this.currentItem.discount_percent; this.calculateTax(); } }, removeItem() { this.$emit("remove", this.index); }, itemDescriptionChange() { this.$emit("change", this.id, this.getUpdatedItem()); }, computeAmount() { this.cumputeDiscountAmount(); this.cumputePercentage(); if (this.invoice_quantity) { this.invoice_amount = Number(this.rawUnit_price) * Number(this.invoice_quantity); } this.calculateTax(); this.$emit("change", this.id, this.getUpdatedItem()); }, generateErrorMsg() { const item = this.currentItem; return `Invalid quantity provided for ${item.name}, you have ${item.quantity} of ${item.name} in stock`; }, hasQuantityError() { if ( this.currentItem && this.currentItem.track_inventory === 1 && this.invoice_quantity > this.currentItem.quantity ) { this.errorMsg = this.generateErrorMsg(); this.quantity_error = true; return true; } this.quantity_error = false; return false; }, calculateTaxes() { const taxObjs = this.taxes.filter((tax) => this.applied_taxes.includes(tax.id) ); let taxAmount = 0; taxObjs.forEach((taxObj) => { if (taxObj.type === "Flat") { taxAmount += (Number(taxObj.rate) / 100) * Number(this.amount_due); } else if (taxObj.type === "Compound") { const initialTax = (Number(taxObj.sub_rate) / 100) * Number(this.amount_due); const compoundAmount = initialTax + Number(this.amount_due); const compoundTax = (Number(taxObj.rate) / 100) * Number(compoundAmount); taxAmount += compoundTax + initialTax; } }); return taxAmount; }, calculateTax() { this.tax_amount = this.calculateTaxes(); this.$emit("change", this.id, this.getUpdatedItem()); }, itemPriceChange() { this.rawUnit_price ? (this.rawUnit_price = this.rawUnit_price) : (this.rawUnit_price = 0); this.computeAmount(); }, itemQuantityChange() { this.invoice_quantity ? (this.invoice_quantity = this.invoice_quantity) : (this.invoice_quantity = 1); this.computeAmount(); }, }, mounted() { if (this.defaultItem) { this.currentItem = this.defaultItem; this.applyItem(); } }, }; </script> <style scoped> td, td * { vertical-align: top !important; margin-top: 0.5rem !important; } .text-sm { font-size: 11px; } .text-md { font-size: 14px; } .text-bold { font-weight: 800; } .up { transform: translateY(-0.5rem); } .chip { padding: 0.3rem 1rem; background-color: #eeeeee; border-radius: 6rem; } </style>
Editor is loading...