관리-도구
편집 파일: Edit_Adjustment.vue
<template> <div class="main-content"> <breadcumb :page="$t('EditAdjustement')" :folder="$t('ListAdjustments')"/> <div v-if="isLoading" class="loading_page spinner spinner-primary mr-3"></div> <validation-observer ref="Edit_adjustment" v-if="!isLoading"> <b-form @submit.prevent="Submit_Adjustment"> <b-row> <b-col lg="12" md="12" sm="12"> <b-card> <b-row> <b-modal hide-footer id="open_scan" size="md" title="Barcode Scanner"> <qrcode-scanner :qrbox="250" :fps="10" style="width: 100%; height: calc(100vh - 56px);" @result="onScan" /> </b-modal> <!-- warehouse --> <b-col md="6" class="mb-3"> <validation-provider name="warehouse" :rules="{ required: true}"> <b-form-group slot-scope="{ valid, errors }" :label="$t('warehouse') + ' ' + '*'"> <v-select :class="{'is-invalid': !!errors.length}" :state="errors[0] ? false : (valid ? true : null)" :disabled="details.length > 0" @input="Selected_Warehouse" v-model="adjustment.warehouse_id" :reduce="label => label.value" :placeholder="$t('Choose_Warehouse')" :options="warehouses.map(warehouses => ({label: warehouses.name, value: warehouses.id}))" /> <b-form-invalid-feedback>{{ errors[0] }}</b-form-invalid-feedback> </b-form-group> </validation-provider> </b-col> <!-- date --> <b-col lg="6" md="6" sm="12"> <validation-provider name="date" :rules="{ required: true}" v-slot="validationContext" > <b-form-group :label="$t('date') + ' ' + '*'"> <b-form-input :state="getValidationState(validationContext)" aria-describedby="date-feedback" type="date" v-model="adjustment.date" ></b-form-input> <b-form-invalid-feedback id="OrderTax-feedback" >{{ validationContext.errors[0] }}</b-form-invalid-feedback> </b-form-group> </validation-provider> </b-col> <!-- Product --> <b-col md="12" class="mb-5"> <h6>{{$t('ProductName')}}</h6> <div id="autocomplete" class="autocomplete"> <div class="input-with-icon"> <img src="/assets_setup/scan.png" alt="Scan" class="scan-icon" @click="showModal"> <input :placeholder="$t('Scan_Search_Product_by_Code_Name')" @input='e => search_input = e.target.value' @keyup="search(search_input)" @focus="handleFocus" @blur="handleBlur" ref="product_autocomplete" class="autocomplete-input" /> </div> <ul class="autocomplete-result-list" v-show="focused"> <li class="autocomplete-result" v-for="product_fil in product_filter" @mousedown="SearchProduct(product_fil)">{{getResultValue(product_fil)}}</li> </ul> </div> </b-col> <!-- Details product --> <b-col md="12"> <div class="table-responsive"> <table class="table table-hover"> <thead class="bg-gray-300"> <tr> <th scope="col">#</th> <th scope="col">{{$t('CodeProduct')}}</th> <th scope="col">{{$t('ProductName')}}</th> <th scope="col">{{$t('CurrentStock')}}</th> <th scope="col">{{$t('Qty')}}</th> <th scope="col">{{$t('type')}}</th> <th scope="col" class="text-center"> <i class="fa fa-trash"></i> </th> </tr> </thead> <tbody> <tr v-if="details.length <=0"> <td colspan="7">{{$t('NodataAvailable')}}</td> </tr> <tr v-for="detail in details" :class="{'row_deleted': detail.del === 1}" :key="detail.detail_id" > <td>{{detail.detail_id}}</td> <td>{{detail.code}}</td> <td>({{detail.name}})</td> <td> <span class="badge badge-outline-warning" >{{detail.current}} {{detail.unit}}</span> </td> <td> <div class="quantity"> <b-input-group> <b-input-group-prepend v-if="detail.product_type != 'is_combo'"> <span class="btn btn-primary btn-sm" @click="decrement(detail ,detail.detail_id)" >-</span> </b-input-group-prepend> <input class="form-control" @keyup="Verified_Qty(detail,detail.detail_id)" :min="0.00" :max="detail.current" v-model.number="detail.quantity" :disabled="detail.product_type == 'is_combo' || detail.del === 1" > <b-input-group-append v-if="detail.product_type != 'is_combo'"> <span class="btn btn-primary btn-sm" @click="increment(detail ,detail.detail_id)" >+</span> </b-input-group-append> </b-input-group> </div> </td> <td v-if="detail.product_type != 'is_combo'"> <select v-model="detail.type" @change="Verified_Qty(detail,detail.detail_id)" type="text" required class="form-control" > <option value="add">{{$t('Addition')}}</option> <option value="sub">{{$t('Subtraction')}}</option> </select> </td> <td v-if="detail.product_type == 'is_combo'"> <span v-if="detail.type == 'add'">{{$t('Addition')}}</span> <span v-if="detail.type == 'sub'">{{$t('Subtraction')}}</span> </td> <td> <a @click="Remove_Product(detail.detail_id)" class="btn btn-icon btn-sm" title="Delete" > <i class="i-Close-Window text-25 text-danger"></i> </a> </td> </tr> </tbody> </table> </div> </b-col> <b-col md="12"> <b-form-group :label="$t('Note')" class="mt-4"> <textarea v-model="adjustment.notes" rows="4" class="form-control" :placeholder="$t('Afewwords')" ></textarea> </b-form-group> </b-col> <b-col md="12"> <b-form-group> <b-button variant="primary" :disabled="SubmitProcessing" @click="Submit_Adjustment"><i class="i-Yes me-2 font-weight-bold"></i> {{$t('submit')}}</b-button> <div v-once class="typo__p" v-if="SubmitProcessing"> <div class="spinner sm spinner-primary mt-3"></div> </div> </b-form-group> </b-col> </b-row> </b-card> </b-col> </b-row> </b-form> </validation-observer> </div> </template> <script> import NProgress from "nprogress"; export default { metaInfo: { title: "Edit Adjustment" }, data() { return { focused: false, timer:null, search_input:'', product_filter:[], isLoading: true, SubmitProcessing:false, warehouses: [], products: [], details: [], adjustment: { id: "", notes: "", warehouse_id: "", date: "" }, product: { id: "", code: "", current: "", quantity: 1, name: "", product_id: "", detail_id: "", product_variant_id: "", del: "", unit: "" } }; }, methods: { showModal() { this.$bvModal.show('open_scan'); }, onScan (decodedText, decodedResult) { const code = decodedText; this.search_input = code; this.search(); this.$bvModal.hide('open_scan'); }, handleFocus() { this.focused = true }, handleBlur() { this.focused = false }, // Search Products search(){ if (this.timer) { clearTimeout(this.timer); this.timer = null; } if (this.search_input.length < 2) { return this.product_filter= []; } if (this.adjustment.warehouse_id != "" && this.adjustment.warehouse_id != null) { this.timer = setTimeout(() => { const product_filter = this.products.filter(product => product.code === this.search_input || product.barcode.includes(this.search_input)); if(product_filter.length === 1){ this.SearchProduct(product_filter[0]) }else{ this.product_filter= this.products.filter(product => { return ( product.name.toLowerCase().includes(this.search_input.toLowerCase()) || product.code.toLowerCase().includes(this.search_input.toLowerCase()) || product.barcode.toLowerCase().includes(this.search_input.toLowerCase()) ); }); // Check if product_filter is empty and show alert if (this.product_filter.length <= 0) { this.makeToast( "warning", "Product Not Found", "Warning" ); } } }, 800); } else { this.makeToast( "warning", this.$t("SelectWarehouse"), this.$t("Warning") ); } }, //---------------------- Event Get Value Search ------------------------------\\ getResultValue(result) { return result.code + " " + "(" + result.name + ")"; }, //---------------- Submit Search Product-----------------\\ SearchProduct(result) { this.product = {}; if ( this.details.length > 0 && this.details.some(detail => detail.code === result.code) ) { this.makeToast("warning", this.$t("AlreadyAdd"), this.$t("Warning")); } else { this.product.code = result.code; this.product.current = result.qte; if (result.qte < 1) { this.product.quantity = result.qte; } else { this.product.quantity = 1; } this.product.product_variant_id = result.product_variant_id; this.Get_Product_Details(result.id, result.product_variant_id); } this.search_input= ''; this.$refs.product_autocomplete.value = ""; this.product_filter = []; }, //------------- Submit Validation Update Adjustment Submit_Adjustment() { this.$refs.Edit_adjustment.validate().then(success => { if (!success) { this.makeToast( "danger", this.$t("Please_fill_the_form_correctly"), this.$t("Failed") ); } else { this.Update_Adjustment(); } }); }, //------------- Event Get Validation state getValidationState({ dirty, validated, valid = null }) { return dirty || validated ? valid : null; }, //------ Toast makeToast(variant, msg, title) { this.$root.$bvToast.toast(msg, { title: title, variant: variant, solid: true }); }, //------------------------------------ Get Products By Warehouse -------------------------\\ Get_Products_By_Warehouse(id) { // Start the progress bar. NProgress.start(); NProgress.set(0.1); axios .get("get_Products_by_warehouse/" + id + "?stock=" + 0 + "&product_service=" + 0 + "&product_combo=" + 1) .then(response => { this.products = response.data; NProgress.done(); }) .catch(error => { }); }, //----------------------------------------- Add Product To list -------------------------\\ add_product() { if (this.details.length > 0) { this.detail_order_id(); } else if (this.details.length === 0) { this.product.detail_id = 1; } this.details.push(this.product); }, //-----------------------------------Verified QTY ------------------------------\\ Verified_Qty(detail, id) { for (var i = 0; i < this.details.length; i++) { if (this.details[i].detail_id === id) { if (isNaN(detail.quantity)) { this.details[i].quantity = detail.current; } if (detail.type == "sub" && detail.quantity > detail.current) { this.makeToast("warning", this.$t("LowStock"), this.$t("Warning")); this.details[i].quantity = detail.current; } else { this.details[i].quantity = detail.quantity; } } } this.$forceUpdate(); }, //----------------------------------- Increment QTY ------------------------------\\ increment(detail, id) { for (var i = 0; i < this.details.length; i++) { if (this.details[i].detail_id == id) { if (detail.type == "sub") { if (detail.quantity + 1 > detail.current) { this.makeToast( "warning", this.$t("LowStock"), this.$t("Warning") ); } else { this.formatNumber(this.details[i].quantity++, 2); } } else { this.formatNumber(this.details[i].quantity++, 2); } } } this.$forceUpdate(); }, //----------------------------------- Decrement QTY ------------------------------\\ decrement(detail, id) { for (var i = 0; i < this.details.length; i++) { if (this.details[i].detail_id == id) { if (detail.quantity - 1 > 0) { if (detail.type == "sub" && detail.quantity - 1 > detail.current) { this.makeToast( "warning", this.$t("LowStock"), this.$t("Warning") ); } else { this.formatNumber(this.details[i].quantity--, 2); } } } } this.$forceUpdate(); }, //------------------------------Formetted Numbers -------------------------\\ formatNumber(number, dec) { const value = (typeof number === "string" ? number : number.toString() ).split("."); if (dec <= 0) return value[0]; let formated = value[1] || ""; if (formated.length > dec) return `${value[0]}.${formated.substr(0, dec)}`; while (formated.length < dec) formated += "0"; return `${value[0]}.${formated}`; }, //-----------------------------------Remove the product from the order list ------------------------------\\ Remove_Product(id) { for (var i = 0; i < this.details.length; i++) { if (id === this.details[i].detail_id) { this.details.splice(i, 1); } } }, //----------------------------------- Verified Quantity if Null Or zero ------------------------------\\ verifiedForm() { if (this.details.length <= 0) { this.makeToast( "warning", this.$t("AddProductToList"), this.$t("Warning") ); return false; } else { var count = 0; for (var i = 0; i < this.details.length; i++) { if ( this.details[i].quantity == "" || this.details[i].quantity === 0 ) { count += 1; } } if (count > 0) { this.makeToast("warning", this.$t("AddQuantity"), this.$t("Warning")); return false; } else { return true; } } }, //--------------------------------- Update Adjustment -------------------------\\ Update_Adjustment() { if (this.verifiedForm()) { this.SubmitProcessing = true; // Start the progress bar. NProgress.start(); NProgress.set(0.1); let id = this.$route.params.id; axios .put(`adjustments/${id}`, { warehouse_id: this.adjustment.warehouse_id, date: this.adjustment.date, notes: this.adjustment.notes, details: this.details }) .then(response => { // Complete the animation of theprogress bar. NProgress.done(); this.SubmitProcessing = false; this.$router.push({ name: "index_adjustment" }); this.makeToast( "success", this.$t("Successfully_Updated"), this.$t("Success") ); }) .catch(error => { // Complete the animation of theprogress bar. NProgress.done(); this.makeToast("danger", this.$t("InvalidData"), this.$t("Failed")); this.SubmitProcessing = false; }); } }, //-------------------------------- detail order id -------------------------\\ detail_order_id() { this.product.detail_id = 0; var len = this.details.length; this.product.detail_id = this.details[len - 1].detail_id + 1; }, //---------------------- Event Select Warehouse ------------------------------\\ Selected_Warehouse(value) { this.search_input= ''; this.product_filter = []; this.Get_Products_By_Warehouse(value); }, //---------------------------------Get Product Details ------------------------\\ Get_Product_Details(product_id, variant_id) { axios.get("/show_product_data/" + product_id +"/"+ variant_id).then(response => { this.product.del = 0; this.product.id = 0; this.product.product_id = response.data.id; this.product.name = response.data.name; this.product.type = "add"; this.product.unit = response.data.unit; this.add_product(); }); }, //---------------------------------------Get Adjustment Elements------------------------------\\ GetElements() { let id = this.$route.params.id; axios .get(`adjustments/${id}/edit`) .then(response => { this.adjustment = response.data.adjustment; this.details = response.data.details; this.warehouses = response.data.warehouses; this.Get_Products_By_Warehouse(this.adjustment.warehouse_id); this.isLoading = false; }) .catch(response => { setTimeout(() => { this.isLoading = false; }, 500); }); } }, //----------------------------- Created function------------------- created() { this.GetElements(); } }; </script> <style> .input-with-icon { display: flex; align-items: center; } .scan-icon { width: 50px; /* Adjust size as needed */ height: 50px; margin-right: 8px; /* Adjust spacing as needed */ cursor: pointer; } </style>