1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-05 18:52:44 +01:00

Client Settings (#2668)

* Clean up Client Show

* Working on Show Client menu action

* working on client view permissions

* Finishing up Client Statement View

* Workig on client settings

* add mix manifest

* css for client settings

* Client Settings

* Working on Client Settings

* Implement StartupCheck and static seeders

* Implement cached statics in view composers

* Working on client settings

* Payment Terms

* Working on Payment Terms View Composer

* Payment Terms builder

* Client Settings

* refactor companies table

* Refactor for company settings, move settings to json

* Set object cast on settings column of Company table

* Fixes for refactor of companies and clients table

* Test

* Client Settings Datamapper

* Client Settings

* Default client language

* Client Settings

* Working on client settings options

* Client Settings

* Settings Json serialization/deserialization handling
This commit is contained in:
David Bomba 2019-02-17 21:34:46 +11:00 committed by GitHub
parent 1ad19734e3
commit eddb9adc73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 3460 additions and 1746 deletions

View File

@ -33,4 +33,5 @@ MAIL_ENCRYPTION=null
MULTI_DB_ENABLED=true
POSTMARK_API_TOKEN=
GOOGLE_MAPS_API_KEY
GOOGLE_MAPS_API_KEY=
COMPANY_SETTINGS='{"timezone_id":"15","currency_id":"1","language_id":"1","payment_terms":"7"}'

View File

@ -13,5 +13,75 @@ define('BANK_LIBRARY_OFX', 1);
define('RANDOM_KEY_LENGTH', 32); //63340286662973277706162286946811886609896461828096 combinations
$cached_tables = [
'currencies' => 'App\Models\Currency',
// 'sizes' => 'App\Models\Size',
'industries' => 'App\Models\Industry',
// 'timezones' => 'App\Models\Timezone',
// 'dateFormats' => 'App\Models\DateFormat',
// 'datetimeFormats' => 'App\Models\DatetimeFormat',
'languages' => 'App\Models\Language',
'paymentTypes' => 'App\Models\PaymentType',
'countries' => 'App\Models\Country',
// 'invoiceDesigns' => 'App\Models\InvoiceDesign',
// 'invoiceStatus' => 'App\Models\InvoiceStatus',
// 'frequencies' => 'App\Models\Frequency',
// 'gateways' => 'App\Models\Gateway',
// 'gatewayTypes' => 'App\Models\GatewayType',
// 'fonts' => 'App\Models\Font',
// 'banks' => 'App\Models\Bank',
];
define('CACHED_TABLES', serialize($cached_tables));
define('CACHED_PAYMENT_TERMS', serialize(
[
[
'num_days' => 0,
'name' => '',
],
[
'num_days' => 7,
'name' => '',
],
[
'num_days' => 10,
'name' => '',
],
[
'num_days' => 14,
'name' => '',
],
[
'num_days' => 15,
'name' => '',
],
[
'num_days' => 30,
'name' => '',
],
[
'num_days' => 60,
'name' => '',
],
[
'num_days' => 90,
'name' => '',
]
]));
define('GATEWAY_TYPE_CREDIT_CARD', 1);
define('GATEWAY_TYPE_BANK_TRANSFER', 2);
define('GATEWAY_TYPE_PAYPAL', 3);
define('GATEWAY_TYPE_BITCOIN', 4);
define('GATEWAY_TYPE_DWOLLA', 5);
define('GATEWAY_TYPE_CUSTOM1', 6);
define('GATEWAY_TYPE_ALIPAY', 7);
define('GATEWAY_TYPE_SOFORT', 8);
define('GATEWAY_TYPE_SEPA', 9);
define('GATEWAY_TYPE_GOCARDLESS', 10);
define('GATEWAY_TYPE_APPLE_PAY', 11);
define('GATEWAY_TYPE_CUSTOM2', 12);
define('GATEWAY_TYPE_CUSTOM3', 13);
define('GATEWAY_TYPE_TOKEN', 'token');

View File

@ -0,0 +1,28 @@
<?php
namespace App\DataMapper;
/**
* ClientSettings
*/
class BaseSettings
{
/**
* Migrates properties of the datamapper classes when new properties are added
*
* @param \stdClass $object Datamapper settings object
* @return \stdClass $object Datamapper settings object updated
*/
public function migrate(\stdClass $object) : \stdClass
{
$properties = self::default();
foreach($properties as $property)
{
if(!property_exists($object, $property))
$object->{$property} = NULL;
}
return $object;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\DataMapper;
/**
* ClientSettings
*/
class ClientSettings extends BaseSettings
{
public $timezone_id;
public $language_id;
public $currency_id;
/**
* @return \stdClass
*
*/
public static function defaults() : \stdClass
{
return (object)[
'timezone_id' => NULL,
'language_id' => NULL,
'currency_id' => NULL,
'payment_terms' => NULL,
];
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace App\DataMapper;
/**
* CompanySettings
*/
class CompanySettings extends BaseSettings
{
public $timezone_id;
public $language_id;
public $currency_id;
public $payment_terms;
public $custom_label1;
public $custom_value1;
public $custom_label2;
public $custom_value2;
public $custom_label3;
public $custom_value3;
public $custom_label4;
public $custom_value5;
public $custom_client_label1;
public $custom_client_label2;
public $custom_client_label3;
public $custom_client_label4;
public $custom_client_contact_label1;
public $custom_client_contact_label2;
public $custom_client_contact_label3;
public $custom_client_contact_label4;
public $custom_invoice_label1;
public $custom_invoice_label2;
public $custom_invoice_label3;
public $custom_invoice_label4;
public $custom_product_label1;
public $custom_product_label2;
public $custom_product_label3;
public $custom_product_label4;
public $custom_task_label1;
public $custom_task_label2;
public $custom_task_label3;
public $custom_task_label4;
public $custom_expense_label1;
public $custom_expense_label2;
public $custom_expense_label3;
public $custom_expense_label4;
public $translations;
/**
* Cast object values and return entire class
* prevents missing properties from not being returned
* and always ensure an up to date class is returned
*
* @return \stdClass
*/
public function __construct($obj)
{
foreach($obj as $key => $value)
$this->{$key} = $value;
}
/**
* Provides class defaults on init
* @return object
*/
public static function defaults() : object
{
$config = json_decode(config('ninja.settings'));
return (object) [
'timezone_id' => $config->timezone_id,
'language_id' => $config->language_id,
'currency_id' => $config->currency_id,
'payment_terms' => $config->payment_terms,
'translations' => (object) [],
];
}
}

View File

@ -8,7 +8,7 @@ use App\Models\Client;
* Class DefaultSettings
* @package App\DataMapper
*/
class DefaultSettings
class DefaultSettings extends BaseSettings
{
/**

View File

@ -2,6 +2,7 @@
namespace App\Factory;
use App\DataMapper\ClientSettings;
use App\Models\Client;
class ClientFactory
@ -18,7 +19,8 @@ class ClientFactory
$client->paid_to_date = 0;
$client->country_id = 4;
$client->is_deleted = 0;
$client->settings = ClientSettings::defaults();
$client_contact = ClientContactFactory::create($company_id, $user_id);
$client->contacts->add($client_contact);

View File

@ -130,7 +130,7 @@ class ClientFilters extends QueryFilters
->where('client_contacts.deleted_at', '=', null)
//->whereRaw('(clients.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
->select(
DB::raw('COALESCE(clients.currency_id, companies.currency_id) currency_id'),
// DB::raw('COALESCE(clients.currency_id, companies.currency_id) currency_id'),
DB::raw('COALESCE(clients.country_id, companies.country_id) country_id'),
DB::raw("CONCAT(COALESCE(client_contacts.first_name, ''), ' ', COALESCE(client_contacts.last_name, '')) contact"),
'clients.id',
@ -151,7 +151,8 @@ class ClientFilters extends QueryFilters
'clients.deleted_at',
'clients.is_deleted',
'clients.user_id',
'clients.id_number'
'clients.id_number',
'clients.settings'
);
/**

View File

@ -16,11 +16,15 @@ use App\Jobs\Entity\ActionEntity;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Country;
use App\Models\Currency;
use App\Models\Size;
use App\Repositories\ClientRepository;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesMenu;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
/**
* Class ClientController
@ -121,11 +125,11 @@ class ClientController extends Controller
$data = [
'client' => $client,
'settings' => $client,
'settings' => collect($client->settings),
'pills' => $this->makeEntityTabMenu(Client::class),
'hashed_id' => $this->encodePrimarykey($client->id),
'countries' => Country::all(),
'company' => auth()->user()->company()
'company' => auth()->user()->company(),
'sizes' => Size::all(),
];
return view('client.edit', $data);

View File

@ -33,10 +33,14 @@ class TranslationController extends Controller
});
header('Content-Type: text/javascript');
echo('i18n = ' . json_encode($strings) . ';');
echo('i18n = ' . $this->easyMinify(json_encode($strings)) . ';');
exit();
}
private function easyMinify($javascript){
return preg_replace(array("/\s+\n/", "/\n\s+/", "/ +/"), array("\n", "\n ", " "), $javascript);
}
/**
* Show the form for creating a new resource.
*

View File

@ -35,6 +35,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\StartupCheck::class,
],
'api' => [

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Closure;
/**
* Class StartupCheck.
*/
class StartupCheck
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$cached_tables = unserialize(CACHED_TABLES);
if (Input::has('clear_cache')) {
Session::flash('message', 'Cache cleared');
}
foreach ($cached_tables as $name => $class) {
if (Input::has('clear_cache') || ! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'paymentTerms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
$response = $next($request);
return $response;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace App\Http\ViewComposers;
use App\Models\Country;
use App\Models\Currency;
use App\Models\PaymentTerm;
use Cache;
use Illuminate\Support\Str;
use Illuminate\View\View;
class TranslationComposer
{
/**
* Bind data to the view.
*
* @param View $view
*
* @return void
*/
public function compose(View $view) :void
{
$view->with('industries', Cache::get('industries')->each(function ($industry) {
$industry->name = trans('texts.industry_'.$industry->name);
})->sortBy(function ($industry) {
return $industry->name;
}));
$view->with('countries', Cache::get('countries')->each(function ($country) {
$country->name = trans('texts.country_'.$country->name);
})->sortBy(function ($country) {
return $country->name;
}));
$view->with('payment_types', Cache::get('paymentTypes')->each(function ($pType) {
$pType->name = trans('texts.payment_type_'.$pType->name);
})->sortBy(function ($pType) {
return $pType->name;
}));
$view->with('languages', Cache::get('languages')->each(function ($lang) {
$lang->name = trans('texts.lang_'.$lang->name);
})->sortBy(function ($lang) {
return $lang->name;
}));
$view->with('currencies', Cache::get('currencies')->each(function ($currency) {
$currency->name = trans('texts.currency_' . Str::slug($currency->name, '_'));
})->sortBy(function ($currency) {
return $currency->name;
}));
$view->with('payment_terms', PaymentTerm::getCompanyTerms()->map(function ($term){
$term['name'] = trans('texts.payment_terms_net') . ' ' . $term['num_days'];
return $term;
}));
}
}

View File

@ -3,11 +3,14 @@
namespace App\Models;
use App\Filters\QueryFilters;
use App\Utils\Traits\UserSessionAttributes;
use Hashids\Hashids;
use Illuminate\Database\Eloquent\Model;
class BaseModel extends Model
{
use UserSessionAttributes;
public function __call($method, $params)
{
$entity = strtolower(class_basename($this));
@ -25,4 +28,11 @@ class BaseModel extends Model
return parent::__call($method, $params);
}
public function scopeScope($query)
{
$query->where($this->getTable() .'.company_id', '=', $this->getCurrentCompanyId());
return $query;
}
}

View File

@ -31,10 +31,13 @@ class Client extends BaseModel
'country',
'shipping_country'
];
protected $with = ['contacts', 'primary_contact', 'country', 'shipping_country'];
protected $casts = [
'settings' => 'object'
];
//protected $dates = ['deleted_at'];
public function getHashedIdAttribute()

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\DataMapper\CompanySettings;
use App\Models\Account;
use App\Models\AccountGateway;
use App\Models\Client;
@ -34,7 +35,16 @@ class Company extends BaseModel
'company_id'
];
//protected $appends = ['company_id'];
protected $appends = ['settings_object'];
protected $casts = [
'settings' => 'object'
];
public function getSettingsObjectAttribute()
{
return new CompanySettings($this->settings);
}
public function getRouteKeyName()
{

View File

@ -0,0 +1,63 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class PaymentTerm.
*/
class PaymentTerm extends BaseModel
{
use SoftDeletes;
/**
* @var bool
*/
public $timestamps = true;
/**
* @var array
*/
protected $dates = ['deleted_at'];
public function getNumDays()
{
return $this->num_days == -1 ? 0 : $this->num_days;
}
public static function getCompanyTerms()
{
$default_terms = collect(unserialize(CACHED_PAYMENT_TERMS));
$terms = self::scope()->get();
$terms->map(function($term) {
return $term['num_days'];
});
$default_terms->merge($terms)
->sort()
->values()
->all();
return $default_terms;
}
public static function getSelectOptions()
{
/*
$terms = PaymentTerm::whereAccountId(0)->get();
foreach (PaymentTerm::scope()->get() as $term) {
$terms->push($term);
}
foreach ($terms as $term) {
$term->name = trans('texts.payment_terms_net') . ' ' . $term->getNumDays();
}
return $terms->sortBy('num_days');
*/
}
}

16
app/Models/Size.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
/**
* Class Size.
*/
class Size extends BaseModel
{
/**
* @var bool
*/
public $timestamps = false;
}

View File

@ -2,7 +2,6 @@
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
@ -14,7 +13,14 @@ class ComposerServiceProvider extends ServiceProvider
*/
public function boot()
{
View::composer('*', 'App\Http\ViewComposers\HeaderComposer');
view()->composer('*', 'App\Http\ViewComposers\HeaderComposer');
view()->composer(
[
'client.edit',
],
'App\Http\ViewComposers\TranslationComposer'
);
}
/**

179
composer.lock generated
View File

@ -865,16 +865,16 @@
},
{
"name": "laravel/framework",
"version": "v5.7.24",
"version": "v5.7.26",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "2ede55db4b8201ed0450fa7e7a4d7220aa29bc34"
"reference": "ca3bc9769969e8af3bd9878a3e0242051c74dae4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/2ede55db4b8201ed0450fa7e7a4d7220aa29bc34",
"reference": "2ede55db4b8201ed0450fa7e7a4d7220aa29bc34",
"url": "https://api.github.com/repos/laravel/framework/zipball/ca3bc9769969e8af3bd9878a3e0242051c74dae4",
"reference": "ca3bc9769969e8af3bd9878a3e0242051c74dae4",
"shasum": ""
},
"require": {
@ -1007,7 +1007,7 @@
"framework",
"laravel"
],
"time": "2019-01-29T22:13:46+00:00"
"time": "2019-02-12T14:52:21+00:00"
},
{
"name": "laravel/nexmo-notification-channel",
@ -1592,16 +1592,16 @@
},
{
"name": "nexmo/client",
"version": "1.6.1",
"version": "1.6.2",
"source": {
"type": "git",
"url": "https://github.com/Nexmo/nexmo-php.git",
"reference": "3dc03ca1dab726a23b757110897740e54304fc65"
"reference": "2f79f67f24225ea627ee14578e98c96276cdd4c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Nexmo/nexmo-php/zipball/3dc03ca1dab726a23b757110897740e54304fc65",
"reference": "3dc03ca1dab726a23b757110897740e54304fc65",
"url": "https://api.github.com/repos/Nexmo/nexmo-php/zipball/2f79f67f24225ea627ee14578e98c96276cdd4c5",
"reference": "2f79f67f24225ea627ee14578e98c96276cdd4c5",
"shasum": ""
},
"require": {
@ -1636,7 +1636,7 @@
}
],
"description": "PHP Client for using Nexmo's API.",
"time": "2019-01-02T09:06:47+00:00"
"time": "2019-02-07T11:14:34+00:00"
},
{
"name": "nikic/php-parser",
@ -2600,16 +2600,16 @@
},
{
"name": "symfony/console",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "b0a03c1bb0fcbe288629956cf2f1dd3f1dc97522"
"reference": "1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/b0a03c1bb0fcbe288629956cf2f1dd3f1dc97522",
"reference": "b0a03c1bb0fcbe288629956cf2f1dd3f1dc97522",
"url": "https://api.github.com/repos/symfony/console/zipball/1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4",
"reference": "1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4",
"shasum": ""
},
"require": {
@ -2621,6 +2621,9 @@
"symfony/dependency-injection": "<3.4",
"symfony/process": "<3.3"
},
"provide": {
"psr/log-implementation": "1.0"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~3.4|~4.0",
@ -2630,7 +2633,7 @@
"symfony/process": "~3.4|~4.0"
},
"suggest": {
"psr/log-implementation": "For using the console logger",
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
@ -2665,7 +2668,7 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2019-01-04T15:13:53+00:00"
"time": "2019-01-25T14:35:16+00:00"
},
{
"name": "symfony/contracts",
@ -2737,16 +2740,16 @@
},
{
"name": "symfony/css-selector",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "76dac1dbe2830213e95892c7c2ec1edd74113ea4"
"reference": "48eddf66950fa57996e1be4a55916d65c10c604a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/76dac1dbe2830213e95892c7c2ec1edd74113ea4",
"reference": "76dac1dbe2830213e95892c7c2ec1edd74113ea4",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/48eddf66950fa57996e1be4a55916d65c10c604a",
"reference": "48eddf66950fa57996e1be4a55916d65c10c604a",
"shasum": ""
},
"require": {
@ -2786,20 +2789,20 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2019-01-03T09:07:35+00:00"
"time": "2019-01-16T20:31:39+00:00"
},
{
"name": "symfony/debug",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "64cb33c81e37d19b7715d4a6a4d49c1c382066dd"
"reference": "cf9b2e33f757deb884ce474e06d2647c1c769b65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/64cb33c81e37d19b7715d4a6a4d49c1c382066dd",
"reference": "64cb33c81e37d19b7715d4a6a4d49c1c382066dd",
"url": "https://api.github.com/repos/symfony/debug/zipball/cf9b2e33f757deb884ce474e06d2647c1c769b65",
"reference": "cf9b2e33f757deb884ce474e06d2647c1c769b65",
"shasum": ""
},
"require": {
@ -2842,20 +2845,20 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2019-01-03T09:07:35+00:00"
"time": "2019-01-25T14:35:16+00:00"
},
{
"name": "symfony/event-dispatcher",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "887de6d34c86cf0cb6cbf910afb170cdb743cb5e"
"reference": "bd09ad265cd50b2b9d09d65ce6aba2d29bc81fe1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/887de6d34c86cf0cb6cbf910afb170cdb743cb5e",
"reference": "887de6d34c86cf0cb6cbf910afb170cdb743cb5e",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bd09ad265cd50b2b9d09d65ce6aba2d29bc81fe1",
"reference": "bd09ad265cd50b2b9d09d65ce6aba2d29bc81fe1",
"shasum": ""
},
"require": {
@ -2906,20 +2909,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2019-01-05T16:37:49+00:00"
"time": "2019-01-16T20:35:37+00:00"
},
{
"name": "symfony/finder",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "9094d69e8c6ee3fe186a0ec5a4f1401e506071ce"
"reference": "ef71816cbb264988bb57fe6a73f610888b9aa70c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/9094d69e8c6ee3fe186a0ec5a4f1401e506071ce",
"reference": "9094d69e8c6ee3fe186a0ec5a4f1401e506071ce",
"url": "https://api.github.com/repos/symfony/finder/zipball/ef71816cbb264988bb57fe6a73f610888b9aa70c",
"reference": "ef71816cbb264988bb57fe6a73f610888b9aa70c",
"shasum": ""
},
"require": {
@ -2955,20 +2958,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2019-01-03T09:07:35+00:00"
"time": "2019-01-16T20:35:37+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "a633d422a09242064ba24e44a6e1494c5126de86"
"reference": "8d2318b73e0a1bc75baa699d00ebe2ae8b595a39"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/a633d422a09242064ba24e44a6e1494c5126de86",
"reference": "a633d422a09242064ba24e44a6e1494c5126de86",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/8d2318b73e0a1bc75baa699d00ebe2ae8b595a39",
"reference": "8d2318b73e0a1bc75baa699d00ebe2ae8b595a39",
"shasum": ""
},
"require": {
@ -3009,20 +3012,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2019-01-05T16:37:49+00:00"
"time": "2019-01-29T09:49:29+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "83de6543328917c18d5498eeb6bb6d36f7aab31b"
"reference": "d56b1706abaa771eb6acd894c6787cb88f1dc97d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/83de6543328917c18d5498eeb6bb6d36f7aab31b",
"reference": "83de6543328917c18d5498eeb6bb6d36f7aab31b",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/d56b1706abaa771eb6acd894c6787cb88f1dc97d",
"reference": "d56b1706abaa771eb6acd894c6787cb88f1dc97d",
"shasum": ""
},
"require": {
@ -3098,7 +3101,7 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"time": "2019-01-06T16:19:23+00:00"
"time": "2019-02-03T12:47:33+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -3274,16 +3277,16 @@
},
{
"name": "symfony/process",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "ea043ab5d8ed13b467a9087d81cb876aee7f689a"
"reference": "6c05edb11fbeff9e2b324b4270ecb17911a8b7ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/ea043ab5d8ed13b467a9087d81cb876aee7f689a",
"reference": "ea043ab5d8ed13b467a9087d81cb876aee7f689a",
"url": "https://api.github.com/repos/symfony/process/zipball/6c05edb11fbeff9e2b324b4270ecb17911a8b7ad",
"reference": "6c05edb11fbeff9e2b324b4270ecb17911a8b7ad",
"shasum": ""
},
"require": {
@ -3319,20 +3322,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2019-01-03T14:48:52+00:00"
"time": "2019-01-24T22:05:03+00:00"
},
{
"name": "symfony/routing",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "e69b7a13a0b58af378a49b49dd7084462de16cee"
"reference": "7f8e44fc498972466f0841c3e48dc555f23bdf53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/e69b7a13a0b58af378a49b49dd7084462de16cee",
"reference": "e69b7a13a0b58af378a49b49dd7084462de16cee",
"url": "https://api.github.com/repos/symfony/routing/zipball/7f8e44fc498972466f0841c3e48dc555f23bdf53",
"reference": "7f8e44fc498972466f0841c3e48dc555f23bdf53",
"shasum": ""
},
"require": {
@ -3396,20 +3399,20 @@
"uri",
"url"
],
"time": "2019-01-03T09:07:35+00:00"
"time": "2019-01-29T09:49:29+00:00"
},
{
"name": "symfony/translation",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "939fb792d73f2ce80e6ae9019d205fc480f1c9a0"
"reference": "23fd7aac70d99a17a8e6473a41fec8fab3331050"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/939fb792d73f2ce80e6ae9019d205fc480f1c9a0",
"reference": "939fb792d73f2ce80e6ae9019d205fc480f1c9a0",
"url": "https://api.github.com/repos/symfony/translation/zipball/23fd7aac70d99a17a8e6473a41fec8fab3331050",
"reference": "23fd7aac70d99a17a8e6473a41fec8fab3331050",
"shasum": ""
},
"require": {
@ -3469,20 +3472,20 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2019-01-03T09:07:35+00:00"
"time": "2019-01-27T23:11:39+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v4.2.2",
"version": "v4.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "85bde661b178173d85c6f11ea9d03b61d1212bb2"
"reference": "223bda89f9be41cf7033eeaf11bc61a280489c17"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/85bde661b178173d85c6f11ea9d03b61d1212bb2",
"reference": "85bde661b178173d85c6f11ea9d03b61d1212bb2",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/223bda89f9be41cf7033eeaf11bc61a280489c17",
"reference": "223bda89f9be41cf7033eeaf11bc61a280489c17",
"shasum": ""
},
"require": {
@ -3545,7 +3548,7 @@
"debug",
"dump"
],
"time": "2019-01-03T09:07:35+00:00"
"time": "2019-01-30T11:44:30+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@ -3799,16 +3802,16 @@
"packages-dev": [
{
"name": "barryvdh/laravel-debugbar",
"version": "v3.2.1",
"version": "v3.2.2",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
"reference": "9d5caf43c5f3a3aea2178942f281054805872e7c"
"reference": "ba046deba51f3899963c7d09840bf623c4ebf5ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/9d5caf43c5f3a3aea2178942f281054805872e7c",
"reference": "9d5caf43c5f3a3aea2178942f281054805872e7c",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/ba046deba51f3899963c7d09840bf623c4ebf5ed",
"reference": "ba046deba51f3899963c7d09840bf623c4ebf5ed",
"shasum": ""
},
"require": {
@ -3863,7 +3866,7 @@
"profiler",
"webprofiler"
],
"time": "2018-11-09T08:37:55+00:00"
"time": "2019-02-04T10:23:43+00:00"
},
{
"name": "beyondcode/laravel-dump-server",
@ -4323,16 +4326,16 @@
},
{
"name": "mockery/mockery",
"version": "1.2.0",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "100633629bf76d57430b86b7098cd6beb996a35a"
"reference": "0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/100633629bf76d57430b86b7098cd6beb996a35a",
"reference": "100633629bf76d57430b86b7098cd6beb996a35a",
"url": "https://api.github.com/repos/mockery/mockery/zipball/0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2",
"reference": "0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2",
"shasum": ""
},
"require": {
@ -4341,7 +4344,7 @@
"php": ">=5.6.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7.10|~6.5|~7.0"
"phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0"
},
"type": "library",
"extra": {
@ -4384,7 +4387,7 @@
"test double",
"testing"
],
"time": "2018-10-02T21:52:37+00:00"
"time": "2019-02-13T09:37:52+00:00"
},
{
"name": "myclabs/deep-copy",
@ -5069,16 +5072,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.5.3",
"version": "7.5.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "2cb759721e53bc05f56487f628c6b9fbb6c18746"
"reference": "23a200a60552cb9ba483a8d1e106c70fb0be0bb9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2cb759721e53bc05f56487f628c6b9fbb6c18746",
"reference": "2cb759721e53bc05f56487f628c6b9fbb6c18746",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/23a200a60552cb9ba483a8d1e106c70fb0be0bb9",
"reference": "23a200a60552cb9ba483a8d1e106c70fb0be0bb9",
"shasum": ""
},
"require": {
@ -5149,7 +5152,7 @@
"testing",
"xunit"
],
"time": "2019-02-01T05:24:07+00:00"
"time": "2019-02-15T14:00:34+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -5262,23 +5265,23 @@
},
{
"name": "sebastian/diff",
"version": "3.0.1",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "366541b989927187c4ca70490a35615d3fef2dce"
"reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce",
"reference": "366541b989927187c4ca70490a35615d3fef2dce",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
"reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.0",
"phpunit/phpunit": "^7.5 || ^8.0",
"symfony/process": "^2 || ^3.3 || ^4"
},
"type": "library",
@ -5314,7 +5317,7 @@
"unidiff",
"unified diff"
],
"time": "2018-06-10T07:54:39+00:00"
"time": "2019-02-04T06:01:07+00:00"
},
{
"name": "sebastian/environment",

View File

@ -54,4 +54,6 @@ return [
'from_name' => env('MAIL_FROM_NAME'),
],
'settings' => env('COMPANY_SETTINGS', '{"timezone_id":"15","currency_id":"1","language_id":"1","payment_terms":"7"}'),
];

View File

@ -1,5 +1,6 @@
<?php
use App\DataMapper\ClientSettings;
use Faker\Generator as Faker;
$factory->define(App\Models\Client::class, function (Faker $faker) {
@ -26,5 +27,6 @@ $factory->define(App\Models\Client::class, function (Faker $faker) {
'shipping_state' => $faker->state,
'shipping_postal_code' => $faker->postcode,
'shipping_country_id' => 4,
'settings' => ClientSettings::defaults(),
];
});

View File

@ -1,5 +1,6 @@
<?php
use App\DataMapper\CompanySettings;
use Faker\Generator as Faker;
$factory->define(App\Models\Company::class, function (Faker $faker) {
@ -8,5 +9,6 @@ $factory->define(App\Models\Company::class, function (Faker $faker) {
'company_key' => strtolower(str_random(RANDOM_KEY_LENGTH)),
'ip' => $faker->ipv4,
'db' => config('database.default'),
'settings' => CompanySettings::defaults(),
];
});

View File

@ -44,6 +44,8 @@ class CreateUsersTable extends Migration
Schema::create('payment_types', function ($table) {
$table->increments('id');
$table->string('name');
$table->integer('gateway_type_id');
$table->timestamps();
});
Schema::create('timezones', function ($table) {
@ -73,6 +75,7 @@ class CreateUsersTable extends Migration
Schema::create('industries', function ($table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('gateways', function ($table) {
@ -126,9 +129,8 @@ class CreateUsersTable extends Migration
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->unsignedInteger('timezone_id')->nullable();
$table->unsignedInteger('account_id')->index();
$table->unsignedInteger('currency_id')->nullable();
$table->unsignedInteger('industry_id')->nullable();
$table->string('ip');
$table->string('company_key',100)->unique();
$table->timestamp('last_login')->nullable();
@ -140,70 +142,20 @@ class CreateUsersTable extends Migration
$table->string('work_phone')->nullable();
$table->string('work_email')->nullable();
$table->unsignedInteger('country_id')->nullable();
$table->unsignedInteger('industry_id')->nullable();
$table->unsignedInteger('size_id')->nullable();
$table->string('subdomain')->nullable();
$table->string('db')->nullable();
$table->string('custom_label1')->nullable();
$table->string('custom_value1')->nullable();
$table->string('custom_label2')->nullable();
$table->string('custom_value2')->nullable();
$table->string('custom_label3')->nullable();
$table->string('custom_value3')->nullable();
$table->string('custom_label4')->nullable();
$table->string('custom_value4')->nullable();
$table->string('custom_client_label1')->nullable();
$table->string('custom_client_label2')->nullable();
$table->string('custom_client_label3')->nullable();
$table->string('custom_client_label4')->nullable();
$table->string('custom_client_contact_label1')->nullable();
$table->string('custom_client_contact_label2')->nullable();
$table->string('custom_client_contact_label3')->nullable();
$table->string('custom_client_contact_label4')->nullable();
$table->string('custom_invoice_label1')->nullable();
$table->string('custom_invoice_label2')->nullable();
$table->string('custom_invoice_label3')->nullable();
$table->string('custom_invoice_label4')->nullable();
$table->string('custom_product_label1')->nullable();
$table->string('custom_product_label2')->nullable();
$table->string('custom_product_label3')->nullable();
$table->string('custom_product_label4')->nullable();
$table->string('custom_task_label1')->nullable();
$table->string('custom_task_label2')->nullable();
$table->string('custom_task_label3')->nullable();
$table->string('custom_task_label4')->nullable();
$table->string('custom_expense_label1')->nullable();
$table->string('custom_expense_label2')->nullable();
$table->string('custom_expense_label3')->nullable();
$table->string('custom_expense_label4')->nullable();
$table->string('vat_number')->nullable();
$table->string('id_number')->nullable();
$table->unsignedInteger('size_id')->nullable();
$table->text('settings');
$table->text('translations')->nullable();
$table->unsignedInteger('language_id')->default(1);
$table->timestamps();
$table->softDeletes();
$table->foreign('timezone_id')->references('id')->on('timezones');
$table->foreign('country_id')->references('id')->on('countries');
$table->foreign('currency_id')->references('id')->on('currencies');
$table->foreign('industry_id')->references('id')->on('industries');
$table->foreign('size_id')->references('id')->on('sizes');
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('language_id')->references('id')->on('languages');
});
@ -296,11 +248,8 @@ class CreateUsersTable extends Migration
$table->string('shipping_state')->nullable();
$table->string('shipping_postal_code')->nullable();
$table->unsignedInteger('shipping_country_id')->nullable();
$table->decimal('latitude', 11, 8)->nullable();
$table->decimal('longitude', 11, 8)->nullable();
$table->text('settings');
$table->decimal('shipping_latitude', 11, 8)->nullable();
$table->decimal('shipping_longitude', 11, 8)->nullable();
$table->boolean('is_deleted')->default(false);
$table->string('payment_terms')->nullable(); //todo type? depends how we are storing this
@ -605,6 +554,19 @@ class CreateUsersTable extends Migration
});
Schema::create('payment_terms', function ($table) {
$table->increments('id');
$table->integer('num_days');
$table->string('name');
$table->unsignedInteger('company_id');
$table->unsignedInteger('user_id');
$table->timestamps();
$table->softDeletes();
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
/**

View File

@ -1,5 +1,7 @@
<?php
use App\Models\PaymentLibrary;
use App\Models\Size;
use App\Models\Timezone;
use Illuminate\Database\Seeder;
@ -8,7 +10,15 @@ class ConstantsSeeder extends Seeder
public function run()
{
\App\Models\PaymentLibrary::create(['name' => 'Omnipay']);
Size::create(['name' => '1 - 3']);
Size::create(['name' => '4 - 10']);
Size::create(['name' => '11 - 50']);
Size::create(['name' => '51 - 100']);
Size::create(['name' => '101 - 500']);
Size::create(['name' => '500+']);
PaymentLibrary::create(['name' => 'Omnipay']);
/*
d, dd: Numeric date, no leading zero and leading zero, respectively. Eg, 5, 05.
@ -18,119 +28,119 @@ class ConstantsSeeder extends Seeder
yy, yyyy: 2- and 4-digit years, respectively. Eg, 12, 2012.)
*/
$timezones[] = ['name'=>'Pacific/Midway', 'location', 'location' => '(GMT-11:00) Midway Island', 'utc_offset' => -39600];
$timezones[] = ['name'=>'US/Samoa', 'location' => '(GMT-11:00) Samoa', 'utc_offset' => -39600];
$timezones[] = ['name'=>'US/Hawaii', 'location' => '(GMT-10:00) Hawaii', 'utc_offset' => -36000];
$timezones[] = ['name'=>'US/Alaska', 'location' => '(GMT-09:00) Alaska', 'utc_offset' => -32400];
$timezones[] = ['name'=>'US/Pacific', 'location' => '(GMT-08:00) Pacific Time (US &amp; Canada)', 'utc_offset' => -28800];
$timezones[] = ['name'=>'America/Tijuana', 'location' => '(GMT-08:00) Tijuana', 'utc_offset' => -28800];
$timezones[] = ['name'=>'US/Arizona', 'location' => '(GMT-07:00) Arizona', 'utc_offset' => -25200];
$timezones[] = ['name'=>'US/Mountain', 'location' => '(GMT-07:00) Mountain Time (US &amp; Canada)', 'utc_offset' => -25200];
$timezones[] = ['name'=>'America/Chihuahua', 'location' => '(GMT-07:00) Chihuahua', 'utc_offset' => -25200];
$timezones[] = ['name'=>'America/Mazatlan', 'location' => '(GMT-07:00) Mazatlan', 'utc_offset' => -25200];
$timezones[] = ['name'=>'America/Mexico_City', 'location' => '(GMT-06:00) Mexico City', 'utc_offset' => -21600];
$timezones[] = ['name'=>'America/Monterrey', 'location' => '(GMT-06:00) Monterrey', 'utc_offset' => -21600];
$timezones[] = ['name'=>'Canada/Saskatchewan', 'location' => '(GMT-06:00) Saskatchewan', 'utc_offset' => -21600];
$timezones[] = ['name'=>'US/Central', 'location' => '(GMT-06:00) Central Time (US &amp; Canada)', 'utc_offset' => -21600];
$timezones[] = ['name'=>'US/Eastern', 'location' => '(GMT-05:00) Eastern Time (US &amp; Canada)', 'utc_offset' => -18000];
$timezones[] = ['name'=>'US/East-Indiana', 'location' => '(GMT-05:00) Indiana (East)', 'utc_offset' => -18000];
$timezones[] = ['name'=>'America/Bogota', 'location' => '(GMT-05:00) Bogota', 'utc_offset' => -18000];
$timezones[] = ['name'=>'America/Lima', 'location' => '(GMT-05:00) Lima', 'utc_offset' => -18000];
$timezones[] = ['name'=>'America/Caracas', 'location' => '(GMT-04:00) Caracas', 'utc_offset' => -14400];
$timezones[] = ['name'=>'Canada/Atlantic', 'location' => '(GMT-04:00) Atlantic Time (Canada)', 'utc_offset' => -14400];
$timezones[] = ['name'=>'America/La_Paz', 'location' => '(GMT-04:00) La Paz', 'utc_offset' => -14400];
$timezones[] = ['name'=>'America/Santiago', 'location' => '(GMT-04:00) Santiago', 'utc_offset' => -14400];
$timezones[] = ['name'=>'Canada/Newfoundland', 'location' => '(GMT-03:30) Newfoundland', 'utc_offset' => -12600];
$timezones[] = ['name'=>'America/Buenos_Aires', 'location' => '(GMT-03:00) Buenos Aires', 'utc_offset' => -10800];
$timezones[] = ['name'=>'America/Godthab', 'location' => '(GMT-03:00) Greenland', 'utc_offset' => -10800];
$timezones[] = ['name'=>'Atlantic/Stanley', 'location' => '(GMT-02:00) Stanley', 'utc_offset' => -7200];
$timezones[] = ['name'=>'Atlantic/Azores', 'location' => '(GMT-01:00) Azores', 'utc_offset' => -3600];
$timezones[] = ['name'=>'Atlantic/Cape_Verde', 'location' => '(GMT-01:00) Cape Verde Is.', 'utc_offset' => -3600];
$timezones[] = ['name'=>'Africa/Casablanca', 'location' => '(GMT) Casablanca', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/Dublin', 'location' => '(GMT) Dublin', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/Lisbon', 'location' => '(GMT) Lisbon', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/London', 'location' => '(GMT) London', 'utc_offset' => 0];
$timezones[] = ['name'=>'Africa/Monrovia', 'location' => '(GMT) Monrovia', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/Amsterdam', 'location' => '(GMT+01:00) Amsterdam', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Belgrade', 'location' => '(GMT+01:00) Belgrade', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Berlin', 'location' => '(GMT+01:00) Berlin', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Bratislava', 'location' => '(GMT+01:00) Bratislava', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Brussels', 'location' => '(GMT+01:00) Brussels', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Budapest', 'location' => '(GMT+01:00) Budapest', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Copenhagen', 'location' => '(GMT+01:00) Copenhagen', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Ljubljana', 'location' => '(GMT+01:00) Ljubljana', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Madrid', 'location' => '(GMT+01:00) Madrid', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Paris', 'location' => '(GMT+01:00) Paris', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Prague', 'location' => '(GMT+01:00) Prague', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Rome', 'location' => '(GMT+01:00) Rome', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Sarajevo', 'location' => '(GMT+01:00) Sarajevo', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Skopje', 'location' => '(GMT+01:00) Skopje', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Stockholm', 'location' => '(GMT+01:00) Stockholm', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Vienna', 'location' => '(GMT+01:00) Vienna', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Warsaw', 'location' => '(GMT+01:00) Warsaw', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Zagreb', 'location' => '(GMT+01:00) Zagreb', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Athens', 'location' => '(GMT+02:00) Athens', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Bucharest', 'location' => '(GMT+02:00) Bucharest', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Africa/Cairo', 'location' => '(GMT+02:00) Cairo', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Africa/Harare', 'location' => '(GMT+02:00) Harare', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Helsinki', 'location' => '(GMT+02:00) Helsinki', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Istanbul', 'location' => '(GMT+02:00) Istanbul', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Asia/Jerusalem', 'location' => '(GMT+02:00) Jerusalem', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Kiev', 'location' => '(GMT+02:00) Kyiv', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Minsk', 'location' => '(GMT+02:00) Minsk', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Riga', 'location' => '(GMT+02:00) Riga', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Sofia', 'location' => '(GMT+02:00) Sofia', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Tallinn', 'location' => '(GMT+02:00) Tallinn', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Vilnius', 'location' => '(GMT+02:00) Vilnius', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Asia/Baghdad', 'location' => '(GMT+03:00) Baghdad', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Asia/Kuwait', 'location' => '(GMT+03:00) Kuwait', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Africa/Nairobi', 'location' => '(GMT+03:00) Nairobi', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Asia/Riyadh', 'location' => '(GMT+03:00) Riyadh', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Asia/Tehran', 'location' => '(GMT+03:30) Tehran', 'utc_offset' => 12600];
$timezones[] = ['name'=>'Europe/Moscow', 'location' => '(GMT+04:00) Moscow', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Baku', 'location' => '(GMT+04:00) Baku', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Europe/Volgograd', 'location' => '(GMT+04:00) Volgograd', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Muscat', 'location' => '(GMT+04:00) Muscat', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Tbilisi', 'location' => '(GMT+04:00) Tbilisi', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Yerevan', 'location' => '(GMT+04:00) Yerevan', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Kabul', 'location' => '(GMT+04:30) Kabul', 'utc_offset' => 16200];
$timezones[] = ['name'=>'Asia/Karachi', 'location' => '(GMT+05:00) Karachi', 'utc_offset' => 18000];
$timezones[] = ['name'=>'Asia/Tashkent', 'location' => '(GMT+05:00) Tashkent', 'utc_offset' => 18000];
$timezones[] = ['name'=>'Asia/Kolkata', 'location' => '(GMT+05:30) Kolkata', 'utc_offset' => 19800];
$timezones[] = ['name'=>'Asia/Kathmandu', 'location' => '(GMT+05:45) Kathmandu', 'utc_offset' => 20700];
$timezones[] = ['name'=>'Asia/Yekaterinburg', 'location' => '(GMT+06:00) Ekaterinburg', 'utc_offset' => 21600];
$timezones[] = ['name'=>'Asia/Almaty', 'location' => '(GMT+06:00) Almaty', 'utc_offset' => 21600];
$timezones[] = ['name'=>'Asia/Dhaka', 'location' => '(GMT+06:00) Dhaka', 'utc_offset' => 21600];
$timezones[] = ['name'=>'Asia/Novosibirsk', 'location' => '(GMT+07:00) Novosibirsk', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Bangkok', 'location' => '(GMT+07:00) Bangkok', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Ho_Chi_Minh', 'location' => '(GMT+07.00) Ho Chi Minh', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Jakarta', 'location' => '(GMT+07:00) Jakarta', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Krasnoyarsk', 'location' => '(GMT+08:00) Krasnoyarsk', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Chongqing', 'location' => '(GMT+08:00) Chongqing', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Hong_Kong', 'location' => '(GMT+08:00) Hong Kong', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Kuala_Lumpur', 'location' => '(GMT+08:00) Kuala Lumpur', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Australia/Perth', 'location' => '(GMT+08:00) Perth', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Singapore', 'location' => '(GMT+08:00) Singapore', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Taipei', 'location' => '(GMT+08:00) Taipei', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Ulaanbaatar', 'location' => '(GMT+08:00) Ulaan Bataar', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Urumqi', 'location' => '(GMT+08:00) Urumqi', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Irkutsk', 'location' => '(GMT+09:00) Irkutsk', 'utc_offset' => 32400];
$timezones[] = ['name'=>'Asia/Seoul', 'location' => '(GMT+09:00) Seoul', 'utc_offset' => 32400];
$timezones[] = ['name'=>'Asia/Tokyo', 'location' => '(GMT+09:00) Tokyo', 'utc_offset' => 32400];
$timezones[] = ['name'=>'Australia/Adelaide', 'location' => '(GMT+09:30) Adelaide', 'utc_offset' => 34200];
$timezones[] = ['name'=>'Australia/Darwin', 'location' => '(GMT+09:30) Darwin', 'utc_offset' => 34200];
$timezones[] = ['name'=>'Asia/Yakutsk', 'location' => '(GMT+10:00) Yakutsk', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Brisbane', 'location' => '(GMT+10:00) Brisbane', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Canberra', 'location' => '(GMT+10:00) Canberra', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Pacific/Guam', 'location' => '(GMT+10:00) Guam', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Hobart', 'location' => '(GMT+10:00) Hobart', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Melbourne', 'location' => '(GMT+10:00) Melbourne', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Pacific/Port_Moresby', 'location' => '(GMT+10:00) Port Moresby', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Sydney', 'location' => '(GMT+10:00) Sydney', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Asia/Vladivostok', 'location' => '(GMT+11:00) Vladivostok', 'utc_offset' => 39600];
$timezones[] = ['name'=>'Asia/Magadan', 'location' => '(GMT+12:00) Magadan', 'utc_offset' => 43200];
$timezones[] = ['name'=>'Pacific/Auckland', 'location' => '(GMT+12:00) Auckland', 'utc_offset' => 43200];
$timezones[] = ['name'=>'Pacific/Fiji', 'location' => '(GMT+12:00) Fiji', 'utc_offset' => 43200];
$timezones[] = ['name'=>'Pacific/Midway', 'location', 'location' => '(GMT-11:00) Midway Island', 'utc_offset' => -39600];
$timezones[] = ['name'=>'US/Samoa', 'location' => '(GMT-11:00) Samoa', 'utc_offset' => -39600];
$timezones[] = ['name'=>'US/Hawaii', 'location' => '(GMT-10:00) Hawaii', 'utc_offset' => -36000];
$timezones[] = ['name'=>'US/Alaska', 'location' => '(GMT-09:00) Alaska', 'utc_offset' => -32400];
$timezones[] = ['name'=>'US/Pacific', 'location' => '(GMT-08:00) Pacific Time (US &amp; Canada)', 'utc_offset' => -28800];
$timezones[] = ['name'=>'America/Tijuana', 'location' => '(GMT-08:00) Tijuana', 'utc_offset' => -28800];
$timezones[] = ['name'=>'US/Arizona', 'location' => '(GMT-07:00) Arizona', 'utc_offset' => -25200];
$timezones[] = ['name'=>'US/Mountain', 'location' => '(GMT-07:00) Mountain Time (US &amp; Canada)', 'utc_offset' => -25200];
$timezones[] = ['name'=>'America/Chihuahua', 'location' => '(GMT-07:00) Chihuahua', 'utc_offset' => -25200];
$timezones[] = ['name'=>'America/Mazatlan', 'location' => '(GMT-07:00) Mazatlan', 'utc_offset' => -25200];
$timezones[] = ['name'=>'America/Mexico_City', 'location' => '(GMT-06:00) Mexico City', 'utc_offset' => -21600];
$timezones[] = ['name'=>'America/Monterrey', 'location' => '(GMT-06:00) Monterrey', 'utc_offset' => -21600];
$timezones[] = ['name'=>'Canada/Saskatchewan', 'location' => '(GMT-06:00) Saskatchewan', 'utc_offset' => -21600];
$timezones[] = ['name'=>'US/Central', 'location' => '(GMT-06:00) Central Time (US &amp; Canada)', 'utc_offset' => -21600];
$timezones[] = ['name'=>'US/Eastern', 'location' => '(GMT-05:00) Eastern Time (US &amp; Canada)', 'utc_offset' => -18000];
$timezones[] = ['name'=>'US/East-Indiana', 'location' => '(GMT-05:00) Indiana (East)', 'utc_offset' => -18000];
$timezones[] = ['name'=>'America/Bogota', 'location' => '(GMT-05:00) Bogota', 'utc_offset' => -18000];
$timezones[] = ['name'=>'America/Lima', 'location' => '(GMT-05:00) Lima', 'utc_offset' => -18000];
$timezones[] = ['name'=>'America/Caracas', 'location' => '(GMT-04:00) Caracas', 'utc_offset' => -14400];
$timezones[] = ['name'=>'Canada/Atlantic', 'location' => '(GMT-04:00) Atlantic Time (Canada)', 'utc_offset' => -14400];
$timezones[] = ['name'=>'America/La_Paz', 'location' => '(GMT-04:00) La Paz', 'utc_offset' => -14400];
$timezones[] = ['name'=>'America/Santiago', 'location' => '(GMT-04:00) Santiago', 'utc_offset' => -14400];
$timezones[] = ['name'=>'Canada/Newfoundland', 'location' => '(GMT-03:30) Newfoundland', 'utc_offset' => -12600];
$timezones[] = ['name'=>'America/Buenos_Aires', 'location' => '(GMT-03:00) Buenos Aires', 'utc_offset' => -10800];
$timezones[] = ['name'=>'America/Godthab', 'location' => '(GMT-03:00) Greenland', 'utc_offset' => -10800];
$timezones[] = ['name'=>'Atlantic/Stanley', 'location' => '(GMT-02:00) Stanley', 'utc_offset' => -7200];
$timezones[] = ['name'=>'Atlantic/Azores', 'location' => '(GMT-01:00) Azores', 'utc_offset' => -3600];
$timezones[] = ['name'=>'Atlantic/Cape_Verde', 'location' => '(GMT-01:00) Cape Verde Is.', 'utc_offset' => -3600];
$timezones[] = ['name'=>'Africa/Casablanca', 'location' => '(GMT) Casablanca', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/Dublin', 'location' => '(GMT) Dublin', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/Lisbon', 'location' => '(GMT) Lisbon', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/London', 'location' => '(GMT) London', 'utc_offset' => 0];
$timezones[] = ['name'=>'Africa/Monrovia', 'location' => '(GMT) Monrovia', 'utc_offset' => 0];
$timezones[] = ['name'=>'Europe/Amsterdam', 'location' => '(GMT+01:00) Amsterdam', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Belgrade', 'location' => '(GMT+01:00) Belgrade', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Berlin', 'location' => '(GMT+01:00) Berlin', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Bratislava', 'location' => '(GMT+01:00) Bratislava', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Brussels', 'location' => '(GMT+01:00) Brussels', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Budapest', 'location' => '(GMT+01:00) Budapest', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Copenhagen', 'location' => '(GMT+01:00) Copenhagen', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Ljubljana', 'location' => '(GMT+01:00) Ljubljana', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Madrid', 'location' => '(GMT+01:00) Madrid', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Paris', 'location' => '(GMT+01:00) Paris', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Prague', 'location' => '(GMT+01:00) Prague', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Rome', 'location' => '(GMT+01:00) Rome', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Sarajevo', 'location' => '(GMT+01:00) Sarajevo', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Skopje', 'location' => '(GMT+01:00) Skopje', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Stockholm', 'location' => '(GMT+01:00) Stockholm', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Vienna', 'location' => '(GMT+01:00) Vienna', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Warsaw', 'location' => '(GMT+01:00) Warsaw', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Zagreb', 'location' => '(GMT+01:00) Zagreb', 'utc_offset' => 3600];
$timezones[] = ['name'=>'Europe/Athens', 'location' => '(GMT+02:00) Athens', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Bucharest', 'location' => '(GMT+02:00) Bucharest', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Africa/Cairo', 'location' => '(GMT+02:00) Cairo', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Africa/Harare', 'location' => '(GMT+02:00) Harare', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Helsinki', 'location' => '(GMT+02:00) Helsinki', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Istanbul', 'location' => '(GMT+02:00) Istanbul', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Asia/Jerusalem', 'location' => '(GMT+02:00) Jerusalem', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Kiev', 'location' => '(GMT+02:00) Kyiv', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Minsk', 'location' => '(GMT+02:00) Minsk', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Riga', 'location' => '(GMT+02:00) Riga', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Sofia', 'location' => '(GMT+02:00) Sofia', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Tallinn', 'location' => '(GMT+02:00) Tallinn', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Europe/Vilnius', 'location' => '(GMT+02:00) Vilnius', 'utc_offset' => 7200];
$timezones[] = ['name'=>'Asia/Baghdad', 'location' => '(GMT+03:00) Baghdad', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Asia/Kuwait', 'location' => '(GMT+03:00) Kuwait', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Africa/Nairobi', 'location' => '(GMT+03:00) Nairobi', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Asia/Riyadh', 'location' => '(GMT+03:00) Riyadh', 'utc_offset' => 10800];
$timezones[] = ['name'=>'Asia/Tehran', 'location' => '(GMT+03:30) Tehran', 'utc_offset' => 12600];
$timezones[] = ['name'=>'Europe/Moscow', 'location' => '(GMT+04:00) Moscow', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Baku', 'location' => '(GMT+04:00) Baku', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Europe/Volgograd', 'location' => '(GMT+04:00) Volgograd', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Muscat', 'location' => '(GMT+04:00) Muscat', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Tbilisi', 'location' => '(GMT+04:00) Tbilisi', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Yerevan', 'location' => '(GMT+04:00) Yerevan', 'utc_offset' => 14400];
$timezones[] = ['name'=>'Asia/Kabul', 'location' => '(GMT+04:30) Kabul', 'utc_offset' => 16200];
$timezones[] = ['name'=>'Asia/Karachi', 'location' => '(GMT+05:00) Karachi', 'utc_offset' => 18000];
$timezones[] = ['name'=>'Asia/Tashkent', 'location' => '(GMT+05:00) Tashkent', 'utc_offset' => 18000];
$timezones[] = ['name'=>'Asia/Kolkata', 'location' => '(GMT+05:30) Kolkata', 'utc_offset' => 19800];
$timezones[] = ['name'=>'Asia/Kathmandu', 'location' => '(GMT+05:45) Kathmandu', 'utc_offset' => 20700];
$timezones[] = ['name'=>'Asia/Yekaterinburg', 'location' => '(GMT+06:00) Ekaterinburg', 'utc_offset' => 21600];
$timezones[] = ['name'=>'Asia/Almaty', 'location' => '(GMT+06:00) Almaty', 'utc_offset' => 21600];
$timezones[] = ['name'=>'Asia/Dhaka', 'location' => '(GMT+06:00) Dhaka', 'utc_offset' => 21600];
$timezones[] = ['name'=>'Asia/Novosibirsk', 'location' => '(GMT+07:00) Novosibirsk', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Bangkok', 'location' => '(GMT+07:00) Bangkok', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Ho_Chi_Minh', 'location' => '(GMT+07.00) Ho Chi Minh', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Jakarta', 'location' => '(GMT+07:00) Jakarta', 'utc_offset' => 25200];
$timezones[] = ['name'=>'Asia/Krasnoyarsk', 'location' => '(GMT+08:00) Krasnoyarsk', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Chongqing', 'location' => '(GMT+08:00) Chongqing', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Hong_Kong', 'location' => '(GMT+08:00) Hong Kong', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Kuala_Lumpur', 'location' => '(GMT+08:00) Kuala Lumpur', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Australia/Perth', 'location' => '(GMT+08:00) Perth', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Singapore', 'location' => '(GMT+08:00) Singapore', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Taipei', 'location' => '(GMT+08:00) Taipei', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Ulaanbaatar', 'location' => '(GMT+08:00) Ulaan Bataar', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Urumqi', 'location' => '(GMT+08:00) Urumqi', 'utc_offset' => 28800];
$timezones[] = ['name'=>'Asia/Irkutsk', 'location' => '(GMT+09:00) Irkutsk', 'utc_offset' => 32400];
$timezones[] = ['name'=>'Asia/Seoul', 'location' => '(GMT+09:00) Seoul', 'utc_offset' => 32400];
$timezones[] = ['name'=>'Asia/Tokyo', 'location' => '(GMT+09:00) Tokyo', 'utc_offset' => 32400];
$timezones[] = ['name'=>'Australia/Adelaide', 'location' => '(GMT+09:30) Adelaide', 'utc_offset' => 34200];
$timezones[] = ['name'=>'Australia/Darwin', 'location' => '(GMT+09:30) Darwin', 'utc_offset' => 34200];
$timezones[] = ['name'=>'Asia/Yakutsk', 'location' => '(GMT+10:00) Yakutsk', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Brisbane', 'location' => '(GMT+10:00) Brisbane', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Canberra', 'location' => '(GMT+10:00) Canberra', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Pacific/Guam', 'location' => '(GMT+10:00) Guam', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Hobart', 'location' => '(GMT+10:00) Hobart', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Melbourne', 'location' => '(GMT+10:00) Melbourne', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Pacific/Port_Moresby', 'location' => '(GMT+10:00) Port Moresby', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Australia/Sydney', 'location' => '(GMT+10:00) Sydney', 'utc_offset' => 36000];
$timezones[] = ['name'=>'Asia/Vladivostok', 'location' => '(GMT+11:00) Vladivostok', 'utc_offset' => 39600];
$timezones[] = ['name'=>'Asia/Magadan', 'location' => '(GMT+12:00) Magadan', 'utc_offset' => 43200];
$timezones[] = ['name'=>'Pacific/Auckland', 'location' => '(GMT+12:00) Auckland', 'utc_offset' => 43200];
$timezones[] = ['name'=>'Pacific/Fiji', 'location' => '(GMT+12:00) Fiji', 'utc_offset' => 43200];
foreach ($timezones as $timezone) {
@ -141,4 +151,6 @@ $timezones[] = ['name'=>'Pacific/Fiji', 'location' => '(GMT+12:00) Fiji', 'utc_o
]);
}
}
}

View File

@ -27,6 +27,8 @@ class DatabaseSeeder extends Seeder
$this->call('CurrenciesSeeder');
$this->call('LanguageSeeder');
$this->call('CountriesSeeder');
$this->call('IndustrySeeder');
$this->call('PaymentTypesSeeder');
}
}

View File

@ -0,0 +1,57 @@
<?php
use App\Models\Industry;
use Illuminate\Database\Seeder;
class IndustrySeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
$industries = [
['name' => 'Accounting & Legal'],
['name' => 'Advertising'],
['name' => 'Aerospace'],
['name' => 'Agriculture'],
['name' => 'Automotive'],
['name' => 'Banking & Finance'],
['name' => 'Biotechnology'],
['name' => 'Broadcasting'],
['name' => 'Business Services'],
['name' => 'Commodities & Chemicals'],
['name' => 'Communications'],
['name' => 'Computers & Hightech'],
['name' => 'Defense'],
['name' => 'Energy'],
['name' => 'Entertainment'],
['name' => 'Government'],
['name' => 'Healthcare & Life Sciences'],
['name' => 'Insurance'],
['name' => 'Manufacturing'],
['name' => 'Marketing'],
['name' => 'Media'],
['name' => 'Nonprofit & Higher Ed'],
['name' => 'Pharmaceuticals'],
['name' => 'Professional Services & Consulting'],
['name' => 'Real Estate'],
['name' => 'Retail & Wholesale'],
['name' => 'Sports'],
['name' => 'Transportation'],
['name' => 'Travel & Luxury'],
['name' => 'Other'],
['name' => 'Photography'],
['name' => 'Construction'],
['name' => 'Restaurant & Catering'],
];
foreach ($industries as $industry) {
$record = Industry::whereName($industry['name'])->first();
if (! $record) {
Industry::create($industry);
}
}
Eloquent::reguard();
}
}

View File

@ -0,0 +1,60 @@
<?php
use App\Models\PaymentType;
use Illuminate\Database\Seeder;
class PaymentTypesSeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
$paymentTypes = [
['name' => 'Apply Credit'],
['name' => 'Bank Transfer', 'gateway_type_id' => GATEWAY_TYPE_BANK_TRANSFER],
['name' => 'Cash'],
['name' => 'Debit', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'ACH', 'gateway_type_id' => GATEWAY_TYPE_BANK_TRANSFER],
['name' => 'Visa Card', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'MasterCard', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'American Express', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Discover Card', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Diners Card', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'EuroCard', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Nova', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Credit Card Other', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'PayPal', 'gateway_type_id' => GATEWAY_TYPE_PAYPAL],
['name' => 'Google Wallet'],
['name' => 'Check'],
['name' => 'Carte Blanche', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'UnionPay', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'JCB', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Laser', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Maestro', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Solo', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Switch', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'iZettle', 'gateway_type_id' => GATEWAY_TYPE_CREDIT_CARD],
['name' => 'Swish', 'gateway_type_id' => GATEWAY_TYPE_BANK_TRANSFER],
['name' => 'Venmo'],
['name' => 'Money Order'],
['name' => 'Alipay', 'gateway_type_id' => GATEWAY_TYPE_ALIPAY],
['name' => 'Sofort', 'gateway_type_id' => GATEWAY_TYPE_SOFORT],
['name' => 'SEPA', 'gateway_type_id' => GATEWAY_TYPE_SEPA],
['name' => 'GoCardless', 'gateway_type_id' => GATEWAY_TYPE_GOCARDLESS],
['name' => 'Bitcoin', 'gateway_type_id' => GATEWAY_TYPE_BITCOIN],
];
foreach ($paymentTypes as $paymentType) {
$record = PaymentType::where('name', '=', $paymentType['name'])->first();
if ($record) {
$record->name = $paymentType['name'];
$record->gateway_type_id = ! empty($paymentType['gateway_type_id']) ? $paymentType['gateway_type_id'] : null;
$record->save();
} else {
PaymentType::create($paymentType);
}
}
}
}

View File

@ -1,5 +1,6 @@
<?php
use App\DataMapper\CompanySettings;
use App\DataMapper\DefaultSettings;
use App\Models\Account;
use App\Models\Client;

1960
public/js/client_edit.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,17 @@
{
"/js/client_list.js": "/js/client_list.js?id=88d90244e855305bcfff",
"/js/client_edit.js": "/js/client_edit.js?id=23f35208ca7847252c92",
"/js/client_show.js": "/js/client_show.js?id=be7307363fb5779cdb24",
"/js/client_create.js": "/js/client_create.js?id=ca114ece0bbf0eb79d38",
"/js/localization.js": "/js/localization.js?id=85f6f7672f9cf65d9745",
"/js/coreui.js": "/js/coreui.js?id=9cfda6dd6df9aaeea844",
"/js/ninja.js": "/js/ninja.js?id=d41d8cd98f00b204e980",
"/js/client_list.js": "/js/client_list.js?id=b6d333a76dc9bfedb99c",
"/js/client_edit.js": "/js/client_edit.js?id=c6857e60873f45d76585",
"/js/client_show.js": "/js/client_show.js?id=c5c15fa08d83dd664bae",
"/js/client_create.js": "/js/client_create.js?id=8877c717377866cb12ba",
"/js/localization.js": "/js/localization.js?id=ae81449ec88ae5c9c0a4",
"/js/coreui.js": "/js/coreui.js?id=713ed1bcd7251ec4523b",
"/js/ninja.min.js": "/js/ninja.min.js?id=d41d8cd98f00b204e980",
"/js/coreui.min.js": "/js/coreui.min.js?id=9cfda6dd6df9aaeea844",
"/js/client_show.min.js": "/js/client_show.min.js?id=be7307363fb5779cdb24",
"/js/client_edit.min.js": "/js/client_edit.min.js?id=23f35208ca7847252c92",
"/js/client_create.min.js": "/js/client_create.min.js?id=ca114ece0bbf0eb79d38",
"/js/client_list.min.js": "/js/client_list.min.js?id=88d90244e855305bcfff",
"/js/localization.min.js": "/js/localization.min.js?id=85f6f7672f9cf65d9745",
"/js/coreui.min.js": "/js/coreui.min.js?id=713ed1bcd7251ec4523b",
"/js/client_show.min.js": "/js/client_show.min.js?id=c5c15fa08d83dd664bae",
"/js/client_edit.min.js": "/js/client_edit.min.js?id=c6857e60873f45d76585",
"/js/client_create.min.js": "/js/client_create.min.js?id=8877c717377866cb12ba",
"/js/client_list.min.js": "/js/client_list.min.js?id=b6d333a76dc9bfedb99c",
"/js/localization.min.js": "/js/localization.min.js?id=ae81449ec88ae5c9c0a4",
"/css/ninja.css": "/css/ninja.css?id=28421bc494c5086ac359",
"/css/ninja.min.css": "/css/ninja.min.css?id=28421bc494c5086ac359"
}

View File

@ -125,21 +125,25 @@
Multiselect
},
props: ['client', 'countries'],
mounted() {
},
data () {
return {
options: this.countries
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
// return this.client.shipping_country_id
},
get: function(value) {
return this.countries.filter(obj => {
return this.countryArray.filter(obj => {
return obj.id === this.client.shipping_country_id
})
@ -153,7 +157,7 @@
},
get: function(value) {
return this.countries.filter(obj => {
return this.countryArray.filter(obj => {
return obj.id === this.client.country_id
})

View File

@ -32,32 +32,32 @@
</div>
</div>
<div class="form-group row" v-if="company.custom_client_contact_label1">
<label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_contact_label1 }}</label>
<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.custom_client_contact_label2">
<label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_contact_label2 }}</label>
<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.custom_client_contact_label3">
<label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_contact_label3 }}</label>
<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.custom_client_contact_label4">
<label for="name" class="col-sm-3 col-form-label text-right">{{ company.custom_client_contact_label4 }}</label>
<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>

View File

@ -1,351 +1,181 @@
<template>
<div class="row" style="background:#fff; padding:20px;">
<div class="row">
<div class="col-2" style="border: 0px; border-style:solid;">
<div class="col-2">
<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>
<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">
<h2></h2>
</div>
<scrollactive
class="menu-list"
active-class="is-active"
:offset="50"
:duration="800"
:exact="true"
>
<a href="#intro" class="scrollactive-item" title="Intro">Intro</a><br>
<a href="#standard-affix" class="scrollactive-item" title="Standard Affix">Standard Affix</a><br>
<a href="#scroll-affix" class="scrollactive-item" title="Scroll Affix">Scroll Affix</a><br>
<a href="#markup-1" class="scrollactive-item" title="Markup 1">Markup 1</a><br>
<a href="#markup-2" class="scrollactive-item" title="Markup 2">Markup 2</a><br>
<a href="#markup-3" class="scrollactive-item" title="Markup 3">Markup 3</a><br>
</scrollactive>
</affix>
</div>
</div>
<div class="col-10">
<div class="col-10">
<div id="example-content">
<div id="example-content">
<section id="intro">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="standard-affix">
<section id="intro">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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" :placeholder="placeHolderCurrency()" :allow-empty="true"></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" value="1" name="show_currency_symbol" v-model="settings.show_currency_symbol">
<label class="form-check-label" for="show_currency_symbol-radio1">{{ trans('texts.currency_symbol') }}:</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" id="inline-radio2" type="radio" value="1" name="show_currency_code" v-model="settings.show_currency_code">
<label class="form-check-label" for="show_currency_code">{{ trans('texts.currency_code') }}:</label>
</div>
</div>
</div>
</section>
<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_client_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>
<section id="scroll-affix">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</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.dashboard"rows="9" placeholder=""></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.unpaid_invoice"rows="9" placeholder=""></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.paid_invoice" rows="9" placeholder=""></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.unapproved_quote" rows="9" placeholder=""></textarea>
</div>
</div>
</div>
</section>
<section id="markup-1">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
</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.language_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>
<section id="markup-2">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
<section id="markup-3">
<div class="card">
<div class="card-header bg-primary2">{{ trans('texts.edit_client') }}</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
<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')" class="form-control">
<div v-if="" class="text-danger" v-text=""></div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
@ -359,23 +189,133 @@
import Vue from 'vue';
import { Affix } from 'vue-affix';
var VueScrollactive = require('vue-scrollactive');
import Multiselect from 'vue-multiselect'
Vue.use(VueScrollactive);
export default {
components: {
Affix,
},
props: ['settings'],
mounted() {
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() {
},
computed: {
settings_currency_id: {
set: function(value){
},
methods: {
onItemChanged(event, currentItem, lastActiveItem) {
// your logic
},
},
this.setObjectValue('currency_id', value.id)
},
get: function(){
return this.options_currency.filter(obj => {
return obj.id == this.settings.currency_id
})
}
},
settings_language_id: {
set: function(value) {
this.setObjectValue('language_id', value.id)
},
get: function() {
return this.options_language.filter(obj => {
return obj.id == this.settings.language_id
})
}
},
settings_payment_terms: {
set: function(value) {
if(value === null)
this.setObjectValue('payment_terms', null)
else
this.setObjectValue('payment_terms', value.num_days)
},
get: function() {
return this.options_payment_term.filter(obj => {
return obj.num_days == this.settings.payment_terms
})
}
}
},
methods: {
onItemChanged(event, currentItem, lastActiveItem) {
// your logic
},
setObjectValue(key, value){
if(value === null)
this.settings[key] = null
else
this.settings[key] = value
},
placeHolderCurrency(){
var currency = this.options_currency.filter(obj => {
return obj.id == this.company.settings.currency_id
})
if(currency.length >= 1)
return currency[0].name
else
return Vue.prototype.trans('texts.currency_id')
},
placeHolderPaymentTerm(){
var payment_terms = this.payment_terms.filter(obj => {
return obj.num_days == this.company.settings.payment_terms
})
if(payment_terms.length >= 1)
return payment_terms[0].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.filter(obj => {
return obj.id == this.company.settings.language_id
})
if(language.length >= 1)
return language[0].name
else
return Vue.prototype.trans('texts.language_id')
}
}
}
</script>
@ -384,37 +324,69 @@ Vue.use(VueScrollactive);
<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: #42b983;
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: #42b983;
color: #027093;
padding-left:10px;
}
a.scrollactive-item.is-active:active {
color: #42b983;
color: #027093;
padding-left:10px;
}
.menu-list a {
color: black;
color: #939393;
font-family: helvetica;
text-decoration: none;
}
.menu-list a:hover {
text-decoration: none;
color: #42b983;
color: #027093;
padding-left:5px;
}
.menu-list a:active {
color: #42b983;
color: #027093;
text-decoration: none;
padding-left:5px;
}

View File

@ -0,0 +1,13 @@
<?php
$lang = [
'client_dashboard' => 'Message to be displayed on clients dashboard',
'client_currency' => 'The client currency.',
'client_language' => 'The client language.',
'client_payment_terms' => 'The client payment terms.',
'client_paid_invoice' => 'Message to be displayed on a clients paid invoice screen',
'client_unpaid_invoice' => 'Message to be displayed on a clients unpaid invoice screen',
'client_unapproved_quote' => 'Message to be displayed on a clients unapproved quote screen',
];
return $lang;

7
resources/lang/en/t.php Normal file
View File

@ -0,0 +1,7 @@
<?php
$lang = [
'client_settings' => 'Client Settings',
];
return $lang;

View File

@ -1,6 +1,7 @@
@extends('layouts.master', ['header' => $header])
@section('body')
<main class="main" id="client_edit">
<!-- Breadcrumb-->
@ -30,9 +31,9 @@
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-content" id="pills-tabContent" style="margin-top:20px; background:#fff;">
<div class="tab-pane fade active show" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" style="background-color: #e4e5e6; padding: 0px;">
<div class="tab-pane fade active show" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab" style="background-color: #fff; padding: 0px;">
<client-edit-form :company="{{ $company }}" :clientdata="{{ $client }}" :hashed_id="'{{ $hashed_id }}'" :countries="{{ $countries }}"></client-edit-form>
@ -48,9 +49,19 @@
@endforeach
<div class="tab-pane fade" id="pills-settings" role="tabpanel" aria-labelledby="pills-settings-tab" style="background-color: #e4e5e6; padding: 0px;">
<div class="tab-pane fade" id="pills-settings" role="tabpanel" aria-labelledby="pills-settings-tab" style="background-color: #fff; padding: 0px;">
<client-settings
:client_settings="{{ $settings }}"
:currencies="{{ $currencies }}"
:languages="{{ $languages }}"
:payment_terms="{{ $payment_terms }}"
:industries="{{ $industries }}"
:sizes="{{ $sizes }}"
:company="{{ $company }}"
>
</client-settings>
<client-settings :settings="{{ $settings }}"></client-settings>
</div>
@ -60,6 +71,6 @@
</main>
<script defer src=" {{ mix('/js/client_edit.min.js') }}"></script>
<script src=" {{ mix('/js/client_edit.min.js') }}"></script>
@endsection

View File

@ -46,6 +46,10 @@ class MultiDBUserTest extends TestCase
'account_id' => $account2->id,
]);
$company->setHidden(['settings', 'settings_object']);
$company2->setHidden(['settings', 'settings_object']);
Company::on('db-ninja-01')->create($company->toArray());
Company::on('db-ninja-02')->create($company2->toArray());

View File

@ -46,6 +46,9 @@ class UniqueEmailTest extends TestCase
'account_id' => $account2->id,
]);
$company->setHidden(['settings', 'settings_object']);
$company2->setHidden(['settings', 'settings_object']);
Company::on('db-ninja-01')->create($company->toArray());
Company::on('db-ninja-02')->create($company2->toArray());

