From 09b895dfcb7018e3f37ad54f805afa7bdd4315a6 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 19 May 2022 00:32:40 +0200 Subject: [PATCH 01/43] INA-3 | Scheduler and ScheduledJob models & migrations --- app/Models/ScheduledJob.php | 49 +++++++++++++++++++ app/Models/Scheduler.php | 45 +++++++++++++++++ ..._18_162152_create_scheduled_jobs_table.php | 44 +++++++++++++++++ ...2_05_18_162443_create_schedulers_table.php | 45 +++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 app/Models/ScheduledJob.php create mode 100644 app/Models/Scheduler.php create mode 100644 database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php create mode 100644 database/migrations/2022_05_18_162443_create_schedulers_table.php diff --git a/app/Models/ScheduledJob.php b/app/Models/ScheduledJob.php new file mode 100644 index 000000000000..1ea55164386f --- /dev/null +++ b/app/Models/ScheduledJob.php @@ -0,0 +1,49 @@ + 'array', + 'scheduled_run' => 'date', + 'parameters' => 'array' + ]; +} diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php new file mode 100644 index 000000000000..2954a1e47ecd --- /dev/null +++ b/app/Models/Scheduler.php @@ -0,0 +1,45 @@ +hasOne(ScheduledJob::class, 'scheduler_id', 'id'); + } +} diff --git a/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php b/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php new file mode 100644 index 000000000000..7b5b93465a69 --- /dev/null +++ b/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php @@ -0,0 +1,44 @@ +id(); + $table->string('action_name'); + $table->string('action_class'); + $table->json('parameters')->nullable(); + $table->foreignIdFor(\App\Models\Scheduler::class); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('scheduled_jobs'); + } +} diff --git a/database/migrations/2022_05_18_162443_create_schedulers_table.php b/database/migrations/2022_05_18_162443_create_schedulers_table.php new file mode 100644 index 000000000000..fbe3ebe6bba1 --- /dev/null +++ b/database/migrations/2022_05_18_162443_create_schedulers_table.php @@ -0,0 +1,45 @@ +id(); + $table->boolean('paused')->default(false); + $table->boolean('archived')->default(false); + $table->string('repeat_every'); + $table->timestamp('start_from'); + $table->timestamp('scheduled_run'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('schedulers'); + } +} From b3bf078f4c7cbb752181c4d9198e4e2efb0ab3b6 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 19 May 2022 00:33:00 +0200 Subject: [PATCH 02/43] INA-3 | Request class for creating scheduled task --- .../CreateScheduledTaskRequest.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php diff --git a/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php b/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php new file mode 100644 index 000000000000..1f3ef03682f1 --- /dev/null +++ b/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php @@ -0,0 +1,32 @@ +user()->isAdmin(); + } + + public function rules() + { + return [ + 'paused' => 'sometimes|bool', + 'archived' => 'sometimes|bool', + 'repeat_every' => 'required|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', + 'start_from' => 'sometimes|string', + 'job' => 'required', + + ]; + } +} From 131cacd47f88745a608dd0c326ebe66d37527958 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 19 May 2022 00:33:29 +0200 Subject: [PATCH 03/43] INA-5 | TaskScheduler Job [WIP] --- app/Jobs/Ninja/TaskScheduler.php | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 app/Jobs/Ninja/TaskScheduler.php diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php new file mode 100644 index 000000000000..772a1722ef78 --- /dev/null +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -0,0 +1,120 @@ +fetchJobs(); + foreach ($pending_schedulers as $scheduler) { + $this->doJob($scheduler); + } + } + + private function doJob(Scheduler $scheduler) + { + $job = $scheduler->job; + $parameters = $job->parameters; + $company = Company::where('company_key', $parameters['company']['company_key'])->first(); + if (!$job) { + return; + } + switch ($job->action_name) { + case ScheduledJob::CREATE_CLIENT_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'contacts.csv'); + break; + case ScheduledJob::CREATE_CLIENT_CONTACT_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'clients.csv'); + break; + case ScheduledJob::CREATE_CREDIT_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'credits.csv'); + break; + case ScheduledJob::CREATE_DOCUMENT_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'documents.csv'); + break; + case ScheduledJob::CREATE_EXPENSE_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'expense.csv'); + break; + case ScheduledJob::CREATE_INVOICE_ITEM_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'invoice_items.csv'); + break; + case ScheduledJob::CREATE_INVOICE_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'invoices.csv'); + break; + case ScheduledJob::CREATE_PAYMENT_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'payments.csv'); + break; + case ScheduledJob::CREATE_PRODUCT_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'products.csv'); + break; + case ScheduledJob::CREATE_PROFIT_AND_LOSS_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'profit_and_loss.csv'); + break; + case ScheduledJob::CREATE_QUOTE_ITEM_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'quote_items.csv'); + break; + case ScheduledJob::CREATE_QUOTE_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'quotes.csv'); + break; + case ScheduledJob::CREATE_RECURRING_INVOICE_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'recurring_invoices.csv'); + break; + case ScheduledJob::CREATE_TASK_REPORT: + SendToAdmin::dispatch($company, $parameters, $job->action_class, 'tasks.csv'); + break; + + } + + //setup new scheduled_run + } + + private function fetchJobs() + { + + return Scheduler::where('paused', false) + ->where('archived', false) + ->whereDate('scheduled_run', '<=', Carbon::now()) + ->get(); + } + +} From 753978fe862769969058b83e3c7341d8a823c20a Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 19 May 2022 00:33:38 +0200 Subject: [PATCH 04/43] INA-5 | TaskScheduler Job [WIP] --- app/Console/Kernel.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index b472bf3a7442..c4e4afdf9e24 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -19,6 +19,7 @@ use App\Jobs\Ledger\LedgerBalanceUpdate; use App\Jobs\Ninja\AdjustEmailQuota; use App\Jobs\Ninja\CompanySizeCheck; use App\Jobs\Ninja\QueueSize; +use App\Jobs\Ninja\TaskScheduler; use App\Jobs\Util\DiskCleanup; use App\Jobs\Util\ReminderJob; use App\Jobs\Util\SchedulerCheck; @@ -69,17 +70,18 @@ class Kernel extends ConsoleKernel $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping(); - $schedule->job(new AutoBillCron)->dailyAt('06:00')->withoutOverlapping(); + $schedule->job(new AutoBillCron)->dailyAt('06:00')->withoutOverlapping(); $schedule->job(new SchedulerCheck)->daily()->withoutOverlapping(); - if(Ninja::isSelfHost()) - { + $schedule->job(new TaskScheduler())->daily()->withoutOverlapping(); + + if (Ninja::isSelfHost()) { $schedule->call(function () { Account::whereNotNull('id')->update(['is_scheduler_running' => true]); - })->everyFiveMinutes(); - + })->everyFiveMinutes(); + } /* Run hosted specific jobs */ @@ -97,12 +99,12 @@ class Kernel extends ConsoleKernel } - if(config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) { + if (config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) { $schedule->command('queue:work database --stop-when-empty')->everyMinute()->withoutOverlapping(); - $schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping(); - + $schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping(); + } } @@ -114,7 +116,7 @@ class Kernel extends ConsoleKernel */ protected function commands() { - $this->load(__DIR__.'/Commands'); + $this->load(__DIR__ . '/Commands'); require base_path('routes/console.php'); } From 39400d3b087c85e566f06889c5c948aae269e096 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 19 May 2022 00:34:09 +0200 Subject: [PATCH 05/43] INA-5 | Scheduler Job Creation [WIP] --- .../Controllers/TaskSchedulerController.php | 175 ++++++++++++++++++ routes/api.php | 12 +- 2 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 app/Http/Controllers/TaskSchedulerController.php diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php new file mode 100644 index 000000000000..706d01cb8e3b --- /dev/null +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -0,0 +1,175 @@ +paused = $request->get('paused', false); + $scheduler->archived = (bool)$request->get('archived', false); + $scheduler->start_from = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now(); + $scheduler->repeat_every = $request->get('repeat_every'); + $scheduler->scheduled_run = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now();; + $scheduler->save(); + + if ($this->createJob($request, $scheduler)) { + $job = ScheduledJob::query()->latest()->first(); + } + return $job; + } + + public function createJob(CreateScheduledTaskRequest $request, Scheduler $scheduler): bool + { + $job = new ScheduledJob(); + + switch ($request->job) { + case 'client_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_CLIENT_REPORT; + $job->action_class = $this->getClassPath(ClientExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'client_contact_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_CLIENT_CONTACT_REPORT; + $job->action_class = $this->getClassPath(ContactExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'credit_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_CREDIT_REPORT; + $job->action_class = $this->getClassPath(CreditExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'document_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_DOCUMENT_REPORT; + $job->action_class = $this->getClassPath(DocumentExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'expense_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_EXPENSE_REPORT; + $job->action_class = $this->getClassPath(ExpenseExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'invoice_item_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_INVOICE_ITEM_REPORT; + $job->action_class = $this->getClassPath(InvoiceItemExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'invoice_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_INVOICE_REPORT; + $job->action_class = $this->getClassPath(InvoiceExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'payment_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_PAYMENT_REPORT; + $job->action_class = $this->getClassPath(PaymentExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'product_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_PRODUCT_REPORT; + $job->action_class = $this->getClassPath(ProductExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'profit_and_loss_report': + $rules = (new ProfitLossRequest())->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_PROFIT_AND_LOSS_REPORT; + $job->action_class = $this->getClassPath(ProfitAndLoss::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'quote_item_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_QUOTE_ITEM_REPORT; + $job->action_class = $this->getClassPath(QuoteItemExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'quote_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_QUOTE_REPORT; + $job->action_class = $this->getClassPath(QuoteExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'recurring_invoice_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_RECURRING_INVOICE_REPORT; + $job->action_class = $this->getClassPath(RecurringInvoiceExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + case 'task_report': + $rules = (new GenericReportRequest)->rules(); + $validated = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_TASK_REPORT; + $job->action_class = $this->getClassPath(TaskExport::class); + $job->parameters = $this->saveActionParameters($rules, $request); + break; + + } + $job->scheduler_id = $scheduler->id; + return $job->save(); + + } + + public function getClassPath($class): string + { + return $class = is_object($class) ? get_class($class) : $class; + } + + public function saveActionParameters(array $rules, $request): array + { + $parameters = []; + foreach ($rules as $rule => $key) { + if (isset($request->{$rule})) { + $parameters[$rule] = $request->{$rule}; + } + } + $parameters['company'] = auth()->user()->company(); + return $parameters; + } + + +} diff --git a/routes/api.php b/routes/api.php index fe04fbe3c2bf..abec15338f5a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -45,8 +45,8 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale Route::post('filters/{entity}', 'FilterController@index')->name('filters'); - Route::resource('client_gateway_tokens', 'ClientGatewayTokenController'); - + Route::resource('client_gateway_tokens', 'ClientGatewayTokenController'); + Route::post('connected_account', 'ConnectedAccountController@index'); Route::post('connected_account/gmail', 'ConnectedAccountController@handleGmailOauth'); @@ -54,9 +54,9 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale Route::post('companies/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected'); Route::post('companies/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected'); - + Route::resource('companies', 'CompanyController'); // name = (companies. index / create / show / update / destroy / edit - + Route::put('companies/{company}/upload', 'CompanyController@upload'); Route::post('companies/{company}/default', 'CompanyController@default'); @@ -170,6 +170,10 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale Route::post('reports/tasks', 'Reports\TaskReportController'); Route::post('reports/profitloss', 'Reports\ProfitAndLossController'); + + Route::post('task_scheduler', 'TaskSchedulerController@store'); + + Route::get('scheduler', 'SchedulerController@index'); Route::post('support/messages/send', 'Support\Messages\SendingController'); From 5a6f4801ceb3eb6e8051c50d561d251acec3e6dd Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Sat, 21 May 2022 20:53:22 +0200 Subject: [PATCH 06/43] INA-5 | Set multidb, set company_id as database parameter --- .../Controllers/TaskSchedulerController.php | 4 ++- app/Jobs/Ninja/TaskScheduler.php | 36 +++++++++++++++++-- app/Models/ScheduledJob.php | 4 +-- ..._18_162152_create_scheduled_jobs_table.php | 1 + 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index 706d01cb8e3b..932aa13ebcbe 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -51,6 +51,8 @@ class TaskSchedulerController extends BaseController switch ($request->job) { case 'client_report': $rules = (new GenericReportRequest)->rules(); + //custom rules for example here we require date_range but in genericRequest class we don't + $rules['date_range'] = 'string|required'; $validated = $request->validate($rules); $job->action_name = ScheduledJob::CREATE_CLIENT_REPORT; $job->action_class = $this->getClassPath(ClientExport::class); @@ -150,6 +152,7 @@ class TaskSchedulerController extends BaseController } $job->scheduler_id = $scheduler->id; + $job->company_id = auth()->user()->company()->id; return $job->save(); } @@ -167,7 +170,6 @@ class TaskSchedulerController extends BaseController $parameters[$rule] = $request->{$rule}; } } - $parameters['company'] = auth()->user()->company(); return $parameters; } diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 772a1722ef78..0ad5a0492a45 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -13,6 +13,7 @@ namespace App\Jobs\Ninja; use App\Jobs\Report\SendToAdmin; +use App\Libraries\MultiDB; use App\Models\Company; use App\Models\ScheduledJob; use App\Models\Scheduler; @@ -45,6 +46,7 @@ class TaskScheduler implements ShouldQueue */ public function handle() { + $pending_schedulers = $this->fetchJobs(); foreach ($pending_schedulers as $scheduler) { $this->doJob($scheduler); @@ -54,11 +56,15 @@ class TaskScheduler implements ShouldQueue private function doJob(Scheduler $scheduler) { $job = $scheduler->job; - $parameters = $job->parameters; - $company = Company::where('company_key', $parameters['company']['company_key'])->first(); - if (!$job) { + + $company = Company::find($job->company_id); + if (!$job || !$company) { return; } + MultiDB::setDb($company->db); + $parameters = $job->parameters; + + switch ($job->action_name) { case ScheduledJob::CREATE_CLIENT_REPORT: SendToAdmin::dispatch($company, $parameters, $job->action_class, 'contacts.csv'); @@ -106,6 +112,30 @@ class TaskScheduler implements ShouldQueue } //setup new scheduled_run + $amount_of_days_until_next_run = $this->getAmountOfDays($scheduler->repeat_every); + $scheduler->scheduled_run = Carbon::now()->addDays($amount_of_days_until_next_run); + $scheduler->save(); + } + + private function getAmountOfDays(string $repeat_every): int + { + switch ($repeat_every) { + case Scheduler::DAILY: + return 1; + break; + case Scheduler::MONTHLY: + return 30; + break; + case Scheduler::WEEKLY: + return 7; + break; + case Scheduler::QUARTERLY: + return 90; + break; + case Scheduler::ANNUALLY: + return 365; + break; + } } private function fetchJobs() diff --git a/app/Models/ScheduledJob.php b/app/Models/ScheduledJob.php index 1ea55164386f..f339ae46ddb6 100644 --- a/app/Models/ScheduledJob.php +++ b/app/Models/ScheduledJob.php @@ -19,6 +19,7 @@ use Illuminate\Database\Eloquent\Model; * @property array parameters * @property string action_name * @property integer scheduler_id + * @property integer company_id */ class ScheduledJob extends Model { @@ -40,9 +41,8 @@ class ScheduledJob extends Model const CREATE_TASK_REPORT = 'create_task_report'; - protected $fillable = ['action_class', 'action_name', 'parameters', 'scheduler_id']; + protected $fillable = ['action_class', 'action_name', 'parameters', 'scheduler_id','company_id']; protected $casts = [ - 'required_parameters' => 'array', 'scheduled_run' => 'date', 'parameters' => 'array' ]; diff --git a/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php b/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php index 7b5b93465a69..1395e8aa16e1 100644 --- a/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php +++ b/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php @@ -27,6 +27,7 @@ class CreateScheduledJobsTable extends Migration $table->string('action_name'); $table->string('action_class'); $table->json('parameters')->nullable(); + $table->foreignIdFor(\App\Models\Company::class); $table->foreignIdFor(\App\Models\Scheduler::class); $table->timestamps(); }); From 89373701c944a9ae6dedf0b5dae675b8b39c37b3 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:08:08 +0200 Subject: [PATCH 07/43] INA-5 | Add service | append linked job --- app/Models/Scheduler.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index 2954a1e47ecd..6eb3c837539b 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -11,6 +11,7 @@ namespace App\Models; +use App\Services\TaskScheduler\TaskSchedulerService; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -32,12 +33,27 @@ class Scheduler extends Model 'repeat_every', 'scheduled_run', ]; + protected $appends = ['linked_job']; + const DAILY = 'DAY'; const WEEKLY = 'WEEK'; const MONTHLY = 'MONTH'; const QUARTERLY = '3MONTHS'; const ANNUALLY = 'YEAR'; + public function getLinkedJobAttribute() + { + return $this->job ?? []; + } + + /** + * Service entry points. + */ + public function service(): TaskSchedulerService + { + return new TaskSchedulerService($this); + } + public function job(): \Illuminate\Database\Eloquent\Relations\HasOne { return $this->hasOne(ScheduledJob::class, 'scheduler_id', 'id'); From 041f85291a5dfce6ee82d7b3014fb8cf9d414aed Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:31:48 +0200 Subject: [PATCH 08/43] INA-5 | Add task scheduler resource route --- routes/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routes/api.php b/routes/api.php index abec15338f5a..5621563a79ef 100644 --- a/routes/api.php +++ b/routes/api.php @@ -171,8 +171,8 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale Route::post('reports/profitloss', 'Reports\ProfitAndLossController'); - Route::post('task_scheduler', 'TaskSchedulerController@store'); - + Route::resource('task_scheduler', 'TaskSchedulerController')->except('edit')->parameters(['task_scheduler' => 'scheduler']); + Route::put('task_scheduler/{scheduler}/update_job','TaskSchedulerController@updateJob'); Route::get('scheduler', 'SchedulerController@index'); Route::post('support/messages/send', 'Support\Messages\SendingController'); From 8a6ccba0af2793350f21645bc5cd5ba8a9859159 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:32:31 +0200 Subject: [PATCH 09/43] INA-5 | Update Schedule |Create Scheduler | Update Scheduled Job requests --- .../CreateScheduledTaskRequest.php | 1 - .../TaskScheduler/UpdateScheduleRequest.php | 41 +++++++++++++++++++ .../UpdateScheduledJobRequest.php | 28 +++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php create mode 100644 app/Http/Requests/TaskScheduler/UpdateScheduledJobRequest.php diff --git a/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php b/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php index 1f3ef03682f1..1e86810eb5eb 100644 --- a/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php +++ b/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php @@ -26,7 +26,6 @@ class CreateScheduledTaskRequest extends Request 'repeat_every' => 'required|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', 'start_from' => 'sometimes|string', 'job' => 'required', - ]; } } diff --git a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php new file mode 100644 index 000000000000..28e9cf47319a --- /dev/null +++ b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php @@ -0,0 +1,41 @@ +user()->isAdmin(); + } + + public function rules(): array + { + return [ + 'paused' => 'sometimes|bool', + 'archived' => 'sometimes|bool', + 'repeat_every' => 'sometimes|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', + 'start_from' => 'sometimes|string', + 'job' => 'sometimes|string', + ]; + } +} diff --git a/app/Http/Requests/TaskScheduler/UpdateScheduledJobRequest.php b/app/Http/Requests/TaskScheduler/UpdateScheduledJobRequest.php new file mode 100644 index 000000000000..96e3ec47bbfd --- /dev/null +++ b/app/Http/Requests/TaskScheduler/UpdateScheduledJobRequest.php @@ -0,0 +1,28 @@ +user()->isAdmin(); + } + + public function rules(): array + { + return [ + 'action_name' => 'sometimes|string', + ]; + } +} From 209ddf12b452edf1a9271052451765f523f3da29 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:33:13 +0200 Subject: [PATCH 10/43] INA-5 | Fix formatting --- app/Models/ScheduledJob.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Models/ScheduledJob.php b/app/Models/ScheduledJob.php index f339ae46ddb6..7b8e0a002439 100644 --- a/app/Models/ScheduledJob.php +++ b/app/Models/ScheduledJob.php @@ -35,13 +35,13 @@ class ScheduledJob extends Model const CREATE_PAYMENT_REPORT = 'create_payment_report'; const CREATE_PRODUCT_REPORT = 'create_product_report'; const CREATE_PROFIT_AND_LOSS_REPORT = 'create_profit_and_loss_report'; - const CREATE_QUOTE_ITEM_REPORT ='create_quote_item_report'; + const CREATE_QUOTE_ITEM_REPORT = 'create_quote_item_report'; const CREATE_QUOTE_REPORT = 'create_quote_report'; const CREATE_RECURRING_INVOICE_REPORT = 'create_recurring_invoice_report'; const CREATE_TASK_REPORT = 'create_task_report'; - protected $fillable = ['action_class', 'action_name', 'parameters', 'scheduler_id','company_id']; + protected $fillable = ['action_class', 'action_name', 'parameters', 'scheduler_id', 'company_id']; protected $casts = [ 'scheduled_run' => 'date', 'parameters' => 'array' From be5439c1ec72dc06b008333c58369a12034679dc Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:33:33 +0200 Subject: [PATCH 11/43] INA-5 | Added job @property in the Scheduler Model --- app/Models/Scheduler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index 6eb3c837539b..6cc11588bf4d 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\Model; * @property \Carbon\Carbon|mixed start_from * @property string repeat_every * @property \Carbon\Carbon|mixed scheduled_run + * @property mixed job */ class Scheduler extends Model { From 6aa01001c949097b939c5d84461bc3f5440b0b26 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:33:56 +0200 Subject: [PATCH 12/43] INA-5 | Moved Actions to the service | Controller clean-up --- .../Controllers/TaskSchedulerController.php | 177 +++----------- .../TaskScheduler/TaskSchedulerService.php | 216 ++++++++++++++++++ 2 files changed, 247 insertions(+), 146 deletions(-) create mode 100644 app/Services/TaskScheduler/TaskSchedulerService.php diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index 932aa13ebcbe..0dc9f536e7f5 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -3,174 +3,59 @@ namespace App\Http\Controllers; -use App\Export\CSV\ClientExport; -use App\Export\CSV\ContactExport; -use App\Export\CSV\CreditExport; -use App\Export\CSV\DocumentExport; -use App\Export\CSV\ExpenseExport; -use App\Export\CSV\InvoiceExport; -use App\Export\CSV\InvoiceItemExport; -use App\Export\CSV\PaymentExport; -use App\Export\CSV\ProductExport; -use App\Export\CSV\QuoteExport; -use App\Export\CSV\QuoteItemExport; -use App\Export\CSV\RecurringInvoiceExport; -use App\Export\CSV\TaskExport; -use App\Http\Requests\Report\GenericReportRequest; -use App\Http\Requests\Report\ProfitLossRequest; use App\Http\Requests\TaskScheduler\CreateScheduledTaskRequest; +use App\Http\Requests\TaskScheduler\UpdateScheduledJobRequest; +use App\Http\Requests\TaskScheduler\UpdateScheduleRequest; use App\Jobs\Report\ProfitAndLoss; use App\Models\ScheduledJob; use App\Models\Scheduler; use Carbon\Carbon; +use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpFoundation\Request; class TaskSchedulerController extends BaseController { + + public function index() + { + return Scheduler::all(); + } + + + /** + * @param \App\Http\Requests\TaskScheduler\CreateScheduledTaskRequest $request + */ public function store(CreateScheduledTaskRequest $request) { - $scheduler = new Scheduler(); - $scheduler->paused = $request->get('paused', false); - $scheduler->archived = (bool)$request->get('archived', false); - $scheduler->start_from = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now(); - $scheduler->repeat_every = $request->get('repeat_every'); - $scheduler->scheduled_run = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now();; - $scheduler->save(); - - if ($this->createJob($request, $scheduler)) { - $job = ScheduledJob::query()->latest()->first(); - } - return $job; + return $scheduler->service()->store($scheduler, $request); } - public function createJob(CreateScheduledTaskRequest $request, Scheduler $scheduler): bool + public function show(Scheduler $scheduler): Scheduler { - $job = new ScheduledJob(); + return $scheduler; + } - switch ($request->job) { - case 'client_report': - $rules = (new GenericReportRequest)->rules(); - //custom rules for example here we require date_range but in genericRequest class we don't - $rules['date_range'] = 'string|required'; - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_CLIENT_REPORT; - $job->action_class = $this->getClassPath(ClientExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'client_contact_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_CLIENT_CONTACT_REPORT; - $job->action_class = $this->getClassPath(ContactExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'credit_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_CREDIT_REPORT; - $job->action_class = $this->getClassPath(CreditExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'document_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_DOCUMENT_REPORT; - $job->action_class = $this->getClassPath(DocumentExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'expense_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_EXPENSE_REPORT; - $job->action_class = $this->getClassPath(ExpenseExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'invoice_item_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_INVOICE_ITEM_REPORT; - $job->action_class = $this->getClassPath(InvoiceItemExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'invoice_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_INVOICE_REPORT; - $job->action_class = $this->getClassPath(InvoiceExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'payment_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_PAYMENT_REPORT; - $job->action_class = $this->getClassPath(PaymentExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'product_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_PRODUCT_REPORT; - $job->action_class = $this->getClassPath(ProductExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'profit_and_loss_report': - $rules = (new ProfitLossRequest())->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_PROFIT_AND_LOSS_REPORT; - $job->action_class = $this->getClassPath(ProfitAndLoss::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'quote_item_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_QUOTE_ITEM_REPORT; - $job->action_class = $this->getClassPath(QuoteItemExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'quote_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_QUOTE_REPORT; - $job->action_class = $this->getClassPath(QuoteExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'recurring_invoice_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_RECURRING_INVOICE_REPORT; - $job->action_class = $this->getClassPath(RecurringInvoiceExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; - case 'task_report': - $rules = (new GenericReportRequest)->rules(); - $validated = $request->validate($rules); - $job->action_name = ScheduledJob::CREATE_TASK_REPORT; - $job->action_class = $this->getClassPath(TaskExport::class); - $job->parameters = $this->saveActionParameters($rules, $request); - break; + /** + * @param \App\Models\Scheduler $scheduler + * @param \App\Http\Requests\TaskScheduler\UpdateScheduleRequest $request + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response + */ + public function update(Scheduler $scheduler, UpdateScheduleRequest $request) + { + return $scheduler->service()->update($scheduler, $request); + } - } - $job->scheduler_id = $scheduler->id; - $job->company_id = auth()->user()->company()->id; - return $job->save(); + public function updateJob(Scheduler $scheduler, UpdateScheduledJobRequest $request) + { + return $scheduler->service()->updateJob($scheduler, $request); } - public function getClassPath($class): string - { - return $class = is_object($class) ? get_class($class) : $class; - } - public function saveActionParameters(array $rules, $request): array + public function destroy(Scheduler $scheduler) { - $parameters = []; - foreach ($rules as $rule => $key) { - if (isset($request->{$rule})) { - $parameters[$rule] = $request->{$rule}; - } - } - return $parameters; + return $scheduler->service()->destroy($scheduler); } diff --git a/app/Services/TaskScheduler/TaskSchedulerService.php b/app/Services/TaskScheduler/TaskSchedulerService.php new file mode 100644 index 000000000000..c47f57a4e2c0 --- /dev/null +++ b/app/Services/TaskScheduler/TaskSchedulerService.php @@ -0,0 +1,216 @@ +scheduler = $scheduler; + } + + public function store(Scheduler $scheduler, CreateScheduledTaskRequest $request) + { + $scheduler->paused = $request->get('paused', false); + $scheduler->archived = (bool)$request->get('archived', false); + $scheduler->start_from = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now(); + $scheduler->repeat_every = $request->get('repeat_every'); + $scheduler->scheduled_run = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now();; + $scheduler->save(); + + if ($this->createJob($request, $scheduler)) { + return response(['job_has_been_created'], 200); + } + return response(['failed_to_create_job'], 400); + } + + public function update(Scheduler $scheduler, UpdateScheduleRequest $request) + { + $data = $request->validated(); + if ($request->has('start_from')) { + $data['start_from'] = Carbon::parse((int)$request->get('start_from')); + $data['scheduled_run'] = Carbon::parse((int)$request->get('start_from')); + } + $update = $this->scheduler->update($data); + if ($update) { + return response(['successfully_updated_scheduler']); + } + return response(['failed_to_update_scheduler'], 400); + } + + public function createJob(CreateScheduledTaskRequest $request, Scheduler $scheduler): bool + { + $job = new ScheduledJob(); + $job = $this->setJobParameters($job, $request); + $job->scheduler_id = $scheduler->id; + $job->company_id = auth()->user()->company()->id; + return $job->save(); + + } + + public function setJobParameters(ScheduledJob $job, $request): ScheduledJob + { + switch ($request->job) { + case ScheduledJob::CREATE_CLIENT_REPORT: + $rules = (new GenericReportRequest)->rules(); + //custom rules for example here we require date_range but in genericRequest class we don't + $rules['date_range'] = 'string|required'; + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_CLIENT_REPORT; + $job->action_class = $this->getClassPath(ClientExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_CLIENT_CONTACT_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_CLIENT_CONTACT_REPORT; + $job->action_class = $this->getClassPath(ContactExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_CREDIT_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_CREDIT_REPORT; + $job->action_class = $this->getClassPath(CreditExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_DOCUMENT_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_DOCUMENT_REPORT; + $job->action_class = $this->getClassPath(DocumentExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_EXPENSE_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_EXPENSE_REPORT; + $job->action_class = $this->getClassPath(ExpenseExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_INVOICE_ITEM_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_INVOICE_ITEM_REPORT; + $job->action_class = $this->getClassPath(InvoiceItemExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_INVOICE_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_INVOICE_REPORT; + $job->action_class = $this->getClassPath(InvoiceExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_PAYMENT_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_PAYMENT_REPORT; + $job->action_class = $this->getClassPath(PaymentExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_PRODUCT_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_PRODUCT_REPORT; + $job->action_class = $this->getClassPath(ProductExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_PROFIT_AND_LOSS_REPORT: + $rules = (new ProfitLossRequest())->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_PROFIT_AND_LOSS_REPORT; + $job->action_class = $this->getClassPath(ProfitAndLoss::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_QUOTE_ITEM_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_QUOTE_ITEM_REPORT; + $job->action_class = $this->getClassPath(QuoteItemExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_QUOTE_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_QUOTE_REPORT; + $job->action_class = $this->getClassPath(QuoteExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_RECURRING_INVOICE_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_RECURRING_INVOICE_REPORT; + $job->action_class = $this->getClassPath(RecurringInvoiceExport::class); + $job->parameters = $validatedJobData; + break; + case ScheduledJob::CREATE_TASK_REPORT: + $rules = (new GenericReportRequest)->rules(); + $validatedJobData = $request->validate($rules); + $job->action_name = ScheduledJob::CREATE_TASK_REPORT; + $job->action_class = $this->getClassPath(TaskExport::class); + $job->parameters = $validatedJobData; + break; + + } + return $job; + } + + public function getClassPath($class): string + { + return $class = is_object($class) ? get_class($class) : $class; + } + + public function destroy(Scheduler $scheduler) + { + $job = $scheduler->job; + if ($job) { + $job->delete(); + } + $scheduler->delete(); + return response(['successfully_deleted_scheduler_and_job_associated_to_him']); + } + + public function updateJob(Scheduler $scheduler, UpdateScheduledJobRequest $request) + { + $job = $scheduler->job; + if (!$job) { + return abort(404); + } + $job = $this->setJobParameters($job, $request); + $job->save(); + + return response('job_successfully_updated'); + + + } +} From b4c0fe3d5329189ef91b93c567bb11c71198c744 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:39:10 +0200 Subject: [PATCH 13/43] INA-5 | Removed excess comment --- app/Jobs/Ninja/TaskScheduler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 0ad5a0492a45..dabc0701d0db 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -111,7 +111,6 @@ class TaskScheduler implements ShouldQueue } - //setup new scheduled_run $amount_of_days_until_next_run = $this->getAmountOfDays($scheduler->repeat_every); $scheduler->scheduled_run = Carbon::now()->addDays($amount_of_days_until_next_run); $scheduler->save(); From e8a24764630212fcebc9269af9e449bbf359b67f Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 01:39:20 +0200 Subject: [PATCH 14/43] INA-5 | Fix old docs conflicts --- app/Http/Controllers/Reports/CreditReportController.php | 8 ++++---- .../Controllers/Reports/QuoteItemReportController.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Reports/CreditReportController.php b/app/Http/Controllers/Reports/CreditReportController.php index 87087ed5f545..150a3c1313b4 100644 --- a/app/Http/Controllers/Reports/CreditReportController.php +++ b/app/Http/Controllers/Reports/CreditReportController.php @@ -31,11 +31,11 @@ class CreditReportController extends BaseController /** * @OA\Post( - * path="/api/v1/reports/clients", - * operationId="getClientReport", + * path="/api/v1/reports/credit", + * operationId="getCreditReport", * tags={"reports"}, - * summary="Client reports", - * description="Export client reports", + * summary="Credit reports", + * description="Export credit reports", * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\RequestBody( diff --git a/app/Http/Controllers/Reports/QuoteItemReportController.php b/app/Http/Controllers/Reports/QuoteItemReportController.php index 91abb4d7d299..9368e3d2c243 100644 --- a/app/Http/Controllers/Reports/QuoteItemReportController.php +++ b/app/Http/Controllers/Reports/QuoteItemReportController.php @@ -31,11 +31,11 @@ class QuoteItemReportController extends BaseController /** * @OA\Post( - * path="/api/v1/reports/invoice_items", + * path="/api/v1/reports/quote_items", * operationId="getQuoteItemReport", * tags={"reports"}, - * summary="Invoice item reports", - * description="Export invoice item reports", + * summary="Quote item reports", + * description="Export Quote item reports", * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\RequestBody( From fd028102bd0fc36e05a40e1bc811faca1898f498 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 02:13:23 +0200 Subject: [PATCH 15/43] INA-5 | Add docs for a task scheduler --- .../OpenAPI/TaskSchedulerSchema.php | 51 ++++++ .../Controllers/TaskSchedulerController.php | 171 +++++++++++++++++- .../TaskScheduler/UpdateScheduleRequest.php | 1 - 3 files changed, 215 insertions(+), 8 deletions(-) create mode 100644 app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php diff --git a/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php b/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php new file mode 100644 index 000000000000..59f6c999a8b4 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php @@ -0,0 +1,51 @@ +service()->store($scheduler, $request); } + /** + * @OA\GET( + * path="/api/v1/task_scheduler/{scheduler}", + * operationId="showTaskScheduler", + * tags={"task_scheduler"}, + * summary="Show given scheduler", + * description="Get scheduler with associated job", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Response( + * response=200, + * description="success", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + */ + public function show(Scheduler $scheduler): Scheduler { return $scheduler; } /** - * @param \App\Models\Scheduler $scheduler - * @param \App\Http\Requests\TaskScheduler\UpdateScheduleRequest $request - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response + * @OA\PUT( + * path="/api/v1/task_scheduler/{scheduler}", + * operationId="updateTaskScheduler", + * tags={"task_scheduler"}, + * summary="Update task scheduler ", + * description="Update task scheduler", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\RequestBody( + * required=true, + * @OA\JsonContent(ref="#/components/schemas/UpdateTaskSchedulerSchema") + * ), + * @OA\Response( + * response=200, + * description="success", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) */ public function update(Scheduler $scheduler, UpdateScheduleRequest $request) { return $scheduler->service()->update($scheduler, $request); } - + /** + * @OA\PUT( + * path="/api/v1/task_scheduler/{scheduler}/update_job/", + * operationId="updateTaskSchedulerJob", + * tags={"task_scheduler"}, + * summary="Update job for a task scheduler ", + * description="Update job for a task scheduler | if we are changing action for a job, we should send the request for a new job same as we are creating new scheduler", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\RequestBody( + * required=true, + * @OA\JsonContent(ref="#/components/schemas/UpdateJobForASchedulerSchema") + * ), + * @OA\Response( + * response=200, + * description="success", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + */ public function updateJob(Scheduler $scheduler, UpdateScheduledJobRequest $request) { return $scheduler->service()->updateJob($scheduler, $request); } - + /** + * @OA\DELETE( + * path="/api/v1/task_scheduler/{scheduler}", + * operationId="destroyTaskScheduler", + * tags={"task_scheduler"}, + * summary="Destroy Task Scheduler", + * description="Destroy task scheduler and its associated job", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Response( + * response=200, + * description="success", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + */ public function destroy(Scheduler $scheduler) { return $scheduler->service()->destroy($scheduler); diff --git a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php index 28e9cf47319a..a951ee72d9f9 100644 --- a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php +++ b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php @@ -35,7 +35,6 @@ class UpdateScheduleRequest extends Request 'archived' => 'sometimes|bool', 'repeat_every' => 'sometimes|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', 'start_from' => 'sometimes|string', - 'job' => 'sometimes|string', ]; } } From 3489e36a71b08fca365591243cb719fd04a5755b Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 02:31:37 +0200 Subject: [PATCH 16/43] INA-5 | fix params --- app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php b/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php index 59f6c999a8b4..cdb90e01cb1c 100644 --- a/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php +++ b/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php @@ -44,7 +44,7 @@ * @OA\Schema( * schema="UpdateJobForASchedulerSchema", * type="object", - * @OA\Property(property="action_name",type="string",example="create_client_report",description="Set action name, action names can be found in ScheduledJob Model"), + * @OA\Property(property="job",type="string",example="create_client_report",description="Set action name, action names can be found in ScheduledJob Model"), * * ) */ From 14a988f5fa426e12d42788e9c5439493356821dc Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 21:25:27 +0200 Subject: [PATCH 17/43] INA-5 | Fix license --- app/Http/Controllers/TaskSchedulerController.php | 10 +++++++++- .../2022_05_18_162152_create_scheduled_jobs_table.php | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index bc40c1a43fca..42675b765062 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -1,5 +1,13 @@ Date: Mon, 23 May 2022 21:25:46 +0200 Subject: [PATCH 18/43] INA-5 | Set explicit response code for better readability. --- app/Services/TaskScheduler/TaskSchedulerService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/TaskScheduler/TaskSchedulerService.php b/app/Services/TaskScheduler/TaskSchedulerService.php index c47f57a4e2c0..05fcf0336a5e 100644 --- a/app/Services/TaskScheduler/TaskSchedulerService.php +++ b/app/Services/TaskScheduler/TaskSchedulerService.php @@ -62,7 +62,7 @@ class TaskSchedulerService } $update = $this->scheduler->update($data); if ($update) { - return response(['successfully_updated_scheduler']); + return response(['successfully_updated_scheduler'],200); } return response(['failed_to_update_scheduler'], 400); } @@ -209,7 +209,7 @@ class TaskSchedulerService $job = $this->setJobParameters($job, $request); $job->save(); - return response('job_successfully_updated'); + return response(['job_successfully_updated'],200); } From 270166b050cd8a8e7cf6d0eb402342957fb9ce9b Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 21:32:43 +0200 Subject: [PATCH 19/43] INA-5 | Fix syntax --- app/Console/Kernel.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 675ac7781029..f77695f6b35b 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -76,7 +76,6 @@ class Kernel extends ConsoleKernel $schedule->job(new TaskScheduler())->daily()->withoutOverlapping(); - if (Ninja::isSelfHost()) { $schedule->job(new SystemMaintenance)->weekly()->withoutOverlapping(); if(Ninja::isSelfHost()) { From 05ff9531abb775b71babbd66af8bdd3a412e10b0 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 21:57:48 +0200 Subject: [PATCH 20/43] INA-5 | Add prepareForValidation |code cleanup --- .../TaskScheduler/UpdateScheduleRequest.php | 16 +++++++++++++++- app/Jobs/Ninja/TaskScheduler.php | 2 +- .../TaskScheduler/TaskSchedulerService.php | 11 +++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php index a951ee72d9f9..3989d131174f 100644 --- a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php +++ b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php @@ -14,6 +14,7 @@ namespace App\Http\Requests\TaskScheduler; use App\Http\Requests\Request; use App\Models\ScheduledJob; +use Carbon\Carbon; use Illuminate\Validation\Rule; class UpdateScheduleRequest extends Request @@ -34,7 +35,20 @@ class UpdateScheduleRequest extends Request 'paused' => 'sometimes|bool', 'archived' => 'sometimes|bool', 'repeat_every' => 'sometimes|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', - 'start_from' => 'sometimes|string', + 'start_from' => 'sometimes', + 'scheduled_run'=>'sometimes' ]; } + + public function prepareForValidation() + { + $request = $this->all(); + + if (isset($request['start_from'])) { + $request['scheduled_run'] = Carbon::parse((int)$request['start_from']); + $request['start_from'] = Carbon::parse((int)$request['start_from']); + } + + $this->replace($request); + } } diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index dabc0701d0db..6b09f057a0a0 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -143,7 +143,7 @@ class TaskScheduler implements ShouldQueue return Scheduler::where('paused', false) ->where('archived', false) ->whereDate('scheduled_run', '<=', Carbon::now()) - ->get(); + ->cursor(); } } diff --git a/app/Services/TaskScheduler/TaskSchedulerService.php b/app/Services/TaskScheduler/TaskSchedulerService.php index 05fcf0336a5e..01e62c950629 100644 --- a/app/Services/TaskScheduler/TaskSchedulerService.php +++ b/app/Services/TaskScheduler/TaskSchedulerService.php @@ -45,6 +45,7 @@ class TaskSchedulerService $scheduler->start_from = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now(); $scheduler->repeat_every = $request->get('repeat_every'); $scheduler->scheduled_run = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now();; + $scheduler->company_id = auth()->user()->company()->id; $scheduler->save(); if ($this->createJob($request, $scheduler)) { @@ -55,14 +56,12 @@ class TaskSchedulerService public function update(Scheduler $scheduler, UpdateScheduleRequest $request) { + $data = $request->validated(); - if ($request->has('start_from')) { - $data['start_from'] = Carbon::parse((int)$request->get('start_from')); - $data['scheduled_run'] = Carbon::parse((int)$request->get('start_from')); - } + $update = $this->scheduler->update($data); if ($update) { - return response(['successfully_updated_scheduler'],200); + return response(['successfully_updated_scheduler'], 200); } return response(['failed_to_update_scheduler'], 400); } @@ -209,7 +208,7 @@ class TaskSchedulerService $job = $this->setJobParameters($job, $request); $job->save(); - return response(['job_successfully_updated'],200); + return response(['job_successfully_updated'], 200); } From 99dc9ac65ae614e51dc7cc75c42ccc320fc63be8 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Mon, 23 May 2022 21:58:05 +0200 Subject: [PATCH 21/43] INA-5 | Add company_id to schedulers so we can fetch them via company_id --- app/Http/Controllers/TaskSchedulerController.php | 4 ++-- app/Models/Scheduler.php | 2 ++ .../migrations/2022_05_18_162443_create_schedulers_table.php | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index 42675b765062..f7732a3c5982 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -49,7 +49,7 @@ class TaskSchedulerController extends BaseController public function index() { - return Scheduler::all(); + return Scheduler::where('company_id', auth()->user()->company()->id)->cursor(); } /** @@ -157,6 +157,7 @@ class TaskSchedulerController extends BaseController { return $scheduler->service()->update($scheduler, $request); } + /** * @OA\PUT( * path="/api/v1/task_scheduler/{scheduler}/update_job/", @@ -192,7 +193,6 @@ class TaskSchedulerController extends BaseController public function updateJob(Scheduler $scheduler, UpdateScheduledJobRequest $request) { return $scheduler->service()->updateJob($scheduler, $request); - } /** diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index 6cc11588bf4d..ce1939b109ba 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -22,6 +22,7 @@ use Illuminate\Database\Eloquent\Model; * @property string repeat_every * @property \Carbon\Carbon|mixed scheduled_run * @property mixed job + * @property integer company_id */ class Scheduler extends Model { @@ -33,6 +34,7 @@ class Scheduler extends Model 'archived', 'repeat_every', 'scheduled_run', + 'company_id' ]; protected $appends = ['linked_job']; diff --git a/database/migrations/2022_05_18_162443_create_schedulers_table.php b/database/migrations/2022_05_18_162443_create_schedulers_table.php index fbe3ebe6bba1..33a5c6ca5722 100644 --- a/database/migrations/2022_05_18_162443_create_schedulers_table.php +++ b/database/migrations/2022_05_18_162443_create_schedulers_table.php @@ -29,6 +29,7 @@ class CreateSchedulersTable extends Migration $table->string('repeat_every'); $table->timestamp('start_from'); $table->timestamp('scheduled_run'); + $table->foreignIdFor(\App\Models\Company::class); $table->timestamps(); }); } From fbb64effeca2f42ce2e04b1119ad758eb65c10c4 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Tue, 24 May 2022 01:42:43 +0200 Subject: [PATCH 22/43] INA-5 | New way of setting next scheduled run date --- app/Jobs/Ninja/TaskScheduler.php | 23 +---------------------- app/Models/Scheduler.php | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 6b09f057a0a0..106281c58d70 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -111,31 +111,10 @@ class TaskScheduler implements ShouldQueue } - $amount_of_days_until_next_run = $this->getAmountOfDays($scheduler->repeat_every); - $scheduler->scheduled_run = Carbon::now()->addDays($amount_of_days_until_next_run); + $scheduler->scheduled_run = $scheduler->nextScheduledDate(); $scheduler->save(); } - private function getAmountOfDays(string $repeat_every): int - { - switch ($repeat_every) { - case Scheduler::DAILY: - return 1; - break; - case Scheduler::MONTHLY: - return 30; - break; - case Scheduler::WEEKLY: - return 7; - break; - case Scheduler::QUARTERLY: - return 90; - break; - case Scheduler::ANNUALLY: - return 365; - break; - } - } private function fetchJobs() { diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index ce1939b109ba..7014f37ad41d 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -14,6 +14,7 @@ namespace App\Models; use App\Services\TaskScheduler\TaskSchedulerService; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Carbon; /** * @property boolean paused @@ -61,4 +62,29 @@ class Scheduler extends Model { return $this->hasOne(ScheduledJob::class, 'scheduler_id', 'id'); } + + public function nextScheduledDate() :?Carbon + { + + /* + As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need + to add ON a day - a day = 86400 seconds + */ + $offset = 86400; + + switch ($this->repeat_every) { + case self::DAILY: + return Carbon::parse($this->scheduled_run)->startOfDay()->addDay()->addSeconds($offset); + case self::WEEKLY: + return Carbon::parse($this->scheduled_run)->startOfDay()->addWeek()->addSeconds($offset); + case self::MONTHLY: + return Carbon::parse($this->scheduled_run)->startOfDay()->addMonth()->addSeconds($offset); + case self::QUARTERLY: + return Carbon::parse($this->scheduled_run)->startOfDay()->addMonths(3)->addSeconds($offset); + case self::ANNUALLY: + return Carbon::parse($this->scheduled_run)->startOfDay()->addYear()->addSeconds($offset); + default: + return null; + } + } } From 5d744ed8e43e6be2e2a58f30d4500190d09bad0c Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Tue, 24 May 2022 02:35:10 +0200 Subject: [PATCH 23/43] INA-5 | Feature Tests for Scheduler --- tests/Feature/Scheduler/SchedulerTest.php | 163 ++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 tests/Feature/Scheduler/SchedulerTest.php diff --git a/tests/Feature/Scheduler/SchedulerTest.php b/tests/Feature/Scheduler/SchedulerTest.php new file mode 100644 index 000000000000..863b76c2f790 --- /dev/null +++ b/tests/Feature/Scheduler/SchedulerTest.php @@ -0,0 +1,163 @@ +faker = \Faker\Factory::create(); + + Model::reguard(); + + $this->makeTestData(); + + + $this->withoutMiddleware( + ThrottleRequests::class + ); + } + + public function testSchedulerCantBeCreatedWithWrongData() + { + $data = [ + 'repeat_every' => Scheduler::DAILY, + 'job' => ScheduledJob::CREATE_CLIENT_REPORT, + 'date_key' => '123', + 'report_keys' => ['test'], + // 'date_range' => 'all', + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/task_scheduler/', $data); + + $response->assertSessionHasErrors(); + + } + + public function testSchedulerCanBeUpdated() + { + $this->createScheduler(); + + + $scheduler = Scheduler::first(); + $updateData = [ + 'start_from' => 1655934741 + ]; + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/task_scheduler/' . $scheduler->id, $updateData); + + $responseData = $response->json(); + $this->assertEquals(['successfully_updated_scheduler'], $responseData); + } + + public function testSchedulerCanBeSeen() + { + $this->createScheduler(); + + + $scheduler = Scheduler::first(); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/task_scheduler/' . $scheduler->id); + + $arr = $response->json(); + + $this->assertEquals('create_client_report', $arr['linked_job']['action_name']); + + + } + + public function testSchedulerCanBeDeleted() + { + $this->createScheduler(); + + $scheduler = Scheduler::first(); + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->delete('/api/v1/task_scheduler/' . $scheduler->id); + + $this->assertEquals(0, Scheduler::count()); + + } + + public function testSchedulerJobCanBeUpdated() + { + $this->createScheduler(); + + $scheduler = Scheduler::first(); + $this->assertSame('create_client_report', $scheduler->job->action_name); + + $updateData = [ + 'job' => ScheduledJob::CREATE_CREDIT_REPORT, + 'date_range' => 'all', + 'report_keys' => ['test1'] + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/task_scheduler/' . $scheduler->id . '/update_job', $updateData); + + $updatedSchedulerJob = Scheduler::first()->job->action_name; + $this->assertSame('create_credit_report', $updatedSchedulerJob); + } + + public function testSchedulerCanBeCreated() + { + $response = $this->createScheduler(); + + $all_schedulers = Scheduler::count(); + + $this->assertSame(1, $all_schedulers); + + $response->assertStatus(200); + + } + + public function createScheduler() + { + $data = [ + 'repeat_every' => Scheduler::DAILY, + 'job' => ScheduledJob::CREATE_CLIENT_REPORT, + 'date_key' => '123', + 'report_keys' => ['test'], + 'date_range' => 'all', + ]; + + return $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/task_scheduler/', $data); + } +} From de4adadfae3e24465c1404877404adaca27c8ab0 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 00:06:42 +0200 Subject: [PATCH 24/43] INA-5 | Use fractal --- .../Controllers/TaskSchedulerController.php | 16 ++++++-- app/Jobs/Ninja/TaskScheduler.php | 10 +++-- app/Transformers/ScheduledJobTransformer.php | 22 +++++++++++ app/Transformers/TaskSchedulerTransformer.php | 39 +++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 app/Transformers/ScheduledJobTransformer.php create mode 100644 app/Transformers/TaskSchedulerTransformer.php diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index f7732a3c5982..2d35bb34def4 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -14,15 +14,20 @@ namespace App\Http\Controllers; use App\Http\Requests\TaskScheduler\CreateScheduledTaskRequest; use App\Http\Requests\TaskScheduler\UpdateScheduledJobRequest; use App\Http\Requests\TaskScheduler\UpdateScheduleRequest; +use App\Jobs\Ninja\TaskScheduler; use App\Jobs\Report\ProfitAndLoss; use App\Models\ScheduledJob; use App\Models\Scheduler; +use App\Transformers\TaskSchedulerTransformer; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpFoundation\Request; class TaskSchedulerController extends BaseController { + protected $entity_type = TaskScheduler::class; + protected $entity_transformer = TaskSchedulerTransformer::class; + /** * @OA\GET( * path="/api/v1/task_scheduler/", @@ -49,7 +54,12 @@ class TaskSchedulerController extends BaseController public function index() { - return Scheduler::where('company_id', auth()->user()->company()->id)->cursor(); + set_time_limit(45); + + $schedulers = Scheduler::where('company_id', auth()->user()->company()->id); + + return $this->listResponse($schedulers); + } /** @@ -116,9 +126,9 @@ class TaskSchedulerController extends BaseController * ) */ - public function show(Scheduler $scheduler): Scheduler + public function show(Scheduler $scheduler) { - return $scheduler; + return $this->itemResponse($scheduler); } /** diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 106281c58d70..0adc6f7d22d6 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -46,10 +46,13 @@ class TaskScheduler implements ShouldQueue */ public function handle() { + foreach (MultiDB::$dbs as $db) { - $pending_schedulers = $this->fetchJobs(); - foreach ($pending_schedulers as $scheduler) { - $this->doJob($scheduler); + MultiDB::setDB($db); + $pending_schedulers = $this->fetchJobs(); + foreach ($pending_schedulers as $scheduler) { + $this->doJob($scheduler); + } } } @@ -61,7 +64,6 @@ class TaskScheduler implements ShouldQueue if (!$job || !$company) { return; } - MultiDB::setDb($company->db); $parameters = $job->parameters; diff --git a/app/Transformers/ScheduledJobTransformer.php b/app/Transformers/ScheduledJobTransformer.php new file mode 100644 index 000000000000..2baae9e8cba5 --- /dev/null +++ b/app/Transformers/ScheduledJobTransformer.php @@ -0,0 +1,22 @@ + $this->encodePrimaryKey($job->id), + 'action_name' => $job->action_name, + 'parameters' => $job->parameters + ]; + } +} diff --git a/app/Transformers/TaskSchedulerTransformer.php b/app/Transformers/TaskSchedulerTransformer.php new file mode 100644 index 000000000000..b11b51be5ec5 --- /dev/null +++ b/app/Transformers/TaskSchedulerTransformer.php @@ -0,0 +1,39 @@ +serializer); + + return $this->item($scheduler->job, $transformer, ScheduledJob::class); + } + + public function transform(Scheduler $scheduler) + { + return [ + 'id' => $this->encodePrimaryKey($scheduler->id), + 'company_id' => $this->encodePrimaryKey($scheduler->user_id), + 'paused' => $scheduler->paused, + 'archived' => $scheduler->archived, + 'repeat_every' => $scheduler->repeat_every, + 'start_from' => $scheduler->start_from, + 'scheduled_run' => $scheduler->scheduled_run, + ]; + } + +} From 05a1d0c0e6297d027a1d68fb800fba4eff557d3e Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 00:11:36 +0200 Subject: [PATCH 25/43] INA-5 | update tests --- tests/Feature/Scheduler/SchedulerTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Feature/Scheduler/SchedulerTest.php b/tests/Feature/Scheduler/SchedulerTest.php index 863b76c2f790..c00302d482bf 100644 --- a/tests/Feature/Scheduler/SchedulerTest.php +++ b/tests/Feature/Scheduler/SchedulerTest.php @@ -91,8 +91,7 @@ class SchedulerTest extends TestCase ])->get('/api/v1/task_scheduler/' . $scheduler->id); $arr = $response->json(); - - $this->assertEquals('create_client_report', $arr['linked_job']['action_name']); + $this->assertEquals('create_client_report', $arr['data']['job']['action_name']); } From 5b744a91230c2e958d238235bd09a58df2c9c215 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:17:23 +0200 Subject: [PATCH 26/43] INA-5 | use soft deletes --- .../migrations/2022_05_18_162152_create_scheduled_jobs_table.php | 1 + .../migrations/2022_05_18_162443_create_schedulers_table.php | 1 + 2 files changed, 2 insertions(+) diff --git a/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php b/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php index 28c9362a3a59..80fcf410f499 100644 --- a/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php +++ b/database/migrations/2022_05_18_162152_create_scheduled_jobs_table.php @@ -30,6 +30,7 @@ class CreateScheduledJobsTable extends Migration $table->foreignIdFor(\App\Models\Company::class); $table->foreignIdFor(\App\Models\Scheduler::class); $table->timestamps(); + $table->softDeletes(); }); } diff --git a/database/migrations/2022_05_18_162443_create_schedulers_table.php b/database/migrations/2022_05_18_162443_create_schedulers_table.php index 33a5c6ca5722..5a06f4677b77 100644 --- a/database/migrations/2022_05_18_162443_create_schedulers_table.php +++ b/database/migrations/2022_05_18_162443_create_schedulers_table.php @@ -31,6 +31,7 @@ class CreateSchedulersTable extends Migration $table->timestamp('scheduled_run'); $table->foreignIdFor(\App\Models\Company::class); $table->timestamps(); + $table->softDeletes(); }); } From 72f7b1c76cfd75edf059b0b0b5eb94c174d63885 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:24:13 +0200 Subject: [PATCH 27/43] INA-5 | ScheduledJob [Soft deletes, BaseModel] --- app/Models/ScheduledJob.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Models/ScheduledJob.php b/app/Models/ScheduledJob.php index 7b8e0a002439..b2960af0c7c7 100644 --- a/app/Models/ScheduledJob.php +++ b/app/Models/ScheduledJob.php @@ -13,6 +13,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; /** * @property mixed|string action_class @@ -21,9 +22,9 @@ use Illuminate\Database\Eloquent\Model; * @property integer scheduler_id * @property integer company_id */ -class ScheduledJob extends Model +class ScheduledJob extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; const CREATE_CLIENT_REPORT = 'create_client_report'; const CREATE_CLIENT_CONTACT_REPORT = 'create_client_contact_report'; @@ -43,7 +44,6 @@ class ScheduledJob extends Model protected $fillable = ['action_class', 'action_name', 'parameters', 'scheduler_id', 'company_id']; protected $casts = [ - 'scheduled_run' => 'date', 'parameters' => 'array' ]; } From 384038d910eda1b3c73b4158c3f998c68c5783ee Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:24:35 +0200 Subject: [PATCH 28/43] INA-5 | ScheduledJobTransformer [Licence, type hints] --- app/Transformers/ScheduledJobTransformer.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Transformers/ScheduledJobTransformer.php b/app/Transformers/ScheduledJobTransformer.php index 2baae9e8cba5..2028fd2e0dcd 100644 --- a/app/Transformers/ScheduledJobTransformer.php +++ b/app/Transformers/ScheduledJobTransformer.php @@ -1,5 +1,13 @@ $this->encodePrimaryKey($job->id), - 'action_name' => $job->action_name, - 'parameters' => $job->parameters + 'action_name' => (string)$job->action_name, + 'parameters' => (array)$job->parameters ]; } } From 25b7d8962b1d319f0451b61f5808d4c86595f3b4 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:25:54 +0200 Subject: [PATCH 29/43] INA-5 | Scheduler [Soft Deletes, Timezone calculations ] --- app/Models/Scheduler.php | 41 +++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index 7014f37ad41d..b53d853a2091 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -13,7 +13,7 @@ namespace App\Models; use App\Services\TaskScheduler\TaskSchedulerService; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; /** @@ -24,10 +24,13 @@ use Illuminate\Support\Carbon; * @property \Carbon\Carbon|mixed scheduled_run * @property mixed job * @property integer company_id + * @property integer updated_at + * @property integer created_at + * @property integer deleted_at */ -class Scheduler extends Model +class Scheduler extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $fillable = [ 'start_from', @@ -37,6 +40,15 @@ class Scheduler extends Model 'scheduled_run', 'company_id' ]; + protected $casts = [ + 'start_from' => 'timestamp', + 'scheduled_run' => 'timestamp', + 'created_at'=> 'timestamp', + 'updated_at'=> 'timestamp', + 'deleted_at'=> 'timestamp', + 'paused' => 'boolean', + 'archived' => 'boolean', + ]; protected $appends = ['linked_job']; const DAILY = 'DAY'; @@ -63,14 +75,33 @@ class Scheduler extends Model return $this->hasOne(ScheduledJob::class, 'scheduler_id', 'id'); } - public function nextScheduledDate() :?Carbon + public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo { + return $this->belongsTo(Company::class); + } + + + public function nextScheduledDate(): ?Carbon + { + + $offset = 0; + + $entity_send_time = $this->company->settings->entity_send_time; + + if ($entity_send_time != 0) { + $timezone = $this->company->timezone(); + + $offset -= $timezone->utc_offset; + $offset += ($entity_send_time * 3600); + } /* As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need to add ON a day - a day = 86400 seconds */ - $offset = 86400; + + if ($offset < 0) + $offset += 86400; switch ($this->repeat_every) { case self::DAILY: From dfd33773d2d3ed343cff43771019a22fa5d7f065 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:26:12 +0200 Subject: [PATCH 30/43] INA-5 | SchedulerTest [Update because of transformers and encoded id] --- tests/Feature/Scheduler/SchedulerTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Feature/Scheduler/SchedulerTest.php b/tests/Feature/Scheduler/SchedulerTest.php index c00302d482bf..1786e001d15f 100644 --- a/tests/Feature/Scheduler/SchedulerTest.php +++ b/tests/Feature/Scheduler/SchedulerTest.php @@ -72,7 +72,7 @@ class SchedulerTest extends TestCase $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, - ])->put('/api/v1/task_scheduler/' . $scheduler->id, $updateData); + ])->put('/api/v1/task_scheduler/' . $this->encodePrimaryKey($scheduler->id), $updateData); $responseData = $response->json(); $this->assertEquals(['successfully_updated_scheduler'], $responseData); @@ -88,7 +88,7 @@ class SchedulerTest extends TestCase $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, - ])->get('/api/v1/task_scheduler/' . $scheduler->id); + ])->get('/api/v1/task_scheduler/' . $this->encodePrimaryKey($scheduler->id)); $arr = $response->json(); $this->assertEquals('create_client_report', $arr['data']['job']['action_name']); @@ -104,7 +104,7 @@ class SchedulerTest extends TestCase $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, - ])->delete('/api/v1/task_scheduler/' . $scheduler->id); + ])->delete('/api/v1/task_scheduler/' . $this->encodePrimaryKey($scheduler->id)); $this->assertEquals(0, Scheduler::count()); @@ -126,7 +126,7 @@ class SchedulerTest extends TestCase $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, - ])->put('/api/v1/task_scheduler/' . $scheduler->id . '/update_job', $updateData); + ])->put('/api/v1/task_scheduler/' . $this->encodePrimaryKey($scheduler->id) . '/update_job', $updateData); $updatedSchedulerJob = Scheduler::first()->job->action_name; $this->assertSame('create_credit_report', $updatedSchedulerJob); From b1795df3ec536bd0aeaeb58f82a7440fbc255c51 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:26:38 +0200 Subject: [PATCH 31/43] INA-5 | TaskSchedulerTransformer [License, typehints] --- app/Transformers/TaskSchedulerTransformer.php | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/Transformers/TaskSchedulerTransformer.php b/app/Transformers/TaskSchedulerTransformer.php index b11b51be5ec5..82e98e7b751d 100644 --- a/app/Transformers/TaskSchedulerTransformer.php +++ b/app/Transformers/TaskSchedulerTransformer.php @@ -1,5 +1,13 @@ $this->encodePrimaryKey($scheduler->id), - 'company_id' => $this->encodePrimaryKey($scheduler->user_id), - 'paused' => $scheduler->paused, - 'archived' => $scheduler->archived, - 'repeat_every' => $scheduler->repeat_every, - 'start_from' => $scheduler->start_from, - 'scheduled_run' => $scheduler->scheduled_run, + 'paused' => (bool)$scheduler->paused, + 'archived' => (bool)$scheduler->archived, + 'repeat_every' => (string)$scheduler->repeat_every, + 'start_from' => (int)$scheduler->start_from, + 'scheduled_run' => (int)$scheduler->scheduled_run, + 'updated_at' => (int)$scheduler->updated_at, + 'created_at' => (int)$scheduler->created_at, ]; } From 5538c2ee592ec43f8beb1974106a0a1ae8f08f6a Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Wed, 25 May 2022 23:27:07 +0200 Subject: [PATCH 32/43] INA-5 | TaskSchedulerTransformer [License, typehints] --- app/Jobs/Ninja/TaskScheduler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 0adc6f7d22d6..135f30cf1e93 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -120,7 +120,6 @@ class TaskScheduler implements ShouldQueue private function fetchJobs() { - return Scheduler::where('paused', false) ->where('archived', false) ->whereDate('scheduled_run', '<=', Carbon::now()) From 1d09cda4a4438fcc1153117abda58099b9315d25 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:16:19 +0200 Subject: [PATCH 33/43] INA-5 | Scheduler [License fix, is_deleted introduced, archived property removed] --- app/Models/ScheduledJob.php | 4 ++-- app/Models/Scheduler.php | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/Models/ScheduledJob.php b/app/Models/ScheduledJob.php index b2960af0c7c7..a2baedebb226 100644 --- a/app/Models/ScheduledJob.php +++ b/app/Models/ScheduledJob.php @@ -4,9 +4,9 @@ * * @link https://github.com/invoiceninja/invoiceninja source repository * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://opensource.org/licenses/AAL + * @license https://www.elastic.co/licensing/elastic-license */ namespace App\Models; diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index b53d853a2091..4f78ac36b593 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -4,9 +4,9 @@ * * @link https://github.com/invoiceninja/invoiceninja source repository * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://opensource.org/licenses/AAL + * @license https://www.elastic.co/licensing/elastic-license */ namespace App\Models; @@ -18,7 +18,7 @@ use Illuminate\Support\Carbon; /** * @property boolean paused - * @property boolean archived + * @property boolean is_deleted * @property \Carbon\Carbon|mixed start_from * @property string repeat_every * @property \Carbon\Carbon|mixed scheduled_run @@ -35,7 +35,6 @@ class Scheduler extends BaseModel protected $fillable = [ 'start_from', 'paused', - 'archived', 'repeat_every', 'scheduled_run', 'company_id' @@ -47,7 +46,7 @@ class Scheduler extends BaseModel 'updated_at'=> 'timestamp', 'deleted_at'=> 'timestamp', 'paused' => 'boolean', - 'archived' => 'boolean', + 'is_deleted' => 'boolean', ]; protected $appends = ['linked_job']; From 3b26246e1bb76c37079e52711ef88f0899c29566 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:16:47 +0200 Subject: [PATCH 34/43] INA-5 | Requests [Removed unused property] --- app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php | 1 - app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php b/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php index 1e86810eb5eb..bde04e199ced 100644 --- a/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php +++ b/app/Http/Requests/TaskScheduler/CreateScheduledTaskRequest.php @@ -22,7 +22,6 @@ class CreateScheduledTaskRequest extends Request { return [ 'paused' => 'sometimes|bool', - 'archived' => 'sometimes|bool', 'repeat_every' => 'required|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', 'start_from' => 'sometimes|string', 'job' => 'required', diff --git a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php index 3989d131174f..51eb97001bf3 100644 --- a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php +++ b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php @@ -33,7 +33,6 @@ class UpdateScheduleRequest extends Request { return [ 'paused' => 'sometimes|bool', - 'archived' => 'sometimes|bool', 'repeat_every' => 'sometimes|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', 'start_from' => 'sometimes', 'scheduled_run'=>'sometimes' From fabc009f5d5cdd58ac73b0e287bdfdeee600f966 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:17:09 +0200 Subject: [PATCH 35/43] INA-5 | Updated migration (is_deleted added, archived removed) --- .../migrations/2022_05_18_162443_create_schedulers_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2022_05_18_162443_create_schedulers_table.php b/database/migrations/2022_05_18_162443_create_schedulers_table.php index 5a06f4677b77..72e3fe743a0c 100644 --- a/database/migrations/2022_05_18_162443_create_schedulers_table.php +++ b/database/migrations/2022_05_18_162443_create_schedulers_table.php @@ -25,7 +25,7 @@ class CreateSchedulersTable extends Migration Schema::create('schedulers', function (Blueprint $table) { $table->id(); $table->boolean('paused')->default(false); - $table->boolean('archived')->default(false); + $table->boolean('is_deleted')->default(false); $table->string('repeat_every'); $table->timestamp('start_from'); $table->timestamp('scheduled_run'); From 798258ab16ea7870907d68068555797015305fcf Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:18:10 +0200 Subject: [PATCH 36/43] INA-5 | TaskSchedulerService (Fixed license,refactored methods for update|destroy|store) --- .../TaskScheduler/TaskSchedulerService.php | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/app/Services/TaskScheduler/TaskSchedulerService.php b/app/Services/TaskScheduler/TaskSchedulerService.php index 01e62c950629..4bce031574c6 100644 --- a/app/Services/TaskScheduler/TaskSchedulerService.php +++ b/app/Services/TaskScheduler/TaskSchedulerService.php @@ -1,5 +1,13 @@ paused = $request->get('paused', false); - $scheduler->archived = (bool)$request->get('archived', false); $scheduler->start_from = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now(); $scheduler->repeat_every = $request->get('repeat_every'); $scheduler->scheduled_run = $request->get('start_from') ? Carbon::parse((int)$request->get('start_from')) : Carbon::now();; $scheduler->company_id = auth()->user()->company()->id; $scheduler->save(); + $this->createJob($request, $scheduler); - if ($this->createJob($request, $scheduler)) { - return response(['job_has_been_created'], 200); - } - return response(['failed_to_create_job'], 400); } public function update(Scheduler $scheduler, UpdateScheduleRequest $request) @@ -189,15 +195,6 @@ class TaskSchedulerService return $class = is_object($class) ? get_class($class) : $class; } - public function destroy(Scheduler $scheduler) - { - $job = $scheduler->job; - if ($job) { - $job->delete(); - } - $scheduler->delete(); - return response(['successfully_deleted_scheduler_and_job_associated_to_him']); - } public function updateJob(Scheduler $scheduler, UpdateScheduledJobRequest $request) { @@ -208,8 +205,5 @@ class TaskSchedulerService $job = $this->setJobParameters($job, $request); $job->save(); - return response(['job_successfully_updated'], 200); - - } } From c4809b1e18e0e2c5df8c8bc1297a2ac084e0ceea Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:18:53 +0200 Subject: [PATCH 37/43] INA-5 | TaskSchedulerController (fixes for fractal) --- .../Controllers/TaskSchedulerController.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index 2d35bb34def4..249ce9c6db21 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -18,6 +18,7 @@ use App\Jobs\Ninja\TaskScheduler; use App\Jobs\Report\ProfitAndLoss; use App\Models\ScheduledJob; use App\Models\Scheduler; +use App\Repositories\SchedulerRepository; use App\Transformers\TaskSchedulerTransformer; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; @@ -27,6 +28,14 @@ class TaskSchedulerController extends BaseController { protected $entity_type = TaskScheduler::class; protected $entity_transformer = TaskSchedulerTransformer::class; + protected SchedulerRepository $scheduler_repository; + + public function __construct(SchedulerRepository $scheduler_repository) + { + parent::__construct(); + + $this->scheduler_repository = $scheduler_repository; + } /** * @OA\GET( @@ -99,7 +108,8 @@ class TaskSchedulerController extends BaseController public function store(CreateScheduledTaskRequest $request) { $scheduler = new Scheduler(); - return $scheduler->service()->store($scheduler, $request); + $scheduler->service()->store($scheduler, $request); + return $this->itemResponse($scheduler); } /** @@ -165,7 +175,8 @@ class TaskSchedulerController extends BaseController */ public function update(Scheduler $scheduler, UpdateScheduleRequest $request) { - return $scheduler->service()->update($scheduler, $request); + $scheduler->service()->update($scheduler, $request); + return $this->itemResponse($scheduler); } /** @@ -202,7 +213,8 @@ class TaskSchedulerController extends BaseController */ public function updateJob(Scheduler $scheduler, UpdateScheduledJobRequest $request) { - return $scheduler->service()->updateJob($scheduler, $request); + $scheduler->service()->updateJob($scheduler, $request); + return $this->itemResponse($scheduler); } /** @@ -230,7 +242,8 @@ class TaskSchedulerController extends BaseController */ public function destroy(Scheduler $scheduler) { - return $scheduler->service()->destroy($scheduler); + $this->scheduler_repository->delete($scheduler); + return $this->itemResponse($scheduler->fresh()); } From b3e58d93dc28d41adbcd061ebeb5f63c63549433 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:29:57 +0200 Subject: [PATCH 38/43] INA-5 | TaskSchedulerTransformer updated to use archived_at --- app/Transformers/TaskSchedulerTransformer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Transformers/TaskSchedulerTransformer.php b/app/Transformers/TaskSchedulerTransformer.php index 82e98e7b751d..bc36393e2db0 100644 --- a/app/Transformers/TaskSchedulerTransformer.php +++ b/app/Transformers/TaskSchedulerTransformer.php @@ -36,12 +36,12 @@ class TaskSchedulerTransformer extends EntityTransformer return [ 'id' => $this->encodePrimaryKey($scheduler->id), 'paused' => (bool)$scheduler->paused, - 'archived' => (bool)$scheduler->archived, 'repeat_every' => (string)$scheduler->repeat_every, 'start_from' => (int)$scheduler->start_from, 'scheduled_run' => (int)$scheduler->scheduled_run, 'updated_at' => (int)$scheduler->updated_at, 'created_at' => (int)$scheduler->created_at, + 'archived_at' => (int) $scheduler->deleted_at, ]; } From e137b1ac0b53b44c316a3d4d5846fd639992116e Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:30:07 +0200 Subject: [PATCH 39/43] INA-5 | Update Tests --- tests/Feature/Scheduler/SchedulerTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Feature/Scheduler/SchedulerTest.php b/tests/Feature/Scheduler/SchedulerTest.php index 1786e001d15f..d9a616e564e5 100644 --- a/tests/Feature/Scheduler/SchedulerTest.php +++ b/tests/Feature/Scheduler/SchedulerTest.php @@ -75,7 +75,7 @@ class SchedulerTest extends TestCase ])->put('/api/v1/task_scheduler/' . $this->encodePrimaryKey($scheduler->id), $updateData); $responseData = $response->json(); - $this->assertEquals(['successfully_updated_scheduler'], $responseData); + $this->assertEquals($updateData['start_from'], $responseData['data']['start_from']); } public function testSchedulerCanBeSeen() @@ -129,7 +129,8 @@ class SchedulerTest extends TestCase ])->put('/api/v1/task_scheduler/' . $this->encodePrimaryKey($scheduler->id) . '/update_job', $updateData); $updatedSchedulerJob = Scheduler::first()->job->action_name; - $this->assertSame('create_credit_report', $updatedSchedulerJob); + $arr = $response->json(); + $this->assertSame('create_credit_report', $arr['data']['job']['action_name']); } public function testSchedulerCanBeCreated() From 63061b6cf4332bf3b6b2ebe6fad0fb11c5f3cd83 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Thu, 26 May 2022 04:32:37 +0200 Subject: [PATCH 40/43] INA-5 | Renamed repository and added license --- .../Controllers/TaskSchedulerController.php | 6 +++--- app/Repositories/TaskSchedulerRepository.php | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 app/Repositories/TaskSchedulerRepository.php diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index 249ce9c6db21..7867137df920 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -18,7 +18,7 @@ use App\Jobs\Ninja\TaskScheduler; use App\Jobs\Report\ProfitAndLoss; use App\Models\ScheduledJob; use App\Models\Scheduler; -use App\Repositories\SchedulerRepository; +use App\Repositories\TaskSchedulerRepository; use App\Transformers\TaskSchedulerTransformer; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; @@ -28,9 +28,9 @@ class TaskSchedulerController extends BaseController { protected $entity_type = TaskScheduler::class; protected $entity_transformer = TaskSchedulerTransformer::class; - protected SchedulerRepository $scheduler_repository; + protected TaskSchedulerRepository $scheduler_repository; - public function __construct(SchedulerRepository $scheduler_repository) + public function __construct(TaskSchedulerRepository $scheduler_repository) { parent::__construct(); diff --git a/app/Repositories/TaskSchedulerRepository.php b/app/Repositories/TaskSchedulerRepository.php new file mode 100644 index 000000000000..5c5b3145f9cf --- /dev/null +++ b/app/Repositories/TaskSchedulerRepository.php @@ -0,0 +1,18 @@ + Date: Fri, 27 May 2022 02:50:03 +0200 Subject: [PATCH 41/43] INA-5 | Add biweekly option --- .../TaskScheduler/UpdateScheduleRequest.php | 2 +- app/Models/Scheduler.php | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php index 51eb97001bf3..643edbadee32 100644 --- a/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php +++ b/app/Http/Requests/TaskScheduler/UpdateScheduleRequest.php @@ -33,7 +33,7 @@ class UpdateScheduleRequest extends Request { return [ 'paused' => 'sometimes|bool', - 'repeat_every' => 'sometimes|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', + 'repeat_every' => 'sometimes|string|in:DAY,WEEK,BIWEEKLY,MONTH,3MONTHS,YEAR', 'start_from' => 'sometimes', 'scheduled_run'=>'sometimes' ]; diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index 4f78ac36b593..38943cb709b3 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -42,9 +42,9 @@ class Scheduler extends BaseModel protected $casts = [ 'start_from' => 'timestamp', 'scheduled_run' => 'timestamp', - 'created_at'=> 'timestamp', - 'updated_at'=> 'timestamp', - 'deleted_at'=> 'timestamp', + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp', + 'deleted_at' => 'timestamp', 'paused' => 'boolean', 'is_deleted' => 'boolean', ]; @@ -52,6 +52,7 @@ class Scheduler extends BaseModel const DAILY = 'DAY'; const WEEKLY = 'WEEK'; + const BIWEEKLY = 'BIWEEKLY'; const MONTHLY = 'MONTH'; const QUARTERLY = '3MONTHS'; const ANNUALLY = 'YEAR'; @@ -107,12 +108,14 @@ class Scheduler extends BaseModel return Carbon::parse($this->scheduled_run)->startOfDay()->addDay()->addSeconds($offset); case self::WEEKLY: return Carbon::parse($this->scheduled_run)->startOfDay()->addWeek()->addSeconds($offset); + case self::BIWEEKLY: + return Carbon::parse($this->scheduled_run)->startOfDay()->addWeeks(2)->addSeconds($offset); case self::MONTHLY: - return Carbon::parse($this->scheduled_run)->startOfDay()->addMonth()->addSeconds($offset); + return Carbon::parse($this->scheduled_run)->startOfDay()->addMonthNoOverflow()->addSeconds($offset); case self::QUARTERLY: - return Carbon::parse($this->scheduled_run)->startOfDay()->addMonths(3)->addSeconds($offset); + return Carbon::parse($this->scheduled_run)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset); case self::ANNUALLY: - return Carbon::parse($this->scheduled_run)->startOfDay()->addYear()->addSeconds($offset); + return Carbon::parse($this->scheduled_run)->startOfDay()->addYearNoOverflow()->addSeconds($offset); default: return null; } From 9bd22792905314322b91edb562db5da426edda67 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Fri, 27 May 2022 02:50:12 +0200 Subject: [PATCH 42/43] INA-5 | Update OpenAPI schema --- app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php b/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php index cdb90e01cb1c..7976fd475544 100644 --- a/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php +++ b/app/Http/Controllers/OpenAPI/TaskSchedulerSchema.php @@ -6,13 +6,14 @@ * schema="TaskSchedulerSchema", * type="object", * - * * @OA\Property(property="paused",type="bool",example="false",description="The scheduler paused state"), + * + * @OA\Property(property="paused",type="bool",example="false",description="The scheduler paused state"), * @OA\Property(property="repeat_every",type="string",example="DAY",description="Accepted values (DAY,WEEK,MONTH,3MONTHS,YEAR)"), * @OA\Property(property="start_from",type="integer",example="1652898504",description="Timestamp when we should start the scheduler, default is today"), * @OA\Property(property="date_range", type="string", example="last7", description="The string representation of the date range of data to be returned"), * @OA\Property(property="date_key", type="string", example="created_at", description="The date column to search between."), - * @OA\Property(property="start_date", type="string", example="2000-10-31", description="The start date to search between"), - * @OA\Property(property="end_date", type="string", example="2", description="The end date to search between"), + * @OA\Property(property="start_date", type="string", example="2022-10-31", description="The start date to search between"), + * @OA\Property(property="end_date", type="string", example="2022-10-31", description="The end date to search between"), * @OA\Property( * property="report_keys", * type="array", @@ -33,7 +34,6 @@ * type="object", * * @OA\Property(property="paused",type="bool",example="false",description="The scheduler paused state"), - * @OA\Property(property="archived",type="bool",example="false",description="The scheduler archived state"), * * @OA\Property(property="repeat_every",type="string",example="DAY",description="Accepted values (DAY,WEEK,MONTH,3MONTHS,YEAR)"), * @OA\Property(property="start_from",type="integer",example="1652898504",description="Timestamp when we should start the scheduler, default is today"), * From 3d67c9956797347cdf71bbddff399464a8453733 Mon Sep 17 00:00:00 2001 From: Nikola Cirkovic Date: Fri, 27 May 2022 02:50:50 +0200 Subject: [PATCH 43/43] INA-5 | replace archived with is_deleted property --- app/Jobs/Ninja/TaskScheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 135f30cf1e93..af7f74efaf17 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -121,7 +121,7 @@ class TaskScheduler implements ShouldQueue private function fetchJobs() { return Scheduler::where('paused', false) - ->where('archived', false) + ->where('is_deleted', false) ->whereDate('scheduled_run', '<=', Carbon::now()) ->cursor(); }