From 485fea03c782fea807f13984b080f48bbd9aa33d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 9 Apr 2022 16:02:12 +1000 Subject: [PATCH 01/22] Coerce default_task_rate to float --- app/DataMapper/CompanySettings.php | 1 + app/Http/Requests/Client/StoreClientRequest.php | 4 ++++ app/Http/Requests/Client/UpdateClientRequest.php | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 9c2ffd076a37..ec223897bdd0 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -503,6 +503,7 @@ class CompanySettings extends BaseSettings 'language_id' => 'string', 'show_currency_code' => 'bool', 'website' => 'string', + 'default_task_rate' => 'float', ]; /** diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index 5b7c1ae7b94f..cf829ddcc1d7 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -95,6 +95,10 @@ class StoreClientRequest extends Request if (array_key_exists('settings', $input) && ! empty($input['settings'])) { foreach ($input['settings'] as $key => $value) { + + if($key == 'default_task_rate') + $value = floatval($value); + $settings->{$key} = $value; } } diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index c8d60ceeb5ec..ccc6af785c47 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -157,6 +157,10 @@ class UpdateClientRequest extends Request if (! array_key_exists($key, $saveable_casts)) { unset($settings->{$key}); } + + if($key == 'default_task_rate'){ + $settings->default_task_rate = floatval($value); + } } return $settings; From fabf8e0baebd92ab7b498473c598353593f1b87a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 11 Apr 2022 17:45:37 +1000 Subject: [PATCH 02/22] Query lazy loading imrpovements --- app/Http/Controllers/BaseController.php | 28 ++++++++++++++----- app/Http/Middleware/QueryLogging.php | 10 +++---- app/Models/CompanyUser.php | 2 ++ app/Providers/AppServiceProvider.php | 6 ++++ app/Transformers/CompanyUserTransformer.php | 2 +- .../Traits/Notifications/UserNotifies.php | 4 +-- 6 files changed, 37 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 67973e1bf0d0..e798af27eedc 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -218,7 +218,7 @@ class BaseController extends Controller $query->with( [ 'company' => function ($query) use ($updated_at, $user) { - $query->whereNotNull('updated_at')->with('documents')->with('users'); + $query->whereNotNull('updated_at')->with('documents','users'); }, 'company.clients' => function ($query) use ($updated_at, $user) { $query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents'); @@ -392,7 +392,7 @@ class BaseController extends Controller $query->with( [ 'company' => function ($query) use ($created_at, $user) { - $query->whereNotNull('created_at')->with('documents'); + $query->whereNotNull('created_at')->with('documents','users'); }, 'company.designs'=> function ($query) use ($created_at, $user) { $query->where('created_at', '>=', $created_at)->with('company'); @@ -466,7 +466,7 @@ class BaseController extends Controller $query->with( [ 'company' => function ($query) use ($created_at, $user) { - $query->whereNotNull('created_at')->with('documents'); + $query->whereNotNull('created_at')->with('documents','users'); }, 'company.clients' => function ($query) use ($created_at, $user) { $query->where('clients.created_at', '>=', $created_at)->with('contacts.company', 'gateway_tokens', 'documents'); @@ -500,9 +500,6 @@ class BaseController extends Controller }, 'company.groups' => function ($query) use ($created_at, $user) { $query->where('created_at', '>=', $created_at)->with('documents'); - - // if(!$user->isAdmin()) - // $query->where('group_settings.user_id', $user->id); }, 'company.invoices'=> function ($query) use ($created_at, $user) { $query->where('created_at', '>=', $created_at)->with('invitations', 'documents'); @@ -583,13 +580,30 @@ class BaseController extends Controller $query->where('activities.user_id', $user->id); }, + 'company.webhooks'=> function ($query) use($user) { + + if(!$user->isAdmin()) + $query->where('webhooks.user_id', $user->id); + + }, + 'company.tokens'=> function ($query) use($user) { + $query->where('company_tokens.user_id', $user->id); + }, + 'company.system_logs', 'company.subscriptions'=> function ($query) use($created_at, $user) { $query->where('created_at', '>=', $created_at); if(!$user->isAdmin()) $query->where('subscriptions.user_id', $user->id); - } + }, + 'company.recurring_expenses'=> function ($query) use ($created_at, $user) { + $query->where('created_at', '>=', $created_at)->with('documents'); + + if(!$user->hasPermission('view_recurring_expense')) + $query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id); + + }, ] ); diff --git a/app/Http/Middleware/QueryLogging.php b/app/Http/Middleware/QueryLogging.php index 94ba71086a22..97630957fbe5 100644 --- a/app/Http/Middleware/QueryLogging.php +++ b/app/Http/Middleware/QueryLogging.php @@ -35,9 +35,9 @@ class QueryLogging { // Enable query logging for development - if (!Ninja::isHosted() || !config('beacon.enabled')) { - return $next($request); - } + // if (!Ninja::isHosted() || !config('beacon.enabled')) { + // return $next($request); + // } $timeStart = microtime(true); DB::enableQueryLog(); @@ -53,10 +53,10 @@ class QueryLogging // info("Query count = {$count}"); - if($count > 175){ + // if($count > 175){ nlog("Query count = {$count}"); nlog($queries); - } + // } $ip = ''; diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index cf9fbfd14d1c..fc77eba1d8bf 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -52,6 +52,8 @@ class CompanyUser extends Pivot protected $touches = ['user']; + protected $with = ['user','account']; + public function getEntityType() { return self::class; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ee6c6775d724..1ed3c92bebb5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -25,6 +25,7 @@ use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; +use Illuminate\Database\Eloquent\Model; use Livewire\Livewire; class AppServiceProvider extends ServiceProvider @@ -69,6 +70,11 @@ class AppServiceProvider extends ServiceProvider app()->instance(TruthSource::class, new TruthSource()); + + Model::preventLazyLoading( + !$this->app->isProduction() + ); + } /** diff --git a/app/Transformers/CompanyUserTransformer.php b/app/Transformers/CompanyUserTransformer.php index 1f63e2716b3d..54c0b5ab9e01 100644 --- a/app/Transformers/CompanyUserTransformer.php +++ b/app/Transformers/CompanyUserTransformer.php @@ -79,7 +79,7 @@ class CompanyUserTransformer extends EntityTransformer public function includeToken(CompanyUser $company_user) { - $token = $company_user->tokens->where('company_id', $company_user->company_id)->where('user_id', $company_user->user_id)->first(); + $token = $company_user->tokens()->where('company_id', $company_user->company_id)->where('user_id', $company_user->user_id)->first(); $transformer = new CompanyTokenTransformer($this->serializer); diff --git a/app/Utils/Traits/Notifications/UserNotifies.php b/app/Utils/Traits/Notifications/UserNotifies.php index f5d8f63adcec..3985f80dd242 100644 --- a/app/Utils/Traits/Notifications/UserNotifies.php +++ b/app/Utils/Traits/Notifications/UserNotifies.php @@ -32,7 +32,7 @@ trait UserNotifies $notifiable_methods = []; $notifications = $company_user->notifications; - if ($company_user->company->is_disabled && is_array($notifications->email) || $company_user->trashed() || $company_user->user->trashed()) { + if ($invitation->company->is_disabled && is_array($notifications->email) || $company_user->trashed() || $company_user->user->trashed()) { return []; } @@ -56,7 +56,7 @@ trait UserNotifies $notifiable_methods = []; $notifications = $company_user->notifications; - if ($company_user->company->is_disabled || ! $notifications || $company_user->trashed() || $company_user->user->trashed()) { + if ($entity->company->is_disabled || ! $notifications || $company_user->trashed() || $company_user->user->trashed()) { return []; } From c2ec39dc4c54a4be7c7db653223a680969881409 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 11 Apr 2022 17:45:59 +1000 Subject: [PATCH 03/22] Disable lazy load blocker --- app/Providers/AppServiceProvider.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1ed3c92bebb5..7374fe18ad27 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -71,9 +71,9 @@ class AppServiceProvider extends ServiceProvider app()->instance(TruthSource::class, new TruthSource()); - Model::preventLazyLoading( - !$this->app->isProduction() - ); + // Model::preventLazyLoading( + // !$this->app->isProduction() + // ); } From c466f2f15464cbd5ef81c1f71d7ab9f5cb6c8eb4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 12 Apr 2022 10:23:09 +1000 Subject: [PATCH 04/22] restore query logging middleware --- app/Http/Middleware/QueryLogging.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Http/Middleware/QueryLogging.php b/app/Http/Middleware/QueryLogging.php index 97630957fbe5..94ba71086a22 100644 --- a/app/Http/Middleware/QueryLogging.php +++ b/app/Http/Middleware/QueryLogging.php @@ -35,9 +35,9 @@ class QueryLogging { // Enable query logging for development - // if (!Ninja::isHosted() || !config('beacon.enabled')) { - // return $next($request); - // } + if (!Ninja::isHosted() || !config('beacon.enabled')) { + return $next($request); + } $timeStart = microtime(true); DB::enableQueryLog(); @@ -53,10 +53,10 @@ class QueryLogging // info("Query count = {$count}"); - // if($count > 175){ + if($count > 175){ nlog("Query count = {$count}"); nlog($queries); - // } + } $ip = ''; From e452c04214a11c474bf2cc7d38b35a343abfdceb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 13 Apr 2022 00:32:39 +1000 Subject: [PATCH 05/22] coerce string to int --- app/Filters/QueryFilters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Filters/QueryFilters.php b/app/Filters/QueryFilters.php index 28d9f7300b0b..9b5d4ea1e3fc 100644 --- a/app/Filters/QueryFilters.php +++ b/app/Filters/QueryFilters.php @@ -168,7 +168,7 @@ abstract class QueryFilters public function created_at($value) { - $created_at = $value ? $value : 0; + $created_at = $value ? (int)$value : 0; $created_at = date('Y-m-d H:i:s', $value); From 87f5c73787e930163b8c68a50c6edbf83d7b1d15 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 13 Apr 2022 16:48:02 +1000 Subject: [PATCH 06/22] Enforce character lengths for authorize fields --- app/Jobs/Company/CompanyImport.php | 2 +- app/Models/BaseModel.php | 2 +- .../Authorize/AuthorizePaymentMethod.php | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index ae006fb828f1..25515fe01c4a 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -140,7 +140,7 @@ class CompanyImport implements ShouldQueue 'expenses', 'tasks', 'payments', - 'activities', + // 'activities', // 'backups', 'company_ledger', 'designs', diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index a1563c89394a..882ade4c8a1e 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -190,7 +190,7 @@ class BaseModel extends Model public function numberFormatter() { - $number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5); ; + $number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5); $formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number); // Remove any runs of periods (thanks falstro!) diff --git a/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php b/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php index 3910fc487119..ef586ac8c348 100644 --- a/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php +++ b/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php @@ -164,13 +164,13 @@ class AuthorizePaymentMethod if ($contact) { // Create the Bill To info for new payment type $billto = new CustomerAddressType(); - $billto->setFirstName($contact->present()->first_name()); - $billto->setLastName($contact->present()->last_name()); - $billto->setCompany($this->authorize->client->present()->name()); - $billto->setAddress($this->authorize->client->address1); - $billto->setCity($this->authorize->client->city); - $billto->setState($this->authorize->client->state); - $billto->setZip($this->authorize->client->postal_code); + $billto->setFirstName(substr(0,50,$contact->present()->first_name())); + $billto->setLastName(substr(0,50,$contact->present()->last_name())); + $billto->setCompany(substr(0,50,$this->authorize->client->present()->name())); + $billto->setAddress(substr(0,60,$this->authorize->client->address1)); + $billto->setCity(substr(0,40,$this->authorize->client->city)); + $billto->setState(substr(0,40,$this->authorize->client->state)); + $billto->setZip(substr(0,20,$this->authorize->client->postal_code)); if ($this->authorize->client->country_id) { $billto->setCountry($this->authorize->client->country->name); From 0a40bd9545467a4e8c20eff3d7472ae66f95f6c6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 13 Apr 2022 17:00:01 +1000 Subject: [PATCH 07/22] Add Partial Due Date variable --- app/Utils/HtmlEngine.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 996b2eaefdb7..34585a2efb06 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -122,6 +122,9 @@ class HtmlEngine $data['$invoice.date'] = &$data['$date']; $data['$invoiceDate'] = &$data['$date']; $data['$due_date'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')]; + + $data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')]; + $data['$dueDate'] = &$data['$due_date']; $data['$payment_due'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.payment_due')]; From 719f6715e8341799000051bb071cbaa03f0486f1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 14 Apr 2022 18:44:35 +1000 Subject: [PATCH 08/22] Fixes for company gateway creation --- app/Factory/CompanyGatewayFactory.php | 2 ++ app/Models/CompanyGateway.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Factory/CompanyGatewayFactory.php b/app/Factory/CompanyGatewayFactory.php index 3d0b383c12ca..97c71a80a478 100644 --- a/app/Factory/CompanyGatewayFactory.php +++ b/app/Factory/CompanyGatewayFactory.php @@ -23,6 +23,8 @@ class CompanyGatewayFactory $company_gateway->user_id = $user_id; $company_gateway->require_billing_address = false; $company_gateway->require_shipping_address = false; + $company_gateway->config = encrypt(json_encode(new \stdClass)); + // $company_gateway->fees_and_limits = new FeesAndLimits; return $company_gateway; diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 63c4d499e177..0622afbdd934 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -250,7 +250,7 @@ class CompanyGateway extends BaseModel { $config = $this->getConfig(); - if ($this->gateway->provider == 'Stripe' && property_exists($config, 'publishableKey') && strpos($config->publishableKey, 'test')) { + if ($this->gateway && $this->gateway->provider == 'Stripe' && property_exists($config, 'publishableKey') && strpos($config->publishableKey, 'test')) { return true; } From f33f1d81e704894c69b1bc9795fff19b3540d7e6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 14 Apr 2022 20:15:46 +1000 Subject: [PATCH 09/22] Fixes for tests --- tests/Integration/UpdateCompanyUserTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/UpdateCompanyUserTest.php b/tests/Integration/UpdateCompanyUserTest.php index 7198a5af4593..de8808955d5b 100644 --- a/tests/Integration/UpdateCompanyUserTest.php +++ b/tests/Integration/UpdateCompanyUserTest.php @@ -36,7 +36,7 @@ class UpdateCompanyUserTest extends TestCase public function testUpdatingCompanyUserAsAdmin() { - User::unguard(); + // User::unguard(); $settings = new \stdClass; $settings->invoice = 'ninja'; @@ -59,7 +59,7 @@ class UpdateCompanyUserTest extends TestCase $message = json_decode($e->validator->getMessageBag(), 1); $this->assertNotNull($message); } - + $response->assertStatus(200); $arr = $response->json(); From e8e8598fd88d194840261df34fbdac82a38a9171 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 14 Apr 2022 21:38:29 +1000 Subject: [PATCH 10/22] Webhooks for Projects --- .../RecurringInvoiceToInvoiceFactory.php | 10 +++ app/Models/Webhook.php | 5 ++ app/Observers/ProjectObserver.php | 89 +++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 + 4 files changed, 107 insertions(+) create mode 100644 app/Observers/ProjectObserver.php diff --git a/app/Factory/RecurringInvoiceToInvoiceFactory.php b/app/Factory/RecurringInvoiceToInvoiceFactory.php index 3b9879cecfb9..981fb8f45fb5 100644 --- a/app/Factory/RecurringInvoiceToInvoiceFactory.php +++ b/app/Factory/RecurringInvoiceToInvoiceFactory.php @@ -48,6 +48,16 @@ class RecurringInvoiceToInvoiceFactory $invoice->custom_value4 = $recurring_invoice->custom_value4; $invoice->amount = $recurring_invoice->amount; $invoice->uses_inclusive_taxes = $recurring_invoice->uses_inclusive_taxes; + + $invoice->custom_surcharge1 = $recurring_invoice->custom_surcharge1; + $invoice->custom_surcharge2 = $recurring_invoice->custom_surcharge2; + $invoice->custom_surcharge3 = $recurring_invoice->custom_surcharge3; + $invoice->custom_surcharge4 = $recurring_invoice->custom_surcharge4; + $invoice->custom_surcharge_tax1 = $recurring_invoice->custom_surcharge_tax1; + $invoice->custom_surcharge_tax2 = $recurring_invoice->custom_surcharge_tax2; + $invoice->custom_surcharge_tax3 = $recurring_invoice->custom_surcharge_tax3; + $invoice->custom_surcharge_tax4 = $recurring_invoice->custom_surcharge_tax4; + // $invoice->balance = $recurring_invoice->balance; $invoice->user_id = $recurring_invoice->user_id; $invoice->assigned_user_id = $recurring_invoice->assigned_user_id; diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 69bd78692c42..e0f1edc583f0 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -42,7 +42,10 @@ class Webhook extends BaseModel const EVENT_LATE_INVOICE = 22; const EVENT_EXPIRED_QUOTE = 23; const EVENT_REMIND_INVOICE = 24; + const EVENT_PROJECT_CREATE = 25; + const EVENT_PROJECT_UPDATE = 26; + public static $valid_events = [ self::EVENT_CREATE_CLIENT, self::EVENT_CREATE_INVOICE, @@ -68,6 +71,8 @@ class Webhook extends BaseModel self::EVENT_LATE_INVOICE, self::EVENT_EXPIRED_QUOTE, self::EVENT_REMIND_INVOICE, + self::EVENT_PROJECT_CREATE, + self::EVENT_PROJECT_UPDATE ]; protected $fillable = [ diff --git a/app/Observers/ProjectObserver.php b/app/Observers/ProjectObserver.php new file mode 100644 index 000000000000..a109eb827190 --- /dev/null +++ b/app/Observers/ProjectObserver.php @@ -0,0 +1,89 @@ +company_id) + ->where('event_id', Webhook::EVENT_PROJECT_CREATE) + ->exists(); + + if ($subscriptions) { + + WebhookHandler::dispatch(Webhook::EVENT_PROJECT_CREATE, $project, $project->company, 'client'); + } + } + + /** + * Handle the product "updated" event. + * + * @param Project $project + * @return void + */ + public function updated(Project $project) + { + $subscriptions = Webhook::where('company_id', $project->company_id) + ->where('event_id', Webhook::EVENT_PROJECT_UPDATE) + ->exists(); + + if ($subscriptions) { + + WebhookHandler::dispatch(Webhook::EVENT_PROJECT_UPDATE, $project, $project->company, 'client'); + } + } + + /** + * Handle the product "deleted" event. + * + * @param Project $project + * @return void + */ + public function deleted(Project $project) + { + // + } + + /** + * Handle the product "restored" event. + * + * @param Project $project + * @return void + */ + public function restored(Project $project) + { + // + } + + /** + * Handle the product "force deleted" event. + * + * @param Project $project + * @return void + */ + public function forceDeleted(Project $project) + { + // + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 5acc2f234388..33788ded0acd 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -213,6 +213,7 @@ use App\Models\Expense; use App\Models\Invoice; use App\Models\Payment; use App\Models\Product; +use App\Models\Project; use App\Models\Proposal; use App\Models\Quote; use App\Models\Subscription; @@ -228,6 +229,7 @@ use App\Observers\ExpenseObserver; use App\Observers\InvoiceObserver; use App\Observers\PaymentObserver; use App\Observers\ProductObserver; +use App\Observers\ProjectObserver; use App\Observers\ProposalObserver; use App\Observers\QuoteObserver; use App\Observers\SubscriptionObserver; @@ -586,6 +588,7 @@ class EventServiceProvider extends ServiceProvider Invoice::observe(InvoiceObserver::class); Payment::observe(PaymentObserver::class); Product::observe(ProductObserver::class); + Project::observe(ProjectObserver::class); Proposal::observe(ProposalObserver::class); Quote::observe(QuoteObserver::class); Task::observe(TaskObserver::class); From 62e0e2e4662815bec1704490b4e991b694ab5bc4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 14 Apr 2022 21:51:25 +1000 Subject: [PATCH 11/22] Updates for self updater --- app/Http/Controllers/SelfUpdateController.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index ead8e65e861c..325aa29c5b73 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -21,6 +21,15 @@ class SelfUpdateController extends BaseController { use DispatchesJobs; + private array $purge_file_list = [ + 'bootstrap/cache/compiled.php', + 'bootstrap/cache/config.php', + 'bootstrap/cache/packages.php', + 'bootstrap/cache/services.php', + 'bootstrap/cache/routes-v7.php', + 'bootstrap/cache/livewire-components.php', + ]; + public function __construct() { } @@ -117,10 +126,12 @@ class SelfUpdateController extends BaseController unlink($file); - $cacheCompiled = base_path('bootstrap/cache/compiled.php'); - if (file_exists($cacheCompiled)) { unlink ($cacheCompiled); } - $cacheServices = base_path('bootstrap/cache/services.php'); - if (file_exists($cacheServices)) { unlink ($cacheServices); } + foreach($this->purge_file_list as $purge_file_path) + { + $purge_file = base_path($purge_file_path); + if (file_exists($purge_file)) { unlink ($purge_file); } + + } Artisan::call('clear-compiled'); Artisan::call('route:clear'); From 0c5d2911931a59b3f19688086803373b3f0c674e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 15 Apr 2022 09:51:39 +1000 Subject: [PATCH 12/22] Gateway fees for PayPal --- .../PayPalExpressPaymentDriver.php | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php index 75a569c84c81..f6016c7e586e 100644 --- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php +++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php @@ -32,6 +32,8 @@ class PayPalExpressPaymentDriver extends BaseDriver private $omnipay_gateway; + private float $fee = 0; + const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL; public function gatewayTypes() @@ -173,11 +175,17 @@ class PayPalExpressPaymentDriver extends BaseDriver public function generatePaymentDetails(array $data) { + + $_invoice = collect($this->payment_hash->data->invoices)->first(); + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + $this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']); + return [ 'currency' => $this->client->getCurrencyCode(), 'transactionType' => 'Purchase', 'clientIp' => request()->getClientIp(), - 'amount' => $data['total']['amount_with_fee'], + 'amount' => $data['total']['amount_with_fee'] + $this->fee, 'returnUrl' => route('client.payments.response', [ 'company_gateway_id' => $this->company_gateway->id, 'payment_hash' => $this->payment_hash->hash, @@ -200,8 +208,6 @@ class PayPalExpressPaymentDriver extends BaseDriver $_invoice = collect($this->payment_hash->data->invoices)->first(); $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); - $line_item = collect($invoice->line_items)->first(); - $items = []; $items[] = new Item([ @@ -211,8 +217,44 @@ class PayPalExpressPaymentDriver extends BaseDriver 'quantity' => 1, ]); + + if($this->fee > 0.1){ + + $items[] = new Item([ + 'name' => " ", + 'description' => ctrans('texts.gateway_fee_description'), + 'price' => $this->fee, + 'quantity' => 1, + ]); + + + } + return $items; } + private function feeCalc($invoice, $invoice_total) + { + + $invoice->service()->removeUnpaidGatewayFees(); + $invoice = $invoice->fresh(); + + $balance = floatval($invoice->balance); + + $_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save(); + + if(floatval($_updated_invoice->balance) > $balance){ + + $fee = floatval($_updated_invoice->balance) - $balance; + + $this->payment_hash->fee_total = $fee; + $this->payment_hash->save(); + + return $fee; + } + + return 0; + } + } From 38878594641b66b6dfea44c35168525247263d39 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 15 Apr 2022 11:12:15 +1000 Subject: [PATCH 13/22] Restrict reminders to paid accounts on hosted --- app/Jobs/Util/ReminderJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 88a0aabf679c..a43058685602 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -84,7 +84,7 @@ class ReminderJob implements ShouldQueue //check if this reminder needs to be emailed //15-01-2022 - insert addition if block if send_reminders is definitely set - if(in_array($reminder_template, ['reminder1','reminder2','reminder3','reminder_endless']) && $invoice->client->getSetting("enable_".$reminder_template) && $invoice->client->getSetting("send_reminders")) + if(in_array($reminder_template, ['reminder1','reminder2','reminder3','reminder_endless']) && $invoice->client->getSetting("enable_".$reminder_template) && $invoice->client->getSetting("send_reminders") && $invoice->company->account->isPaid()) { $invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) { EmailEntity::dispatch($invitation, $invitation->company, $reminder_template); From cf1b790786646f3b642438e038b68151175fa40e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 15 Apr 2022 11:17:29 +1000 Subject: [PATCH 14/22] Restrict reminders to paid accounts on hosted --- app/Jobs/Util/ReminderJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index a43058685602..edb96ff59f0e 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -84,7 +84,7 @@ class ReminderJob implements ShouldQueue //check if this reminder needs to be emailed //15-01-2022 - insert addition if block if send_reminders is definitely set - if(in_array($reminder_template, ['reminder1','reminder2','reminder3','reminder_endless']) && $invoice->client->getSetting("enable_".$reminder_template) && $invoice->client->getSetting("send_reminders") && $invoice->company->account->isPaid()) + if(in_array($reminder_template, ['reminder1','reminder2','reminder3','reminder_endless']) && $invoice->client->getSetting("enable_".$reminder_template) && $invoice->client->getSetting("send_reminders") && $invoice->company->account->isPaidHostedClient()) { $invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) { EmailEntity::dispatch($invitation, $invitation->company, $reminder_template); From 0d5ee8269d196b1e0586078d3716f249f36ba9e1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 18 Apr 2022 21:02:06 +1000 Subject: [PATCH 15/22] Fixes for client tests --- app/DataMapper/ClientSettings.php | 9 + app/Factory/CloneQuoteToInvoiceFactory.php | 6 +- app/Utils/Traits/ClientGroupSettingsSaver.php | 16 +- app/Utils/Traits/SettingsSaver.php | 2 +- tests/Unit/ClientSettingsTest.php | 337 ++++++++++++++++++ 5 files changed, 357 insertions(+), 13 deletions(-) create mode 100644 tests/Unit/ClientSettingsTest.php diff --git a/app/DataMapper/ClientSettings.php b/app/DataMapper/ClientSettings.php index d0b559090b65..8b7935144180 100644 --- a/app/DataMapper/ClientSettings.php +++ b/app/DataMapper/ClientSettings.php @@ -37,6 +37,15 @@ class ClientSettings extends BaseSettings 'size_id' => 'string', ]; + public static $property_casts = [ + 'language_id' => 'string', + 'currency_id' => 'string', + 'payment_terms' => 'string', + 'valid_until' => 'string', + 'default_task_rate' => 'float', + 'send_reminders' => 'bool', + ]; + /** * Cast object values and return entire class * prevents missing properties from not being returned diff --git a/app/Factory/CloneQuoteToInvoiceFactory.php b/app/Factory/CloneQuoteToInvoiceFactory.php index 936ca18c5706..f2277c4eeb9f 100644 --- a/app/Factory/CloneQuoteToInvoiceFactory.php +++ b/app/Factory/CloneQuoteToInvoiceFactory.php @@ -28,7 +28,11 @@ class CloneQuoteToInvoiceFactory unset($quote_array['invoice_id']); unset($quote_array['id']); unset($quote_array['invitations']); - unset($quote_array['terms']); + + //preserve terms if they exist on Quotes + if(strlen($quote_array['terms']) < 2) + unset($quote_array['terms']); + // unset($quote_array['public_notes']); unset($quote_array['footer']); unset($quote_array['design_id']); diff --git a/app/Utils/Traits/ClientGroupSettingsSaver.php b/app/Utils/Traits/ClientGroupSettingsSaver.php index a7dabe4f2076..d04e830413ec 100644 --- a/app/Utils/Traits/ClientGroupSettingsSaver.php +++ b/app/Utils/Traits/ClientGroupSettingsSaver.php @@ -11,6 +11,7 @@ namespace App\Utils\Traits; +use App\DataMapper\ClientSettings; use App\DataMapper\CompanySettings; use stdClass; @@ -63,15 +64,6 @@ trait ClientGroupSettingsSaver $entity_settings->{$key} = $value; } - //this pass will handle any null values that are in the translations - // foreach ($settings->translations as $key => $value) { - // if (is_null($settings->translations[$key])) { - // $settings->translations[$key] = ''; - // } - // } - - // $entity_settings->translations = $settings->translations; - $entity->settings = $entity_settings; $entity->save(); @@ -92,6 +84,8 @@ trait ClientGroupSettingsSaver $settings = (object) $settings; $casts = CompanySettings::$casts; + // $casts = ClientSettings::$property_casts; + ksort($casts); if(property_exists($settings, 'translations')) @@ -121,7 +115,7 @@ trait ClientGroupSettingsSaver continue; } /*Separate loop if it is a _id field which is an integer cast as a string*/ - elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') { + elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || $key == 'payment_terms' || $key == 'valid_until') { $value = 'integer'; if (! property_exists($settings, $key)) { @@ -170,7 +164,7 @@ trait ClientGroupSettingsSaver } /*Separate loop if it is a _id field which is an integer cast as a string*/ - if (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') { + if (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || $key == 'payment_terms' || $key == 'valid_until') { $value = 'integer'; if (! property_exists($settings, $key)) { diff --git a/app/Utils/Traits/SettingsSaver.php b/app/Utils/Traits/SettingsSaver.php index fbcc7d44e1ad..764e18403145 100644 --- a/app/Utils/Traits/SettingsSaver.php +++ b/app/Utils/Traits/SettingsSaver.php @@ -52,7 +52,7 @@ trait SettingsSaver continue; } /*Separate loop if it is a _id field which is an integer cast as a string*/ - elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') { + elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || $key == 'payment_terms' || $key == 'valid_until') { $value = 'integer'; if($key == 'gmail_sending_user_id') diff --git a/tests/Unit/ClientSettingsTest.php b/tests/Unit/ClientSettingsTest.php new file mode 100644 index 000000000000..ea224af9d914 --- /dev/null +++ b/tests/Unit/ClientSettingsTest.php @@ -0,0 +1,337 @@ +makeTestData(); + + $this->faker = \Faker\Factory::create(); + + } + + + public function testClientBaseline() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals("1", $arr['data']['settings']['currency_id']); + + } + + + public function testClientValidSettings() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => '1', + 'payment_terms' => '1', + 'valid_until' => '1', + 'default_task_rate' => 10, + 'send_reminders' => true + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals("1", $arr['data']['settings']['currency_id']); + $this->assertEquals("1", $arr['data']['settings']['language_id']); + $this->assertEquals("1", $arr['data']['settings']['payment_terms']); + $this->assertEquals(10, $arr['data']['settings']['default_task_rate']); + $this->assertEquals(true, $arr['data']['settings']['send_reminders']); + $this->assertEquals("1", $arr['data']['settings']['valid_until']); + + } + + public function testClientIllegalCurrency() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => 'a', + 'language_id' => '1', + 'payment_terms' => '1', + 'valid_until' => '1', + 'default_task_rate' => 10, + 'send_reminders' => true + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(302); + + } + + public function testClientIllegalLanguage() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => 'a', + 'payment_terms' => '1', + 'valid_until' => '1', + 'default_task_rate' => 10, + 'send_reminders' => true + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(302); + + } + + public function testClientIllegalPaymenTerms() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => '1', + 'payment_terms' => 'a', + 'valid_until' => '1', + 'default_task_rate' => 10, + 'send_reminders' => true + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(302); + + } + + + public function testClientIllegalValidUntil() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => '1', + 'payment_terms' => '1', + 'valid_until' => 'a', + 'default_task_rate' => 10, + 'send_reminders' => true + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(302); + + } + + public function testClientIllegalDefaultTaskRate() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => '1', + 'payment_terms' => '1', + 'valid_until' => '1', + 'default_task_rate' => "a", + 'send_reminders' => true + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertFalse(array_key_exists('default_task_rate', $arr)); + + } + + public function testClientIllegalSendReminderBool() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => '1', + 'payment_terms' => '1', + 'valid_until' => '1', + 'default_task_rate' => "a", + 'send_reminders' => "faaalse" + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(302); + + } + + public function testClientSettingBools() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'settings' => [ + 'currency_id' => '1', + 'language_id' => '1', + 'payment_terms' => '1', + 'valid_until' => '1', + 'default_task_rate' => "a", + 'send_reminders' => "true" + ] + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + + + } + +} From cfd9ed487504b0d019d6b4093038156ee34e3e74 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 18 Apr 2022 23:10:43 +1000 Subject: [PATCH 16/22] Fixes for types in settings --- app/Factory/CloneQuoteToInvoiceFactory.php | 2 +- .../Requests/Client/StoreClientRequest.php | 2 +- app/Utils/Traits/ClientGroupSettingsSaver.php | 19 ++++++++++++------- app/Utils/Traits/SettingsSaver.php | 5 ++--- tests/Feature/CompanySettingsTest.php | 2 -- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/Factory/CloneQuoteToInvoiceFactory.php b/app/Factory/CloneQuoteToInvoiceFactory.php index f2277c4eeb9f..a9406449af2c 100644 --- a/app/Factory/CloneQuoteToInvoiceFactory.php +++ b/app/Factory/CloneQuoteToInvoiceFactory.php @@ -30,7 +30,7 @@ class CloneQuoteToInvoiceFactory unset($quote_array['invitations']); //preserve terms if they exist on Quotes - if(strlen($quote_array['terms']) < 2) + if(array_key_exists('terms', $quote_array) && strlen($quote_array['terms']) < 2) unset($quote_array['terms']); // unset($quote_array['public_notes']); diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index cf829ddcc1d7..06693fff25f9 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -36,7 +36,7 @@ class StoreClientRequest extends Request } public function rules() - { + {nlog($this->input); if ($this->input('documents') && is_array($this->input('documents'))) { $documents = count($this->input('documents')); diff --git a/app/Utils/Traits/ClientGroupSettingsSaver.php b/app/Utils/Traits/ClientGroupSettingsSaver.php index d04e830413ec..663b17394a2f 100644 --- a/app/Utils/Traits/ClientGroupSettingsSaver.php +++ b/app/Utils/Traits/ClientGroupSettingsSaver.php @@ -84,8 +84,6 @@ trait ClientGroupSettingsSaver $settings = (object) $settings; $casts = CompanySettings::$casts; - // $casts = ClientSettings::$property_casts; - ksort($casts); if(property_exists($settings, 'translations')) @@ -115,8 +113,12 @@ trait ClientGroupSettingsSaver continue; } /*Separate loop if it is a _id field which is an integer cast as a string*/ - elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || $key == 'payment_terms' || $key == 'valid_until') { - $value = 'integer'; + elseif (substr($key, -3) == '_id' || + substr($key, -14) == 'number_counter' || + ($key == 'payment_terms' && property_exists($settings, 'payment_terms') && strlen($settings->{$key}) >= 1) || + ($key == 'valid_until' && property_exists($settings, 'valid_until') && strlen($settings->{$key}) >= 1)) { + + $value = 'integer'; if (! property_exists($settings, $key)) { continue; @@ -164,7 +166,11 @@ trait ClientGroupSettingsSaver } /*Separate loop if it is a _id field which is an integer cast as a string*/ - if (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || $key == 'payment_terms' || $key == 'valid_until') { + if (substr($key, -3) == '_id' || + substr($key, -14) == 'number_counter' || + ($key == 'payment_terms' && property_exists($settings, 'payment_terms') && strlen($settings->{$key}) >= 1) || + ($key == 'valid_until' && property_exists($settings, 'valid_until') && strlen($settings->{$key}) >= 1)) { + $value = 'integer'; if (! property_exists($settings, $key)) { @@ -213,8 +219,7 @@ trait ClientGroupSettingsSaver switch ($key) { case 'int': case 'integer': - // return ctype_digit(strval(abs($value))); - return ctype_digit(strval($value)); + return is_numeric($value) && ctype_digit(strval(abs($value))); case 'real': case 'float': case 'double': diff --git a/app/Utils/Traits/SettingsSaver.php b/app/Utils/Traits/SettingsSaver.php index 764e18403145..b3ff682d281a 100644 --- a/app/Utils/Traits/SettingsSaver.php +++ b/app/Utils/Traits/SettingsSaver.php @@ -52,7 +52,7 @@ trait SettingsSaver continue; } /*Separate loop if it is a _id field which is an integer cast as a string*/ - elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || $key == 'payment_terms' || $key == 'valid_until') { + elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || ($key == 'payment_terms' && strlen($settings->{$key}) >= 1) || ($key == 'valid_until' && strlen($settings->{$key}) >= 1)) { $value = 'integer'; if($key == 'gmail_sending_user_id') @@ -94,12 +94,11 @@ trait SettingsSaver switch ($key) { case 'int': case 'integer': - return ctype_digit(strval(abs($value))); + return is_numeric($value) && ctype_digit(strval(abs($value))); case 'real': case 'float': case 'double': return !is_string($value) && (is_float($value) || is_numeric(strval($value))); - // return is_float($value) || is_numeric(strval($value)); case 'string': return !is_int($value) || ( is_string( $value ) && method_exists($value, '__toString') ) || is_null($value) || is_string($value); case 'bool': diff --git a/tests/Feature/CompanySettingsTest.php b/tests/Feature/CompanySettingsTest.php index 29b031333649..c521802586af 100644 --- a/tests/Feature/CompanySettingsTest.php +++ b/tests/Feature/CompanySettingsTest.php @@ -50,8 +50,6 @@ class CompanySettingsTest extends TestCase $this->company->saveSettings($settings, $this->company); - //$this->withoutExceptionHandling(); - $response = false; try { From 79ce3407eb9c3edfa27d63794abab4a86d5e5097 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 18 Apr 2022 23:38:31 +1000 Subject: [PATCH 17/22] Add back livewire configuration --- config/livewire.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/livewire.php b/config/livewire.php index 59f8d5c4ddc7..5be8cc56356d 100644 --- a/config/livewire.php +++ b/config/livewire.php @@ -54,7 +54,7 @@ return [ | */ - 'asset_url' => null, + 'asset_url' => env('ASSET_URL', null), /* |-------------------------------------------------------------------------- From 7d97b74f863385971c5e817bcb5cbbe524ca3275 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Apr 2022 09:56:26 +1000 Subject: [PATCH 18/22] Sort statements by due date ascending --- app/Services/Client/Statement.php | 2 +- app/Utils/HostedPDF/NinjaPdf.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Client/Statement.php b/app/Services/Client/Statement.php index c93ca1264daa..c05044f52c86 100644 --- a/app/Services/Client/Statement.php +++ b/app/Services/Client/Statement.php @@ -231,7 +231,7 @@ class Statement ->where('client_id', $this->client->id) ->whereIn('status_id', $this->invoiceStatuses()) ->whereBetween('date', [Carbon::parse($this->options['start_date']), Carbon::parse($this->options['end_date'])]) - ->orderBy('date', 'ASC') + ->orderBy('due_date', 'ASC') ->cursor(); } diff --git a/app/Utils/HostedPDF/NinjaPdf.php b/app/Utils/HostedPDF/NinjaPdf.php index bab5d6739fb6..6169a3980709 100644 --- a/app/Utils/HostedPDF/NinjaPdf.php +++ b/app/Utils/HostedPDF/NinjaPdf.php @@ -30,7 +30,7 @@ class NinjaPdf $response = $client->post($this->url,[ RequestOptions::JSON => ['html' => $html] ]); - + return $response->getBody(); } From 7bbc2b7f9cf3ecc5dc04b20f6e4b092e93dbb147 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Apr 2022 13:41:44 +1000 Subject: [PATCH 19/22] Remove unused includes --- app/Services/Client/Statement.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/Services/Client/Statement.php b/app/Services/Client/Statement.php index c05044f52c86..b96d72e23a7c 100644 --- a/app/Services/Client/Statement.php +++ b/app/Services/Client/Statement.php @@ -19,20 +19,14 @@ use App\Models\Client; use App\Models\Design; use App\Models\Invoice; use App\Models\Payment; -use App\Models\Product; use App\Services\PdfMaker\Design as PdfMakerDesign; use App\Services\PdfMaker\PdfMaker; use App\Utils\HostedPDF\NinjaPdf; use App\Utils\HtmlEngine; -use App\Utils\Ninja; use App\Utils\Number; use App\Utils\PhantomJS\Phantom; use App\Utils\Traits\Pdf\PdfMaker as PdfMakerTrait; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\LazyCollection; class Statement { From 6bffa1af109abe491ff55e8f293f4ab9acd31094 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Apr 2022 14:53:19 +1000 Subject: [PATCH 20/22] Improve error response from eWay gateway --- app/PaymentDrivers/Eway/CreditCard.php | 78 +++++++++++++++++++++----- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/app/PaymentDrivers/Eway/CreditCard.php b/app/PaymentDrivers/Eway/CreditCard.php index e1bc090ba3f6..b74e06863300 100644 --- a/app/PaymentDrivers/Eway/CreditCard.php +++ b/app/PaymentDrivers/Eway/CreditCard.php @@ -168,18 +168,56 @@ class CreditCard $response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction); +nlog($response); + $this->logResponse($response); - $response_status = ErrorCode::getStatus($response->ResponseMessage); + // if(!$response || !property_exists($response, 'ResponseMessage')) + // throw new PaymentFailed('The gateway did not return a valid response. Please check your gateway credentials.', 400); - if(!$response_status['success']){ + // $response_status = ErrorCode::getStatus($response->ResponseMessage); - $this->eway_driver->sendFailureMail($response_status['message']); + // if(!$response_status['success']){ - throw new PaymentFailed($response_status['message'], 400); - } + // if($response->getErrors()) + // { + // $message = false; + + // foreach ($response->getErrors() as $error) { + // $message = \Eway\Rapid::getMessage($error); + // } + + // $return_message = $message ?: $response_status['message']; + // } + + // $this->eway_driver->sendFailureMail($response_status['message']); + + // throw new PaymentFailed($response_status['message'], 400); + // } + + if($response->TransactionStatus) + $payment = $this->storePayment($response); + else { + + $message = 'Error processing payment.'; + + if(isset($response->ResponseMessage)) + $message .= " Gateway Error Code = {$response->ResponseMessage}"; + + if($response->getErrors()) + { + + foreach ($response->getErrors() as $error) { + $message = \Eway\Rapid::getMessage($error); + } + + } + + $this->eway_driver->sendFailureMail($message); + + throw new PaymentFailed($message, 400); + } - $payment = $this->storePayment($response); return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); @@ -257,20 +295,32 @@ class CreditCard $response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction); - $response_status = ErrorCode::getStatus($response->ResponseMessage); + if($response->TransactionStatus){ + $this->logResponse($response, true); + $payment = $this->storePayment($response); + } + else { - if(!$response_status['success']){ + $message = 'Error processing payment.'; - $this->logResponse($response, false); + if(isset($response->ResponseMessage)) + $message .= " Gateway Error Code = {$response->ResponseMessage}"; - $this->eway_driver->sendFailureMail($response_status['message']); + if($response->getErrors()) + { + + foreach ($response->getErrors() as $error) { + $message = \Eway\Rapid::getMessage($error); + } - throw new PaymentFailed($response_status['message'], 400); - } + } - $this->logResponse($response, true); + $this->logResponse($response, false); - $payment = $this->storePayment($response); + $this->eway_driver->sendFailureMail($message); + + throw new PaymentFailed($message, 400); + } return $payment; } From 5172723b8942a08c027d54f4c0fe048f2c191e40 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Apr 2022 16:32:54 +1000 Subject: [PATCH 21/22] Clean up logging --- app/PaymentDrivers/Eway/CreditCard.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/PaymentDrivers/Eway/CreditCard.php b/app/PaymentDrivers/Eway/CreditCard.php index b74e06863300..1190c4ca2942 100644 --- a/app/PaymentDrivers/Eway/CreditCard.php +++ b/app/PaymentDrivers/Eway/CreditCard.php @@ -168,8 +168,6 @@ class CreditCard $response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction); -nlog($response); - $this->logResponse($response); // if(!$response || !property_exists($response, 'ResponseMessage')) From a8b1252caac702a67de32b572e033ea790f3c943 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Apr 2022 16:33:46 +1000 Subject: [PATCH 22/22] v5.3.80 --- .github/workflows/release.yml | 1 - VERSION.txt | 2 +- config/ninja.php | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 31e5e8a4e0b1..ef67001aca93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,6 @@ jobs: php artisan key:generate php artisan optimize php artisan storage:link - php artisan livewire:publish sudo php artisan cache:clear sudo find ./vendor/bin/ -type f -exec chmod +x {} \; sudo find ./ -type d -exec chmod 755 {} \; diff --git a/VERSION.txt b/VERSION.txt index e003fe334fcd..9f7319154f05 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.3.79 \ No newline at end of file +5.3.80 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index b89a23be5bd8..e9e48c3786d8 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -14,8 +14,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.3.79', - 'app_tag' => '5.3.79', + 'app_version' => '5.3.80', + 'app_tag' => '5.3.80', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''),