mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-31 05:31:10 -04:00 
			
		
		
		
	Merge branch 'add-customization' into 'master'
Add customization See merge request mohit.panjvani/crater-web!134
This commit is contained in:
		
							
								
								
									
										71
									
								
								resources/assets/js/components/base/BasePrefixInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								resources/assets/js/components/base/BasePrefixInput.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| <template> | ||||
|   <div class="base-prefix-input" @click="focusInput"> | ||||
|     <font-awesome-icon v-if="icon" :icon="icon" class="icon" /> | ||||
|     <p class="prefix-label"><span class="mr-1">{{ prefix }}</span>-</p> | ||||
|     <input | ||||
|       ref="basePrefixInput" | ||||
|       v-model="inputValue" | ||||
|       :type="type" | ||||
|       class="prefix-input-field" | ||||
|       @input="handleInput" | ||||
|       @change="handleChange" | ||||
|       @keyup="handleKeyupEnter" | ||||
|       @keydown="handleKeyDownEnter" | ||||
|       @blur="handleFocusOut" | ||||
|     > | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     prefix: { | ||||
|       type: String, | ||||
|       default: null, | ||||
|       required: true | ||||
|     }, | ||||
|     icon: { | ||||
|       type: String, | ||||
|       default: null | ||||
|     }, | ||||
|     value: { | ||||
|       type: [String, Number, File], | ||||
|       default: '' | ||||
|     }, | ||||
|     type: { | ||||
|       type: String, | ||||
|       default: 'text' | ||||
|     } | ||||
|   }, | ||||
|   data () { | ||||
|     return { | ||||
|       inputValue: this.value | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     'value' () { | ||||
|       this.inputValue = this.value | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     focusInput () { | ||||
|       this.$refs.basePrefixInput.focus() | ||||
|     }, | ||||
|     handleInput (e) { | ||||
|       this.$emit('input', this.inputValue) | ||||
|     }, | ||||
|     handleChange (e) { | ||||
|       this.$emit('change', this.inputValue) | ||||
|     }, | ||||
|     handleKeyupEnter (e) { | ||||
|       this.$emit('keyup', this.inputValue) | ||||
|     }, | ||||
|     handleKeyDownEnter (e) { | ||||
|       this.$emit('keydown', e, this.inputValue) | ||||
|     }, | ||||
|     handleFocusOut (e) { | ||||
|       this.$emit('blur', this.inputValue) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| @ -8,6 +8,7 @@ import BaseTextArea from './BaseTextArea.vue' | ||||
| import BaseSelect from './base-select/BaseSelect.vue' | ||||
| import BaseLoader from './BaseLoader.vue' | ||||
| import BaseCustomerSelect from './BaseCustomerSelect.vue' | ||||
| import BasePrefixInput from './BasePrefixInput.vue' | ||||
|  | ||||
| import BasePopup from './popup/BasePopup.vue' | ||||
| import CustomerSelectPopup from './popup/CustomerSelectPopup.vue' | ||||
| @ -23,6 +24,7 @@ Vue.component('base-input', BaseInput) | ||||
| Vue.component('base-switch', BaseSwitch) | ||||
| Vue.component('base-text-area', BaseTextArea) | ||||
| Vue.component('base-loader', BaseLoader) | ||||
| Vue.component('base-prefix-input', BasePrefixInput) | ||||
|  | ||||
| Vue.component('table-component', TableComponent) | ||||
| Vue.component('table-column', TableColumn) | ||||
|  | ||||
| @ -124,7 +124,7 @@ export default { | ||||
|       }, | ||||
|       percent: { | ||||
|         required, | ||||
|         between: between(0.10, 100) | ||||
|         between: between(-1, 100) | ||||
|       }, | ||||
|       description: { | ||||
|         maxLength: maxLength(255) | ||||
|  | ||||
| @ -530,6 +530,7 @@ | ||||
|     "menu_title": { | ||||
|       "account_settings": "Account Settings", | ||||
|       "company_information": "Company Information", | ||||
|       "customization": "Customization", | ||||
|       "preferences": "Preferences", | ||||
|       "notifications": "Notifications", | ||||
|       "tax_types": "Tax Types", | ||||
| @ -598,6 +599,67 @@ | ||||
|       "save": "Save", | ||||
|       "updated_message": "Company information updated successfully" | ||||
|     }, | ||||
|     "customization": { | ||||
|         "customization": "customization", | ||||
|         "save": "Save", | ||||
|         "addresses": { | ||||
|             "title": "Addresses", | ||||
|             "section_description": "You can set Customer Billing Address and Customer Shipping Address Format (Displayed in PDF only). ", | ||||
|             "customer_billing_address": "Customer Billing Address", | ||||
|             "customer_shipping_address": "Customer Shipping Address", | ||||
|             "company_address": "Company Address", | ||||
|             "insert_fields": "Insert Fields", | ||||
|             "contact": "Contact", | ||||
|             "address": "Address", | ||||
|             "display_name": "Display Name", | ||||
|             "primary_contact_name": "Primary Contact Name", | ||||
|             "email": "Email", | ||||
|             "website": "Website", | ||||
|             "name": "Name", | ||||
|             "country": "Country", | ||||
|             "state": "State", | ||||
|             "city": "City", | ||||
|             "company_name": "Company Name", | ||||
|             "address_street_1": "Address Street 1", | ||||
|             "address_street_2": "Address Street 2", | ||||
|             "phone": "Phone", | ||||
|             "zip_code": "Zip Code", | ||||
|             "address_setting_updated": "Address Setting updated successfully" | ||||
|         }, | ||||
|         "updated_message": "Company information updated successfully", | ||||
|  | ||||
|         "invoices": { | ||||
|             "title": "Invoices", | ||||
|             "notes": "Notes", | ||||
|             "invoice_prefix": "Invoice Prefix", | ||||
|             "invoice_settings": "Invoice Settings", | ||||
|             "autogenerate_invoice_number": "Autogenerate Invoice Number", | ||||
|             "invoice_setting_description": "Disable this, If you don't wish to auto-generate invoice numbers each time you create a new invoice.", | ||||
|             "enter_invoice_prefix": "Enter invoice prefix", | ||||
|             "terms_and_conditions": "Terms and Conditions", | ||||
|             "invoice_setting_updated": "Invoice Setting updated successfully" | ||||
|         }, | ||||
|  | ||||
|         "estimates": { | ||||
|             "title": "Estimates", | ||||
|             "estimate_prefix": "Estimate Prefix", | ||||
|             "estimate_settings": "Estimate Settings", | ||||
|             "autogenerate_estimate_number": "Autogenerate Estimate Number", | ||||
|             "estimate_setting_description": "Disable this, If you don't wish to auto-generate estimate numbers each time you create a new estimate.", | ||||
|             "enter_estimate_prefix": "Enter estmiate prefix", | ||||
|             "estimate_setting_updated": "Estimate Setting updated successfully" | ||||
|         }, | ||||
|  | ||||
|         "payments": { | ||||
|             "title": "Payments", | ||||
|             "payment_prefix": "Payment Prefix", | ||||
|             "payment_settings": "Payment Settings", | ||||
|             "autogenerate_payment_number": "Autogenerate Payment Number", | ||||
|             "payment_setting_description": "Disable this, If you don't wish to auto-generate payment numbers each time you create a new payment.", | ||||
|             "enter_payment_prefix": "Enter Payment Prefix", | ||||
|             "payment_setting_updated": "Payment Setting updated successfully" | ||||
|         } | ||||
|     }, | ||||
|     "account_settings": { | ||||
|       "profile_picture": "Profile Picture", | ||||
|       "name": "Name", | ||||
| @ -812,6 +874,7 @@ | ||||
|     "maximum_options_error": "Maximum  of {max} options selected. First remove a selected option to select another.", | ||||
|     "notes_maxlength": "Notes should not be greater than 255 characters.", | ||||
|     "address_maxlength": "Address should not be greater than 255 characters.", | ||||
|     "ref_number_maxlength": "Ref Number should not be greater than 255 characters." | ||||
|     "ref_number_maxlength": "Ref Number should not be greater than 255 characters.", | ||||
|     "prefix_maxlength": "Prefix should not be greater than 5 characters." | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -66,6 +66,7 @@ import ReportLayout from './views/reports/layout/Index.vue' | ||||
| // Settings | ||||
| import SettingsLayout from './views/settings/layout/Index.vue' | ||||
| import CompanyInfo from './views/settings/CompanyInfo.vue' | ||||
| import Customization from './views/settings/Customization.vue' | ||||
| import Notifications from './views/settings/Notifications.vue' | ||||
| import Preferences from './views/settings/Preferences.vue' | ||||
| import UserProfile from './views/settings/UserProfile.vue' | ||||
| @ -309,6 +310,11 @@ const routes = [ | ||||
|             name: 'company.info', | ||||
|             component: CompanyInfo | ||||
|           }, | ||||
|           { | ||||
|             path: 'customization', | ||||
|             name: 'customization', | ||||
|             component: Customization | ||||
|           }, | ||||
|           { | ||||
|             path: 'user-profile', | ||||
|             name: 'user.profile', | ||||
|  | ||||
| @ -127,14 +127,15 @@ | ||||
|           <div class="row mt-4"> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('estimates.estimate_number') }}<span class="text-danger"> * </span></label> | ||||
|               <base-input | ||||
|                 :invalid="$v.newEstimate.estimate_number.$error" | ||||
|                 :read-only="true" | ||||
|                 v-model="newEstimate.estimate_number" | ||||
|               <base-prefix-input | ||||
|                 v-model="estimateNumAttribute" | ||||
|                 :invalid="$v.estimateNumAttribute.$error" | ||||
|                 :prefix="estimatePrefix" | ||||
|                 icon="hashtag" | ||||
|                 @input="$v.newEstimate.estimate_number.$touch()" | ||||
|                 @input="$v.estimateNumAttribute.$touch()" | ||||
|               /> | ||||
|               <span v-show="$v.newEstimate.estimate_number.$error && !$v.newEstimate.estimate_number.required" class="text-danger mt-1"> {{ $tc('estimates.errors.required') }}  </span> | ||||
|               <span v-show="$v.estimateNumAttribute.$error && !$v.estimateNumAttribute.required" class="text-danger mt-1"> {{ $tc('estimates.errors.required') }}  </span> | ||||
|               <span v-show="!$v.estimateNumAttribute.numeric" class="text-danger mt-1"> {{ $tc('validation.numbers_only') }}  </span> | ||||
|             </div> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('estimates.ref_number') }}</label> | ||||
| @ -320,7 +321,7 @@ import { validationMixin } from 'vuelidate' | ||||
| import Guid from 'guid' | ||||
| import TaxStub from '../../stub/tax' | ||||
| import Tax from './EstimateTax' | ||||
| const { required, between, maxLength } = require('vuelidate/lib/validators') | ||||
| const { required, between, maxLength, numeric } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @ -361,7 +362,9 @@ export default { | ||||
|       discountPerItem: null, | ||||
|       initLoading: false, | ||||
|       isLoading: false, | ||||
|       maxDiscount: 0 | ||||
|       maxDiscount: 0, | ||||
|       estimatePrefix: null, | ||||
|       estimateNumAttribute: null | ||||
|     } | ||||
|   }, | ||||
|   validations () { | ||||
| @ -373,9 +376,6 @@ export default { | ||||
|         expiry_date: { | ||||
|           required | ||||
|         }, | ||||
|         estimate_number: { | ||||
|           required | ||||
|         }, | ||||
|         discount_val: { | ||||
|           between: between(0, this.subtotal) | ||||
|         }, | ||||
| @ -388,6 +388,10 @@ export default { | ||||
|       }, | ||||
|       selectedCustomer: { | ||||
|         required | ||||
|       }, | ||||
|       estimateNumAttribute: { | ||||
|         required, | ||||
|         numeric | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -559,6 +563,8 @@ export default { | ||||
|           this.taxPerItem = response.data.tax_per_item | ||||
|           this.selectedCurrency = this.defaultCurrency | ||||
|           this.estimateTemplates = response.data.estimateTemplates | ||||
|           this.estimatePrefix = response.data.estimate_prefix | ||||
|           this.estimateNumAttribute = response.data.nextEstimateNumber | ||||
|         } | ||||
|         this.initLoading = false | ||||
|         return | ||||
| @ -574,8 +580,9 @@ export default { | ||||
|         let today = new Date() | ||||
|         this.newEstimate.estimate_date = moment(today).toString() | ||||
|         this.newEstimate.expiry_date = moment(today).add(7, 'days').toString() | ||||
|         this.newEstimate.estimate_number = response.data.nextEstimateNumber | ||||
|         this.itemList = response.data.items | ||||
|         this.estimatePrefix = response.data.estimate_prefix | ||||
|         this.estimateNumAttribute = response.data.nextEstimateNumber | ||||
|       } | ||||
|       this.initLoading = false | ||||
|     }, | ||||
| @ -604,6 +611,7 @@ export default { | ||||
|       } | ||||
|  | ||||
|       this.isLoading = true | ||||
|       this.newEstimate.estimate_number = this.estimatePrefix + '-' + this.estimateNumAttribute | ||||
|  | ||||
|       let data = { | ||||
|         ...this.newEstimate, | ||||
| @ -637,7 +645,11 @@ export default { | ||||
|         this.isLoading = false | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         console.log(err) | ||||
|         if (err.response.data.errors.estimate_number) { | ||||
|           window.toastr['error'](err.response.data.errors.estimate_number) | ||||
|           return true | ||||
|         } | ||||
|         window.toastr['error'](err.response.data.message) | ||||
|       }) | ||||
|     }, | ||||
|     submitUpdate (data) { | ||||
| @ -650,7 +662,11 @@ export default { | ||||
|         this.isLoading = false | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         console.log(err) | ||||
|         if (err.response.data.errors.estimate_number) { | ||||
|           window.toastr['error'](err.response.data.errors.estimate_number) | ||||
|           return true | ||||
|         } | ||||
|         window.toastr['error'](err.response.data.message) | ||||
|       }) | ||||
|     }, | ||||
|     checkItemsData (index, isValid) { | ||||
|  | ||||
| @ -127,14 +127,15 @@ | ||||
|           <div class="row mt-4"> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('invoices.invoice_number') }}<span class="text-danger"> * </span></label> | ||||
|               <base-input | ||||
|                 :invalid="$v.newInvoice.invoice_number.$error" | ||||
|                 :read-only="true" | ||||
|                 v-model="newInvoice.invoice_number" | ||||
|               <base-prefix-input | ||||
|                 v-model="invoiceNumAttribute" | ||||
|                 :invalid="$v.invoiceNumAttribute.$error" | ||||
|                 :prefix="invoicePrefix" | ||||
|                 icon="hashtag" | ||||
|                 @input="$v.newInvoice.invoice_number.$touch()" | ||||
|                 @input="$v.invoiceNumAttribute.$touch()" | ||||
|               /> | ||||
|               <span v-show="$v.newInvoice.invoice_number.$error && !$v.newInvoice.invoice_number.required" class="text-danger mt-1"> {{ $tc('validation.required') }}  </span> | ||||
|               <span v-show="$v.invoiceNumAttribute.$error && !$v.invoiceNumAttribute.required" class="text-danger mt-1"> {{ $tc('validation.required') }}  </span> | ||||
|               <span v-show="!$v.invoiceNumAttribute.numeric" class="text-danger mt-1"> {{ $tc('validation.numbers_only') }}  </span> | ||||
|             </div> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('invoices.ref_number') }}</label> | ||||
| @ -320,7 +321,7 @@ import { validationMixin } from 'vuelidate' | ||||
| import Guid from 'guid' | ||||
| import TaxStub from '../../stub/tax' | ||||
| import Tax from './InvoiceTax' | ||||
| const { required, between, maxLength } = require('vuelidate/lib/validators') | ||||
| const { required, between, maxLength, numeric } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @ -361,7 +362,9 @@ export default { | ||||
|       discountPerItem: null, | ||||
|       initLoading: false, | ||||
|       isLoading: false, | ||||
|       maxDiscount: 0 | ||||
|       maxDiscount: 0, | ||||
|       invoicePrefix: null, | ||||
|       invoiceNumAttribute: null | ||||
|     } | ||||
|   }, | ||||
|   validations () { | ||||
| @ -373,9 +376,6 @@ export default { | ||||
|         due_date: { | ||||
|           required | ||||
|         }, | ||||
|         invoice_number: { | ||||
|           required | ||||
|         }, | ||||
|         discount_val: { | ||||
|           between: between(0, this.subtotal) | ||||
|         }, | ||||
| @ -388,6 +388,10 @@ export default { | ||||
|       }, | ||||
|       selectedCustomer: { | ||||
|         required | ||||
|       }, | ||||
|       invoiceNumAttribute: { | ||||
|         required, | ||||
|         numeric | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -559,6 +563,8 @@ export default { | ||||
|           this.taxPerItem = response.data.tax_per_item | ||||
|           this.selectedCurrency = this.defaultCurrency | ||||
|           this.invoiceTemplates = response.data.invoiceTemplates | ||||
|           this.invoicePrefix = response.data.invoice_prefix | ||||
|           this.invoiceNumAttribute = response.data.nextInvoiceNumber | ||||
|         } | ||||
|         this.initLoading = false | ||||
|         return | ||||
| @ -574,8 +580,9 @@ export default { | ||||
|         let today = new Date() | ||||
|         this.newInvoice.invoice_date = moment(today).toString() | ||||
|         this.newInvoice.due_date = moment(today).add(7, 'days').toString() | ||||
|         this.newInvoice.invoice_number = response.data.nextInvoiceNumber | ||||
|         this.itemList = response.data.items | ||||
|         this.invoicePrefix = response.data.invoice_prefix | ||||
|         this.invoiceNumAttribute = response.data.nextInvoiceNumber | ||||
|       } | ||||
|       this.initLoading = false | ||||
|     }, | ||||
| @ -604,6 +611,7 @@ export default { | ||||
|       } | ||||
|  | ||||
|       this.isLoading = true | ||||
|       this.newInvoice.invoice_number = this.invoicePrefix + '-' + this.invoiceNumAttribute | ||||
|  | ||||
|       let data = { | ||||
|         ...this.newInvoice, | ||||
| @ -637,6 +645,10 @@ export default { | ||||
|         this.isLoading = false | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         if (err.response.data.errors.invoice_number) { | ||||
|           window.toastr['error'](err.response.data.errors.invoice_number) | ||||
|           return true | ||||
|         } | ||||
|         console.log(err) | ||||
|       }) | ||||
|     }, | ||||
| @ -653,6 +665,10 @@ export default { | ||||
|         } | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         if (err.response.data.errors.invoice_number) { | ||||
|           window.toastr['error'](err.response.data.errors.invoice_number) | ||||
|           return true | ||||
|         } | ||||
|         console.log(err) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
| @ -40,16 +40,15 @@ | ||||
|             <div class="col-sm-6"> | ||||
|               <div class="form-group"> | ||||
|                 <label class="form-label">{{ $t('payments.payment_number') }}</label><span class="text-danger"> *</span> | ||||
|                 <base-input | ||||
|                   :invalid="$v.formData.payment_number.$error" | ||||
|                   v-model.trim="formData.payment_number" | ||||
|                   read-only | ||||
|                   type="text" | ||||
|                   name="email" | ||||
|                   @input="$v.formData.payment_number.$touch()" | ||||
|                 <base-prefix-input | ||||
|                   :invalid="$v.paymentNumAttribute.$error" | ||||
|                   v-model.trim="paymentNumAttribute" | ||||
|                   :prefix="paymentPrefix" | ||||
|                   @input="$v.paymentNumAttribute.$touch()" | ||||
|                 /> | ||||
|                 <div v-if="$v.formData.payment_number.$error"> | ||||
|                   <span v-if="!$v.formData.payment_number.required" class="text-danger">{{ $tc('validation.required') }}</span> | ||||
|                 <div v-if="$v.paymentNumAttribute.$error"> | ||||
|                   <span v-if="!$v.paymentNumAttribute.required" class="text-danger">{{ $tc('validation.required') }}</span> | ||||
|                   <span v-if="!$v.paymentNumAttribute.numeric" class="text-danger">{{ $tc('validation.numbers_only') }}</span> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
| @ -155,7 +154,7 @@ import { mapActions, mapGetters } from 'vuex' | ||||
| import MultiSelect from 'vue-multiselect' | ||||
| import { validationMixin } from 'vuelidate' | ||||
| import moment from 'moment' | ||||
| const { required, between, maxLength } = require('vuelidate/lib/validators') | ||||
| const { required, between, maxLength, numeric } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   components: { MultiSelect }, | ||||
| @ -184,7 +183,9 @@ export default { | ||||
|       invoiceList: [], | ||||
|       isLoading: false, | ||||
|       maxPayableAmount: Number.MAX_SAFE_INTEGER, | ||||
|       isSettingInitialData: true | ||||
|       isSettingInitialData: true, | ||||
|       paymentNumAttribute: null, | ||||
|       paymentPrefix: '' | ||||
|     } | ||||
|   }, | ||||
|   validations () { | ||||
| @ -193,9 +194,6 @@ export default { | ||||
|         required | ||||
|       }, | ||||
|       formData: { | ||||
|         payment_number: { | ||||
|           required | ||||
|         }, | ||||
|         payment_date: { | ||||
|           required | ||||
|         }, | ||||
| @ -206,6 +204,10 @@ export default { | ||||
|         notes: { | ||||
|           maxLength: maxLength(255) | ||||
|         } | ||||
|       }, | ||||
|       paymentNumAttribute: { | ||||
|         required, | ||||
|         numeric | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -297,6 +299,8 @@ export default { | ||||
|         this.customer = response.data.payment.user | ||||
|         this.formData.payment_date = moment(response.data.payment.payment_date, 'YYYY-MM-DD').toString() | ||||
|         this.formData.amount = parseFloat(response.data.payment.amount) | ||||
|         this.paymentPrefix = response.data.payment_prefix | ||||
|         this.paymentNumAttribute = response.data.nextPaymentNumber | ||||
|         if (response.data.payment.invoice !== null) { | ||||
|           this.maxPayableAmount = parseInt(response.data.payment.amount) + parseInt(response.data.payment.invoice.due_amount) | ||||
|           this.invoice = response.data.payment.invoice | ||||
| @ -305,7 +309,8 @@ export default { | ||||
|       } else { | ||||
|         let response = await this.fetchCreatePayment() | ||||
|         this.customerList = response.data.customers | ||||
|         this.formData.payment_number = response.data.nextPaymentNumber | ||||
|         this.paymentNumAttribute = response.data.nextPaymentNumber | ||||
|         this.paymentPrefix = response.data.payment_prefix | ||||
|         this.formData.payment_date = moment(new Date()).toString() | ||||
|       } | ||||
|       return true | ||||
| @ -332,6 +337,9 @@ export default { | ||||
|       if (this.$v.$invalid) { | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       this.formData.payment_number = this.paymentPrefix + '-' + this.paymentNumAttribute | ||||
|  | ||||
|       if (this.isEdit) { | ||||
|         let data = { | ||||
|           editData: { | ||||
| @ -340,35 +348,53 @@ export default { | ||||
|           }, | ||||
|           id: this.$route.params.id | ||||
|         } | ||||
|         let response = await this.updatePayment(data) | ||||
|         if (response.data.success) { | ||||
|           window.toastr['success'](this.$t('payments.updated_message')) | ||||
|           this.$router.push('/admin/payments') | ||||
|           return true | ||||
|         try { | ||||
|           let response = await this.updatePayment(data) | ||||
|           if (response.data.success) { | ||||
|             window.toastr['success'](this.$t('payments.updated_message')) | ||||
|             this.$router.push('/admin/payments') | ||||
|             return true | ||||
|           } | ||||
|           if (response.data.error === 'invalid_amount') { | ||||
|             window.toastr['error'](this.$t('invalid_amount_message')) | ||||
|             return false | ||||
|           } | ||||
|           window.toastr['error'](response.data.error) | ||||
|         } catch (err) { | ||||
|           this.isLoading = false | ||||
|           if (err.response.data.errors.payment_number) { | ||||
|             window.toastr['error'](err.response.data.errors.payment_number) | ||||
|             return true | ||||
|           } | ||||
|           window.toastr['error'](err.response.data.message) | ||||
|         } | ||||
|         if (response.data.error === 'invalid_amount') { | ||||
|           window.toastr['error'](this.$t('invalid_amount_message')) | ||||
|           return false | ||||
|         } | ||||
|         window.toastr['error'](response.data.error) | ||||
|       } else { | ||||
|         let data = { | ||||
|           ...this.formData, | ||||
|           payment_date: moment(this.formData.payment_date).format('DD/MM/YYYY') | ||||
|         } | ||||
|         this.isLoading = true | ||||
|         let response = await this.addPayment(data) | ||||
|         if (response.data.success) { | ||||
|           window.toastr['success'](this.$t('payments.created_message')) | ||||
|           this.$router.push('/admin/payments') | ||||
|           this.isLoading = true | ||||
|           return true | ||||
|         try { | ||||
|           let response = await this.addPayment(data) | ||||
|           if (response.data.success) { | ||||
|             window.toastr['success'](this.$t('payments.created_message')) | ||||
|             this.$router.push('/admin/payments') | ||||
|             this.isLoading = true | ||||
|             return true | ||||
|           } | ||||
|           if (response.data.error === 'invalid_amount') { | ||||
|             window.toastr['error'](this.$t('invalid_amount_message')) | ||||
|             return false | ||||
|           } | ||||
|           window.toastr['error'](response.data.error) | ||||
|         } catch (err) { | ||||
|           this.isLoading = false | ||||
|           if (err.response.data.errors.payment_number) { | ||||
|             window.toastr['error'](err.response.data.errors.payment_number) | ||||
|             return true | ||||
|           } | ||||
|           window.toastr['error'](err.response.data.message) | ||||
|         } | ||||
|         if (response.data.error === 'invalid_amount') { | ||||
|           window.toastr['error'](this.$t('invalid_amount_message')) | ||||
|           return false | ||||
|         } | ||||
|         window.toastr['error'](response.data.error) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
							
								
								
									
										385
									
								
								resources/assets/js/views/settings/Customization.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								resources/assets/js/views/settings/Customization.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,385 @@ | ||||
| <template> | ||||
|   <div class="setting-main-container customization"> | ||||
|     <div class="card setting-card"> | ||||
|       <ul class="tabs"> | ||||
|         <li class="tab" @click="setActiveTab('INVOICES')"> | ||||
|           <a :class="['tab-link', {'a-active': activeTab === 'INVOICES'}]" href="#">{{ $t('settings.customization.invoices.title') }}</a> | ||||
|         </li> | ||||
|         <li class="tab" @click="setActiveTab('ESTIMATES')"> | ||||
|           <a :class="['tab-link', {'a-active': activeTab === 'ESTIMATES'}]" href="#">{{ $t('settings.customization.estimates.title') }}</a> | ||||
|         </li> | ||||
|         <li class="tab" @click="setActiveTab('PAYMENTS')"> | ||||
|           <a :class="['tab-link', {'a-active': activeTab === 'PAYMENTS'}]" href="#">{{ $t('settings.customization.payments.title') }}</a> | ||||
|         </li> | ||||
|       </ul> | ||||
|  | ||||
|       <!-- Invoices Tab --> | ||||
|       <transition name="fade-customize"> | ||||
|         <div v-if="activeTab === 'INVOICES'" class="invoice-tab"> | ||||
|           <form action="" class="form-section" @submit.prevent="updateInvoiceSetting"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-12 mb-4"> | ||||
|                 <label class="input-label">{{ $t('settings.customization.invoices.invoice_prefix') }}</label> | ||||
|                 <base-input | ||||
|                   v-model="invoices.invoice_prefix" | ||||
|                   :invalid="$v.invoices.invoice_prefix.$error" | ||||
|                   class="prefix-input" | ||||
|                   @input="$v.invoices.invoice_prefix.$touch()" | ||||
|                   @keyup="changeToUppercase('INVOICES')" | ||||
|                 /> | ||||
|                 <span v-show="!$v.invoices.invoice_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span> | ||||
|                 <span v-if="!$v.invoices.invoice_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span> | ||||
|                 <span v-if="!$v.invoices.invoice_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="row mb-3"> | ||||
|               <div class="col-md-12"> | ||||
|                 <base-button | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                 > | ||||
|                   {{ $t('settings.customization.save') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <hr> | ||||
|           </form> | ||||
|           <div class="col-md-12 mt-3"> | ||||
|             <div class="page-header"> | ||||
|               <h3 class="page-title"> | ||||
|                 {{ $t('settings.customization.invoices.invoice_settings') }} | ||||
|               </h3> | ||||
|               <div class="flex-box"> | ||||
|                 <div class="left"> | ||||
|                   <base-switch | ||||
|                     v-model="invoiceAutogenerate" | ||||
|                     class="btn-switch" | ||||
|                     @change="setInvoiceSetting" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="right ml-15"> | ||||
|                   <p class="box-title">  {{ $t('settings.customization.invoices.autogenerate_invoice_number') }} </p> | ||||
|                   <p class="box-desc">  {{ $t('settings.customization.invoices.invoice_setting_description') }} </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </transition> | ||||
|  | ||||
|       <!-- Estimates Tab --> | ||||
|       <transition name="fade-customize"> | ||||
|         <div v-if="activeTab === 'ESTIMATES'" class="estimate-tab"> | ||||
|           <form action="" class="form-section" @submit.prevent="updateEstimateSetting"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-12 mb-4"> | ||||
|                 <label class="input-label">{{ $t('settings.customization.estimates.estimate_prefix') }}</label> | ||||
|                 <base-input | ||||
|                   v-model="estimates.estimate_prefix" | ||||
|                   :invalid="$v.estimates.estimate_prefix.$error" | ||||
|                   class="prefix-input" | ||||
|                   @input="$v.estimates.estimate_prefix.$touch()" | ||||
|                   @keyup="changeToUppercase('ESTIMATES')" | ||||
|                 /> | ||||
|                 <span v-show="!$v.estimates.estimate_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span> | ||||
|                 <span v-if="!$v.estimates.estimate_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span> | ||||
|                 <span v-if="!$v.estimates.estimate_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="row mb-3"> | ||||
|               <div class="col-md-12"> | ||||
|                 <base-button | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                 > | ||||
|                   {{ $t('settings.customization.save') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <hr> | ||||
|           </form> | ||||
|           <div class="col-md-12 mt-3"> | ||||
|             <div class="page-header"> | ||||
|               <h3 class="page-title"> | ||||
|                 {{ $t('settings.customization.estimates.estimate_settings') }} | ||||
|               </h3> | ||||
|               <div class="flex-box"> | ||||
|                 <div class="left"> | ||||
|                   <base-switch | ||||
|                     v-model="estimateAutogenerate" | ||||
|                     class="btn-switch" | ||||
|                     @change="setEstimateSetting" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="right ml-15"> | ||||
|                   <p class="box-title">  {{ $t('settings.customization.estimates.autogenerate_estimate_number') }} </p> | ||||
|                   <p class="box-desc">  {{ $t('settings.customization.estimates.estimate_setting_description') }} </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </transition> | ||||
|  | ||||
|       <!-- Payments Tab --> | ||||
|       <transition name="fade-customize"> | ||||
|         <div v-if="activeTab === 'PAYMENTS'" class="payment-tab"> | ||||
|           <form action="" class="form-section" @submit.prevent="updatePaymentSetting"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-12 mb-4"> | ||||
|                 <label class="input-label">{{ $t('settings.customization.payments.payment_prefix') }}</label> | ||||
|                 <base-input | ||||
|                   v-model="payments.payment_prefix" | ||||
|                   :invalid="$v.payments.payment_prefix.$error" | ||||
|                   class="prefix-input" | ||||
|                   @input="$v.payments.payment_prefix.$touch()" | ||||
|                   @keyup="changeToUppercase('PAYMENTS')" | ||||
|                 /> | ||||
|                 <span v-show="!$v.payments.payment_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span> | ||||
|                 <span v-if="!$v.payments.payment_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span> | ||||
|                 <span v-if="!$v.payments.payment_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="row mb-3"> | ||||
|               <div class="col-md-12"> | ||||
|                 <base-button | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                 > | ||||
|                   {{ $t('settings.customization.save') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|             </div> | ||||
|           </form> | ||||
|           <hr> | ||||
|           <div class="col-md-12 mt-4"> | ||||
|             <div class="page-header"> | ||||
|               <h3 class="page-title"> | ||||
|                 {{ $t('settings.customization.payments.payment_settings') }} | ||||
|               </h3> | ||||
|               <div class="flex-box"> | ||||
|                 <div class="left"> | ||||
|                   <base-switch | ||||
|                     v-model="paymentAutogenerate" | ||||
|                     class="btn-switch" | ||||
|                     @change="setPaymentSetting" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="right ml-15"> | ||||
|                   <p class="box-title">  {{ $t('settings.customization.payments.autogenerate_payment_number') }} </p> | ||||
|                   <p class="box-desc">  {{ $t('settings.customization.payments.payment_setting_description') }} </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </transition> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script> | ||||
| import { validationMixin } from 'vuelidate' | ||||
| const { required, maxLength, alpha } = require('vuelidate/lib/validators') | ||||
| export default { | ||||
|   mixins: [validationMixin], | ||||
|   data () { | ||||
|     return { | ||||
|       activeTab: 'INVOICES', | ||||
|       invoiceAutogenerate: false, | ||||
|       estimateAutogenerate: false, | ||||
|       paymentAutogenerate: false, | ||||
|       invoices: { | ||||
|         invoice_prefix: null, | ||||
|         invoice_notes: null, | ||||
|         invoice_terms_and_conditions: null | ||||
|       }, | ||||
|       estimates: { | ||||
|         estimate_prefix: null, | ||||
|         estimate_notes: null, | ||||
|         estimate_terms_and_conditions: null | ||||
|       }, | ||||
|       payments: { | ||||
|         payment_prefix: null | ||||
|       }, | ||||
|       currentData: null | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     activeTab () { | ||||
|       this.loadData() | ||||
|     } | ||||
|   }, | ||||
|   validations: { | ||||
|     invoices: { | ||||
|       invoice_prefix: { | ||||
|         required, | ||||
|         maxLength: maxLength(5), | ||||
|         alpha | ||||
|       } | ||||
|     }, | ||||
|     estimates: { | ||||
|       estimate_prefix: { | ||||
|         required, | ||||
|         maxLength: maxLength(5), | ||||
|         alpha | ||||
|       } | ||||
|     }, | ||||
|     payments: { | ||||
|       payment_prefix: { | ||||
|         required, | ||||
|         maxLength: maxLength(5), | ||||
|         alpha | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   created () { | ||||
|     this.loadData() | ||||
|   }, | ||||
|   methods: { | ||||
|     async setInvoiceSetting () { | ||||
|       let data = { | ||||
|         key: 'invoice_auto_generate', | ||||
|         value: this.invoiceAutogenerate ? 'YES' : 'NO' | ||||
|       } | ||||
|       let response = await window.axios.put('/api/settings/update-setting', data) | ||||
|       if (response.data) { | ||||
|         window.toastr['success'](this.$t('general.setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async setEstimateSetting () { | ||||
|       let data = { | ||||
|         key: 'estimate_auto_generate', | ||||
|         value: this.estimateAutogenerate ? 'YES' : 'NO' | ||||
|       } | ||||
|       let response = await window.axios.put('/api/settings/update-setting', data) | ||||
|       if (response.data) { | ||||
|         window.toastr['success'](this.$t('general.setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     changeToUppercase (currentTab) { | ||||
|       if (currentTab === 'INVOICES') { | ||||
|         this.invoices.invoice_prefix = this.invoices.invoice_prefix.toUpperCase() | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       if (currentTab === 'ESTIMATES') { | ||||
|         this.estimates.estimate_prefix = this.estimates.estimate_prefix.toUpperCase() | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       if (currentTab === 'PAYMENTS') { | ||||
|         this.payments.payment_prefix = this.payments.payment_prefix.toUpperCase() | ||||
|         return true | ||||
|       } | ||||
|     }, | ||||
|     async setPaymentSetting () { | ||||
|       let data = { | ||||
|         key: 'payment_auto_generate', | ||||
|         value: this.paymentAutogenerate ? 'YES' : 'NO' | ||||
|       } | ||||
|       let response = await window.axios.put('/api/settings/update-setting', data) | ||||
|       if (response.data) { | ||||
|         window.toastr['success'](this.$t('general.setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async loadData () { | ||||
|       let res = await window.axios.get('/api/settings/get-customize-setting') | ||||
|  | ||||
|       if (res.data) { | ||||
|         this.invoices.invoice_prefix = res.data.invoice_prefix | ||||
|         this.invoices.invoice_notes = res.data.invoice_notes | ||||
|         this.invoices.invoice_terms_and_conditions = res.data.invoice_terms_and_conditions | ||||
|         this.estimates.estimate_prefix = res.data.estimate_prefix | ||||
|         this.estimates.estimate_notes = res.data.estimate_notes | ||||
|         this.estimates.estimate_terms_and_conditions = res.data.estimate_terms_and_conditions | ||||
|         this.payments.payment_prefix = res.data.payment_prefix | ||||
|  | ||||
|         if (res.data.invoice_auto_generate === 'YES') { | ||||
|           this.invoiceAutogenerate = true | ||||
|         } else { | ||||
|           this.invoiceAutogenerate = false | ||||
|         } | ||||
|  | ||||
|         if (res.data.estimate_auto_generate === 'YES') { | ||||
|           this.estimateAutogenerate = true | ||||
|         } else { | ||||
|           this.estimateAutogenerate = false | ||||
|         } | ||||
|  | ||||
|         if (res.data.payment_auto_generate === 'YES') { | ||||
|           this.paymentAutogenerate = true | ||||
|         } else { | ||||
|           this.paymentAutogenerate = false | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     async updateInvoiceSetting () { | ||||
|       this.$v.invoices.$touch() | ||||
|  | ||||
|       if (this.$v.invoices.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       let data = {type: 'INVOICES', ...this.invoices} | ||||
|  | ||||
|       if (this.updateSetting(data)) { | ||||
|         window.toastr['success'](this.$t('settings.customization.invoices.invoice_setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async updateEstimateSetting () { | ||||
|       this.$v.estimates.$touch() | ||||
|  | ||||
|       if (this.$v.estimates.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       let data = {type: 'ESTIMATES', ...this.estimates} | ||||
|  | ||||
|       if (this.updateSetting(data)) { | ||||
|         window.toastr['success'](this.$t('settings.customization.estimates.estimate_setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async updatePaymentSetting () { | ||||
|       this.$v.payments.$touch() | ||||
|  | ||||
|       if (this.$v.payments.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       let data = {type: 'PAYMENTS', ...this.payments} | ||||
|  | ||||
|       if (this.updateSetting(data)) { | ||||
|         window.toastr['success'](this.$t('settings.customization.payments.payment_setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async updateSetting (data) { | ||||
|       let res = await window.axios.put('/api/settings/update-customize-setting', data) | ||||
|  | ||||
|       if (res.data.success) { | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       return false | ||||
|     }, | ||||
|     setActiveTab (val) { | ||||
|       this.activeTab = val | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style> | ||||
|   .fade-customize-enter-active { | ||||
|     transition: opacity 0.9s; | ||||
|   } | ||||
|  | ||||
|   .fade-customize-leave-active  { | ||||
|     transition: opacity 0s; | ||||
|   } | ||||
|  | ||||
|   .fade-customize-enter, .fade-customize-leave-to /* .fade-leave-active below version 2.1.8 */ { | ||||
|     opacity: 0; | ||||
|   } | ||||
| </style> | ||||
| @ -45,6 +45,12 @@ export default { | ||||
|           icon: 'building', | ||||
|           iconType: 'far' | ||||
|         }, | ||||
|         { | ||||
|           link: '/admin/settings/customization', | ||||
|           title: 'settings.menu_title.customization', | ||||
|           icon: 'edit', | ||||
|           iconType: 'fa' | ||||
|         }, | ||||
|         { | ||||
|           link: '/admin/settings/preferences', | ||||
|           title: 'settings.menu_title.preferences', | ||||
|  | ||||
		Reference in New Issue
	
	Block a user