@@ -13,29 +13,48 @@ class CertificateSendWindow extends Command
1313{
1414 protected $ signature = 'certificate:send-window
1515 {--edition=2025 : Target edition year}
16- {--type=excellence : excellence|super-organiser}
17- {--limit=500 : Max recipients to send in this run}
16+ {--type=all : excellence|super-organiser|all }
17+ {--limit=500 : Max recipients to send per type in this run}
1818 {--include-send-failed : Include rows that previously failed sending} ' ;
1919
20- protected $ description = 'Send certificate emails in controlled windows (e.g. 500 at a time) ' ;
20+ protected $ description = 'Send certificate emails in controlled windows (e.g. 500 at a time); use --type=all for both certs ' ;
2121
2222 public function handle (): int
2323 {
2424 $ edition = (int ) $ this ->option ('edition ' );
2525 $ limit = max (1 , (int ) $ this ->option ('limit ' ));
2626 $ includeSendFailed = (bool ) $ this ->option ('include-send-failed ' );
2727 $ typeInput = strtolower (trim ((string ) $ this ->option ('type ' )));
28- $ type = match ($ typeInput ) {
29- 'excellence ' => 'Excellence ' ,
30- 'super-organiser ' , 'superorganiser ' => 'SuperOrganiser ' ,
31- default => null ,
32- };
33-
34- if ($ type === null ) {
35- $ this ->error ("Invalid --type value: {$ typeInput }. Use 'excellence' or 'super-organiser'. " );
28+ $ types = $ this ->resolveTypes ($ typeInput );
29+ if ($ types === null ) {
30+ $ this ->error ("Invalid --type value: {$ typeInput }. Use 'excellence', 'super-organiser', or 'all'. " );
3631 return self ::FAILURE ;
3732 }
3833
34+ $ totalQueued = 0 ;
35+ $ totalFailed = 0 ;
36+ $ any = false ;
37+
38+ foreach ($ types as $ type ) {
39+ $ result = $ this ->sendWindowForType ($ edition , $ type , $ limit , $ includeSendFailed );
40+ $ totalQueued += $ result ['queued ' ];
41+ $ totalFailed += $ result ['failed ' ];
42+ if ($ result ['processed ' ] > 0 ) {
43+ $ any = true ;
44+ }
45+ }
46+
47+ $ this ->newLine ();
48+ $ this ->info ("Send window(s) complete. Total queued: {$ totalQueued }, Total failed: {$ totalFailed }. " );
49+ if ($ any ) {
50+ $ this ->line ('Run the same command again to process the next batch (or ensure queue worker is running: php artisan queue:work). ' );
51+ }
52+ return self ::SUCCESS ;
53+ }
54+
55+ /** @return array{processed: int, queued: int, failed: int} */
56+ private function sendWindowForType (int $ edition , string $ type , int $ limit , bool $ includeSendFailed ): array
57+ {
3958 $ query = Excellence::query ()
4059 ->where ('edition ' , $ edition )
4160 ->where ('type ' , $ type )
@@ -51,12 +70,13 @@ public function handle(): int
5170
5271 $ rows = $ query ->get ();
5372 if ($ rows ->isEmpty ()) {
54- $ this ->info ( ' No recipients found for this send window. ' );
55- return self :: SUCCESS ;
73+ $ this ->line ( " [ { $ type } ] No recipients left for this window." );
74+ return [ ' processed ' => 0 , ' queued ' => 0 , ' failed ' => 0 ] ;
5675 }
5776
58- $ this ->info ("Queueing {$ rows ->count ()} { $ type } emails for edition {$ edition }... " );
77+ $ this ->info ("[ { $ type } ] Queueing {$ rows ->count ()} emails ( edition {$ edition }) ... " );
5978 $ bar = $ this ->output ->createProgressBar ($ rows ->count ());
79+ $ bar ->setFormat (" %current%/%max% [%bar%] %percent:3s%% " );
6080 $ bar ->start ();
6181
6282 $ queued = 0 ;
@@ -89,10 +109,19 @@ public function handle(): int
89109 }
90110
91111 $ bar ->finish ();
92- $ this ->newLine (2 );
93- $ this ->info ("Send window complete. Queued: {$ queued }, Failed: {$ failed }. " );
94- $ this ->line ('Run again to process the next send window. ' );
112+ $ this ->newLine ();
113+ $ this ->line (" [ {$ type }] Done. Queued: {$ queued }, Failed: {$ failed }. " );
114+ return ['processed ' => $ rows ->count (), 'queued ' => $ queued , 'failed ' => $ failed ];
115+ }
95116
96- return self ::SUCCESS ;
117+ /** @return array<string>|null */
118+ private function resolveTypes (string $ typeInput ): ?array
119+ {
120+ return match ($ typeInput ) {
121+ 'all ' => ['Excellence ' , 'SuperOrganiser ' ],
122+ 'excellence ' => ['Excellence ' ],
123+ 'super-organiser ' , 'superorganiser ' => ['SuperOrganiser ' ],
124+ default => null ,
125+ };
97126 }
98127}
0 commit comments