View File

@ -0,0 +1,61 @@
<?php
namespace Tests\Unit;
use App\DataMapper\ClientSettings;
use App\DataMapper\CompanySettings;
use Tests\TestCase;
/**
* @test
* @covers App\DataMapper\BaseSettings
*/
class BaseSettingsTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->settings = ClientSettings::defaults();
}
public function testPropertyExists()
{
$blank_object = new \stdClass;
$this->assertEquals(count(get_object_vars($this->migrate($blank_object))), 4);
}
public function testPropertyNamesExist()
{
$blank_object = new \stdClass;
$updated_object = $this->migrate($blank_object);
$this->assertTrue(property_exists($updated_object, 'language_id'));
}
public function testPropertyNamesNotExist()
{
$blank_object = new \stdClass;
$updated_object = $this->migrate($blank_object);
$this->assertFalse(property_exists($updated_object, 'non_existent_prop'));
}
public function migrate(\stdClass $object) : \stdClass
{
foreach($this->settings as $property => $value)
{
if(!property_exists($object, $property))
$object->{$property} = NULL;
}
return $object;
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Tests\Unit;
use App\Models\PaymentTerm;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Support\Facades\Session;
use Tests\TestCase;
/**
* @test
* @covers App\Http\ViewComposers\TranslationComposer
*/
class CollectionMergingTest extends TestCase
{
use UserSessionAttributes;
public function setUp()
{
parent::setUp();
Session::start();
$this->setCurrentCompanyId(1);
$this->terms = PaymentTerm::scope()->get();
}
public function testBlankCollectionReturned()
{
$this->assertEquals($this->terms->count(), 0);
}
public function testMergingCollection()
{
$payment_terms = collect(unserialize(CACHED_PAYMENT_TERMS));
$new_terms = $this->terms->map(function($term) {
return $term['num_days'];
});
$payment_terms->merge($new_terms);
$this->assertEquals($payment_terms->count(), 8);
}
public function testSortingCollection()
{
$payment_terms = collect(unserialize(CACHED_PAYMENT_TERMS));
$new_terms = $this->terms->map(function($term) {
return $term['num_days'];
});
$payment_terms->merge($new_terms)
->sortBy('num_days')
->values()
->all();
$term = $payment_terms->first();
$this->assertEquals($term['num_days'], 0);
}
public function testSortingCollectionLast()
{
$payment_terms = collect(unserialize(CACHED_PAYMENT_TERMS));
$new_terms = $this->terms->map(function($term) {
return $term['num_days'];
});
$payment_terms->merge($new_terms)
->sortBy('num_days')
->values()
->all();
$term = $payment_terms->last();
$this->assertEquals($term['num_days'], 90);
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace Tests\Unit;
use App\DataMapper\CompanySettings;
use Tests\TestCase;
/**
* @test
* @covers App\DataMapper\CompanySettings
*/
class CompanySettingsTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->company_settings = CompanySettings::defaults();
}
public function testTimezoneId()
{
$this->assertEquals($this->company_settings->timezone_id, 15);
}
public function testCurrencyId()
{
$this->assertEquals($this->company_settings->currency_id, 1);
}
public function testLanguageId()
{
$this->assertEquals($this->company_settings->language_id, 1);
}
public function testPropertyIsNotset()
{
$this->assertFalse(isset($this->company_settings->custom_label1));
}
public function testPropertyIsSet()
{
$this->assertTrue(isset($this->company_settings->currency_id));
}
}

View File

@ -9,7 +9,7 @@ use Tests\TestCase;
* @test
* @covers App\DataMapper\DefaultSettings
*/
class DefaultTest extends TestCase
class DefaultSettingsTest extends TestCase
{
public function setUp()

2
webpack.mix.js vendored
View File

@ -34,9 +34,11 @@ mix.js('resources/js/src/client/client_list.ts', 'public/js');
mix.js('resources/js/src/settings/localization.ts', 'public/js');
mix.js('node_modules/@coreui/coreui/dist/js/coreui.js', 'public/js');
/*
mix.scripts([
'js/src/bootstrap.js'
], 'public/js/ninja.js');
*/
mix.minify('public/js/ninja.js');
mix.minify('public/js/coreui.js');