66use Illuminate \Contracts \Queue \ShouldQueue ;
77use Illuminate \Foundation \Queue \Queueable ;
88use Illuminate \Support \Facades \Log ;
9+ use ProcessMaker \Http \Controllers \Api \Actions \Cases \DeletesCaseRecords ;
910use ProcessMaker \Models \CaseNumber ;
11+ use ProcessMaker \Models \CaseParticipated ;
12+ use ProcessMaker \Models \CaseStarted ;
1013use ProcessMaker \Models \Process ;
1114use ProcessMaker \Models \ProcessRequest ;
15+ use ProcessMaker \Models \ProcessRequestToken ;
16+ use ProcessMaker \Models \TaskDraft ;
1217
1318class EvaluateProcessRetentionJob implements ShouldQueue
1419{
15- use Queueable;
20+ use Queueable, DeletesCaseRecords ;
1621
1722 /**
1823 * Create a new job instance.
@@ -69,14 +74,14 @@ public function handle(): void
6974 return ;
7075 }
7176
72- // Default to 1_year if retention_period is not set
73- $ retentionPeriod = $ process ->properties ['retention_period ' ] ?? '1_year ' ;
77+ // Default to one_year if retention_period is not set
78+ $ retentionPeriod = $ process ->properties ['retention_period ' ] ?? 'one_year ' ;
7479 $ retentionMonths = match ($ retentionPeriod ) {
75- '6_months ' => 6 ,
76- '1_year ' => 12 ,
77- '3_years ' => 36 ,
78- '5_years ' => 60 ,
79- default => 12 , // Default to 1_year
80+ 'six_months ' => 6 ,
81+ 'one_year ' => 12 ,
82+ 'three_years ' => 36 ,
83+ 'five_years ' => 60 ,
84+ default => 12 , // Default to one_year
8085 };
8186
8287 Log::info ('EvaluateProcessRetentionJob: Retention configuration loaded ' , [
@@ -126,41 +131,76 @@ public function handle(): void
126131 // Use subquery to get process request IDs
127132 $ processRequestSubquery = ProcessRequest::where ('process_id ' , $ this ->processId )->select ('id ' );
128133
134+ // Collect all ProcessRequest IDs that will be deleted (to delete them after all chunks are processed)
135+ $ processRequestIdsToDelete = [];
129136 $ totalDeleted = 0 ;
130137 $ chunkCount = 0 ;
131- $ processId = $ this ->processId ;
132138
133139 CaseNumber::whereIn ('process_request_id ' , $ processRequestSubquery )
134- ->where (function ($ query ) use ($ retentionUpdatedAt , $ oldCasesCutoff , $ newCasesCutoff ) {
135- // Cases created before retention_updated_at: delete if created before (retention_updated_at - retention_period)
136- $ query ->where (function ($ q ) use ($ retentionUpdatedAt , $ oldCasesCutoff ) {
137- $ q ->where ('created_at ' , '< ' , $ retentionUpdatedAt )
138- ->where ('created_at ' , '< ' , $ oldCasesCutoff );
139- })
140- // Cases created after retention_updated_at: delete if created before (now - retention_period)
141- ->orWhere (function ($ q ) use ($ retentionUpdatedAt , $ newCasesCutoff ) {
142- $ q ->where ('created_at ' , '>= ' , $ retentionUpdatedAt )
143- ->where ('created_at ' , '< ' , $ newCasesCutoff );
144- });
145- })
146- ->chunkById (100 , function ($ cases ) use (&$ totalDeleted , &$ chunkCount , $ processId ) {
147- $ caseIds = $ cases ->pluck ('id ' );
148- $ chunkSize = $ caseIds ->count ();
140+ ->where ($ this ->buildRetentionQuery ($ retentionUpdatedAt , $ oldCasesCutoff , $ newCasesCutoff ))
141+ ->chunkById (100 , function ($ cases ) use (&$ processRequestIdsToDelete , &$ totalDeleted , &$ chunkCount ) {
142+ $ caseIds = $ cases ->pluck ('id ' )->all ();
143+ $ processRequestIds = $ cases ->pluck ('process_request_id ' )->unique ()->all ();
144+
145+ // Collect ProcessRequest IDs for deletion after all chunks are processed
146+ $ processRequestIdsToDelete = array_merge ($ processRequestIdsToDelete , $ processRequestIds );
149147
150- // Delete the cases
148+ $ processRequestTokenIds = ProcessRequestToken::whereIn ('process_request_id ' , $ processRequestIds )->pluck ('id ' )->all ();
149+ $ draftIds = $ this ->getTaskDraftIds ($ processRequestTokenIds );
150+
151+ // uses case_number to delete
152+ $ this ->deleteCasesStarted ($ caseIds );
153+ $ this ->deleteCasesParticipated ($ caseIds );
154+ $ this ->deleteComments ($ caseIds , $ processRequestIds , $ processRequestTokenIds );
155+
156+ // Delete the CaseNumber records that were returned by the query (by their IDs)
151157 CaseNumber::whereIn ('id ' , $ caseIds )->delete ();
152158
153- $ totalDeleted += $ chunkSize ;
159+ $ this ->deleteProcessRequestLocks ($ processRequestIds , $ processRequestTokenIds );
160+ $ this ->deleteInboxRuleLogs ($ processRequestTokenIds );
161+ $ this ->deleteInboxRules ($ processRequestTokenIds );
162+ $ this ->deleteProcessAbeRequestTokens ($ processRequestIds , $ processRequestTokenIds );
163+ $ this ->deleteScheduledTasks ($ processRequestIds , $ processRequestTokenIds );
164+ $ this ->deleteEllucianEthosSyncTasks ($ processRequestTokenIds );
165+
166+ $ this ->deleteTaskDraftMedia ($ draftIds );
167+ $ this ->deleteTaskDrafts ($ processRequestTokenIds );
168+
169+
154170 $ chunkCount ++;
171+ $ chunkSize = count ($ caseIds );
172+ $ totalDeleted += $ chunkSize ;
155173
156174 Log::info ('EvaluateProcessRetentionJob: Deleted chunk of cases ' , [
157- 'process_id ' => $ processId ,
175+ 'process_id ' => $ this -> processId ,
158176 'chunk_number ' => $ chunkCount ,
159177 'cases_deleted ' => $ chunkSize ,
160178 ]);
161179 });
162180
163- $ executionTime = round ((microtime (true ) - $ startTime ) * 1000 , 2 );
181+ // Delete ProcessRequests after all chunks are processed
182+ // Only delete ProcessRequests that have no remaining cases
183+ if (!empty ($ processRequestIdsToDelete )) {
184+ $ processRequestIdsToDelete = array_unique ($ processRequestIdsToDelete );
185+
186+ // Filter to only ProcessRequests that have no remaining CaseNumbers
187+ $ processRequestIdsWithNoCases = array_filter ($ processRequestIdsToDelete , function ($ requestId ) {
188+ return !CaseNumber::where ('process_request_id ' , $ requestId )->exists ();
189+ });
190+
191+ if (!empty ($ processRequestIdsWithNoCases )) {
192+ $ this ->deleteProcessRequests ($ processRequestIdsWithNoCases );
193+
194+ // Delete any remaining related records
195+ $ this ->deleteRequestMedia ($ processRequestIdsWithNoCases );
196+ $ this ->deleteNotifications ($ processRequestIdsWithNoCases );
197+
198+ $ this ->dispatchSavedSearchRecount ();
199+ }
200+ }
201+
202+ $ endTime = microtime (true );
203+ $ executionTime = round (($ endTime - $ startTime ) * 1000 , 2 );
164204
165205 Log::info ('EvaluateProcessRetentionJob: Evaluation completed ' , [
166206 'process_id ' => $ this ->processId ,
@@ -169,4 +209,44 @@ public function handle(): void
169209 'execution_time_ms ' => $ executionTime ,
170210 ]);
171211 }
212+
213+ /**
214+ * Build a retention query closure that can be applied to any query builder.
215+ *
216+ * This method encapsulates the retention evaluation logic:
217+ * - Cases created before retention_updated_at: delete if created before (retention_updated_at - retention_period)
218+ * - Cases created after retention_updated_at: delete if created before (now - retention_period)
219+ *
220+ * @param Carbon $retentionUpdatedAt The date when the retention policy was updated
221+ * @param Carbon $oldCasesCutoff The cutoff date for cases created before retention_updated_at
222+ * @param Carbon $newCasesCutoff The cutoff date for cases created after retention_updated_at
223+ * @return \Closure A closure that applies the retention query to a query builder
224+ */
225+ private function buildRetentionQuery (Carbon $ retentionUpdatedAt , Carbon $ oldCasesCutoff , Carbon $ newCasesCutoff ): \Closure
226+ {
227+ return function ($ query ) use ($ retentionUpdatedAt , $ oldCasesCutoff , $ newCasesCutoff ) {
228+ // Cases created before retention_updated_at: delete if created before (retention_updated_at - retention_period)
229+ $ query ->where (function ($ q ) use ($ retentionUpdatedAt , $ oldCasesCutoff ) {
230+ $ q ->where ('created_at ' , '< ' , $ retentionUpdatedAt )
231+ ->where ('created_at ' , '< ' , $ oldCasesCutoff );
232+ })
233+ // Cases created after retention_updated_at: delete if created before (now - retention_period)
234+ ->orWhere (function ($ q ) use ($ retentionUpdatedAt , $ newCasesCutoff ) {
235+ $ q ->where ('created_at ' , '>= ' , $ retentionUpdatedAt )
236+ ->where ('created_at ' , '< ' , $ newCasesCutoff );
237+ });
238+ };
239+ }
240+
241+ private function getTaskDraftIds (array $ tokenIds ): array
242+ {
243+ if ($ tokenIds === []) {
244+ return [];
245+ }
246+
247+ return TaskDraft::query ()
248+ ->whereIn ('task_id ' , $ tokenIds )
249+ ->pluck ('id ' )
250+ ->all ();
251+ }
172252}
0 commit comments