Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions ProcessMaker/Http/Controllers/Api/Actions/Cases/DeleteCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace ProcessMaker\Http\Controllers\Api\Actions\Cases;

use Illuminate\Support\Facades\DB;
use ProcessMaker\Models\CaseNumber;
use ProcessMaker\Models\CaseParticipated;
use ProcessMaker\Models\CaseStarted;
use ProcessMaker\Models\ProcessRequest;

class DeleteCase
{
public function __invoke(string $caseNumber): void
{
// Delete later dependent records for requests and tokens (process_request_tokens,
// process_request_locks, process_abe_request_tokens, scheduled_tasks,
// inbox_rules, inbox_rule_logs, ellucian_ethos_sync_global_task_list, comments).

$requestIds = ProcessRequest::query()
->where('case_number', $caseNumber)
->pluck('id')
->all();

if ($requestIds === []) {
abort(404);
}

DB::transaction(function () use ($caseNumber, $requestIds) {
CaseStarted::query()
->where('case_number', $caseNumber)
->delete();

CaseParticipated::query()
->where('case_number', $caseNumber)
->delete();

CaseNumber::query()
->whereIn('process_request_id', $requestIds)
->delete();

ProcessRequest::query()
->whereIn('id', $requestIds)
->delete();
});
}
}
46 changes: 40 additions & 6 deletions ProcessMaker/Http/Controllers/Api/CaseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace ProcessMaker\Http\Controllers\Api;

use Illuminate\Http\JsonResponse;
use ProcessMaker\Http\Controllers\Api\Actions\Cases\DeleteCase;
use ProcessMaker\Http\Controllers\Controller;
use ProcessMaker\Models\Process;
use ProcessMaker\Models\ProcessRequest;
Expand All @@ -12,7 +14,7 @@ class CaseController extends Controller
/**
* Get stage information for cases
*/
public function getStagePerCase($case_number = null)
public function getStagePerCase(?string $case_number = null): JsonResponse
{
if (!empty($case_number)) {
$responseData = $this->getSpecificCaseStages($case_number);
Expand All @@ -31,12 +33,44 @@ public function getStagePerCase($case_number = null)
return response()->json($responseData);
}

/**
* Delete a case and its related requests.
*
* @param string $case_number
* @return JsonResponse
*
* @OA\Delete(
* path="/cases/{case_number}",
* summary="Delete a case and its related requests",
* operationId="deleteCase",
* tags={"Cases"},
* @OA\Parameter(
* description="Case number to delete",
* in="path",
* name="case_number",
* required=true,
* @OA\Schema(type="string")
* ),
* @OA\Response(
* response=204,
* description="success"
* ),
* @OA\Response(response=404, ref="#/components/responses/404"),
* )
*/
public function destroy(string $case_number): JsonResponse
{
(new DeleteCase)($case_number);

return response()->json([], 204);
}

/**
* Get specific case stages information
* @param string $caseNumber The unique identifier of the case to retrieve stages for
* @return array
*/
private function getSpecificCaseStages($caseNumber)
private function getSpecificCaseStages(string $caseNumber): array
{
$allRequests = ProcessRequest::where('case_number', $caseNumber)->get();
// Check if any requests were found
Expand Down Expand Up @@ -75,7 +109,7 @@ private function getSpecificCaseStages($caseNumber)
* @param string|null $status The status to set for the stages
* @return array
*/
private function getDefaultCaseStages($status = null)
private function getDefaultCaseStages(?string $status = null): array
{
return [
[
Expand All @@ -100,7 +134,7 @@ private function getDefaultCaseStages($status = null)
* @param string $stageName The name of the stage ('In Progress' or 'Completed')
* @return string The mapped status
*/
private function mapStatus($status, $stageName)
private function mapStatus(?string $status, string $stageName): string
{
if ($status === 'COMPLETED') {
return 'Done';
Expand All @@ -120,11 +154,11 @@ private function mapStatus($status, $stageName)
/**
* Get the stages summary based on the provided request.
*
* @param $requestId
* @param ProcessRequest $request
* @return array An array of stage results, each containing the stage ID, name, status,
* and completion date.
*/
private function getStagesSummary(ProcessRequest $request)
private function getStagesSummary(ProcessRequest $request): array
{
$requestId = $request->id;
$processId = $request->process_id;
Expand Down
16 changes: 16 additions & 0 deletions database/factories/ProcessMaker/Models/ProcessRequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,20 @@ public function definition()
},
];
}

public function withCaseNumber(int $caseNumber): self
{
$caseTitle = $this->faker->words(4, true);

return $this->state([
'case_number' => $caseNumber,
'case_title' => $caseTitle,
'case_title_formatted' => $caseTitle,
])->afterCreating(function (ProcessRequest $request) use ($caseNumber, $caseTitle) {
$request->case_number = $caseNumber;
$request->case_title = $caseTitle;
$request->case_title_formatted = $caseTitle;
$request->save();
});
}
}
1 change: 1 addition & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@

// Cases
Route::get('cases/stages_bar/{case_number?}', [CaseController::class, 'getStagePerCase'])->name('cases.stage');
Route::delete('cases/{case_number}', [CaseController::class, 'destroy'])->name('cases.destroy');

// TaskDrafts
Route::put('drafts/{task}', [TaskDraftController::class, 'update'])->name('taskdraft.update');
Expand Down
47 changes: 47 additions & 0 deletions tests/Feature/Api/CaseDeleteTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Tests\Feature\Api;

use ProcessMaker\Models\CaseNumber;
use ProcessMaker\Models\CaseParticipated;
use ProcessMaker\Models\CaseStarted;
use ProcessMaker\Models\ProcessRequest;
use Tests\Feature\Shared\RequestHelper;
use Tests\TestCase;

class CaseDeleteTest extends TestCase
{
use RequestHelper;

public function testDeleteCaseRemovesCoreRecords(): void
{
$caseNumber = 12345;
$requests = ProcessRequest::factory()
->count(2)
->withCaseNumber($caseNumber)
->create();

CaseNumber::query()->create(['process_request_id' => $requests->first()->id]);
CaseNumber::query()->create(['process_request_id' => $requests->last()->id]);
CaseStarted::factory()->create(['case_number' => $caseNumber]);
CaseParticipated::factory()->create(['case_number' => $caseNumber]);

$response = $this->apiCall('DELETE', route('api.cases.destroy', ['case_number' => $caseNumber]));

$response->assertStatus(204);
$this->assertDatabaseMissing('process_requests', ['case_number' => $caseNumber]);
$this->assertDatabaseMissing('cases_started', ['case_number' => $caseNumber]);
$this->assertDatabaseMissing('cases_participated', ['case_number' => $caseNumber]);
$this->assertDatabaseMissing('case_numbers', ['process_request_id' => $requests->first()->id]);
$this->assertDatabaseMissing('case_numbers', ['process_request_id' => $requests->last()->id]);
}

public function testDeleteCaseReturnsNotFoundWhenMissing(): void
{
$caseNumber = 99999;

$response = $this->apiCall('DELETE', route('api.cases.destroy', ['case_number' => $caseNumber]));

$response->assertStatus(404);
}
}
Loading