diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php index ee8db4544..8d7e4df58 100644 --- a/database/factories/ApplicationFactory.php +++ b/database/factories/ApplicationFactory.php @@ -60,3 +60,41 @@ $factory->state(\ProcessMaker\Model\Application::class, 'foreign_keys', function 'APP_DATA' => serialize(['APP_NUMBER' => $appNumber]) ]; }); + +$factory->state(\ProcessMaker\Model\Application::class, 'todo', function (Faker $faker) { + return [ + 'APP_NUMBER' => $faker->unique()->numberBetween(1000), + 'APP_STATUS_ID' => 2, + 'APP_STATUS' => 'TO_DO' + ]; +}); + +$factory->state(\ProcessMaker\Model\Application::class, 'draft', function (Faker $faker) { + return [ + 'APP_NUMBER' => $faker->unique()->numberBetween(1000), + 'APP_STATUS_ID' => 1, + 'APP_STATUS' => 'DRAFT' + ]; +}); + +$factory->state(\ProcessMaker\Model\Application::class, 'draft_minor_case', function (Faker $faker) { + $caseNumber = $faker->unique()->numberBetween(1, 1000); + return [ + 'APP_NUMBER' => $caseNumber, + 'APP_TITLE' => 'Case # ' . $caseNumber, + 'APP_STATUS_ID' => 1, + 'APP_STATUS' => 'DRAFT', + 'APP_UPDATE_DATE' => $faker->dateTimeBetween('-2 year', '-1 year') + ]; +}); + +$factory->state(\ProcessMaker\Model\Application::class, 'draft_major_case', function (Faker $faker) { + $caseNumber = $faker->unique()->numberBetween(2000, 3000); + return [ + 'APP_NUMBER' => $caseNumber, + 'APP_TITLE' => 'Case # ' . $caseNumber, + 'APP_STATUS_ID' => 1, + 'APP_STATUS' => 'DRAFT', + 'APP_UPDATE_DATE' => $faker->dateTimeBetween('now', '+1 year') + ]; +}); diff --git a/tests/unit/workflow/engine/src/ProcessMaker/BusinessModel/Cases/DraftTest.php b/tests/unit/workflow/engine/src/ProcessMaker/BusinessModel/Cases/DraftTest.php new file mode 100644 index 000000000..47ad16abe --- /dev/null +++ b/tests/unit/workflow/engine/src/ProcessMaker/BusinessModel/Cases/DraftTest.php @@ -0,0 +1,514 @@ +create(); + //Create user + $user = factory(User::class)->create(); + //Create a task related with the process + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID + ]); + //Create application and app_delegation related with DRAFT status + $casesDraft = 25; + for ($x = 1; $x <= $casesDraft; $x++) { + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + } + + //Create application and app_delegation related with OTHER status + $casesTodo = 25; + for ($x = 1; $x <= $casesTodo; $x++) { + $application = factory(Application::class)->states('todo')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + } + + //Review the count draft + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $result = $draft->getCounter(); + $this->assertEquals($casesDraft, $result); + } + + /** + * This checks to make sure pagination is working properly in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_paged() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID + ]); + //Create application and app_delegation related with DRAFT status + $casesDraft = 51; + for ($x = 1; $x <= $casesDraft; $x++) { + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + } + + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('APP_DELEGATION.APP_NUMBER'); + $draft->setOffset(0); + $draft->setLimit(25); + $results = $draft->getData(); + $this->assertCount(25, $results); + // Get second page + $draft->setOffset(25); + $draft->setLimit(25); + $results = $draft->getData(); + $this->assertCount(25, $results); + // Get third page + $draft->setOffset(50); + $draft->setLimit(25); + $results = $draft->getData(); + $this->assertCount(1, $results); + } + + /** + * This ensures ordering ascending and descending works by case number APP_NUMBER in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_sort_by_case_number() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID + ]); + //Create application and app_delegation related with DRAFT status with a minor case number + $application = factory(Application::class)->states('draft_minor_case')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + //Create application and app_delegation related with DRAFT status with a minor case number + $application2 = factory(Application::class)->states('draft_major_case')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application2->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + //Get the data ordered by APP_NUMBER + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('APP_DELEGATION.APP_NUMBER'); + // Get first page, the minor case id + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertGreaterThan($results[0]['APP_NUMBER'], $results[1]['APP_NUMBER']); + // Get first page, the major case id + $draft->setOrderDirection('DESC'); + $results = $draft->getData(); + $this->assertLessThan($results[0]['APP_NUMBER'], $results[1]['APP_NUMBER']); + } + + /** + * This ensures ordering ascending and descending works by case title APP_TITLE in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_sort_by_case_title() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID + ]); + //Create application and app_delegation related with DRAFT status with a minor case title + $application = factory(Application::class)->states('draft_minor_case')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + //Create application and app_delegation related with DRAFT status with a minor case title + $application2 = factory(Application::class)->states('draft_major_case')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application2->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('APPLICATION.APP_TITLE'); + // Get first page, the minor case title + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertGreaterThan($results[0]['APP_TITLE'], $results[1]['APP_TITLE']); + // Get first page, the major case title + $draft->setOrderDirection('DESC'); + $results = $draft->getData(); + $this->assertLessThan($results[0]['APP_TITLE'], $results[1]['APP_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by case title PRO_TITLE in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_sort_by_process() + { + //Create process + $process = factory(Process::class)->create([ + 'PRO_TITLE' => 'China Supplier Payment Proposal' + ]); + $process2 = factory(Process::class)->create([ + 'PRO_TITLE' => 'Egypt Supplier Payment Proposal' + ]); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + ]); + $task2 = factory(Task::class)->create([ + 'PRO_UID' => $process2->PRO_UID + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'PRO_ID' => $process->id, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'PRO_ID' => $process2->id, + 'TAS_ID' => $task2->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('PRO_TITLE'); + // Get first page, the minor case title + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertGreaterThan($results[0]['PRO_TITLE'], $results[1]['PRO_TITLE']); + // Get first page, the major case title + $draft->setOrderDirection('DESC'); + $results = $draft->getData(); + $this->assertLessThan($results[0]['PRO_TITLE'], $results[1]['PRO_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by task title TAS_TITLE in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_sort_by_task_title() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + 'TAS_TITLE' => 'Initiate Request', + 'TAS_TYPE' => 'NORMAL', + ]); + $task2 = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + 'TAS_TITLE' => 'Waiting for AP Manager Validation', + 'TAS_TYPE' => 'NORMAL', + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + //Create the register in delegation related to draft + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task2->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('TAS_TITLE'); + // Get first page, the minor case title + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertGreaterThan($results[0]['TAS_TITLE'], $results[1]['TAS_TITLE']); + // Get first page, the major case title + $draft->setOrderDirection('DESC'); + $results = $draft->getData(); + $this->assertLessThan($results[0]['TAS_TITLE'], $results[1]['TAS_TITLE']); + } + + /** + * This ensures ordering ascending and descending works by due date DEL_TASK_DUE_DATE in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_sort_due_date() + { + $faker = \Faker\Factory::create(); + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + 'TAS_TYPE' => 'NORMAL', + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + 'DEL_TASK_DUE_DATE' => $faker->dateTimeBetween('now', '+1 year') + ]); + //Create the register in delegation related to draft + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + 'DEL_TASK_DUE_DATE' => $faker->dateTimeBetween('-2 year', '-1 year') + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('DEL_TASK_DUE_DATE'); + // Get first page, the minor case title + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertGreaterThan($results[0]['DEL_TASK_DUE_DATE'], $results[1]['DEL_TASK_DUE_DATE']); + // Get first page, the major case title + $draft->setOrderDirection('DESC'); + $results = $draft->getData(); + $this->assertLessThan($results[0]['DEL_TASK_DUE_DATE'], $results[1]['DEL_TASK_DUE_DATE']); + } + + /** + * This ensures ordering ascending and descending works by last modified APP_UPDATE_DATE in draft + * @covers ::getData + * @test + */ + public function it_should_return_draft_sort_last_modified() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + ]); + //Create application and app_delegation related with DRAFT status with a minor update date + $application = factory(Application::class)->states('draft_minor_case')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID + ]); + //Create application and app_delegation related with DRAFT status with a minor update date + $application2 = factory(Application::class)->states('draft_major_case')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application2->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('APP_UPDATE_DATE'); + // Get first page, the minor update date + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertGreaterThan($results[0]['APP_UPDATE_DATE'], $results[1]['APP_UPDATE_DATE']); + // Get first page, the major update date + $draft->setOrderDirection('DESC'); + $results = $draft->getData(); + $this->assertLessThan($results[0]['APP_UPDATE_DATE'], $results[1]['APP_UPDATE_DATE']); + } + + /** + * This ensures searching specific cases and review the page in draft + * @covers ::getData + * @test + */ + public function it_should_search_draft_search_specific_case_uid() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_UID' => $application->APP_UID, + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID + ]); + //Create other cases + factory(Delegation::class, 5)->create([ + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('APP_DELEGATION.APP_NUMBER'); + // Get first page, the specific case + $draft->setOrderDirection('ASC'); + $draft->setCaseUid($application->APP_UID); + $results = $draft->getData(); + $this->assertEquals($application->APP_UID, $results[0]['APP_UID']); + } + + /** + * This ensures searching specific cases and review the page in draft + * @covers ::getData + * @test + */ + public function it_should_search_draft_search_specific_cases_uid_array() + { + //Create process + $process = factory(Process::class)->create(); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_UID' => $application->APP_UID, + 'APP_NUMBER' => $application->APP_NUMBER, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('APP_UPDATE_DATE'); + $draft->setOrderByColumn('APP_DELEGATION.APP_NUMBER'); + // Get first page, the specific case + $draft->setOrderDirection('ASC'); + $draft->setCasesUids([$application->APP_UID]); + $results = $draft->getData(); + $this->assertEquals($application->APP_UID, $results[0]['APP_UID']); + } + + /** + * This ensures searching specific process and review the page in draft + * @covers ::getData + * @test + */ + public function it_should_search_draft_search_specific_process() + { + //Create process + $process = factory(Process::class)->create([ + 'PRO_TITLE' => 'China Supplier Payment Proposal' + ]); + $process2 = factory(Process::class)->create([ + 'PRO_TITLE' => 'Egypt Supplier Payment Proposal' + ]); + //Create user + $user = factory(User::class)->create(); + //Create a task + $task = factory(Task::class)->create([ + 'PRO_UID' => $process->PRO_UID, + ]); + $task2 = factory(Task::class)->create([ + 'PRO_UID' => $process2->PRO_UID + ]); + //Create application and app_delegation related with DRAFT status + $application = factory(Application::class)->states('draft')->create(); + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'PRO_ID' => $process->id, + 'TAS_ID' => $task->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + //Create the register in delegation related to draft + factory(Delegation::class)->create([ + 'APP_NUMBER' => $application->APP_NUMBER, + 'PRO_ID' => $process2->id, + 'TAS_ID' => $task2->TAS_ID, + 'USR_ID' => $user->USR_ID, + ]); + // Get first page + $draft = new Draft(); + $draft->setUserId($user->USR_ID); + $draft->setOrderByColumn('PRO_TITLE'); + $draft->setProcessId($process->id); + // Get first page, the minor case title + $draft->setOrderDirection('ASC'); + $results = $draft->getData(); + $this->assertEquals($process->PRO_UID, $results[0]['PRO_UID']); + // Get first page, the major case title + $draft->setOrderDirection('DESC'); + $draft->setProcessId($process2->id); + $results = $draft->getData(); + $this->assertEquals($process2->PRO_UID, $results[0]['PRO_UID']); + } +} \ No newline at end of file diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Cases/Draft.php b/workflow/engine/src/ProcessMaker/BusinessModel/Cases/Draft.php index 208b92cb6..46fa649fc 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Cases/Draft.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Cases/Draft.php @@ -2,7 +2,61 @@ namespace ProcessMaker\BusinessModel\Cases; +use ProcessMaker\Model\Delegation; + class Draft extends AbstractCases { + /** + * Get data self-services cases by user + * + * @return array + */ + public function getData() + { + $query = Delegation::query()->select(); + // Add the initial scope for draft cases + $query->draft($this->getUserId()); + // Add join with task + $query->specificTaskTypes(['NORMAL', 'ADHOC']); + // Add join for process, but only for certain scenarios such as category or process + if ($this->getCategoryUid() || $this->getProcessUid() || $this->getOrderByColumn() === 'PRO_TITLE') { + $query->categoryProcess($this->getCategoryUid()); + } + // Specific process + if ($this->getProcessId()) { + $query->processId($this->getProcessId()); + } + // Specific case uid + if (!empty($this->getCaseUid())) { + $query->appUid($this->getCaseUid()); + } + // Specific cases + if (!empty($this->getCasesUids())) { + $query->specificCasesByUid($this->getCasesUids()); + } + // Add any sort if needed + if ($this->getOrderByColumn()) { + $query->orderBy($this->getOrderByColumn(), $this->getOrderDirection()); + } + // Add pagination to the query + $query->offset($this->getOffset())->limit($this->getLimit()); + // Get the data + $results = $query->get(); + return $results->values()->toArray(); + } + + /** + * Count the self-services cases by user + * + * @return int + */ + public function getCounter() + { + $query = Delegation::query()->select(); + // Add the initial scope for draft cases + $query->draft($this->getUserId()); + + return $query->count(); + } } diff --git a/workflow/engine/src/ProcessMaker/Model/Delegation.php b/workflow/engine/src/ProcessMaker/Model/Delegation.php index 9829c6caa..6dbb04086 100644 --- a/workflow/engine/src/ProcessMaker/Model/Delegation.php +++ b/workflow/engine/src/ProcessMaker/Model/Delegation.php @@ -293,6 +293,27 @@ class Delegation extends Model return $query; } + /** + * Scope a join with task and include a specific task assign type: + * NORMAL|ADHOC|SUBPROCESS|HIDDEN|GATEWAYTOGATEWAY|WEBENTRYEVENT|END-MESSAGE-EVENT|START-MESSAGE-EVENT| + * INTERMEDIATE-THROW-MESSAGE-EVENT|INTERMEDIATE-CATCH-MESSAGE-EVENT|SCRIPT-TASK|START-TIMER-EVENT| + * INTERMEDIATE-CATCH-TIMER-EVENT|END-EMAIL-EVENT|INTERMEDIATE-THROW-EMAIL-EVENT|SERVICE-TASK + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param array $taskTypes + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public function scopeSpecificTaskTypes($query, array $taskTypes) + { + $query->join('TASK', function ($join) use ($taskTypes) { + $join->on('APP_DELEGATION.TAS_ID', '=', 'TASK.TAS_ID') + ->whereIn('TASK.TAS_TYPE', $taskTypes); + }); + + return $query; + } + /** * Scope a join with APPLICATION with specific app status * @@ -376,6 +397,24 @@ class Delegation extends Model return $query; } + /** + * Scope a draft cases + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param int $user + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public function scopeDraft($query, $user) + { + // Add join for application, for get the case title when the case status is DRAFT + $query->appStatusId(Application::STATUS_DRAFT); + // Case assigned to the user + $query->userId($user); + + return $query; + } + /** * Get specific cases unassigned that the user can view *