diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index aec8480033e3..0abc57428d19 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -396,6 +396,43 @@ class CompanySettings extends BaseSettings 'portal_custom_js' => 'string', ]; + + public static $free_plan_casts = [ + 'currency_id' => 'string', + 'company_gateway_ids' => 'string', + 'address1' => 'string', + 'address2' => 'string', + 'city' => 'string', + 'company_logo' => 'string', + 'country_id' => 'string', + 'custom_value1' => 'string', + 'custom_value2' => 'string', + 'custom_value3' => 'string', + 'custom_value4' => 'string', + 'inclusive_taxes' => 'bool', + 'name' => 'string', + 'payment_terms' => 'integer', + 'payment_type_id' => 'string', + 'phone' => 'string', + 'postal_code' => 'string', + 'state' => 'string', + 'email' => 'string', + 'vat_number' => 'string', + 'id_number' => 'string', + 'tax_name1' => 'string', + 'tax_name2' => 'string', + 'tax_name3' => 'string', + 'tax_rate1' => 'float', + 'tax_rate2' => 'float', + 'tax_rate3' => 'float', + 'timezone_id' => 'string', + 'date_format_id' => 'string', + 'military_time' => 'bool', + 'language_id' => 'string', + 'show_currency_code' => 'bool', + 'website' => 'string', + ]; + /** * Array of variables which * cannot be modified client side @@ -406,6 +443,12 @@ class CompanySettings extends BaseSettings // 'quote_number_counter', ]; + public static $string_casts = [ + 'invoice_design_id', + 'quote_design_id', + 'credit_design_id', + ]; + /** * Cast object values and return entire class * prevents missing properties from not being returned @@ -430,7 +473,9 @@ class CompanySettings extends BaseSettings unset($data->casts); unset($data->protected_fields); - + unset($data->free_plan_casts); + unset($data->string_casts); + $data->timezone_id = (string) config('ninja.i18n.timezone_id'); $data->currency_id = (string) config('ninja.i18n.currency_id'); $data->language_id = (string) config('ninja.i18n.language_id'); diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index d7bf85997ed8..ba39e6a90d95 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -66,15 +66,23 @@ class SelfUpdateController extends BaseController return response()->json(['message' => 'Self update not available on this system.'], 403); } + info("is new version available = ". $updater->source()->isNewVersionAvailable()); + // Get the new version available $versionAvailable = $updater->source()->getVersionAvailable(); +info($versionAvailable); + // Create a release $release = $updater->source()->fetch($versionAvailable); +info(print_r($release,1)); + // Run the update process $res = $updater->source()->update($release); +info(print_r($res,1)); + return response()->json(['message'=>$res], 200); } } diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index 540d7eedf171..3c84212f3a6a 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -13,6 +13,7 @@ namespace App\Http\Requests\Client; use App\DataMapper\ClientSettings; use App\Http\Requests\Request; +use App\Http\ValidationRules\Ninja\CanStoreClientsRule; use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Models\Client; use App\Models\GroupSetting; @@ -53,15 +54,8 @@ class StoreClientRequest extends Request //'regex:/[@$!%*#?&.]/', // must contain a special character ]; - -// $contacts = request('contacts'); - - // if (is_array($contacts)) { - // for ($i = 0; $i < count($contacts); $i++) { - - // //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct'; - // } - // } + if(auth()->user()->company()->account->isFreeHostedClient()) + $rules['hosted_clients'] = new CanStoreClientsRule($this->company_id); return $rules; } diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index 9aefe6f465cf..44bec2a6a809 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -12,10 +12,12 @@ namespace App\Http\Requests\Client; use App\DataMapper\ClientSettings; +use App\DataMapper\CompanySettings; use App\Http\Requests\Request; use App\Http\ValidationRules\IsDeletedRule; use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Http\ValidationRules\ValidSettingsRule; +use App\Utils\Ninja; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\MakesHash; use Illuminate\Validation\Rule; @@ -103,6 +105,41 @@ class UpdateClientRequest extends Request } } } + + if(array_key_exists('settings', $input)) + $input['settings'] = $this->filterSaveableSettings($input['settings']); + $this->replace($input); } + + + /** + * For the hosted platform, we restrict the feature settings. + * + * This method will trim the company settings object + * down to the free plan setting properties which + * are saveable + * + * @param object $settings + * @return object $settings + */ + private function filterSaveableSettings($settings) + { + $account = $this->client->company->account; + + if(!$account->isFreeHostedClient()) + return $settings; + + $saveable_casts = CompanySettings::$free_plan_casts; + + foreach($settings as $key => $value){ + + if(!array_key_exists($key, $saveable_casts)) + unset($settings->{$key}); + + } + + return $settings; + + } } diff --git a/app/Http/Requests/Company/StoreCompanyRequest.php b/app/Http/Requests/Company/StoreCompanyRequest.php index 3da204980667..5f482cba6881 100644 --- a/app/Http/Requests/Company/StoreCompanyRequest.php +++ b/app/Http/Requests/Company/StoreCompanyRequest.php @@ -66,13 +66,6 @@ class StoreCompanyRequest extends Request } } - // $company_settings->invoice_design_id = $this->encodePrimaryKey(1); - // $company_settings->quote_design_id = $this->encodePrimaryKey(1); - // $company_settings->credit_design_id = $this->encodePrimaryKey(1); - - // $input['settings'] = $company_settings; - - $this->replace($input); } } diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php index c051ac6e6d3b..ba142d50f561 100644 --- a/app/Http/Requests/Company/UpdateCompanyRequest.php +++ b/app/Http/Requests/Company/UpdateCompanyRequest.php @@ -14,6 +14,7 @@ namespace App\Http\Requests\Company; use App\DataMapper\CompanySettings; use App\Http\Requests\Request; use App\Http\ValidationRules\ValidSettingsRule; +use App\Utils\Ninja; use App\Utils\Traits\MakesHash; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; @@ -50,10 +51,51 @@ class UpdateCompanyRequest extends Request $rules['portal_domain'] = 'nullable|alpha_num'; } + if($this->company->account->isPaidHostedClient()) + return $settings; + return $rules; } protected function prepareForValidation() { + + $input = $this->all(); + + if(array_key_exists('settings', $input)) + $input['settings'] = $this->filterSaveableSettings($input['settings']); + + $this->replace($input); } + + /** + * For the hosted platform, we restrict the feature settings. + * + * This method will trim the company settings object + * down to the free plan setting properties which + * are saveable + * + * @param object $settings + * @return object $settings + */ + private function filterSaveableSettings($settings) + { + $account = $this->company->account; + + if(!$account->isFreeHostedClient()) + return $settings; + + $saveable_casts = CompanySettings::$free_plan_casts; + + foreach($settings as $key => $value){ + + if(!array_key_exists($key, $saveable_casts)) + unset($settings->{$key}); + + } + + return $settings; + + } + } diff --git a/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php b/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php index 5a286999f5de..4389d419aab6 100644 --- a/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php +++ b/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php @@ -11,8 +11,10 @@ namespace App\Http\Requests\GroupSetting; +use App\DataMapper\CompanySettings; use App\Http\Requests\Request; use App\Http\ValidationRules\ValidClientGroupSettingsRule; +use App\Utils\Ninja; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; @@ -40,6 +42,40 @@ class UpdateGroupSettingRequest extends Request { $input = $this->all(); + if(array_key_exists('settings', $input)) + $input['settings'] = $this->filterSaveableSettings($input['settings']); + $this->replace($input); } + + /** + * For the hosted platform, we restrict the feature settings. + * + * This method will trim the company settings object + * down to the free plan setting properties which + * are saveable + * + * @param object $settings + * @return object $settings + */ + private function filterSaveableSettings($settings) + { + $account = $this->group_setting->company->account; + + if(!$account->isFreeHostedClient()) + return $settings; + + $saveable_casts = CompanySettings::$free_plan_casts; + + foreach($settings as $key => $value){ + + if(!array_key_exists($key, $saveable_casts)) + unset($settings->{$key}); + + } + + return $settings; + + } + } diff --git a/app/Http/ValidationRules/Ninja/CanAddUserRule.php b/app/Http/ValidationRules/Ninja/CanAddUserRule.php new file mode 100644 index 000000000000..2a997db5f87a --- /dev/null +++ b/app/Http/ValidationRules/Ninja/CanAddUserRule.php @@ -0,0 +1,49 @@ +account = $account; + } + + /** + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + + } + + /** + * @return string + */ + public function message() + { + return ctrans('texts.limit_users', ['limit' => 1]); + } + +} diff --git a/app/Http/ValidationRules/Ninja/CanStoreClientsRule.php b/app/Http/ValidationRules/Ninja/CanStoreClientsRule.php new file mode 100644 index 000000000000..0079d9e728e7 --- /dev/null +++ b/app/Http/ValidationRules/Ninja/CanStoreClientsRule.php @@ -0,0 +1,51 @@ +company_id = $company_id; + } + + /** + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + $company = Company::find($this->company_id); + + return $company->clients->count() < config('ninja.quotas.free.clients'); + } + + /** + * @return string + */ + public function message() + { + return ctrans('texts.limit_clients', ['count' => config('ninja.quotas.free.clients')]); + } + +} diff --git a/app/Listeners/Invoice/CreateInvoicePdf.php b/app/Listeners/Invoice/CreateInvoicePdf.php index 46cc9a1202d5..6db605b83f41 100644 --- a/app/Listeners/Invoice/CreateInvoicePdf.php +++ b/app/Listeners/Invoice/CreateInvoicePdf.php @@ -35,6 +35,6 @@ class CreateInvoicePdf implements ShouldQueue */ public function handle($event) { - PdfCreator::dispatch($invoice->invitations->first()); + PdfCreator::dispatch($event->invoice->invitations->first()); } } diff --git a/app/Models/Account.php b/app/Models/Account.php index 575843e43eb5..5bc798c73b91 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -194,6 +194,15 @@ class Account extends BaseModel return $this->plan == 'pro' || $this->plan == 'enterprise'; } + public function isFreeHostedClient() + { + if (! Ninja::isNinja()) { + return false; + } + + return $this->plan == 'free'; + } + public function isTrial() { if (! Ninja::isNinja()) { diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index b8ec2d7c005b..3f24490708c3 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -64,12 +64,6 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ - \Codedge\Updater\Events\UpdateAvailable::class => [ - \Codedge\Updater\Listeners\SendUpdateAvailableNotification::class - ], // [3] - \Codedge\Updater\Events\UpdateSucceeded::class => [ - \Codedge\Updater\Listeners\SendUpdateSucceededNotification::class - ], UserWasCreated::class => [ SendVerificationNotification::class, ], diff --git a/app/Utils/Traits/ClientGroupSettingsSaver.php b/app/Utils/Traits/ClientGroupSettingsSaver.php index 4e88f9ada00e..63c3be725367 100644 --- a/app/Utils/Traits/ClientGroupSettingsSaver.php +++ b/app/Utils/Traits/ClientGroupSettingsSaver.php @@ -12,10 +12,16 @@ namespace App\Utils\Traits; use App\DataMapper\CompanySettings; -use App\Utils\Traits\SettingsSaver; /** * Class ClientGroupSettingsSaver + * + * Whilst it may appear that this CompanySettingsSaver and ClientGroupSettingsSaver + * could be duplicates, they are not. + * + * Each requires their own approach to saving and attempts to + * merge the two code paths should be avoided. + * * @package App\Utils\Traits */ trait ClientGroupSettingsSaver @@ -42,7 +48,6 @@ trait ClientGroupSettingsSaver unset($settings[$field]); } - /** * for clients and group settings, if a field is not set or is set to a blank value, * we unset it from the settings object @@ -90,7 +95,7 @@ trait ClientGroupSettingsSaver } foreach ($casts as $key => $value) { - if (in_array($key, SettingsSaver::$string_casts)) { + if (in_array($key, CompanySettings::$string_casts)) { $value = "string"; if (!property_exists($settings, $key)) { diff --git a/app/Utils/Traits/CompanySettingsSaver.php b/app/Utils/Traits/CompanySettingsSaver.php index 0697446af6b9..f975f4a7b589 100644 --- a/app/Utils/Traits/CompanySettingsSaver.php +++ b/app/Utils/Traits/CompanySettingsSaver.php @@ -12,10 +12,18 @@ namespace App\Utils\Traits; use App\DataMapper\CompanySettings; -use App\Utils\Traits\SettingsSaver; +use App\Models\Company; +use App\Utils\Ninja; /** * Class CompanySettingsSaver + * + * Whilst it may appear that this CompanySettingsSaver and ClientGroupSettingsSaver + * could be duplicates, they are not. + * + * Each requires their own approach to saving and attempts to + * merge the two code paths should be avoided. + * * @package App\Utils\Traits */ trait CompanySettingsSaver @@ -44,12 +52,13 @@ trait CompanySettingsSaver $settings = $this->checkSettingType($settings); $company_settings = CompanySettings::defaults(); - //Iterate and set NEW settings - foreach ($settings as $key => $value) { + + foreach ($settings as $key => $value) { + if (is_null($settings->{$key})) { $company_settings->{$key} = ''; } else { - $company_settings->{$key} = $value; + $company_settings->{$key} = $value; } } @@ -72,14 +81,11 @@ trait CompanySettingsSaver $settings = (object)$settings; $casts = CompanySettings::$casts; - - // if(property_exists($settings, 'pdf_variables')) - // unset($settings->pdf_variables); ksort($casts); foreach ($casts as $key => $value) { - if (in_array($key, SettingsSaver::$string_casts)) { + if (in_array($key, CompanySettings::$string_casts)) { $value = "string"; if (!property_exists($settings, $key)) { @@ -109,8 +115,6 @@ trait CompanySettingsSaver if (!property_exists($settings, $key) || is_null($settings->{$key}) || !isset($settings->{$key}) || $settings->{$key} == '') { continue; } - - /*Catch all filter */ if (!$this->checkAttribute($value, $settings->{$key})) { @@ -139,7 +143,7 @@ trait CompanySettingsSaver $casts = CompanySettings::$casts; foreach ($casts as $key => $value) { - if (in_array($key, SettingsSaver::$string_casts)) { + if (in_array($key, CompanySettings::$string_casts)) { $value = "string"; if (!property_exists($settings, $key)) { @@ -230,4 +234,12 @@ trait CompanySettingsSaver return false; } } + + private function getAccountFromEntity($entity) + { + if($entity instanceof Company) + return $entity->account; + + return $entity->company->account; + } } diff --git a/app/Utils/Traits/SettingsSaver.php b/app/Utils/Traits/SettingsSaver.php index 27a26bf9d371..7f15b93d70ad 100644 --- a/app/Utils/Traits/SettingsSaver.php +++ b/app/Utils/Traits/SettingsSaver.php @@ -19,43 +19,6 @@ use App\DataMapper\CompanySettings; */ trait SettingsSaver { - public static $string_casts = [ - 'invoice_design_id', - 'quote_design_id', - 'credit_design_id', - ]; - /** - * Saves a setting object - * - * Works for groups|clients|companies - * @param array $settings The request input settings array - * @param object $entity The entity which the settings belongs to - * @return void - */ - public function saveSettings($settings, $entity) - { - if (!$settings) { - return; - } - - $entity_settings = $this->settings; - - //unset protected properties. - foreach (CompanySettings::$protected_fields as $field) { - unset($settings[$field]); - } - - $settings = $this->checkSettingType($settings); - - //iterate through set properties with new values; - foreach ($settings as $key => $value) { - $entity_settings->{$key} = $value; - } - - $entity->settings = $entity_settings; - $entity->save(); - } - /** * Used for custom validation of inbound * settings request. @@ -70,13 +33,10 @@ trait SettingsSaver $settings = (object)$settings; $casts = CompanySettings::$casts; - // if(property_exists($settings, 'pdf_variables')) - // unset($settings->pdf_variables); - ksort($casts); foreach ($casts as $key => $value) { - if (in_array($key, self::$string_casts)) { + if (in_array($key, CompanySettings::$string_casts)) { $value = "string"; if (!property_exists($settings, $key)) { continue; @@ -115,68 +75,6 @@ trait SettingsSaver return true; } - /** - * Checks the settings object for - * correct property types. - * - * The method will drop invalid types from - * the object and will also settype() the property - * so that it can be saved cleanly - * - * @param array $settings The settings request() array - * @return object stdClass object - */ - private function checkSettingType($settings) : \stdClass - { - $settings = (object)$settings; - - /* Because of the object casting we cannot check pdf_variables */ - if (property_exists($settings, 'pdf_variables')) { - unset($settings->pdf_variables); - } - - $casts = CompanySettings::$casts; - - foreach ($casts as $key => $value) { - if (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') { - $value = "integer"; - - if (!property_exists($settings, $key)) { - continue; - } elseif ($this->checkAttribute($value, $settings->{$key})) { - if (substr($key, -3) == '_id') { - settype($settings->{$key}, 'string'); - } else { - settype($settings->{$key}, $value); - } - } else { - unset($settings->{$key}); - } - - continue; - } - - /* Handles unset settings or blank strings */ - if (!property_exists($settings, $key) || is_null($settings->{$key}) || !isset($settings->{$key}) || $settings->{$key} == '') { - continue; - } - - /*Catch all filter */ - if ($this->checkAttribute($value, $settings->{$key})) { - if ($value == 'string' && is_null($settings->{$key})) { - $settings->{$key} = ''; - } - - settype($settings->{$key}, $value); - } else { - unset($settings->{$key}); - } - } - - return $settings; - } - - /** * Type checks a object property. * @param string $key The type diff --git a/config/ninja.php b/config/ninja.php index f3ec465a9ed5..6ee84964baf3 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -134,4 +134,16 @@ return [ 'global' => 'ninja2020', 'portal' => 'ninja2020', ], + 'quotas' => [ + 'free' => [ + 'clients' => 50, + 'daily_emails' => 50, + ], + 'pro' => [ + 'daily_emails' => 100, + ], + 'enterprise' => [ + 'daily_emails' => 200, + ] + ] ]; diff --git a/config/self-update.php b/config/self-update.php index 272108a3a892..602bec8fd977 100644 --- a/config/self-update.php +++ b/config/self-update.php @@ -144,7 +144,8 @@ return [ 'log' => 1, 'reset' => false, // etc. - ] ], + ] + ], ], ], diff --git a/tests/Unit/CompanySettingsSaveableTest.php b/tests/Unit/CompanySettingsSaveableTest.php new file mode 100644 index 000000000000..12c2fb76c63f --- /dev/null +++ b/tests/Unit/CompanySettingsSaveableTest.php @@ -0,0 +1,52 @@ +filterSaver(CompanySettings::defaults()); + + $this->assertTrue(property_exists($filtered, 'timezone_id')); + + $this->assertTrue(property_exists(CompanySettings::defaults(), 'timezone_id')); + + $this->assertTrue(property_exists(CompanySettings::defaults(), 'auto_archive_invoice')); + + $this->assertFalse(property_exists($filtered, 'auto_archive_invoice')); + + } + + private function filterSaver($settings) + { + + $saveable_cast = CompanySettings::$free_plan_casts; + + foreach($settings as $key => $value){ + + if(!array_key_exists($key, $saveable_cast)) + unset($settings->{$key}); + + } + + return $settings; + + } +} \ No newline at end of file