Untitled
unknown
plain_text
2 years ago
34 kB
4
Indexable
<template> <div> <div :class="$vuetify.theme.isDark ? 'bg-black' : 'bg-white'" v-if="!not_allowed" class="col-md-10 mx-auto col-sm-12 card" > <v-overlay :value="loading" :dark="false"> <v-progress-circular :size="50" color="green" indeterminate ></v-progress-circular> </v-overlay> <div class="card-header"> <v-row> <v-col cols="12" sm="2"> <v-btn text rounded color="green" to="/sales"> <v-icon>mdi-arrow-left</v-icon> {{ $t("main.back") }} </v-btn> </v-col> <v-col cols="12" sm="10"> <h3 class="font-weight-light"> {{ $t("quick_sale.create_new_sale") }} </h3> </v-col> </v-row> </div> <v-row> <v-col cols="12" sm="12" class="mx-auto"> <v-row> <v-col cols="12" sm="12" class="text-center"> <h1 class="font-weight-black"> {{ $t("main.amount_due") }}: {{ amount_due | toMoney | currency_symbol }} </h1> <h4 class="font-weight-light"> {{ $t("main.subtotal") }}: {{ gross_amount | toMoney | currency_symbol }} | {{ $t("main.taxes") }}: {{ tax_amount | toMoney | currency_symbol }} | {{ $t("main.discount") }}: ({{ discount_amount | toMoney | currency_symbol }}) </h4> </v-col> </v-row> <v-row> <v-col cols="12" sm="3"> <v-menu v-model="startDate_menu" :close-on-content-click="false" :nudge-right="40" transition="scale-transition" offset-y min-width="290px" > <template v-slot:activator="{ on }"> <v-text-field v-model="entrydate" :label="$t('main.entry_date')" readonly outlined="" v-on="on" dense ></v-text-field> </template> <v-date-picker v-model="entrydate" @input="startDate_menu = false" ></v-date-picker> </v-menu> <v-text-field outlined label="Change/Balance" type="number" v-model="balance" dense ></v-text-field> <h6 class="green--text font-weight-bold" v-if="balance > 0"> Amount Collected:{{ (Number(balance) + Number(amount_due)) | toMoney | currency_symbol }} </h6> </v-col> <v-col cols="12" sm="9"> <v-btn color="green" text small block @click="resetpayment"> Reset Payments <v-icon>mdi-reload</v-icon> </v-btn> <v-row v-for="(payment, index) in payments" :key="index" dense> <v-col cols="7"> <account-dropdown :dense="true" :accountTypes="['Assets']" :subAccounts="payment_accounts" :label="$t('main.payment_account')" :hint="$t('main.payment_account_hint')" :defaultAccount="payment.account_id" @accountChange="(id) => (payment.account_id = id)" ></account-dropdown> </v-col> <v-col cols="4"> <v-text-field dense outlined :label="$t('main.amount')" v-model="payment.amount" ></v-text-field> </v-col> <v-col cols="1"> <v-btn :disabled="payments.length < 2" icon color="red" @click="removePayment(index)" > <v-icon>mdi-delete-outline</v-icon> </v-btn> </v-col> </v-row> <v-btn block text color="green" rounded @click="addPayment" >Payment <v-icon>mdi-plus</v-icon> </v-btn> <v-alert color="red lighten-5 red--text" v-if="totalPayment != amount_due" > Total payment amount must be equal to amount due<br /> Total Payment:{{ totalPayment | toMoney | currency_symbol }}<br /> Amount Due:{{ amount_due | toMoney | currency_symbol }}<br /> <small >TIP: Click on <strong>reset payments</strong> button to reset it back to default</small > </v-alert> </v-col> </v-row> <v-row> <v-col cols="12" sm="12" class="mx-auto text-center"> <h5 v-if="!sales_items.length > 0" class="font-italic align-center" > {{ $t("quick_sale.add_item") }} </h5> <v-form ref="item_form" class="mb-2"> <v-simple-table v-if="sales_items" class="table-hover table-striped" > <tbody> <sale-item @remove="remove_item" :saving_progress="saving_progress" :taxes="taxes" @itemApplied="apply_item" :items="items" :item="item" v-for="(item, index) in sales_items" v-bind:key="index" :index="index" @itemChanged="itemChanged" ></sale-item> </tbody> </v-simple-table> </v-form> <v-btn :disabled="quantity_error" rounded x-large outlined color="green" @click="add_line" >{{ sales_items.length > 0 ? "Add another item" : "Add item" }} </v-btn> <v-btn small :disabled="quantity_error" rounded x-large outlined color="green" text dark @click=" $store.state.new_item = true; retailbyDefaule = true; " > <v-icon>mdi-plus</v-icon> {{ $t("main.new_item") }} </v-btn> </v-col> <tag-input :message="$t('tags.sale_tag')" color="green" @tags="setTags" :tagFill="tagFill" > </tag-input> </v-row> <v-row class="mb-2"> <v-col cols="12" sm="10" class="mx-auto" v-if="sales_items.length"> <v-textarea outlined :label="$t('main.note')" v-model="note" rows="2" auto-grow color="green" > </v-textarea> </v-col> </v-row> </v-col> </v-row> <div class="card-footer"> <v-row> <v-spacer></v-spacer> <v-col cols="12" sm="6"> <!-- <v-menu--> <!-- v-model="discount_dlg"--> <!-- :close-on-content-click="false"--> <!-- :nudge-width="200"--> <!-- offset-x--> <!-- ref="discount_menu"--> <!-- >--> <!-- <template v-slot:activator="{ on }">--> <!-- <v-btn--> <!-- outlined--> <!-- color="green"--> <!-- rounded--> <!-- v-on="on"--> <!-- :disabled="hasItemDiscount"--> <!-- >--> <!-- {{ $t("main.add_discount") }}--> <!-- </v-btn>--> <!-- </template>--> <!-- <v-card--> <!-- style="max-width: 400px"--> <!-- class="p-1"--> <!-- >--> <!-- <v-list>--> <!-- <v-list-item>--> <!-- <v-list-item-title> {{ $t("main.apply_discount") }}</v-list-item-title>--> <!-- </v-list-item>--> <!-- <v-list-item>--> <!-- <v-list-item-action>--> <!-- <v-switch :label="$t('main.enter_amount')"--> <!-- @change="discount_percentageSwitch = ! discount_percentageSwitch"--> <!-- v-model="discount_amountSwitch" color="blue darken-4"></v-switch>--> <!-- </v-list-item-action>--> <!-- <v-list-item-title>--> <!-- <v-text-field--> <!-- class="m-2"--> <!-- outlined--> <!-- @keyup="compute_total_amount"--> <!-- v-model="discount_amount"--> <!-- :disabled="!discount_amountSwitch"--> <!-- :placeholder="$t('main.amount')"--> <!-- ></v-text-field>--> <!-- </v-list-item-title>--> <!-- </v-list-item>--> <!-- <v-list-item>--> <!-- <v-list-item-action>--> <!-- <v-switch label=" %" @change="discount_amountSwitch = !discount_amountSwitch"--> <!-- v-model="discount_percentageSwitch" color="purple"></v-switch>--> <!-- </v-list-item-action>--> <!-- <v-list-item-title>--> <!-- <v-text-field--> <!-- class="m-2"--> <!-- outlined--> <!-- @keyup="compute_discountPercentage"--> <!-- v-model="discount_percentage"--> <!-- :disabled="!discount_percentageSwitch"--> <!-- @change="compute_discountPercentage"--> <!-- placeholder="%"--> <!-- ></v-text-field>--> <!-- </v-list-item-title>--> <!-- </v-list-item>--> <!-- <v-list-item>--> <!-- <v-btn @click="$refs.discount_menu.isActive =false;" small text color="success"> {{--> <!-- $t("main.done")--> <!-- }}--> <!-- </v-btn>--> <!-- </v-list-item>--> <!-- </v-list>--> <!-- </v-card>--> <!-- </v-menu>--> <!-- <span v-if="discount_percentageSwitch" style="font-size: 18px;"--> <!-- class="font-weight-light ">--> <!-- <span v-if="!hasItemDiscount">--> <!-- {{ discount_percentage }}%:--> <!-- </span> {{ discount_amount | toMoney | currency_symbol }} </span>--> <!-- <span--> <!-- v-if="discount_amountSwitch"--> <!-- style="font-size: 18px;"--> <!-- class="font-weight-light"--> <!-- >{{ discount_amount | toMoney | currency_symbol }} </span>--> </v-col> <v-col cols="12" sm="2"> <v-btn :disabled="quantity_error" :loading="saving_progress" @click="save_cart" rounded color="success" block depressed x-large >{{ $t("main.save") }} </v-btn> </v-col> </v-row> </div> </div> <cannotview-component v-else></cannotview-component> <new-item-component @ItemCreated="create_item" :selling_default="retailbyDefaule" > </new-item-component> <v-snackbar v-model="success_message" :timeout="snack_timeout"> {{ toast_msg }} <v-btn color="success" text @click="success_message = false"> {{ $t("main.ok") }} </v-btn> </v-snackbar> <v-snackbar v-model="error_message" color="error" :timeout="snack_timeout"> {{ error_msg }} <v-btn color="light" text @click="error_message = false"> {{ $t("main.close") }} </v-btn> </v-snackbar> </div> </template> <script> import moment from "moment"; import CannotviewComponent from "./cannotviewComponent"; import NewItemComponent from "../product/NewItemComponent"; import DB from "../../drivers/DB"; import SaleItem from "../sales/saleItem"; const AccountDropdown = () => import("./accountDropdown.vue"); const tagInput = () => import("./tagInput"); export default { name: "newsaleComponent", components: { SaleItem, NewItemComponent, CannotviewComponent, tagInput, AccountDropdown, }, data() { return { payments: [ { account_id: null, amount: 0, }, ], balance: 0, applied_taxes: [], retailbyDefaule: false, tags: [], tagFill: false, categories: [], cat_loading: false, category_id: null, img_preview: "/img/item_photo.png", has_photo: false, change_photo: false, not_allowed: false, print_receipt: true, selling_price: 0, display_unitPrice: 0, code: "", quantity_error: false, unit_price: 0, name: "", formValid: false, saving_progress: false, is_product: true, new_product: false, search_value: "", payment_accounts: [], payment_account: null, delete_dialog: false, sale_id: "", gross_amount: 0, amount_due: 0, snack_timeout: 4500, snack_infinite: 0, search: "", business_name: "", business_logo: "", business_address: "", business_phone: "", business_email: "", loading: false, success_message: false, sales_preview: false, toast_msg: "", items: [], items_alt: [], sales: [], error_msg: "", error_message: false, client_error: false, editing: false, create_overlay: false, discount_percentage: 0, discount_percentageSwitch: true, discount_amountSwitch: false, discount_amount: 0, discounted_amount: 0, note: "", sale_number: "", discount_dlg: false, sales_items: [], sales_dialog: false, startDate_menu: false, entrydate: "", clear_dialog: false, item_type: "Product", item_types: [ { text: "Product", value: "Product" }, { text: "Service", value: "Service" }, ], buy: false, tax_amount: 0, retail: true, taxed: false, sales_accounts: [], sales_account: null, purchase_accounts: [], purchase_account: null, track_inventory: false, description: "", item_tax: null, nameRules: [ (v) => !!v || this.$t("main.item_name_required"), //v => (v && v.length <= 10) || 'Name must be less than 10 characters', ], required_rules: [ (v) => !!v || this.$t("main.required"), (v) => v > 0 || this.$t("main.invalid_value"), ], headers: [ { text: "SALE #", align: "center", sortable: true, value: "sales_number", }, { text: "NET AMOUNT", value: "amount_due" }, { text: "ENTRY DATE", value: "entry_date", align: "center", }, { text: "CREATED ON", value: "created_at" }, { text: "ACTIONS", value: "id", align: "center" }, ], }; }, computed: { hasItemDiscount() { const items = this.sales_items.filter((item) => { return Number(item.discount_amount) > 0; }); return items.length > 0; }, itemsDiscountAmount() { let sum = 0; this.sales_items.forEach((item) => { sum += Number(item.discount_amount); }); return sum; }, totalDiscount() { return ( Number(this.itemsDiscountAmount) + Number(this.discount_amount) ).toFixed(2); }, currentBusiness() { return this.$store.state.user.user_infor.current_business; }, taxes() { return this.currentBusiness.taxes; }, totalPayment() { let sum = 0; this.payments.forEach((payment) => { sum += Number(payment.amount); }); return sum; }, accountsValid() { let status = true; this.payments.forEach((payment) => { if (!payment.account_id) { status = false; } }); return status; }, baseUrl() { return this.$store.state.baseURL; }, businessCurrency() { return this.$store.state.user.user_infor.current_business.currency; }, totalAmountDue() { let sum = 0; this.sales_items.forEach((item) => { sum += Number(item.amount_due); }); return sum; }, totalGrossAmount() { let sum = 0; this.sales_items.forEach((item) => { sum += Number(item.invoice_amount); }); return sum; }, }, watch: { hasItemDiscount() { if (this.hasItemDiscount) { this.discount_amount = 0; this.discount_percentage = 0; } }, }, methods: { resetpayment() { const account_id = this.payments[0].account_id; this.payments = [ { account_id: account_id, amount: Number(this.amount_due).toFixed(2) }, ]; }, removePayment(index) { const payment = this.payments[index]; if (index > 0) { this.payments[index - 1].amount = Number(payment.amount) + Number(this.payments[index - 1].amount); } else { this.payments[index + 1].amount = Number(payment.amount) + Number(this.payments[index + 1].amount); } this.payments.splice(index, 1); }, addPayment() { const diff = this.amount_due - this.totalPayment; const am = diff >= 0 ? diff : 0; this.payments.push({ account_id: null, amount: am }); }, setTags(tags) { this.tags = tags; }, handleAccountChange(accountId, type) { if (type === "payment") this.payment_account = accountId; if (type === "sales") this.sales_account = accountId; if (type === "purchase") this.purchase_account = accountId; }, get_categories() { this.cat_loading = true; axios.get("/api/itemcategories").then((res) => { this.categories = res.data; this.category_id = this.categories[0].id; this.cat_loading = false; }); }, remove_photo() { this.photo = null; this.img_preview = "/img/item_photo.png"; this.has_photo = false; this.change_photo = true; }, select_image() { let image_selector = document.getElementById("item_photo"); image_selector.click(); }, get_default_account(accounts, account_name = null) { return accounts.filter((account) => { return account.account_name == account_name; }); }, apply_item(data) { const i = data.index; this.sales_items[i] = data.item; let item = this.sales_items[i]; if (item.track_inventory && Number(item.display_unitPrice) === 0) { this.error_msg = this.$t("item.stock_error"); this.error_message = true; } let product = JSON.parse(JSON.stringify(data.item.item)); product.invoice_quantity = 1; this.sales_items[i] = product; this.sales_items[i].item = product; //item = product ; //this.compute_amount(this.sales_items[i]); this.compute_total_amount(); //this.compute_discountPercentage(); }, type_change() { this.is_product = this.item_type == "Product" ? true : false; }, create_item(item) { this.add_item(item); this.items.push(item); this.retailbyDefaule = false; }, new_sale() { this.editing = false; this.sales_dialog = true; this.sale_number = ""; this.get_items(); }, compute_discountPercentage() { if (this.discount_percentageSwitch) { this.discount_amount = this.gross_amount * (this.discount_percentage / 100); this.amount_due = this.gross_amount - this.discount_amount; this.amount_due += Number(this.tax_amount); } }, compute_inputTax(rate, amount) { return (Number(rate) / 100) * Number(amount); }, itemChanged(data) { const item = data.item; item.invoice_amount = Number(data.item.invoice_quantity) * Number(data.item.rawUnit_price); this.sales_items[data.index] = item; this.compute_total_amount(); }, compute_total_amount() { let gross_amount = 0; let tax_amount = 0; let sumAmountDue = 0; let totalDiscount = 0; this.tax_amount = 0; this.quantity_error = false; this.discount_amount = 0; this.sales_items.forEach((item) => { totalDiscount += Number(item.discount_amount); item.invoice_amount = item.invoice_quantity * item.rawUnit_price; gross_amount += item.invoice_amount; item.amount_due = ( Number(item.invoice_amount) - Number(item.discount_amount) ).toFixed(2); sumAmountDue += Number(item.amount_due); item.tax_amount = 0; if (item.applied_taxes) { item.applied_taxes.forEach((tax_id) => { let selected_tax = this.taxes.find((tax) => { return tax.id == tax_id; }); if (selected_tax.type == "Flat") { item.tax_amount += this.compute_inputTax( selected_tax.rate, item.amount_due ); } else if (selected_tax.type == "Compound") { //compute input taxes before applying compound taxes let subamount = this.compute_inputTax( selected_tax.sub_rate, item.amount_due ); let compound_amount = Number(subamount) + Number(item.amount_due); let compound_taxAmount = this.compute_inputTax( selected_tax.rate, compound_amount ); item.tax_amount += compound_taxAmount + subamount; } }); } tax_amount += item.tax_amount; }); this.compute_discountPercentage(); this.gross_amount = gross_amount; this.tax_amount = tax_amount; this.discount_amount = totalDiscount; console.log(totalDiscount); this.amount_due = (Number(sumAmountDue) + this.tax_amount).toFixed(2); this.resetpayment(); }, remove_item(index) { this.sales_items.splice(index, 1); this.compute_total_amount(); }, compute_amount(item) { if (item.invoice_quantity) { item.invoice_amount = Number(item.rawUnit_price) * Number(item.invoice_quantity); } this.compute_total_amount(); }, delete_item(item) { this.sale_number = item.sales_number; this.sale_id = item.id; this.delete_dialog = true; }, get_items() { // // const local = DB.getInvoiceItems(); // // local.then(res=>{ // // // const localItems = res.find(item=>{ // return item.id === this.currentBusiness.id; // // }); // // if(localItems){ // // this.items = localItems.items; // // if(this.$route.query.item_id){ // let searchItem = this.items.find(item=>{ return item.id==this.$route.query.item_id}); // // // if (searchItem){ // this.create_item(searchItem); // } // } // // }else{ this.loading = true; axios .get("/api/getinvoiceitems") .then((res) => { this.items = res.data; this.items_alt = res.data; this.loading = false; if (this.$route.query.item_id) { let searchItem = this.items.find((item) => { return item.id == this.$route.query.item_id; }); if (searchItem) { this.create_item(searchItem); } } }) .catch(() => { this.error_msg = this.$t("main.error_msg"); this.error_message = true; }); // } //}) }, reload_items() { this.loading = true; axios .get("/api/getinvoiceitems") .then((res) => { this.items = res.data; this.items_alt = res.data; this.loading = false; }) .catch(() => { this.error_msg = this.$t("main.error_msg"); this.error_message = true; }); }, prep_items(items) { let new_items = []; items.forEach((item) => { item.item = null; new_items.push(item); }); return new_items; }, reprep_items() { this.sales_items.forEach((item) => { item.item = item; }); }, save_cart() { if (this.quantity_error) { this.error_message = true; return false; } if (!this.validate_items()) { return false; } if (this.totalPayment != this.amount_due) { this.error_msg = "Invalid payment amount, payment amount should be equal to amount due"; this.error_message = true; return false; } if (!this.accountsValid) { this.error_msg = this.$t("main.payment_account_error"); this.error_message = true; return false; } if (this.sales_items.length) { if (this.$refs.item_form.validate()) { this.saving_progress = true; let cart = { entry_date: this.entrydate, sale_number: this.sale_number, gross_amount: this.gross_amount, discount_amount: this.discount_amount, amount_due: this.amount_due, items: this.prep_items(this.sales_items), note: this.note, tag: this.tags.toString(), payments: this.payments, balance: this.balance, tax_amount: this.tax_amount, }; axios .post("/api/v2/sale", cart) .then((sale) => { this.saving_progress = false; this.toast_msg = this.$t("quick_sale.sale_create_success"); this.success_message = true; this.sales_items = []; this.discount_amount = 0; this.discount_percentage = 0; this.tax_amount = 0; this.amount_due = 0; this.gross_amount = 0; this.tags = []; this.tagFill = true; this.resetpayment(); if (this.print_receipt) { let w = window.open( this.baseUrl + "/printposreceipt/" + sale.data.enc_id, "Sales receipt" + sale.data.sales_number, "height=800,width=900" ); w.onload = function (e) { w.print(); }; } this.reload_items(); }) .catch((err) => { this.saving_progress = false; this.error_msg = err.response.status === 302 ? err.response.data : "Something went wrong, could not save sale record"; this.error_message = true; this.reprep_items(); }); } } else { this.error_msg = this.$t("quick_sale.cart_empty_error"); this.error_message = true; } }, // add_item(item){ // if(item.track_inventory === 1 && Number(item.display_unitPrice) ==0){ // this.error_msg = "This item is out of stocked, please purchase this item before you can sell it"; // this.error_message =true; // return false; // } // let newitem =JSON.parse(JSON.stringify(item)); // newitem.invoice_quantity=1; // this.sales_items.push(newitem); // // this.compute_total_amount(); // this.compute_discountPercentage(); // // this.$refs.item_menu.isActive = false; // // }, validate_items() { let res = true; for (let item of this.sales_items) { if (!item.id) { this.error_msg = this.$t("item.item_line_error"); this.error_message = true; res = false; break; } } return res; }, add_line() { let item = { id: null, description: "", tax_id: "", rawUnit_price: 0, invoice_amount: 0, invoice_quantity: 1, discount_amount: 0, amount_due: 0, item: {}, }; this.sales_items.push(item); }, add_item(item) { console.log(item); if (item.track_inventory == 1 && Number(item.display_unitPrice) == 0) { this.error_msg = this.$t("item.stock_error"); this.error_message = true; return false; } let newitem = JSON.parse(JSON.stringify(item)); this.items.push(newitem); newitem.item = JSON.parse(JSON.stringify(item)); newitem.invoice_quantity = 1; newitem.discount_amount = 0; newitem.amount_due = 0; this.sales_items.push(newitem); this.compute_total_amount(); }, moveItem_up(index, item) { let index2 = index - 1; let item2 = this.sales_items[index2]; this.sales_items[index] = item2; this.sales_items[index2] = item; this.sales_items.push({}); let lastindex = this.sales_items.length - 1; this.remove_item(lastindex); }, moveItem_down(index, item) { let index2 = index + 1; let item2 = this.sales_items[index2]; this.sales_items[index] = item2; this.sales_items[index2] = item; this.sales_items.push({}); let lastindex = this.sales_items.length - 1; this.remove_item(lastindex); }, }, mounted() { if (this.$store.state.user.user_infor.is_admin == 1) { this.not_allowed = false; this.get_items(); } else { if (this.$store.state.user.user_infor.components[2].roles.create == 1) { this.not_allowed = false; this.get_items(); } else { this.not_allowed = true; this.progress = false; return false; } } let sales_sub = this.$store.state.user.countryInfo.incomeAccounts.subtypes.filter( (account_type) => { return account_type.name == "Revenue"; } ); this.sales_accounts = sales_sub[0].accounts; this.sales_account = this.sales_accounts[1].id; let expense_sub = this.$store.state.user.countryInfo.expenseAccounts.subtypes.filter( (account_type) => { return account_type.name == "Cost of Sale"; } ); let expense_sub2 = this.$store.state.user.countryInfo.expenseAccounts.subtypes.filter( (account_type) => { return account_type.name == "Operating Expenses"; } ); this.purchase_accounts = [ ...expense_sub[0].accounts, ...expense_sub2[0].accounts, ]; // let default_account = this.get_default_account(expense_sub[0].accounts,'Purchases'); // let default_sales = this.get_default_account(sales_sub[0].accounts,'Sales Revenue'); // // this.sales_account=default_sales[0].id; // this.purchase_account = default_account[0].id; this.entrydate = moment().format("Y-M-D"); let sales2_sub = this.$store.state.user.countryInfo.assetAccounts.subtypes.filter( (account_type) => { return account_type.name == "Cash & Bank"; } ); let all_payment_accounts = sales2_sub[0].accounts; this.payment_accounts = all_payment_accounts.filter((account) => { return ( !account.currency || account.currency === this.businessCurrency.code ); }); this.payments[0].account_id = this.payment_accounts[0].id; }, }; </script> <style scoped> .v-menu__content { max-height: 100%; overflow-y: auto !important; } @media screen and (max-device-width: 768px) { td { display: table-row; width: 100% !important; } td > .v-input { min-width: 360px !important; align-self: center; } } @media screen and (device-width: 768px) { td { display: table-row; width: 100% !important; } td > .v-input { min-width: 660px !important; align-self: center; } } </style>
Editor is loading...