mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-01 20:17:35 -04:00 
			
		
		
		
	Import (#3360)
* Fixes for test data * Fixes for tests * Remove legacy vue components * Add routing number to client gateway tokens * working on important documents and company gateways * Import fixes
This commit is contained in:
		
							parent
							
								
									0e7904a74b
								
							
						
					
					
						commit
						c1d3fd12a8
					
				| @ -98,7 +98,7 @@ class Creative extends AbstractDesign | ||||
|         <tbody> | ||||
|             $table_body | ||||
|             <tr> | ||||
|                 <td colspan="7" ref="note" class="px-4 py-4">$entity.public_notes</td> | ||||
|                 <td colspan="5" ref="note" class="px-4 py-4">$entity.public_notes</td> | ||||
|                 <td ref="quantity" class="px-4 py-4 flex flex-col"> | ||||
|                     $total_tax_labels | ||||
|                     $line_tax_labels | ||||
|  | ||||
| @ -61,7 +61,7 @@ class InvoiceItemFactory | ||||
|             $item->line_total = $item->quantity * $item->cost; | ||||
|             $item->is_amount_discount = true; | ||||
|             $item->discount = $faker->numberBetween(1, 10); | ||||
|             $item->notes = $faker->realText(20); | ||||
|             $item->notes = $faker->realText(50); | ||||
|             $item->product_key = $faker->word(); | ||||
|             $item->custom_value1 = $faker->realText(10); | ||||
|             $item->custom_value2 = $faker->realText(10); | ||||
|  | ||||
| @ -7,7 +7,9 @@ | ||||
|  *       @OA\Property(property="company_id", type="string", example="2", description="______"), | ||||
|  *       @OA\Property(property="client_id", type="string", example="2", description="______"), | ||||
|  *       @OA\Property(property="token", type="string", example="2", description="______"), | ||||
|  *       @OA\Property(property="routing_number", type="string", example="2", description="______"), | ||||
|  *       @OA\Property(property="company_gateway_id", type="string", example="2", description="______"), | ||||
|  *       @OA\Property(property="is_default", type="boolean", example="true", description="______"), | ||||
|  *        | ||||
|  * ) | ||||
|  */ | ||||
|  | ||||
| @ -143,6 +143,7 @@ class Import implements ShouldQueue | ||||
|         $validator = Validator::make($data, $rules); | ||||
| 
 | ||||
|         if ($validator->fails()) { | ||||
|             \Log::error($validator->errors()); | ||||
|             throw new MigrationValidatorFailed($validator->errors()); | ||||
|         } | ||||
| 
 | ||||
| @ -443,12 +444,12 @@ class Import implements ShouldQueue | ||||
|             ); | ||||
| 
 | ||||
|             $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; | ||||
|              | ||||
|             $key = "invoices_{$resource['id']}"; | ||||
| 
 | ||||
|             $this->ids['quotes'] = [ | ||||
|                 "quotes_{$old_user_key}" => [ | ||||
|                     'old' => $old_user_key, | ||||
|                     'new' => $invoice->id, | ||||
|                 ] | ||||
|             $this->ids['quotes'][$key] = [ | ||||
|                 'old' => $resource['id'], | ||||
|                 'new' => $invoice->id, | ||||
|             ]; | ||||
| 
 | ||||
|         } | ||||
| @ -517,10 +518,12 @@ class Import implements ShouldQueue | ||||
|             $modified = $resource; | ||||
| 
 | ||||
|             if (array_key_exists('invoice_id', $resource) && !array_key_exists('invoices', $this->ids)) { | ||||
|                 \Log::error("ivoice id missing"); | ||||
|                 throw new ResourceDependencyMissing(array_key_first($data), 'invoices'); | ||||
|             } | ||||
| 
 | ||||
|             if (array_key_exists('expense_id', $resource) && !array_key_exists('expenses', $this->ids)) { | ||||
|                 \Log::error("expense id missing"); | ||||
|                 throw new ResourceDependencyMissing(array_key_first($data), 'expenses'); | ||||
|             } | ||||
|              | ||||
| @ -534,21 +537,21 @@ class Import implements ShouldQueue | ||||
|             } | ||||
| 
 | ||||
|             if(array_key_exists('expense_id', $resource) && $resource['expense_id']) { | ||||
|                 $modified['documentable_id'] = $this->transformId('expense', $resource['expense_id']); | ||||
|                 $modified['documentable_id'] = $this->transformId('expenses', $resource['expense_id']); | ||||
|                 $modified['documentable_type'] = 'App\\Models\\Expense'; | ||||
|             } | ||||
| 
 | ||||
|             $modified['user_id'] = $this->processUserId($resource); | ||||
|             $modified['company_id'] = $this->company->id; | ||||
| 
 | ||||
|             $payment = Document::create($modified); | ||||
|             $document = Document::create($modified); | ||||
| 
 | ||||
|             $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id; | ||||
| 
 | ||||
