diff --git a/ProcessMaker/Contracts/CaseRepositoryInterface.php b/ProcessMaker/Contracts/CaseRepositoryInterface.php new file mode 100644 index 0000000000..9ad880cb1b --- /dev/null +++ b/ProcessMaker/Contracts/CaseRepositoryInterface.php @@ -0,0 +1,32 @@ +input('user_id'); $task->reassign($userToAssign, $request->user()); - return new Resource($task->refresh()); + $taskRefreshed = $task->refresh(); + + CaseUpdate::dispatch($task->processRequest, $taskRefreshed); + + return new Resource($taskRefreshed); } else { return abort(422); } diff --git a/ProcessMaker/Jobs/CaseStore.php b/ProcessMaker/Jobs/CaseStore.php new file mode 100644 index 0000000000..04aeaa289a --- /dev/null +++ b/ProcessMaker/Jobs/CaseStore.php @@ -0,0 +1,32 @@ +create($this->instance); + } +} diff --git a/ProcessMaker/Jobs/CaseUpdate.php b/ProcessMaker/Jobs/CaseUpdate.php new file mode 100644 index 0000000000..005d347aff --- /dev/null +++ b/ProcessMaker/Jobs/CaseUpdate.php @@ -0,0 +1,33 @@ +update($this->instance, $this->token); + } +} diff --git a/ProcessMaker/Jobs/CaseUpdateStatus.php b/ProcessMaker/Jobs/CaseUpdateStatus.php new file mode 100644 index 0000000000..225408f6e1 --- /dev/null +++ b/ProcessMaker/Jobs/CaseUpdateStatus.php @@ -0,0 +1,32 @@ +updateStatus($this->instance); + } +} diff --git a/ProcessMaker/Models/CaseParticipated.php b/ProcessMaker/Models/CaseParticipated.php index 60b076d795..29ce39e7b0 100644 --- a/ProcessMaker/Models/CaseParticipated.php +++ b/ProcessMaker/Models/CaseParticipated.php @@ -3,7 +3,7 @@ namespace ProcessMaker\Models; use Database\Factories\CaseParticipatedFactory; -use Illuminate\Database\Eloquent\Casts\AsArrayObject; +use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; use ProcessMaker\Models\ProcessMakerModel; @@ -31,13 +31,20 @@ class CaseParticipated extends ProcessMakerModel ]; protected $casts = [ - 'processes' => AsArrayObject::class, - 'requests' => AsArrayObject::class, - 'request_tokens' => AsArrayObject::class, - 'tasks' => AsArrayObject::class, - 'participants' => AsArrayObject::class, - 'initiated_at' => 'datetime', - 'completed_at' => 'datetime', + 'processes' => AsCollection::class, + 'requests' => AsCollection::class, + 'request_tokens' => AsCollection::class, + 'tasks' => AsCollection::class, + 'participants' => AsCollection::class, + ]; + + protected $dates = [ + 'initiated_at', + 'completed_at', + ]; + + protected $attributes = [ + 'keywords' => '', ]; protected static function newFactory(): Factory diff --git a/ProcessMaker/Models/CaseStarted.php b/ProcessMaker/Models/CaseStarted.php index 41019b5c6b..3840551816 100644 --- a/ProcessMaker/Models/CaseStarted.php +++ b/ProcessMaker/Models/CaseStarted.php @@ -4,7 +4,7 @@ use Database\Factories\CaseStartedFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Casts\AsArrayObject; +use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Factories\Factory; use ProcessMaker\Models\ProcessMakerModel; @@ -31,13 +31,20 @@ class CaseStarted extends ProcessMakerModel ]; protected $casts = [ - 'processes' => AsArrayObject::class, - 'requests' => AsArrayObject::class, - 'request_tokens' => AsArrayObject::class, - 'tasks' => AsArrayObject::class, - 'participants' => AsArrayObject::class, - 'initiated_at' => 'datetime', - 'completed_at' => 'datetime', + 'processes' => AsCollection::class, + 'requests' => AsCollection::class, + 'request_tokens' => AsCollection::class, + 'tasks' => AsCollection::class, + 'participants' => AsCollection::class, + ]; + + protected $dates = [ + 'initiated_at', + 'completed_at', + ]; + + protected $attributes = [ + 'keywords' => '', ]; protected static function newFactory(): Factory diff --git a/ProcessMaker/Repositories/CaseParticipatedRepository.php b/ProcessMaker/Repositories/CaseParticipatedRepository.php new file mode 100644 index 0000000000..025d3fe7d8 --- /dev/null +++ b/ProcessMaker/Repositories/CaseParticipatedRepository.php @@ -0,0 +1,106 @@ + $token->user->id, + 'case_number' => $case->case_number, + 'case_title' => $case->case_title, + 'case_title_formatted' => $case->case_title_formatted, + 'case_status' => $case->case_status, + 'processes' => $case->processes, + 'requests' => $case->requests, + 'request_tokens' => [$token->getKey()], + 'tasks' => [ + [ + 'id' => $token->getKey(), + 'element_id' => $token->element_id, + 'name' => $token->element_name, + 'process_id' => $token->process_id, + ], + ], + 'participants' => $case->participants, + 'initiated_at' => $case->initiated_at, + 'completed_at' => null, + ]); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } + + /** + * Update the case participated. + * + * @param CaseStarted $case + * @param TokenInterface $token + * @return void + */ + public function update(CaseStarted $case, TokenInterface $token) + { + try { + $caseParticipated = CaseParticipated::where('user_id', $token->user->id) + ->where('case_number', $case->case_number) + ->first(); + + // Add the token data to the request_tokens + $requestTokens = $caseParticipated->request_tokens->push($token->getKey()) + ->unique() + ->values(); + // Add the task data to the tasks + $tasks = $caseParticipated->tasks->push([ + 'id' => $token->getKey(), + 'element_id' => $token->element_id, + 'name' => $token->element_name, + 'process_id' => $token->process_id, + ]) + ->unique('id') + ->values(); + + $caseParticipated->update([ + 'case_title' => $case->case_title, + 'case_title_formatted' => $case->case_title_formatted, + 'case_status' => $case->case_status, + 'processes' => $case->processes, + 'requests' => $case->requests, + 'request_tokens' => $requestTokens, + 'tasks' => $tasks, + 'participants' => $case->participants, + ]); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } + + /** + * Update the status of a case participated. + * + * @param int $caseNumber + * @param array $statusData + * @return void + */ + public function updateStatus(int $caseNumber, array $statusData) + { + try { + CaseParticipated::where('case_number', $caseNumber) + ->update($statusData); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } +} diff --git a/ProcessMaker/Repositories/CaseRepository.php b/ProcessMaker/Repositories/CaseRepository.php new file mode 100644 index 0000000000..3bdd35d65e --- /dev/null +++ b/ProcessMaker/Repositories/CaseRepository.php @@ -0,0 +1,169 @@ +checkIfCaseStartedExist($instance->case_number)) { + return; + } + + try { + $processes = [ + [ + 'id' => $instance->process->id, + 'name' => $instance->process->name, + ], + ]; + + $requests = [ + [ + 'id' => $instance->id, + 'name' => $instance->name, + 'parent_request_id' => $instance->parentRequest->id ?? 0, + ], + ]; + + CaseStarted::create([ + 'case_number' => $instance->case_number, + 'user_id' => $instance->user_id, + 'case_title' => $instance->case_title, + 'case_title_formatted' => $instance->case_title_formatted, + 'case_status' => 'IN_PROGRESS', + 'processes' => $processes, + 'requests' => $requests, + 'request_tokens' => [], + 'tasks' => [], + 'participants' => [], + 'initiated_at' => $instance->initiated_at, + 'completed_at' => null, + ]); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } + + /** + * Update the case started. + * + * @param ExecutionInstanceInterface $instance + * @param TokenInterface $token + * @return void + */ + public function update(ExecutionInstanceInterface $instance, TokenInterface $token): void + { + try { + $case = CaseStarted::where('case_number', $instance->case_number)->first(); + + $case->case_title = $instance->case_title; + $case->case_status = $instance->status === 'ACTIVE' ? 'IN_PROGRESS' : $instance->status; + + $case->request_tokens->push($token->getKey()) + ->unique() + ->values(); + + if (!in_array($token->element_type, ['scriptTask'])) { + $case->tasks->push([ + 'id' => $token->getKey(), + 'element_id' => $token->element_id, + 'name' => $token->element_name, + 'process_id' => $token->process_id, + ]) + ->unique('id') + ->values(); + } + + $this->updateParticipants($case, $token); + + $case->saveOrFail(); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } + + /** + * Update the status of a case started. + * + * @param ExecutionInstanceInterface $instance + * @return void + */ + public function updateStatus(ExecutionInstanceInterface $instance): void + { + try { + $data = [ + 'case_status' => $instance->status, + ]; + + if ($instance->status === 'COMPLETED') { + $data['completed_at'] = Carbon::now(); + } + + // Update the case started and case participated + CaseStarted::where('case_number', $instance->case_number)->update($data); + $this->caseParticipatedRepository->updateStatus($instance->case_number, $data); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } + + /** + * Update the participants of the case started. + * + * @param CaseStarted $case + * @param TokenInterface $token + * @return void + */ + private function updateParticipants(CaseStarted $case, TokenInterface $token): void + { + $user = $token->user; + + if (!$user) { + return; + } + + $participantExists = $case->participants->contains(function ($participant) use ($user) { + return $participant['id'] === $user->id; + }); + + if (!$participantExists) { + $case->participants->push([ + 'id' => $user->id, + 'name' => $user->getFullName(), + 'title' => $user->title, + 'avatar' => $user->avatar, + ]); + + $this->caseParticipatedRepository->create($case, $token); + } + + $this->caseParticipatedRepository->update($case, $token); + } + + /** + * Check if the case started exist. + * + * @param int $caseNumber + * @return bool + */ + private function checkIfCaseStartedExist(int $caseNumber): bool + { + return CaseStarted::where('case_number', $caseNumber)->count() > 0; + } +} diff --git a/ProcessMaker/Repositories/ExecutionInstanceRepository.php b/ProcessMaker/Repositories/ExecutionInstanceRepository.php index 5c6641ddb0..f8110986f1 100644 --- a/ProcessMaker/Repositories/ExecutionInstanceRepository.php +++ b/ProcessMaker/Repositories/ExecutionInstanceRepository.php @@ -3,6 +3,8 @@ namespace ProcessMaker\Repositories; use Carbon\Carbon; +use ProcessMaker\Jobs\CaseStore; +use ProcessMaker\Jobs\CaseUpdateStatus; use ProcessMaker\Models\ProcessCollaboration; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Nayra\Contracts\Bpmn\ParticipantInterface; @@ -184,6 +186,8 @@ public function persistInstanceCreated(ExecutionInstanceInterface $instance) // Set id $instance->setId($instance->getKey()); + CaseStore::dispatch($instance); + // Persists collaboration $this->persistCollaboration($instance); } @@ -208,6 +212,8 @@ public function persistInstanceError(ExecutionInstanceInterface $instance) $instance->status = 'ERROR'; $instance->mergeLatestStoredData(); $instance->saveOrFail(); + + CaseUpdateStatus::dispatch($instance); } /** @@ -255,6 +261,8 @@ public function persistInstanceCompleted(ExecutionInstanceInterface $instance) $instance->completed_at = Carbon::now(); $instance->mergeLatestStoredData(); $instance->saveOrFail(); + + CaseUpdateStatus::dispatch($instance); } /** diff --git a/ProcessMaker/Repositories/TokenRepository.php b/ProcessMaker/Repositories/TokenRepository.php index 7e3e00179a..c33bb4add3 100644 --- a/ProcessMaker/Repositories/TokenRepository.php +++ b/ProcessMaker/Repositories/TokenRepository.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Mustache_Engine; +use ProcessMaker\Jobs\CaseUpdate; use ProcessMaker\Mail\TaskActionByEmail; use ProcessMaker\Models\ProcessAbeRequestToken; use ProcessMaker\Models\ProcessCollaboration; @@ -158,6 +159,9 @@ public function persistActivityActivated(ActivityInterface $activity, TokenInter $token->setId($token->getKey()); $request = $token->getInstance(); $request->notifyProcessUpdated('ACTIVITY_ACTIVATED', $token); + + CaseUpdate::dispatch($request, $token); + if (!is_null($user)) { $this->validateAndSendActionByEmail($activity, $token, $user->email); } diff --git a/composer.json b/composer.json index f591ce30a8..921193f492 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "jenssegers/agent": "^2.6", "laravel/framework": "^10.19", "laravel/horizon": "^5.12", + "laravel/pail": "*", "laravel/passport": "^11.5", "laravel/scout": "^9.8", "laravel/telescope": "^4.12", diff --git a/composer.lock b/composer.lock index 810e0e5350..d24712a93b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cfd04a0f3ee33c659f5333738092adeb", + "content-hash": "2e3ed0dbb125cedadc0fa04b8380026d", "packages": [ { "name": "aws/aws-crt-php", @@ -3484,6 +3484,84 @@ }, "time": "2023-11-23T15:47:58+00:00" }, + { + "name": "laravel/pail", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "c22fe771277971eb9cd224955996bcf39c1a710d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/c22fe771277971eb9cd224955996bcf39c1a710d", + "reference": "c22fe771277971eb9cd224955996bcf39c1a710d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-pcntl": "*", + "illuminate/console": "^10.24|^11.0", + "illuminate/contracts": "^10.24|^11.0", + "illuminate/log": "^10.24|^11.0", + "illuminate/process": "^10.24|^11.0", + "illuminate/support": "^10.24|^11.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "laravel/pint": "^1.13", + "orchestra/testbench": "^8.12|^9.0", + "pestphp/pest": "^2.20", + "pestphp/pest-plugin-type-coverage": "^2.3", + "phpstan/phpstan": "^1.10", + "symfony/var-dumper": "^6.3|^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2024-05-08T18:19:39+00:00" + }, { "name": "laravel/passport", "version": "v11.10.0", diff --git a/database/migrations/2024_09_09_181717_create_cases_started_table.php b/database/migrations/2024_09_09_181717_create_cases_started_table.php index 2f9d32efe5..73f6b35db5 100644 --- a/database/migrations/2024_09_09_181717_create_cases_started_table.php +++ b/database/migrations/2024_09_09_181717_create_cases_started_table.php @@ -12,7 +12,8 @@ public function up(): void { Schema::create('cases_started', function (Blueprint $table) { - $table->unsignedInteger('case_number')->primary(); + $table->id(); + $table->unsignedInteger('case_number')->unique(); $table->unsignedInteger('user_id'); $table->string('case_title', 255); $table->text('case_title_formatted'); @@ -29,6 +30,7 @@ public function up(): void $table->foreign('user_id')->references('id')->on('users'); + $table->index(['case_number']); $table->index(['user_id', 'case_status', 'created_at']); $table->index(['user_id', 'case_status', 'updated_at']); diff --git a/database/migrations/2024_09_12_172734_create_cases_participated_table.php b/database/migrations/2024_09_12_172734_create_cases_participated_table.php index 0e28554f63..d6d9dd00b8 100644 --- a/database/migrations/2024_09_12_172734_create_cases_participated_table.php +++ b/database/migrations/2024_09_12_172734_create_cases_participated_table.php @@ -12,6 +12,7 @@ public function up(): void { Schema::create('cases_participated', function (Blueprint $table) { + $table->id(); $table->unsignedInteger('user_id'); $table->unsignedInteger('case_number'); $table->string('case_title', 255); @@ -27,9 +28,9 @@ public function up(): void $table->timestamps(); $table->text('keywords'); - $table->primary(['user_id', 'case_number']); $table->foreign('user_id')->references('id')->on('users'); + $table->index(['user_id', 'case_number']); $table->index(['user_id', 'case_status', 'created_at']); $table->index(['user_id', 'case_status', 'completed_at']);