|             $this->ids['payments'] = [ | ||||
|                 "payments_{$old_user_key}" => [ | ||||
|             $this->ids['documents'] = [ | ||||
|                 "documents_{$old_user_key}" => [ | ||||
|                     'old' => $old_user_key, | ||||
|                     'new' => $payment->id, | ||||
|                     'new' => $document->id, | ||||
|                 ] | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
| @ -18,6 +18,7 @@ use App\Models\DateFormat; | ||||
| use App\Models\Filterable; | ||||
| use App\Models\Paymentable; | ||||
| use App\Services\Ledger\LedgerService; | ||||
| use App\Services\Payment\PaymentService; | ||||
| use App\Utils\Number; | ||||
| use App\Utils\Traits\MakesDates; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| @ -175,6 +176,11 @@ class Payment extends BaseModel | ||||
|         return new LedgerService($this); | ||||
|     } | ||||
| 
 | ||||
|     public function service() | ||||
|     { | ||||
|         return new PaymentService($this); | ||||
|     } | ||||
| 
 | ||||
|     public function resolveRouteBinding($value) | ||||
|     { | ||||
|         return $this | ||||
|  | ||||
| @ -178,7 +178,7 @@ class StripePaymentDriver extends BasePaymentDriver | ||||
|             $payment_meta->exp_year = $stripe_payment_method_obj['card']['exp_year']; | ||||
|             $payment_meta->brand = $stripe_payment_method_obj['card']['brand']; | ||||
|             $payment_meta->last4 = $stripe_payment_method_obj['card']['last4']; | ||||
|             $payment_meta->type = $stripe_payment_method_obj['type']; | ||||
|             $payment_meta->type = GatewayType::CREDIT_CARD; | ||||
|         } | ||||
| 
 | ||||
|         $cgt = new ClientGatewayToken; | ||||
|  | ||||
| @ -5,13 +5,12 @@ use App\Credit; | ||||
| 
 | ||||
| class CreditService | ||||
| { | ||||
|      | ||||
|     protected $credit; | ||||
| 
 | ||||
| 
 | ||||
|     public function __construct($credit) | ||||
|     { | ||||
|         $this->credit = $credit; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function getCreditPdf($contact) | ||||
| @ -44,6 +43,7 @@ class CreditService | ||||
|     public function save() : ?Credit | ||||
|     { | ||||
|         $this->credit->save(); | ||||
| 
 | ||||
|         return $this->credit; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -29,7 +29,7 @@ class UpdateInvoicePayment | ||||
|                  | ||||
|                 $this->payment | ||||
|                      ->ledger() | ||||
|                      ->updatePaymentBalance($this->payment, ($invoice->balance*-1)); | ||||
|                      ->updatePaymentBalance($invoice->balance*-1); | ||||
|                  | ||||
|                 $this->payment->client | ||||
|                     ->service() | ||||
| @ -66,7 +66,7 @@ class UpdateInvoicePayment | ||||
| 
 | ||||
|                         $this->payment | ||||
|                              ->ledger() | ||||
|                              ->updatePaymentBalance($this->payment, ($invoice->partial*-1)); | ||||
|                              ->updatePaymentBalance($invoice->partial*-1); | ||||
| 
 | ||||
|                         $this->payment->client->service() | ||||
|                                                 ->updateBalance($invoice->partial*-1) | ||||
| @ -85,7 +85,7 @@ class UpdateInvoicePayment | ||||
|                          | ||||
|                         $this->payment | ||||
|                              ->ledger() | ||||
|                              ->updatePaymentBalance($this->payment, ($invoice->balance*-1)); | ||||
|                              ->updatePaymentBalance($invoice->balance*-1); | ||||
| 
 | ||||
|                         $this->payment->client->service() | ||||
|                                               ->updateBalance($invoice->balance*-1) | ||||
|  | ||||
| @ -1128,6 +1128,7 @@ class CreateUsersTable extends Migration | ||||
|             $table->unsignedInteger('company_id'); | ||||
|             $table->unsignedInteger('client_id')->nullable(); | ||||
|             $table->text('token')->nullable(); | ||||
|             $table->text('routing_number')->nullable(); | ||||
|             $table->unsignedInteger('company_gateway_id'); | ||||
|             $table->string('gateway_customer_reference')->nullable(); | ||||
|             $table->unsignedInteger('gateway_type_id'); | ||||
|  | ||||
| @ -8,8 +8,6 @@ use App\Events\Invoice\InvoiceWasUpdated; | ||||
| use App\Events\Payment\PaymentWasCreated; | ||||
| use App\Helpers\Invoice\InvoiceSum; | ||||
| use App\Helpers\Invoice\InvoiceSumInclusive; | ||||
| use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; | ||||
| //use App\Jobs\Invoice\UpdateInvoicePayment;
 | ||||
| use App\Listeners\Credit\CreateCreditInvitation; | ||||
| use App\Listeners\Invoice\CreateInvoiceInvitation; | ||||
| use App\Models\Account; | ||||
| @ -165,7 +163,7 @@ class RandomDataSeeder extends Seeder | ||||
| 
 | ||||
|             event(new CreateInvoiceInvitation($invoice)); | ||||
| 
 | ||||
|             UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, $invoice->balance, $invoice->company); | ||||
|             $invoice->ledger()->updateInvoiceBalance($invoice->balance); | ||||
| 
 | ||||
|             $invoice->service()->markSent()->save(); | ||||
| 
 | ||||
| @ -187,7 +185,7 @@ class RandomDataSeeder extends Seeder | ||||
| 
 | ||||
|                 event(new PaymentWasCreated($payment, $payment->company)); | ||||
| 
 | ||||
|                 $payment->service()->UpdateInvoicePayment(); | ||||
|                 $payment->service()->updateInvoicePayment(); | ||||
| 
 | ||||
|     //            UpdateInvoicePayment::dispatchNow($payment, $payment->company);
 | ||||
|             } | ||||
|  | ||||
							
								
								
									
										17
									
								
								jest.config.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								jest.config.js
									
									
									
									
										vendored
									
									
								
							| @ -1,17 +0,0 @@ | ||||
| module.exports = { | ||||
|   "roots": [ | ||||
|     "resources/js/src" | ||||
|   ], | ||||
|   "transform": { | ||||
|     "^.+\\.tsx?$": "ts-jest" | ||||
|   }, | ||||
|   "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$", | ||||
|   "moduleFileExtensions": [ | ||||
|     "ts", | ||||
|     "tsx", | ||||
|     "js", | ||||
|     "jsx", | ||||
|     "json", | ||||
|     "node" | ||||
|   ], | ||||
| } | ||||
							
								
								
									
										106
									
								
								resources/assets/js/vendor/I18n.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										106
									
								
								resources/assets/js/vendor/I18n.js
									
									
									
									
										vendored
									
									
								
							| @ -1,106 +0,0 @@ | ||||
| export default class I18n | ||||
| { | ||||
|     /** | ||||
|      * Initialize a new translation instance. | ||||
|      * | ||||
|      * @param  {string}  key | ||||
|      * @return {void} | ||||
|      */ | ||||
|     constructor(key = 'translations') | ||||
|     { | ||||
|         this.key = key; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get and replace the string of the given key. | ||||
|      * | ||||
|      * @param  {string}  key | ||||
|      * @param  {object}  replace | ||||
|      * @return {string} | ||||
|      */ | ||||
|     trans(key, replace = {}) | ||||
|     { | ||||
|         return this._replace(this._extract(key), replace); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get and pluralize the strings of the given key. | ||||
|      * | ||||
|      * @param  {string}  key | ||||
|      * @param  {number}  count | ||||
|      * @param  {object}  replace | ||||
|      * @return {string} | ||||
|      */ | ||||
|     trans_choice(key, count = 1, replace = {}) | ||||
|     { | ||||
|         let translations = this._extract(key, '|').split('|'), translation; | ||||
| 
 | ||||
|         translations.some(t => translation = this._match(t, count)); | ||||
| 
 | ||||
|         translation = translation || (count > 1 ? translations[1] : translations[0]); | ||||
| 
 | ||||
|         return this._replace(translation, replace); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Match the translation limit with the count. | ||||
|      * | ||||
|      * @param  {string}  translation | ||||
|      * @param  {number}  count | ||||
|      * @return {string|null} | ||||
|      */ | ||||
|     _match(translation, count) | ||||
|     { | ||||
|         let match = translation.match(/^[\{\[]([^\[\]\{\}]*)[\}\]](.*)/); | ||||
| 
 | ||||
|         if (! match) return; | ||||
| 
 | ||||
|         if (match[1].includes(',')) { | ||||
|             let [from, to] = match[1].split(','); | ||||
| 
 | ||||
|             if (to === '*' && count >= from) { | ||||
|                 return match[2]; | ||||
|             } else if (from === '*' && count <= to) { | ||||
|                 return match[2]; | ||||
|             } else if (count >= from && count <= to) { | ||||
|                 return match[2]; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return match[1] == count ? match[2] : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Replace the placeholders. | ||||
|      * | ||||
|      * @param  {string}  translation | ||||
|      * @param  {object}  replace | ||||
|      * @return {string} | ||||
|      */ | ||||
|     _replace(translation, replace) | ||||
|     { | ||||
|         for (let placeholder in replace) { | ||||
|             translation = translation | ||||
|                 .replace(`:${placeholder}`, replace[placeholder]) | ||||
|                 .replace(`:${placeholder.toUpperCase()}`, replace[placeholder].toUpperCase()) | ||||
|                 .replace( | ||||
|                     `:${placeholder.charAt(0).toUpperCase()}${placeholder.slice(1)}`, | ||||
|                     replace[placeholder].charAt(0).toUpperCase()+replace[placeholder].slice(1) | ||||
|                 ); | ||||
|         } | ||||
| 
 | ||||
|         return translation.trim(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The extract helper. | ||||
|      * | ||||
|      * @param  {string}  key | ||||
|      * @param  {mixed}  value | ||||
|      * @return {mixed} | ||||
|      */ | ||||
|     _extract(key, value = null) | ||||
|     { | ||||
|         return key.toString().split('.').reduce((t, i) => t[i] || (value || key), window[this.key]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										81762
									
								
								resources/assets/js/vue-i18n-locales.generated.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										81762
									
								
								resources/assets/js/vue-i18n-locales.generated.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										61
									
								
								resources/js/src/bootstrap.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								resources/js/src/bootstrap.js
									
									
									
									
										vendored
									
									
								
							| @ -1,61 +0,0 @@ | ||||
| // lodash handles our translations 
 | ||||
| import * as get from "lodash.get" | ||||
| 
 | ||||
| // import Toastr
 | ||||
| import Toastr from 'vue-toastr'; | ||||
| 
 | ||||
| // Import toastr scss file: need webpack sass-loader
 | ||||
| require('vue-toastr/src/vue-toastr.scss'); | ||||
| 
 | ||||
| import Vue from 'vue'; | ||||
| 
 | ||||
| // Register vue component
 | ||||
| Vue.component('vue-toastr',Toastr); | ||||
| 
 | ||||
| // Global translation helper
 | ||||
| Vue.prototype.trans = string => get(i18n, string); | ||||
| 
 | ||||
| 
 | ||||
| window.axios = require('axios'); | ||||
| window.Vue = require('vue'); | ||||
| 
 | ||||
| window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; | ||||
| 
 | ||||
| /* Development only*/ | ||||
| Vue.config.devtools = true; | ||||
| 
 | ||||
| window.axios.defaults.headers.common = { | ||||
|     'X-Requested-With': 'XMLHttpRequest', | ||||
|     'X-CSRF-TOKEN' : document.querySelector('meta[name="csrf-token"]').getAttribute('content') | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Next we will register the CSRF Token as a common header with Axios so that | ||||
|  * all outgoing HTTP requests automatically have it attached. This is just | ||||
|  * a simple convenience so we don't have to attach every token manually. | ||||
|  */ | ||||
| 
 | ||||
| let token = document.head.querySelector('meta[name="csrf-token"]'); | ||||
| 
 | ||||
| if (token) { | ||||
|     window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; | ||||
| } else { | ||||
|     console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Echo exposes an expressive API for subscribing to channels and listening | ||||
|  * for events that are broadcast by Laravel. Echo and event broadcasting | ||||
|  * allows your team to easily build robust real-time web applications. | ||||
|  */ | ||||
| 
 | ||||
| // import Echo from 'laravel-echo'
 | ||||
| 
 | ||||
| // window.Pusher = require('pusher-js');
 | ||||
| 
 | ||||
| // window.Echo = new Echo({
 | ||||
| //     broadcaster: 'pusher',
 | ||||
| //     key: process.env.MIX_PUSHER_APP_KEY,
 | ||||
| //     cluster: process.env.MIX_PUSHER_APP_CLUSTER,
 | ||||
| //     encrypted: true
 | ||||
| // });
 | ||||
| @ -1,70 +0,0 @@ | ||||
| //import * as Vue from 'vue';
 | ||||
| import Vue from 'vue'; | ||||
| import axios from 'axios'; | ||||
| import Form from '../utils/form'; | ||||
| import Client from '../models/client-model'; | ||||
| 
 | ||||
| // import Toastr
 | ||||
| import Toastr from 'vue-toastr'; | ||||
| // import toastr scss file: need webpack sass-loader
 | ||||
| require('vue-toastr/src/vue-toastr.scss'); | ||||
| // Register vue component
 | ||||
| Vue.component('vue-toastr',Toastr); | ||||
| 
 | ||||
| declare var client_object: any; | ||||
| declare var hashed_id: string; | ||||
| 
 | ||||
|  new Vue({ | ||||
|     el : '#client_create', | ||||
|     data: function () { | ||||
|         return { | ||||
|             form: new Form(<Client>client_object) | ||||
|         } | ||||
|     }, | ||||
|     methods:{ | ||||
|         remove(this:any, contact:any){ | ||||
|             let index = this.form.contacts.indexOf(contact); | ||||
|             this.form.contacts.splice(index, 1); | ||||
|         }, | ||||
|         add(this: any){ | ||||
|             this.form.contacts.push({first_name: '', last_name: '', email: '', phone: '', id: 0}); | ||||
|             window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); | ||||
|             this.$nextTick(() => { | ||||
|                      let index = this.form.contacts.length - 1; | ||||
|                      let input = this.$refs.first_name[index]; | ||||
|                      input.focus(); | ||||
|                   }); | ||||
|         }, | ||||
|         onSubmit() { | ||||
|             this.form.post('/clients/') | ||||
|                 .then(response => { | ||||
|                     this.$root.$refs.toastr.s("Created client"); //how are we going to handle translations here?
 | ||||
|                          | ||||
|                     window.location.href = '/clients/' + this.form.hashed_id + '/edit'; | ||||
| 
 | ||||
|                 }) | ||||
|                 .catch(error => { | ||||
| 
 | ||||
|                     this.$root.$refs.toastr.e("Error saving client"); | ||||
| 
 | ||||
|                 }); | ||||
|         }, | ||||
|         copy(type: any) { | ||||
|             if(type.includes('copy_billing')){ | ||||
|                 this.form.shipping_address1 = this.form.address1;  | ||||
|                 this.form.shipping_address2 = this.form.address2;  | ||||
|                 this.form.shipping_city = this.form.city;  | ||||
|                 this.form.shipping_state = this.form.state;  | ||||
|                 this.form.shipping_postal_code = this.form.postal_code;  | ||||
|                 this.form.shipping_country_id = this.form.country_id;  | ||||
|                 }else { | ||||
|                 this.form.address1 = this.form.shipping_address1;  | ||||
|                 this.form.address2 = this.form.shipping_address2;  | ||||
|                 this.form.city = this.form.shipping_city;  | ||||
|                 this.form.state = this.form.shipping_state;  | ||||
|                 this.form.postal_code = this.form.shipping_postal_code;  | ||||
|                 this.form.country_id = this.form.shipping_country_id;  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| @ -1,24 +0,0 @@ | ||||
| /* Allows us to use our native translation easily using {{ trans() }} syntax */ | ||||
| //const _ = require('lodash');
 | ||||
| 
 | ||||
| require('../bootstrap'); | ||||
| 
 | ||||
| /* Must be declare in every child view*/ | ||||
| declare var i18n; | ||||
| 
 | ||||
| import Vue from 'vue'; | ||||
| 
 | ||||
| Vue.component('client-edit', require('../components/client/ClientEdit.vue')); | ||||
| Vue.component('client-address', require('../components/client/ClientAddress.vue')); | ||||
| Vue.component('generic-address', require('../components/generic/Address.vue')); | ||||
| Vue.component('client-edit-form', require('../components/client/ClientEditForm.vue')); | ||||
| Vue.component('contact-edit', require('../components/client/ClientContactEdit.vue')); | ||||
| Vue.component('client-settings', require('../components/client/ClientSettings.vue')); | ||||
| 
 | ||||
| window.onload = function () { | ||||
| 
 | ||||
|     const app = new Vue({ | ||||
|         el: '#client_edit' | ||||
|     }); | ||||
| 
 | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| require('../bootstrap'); | ||||
| 
 | ||||
| /* Must be declare in every child view*/ | ||||
| declare var i18n; | ||||
| 
 | ||||
| import Vue from 'vue'; | ||||
| import axios from 'axios'; | ||||
| import store from '../store' | ||||
| 
 | ||||
| export default store | ||||
| 
 | ||||
| Vue.component('client-list', require('../components/client/ClientList.vue')); | ||||
| Vue.component('client-actions', require('../components/client/ClientActions.vue')); | ||||
| Vue.component('vuetable', require('vuetable-2/src/components/Vuetable')); | ||||
| Vue.component('vuetable-pagination', require('vuetable-2/src/components/VuetablePagination')); | ||||
| Vue.component('vuetable-pagination-bootstrap', require('../components/util/VuetablePaginationBootstrap')); | ||||
| Vue.component('vuetable-filter-bar', require('../components/util/VuetableFilterBar')); | ||||
| Vue.component('vuetable-query-filter', require('../components/client/ClientFilters.vue')); | ||||
| Vue.component('vuetable-multi-select', require('../components/util/VuetableMultiSelect.vue')); | ||||
| Vue.component('list-actions', require('../components/util/VueListActions.vue')); | ||||
| 
 | ||||
| 
 | ||||
| window.onload = function () { | ||||
| 
 | ||||
|     const app = new Vue({ | ||||
|         el: '#client_list', | ||||
|         store  | ||||
|     }); | ||||
| 
 | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| /* Allows us to use our native translation easily using {{ trans() }} syntax */ | ||||
| //const _ = require('lodash');
 | ||||
| 
 | ||||
| require('../bootstrap'); | ||||
| 
 | ||||
| /* Must be declare in every child view*/ | ||||
| declare var i18n; | ||||
| 
 | ||||
| import Vue from 'vue'; | ||||
| 
 | ||||
| Vue.component('client-show', require('../components/client/ClientShow.vue')); | ||||
| 
 | ||||
|   | ||||
| window.onload = function () { | ||||
| 
 | ||||
|     const app = new Vue({ | ||||
|         el: '#client_show' | ||||
|     }); | ||||
| 
 | ||||
| } | ||||
| @ -1,52 +0,0 @@ | ||||
| <template> | ||||
| 
 | ||||
| 	<div class="dropdown"> | ||||
| 		<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||||
| 		{{ trans('texts.select') }} | ||||
| 		</button> | ||||
| 			<div class="dropdown-menu" aria-labelledby="dropdownMenu"> | ||||
| 				<a class="dropdown-item" :href="action.url" v-for="action in rowData.actions">{{ action.name }}</a> | ||||
|         <div class="dropdown-divider"></div> | ||||
|         <a class="dropdown-item" href="#" @click="itemAction('archive', rowData, rowIndex)" v-if="rowData.deleted_at == null">{{ trans('texts.archive') }}</a> | ||||
|         <a class="dropdown-item" href="#" @click="itemAction('restore', rowData, rowIndex)" v-if="rowData.is_deleted == 1 || rowData.deleted_at != null">{{ trans('texts.restore') }}</a> | ||||
|         <a class="dropdown-item" href="#" @click="itemAction('delete', rowData, rowIndex)" v-if="rowData.is_deleted == 0">{{ trans('texts.delete') }}</a> | ||||
| 			</div> | ||||
| 	</div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|   export default { | ||||
|     props: { | ||||
|       rowData: { | ||||
|         type: Object, | ||||
|         required: true | ||||
|       }, | ||||
|       rowIndex: { | ||||
|         type: Number | ||||
|       } | ||||
|     }, | ||||
|     methods: { | ||||
|       itemAction (action, data, index) { | ||||
| 
 | ||||
|         this.$events.fire('single-action', {'action': action, 'ids': [data.id]}) | ||||
|        | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| 	.custom-actions button.ui.button { | ||||
| 	  padding: 8px 8px; | ||||
| 	} | ||||
| 	.custom-actions button.ui.button > i.icon { | ||||
| 	  margin: auto !important; | ||||
| 	} | ||||
|   .dropdown-item { | ||||
|     outline:0px;  | ||||
|     border:0px;  | ||||
|     font-weight: bold; | ||||
|   } | ||||
| 
 | ||||
| </style> | ||||
| @ -1,180 +0,0 @@ | ||||
| <template> | ||||
| 	<div> | ||||
| 		<ul class="nav nav-tabs" role="tablist"> | ||||
| 			<li class="nav-item"> | ||||
| 				<a class="nav-link active" data-toggle="tab" href="#billing" role="tab" aria-controls="billing">{{ trans('texts.billing_address') }}</a> | ||||
| 			</li> | ||||
| 			<li class="nav-item"> | ||||
| 				<a class="nav-link" data-toggle="tab" href="#shipping" role="tab" aria-controls="shipping">{{ trans('texts.shipping_address') }}</a> | ||||
| 			</li> | ||||
| 		</ul> | ||||
| 		<div class="tab-content"> | ||||
| 			<div class="tab-pane active" id="billing" role="tabpanel"> | ||||
| 				<button type="button" class="btn btn-sm btn-light" v-on:click="$emit('copy', 'copy_shipping')"> {{ trans('texts.copy_shipping') }}</button> | ||||
| 				<div class="card-body"> | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address1') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.address1')" v-model="client.address1" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('address1')" class="text-danger" v-text="client.errors.get('address1')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address2') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text":placeholder="trans('texts.address2')" v-model="client.address2" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('address2')" class="text-danger" v-text="client.errors.get('address2')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.city') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text":placeholder="trans('texts.city')" v-model="client.city" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('city')" class="text-danger" v-text="client.errors.get('city')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.state') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.state')" v-model="client.state" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('state')" class="text-danger" v-text="client.errors.get('state')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.postal_code') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.postal_code')" v-model="client.postal_code" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('postal_code')" class="text-danger" v-text="client.errors.get('postal_code')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.country') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 			            	<multiselect v-model="billingCountry" :options="options" :placeholder="trans('texts.country')" label="name" track-by="id" @input="onChangeBilling"></multiselect> | ||||
|                  			<div v-if="client.errors.has('country_id')" class="text-danger" v-text="client.errors.get('country_id')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 				</div>	 | ||||
| 			</div> | ||||
| 			<div class="tab-pane" id="shipping" role="tabpanel"> | ||||
| 				<button type="button" class="btn btn-sm btn-light" v-on:click="$emit('copy',' copy_billing')"> {{ trans('texts.copy_billing') }}</button> | ||||
| 				<div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address1') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.address1')" v-model="client.shipping_address1" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('shipping_address1')" class="text-danger" v-text="client.errors.get('shipping_address1')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address2') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.address2')" v-model="client.shipping_address2" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('shipping_address2')" class="text-danger" v-text="client.errors.get('shipping_address2')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.city') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.city')" v-model="client.shipping_city" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('shipping_city')" class="text-danger" v-text="client.errors.get('shipping_city')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.state') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.state')" v-model="client.shipping_state" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('shipping_state')" class="text-danger" v-text="client.errors.get('shipping_state')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.postal_code') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 				            <input type="text" :placeholder="trans('texts.postal_code')" v-model="client.shipping_postal_code" class="form-control"> | ||||
|                  			<div v-if="client.errors.has('shipping_postal_code')" class="text-danger" v-text="client.errors.get('shipping_postal_code')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 				    <div class="form-group row"> | ||||
| 				        <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.country') }}</label> | ||||
| 				        <div class="col-sm-9"> | ||||
| 			            	<multiselect v-model="shippingCountry" :options="options" :placeholder="trans('texts.country')" label="name" track-by="id" @input="onChangeShipping"></multiselect> | ||||
|              				<div v-if="client.errors.has('shipping_country_id')" class="text-danger" v-text="client.errors.get('shipping_country_id')"></div> | ||||
| 				        </div> | ||||
| 				    </div> | ||||
| 				</div>	 | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div>	 | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| 
 | ||||
| 	import Multiselect from 'vue-multiselect' | ||||
| 
 | ||||
| 	export default { | ||||
| 		components: { | ||||
| 		    Multiselect | ||||
| 		  }, | ||||
|         props: ['client', 'countries'], | ||||
|         mounted() { | ||||
|         }, | ||||
|         data () { | ||||
| 		    return { | ||||
| 		      options: Object.keys(this.countries).map(i => this.countries[i]), | ||||
| 		      countryArray: Object.keys(this.countries).map(i => this.countries[i]) | ||||
| 		    } | ||||
| 		  }, | ||||
| 		computed: { | ||||
| 	        shippingCountry: { | ||||
| 	            set: function() { | ||||
| 	             | ||||
| 	              //  return this.client.shipping_country_id | ||||
| 	             | ||||
| 	            }, | ||||
| 	            get: function(value) { | ||||
| 
 | ||||
| 
 | ||||
| 	            	return this.countryArray.filter(obj => { | ||||
| 					  return obj.id === this.client.shipping_country_id | ||||
| 					}) | ||||
| 
 | ||||
| 	            } | ||||
| 	        }, | ||||
| 	        billingCountry: { | ||||
| 	            set: function() { | ||||
| 	             | ||||
| 	                return this.client.country_id | ||||
| 	             | ||||
| 	            }, | ||||
| 	            get: function(value) { | ||||
| 
 | ||||
| 	            	return this.countryArray.filter(obj => { | ||||
| 					  return obj.id === this.client.country_id | ||||
| 					}) | ||||
| 
 | ||||
| 	            } | ||||
| 	        } | ||||
| 
 | ||||
| 	    }, | ||||
| 	  	methods: { | ||||
| 	  		onChangeShipping(value) { | ||||
| 	  			this.client.shipping_country_id = value.id | ||||
| 	  		}, | ||||
| 	  		onChangeBilling(value) { | ||||
| 	  			this.client.country_id = value.id | ||||
| 	  		} | ||||
| 	  	} | ||||
| 	} | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style src="vue-multiselect/dist/vue-multiselect.min.css"></style> | ||||
| @ -1,76 +0,0 @@ | ||||
| <template> | ||||
|     <div class="card-body"> | ||||
|         <div class="form-group row"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.first_name') }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input ref="first_name" name="first_name" type="text" :placeholder="trans('texts.first_name')" v-model="contact.first_name" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.first_name')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.first_name')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.last_name') }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="text" :placeholder="trans('texts.last_name')" v-model="contact.last_name" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.last_name')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.last_name')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.email') }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="email" :placeholder="trans('texts.email')" v-model="contact.email" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.email')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.email')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.phone') }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="text" :placeholder="trans('texts.phone')" v-model="contact.phone" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.phone')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.phone')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row" v-if="!!company.settings.custom_client_contact_label1"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ company.settings.custom_client_contact_label1 }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="text" :placeholder="trans('texts.custom_value1')" v-model="contact.custom_value1" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.custom_value1')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.custom_value1')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row" v-if="!!company.settings.custom_client_contact_label2"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ company.settings.custom_client_contact_label2 }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="text" :placeholder="trans('texts.custom_value1')" v-model="contact.custom_value2" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.custom_value2')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.custom_value2')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row" v-if="!!company.settings.custom_client_contact_label3"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ company.settings.custom_client_contact_label3 }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="text" :placeholder="trans('texts.custom_value1')" v-model="contact.custom_value3" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.custom_value3')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.custom_value3')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="form-group row" v-if="!!company.settings.custom_client_contact_label4"> | ||||
|             <label for="name" class="col-sm-3 col-form-label text-right">{{ company.settings.custom_client_contact_label4 }}</label> | ||||
|             <div class="col-sm-9"> | ||||
|                 <input type="text" :placeholder="trans('texts.custom_value1')" v-model="contact.custom_value4" class="form-control"> | ||||
|                 <div v-if="form.errors.has('contacts.'+error_index+'.custom_value4')" class="text-danger" v-text="form.errors.get('contacts.'+error_index+'.custom_value4')"></div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="float-right"> | ||||
|             <button type="button" class="btn btn-danger" v-on:click="$emit('remove',contact)"> {{ trans('texts.remove_contact') }}</button> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|     export default { | ||||
|         props: ['company', 'contact', 'form', 'error_index'] | ||||
|     } | ||||
| </script> | ||||
| @ -1,72 +0,0 @@ | ||||
| <template> | ||||
| <div class="card-body"> | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.client_name') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" :placeholder="trans('texts.client_name')" v-model="client.name" class="form-control"> | ||||
|                  <div v-if="client.errors.has('name')" class="text-danger" v-text="client.errors.get('name')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.id_number') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="id_number" :placeholder="trans('texts.id_number')" v-model="client.id_number" class="form-control" id="id_number"> | ||||
|             <div v-if="client.errors.has('id_number')" class="text-danger" v-text="client.errors.get('id_number')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.vat_number') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="vat_number" :placeholder="trans('texts.vat_number')" v-model="client.vat_number" class="form-control" id="vat_number"> | ||||
|             <div v-if="client.errors.has('vat_number')" class="text-danger" v-text="client.errors.get('vat_number')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.website') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="website" :placeholder="trans('texts.website')" v-model="client.website" class="form-control" id="websites"> | ||||
|             <div v-if="client.errors.has('website')" class="text-danger" v-text="client.errors.get('website')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row" v-if="company.custom_client_label1 && company.custom_client_label1.length >= 1"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_label1 }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="custom_value1" :placeholder="trans('texts.custom_value1')" v-model="client.custom_value1" class="form-control" id="custom_value1"> | ||||
|             <div v-if="client.errors.has('custom_value1')" class="text-danger" v-text="client.errors.get('custom_value1')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row" v-if="company.custom_client_label2 && company.custom_client_label2.length >= 1"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_label2 }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="custom_value2" :placeholder="trans('texts.custom_value1')" v-model="client.custom_value2" class="form-control" id="custom_value2"> | ||||
|             <div v-if="client.errors.has('custom_value2')" class="text-danger" v-text="client.errors.get('custom_value2')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row" v-if="company.custom_client_label3 && company.custom_client_label3.length >= 1"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_label3 }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="custom_value3" :placeholder="trans('texts.custom_value1')" v-model="client.custom_value3" class="form-control" id="custom_value3"> | ||||
|             <div v-if="client.errors.has('custom_value3')" class="text-danger" v-text="client.errors.get('custom_value2')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row" v-if="company.custom_client_label4 && company.custom_client_label4.length >= 1"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_label2 }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="custom_value4" :placeholder="trans('texts.custom_value1')" v-model="client.custom_value4" class="form-control" id="custom_value4"> | ||||
|             <div v-if="client.errors.has('custom_value4')" class="text-danger" v-text="client.errors.get('custom_value4')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </template> | ||||
| <script> | ||||
|     export default { | ||||
|         props: ['client','errors', 'company'] | ||||
|     } | ||||
| </script>	 | ||||
| @ -1,118 +0,0 @@ | ||||
| <template> | ||||
|     <form @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)"> | ||||
|          | ||||
|             <div class="row"> | ||||
|                 <!-- Client Details and Address Column --> | ||||
|                 <div class="col-md-6"> | ||||
|                     <div class="card"> | ||||
|                         <div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div> | ||||
|                             <client-edit :client="form" :company="company"></client-edit> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div class="card"> | ||||
|                         <div class="card-header bg-primary2">{{ trans('texts.address') }}</div> | ||||
|                             <client-address v-bind:client="form" @copy="copy" :countries="countries"></client-address> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <!-- End Client Details and Address Column --> | ||||
| 
 | ||||
|                 <!-- Contact Details Column --> | ||||
|                 <div class="col-md-6"> | ||||
|                     <div class="card"> | ||||
|                         <div class="card-header bg-primary2">{{ trans('texts.contact_information') }} | ||||
|                             <span class="float-right"> | ||||
|                                 <button type="button" class="btn btn-primary btn-sm" @click="add"><i class="fa fa-plus-circle"></i> {{ trans('texts.add_contact') }}</button> | ||||
|                             </span> | ||||
|                         </div> | ||||
|                             <contact-edit   v-for="(contact, key, index) in form.contacts"  | ||||
|                                             :contact="contact"  | ||||
|                                             :form="form" | ||||
|                                             :key="contact.id" | ||||
|                                             :error_index="key" | ||||
|                                             :company="company" | ||||
|                                             @remove="remove"></contact-edit> | ||||
|                     </div>     | ||||
|                 </div>      | ||||
|                 <!-- End Contact Details Column -->  | ||||
|             </div>      | ||||
| 
 | ||||
|             <div class="row">  | ||||
| 
 | ||||
|                 <div class="col-md-12 text-center"> | ||||
| 
 | ||||
|                     <button class="btn btn-lg btn-success" type="button" @click="onSubmit"><i class="fa fa-save"></i> {{ trans('texts.save') }}</button> | ||||
| 
 | ||||
|                 </div> | ||||
| 
 | ||||
|             </div>    | ||||
| 
 | ||||
|     </form> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
| import Form from '../../utils/form'; | ||||
| import Client from '../../models/client-model'; | ||||
| import Vue from 'vue' | ||||
| 
 | ||||
| export default { | ||||
|     data: function () { | ||||
|         return { | ||||
|             form: new Form(<Client>this.clientdata) | ||||
|         } | ||||
|     }, | ||||
|     props: ['hashed_id', 'clientdata', 'countries', 'company'], | ||||
|     beforeMount: function () { | ||||
|     }, | ||||
|     methods:{ | ||||
|         remove(this:any, contact:any){ | ||||
|             let index = this.form.contacts.indexOf(contact); | ||||
|             this.form.contacts.splice(index, 1); | ||||
|         }, | ||||
|         add(this: any){ | ||||
|             this.form.contacts.push({first_name: '', last_name: '', email: '', phone: '', id: 0}); | ||||
|             window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight); | ||||
|             this.$nextTick(() => { | ||||
|                      let index = this.form.contacts.length - 1; | ||||
|                      //this.$refs.first_name[index].$el.focus(); | ||||
|                      //this.$refs.first_name[index].focus(); | ||||
|                   }); | ||||
|         }, | ||||
|         onSubmit() { | ||||
|             this.form.put('/clients/' + this.hashed_id) | ||||
|                 .then(response => this.$root.$refs.toastr.s( Vue.prototype.trans('texts.updated_client') )) | ||||
|                 .catch(error => { | ||||
| 
 | ||||
|                     this.$root.$refs.toastr.e("Error saving client"); | ||||
| 
 | ||||
|                 }); | ||||
|         }, | ||||
|         copy(type: any) { | ||||
|             if(type.includes('copy_billing')){ | ||||
|                 this.form.shipping_address1 = this.form.address1;  | ||||
|                 this.form.shipping_address2 = this.form.address2;  | ||||
|                 this.form.shipping_city = this.form.city;  | ||||
|                 this.form.shipping_state = this.form.state;  | ||||
|                 this.form.shipping_postal_code = this.form.postal_code;  | ||||
|                 this.form.shipping_country_id = this.form.country_id;  | ||||
|                 }else { | ||||
|                 this.form.address1 = this.form.shipping_address1;  | ||||
|                 this.form.address2 = this.form.shipping_address2;  | ||||
|                 this.form.city = this.form.shipping_city;  | ||||
|                 this.form.state = this.form.shipping_state;  | ||||
|                 this.form.postal_code = this.form.shipping_postal_code;  | ||||
|                 this.form.country_id = this.form.shipping_country_id;  | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     created:function() { | ||||
|          | ||||
|          | ||||
|     }, | ||||
|     updated:function() { | ||||
|          | ||||
|     } | ||||
|      | ||||
| } | ||||
| </script> | ||||
| @ -1,23 +0,0 @@ | ||||
| <template> | ||||
| 	<div>		       | ||||
| 		<vuetable-filter-bar></vuetable-filter-bar> | ||||
| 	</div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
| import Vue from 'vue' | ||||
| 
 | ||||
| export default { | ||||
| 
 | ||||
| mounted() { | ||||
| 	 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style type="text/css"> | ||||
| 	 | ||||
| </style> | ||||
| @ -1,181 +0,0 @@ | ||||
| <template> | ||||
| 
 | ||||
| 	<div> | ||||
| 
 | ||||
|       <vuetable ref="vuetable" | ||||
| 	      api-url="/clients" | ||||
| 	      :fields="fields" | ||||
|       	:per-page="perPage" | ||||
|       	:sort-order="sortOrder" | ||||
|       	:append-params="moreParams" | ||||
|         :css="css.table" | ||||
|   		  pagination-path="" | ||||
|         @vuetable:checkbox-toggled="toggledCheckBox()" | ||||
|         @vuetable:checkbox-toggled-all="toggledCheckBox()" | ||||
|       	@vuetable:pagination-data="onPaginationData"></vuetable> | ||||
| 
 | ||||
|   		<div class="vuetable-pagination ui basic segment grid"> | ||||
| 
 | ||||
|         	<vuetable-pagination-info ref="paginationInfo"></vuetable-pagination-info> | ||||
| 
 | ||||
|         	<vuetable-pagination ref="pagination" | ||||
|         	:css="css.pagination" | ||||
|         	@vuetable-pagination:change-page="onChangePage"></vuetable-pagination> | ||||
| 
 | ||||
|     	</div> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
| import Vuetable from 'vuetable-2/src/components/Vuetable.vue' | ||||
| import VuetablePagination from 'vuetable-2/src/components/VuetablePagination.vue' | ||||
| import VuetablePaginationInfo from 'vuetable-2/src/components/VuetablePaginationInfo.vue' | ||||
| import Vue from 'vue' | ||||
| import VueEvents from 'vue-events' | ||||
| import VuetableCss from '../util/VuetableCss' | ||||
| import axios from 'axios' | ||||
| 
 | ||||
| Vue.use(VueEvents) | ||||
| 
 | ||||
| declare var bulk_count : number; | ||||
| 
 | ||||
| export default { | ||||
| 
 | ||||
| 	components: { | ||||
|         	Vuetable, | ||||
| 	      	VuetablePagination, | ||||
| 	      	VuetablePaginationInfo | ||||
| 	}, | ||||
|   data: function () { | ||||
|       return { | ||||
|           css: VuetableCss, | ||||
|           perPage: this.datatable.per_page, | ||||
|           sortOrder: this.datatable.sort_order, | ||||
|           moreParams: this.$store.getters['client_list/getQueryStringObject'], | ||||
|           fields: this.datatable.fields | ||||
|       } | ||||
|   }, | ||||
|   props: ['datatable'], | ||||
|   mounted() { | ||||
| 
 | ||||
|     this.$events.$on('filter-set', eventData => this.onFilterSet()) | ||||
|     this.$events.$on('bulk-action', eventData => this.bulkAction(eventData)) | ||||
|     this.$events.$on('multi-select', eventData => this.multiSelect(eventData)) | ||||
|     this.$events.$on('single-action', eventData => this.singleAction(eventData)) | ||||
|     this.$events.$on('perpage_action', eventData => this.onPerPageUpdate(eventData)) | ||||
| 
 | ||||
|   }, | ||||
|   methods: { | ||||
| 
 | ||||
|     onPaginationData (paginationData : any) { | ||||
| 
 | ||||
|       this.$refs.pagination.setPaginationData(paginationData) | ||||
|       this.$refs.paginationInfo.setPaginationData(paginationData)  | ||||
| 
 | ||||
|     }, | ||||
|     onChangePage (page : any) { | ||||
| 
 | ||||
| 		this.$refs.vuetable.changePage(page) | ||||
| 
 | ||||
|     }, | ||||
| 	  onFilterSet () { | ||||
| 
 | ||||
|       this.moreParams = this.$store.getters['client_list/getQueryStringObject'] | ||||
| 			Vue.nextTick( () => this.$refs.vuetable.refresh()) | ||||
| 
 | ||||
| 	  }, | ||||
|     onPerPageUpdate(per_page){ | ||||
| 
 | ||||
|       this.perPage = Number(per_page) | ||||
|       Vue.nextTick( () => this.$refs.vuetable.refresh()) | ||||
| 
 | ||||
|     }, | ||||
|     bulkAction (action){ | ||||
| 
 | ||||
|       var dataObj = { | ||||
|         'action' : action, | ||||
|         'ids' : this.$refs.vuetable.selectedTo | ||||
|       } | ||||
| 
 | ||||
|       this.postBulkAction(dataObj) | ||||
| 
 | ||||
|     }, | ||||
|     singleAction(dataObj) { | ||||
| 
 | ||||
|       this.postBulkAction(dataObj) | ||||
| 
 | ||||
|     }, | ||||
|     postBulkAction(dataObj) { | ||||
| 
 | ||||
|       axios.post('/clients/bulk', dataObj) | ||||
|       .then((response) => { | ||||
|         this.$root.$refs.toastr.s( Vue.prototype.trans('texts.'+dataObj.action+'d_client') ) | ||||
|         this.$store.commit('client_list/setBulkCount', 0) | ||||
|         this.$refs.vuetable.selectedTo = [] | ||||
|         this.$refs.vuetable.refresh() | ||||
| //        console.dir(response) | ||||
|       }) | ||||
|       .catch(function (error) { | ||||
|         this.$root.$refs.toastr.e( "A error occurred" ) | ||||
|       }); | ||||
| 
 | ||||
|     }, | ||||
|     toggledCheckBox(){ | ||||
|       this.$store.commit('client_list/setBulkCount', this.$refs.vuetable.selectedTo.length) | ||||
|     }, | ||||
|     multiSelect(value) | ||||
|     { | ||||
|       this.moreParams = this.$store.getters['client_list/getQueryStringObject'] | ||||
|       Vue.nextTick( () => this.$refs.vuetable.refresh()) | ||||
|     } | ||||
| 
 | ||||
|  } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style type="text/css"> | ||||
| 
 | ||||
|   .pagination { | ||||
|     margin: 0; | ||||
|     float: right; | ||||
|   } | ||||
|   .pagination a.page { | ||||
|     border: 1px solid lightgray; | ||||
|     border-radius: 3px; | ||||
|     padding: 5px 10px; | ||||
|     margin-right: 2px; | ||||
|   } | ||||
|   .pagination a.page.active { | ||||
|     color: white; | ||||
|     background-color: #337ab7; | ||||
|     border: 1px solid lightgray; | ||||
|     border-radius: 3px; | ||||
|     padding: 5px 10px; | ||||
|     margin-right: 2px; | ||||
|   } | ||||
|   .pagination a.btn-nav { | ||||
|     border: 1px solid lightgray; | ||||
|     border-radius: 3px; | ||||
|     padding: 5px 7px; | ||||
|     margin-right: 2px; | ||||
|   } | ||||
|   .pagination a.btn-nav.disabled { | ||||
|     color: lightgray; | ||||
|     border: 1px solid lightgray; | ||||
|     border-radius: 3px; | ||||
|     padding: 5px 7px; | ||||
|     margin-right: 2px; | ||||
|     cursor: not-allowed; | ||||
|   } | ||||
|   .pagination-info { | ||||
|     float: left; | ||||
|   } | ||||
|   th { | ||||
|     background: #777777; | ||||
|     color: #fff; | ||||
|   } | ||||
| 
 | ||||
| </style> | ||||
| @ -1,421 +0,0 @@ | ||||
| <template> | ||||
| 
 | ||||
| <div class="row" style="background:#fff; padding:20px;"> | ||||
| 
 | ||||
| 	<div class="col-2" style="border: 0px; border-style:solid;"> | ||||
| 
 | ||||
| 		<affix class="menu sidebar-menu" relative-element-selector="#example-content" :offset="{ top: 50, bottom:100 }" :scroll-affix="false" style="width: 200px"> | ||||
|           <div class="menu-label"> | ||||
|             <h3 style="color:#5d5d5d;">{{ trans('texts.settings') }}</h3> | ||||
|           </div> | ||||
|           <scrollactive  | ||||
|           	class="menu-list"  | ||||
|           	active-class="is-active" | ||||
|           	:offset="50" | ||||
| 		  	:duration="800" | ||||
|           	:exact="true" | ||||
|           	> | ||||
|           	<ul class="list-inline justify-content-left"> | ||||
|                 <li class="menu-li"><a href="#intro" class="scrollactive-item" >{{trans('t.client_settings')}}</a></li> | ||||
|                 <li class="menu-li"><a href="#standard-affix" class="scrollactive-item" >{{trans('texts.messages')}}</a></li> | ||||
|                 <li class="menu-li"><a href="#scroll-affix" class="scrollactive-item" >{{trans('texts.classify')}}</a></li> | ||||
|         	</ul> | ||||
|           </scrollactive> | ||||
|         </affix> | ||||
| 
 | ||||
| 	</div> | ||||
| 
 | ||||
| 	<div class="col-10"> | ||||
| 
 | ||||
| 		<div id="example-content"> | ||||
| 
 | ||||
|           <section id="intro"> | ||||
|                 <div class="card"> | ||||
|                     <div class="card-header bg-primary2">{{ trans('t.client_settings') }}</div> | ||||
|                         <div class="card-body px-3"> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 text-left"> | ||||
| 						        	<div>{{ trans('texts.currency') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{ trans('help.client_currency') }}</div> | ||||
| 						        </label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <multiselect v-model="settings.currency_id" :options="options_currency" label="name" track-by="id" :allow-empty="true" @select="currencySettingChange()"></multiselect> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form d-flex justify-content-center"> | ||||
| 								<div class="form-check form-check-inline"> | ||||
| 									<input class="form-check-input" id="inline-radio1" type="radio" name="symbol" value="1" v-model="settings.show_currency_symbol" @click="setCurrencySymbol()"> | ||||
| 									<label class="form-check-label" for="show_currency_symbol-radio1">{{ trans('texts.currency_symbol') }}: {{ currency_symbol_example }}</label> | ||||
| 								</div> | ||||
| 								<div class="form-check form-check-inline"> | ||||
| 									<input class="form-check-input" id="inline-radio2" type="radio" name="code" value="1" v-model="settings.show_currency_code" @click="setCurrencyCode()"> | ||||
| 									<label class="form-check-label" for="show_currency_code">{{ trans('texts.currency_code') }}: {{ currency_code_example }}</label> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="language" class="col-sm-5 text-left"> | ||||
| 						        	<div>{{ trans('texts.language') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{ trans('help.client_language')}}</div> | ||||
| 						        </label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <multiselect v-model="settings.language_id" :options="options_language" :placeholder="placeHolderLanguage()" label="name" track-by="id" :allow-empty="true"></multiselect> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="payment_terms" class="col-sm-5 text-left"> | ||||
| 						        	<div>{{ trans('texts.payment_terms') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{ trans('help.client_payment_terms')}}</div> | ||||
| 						        </label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <multiselect v-model="settings.payment_terms" :options="options_payment_term" :placeholder="placeHolderPaymentTerm()" label="name" track-by="num_days" :allow-empty="true"></multiselect> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						     | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left"> | ||||
| 						        <div>{{ trans('texts.task_rate') }}</div> | ||||
| 								<div style="margin-top:1px; line-height:1.4; color:#939393;">{{ trans('texts.task_rate_help')}}</div> | ||||
| 						    	</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <input type="text" :placeholder="trans('texts.task_rate')" class="form-control" v-model="settings.task_rate"> | ||||
| 						                 <div v-if="" class="text-danger" v-text=""></div> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left">{{ trans('texts.send_client_reminders') }}</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <label class="switch switch-label switch-pill switch-info"> | ||||
| 									<input class="switch-input" type="checkbox" checked="" v-model="settings.send_reminders"> | ||||
| 									<span class="switch-slider" data-checked="✓" data-unchecked="✕"></span> | ||||
| 									</label> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left">{{ trans('texts.show_tasks_in_portal') }}</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <label class="switch switch-label switch-pill switch-info"> | ||||
| 									<input class="switch-input" type="checkbox" checked="" v-model="settings.show_tasks_in_portal"> | ||||
| 									<span class="switch-slider" data-checked="✓" data-unchecked="✕"></span> | ||||
| 									</label> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 
 | ||||
| 						</div> | ||||
| 					 | ||||
|                 </div> | ||||
|           </section> | ||||
| 
 | ||||
|           <section id="standard-affix"> | ||||
|             <div class="card"> | ||||
|                     <div class="card-header bg-primary2">{{ trans('texts.messages') }}</div> | ||||
|                         <div class="card-body"> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left"> | ||||
| 						        	<div>{{ trans('texts.dashboard') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{ trans('help.client_dashboard')}}</div> | ||||
| 						    	</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <textarea class="form-control" id="textarea-input" label="dashboard" v-model="settings.custom_message_dashboard"rows="9" :placeholder="placeHolderMessage('custom_message_dashboard')"></textarea> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left"> | ||||
| 						        	<div>{{ trans('texts.unpaid_invoice') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{ trans('help.client_unpaid_invoice')}}</div> | ||||
| 								</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <textarea class="form-control" id="textarea-input" label="unpaid_invoice" v-model="settings.custom_message_unpaid_invoice"rows="9" :placeholder="placeHolderMessage('custom_message_unpaid_invoice')"></textarea> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left"> | ||||
| 						        	<div>{{ trans('texts.paid_invoice') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{trans('help.client_paid_invoice')}}</div> | ||||
| 								</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <textarea class="form-control" id="textarea-input" label="paid_invoice"  v-model="settings.custom_message_paid_invoice" rows="9" :placeholder="placeHolderMessage('custom_message_paid_invoice')"></textarea> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 								<label class="col-sm-5 col-form-label text-left" for="unapproved_quote"> | ||||
| 									<div>{{ trans('texts.unapproved_quote') }}</div> | ||||
| 									<div style="margin-top:1px; line-height:1.4; color:#939393;">{{trans('help.client_unapproved_quote')}}</div> | ||||
| 								</label> | ||||
| 								<div class="col-md-7"> | ||||
| 									<textarea class="form-control" id="textarea-input" label="unapproved_quote" v-model="settings.custom_message_unapproved_quote" rows="9" :placeholder="placeHolderMessage('custom_message_unapproved_quote')"></textarea> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 
 | ||||
| 						</div> | ||||
| 					 | ||||
|                 </div> | ||||
|           </section> | ||||
| 
 | ||||
|           <section id="scroll-affix"> | ||||
|             <div class="card"> | ||||
|                     <div class="card-header bg-primary2">{{ trans('texts.classify') }}</div> | ||||
|                         <div class="card-body"> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left">{{ trans('texts.industry') }}</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <multiselect :options="options_industry" :placeholder="placeHolderIndustry()" label="name" track-by="id" v-model="settings.industry_id"></multiselect> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 						    <div class="form-group row client_form"> | ||||
| 						        <label for="name" class="col-sm-5 col-form-label text-left">{{ trans('texts.size_id') }}</label> | ||||
| 						        <div class="col-sm-7"> | ||||
| 						            <multiselect :options="options_size" :placeholder="placeHolderSize()" label="name" track-by="id" v-model="settings.size_id"></multiselect> | ||||
| 						        </div> | ||||
| 						    </div> | ||||
| 
 | ||||
| 						</div> | ||||
| 					 | ||||
|                 </div> | ||||
|           </section> | ||||
| 
 | ||||
|         </div> | ||||
|         		 | ||||
| 	</div> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
| import Vue from 'vue' | ||||
| import { Affix } from 'vue-affix' | ||||
| var VueScrollactive = require('vue-scrollactive') | ||||
| import NumberFormat from '../../utils/number-format' | ||||
| import Multiselect from 'vue-multiselect' | ||||
| import ClientSettings from '../../utils/client-settings' | ||||
| 
 | ||||
| Vue.use(VueScrollactive); | ||||
| 
 | ||||
| export default { | ||||
| 	components: { | ||||
| 		Affix, | ||||
| 	    Multiselect, | ||||
| 	}, | ||||
| 	data () { | ||||
| 	    return { | ||||
| 			options_currency: Object.keys(this.currencies).map(i => this.currencies[i]), | ||||
| 			options_language: Object.keys(this.languages).map(i => this.languages[i]), | ||||
| 			options_payment_term: Object.keys(this.payment_terms).map(i => this.payment_terms[i]), | ||||
| 			options_industry: Object.keys(this.industries).map(i => this.industries[i]), | ||||
| 			options_size: this.sizes, | ||||
| 			settings: this.client_settings | ||||
| 	    } | ||||
| 	  }, | ||||
|     props: ['client_settings', 'currencies', 'languages', 'payment_terms', 'industries', 'sizes', 'company'], | ||||
|     mounted() { | ||||
| 
 | ||||
|     	//console.dir(this.settings) | ||||
| 		this.updateCurrencyExample() | ||||
| 	}, | ||||
|     computed: { | ||||
| 		currency_code_example: { | ||||
| 			get: function() { | ||||
| 				return this.updateCurrencyExample(false) | ||||
| 			}, | ||||
| 			set: function() { | ||||
| 			} | ||||
| 		}, | ||||
| 		currency_symbol_example: { | ||||
| 			get: function() { | ||||
| 				return this.updateCurrencyExample(true) | ||||
| 			}, | ||||
| 			set: function() { | ||||
| 			} | ||||
| 		} | ||||
|     }, | ||||
|     methods: { | ||||
| 		setObjectValue(key, value){ | ||||
| 
 | ||||
| 			if(value === null) | ||||
| 				this.settings[key] = null | ||||
| 			else | ||||
| 				this.settings[key] = value | ||||
| 
 | ||||
| 		}, | ||||
| 		placeHolderCurrency(){ | ||||
| 
 | ||||
| 			var currency = this.options_currency.find(obj => { | ||||
| 				return obj.id == this.company.settings_object.currency_id | ||||
| 			}) | ||||
| 
 | ||||
| 			if(currency) | ||||
| 				return currency.name | ||||
| 			else | ||||
| 				return  Vue.prototype.trans('texts.currency_id') 	 | ||||
| 
 | ||||
| 		},		 | ||||
| 		placeHolderPaymentTerm(){ | ||||
| 
 | ||||
| 			var payment_terms = this.payment_terms.find(obj => { | ||||
| 			  return obj.num_days == this.company.settings_object.payment_terms | ||||
| 			}) | ||||
| 
 | ||||
| 			if(payment_terms) | ||||
| 				return payment_terms.name | ||||
| 			else | ||||
| 				return  Vue.prototype.trans('texts.payment_terms') 	 | ||||
| 
 | ||||
| 		}, | ||||
| 		placeHolderIndustry(){ | ||||
| 
 | ||||
| 			return  Vue.prototype.trans('texts.industry_id')  | ||||
| 
 | ||||
| 		}, | ||||
| 		placeHolderSize(){ | ||||
| 
 | ||||
| 			return  Vue.prototype.trans('texts.size_id') 	 | ||||
| 
 | ||||
| 		}, | ||||
| 		placeHolderLanguage(){ | ||||
| 
 | ||||
| 			var language = this.languages.find(obj => { | ||||
| 			  return obj.id == this.company.settings_object.language_id | ||||
| 			}) | ||||
| 
 | ||||
| 			if(language) | ||||
| 				return language.name | ||||
| 			else | ||||
| 				return  Vue.prototype.trans('texts.language_id')  | ||||
| 
 | ||||
| 		}, | ||||
| 		placeHolderMessage(message_setting : string) { | ||||
| 
 | ||||
| 			if(this.company.settings_object[message_setting] && this.company.settings_object[message_setting].length >=1) { | ||||
| 
 | ||||
| 				return this.company.settings_object[message_setting] | ||||
| 				 | ||||
| 			} | ||||
| 
 | ||||
| 		}, | ||||
| 		setCurrencyCode() { | ||||
| 			this.settings.show_currency_symbol = false; | ||||
| 			this.settings.show_currency_code = true; | ||||
| 
 | ||||
| 			this.currencySettingChange() | ||||
| 
 | ||||
| 		}, | ||||
| 		setCurrencySymbol() { | ||||
| 
 | ||||
| 			this.settings.show_currency_symbol = true; | ||||
| 			this.settings.show_currency_code = false; | ||||
| 
 | ||||
| 			this.currencySettingChange() | ||||
| 
 | ||||
| 		}, | ||||
| 		updateCurrencyExample(currency_symbol) { | ||||
| 
 | ||||
| 			 | ||||
| 			var currency = this.options_currency.find(obj => { | ||||
| 				return obj.id == this.company.settings_object.currency_id | ||||
| 			}) | ||||
| 
 | ||||
| 			var language = this.languages.find(obj => { | ||||
| 			  return obj.id == this.company.settings_object.language_id | ||||
| 			}) | ||||
| 
 | ||||
| 
 | ||||
| 			if(this.settings_language_id) | ||||
| 				language = this.settings_language_id | ||||
| 
 | ||||
| 			if(this.settings_currency_id) | ||||
| 				currency = this.settings_currency_id | ||||
| 
 | ||||
| 			return new NumberFormat(1000, currency, currency_symbol, language).format() | ||||
| 		}, | ||||
| 		currencySettingChange() { | ||||
| 			this.currency_code_example = this.updateCurrencyExample(false) | ||||
| 			this.currency_symbol_example = this.updateCurrencyExample(true) | ||||
| 
 | ||||
| 			console.dir(this.currency_symbol_example) | ||||
| 			console.dir(this.currency_code_example) | ||||
| 		} | ||||
| 
 | ||||
| 	 | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <style> | ||||
| 
 | ||||
| #example-content { | ||||
| } | ||||
| 
 | ||||
| .client_form { | ||||
| 	border-bottom: 0px; | ||||
| 	border-bottom-style: solid; | ||||
|     border-bottom-color: #167090; | ||||
| } | ||||
| 
 | ||||
| .menu-li { | ||||
| 	list-style: none; | ||||
|   	padding-left:5px; | ||||
|   	width:200px; | ||||
|   	line-height:1.4; | ||||
|   	margin-top:10px; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| a.scrollactive-item.is-active  { | ||||
|   color: #027093; | ||||
|   font-family: helvetica; | ||||
|   text-decoration: none; | ||||
|   border-left-style: solid; | ||||
|   border-left-color: #027093; | ||||
|   padding-left:10px; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| a.scrollactive-item.is-active:hover { | ||||
|   text-decoration: none; | ||||
| 
 | ||||
|   color: #027093; | ||||
|   padding-left:10px; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| a.scrollactive-item.is-active:active { | ||||
|   color: #027093; | ||||
|   padding-left:10px; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .menu-list a { | ||||
|   color: #939393; | ||||
| 
 | ||||
|   font-family: helvetica; | ||||
|   text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| .menu-list a:hover { | ||||
|   text-decoration: none; | ||||
| 
 | ||||
|   color: #027093; | ||||
|   padding-left:5px; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| .menu-list a:active { | ||||
|   color: #027093; | ||||
|   text-decoration: none; | ||||
|       padding-left:5px; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| </style> | ||||
| @ -1,181 +0,0 @@ | ||||
| <template> | ||||
| 
 | ||||
| 	<div class="container-fluid"> | ||||
| 
 | ||||
| 		<div class="row"> | ||||
| 		    <div class="col" style="padding: 0px;"> | ||||
| 		     | ||||
| 			    <div class="float-right"> | ||||
| 
 | ||||
| 					<div class="btn-group ml-2"> | ||||
| 				      <button type="button" class="btn btn-lg btn-secondary" :disabled="editClientIsDisabled" v-for="link in this.meta.edit_client_route" @click="goToUrl(link.url)">{{ trans('texts.edit_client') }}</button> | ||||
| 				      <button type="button" class="btn btn-lg btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :disabled="editClientIsDisabled"> | ||||
| 				        <span class="sr-only">Toggle Dropdown</span> | ||||
| 				      </button> | ||||
| 				      <div class="dropdown-menu" x-placement="top-start" style="position: absolute; transform: translate3d(189px, -2px, 0px); top: 0px; left: 0px; will-change: transform;"> | ||||
| 							<a class="dropdown-item" href="#" @click="itemAction('archive', client, rowIndex)" v-if="client.deleted_at == null">{{ trans('texts.archive') }}</a> | ||||
| 							<a class="dropdown-item" href="#" @click="itemAction('restore', client, rowIndex)" v-if="client.is_deleted == 1 || client.deleted_at != null">{{ trans('texts.restore') }}</a> | ||||
| 							<a class="dropdown-item" href="#" @click="itemAction('delete', client, rowIndex)" v-if="client.is_deleted == 0">{{ trans('texts.delete') }}</a> | ||||
| 				        <div class="dropdown-divider"></div> | ||||
| 				        <a class="dropdown-item" @click="itemAction('purge', client, rowIndex)">{{ trans('texts.purge_client') }}</a> | ||||
| 				      </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 					<div class="btn-group ml-2"> | ||||
| 				      <button type="button" class="btn btn-lg btn-primary" :disabled="viewStatementIsDisabled" v-for="link in this.meta.view_statement_route" @click="goToUrl(link.url)">{{ trans('texts.view_statement') }}</button> | ||||
| 				      <button type="button" class="btn btn-lg btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" :disabled="viewStatementIsDisabled"> | ||||
| 				        <span class="sr-only">Toggle Dropdown</span> | ||||
| 				      </button> | ||||
| 				      <div class="dropdown-menu" x-placement="top-start" style="position: absolute; transform: translate3d(189px, -2px, 0px); top: 0px; left: 0px; will-change: transform;"> | ||||
| 				        <a class="dropdown-item" v-for="link in this.meta.view_statement_actions" :href="link.url">{{ link.name }}</a> | ||||
| 				      </div> | ||||
| 				    </div> | ||||
| 
 | ||||
| 			    </div> | ||||
| 
 | ||||
| 			</div> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<div class="card"> | ||||
| 			<div class="card-body"> | ||||
| 				<div class="row"> | ||||
| 
 | ||||
| 					<div class="col-sm"> | ||||
| 						<h3> {{ trans('texts.details') }} </h3> | ||||
| 						<p v-if="client.id_number && client.id_number.length >= 1"><b>{{ trans('texts.id_number') }}:</b> {{ client.id_number }}</p> | ||||
| 						<p v-if="client.vat_number && client.vat_number.length >= 1"><b>{{ trans('texts.vat_number') }}:</b> {{ client.vat_number }}</p> | ||||
| 						<p v-if="client.custom_value1 && client.custom_value1.length >= 1"><b>{{ company.custom_client_label1 }}:</b> {{ client.custom_value1 }}</p> | ||||
| 						<p v-if="client.custom_value2 && client.custom_value2.length >= 1"><b>{{ company.custom_client_label2 }}:</b> {{ client.custom_value2 }}</p> | ||||
| 						<p v-if="client.custom_value3 && client.custom_value3.length >= 1"><b>{{ company.custom_client_label3 }}:</b> {{ client.custom_value3 }}</p> | ||||
| 						<p v-if="client.custom_value4 && client.custom_value4.length >= 1"><b>{{ company.custom_client_label4 }}:</b> {{ client.custom_value4 }}</p> | ||||
| 					</div> | ||||
| 
 | ||||
| 					<div class="col-sm"> | ||||
| 						<ul> | ||||
| 							<li><h3> {{ trans('texts.address') }} </h3></li> | ||||
| 							<li><b> {{ trans('texts.billing_address') }}</b></li> | ||||
| 							<li v-if="client.address1 && client.address1.length >=1">{{ client.address1 }} <br></li> | ||||
| 							<li v-if="client.address2 && client.address2.length >=1">{{ client.address2 }} <br></li> | ||||
| 							<li v-if="client.city && client.city.length >=1">{{ client.city }} <br></li> | ||||
| 							<li v-if="client.state && client.state.length >=1" >{{ client.state}} {{client.postal_code}}<br></li> | ||||
| 							<li v-if="client.country && client.country.name.length >=1">{{ client.country.name }}<br></li> | ||||
| 						</ul> | ||||
| 
 | ||||
| 						<ul v-if="client.shipping_address1 && client.shipping_address1.length >=1"> | ||||
| 							<li><b> {{ trans('texts.shipping_address') }}</b></li> | ||||
| 							<li v-if="client.shipping_address1 && client.shipping_address1.length >=1">{{ client.shipping_address1 }} <br></li> | ||||
| 							<li v-if="client.shipping_address2 && client.shipping_address2.length >=1">{{ client.shipping_address2 }} <br></li> | ||||
| 							<li v-if="client.shipping_city && client.shipping_city.length >=1">{{ client.shipping_city }} <br></li> | ||||
| 							<li v-if="client.shipping_state && client.shipping_state.length >=1" >{{ client.shipping_state}} {{client.shipping_postal_code}}<br></li> | ||||
| 							<li v-if="client.shipping_country && client.shipping_country.name.length >=1">{{ client.shipping_country.name }}<br></li> | ||||
| 						</ul> | ||||
| 					</div> | ||||
| 
 | ||||
| 					<div class="col-sm"> | ||||
| 						<h3> {{ trans('texts.contacts') }} </h3> | ||||
| 
 | ||||
| 						<ul v-for="contact in client.contacts">  | ||||
|                         	<li v-if="contact.first_name">{{ contact.first_name }} {{ contact.last_name }}</li> | ||||
|                         	<li v-if="contact.email">{{ contact.email }}</li> | ||||
|                         	<li v-if="contact.phone">{{ contact.phone }}</li> | ||||
|                         	<li v-if="company.custom_client_contact_label1 && company.custom_client_contact_label1.length >= 1"><b>{{ company.custom_client_contact_label1 }}:</b> {{ contact.custom_value1 }}</li> | ||||
| 							<li v-if="company.custom_client_contact_label2 && company.custom_client_contact_label2.length >= 1"><b>{{ company.custom_client_contact_label2 }}:</b> {{ contact.custom_value2 }}</li> | ||||
| 							<li v-if="company.custom_client_contact_label3 && company.custom_client_contact_label3.length >= 1"><b>{{ company.custom_client_contact_label3 }}:</b> {{ contact.custom_value3 }}</li> | ||||
| 							<li v-if="company.custom_client_contact_label4 && company.custom_client_contact_label4.length >= 1"><b>{{ company.custom_client_contact_label4 }}:</b> {{ contact.custom_value4 }}</li> | ||||
|                         </ul> | ||||
| 
 | ||||
| 
 | ||||
| 					</div> | ||||
| 
 | ||||
| 					<div class="col-sm"> | ||||
| 						<h3> {{ trans('texts.standing') }} </h3> | ||||
| 						<p><b>{{ trans('texts.paid_to_date') }} {{client.paid_to_date}}</b></p> | ||||
| 						<p><b>{{ trans('texts.balance') }} {{client.balance }}</b></p> | ||||
| 					</div> | ||||
| 
 | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		 | ||||
| 
 | ||||
| 		<div v-if="this.meta.google_maps_api_key"> | ||||
| 
 | ||||
| 		<iframe | ||||
| 		  width="100%" | ||||
| 		  height="200px" | ||||
| 		  frameborder="0" style="border:0" | ||||
| 		  :src="mapUrl" allowfullscreen> | ||||
| 		</iframe> | ||||
| 
 | ||||
| 		</div> | ||||
| 
 | ||||
| 	</div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
| export default { | ||||
|     props: ['client', 'company', 'meta'], | ||||
|     mounted() { | ||||
| 
 | ||||
|     }, | ||||
| 	methods: { | ||||
| 		goToUrl(url) { | ||||
| 			 location.href=url | ||||
| 
 | ||||
| 		} | ||||
| 	}, | ||||
|     computed: { | ||||
|     	mapUrl: { | ||||
|     		get: function() { | ||||
|         	  return `https://www.google.com/maps/embed/v1/place?key=${this.meta.google_maps_api_key}&q=${this.clientAddress}` | ||||
| 		    } | ||||
|     	}, | ||||
|     	clientAddress: { | ||||
|     		get: function() { | ||||
| 
 | ||||
|     			var addressArray = [] | ||||
| 
 | ||||
|     			if(this.client.address1) | ||||
|     				addressArray.push(this.client.address1.split(' ').join('+')) | ||||
| 
 | ||||
|     			if(this.client.address2) | ||||
|     				addressArray.push(this.client.address2.split(' ').join('+')) | ||||
| 
 | ||||
|     			if(this.client.city) | ||||
|     				addressArray.push(this.client.city.split(' ').join('+')) | ||||
| 
 | ||||
|     			if(this.client.state) | ||||
|     				addressArray.push(this.client.state.split(' ').join('+')) | ||||
| 
 | ||||
|     			if(this.client.postal_code) | ||||
|     				addressArray.push(this.client.postal_code.split(' ').join('+')) | ||||
| 
 | ||||
|     			if(this.client.country.name) | ||||
|     				addressArray.push(this.client.country.name.split(' ').join('+')) | ||||
| 
 | ||||
|     			return encodeURIComponent(addressArray.join(","))  | ||||
|     		} | ||||
|     	}, | ||||
| 		viewStatementIsDisabled() :any | ||||
| 		{ | ||||
| 			return ! this.meta.view_statement_permission | ||||
| 		}, | ||||
| 		editClientIsDisabled() :any | ||||
| 		{ | ||||
| 			return ! this.meta.edit_client_permission | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| .card { margin-top:50px; } | ||||
| 
 | ||||
| li { list-style: none } | ||||
| </style> | ||||
| @ -1,58 +0,0 @@ | ||||
| <template> | ||||
| <div class="card-body"> | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address1') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="address1" :placeholder="trans('texts.address1')" v-model="data.address1" class="form-control" id="address1"> | ||||
|                 <div v-if="errors && errors.address1" class="text-danger">{{ errors.address1[0] }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.address2') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="address2" :placeholder="trans('texts.address2')" v-model="data.address2" class="form-control" id="address2"> | ||||
|             <div v-if="errors && errors.address2" class="text-danger">{{ errors.address2[0] }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.city') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="city" :placeholder="trans('texts.city')" v-model="data.city" class="form-control" id="city"> | ||||
|             <div v-if="errors && errors.city" class="text-danger">{{ errors.city[0] }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.state') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="state" :placeholder="trans('texts.state')" v-model="data.state" class="form-control" id="state"> | ||||
|             <div v-if="errors && errors.state" class="text-danger">{{ errors.state[0] }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.postal_code') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="postal_code" :placeholder="trans('texts.postal_code')" v-model="data.postal_code" class="form-control" id="postal_code"> | ||||
|             <div v-if="errors && errors.postal_code" class="text-danger">{{ errors.postal_code[0] }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <label for="name" class="col-sm-3 col-form-label text-right">{{ trans('texts.country') }}</label> | ||||
|         <div class="col-sm-9"> | ||||
|             <input type="text" name="country" :placeholder="trans('texts.country')" v-model="data.country" class="form-control" id="country"> | ||||
|             <div v-if="errors && errors.country" class="text-danger">{{ errors.country[0] }}</div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div>	 | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| 	export default { | ||||
|         props: ['data', 'errors'], | ||||
|         default: () => {} | ||||
|     } | ||||
| </script> | ||||
| @ -1,117 +0,0 @@ | ||||
| <template> | ||||
| 
 | ||||
| 	<div class="d-flex justify-content-start"> | ||||
| 
 | ||||
|     <div class="p-2"> | ||||
|        | ||||
|       <div class="btn-group"> | ||||
|         <button class="btn btn-primary btn-lg dropdown-toggle" type="button" :disabled="getBulkCount() == 0" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ trans('texts.action') }} <span v-if="getBulkCount() > 0">({{ getBulkCount() }})</span></button> | ||||
|           <div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 44px, 0px);"> | ||||
|             <a class="dropdown-item" @click="archive" href="#">{{ trans('texts.archive') }}</a> | ||||
|             <a class="dropdown-item" @click="restore" href="#">{{ trans('texts.restore') }}</a> | ||||
|             <a class="dropdown-item" @click="del" href="#">{{ trans('texts.delete') }}</a> | ||||
|           </div> | ||||
|       </div> | ||||
|              | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="p-2"> | ||||
|       <vuetable-multi-select :select_options="listaction.multi_select"></vuetable-multi-select> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="mr-auto p-2"> | ||||
|       <div class="input-group mb-3"> | ||||
| 
 | ||||
|         <select class="custom-select" id="per_page" v-model="per_page" @change="updatePerPage()"> | ||||
|           <option value="10">10</option> | ||||
|           <option value="25">25</option> | ||||
|           <option value="50">50</option> | ||||
|           <option value="100">100</option> | ||||
|         </select> | ||||
|          | ||||
|         <div class="input-group-append"> | ||||
|           <label class="input-group-text" for="per_page">{{trans('texts.rows')}}</label> | ||||
|         </div> | ||||
| 
 | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="ml-auto p-2"> | ||||
|     	<vuetable-query-filter></vuetable-query-filter> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="p-2"> | ||||
|       <button class="btn btn-primary btn-lg " @click="goToUrl(listaction.create_entity.url)" :disabled="isDisabled">{{ trans('texts.new_client') }}</button> | ||||
|     </div> | ||||
| 
 | ||||
| 	</div> | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
|   export default { | ||||
|     props: { | ||||
|       listaction: { | ||||
|         type: Object, | ||||
|         required: true | ||||
|       }, | ||||
|       per_page_prop: { | ||||
|         type: Number, | ||||
|         required: true | ||||
|       } | ||||
|     },   | ||||
|     data () { | ||||
|       return { | ||||
| 
 | ||||
|         per_page: this.per_page_prop | ||||
| 
 | ||||
|       } | ||||
|     }, | ||||
|     methods: { | ||||
|       archive () { | ||||
| 
 | ||||
|         this.$events.fire('bulk-action', 'archive')  | ||||
| 
 | ||||
|       }, | ||||
|       del () { | ||||
| 
 | ||||
|         this.$events.fire('bulk-action', 'delete') | ||||
| 
 | ||||
|       }, | ||||
|       restore() { | ||||
| 
 | ||||
|         this.$events.fire('bulk-action', 'restore') | ||||
| 
 | ||||
|       }, | ||||
|       getBulkCount() { | ||||
| 
 | ||||
|         return this.$store.getters['client_list/getBulkCount'] | ||||
| 
 | ||||
|       }, | ||||
|       goToUrl: function (url) { | ||||
| 
 | ||||
|         location.href=url | ||||
| 
 | ||||
|       }, | ||||
|       updatePerPage() { | ||||
| 
 | ||||
|         this.$events.fire('perpage_action', this.per_page) | ||||
|        | ||||
|       } | ||||
|     }, | ||||
|    computed: { | ||||
|       isDisabled() :any | ||||
|       { | ||||
|         return !this.listaction.create_entity.create_permission; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| select.custom-select { | ||||
|     height: 42px; | ||||
|   } | ||||
| </style> | ||||
| @ -1,23 +0,0 @@ | ||||
| export default { | ||||
|   table: { | ||||
|     tableClass: 'table table-striped table-hover', | ||||
|     loadingClass: 'loading', | ||||
|     ascendingIcon: 'fa fa-angle-double-up', | ||||
|     descendingIcon: 'fa fa-angle-double-down', | ||||
|     handleIcon: 'glyphicon glyphicon-menu-hamburger', | ||||
|   }, | ||||
|   pagination: { | ||||
|     infoClass: 'pull-left', | ||||
|     wrapperClass: 'vuetable-pagination pull-right', | ||||
|     activeClass: 'btn-primary', | ||||
|     disabledClass: 'disabled', | ||||
|     pageClass: 'btn btn-border', | ||||
|     linkClass: 'btn btn-border', | ||||
|     icons: { | ||||
|       first: '', | ||||
|       prev: '', | ||||
|       next: '', | ||||
|       last: '', | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| @ -1,51 +0,0 @@ | ||||
| <template> | ||||
|    | ||||
|         <div class="input-group"> | ||||
| 
 | ||||
|           <input type="text" v-model="filterText" class="form-control" @keyup.enter="doFilter" placeholder="search"> | ||||
|           <button class="btn btn-primary" style="margin-left:15px;" @click="doFilter">Go</button> | ||||
|           <!--<button class="btn btn-light" @click="resetFilter">Reset</button>--> | ||||
| 
 | ||||
|         </div> | ||||
|        | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
|    | ||||
|   import Vue from 'vue' | ||||
| 
 | ||||
|   export default { | ||||
|     data () { | ||||
|       return { | ||||
| 
 | ||||
|         filterText: '' | ||||
| 
 | ||||
|       } | ||||
|     }, | ||||
|     methods: { | ||||
|       doFilter () { | ||||
| 
 | ||||
|         this.$store.commit('client_list/setFilterText', this.filterText) | ||||
|         this.$events.fire('filter-set','') | ||||
| 
 | ||||
|       }, | ||||
|       resetFilter () { | ||||
| 
 | ||||
|         this.$store.commit('client_list/setFilterText', '') | ||||
|         this.$events.fire('filter-set','') | ||||
| 
 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| 
 | ||||
|   .form-inline > * { | ||||
|      margin:5px 10px; | ||||
|   } | ||||
|   .form-control { | ||||
|     min-height: 40px; | ||||
|   } | ||||
| 
 | ||||
| </style> | ||||
| @ -1,54 +0,0 @@ | ||||
| <template> | ||||
|   <div style="width:300px;"> | ||||
|     <multiselect v-model="value"  | ||||
|     :options="options"  | ||||
|     :multiple="true" | ||||
|     :placeholder="trans('texts.status')" | ||||
|     :preselect-first="true" | ||||
|     label="name" | ||||
|     track-by="name" | ||||
|     @input="onChange" | ||||
|     ></multiselect> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
|   import Multiselect from 'vue-multiselect' | ||||
| 
 | ||||
|   export default { | ||||
|     components: { Multiselect }, | ||||
|     props:['select_options'], | ||||
|     data () { | ||||
|       return { | ||||
|         value : [], | ||||
|         options: this.select_options | ||||
|       } | ||||
|     }, | ||||
|     mounted() { | ||||
| 
 | ||||
|       this.$events.fire('multi-select', '') | ||||
|      | ||||
|     }, | ||||
|     methods: { | ||||
|       onChange (value) { | ||||
| 
 | ||||
|         this.$store.commit('client_list/setStatusArray', value) | ||||
|         this.$events.fire('multi-select', '') | ||||
| 
 | ||||
|         if (value.indexOf('Reset me!') !== -1) this.value = [] | ||||
| 
 | ||||
|       }, | ||||
|       onSelect (option) { | ||||
| 
 | ||||
|         if (option === 'Disable me!') this.isDisabled = true | ||||
| 
 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <style src="vue-multiselect/dist/vue-multiselect.min.css"></style> | ||||
| 
 | ||||
| <style> | ||||
| </style> | ||||
| @ -1,37 +0,0 @@ | ||||
| <template> | ||||
|   <ul class="pagination"> | ||||
|     <li :class="{'disabled': isOnFirstPage}"> | ||||
|       <a href="" @click.prevent="loadPage('prev')"> | ||||
|         <span>«</span> | ||||
|       </a> | ||||
|     </li> | ||||
|      | ||||
|     <template v-if="notEnoughPages"> | ||||
|       <li v-for="n in totalPage" :class="{'active': isCurrentPage(n)}"> | ||||
|         <a @click.prevent="loadPage(n)" v-html="n"></a> | ||||
|       </li> | ||||
|     </template> | ||||
| 
 | ||||
|     <template v-else> | ||||
|       <li v-for="n in windowSize" :class="{'active': isCurrentPage(windowStart+n-1)}"> | ||||
|         <a @click.prevent="loadPage(windowStart+n-1)" v-html="windowStart+n-1"></a> | ||||
|       </li> | ||||
|     </template> | ||||
| 
 | ||||
|     <li :class="{'disabled': isOnLastPage}"> | ||||
|       <a href="" @click.prevent="loadPage('next')"> | ||||
|         <span>»</span> | ||||
|       </a> | ||||
|     </li> | ||||
|   </ul> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| 
 | ||||
| import VuetablePaginationMixin from 'vuetable-2/src/components/VuetablePaginationMixin' | ||||
| 
 | ||||
| export default { | ||||
|   mixins: [VuetablePaginationMixin] | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| @ -1,37 +0,0 @@ | ||||
| export default class ClientContact { | ||||
| 
 | ||||
| 	id: number | ||||
| 	client_id: number | ||||
| 	user_id: number | ||||
| 	company_id: number | ||||
| 	first_name: string  | ||||
| 	last_name: string  | ||||
| 	phone: string  | ||||
| 	custom_value1: string  | ||||
| 	custom_value2: string  | ||||
| 	email: string  | ||||
| 	email_verified_at: string  | ||||
| 	confirmation_code: string  | ||||
| 	is_primary: boolean  | ||||
| 	confirmed: boolean  | ||||
| 	failed_logins: number  | ||||
| 	oauth_user_id: string  | ||||
| 	oauth_provider_id: string  | ||||
| 	google_2fa_secret: string  | ||||
| 	accepted_terms_version: string  | ||||
| 	avatar: string  | ||||
| 	avatar_width: string  | ||||
| 	avatar_height: string  | ||||
| 	avatar_size: string  | ||||
| 	db: string  | ||||
| 	password: string  | ||||
| 	remember_token: string | ||||
| 	deleted_at: string  | ||||
| 	created_at: string  | ||||
| 	updated_at: string | ||||
| 
 | ||||
| 	public constructor(init?:Partial<ClientContact>) { | ||||
| 	        (<any>Object).assign(this, init); | ||||
| 	    } | ||||
| 
 | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| export default class Client { | ||||
| 
 | ||||
| 	id: number | ||||
| 	name: string | ||||
| 	user_id: number | ||||
| 	company_id: number | ||||
| 	website: string  | ||||
| 	private_notes: string  | ||||
| 	balance: number  | ||||
| 	paid_to_date: number  | ||||
| 	last_login: string  | ||||
| 	industry_id: number  | ||||
| 	size_id: number  | ||||
| 	currency_id: number  | ||||
| 	address1: string  | ||||
| 	address2: string  | ||||
| 	city: string  | ||||
| 	state: string  | ||||
| 	postal_code: string  | ||||
| 	country_id: number  | ||||
| 	latitude: number | ||||
| 	longitude: number | ||||
| 	shipping_latitude: number | ||||
| 	shipping_longitude: number | ||||
| 	custom_value1: string  | ||||
| 	custom_value2: string  | ||||
| 	shipping_address1: string  | ||||
| 	shipping_address2: string  | ||||
| 	shipping_city: string  | ||||
| 	shipping_state: string  | ||||
| 	shipping_postal_code: string  | ||||
| 	shipping_country_id: number  | ||||
| 	is_deleted: boolean  | ||||
| 	payment_terms: string  | ||||
| 	vat_number: string  | ||||
| 	id_number: string  | ||||
| 	created_at: string  | ||||
| 	updated_at: string | ||||
| 
 | ||||
| 	public constructor(init?:Partial<Client>) { | ||||
| 	        (<any>Object).assign(this, init); | ||||
| 	    } | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| export default class ClientSettings { | ||||
| 
 | ||||
| 	timezone_id:number | ||||
| 	language_id:number | ||||
| 	currency_id:number | ||||
| 	default_task_rate:number | ||||
| 	send_reminders:boolean | ||||
| 	show_tasks_in_portal:boolean | ||||
| 	custom_message_dashboard:string | ||||
| 	custom_message_unpaid_invoice:string | ||||
| 	custom_message_paid_invoice:string | ||||
| 	custom_message_unapproved_quote:string | ||||
| 	show_currency_symbol:boolean | ||||
| 	show_currency_code:boolean | ||||
| 	industry_id:number | ||||
| 	size_id:number | ||||
| 
 | ||||
| 	constructor(init?:Partial<ClientSettings>) { | ||||
| 	        (<any>Object).assign(this, init); | ||||
| 	    } | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| 
 | ||||
| import Vue from 'vue'; | ||||
| import VueSelect from 'vue-select'; | ||||
| 
 | ||||
| Vue.component('v-select', VueSelect.VueSelect) | ||||
| 
 | ||||
| new Vue({ | ||||
|   el: '#localization', | ||||
|   data: { | ||||
|     options: ['jim','bob','frank'], | ||||
|     selected: 'frank', | ||||
|   } | ||||
| }) | ||||
							
								
								
									
										4
									
								
								resources/js/src/shims-vue.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								resources/js/src/shims-vue.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +0,0 @@ | ||||
| declare module '*.vue' { | ||||
|   import Vue from 'vue' | ||||
|   export default Vue | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| import Vue from 'vue' | ||||
| import Vuex from 'vuex' | ||||
| import client_list from './modules/client_list' | ||||
| 
 | ||||
| Vue.use(Vuex) | ||||
| 
 | ||||
| const debug = process.env.NODE_ENV !== 'production' | ||||
| 
 | ||||
| const store =  new Vuex.Store({ | ||||
|   modules: { | ||||
|     client_list | ||||
|   }, | ||||
|   strict: debug, | ||||
| }) | ||||
| 
 | ||||
| export default store | ||||
| @ -1,70 +0,0 @@ | ||||
| /** | ||||
|  * State managment for the Client List View | ||||
|  */ | ||||
| 
 | ||||
| const state = { | ||||
|   statuses: [{value: 'active'}], | ||||
|   filter_text: '', | ||||
|   bulk_count : 0 | ||||
| } | ||||
| 
 | ||||
| // getters
 | ||||
| const getters = { | ||||
|   getBulkCount: state => { | ||||
| 
 | ||||
|     return state.bulk_count | ||||
|    | ||||
|   },  | ||||
| 	getFilterText: state => { | ||||
| 
 | ||||
| 		return state.filter_text | ||||
| 
 | ||||
| 	}, | ||||
|   getQueryStringObject: state => { | ||||
| 
 | ||||
|     var values = state.statuses.map(function (state, index, array) { | ||||
|          return state.value;  | ||||
|     }); | ||||
| 
 | ||||
|     var queryObj = { | ||||
|       filter: state.filter_text, | ||||
|       status: [].concat.apply([], values).join(",") | ||||
|     } | ||||
| 
 | ||||
|     return queryObj | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // actions
 | ||||
| const actions = { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // mutations
 | ||||
| const mutations = { | ||||
| 	setFilterText(state, text) { | ||||
| 
 | ||||
|     state.filter_text = text | ||||
| 
 | ||||
| 	}, | ||||
|   setStatusArray(state, statuses) { | ||||
| 
 | ||||
|     state.statuses = statuses | ||||
| 
 | ||||
|   }, | ||||
|   setBulkCount(state, count) { | ||||
| 
 | ||||
|     state.bulk_count = count | ||||
| 
 | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export default { | ||||
| 
 | ||||
|   namespaced: true, | ||||
|   state, | ||||
|   getters, | ||||
|   actions, | ||||
|   mutations | ||||
|    | ||||
| } | ||||
| @ -1,3 +0,0 @@ | ||||
| export function sum(x: number, y: number): number { | ||||
|   return x + y; | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| import { sum } from "./math"; | ||||
| 
 | ||||
| describe("This is a simple test", () => { | ||||
|     test("Check the sum of 0 + 0", () => { | ||||
| 			expect(sum(0,0)).toBe(0); | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| describe("This is a simple test", () => { | ||||
|     test("Check the sum of 1 + 2", () => { | ||||
| 		expect(sum(1, 2)).toBe(3);     | ||||
| 	}); | ||||
| }); | ||||
| @ -1,129 +0,0 @@ | ||||
| import CSettings from '../models/client-settings-model'; | ||||
| 
 | ||||
| export default class ClientSettings { | ||||
| 
 | ||||
| 	client_settings:any | ||||
| 
 | ||||
| 	company_settings:any | ||||
| 
 | ||||
| 	settings:any | ||||
| 
 | ||||
| 	languages:any | ||||
| 
 | ||||
|     currencies:any | ||||
| 
 | ||||
|     payment_terms:any | ||||
| 
 | ||||
|     industries:any | ||||
| 
 | ||||
|     sizes:any | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new Client Settings instance. | ||||
|      */ | ||||
|     constructor( | ||||
|         client_settings: any,  | ||||
|         company_settings: any,  | ||||
|         languages: any, | ||||
|         currencies: any, | ||||
|         payment_terms: any, | ||||
|         industries: any, | ||||
|         sizes: any | ||||
|         ) { | ||||
|     	this.client_settings = client_settings | ||||
|     	this.company_settings = company_settings | ||||
|     	this.languages = languages | ||||
|         this.currencies = currencies | ||||
|         this.payment_terms = payment_terms | ||||
|         this.industries = industries | ||||
|         this.sizes = sizes | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Build Settings object | ||||
|      */ | ||||
|     build() { | ||||
| 
 | ||||
|         this.settings = new CSettings(this.client_settings) | ||||
|         if (this.client_settings.currency_id !== null) {  | ||||
| 
 | ||||
|             this.settings.currency_id = this.currencies.find(obj => { | ||||
|                                             return obj.id == this.client_settings.currency_id | ||||
|                                         }) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if(this.client_settings.show_currency_symbol == null) | ||||
|             this.settings.show_currency_symbol = this.company_settings.show_currency_symbol | ||||
| 
 | ||||
|         if(this.client_settings.show_currency_code == null) | ||||
|             this.settings.show_currency_code = this.company_settings.show_currency_code | ||||
| 
 | ||||
|         if (this.client_settings.language_id !== null) {  | ||||
| 
 | ||||
|             this.settings.language_id = this.languages.find(obj => { | ||||
|                                             return obj.id == this.client_settings.language_id | ||||
|                                         }) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (this.client_settings.payment_terms !== null) {  | ||||
| 
 | ||||
|             this.settings.payment_terms = this.payment_terms.find(obj => { | ||||
|                                             return obj.id == this.client_settings.payment_terms | ||||
|                                         }) | ||||
|         } | ||||
| 
 | ||||
|         this.settings.default_task_rate = this.client_settings.default_task_rate ? this.client_settings.default_task_rate : this.company_settings.default_task_rate | ||||
| 
 | ||||
|         if(this.client_settings.send_reminders) | ||||
|             this.settings.send_reminders = this.client_settings.send_reminders | ||||
|         else | ||||
|             this.settings.send_reminders = this.company_settings.send_reminders | ||||
| 
 | ||||
|         if(this.client_settings.show_tasks_in_portal) | ||||
|             this.settings.show_tasks_in_portal = this.client_settings.show_tasks_in_portal | ||||
|         else | ||||
|             this.settings.show_tasks_in_portal = this.company_settings.show_tasks_in_portal | ||||
| 
 | ||||
|         if(this.client_settings.custom_message_dashboard && this.client_settings.custom_message_dashboard.length >=1) | ||||
|             this.settings.custom_message_dashboard = this.client_settings.custom_message_dashboard | ||||
|         else | ||||
|             this.settings.custom_message_dashboard = this.company_settings.custom_message_dashboard | ||||
| 
 | ||||
|         if(this.client_settings.custom_message_unpaid_invoice && this.client_settings.custom_message_unpaid_invoice.length >=1) | ||||
|             this.settings.custom_message_unpaid_invoice = this.client_settings.custom_message_unpaid_invoice | ||||
|         else | ||||
|             this.settings.custom_message_unpaid_invoice = this.company_settings.custom_message_unpaid_invoice | ||||
| 
 | ||||
|         if(this.client_settings.custom_message_paid_invoice && this.client_settings.custom_message_paid_invoice.length >=1) | ||||
|             this.settings.custom_message_paid_invoice = this.client_settings.custom_message_paid_invoice | ||||
|         else | ||||
|             this.settings.custom_message_paid_invoice = this.company_settings.custom_message_paid_invoice | ||||
| 
 | ||||
|         if(this.client_settings.custom_message_unapproved_quote && this.client_settings.custom_message_unapproved_quote.length >=1) | ||||
|             this.settings.custom_message_unapproved_quote = this.client_settings.custom_message_unapproved_quote | ||||
|         else | ||||
|             this.settings.custom_message_unapproved_quote = this.company_settings.custom_message_unapproved_quote | ||||
| 
 | ||||
|         if (this.client_settings.industry_id !== null) {  | ||||
| 
 | ||||
|             this.settings.industry_id = this.industries.find(obj => { | ||||
|                                             return obj.id == this.client_settings.industry_id | ||||
|                                         }) | ||||
|         } | ||||
| 
 | ||||
|         if (this.client_settings.size_id !== null) {  | ||||
| 
 | ||||
|             this.settings.size_id = this.sizes.find(obj => { | ||||
|                                             return obj.id == this.client_settings.size_id | ||||
|                                         }) | ||||
|         }          | ||||
| 
 | ||||
|         return this.settings | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,74 +0,0 @@ | ||||
| export default class FormErrors { | ||||
| 
 | ||||
| 	errors:any; | ||||
|     /** | ||||
|      * Create a new Errors instance. | ||||
|      */ | ||||
|     constructor() { | ||||
|         this.errors = {}; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Determine if an errors exists for the given field. | ||||
|      * | ||||
|      * @param {string} field | ||||
|      */ | ||||
|     has(field:string) { | ||||
|         return this.errors.hasOwnProperty(field); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Determine if we have any errors. | ||||
|      */ | ||||
|     any() { | ||||
|         return Object.keys(this.errors).length > 0; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve the error message for a field. | ||||
|      * | ||||
|      * @param {string} field | ||||
|      */ | ||||
|     get(field:string) { | ||||
|         if (this.errors[field]) { | ||||
|             return this.errors[field][0]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Record the new errors. | ||||
|      * | ||||
|      * @param {object} errors | ||||
|      */ | ||||
|     record(errors:any) { | ||||
|         this.errors = errors; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Clear one or all error fields. | ||||
|      * | ||||
|      * @param {string|null} field | ||||
|      */ | ||||
|     clear(field:string) { | ||||
|         if (field) { | ||||
|             delete this.errors[field]; | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.errors = {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //@keydown="errors.clear($event.target.name)"
 | ||||
| //
 | ||||
| //
 | ||||
| //
 | ||||
| @ -1,175 +0,0 @@ | ||||
| import axios from 'axios'; | ||||
| import FormErrors from '../utils/form-errors'; | ||||
| 
 | ||||
| export default class Form { | ||||
| 
 | ||||
|     errors:any; | ||||
| 
 | ||||
|     originalData:any; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new Form instance. | ||||
|      * | ||||
|      * @param {object} data | ||||
|      */ | ||||
|     constructor(data) { | ||||
| 
 | ||||
|         this.originalData = data; | ||||
| 
 | ||||
|         for (let field in data) { | ||||
|             this[field] = data[field]; | ||||
|         } | ||||
| 
 | ||||
|         this.errors = new FormErrors(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetch all relevant data for the form. | ||||
|      */ | ||||
|     data() { | ||||
| 
 | ||||
|         let data = {}; | ||||
| 
 | ||||
|         for (let property in this.originalData) { | ||||
|             data[property] = this[property]; | ||||
|         } | ||||
| 
 | ||||
|         return data; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reset the form fields. | ||||
|      */ | ||||
|     reset() { | ||||
| 
 | ||||
|         for (let field in this.originalData) { | ||||
|             this[field] = ''; | ||||
|         } | ||||
| 
 | ||||
|         this.errors.clear(); | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send a POST request to the given URL. | ||||
|      * . | ||||
|      * @param {string} url | ||||
|      */ | ||||
|     post(url) { | ||||
| 
 | ||||
|         return this.submit('post', url); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send a PUT request to the given URL. | ||||
|      * . | ||||
|      * @param {string} url | ||||
|      */ | ||||
|     put(url:string) { | ||||
| 
 | ||||
|         return this.submit('put', url); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send a PATCH request to the given URL. | ||||
|      * . | ||||
|      * @param {string} url | ||||
|      */ | ||||
|     patch(url:string) { | ||||
| 
 | ||||
|         return this.submit('patch', url); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Send a DELETE request to the given URL. | ||||
|      * . | ||||
|      * @param {string} url | ||||
|      */ | ||||
|     delete(url:string) { | ||||
| 
 | ||||
|         return this.submit('delete', url); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Submit the form. | ||||
|      * | ||||
|      * @param {string} requestType | ||||
|      * @param {string} url | ||||
|      */ | ||||
|     submit(requestType:string, url:string) { | ||||
| 
 | ||||
|         return new Promise((resolve, reject) => { | ||||
| 
 | ||||
|             axios[requestType](url, this.data()) | ||||
|                 .then(response => { | ||||
| 
 | ||||
|                     this.onSuccess(response.data); | ||||
| 
 | ||||
|                     resolve(response.data); | ||||
| 
 | ||||
|                 }) | ||||
|                 .catch(error => { | ||||
| 
 | ||||
| 
 | ||||
|                     if (error.response.status === 422) { | ||||
| 
 | ||||
|                         this.onFail(error.response.data.errors); | ||||
| 
 | ||||
|                     } | ||||
|                     else if(error.response.status === 419) { | ||||
| 
 | ||||
|                         //csrf token has expired, we'll need to force a page reload
 | ||||
| 
 | ||||
|                     } | ||||
| 
 | ||||
|                     reject(error.response.data); | ||||
| 
 | ||||
|                                        | ||||
|                 }); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * Update form data on success | ||||
|     * | ||||
|     * @param {object} data | ||||
|     */ | ||||
|     update(data) | ||||
|     { | ||||
|         this.originalData = data; | ||||
| 
 | ||||
|         for (let field in data) { | ||||
|             this[field] = data[field]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle a successful form submission. | ||||
|      * | ||||
|      * @param {object} data | ||||
|      */ | ||||
|     onSuccess(data) { | ||||
|         this.update(data); | ||||
|         this.errors.clear(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle a failed form submission. | ||||
|      * | ||||
|      * @param {object} errors | ||||
|      */ | ||||
|     onFail(errors) { | ||||
| 
 | ||||
|         this.errors.record(errors); | ||||
| 
 | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -1,34 +0,0 @@ | ||||
| export default class NumberFormat { | ||||
| 
 | ||||
| 	amount:any | ||||
| 
 | ||||
| 	currency:any | ||||
| 
 | ||||
| 	symbol_decorator:boolean | ||||
| 
 | ||||
| 	language:any | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new Number Format instance. | ||||
|      */ | ||||
|     constructor(amount: any, currency: any, symbol_decorator: boolean, language: any) { | ||||
|     	this.amount = amount | ||||
|     	this.currency = currency | ||||
|     	this.symbol_decorator = symbol_decorator | ||||
|     	this.language = language | ||||
|     } | ||||
| 
 | ||||
|     format() { | ||||
|     	this.amount = new Intl.NumberFormat(this.language.locale.replace("_", "-"), {style: 'decimal',currency: this.currency.code} ).format(this.amount) | ||||
| 
 | ||||
|     	if(this.symbol_decorator) | ||||
|     		this.amount = this.currency.symbol + this.amount | ||||
|     	else | ||||
|     		this.amount = this.amount + " " + this.currency.code | ||||
| 
 | ||||
| 
 | ||||
|     	return this.amount | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -95,24 +95,26 @@ class MigrationTest extends TestCase | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function testMigrationFileUpload() | ||||
|     { | ||||
|         $file = new UploadedFile(base_path('tests/Unit/Migration/migration.zip'), 'migration.zip'); | ||||
|     // public function testMigrationFileUpload()
 | ||||
|     // {
 | ||||
|     //     $file = new UploadedFile(base_path('tests/Unit/Migration/migration.zip'), 'migration.zip');
 | ||||
| 
 | ||||
|         $data = [ | ||||
|             'migration' => $file, | ||||
|             'force' => true, | ||||
|         ]; | ||||
|     //     $data = [
 | ||||
|     //         'migration' => $file,
 | ||||
|     //         'force' => true,
 | ||||
|     //     ];
 | ||||
| 
 | ||||
|         $token = $this->company->tokens->first()->token; | ||||
|     //     $token = $this->company->tokens->first()->token;
 | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|                 'X-API-TOKEN' => $token, | ||||
|                 'X-API-SECRET' => config('ninja.api_secret'), | ||||
|                 'X-Requested-With' => 'XMLHttpRequest', | ||||
|                 'X-API-PASSWORD' => 'ALongAndBriliantPassword', | ||||
|             ])->post('/api/v1/migration/start', $data); | ||||
|     //     $response = $this->withHeaders([
 | ||||
|     //             'X-API-TOKEN' => $token,
 | ||||
|     //             'X-API-SECRET' => config('ninja.api_secret'),
 | ||||
|     //             'X-Requested-With' => 'XMLHttpRequest',
 | ||||
|     //             'X-API-PASSWORD' => 'ALongAndBriliantPassword',
 | ||||
|     //         ])->post('/api/v1/migration/start', $data);
 | ||||
| 
 | ||||
|     //     $response->assertStatus(200);
 | ||||
|     //     $this->assertTrue(file_exists(base_path('storage/migrations/migration/migration.json')));
 | ||||
|     // }
 | ||||
| 
 | ||||
|         $response->assertStatus(200); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -35,10 +35,17 @@ class DesignTest extends TestCase | ||||
| 
 | ||||
|     	$this->assertNotNull($html); | ||||
| 
 | ||||
|     	//\Log::error($html);
 | ||||
| 
 | ||||
|         $this->invoice = factory(\App\Models\Invoice::class)->create([ | ||||
|                 'user_id' => $this->user->id, | ||||
|                 'client_id' => $this->client->id, | ||||
|                 'company_id' => $this->company->id, | ||||
|             ]); | ||||
| 
 | ||||
|         $this->invoice->uses_inclusive_taxes = false; | ||||
| 
 | ||||
|     	$settings = $this->invoice->client->settings; | ||||
|     	$settings->invoice_design_id = "5"; | ||||
|     	$settings->invoice_design_id = "6"; | ||||
| 
 | ||||
|     	$this->client->settings = $settings; | ||||
|     	$this->client->save(); | ||||
| @ -60,7 +67,7 @@ class DesignTest extends TestCase | ||||
|     	//\Log::error($html);
 | ||||
| 
 | ||||
|     	$settings = $this->invoice->client->settings; | ||||
|     	$settings->quote_design_id = "10"; | ||||
|     	$settings->quote_design_id = "6"; | ||||
| 
 | ||||
|     	$this->client->settings = $settings; | ||||
|     	$this->client->save(); | ||||
|  | ||||
| @ -91,8 +91,6 @@ trait MockAccountData | ||||
|              | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         $this->account = factory(\App\Models\Account::class)->create(); | ||||
|         $this->company = factory(\App\Models\Company::class)->create([ | ||||
|             'account_id' => $this->account->id, | ||||
| @ -189,9 +187,16 @@ trait MockAccountData | ||||
|         $this->client->group_settings_id = $gs->id; | ||||
|         $this->client->save(); | ||||
|   | ||||
|         $this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
 | ||||
|         $this->invoice = InvoiceFactory::create($this->company->id, $this->user->id);//stub the company and user_id
 | ||||
|         $this->invoice->client_id = $this->client->id; | ||||
| 
 | ||||
|         // $this->invoice = factory(\App\Models\Invoice::class)->create([
 | ||||
|         //         'user_id' => $this->user->id,
 | ||||
|         //         'client_id' => $this->client->id,
 | ||||
|         //         'company_id' => $this->company->id,
 | ||||
|         //     ]);
 | ||||
| 
 | ||||
| 
 | ||||
| 		$this->invoice->line_items = $this->buildLineItems(); | ||||
| 		$this->invoice->uses_inclusive_taxes = false; | ||||
| 
 | ||||
|  | ||||
| @ -103,13 +103,10 @@ class ImportTest extends TestCase | ||||
|         $this->makeTestData(); | ||||
| 
 | ||||
|         $this->invoice->forceDelete(); | ||||
|         $this->quote->forceDelete(); | ||||
| 
 | ||||
|         $original_count = Invoice::count(); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         //$this->migration_array = json_decode(file_get_contents($migration_file), 1);
 | ||||
| 
 | ||||
|         Import::dispatchNow($this->migration_array, $this->company, $this->user); | ||||
| 
 | ||||
|         $this->assertGreaterThan($original_count, Invoice::count()); | ||||
| @ -149,7 +146,7 @@ class ImportTest extends TestCase | ||||
|         //$this->makeTestData();
 | ||||
| 
 | ||||
|         $this->invoice->forceDelete(); | ||||
| 
 | ||||
|         $this->quote->forceDelete(); | ||||
|         // $migration_file = base_path() . '/tests/Unit/Migration/migration.json';
 | ||||
| 
 | ||||
|         // $this->migration_array = json_decode(file_get_contents($migration_file), 1);
 | ||||
| @ -190,7 +187,7 @@ class ImportTest extends TestCase | ||||
|         $original_number = Invoice::count(); | ||||
| 
 | ||||
|         $this->invoice->forceDelete(); | ||||
| 
 | ||||
|         $this->quote->forceDelete(); | ||||
|         // $migration_file = base_path() . '/tests/Unit/Migration/migration.json';
 | ||||
| 
 | ||||
|         // $this->migration_array = json_decode(file_get_contents($migration_file), 1);
 | ||||
| @ -264,7 +261,7 @@ class ImportTest extends TestCase | ||||
|         $original_count = Payment::count(); | ||||
| 
 | ||||
|         $this->invoice->forceDelete(); | ||||
| 
 | ||||
|         $this->quote->forceDelete(); | ||||
|         // $migration_file = base_path() . '/tests/Unit/Migration/migration.json';
 | ||||
| 
 | ||||
|         // $this->migration_array = json_decode(file_get_contents($migration_file), 1);
 | ||||
| @ -295,6 +292,7 @@ class ImportTest extends TestCase | ||||
|         $original_count = Credit::count(); | ||||
| 
 | ||||
|         $this->invoice->forceDelete(); | ||||
|         $this->quote->forceDelete(); | ||||
| 
 | ||||
|         // $migration_file = base_path() . '/tests/Unit/Migration/migration.json';
 | ||||
| 
 | ||||
| @ -329,6 +327,7 @@ class ImportTest extends TestCase | ||||
|     public function testValidityOfImportedData() | ||||
|     { | ||||
|         $this->invoice->forceDelete(); | ||||
|         $this->quote->forceDelete(); | ||||
| 
 | ||||
|         // $migration_file = base_path() . '/tests/Unit/Migration/migration.json';
 | ||||
| 
 | ||||
| @ -424,7 +423,7 @@ class ImportTest extends TestCase | ||||
|         }*/ | ||||
| 
 | ||||
|         foreach ($this->migration_array['documents'] as $key => $document) { | ||||
|             $record = Document::whereHash('5a81aa656c8aaf77dca259b7defdda1dc5ae7901') | ||||
|             $record = Document::whereHash($document['hash']) | ||||
|                 ->first(); | ||||
| 
 | ||||
|             if (!$record) { | ||||
| @ -432,12 +431,14 @@ class ImportTest extends TestCase | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         \Log::error($differences); | ||||
|         $this->assertCount(0, $differences); | ||||
|     } | ||||
| 
 | ||||
|     public function testClientContactsImport() | ||||
|     { | ||||
|         $this->invoice->forceDelete(); | ||||
|         $this->quote->forceDelete(); | ||||
| 
 | ||||
|         $original = ClientContact::count(); | ||||
| 
 | ||||
| @ -453,6 +454,7 @@ class ImportTest extends TestCase | ||||
|     public function testDocumentsImport() | ||||
|     { | ||||
|         $this->invoice->forceDelete();  | ||||
|         $this->quote->forceDelete(); | ||||
| 
 | ||||
|         $original = Document::count(); | ||||
| 
 | ||||
| @ -461,6 +463,8 @@ class ImportTest extends TestCase | ||||
|         $this->assertGreaterThan($original, Document::count()); | ||||
| 
 | ||||
|         $document = Document::first(); | ||||
|          | ||||
| \Log::error($document); | ||||
| 
 | ||||
|         $this->assertNotNull(Invoice::find($document->documentable_id)->documents); | ||||
|         $this->assertNotNull($document->documentable); | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,62 +0,0 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     /* Basic Options */ | ||||
|     "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ | ||||
|     "module": "commonjs",                     /* Specify module code generation: 'none', -> 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ | ||||
|     // "lib": [],                             /* Specify library files to be included in the compilation. */ | ||||
|     // "allowJs": true,                       /* Allow javascript files to be compiled. */ | ||||
|     // "checkJs": true,                       /* Report errors in .js files. */ | ||||
|     // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ | ||||
|     // "declaration": true,                   /* Generates corresponding '.d.ts' file. */ | ||||
|     // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */ | ||||
|     "sourceMap": false,                     /* Generates corresponding '.map' file. */ | ||||
|     // "outFile": "./",                       /* Concatenate and emit output to single file. */ | ||||
|     // "outDir": "./",                        /* Redirect output structure to the directory. */ | ||||
|     // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ | ||||
|     // "composite": true,                     /* Enable project compilation */ | ||||
|     // "removeComments": true,                /* Do not emit comments to output. */ | ||||
|     // "noEmit": true,                        /* Do not emit outputs. */ | ||||
|     // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */ | ||||
|     // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | ||||
|     // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ | ||||
| 
 | ||||
|     /* Strict Type-Checking Options */ | ||||
|     "strict": false,                           /* Enable all strict type-checking options. */ | ||||
|     "noImplicitAny": false,                 /* Raise error on expressions and declarations with an implied 'any' type. */ | ||||
|     // "strictNullChecks": true,              /* Enable strict null checks. */ | ||||
|     // "strictFunctionTypes": true,           /* Enable strict checking of function types. */ | ||||
|     // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */ | ||||
|     // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */ | ||||
|     // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */ | ||||
| 
 | ||||
|     /* Additional Checks */ | ||||
|     // "noUnusedLocals": true,                /* Report errors on unused locals. */ | ||||
|     // "noUnusedParameters": true,            /* Report errors on unused parameters. */ | ||||
|     // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */ | ||||
|     // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */ | ||||
| 
 | ||||
|     /* Module Resolution Options */ | ||||
|     // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ | ||||
|     // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */ | ||||
|     // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ | ||||
|     // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */ | ||||
|     // "typeRoots": [],                       /* List of folders to include type definitions from. */ | ||||
|     // "types": [],                           /* Type declaration files to be included in compilation. */ | ||||
|     // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||||
|     "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | ||||
|     // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */ | ||||
| 
 | ||||
|     /* Source Map Options */ | ||||
|     // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | ||||
|     // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */ | ||||
|     // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */ | ||||
|     // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | ||||
| 
 | ||||
|     /* Experimental Options */ | ||||
|     // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */ | ||||
|     // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */ | ||||
|   }, | ||||
|   "include": [ | ||||
|     "resources/js/**/*" | ||||
|   ] | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user