add notifications logic and add support of both JWT and Sanctum
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Attendance\AttendanceAutoPublishJobService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class AttendanceAutoPublishCommand extends Command
|
||||
{
|
||||
protected $signature = 'attendance:auto-publish';
|
||||
protected $description = 'Auto-publish attendance days per "second Sunday backward" rule.';
|
||||
|
||||
public function handle(AttendanceAutoPublishJobService $service): int
|
||||
{
|
||||
$result = $service->run();
|
||||
|
||||
$this->info(sprintf(
|
||||
'Auto-publish checked at %s (TZ: %s). Published rows: %d.',
|
||||
$result['checked_at'],
|
||||
$result['timezone'],
|
||||
$result['published']
|
||||
));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Payments\PaymentMissedCheckService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CheckMissedPaymentsCommand extends Command
|
||||
{
|
||||
protected $signature = 'payments:check-missed';
|
||||
protected $description = 'Checks for users who missed payments and sends reminders.';
|
||||
|
||||
public function handle(PaymentMissedCheckService $service): int
|
||||
{
|
||||
$users = $service->findUsersWithMissedPayments();
|
||||
if (empty($users)) {
|
||||
$this->info('No missed payments found.');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$result = $service->sendReminders($users);
|
||||
$this->info(sprintf('Finished checking missed payments. Sent: %d, Failed: %d.', $result['sent'], $result['failed']));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Notifications\NotificationCleanupService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CleanupExpiredNotificationsCommand extends Command
|
||||
{
|
||||
protected $signature = 'notifications:cleanup';
|
||||
protected $description = 'Deletes expired notifications from the database.';
|
||||
|
||||
public function handle(NotificationCleanupService $service): int
|
||||
{
|
||||
$count = $service->cleanupExpired();
|
||||
|
||||
if ($count <= 0) {
|
||||
$this->info('No expired notifications found to soft delete.');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info("Soft-deleted {$count} expired notifications.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Auth\PasswordResetCleanupService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CleanupPasswordResetsCommand extends Command
|
||||
{
|
||||
protected $signature = 'cleanup:password-resets {--days=30}';
|
||||
protected $description = 'Delete password reset requests older than N days.';
|
||||
|
||||
public function handle(PasswordResetCleanupService $service): int
|
||||
{
|
||||
$days = (int) $this->option('days');
|
||||
$count = $service->cleanup($days > 0 ? $days : 30);
|
||||
|
||||
$this->info("Deleted {$count} old password reset request(s).");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\System\ConfigUpdateService;
|
||||
use DateTimeZone;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ConfigUpdateCommand extends Command
|
||||
{
|
||||
protected $signature = 'config:update {task?} {--task=} {--dry} {--force} {--tz=}';
|
||||
protected $description = 'Run a configuration update task (weekly cron).';
|
||||
|
||||
public function handle(ConfigUpdateService $service): int
|
||||
{
|
||||
$task = (string) ($this->option('task') ?: $this->argument('task') ?: '');
|
||||
$dry = (bool) $this->option('dry');
|
||||
$force = (bool) $this->option('force');
|
||||
$tzName = (string) ($this->option('tz') ?: (config('School')->attendance['timezone'] ?? 'UTC'));
|
||||
$tz = new DateTimeZone($tzName);
|
||||
|
||||
if ($task === '' || !in_array($task, $service->availableTasks(), true)) {
|
||||
$this->error('Invalid or missing --task. Available: ' . implode(', ', $service->availableTasks()));
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$result = $service->runTask($task, $dry, $force, $tz);
|
||||
if (!$result['ok']) {
|
||||
$this->error($result['message'] ?? "Task '{$task}' failed.");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->info("Task '{$task}' finished successfully." . ($dry ? ' [DRY RUN]' : ''));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Users\InactiveUserCleanupService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeleteInactiveUsersCommand extends Command
|
||||
{
|
||||
protected $signature = 'users:delete-inactive-users {--minutes=15}';
|
||||
protected $description = 'Delete users that are inactive and created more than N minutes ago.';
|
||||
|
||||
public function handle(InactiveUserCleanupService $service): int
|
||||
{
|
||||
$minutes = (int) $this->option('minutes');
|
||||
$result = $service->cleanup($minutes > 0 ? $minutes : 15);
|
||||
|
||||
$this->info(sprintf(
|
||||
'Deleted %d inactive users, %d parents rows, %d orphaned user_roles.',
|
||||
$result['deleted_users'],
|
||||
$result['deleted_parents'],
|
||||
$result['deleted_roles']
|
||||
));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Attendance\AttendanceSummaryRebuildService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RecalculateAttendanceCommand extends Command
|
||||
{
|
||||
protected $signature = 'attendance:recalculate-summary';
|
||||
protected $description = 'Recalculates the attendance summary records from raw attendance data.';
|
||||
|
||||
public function handle(AttendanceSummaryRebuildService $service): int
|
||||
{
|
||||
$result = $service->rebuild();
|
||||
$this->info('Attendance summary recalculation finished. Inserted: ' . $result['inserted']);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Attendance\AttendanceDailySummaryService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendAbsenteesSummaryCommand extends Command
|
||||
{
|
||||
protected $signature = 'attendance:absentees-summary';
|
||||
protected $description = 'Sends daily attendance summaries for absences to parents.';
|
||||
|
||||
public function handle(AttendanceDailySummaryService $service): int
|
||||
{
|
||||
$sent = $service->sendAbsenteesSummary();
|
||||
$this->info("Attendance absentees summary completed. Sent: {$sent}.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Attendance\AttendanceDailySummaryService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendLatesSummaryCommand extends Command
|
||||
{
|
||||
protected $signature = 'attendance:lates-summary';
|
||||
protected $description = 'Sends daily attendance summaries for lates to parents.';
|
||||
|
||||
public function handle(AttendanceDailySummaryService $service): int
|
||||
{
|
||||
$sent = $service->sendLatesSummary();
|
||||
$this->info("Attendance lates summary completed. Sent: {$sent}.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\Payments\PaymentNotificationService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendMonthlyPaymentNotificationsCommand extends Command
|
||||
{
|
||||
protected $signature = 'payments:monthly-reminder {--force} {--email=} {--type=}';
|
||||
protected $description = 'Send monthly payment reminders on the first Saturday of every month.';
|
||||
|
||||
public function handle(PaymentNotificationService $service): int
|
||||
{
|
||||
$force = (bool) $this->option('force');
|
||||
$email = (string) ($this->option('email') ?? '');
|
||||
$type = (string) ($this->option('type') ?? '');
|
||||
|
||||
if ($email !== '') {
|
||||
$user = User::query()->where('email', $email)->first();
|
||||
if (!$user) {
|
||||
$this->error('Invalid --email provided.');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$result = $service->send([
|
||||
'parent_id' => (int) $user->id,
|
||||
'type' => $type,
|
||||
'force' => true,
|
||||
]);
|
||||
|
||||
$this->info('Test reminder sent for ' . $email . '.');
|
||||
return $result['failed'] > 0 ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
|
||||
if (!$force && !$this->isFirstSaturday(now())) {
|
||||
$this->info('Not the first Saturday of the month. Use --force to override.');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$result = $service->send([
|
||||
'type' => $type,
|
||||
'force' => $force,
|
||||
]);
|
||||
|
||||
$this->info(sprintf('Done. Sent: %d, Skipped: %d, Failed: %d.', $result['sent'], $result['skipped'], $result['failed']));
|
||||
|
||||
return $result['failed'] > 0 ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
|
||||
private function isFirstSaturday(\DateTimeInterface $dt): bool
|
||||
{
|
||||
return (int) $dt->format('w') === 6 && (int) $dt->format('j') <= 7;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Payments\PaymentTestNotificationService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendTestPaymentNotificationCommand extends Command
|
||||
{
|
||||
protected $signature = 'payments:send-test {--email=} {--type=no_payment}';
|
||||
protected $description = 'Send a single test non-payment or installment reminder to a specific email.';
|
||||
|
||||
public function handle(PaymentTestNotificationService $service): int
|
||||
{
|
||||
$email = (string) $this->option('email');
|
||||
$type = (string) $this->option('type');
|
||||
|
||||
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->error('Usage: php artisan payments:send-test --email=addr@example.com [--type=no_payment|installment]');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$result = $service->send($email, $type);
|
||||
if (!$result['ok']) {
|
||||
$this->error('Failed to send test reminder to ' . $email . '.');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->info('Sent test reminder to ' . $email . '.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Payments\PaypalPaymentSyncService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SyncPaypalPaymentsCommand extends Command
|
||||
{
|
||||
protected $signature = 'payments:sync-paypal {--dry-run} {--report-only}';
|
||||
protected $description = 'Sync PayPal payments to internal payments table from paypal_payments.';
|
||||
|
||||
public function handle(PaypalPaymentSyncService $service): int
|
||||
{
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
$reportOnly = (bool) $this->option('report-only');
|
||||
|
||||
$result = $service->sync($dryRun, $reportOnly);
|
||||
|
||||
$this->info(sprintf('[%s] %d PayPal payments processed.', $result['mode'], $result['processed']));
|
||||
if (!empty($result['failed'])) {
|
||||
$this->error(sprintf('[%s] Failed transactions: %s', $result['mode'], implode(', ', $result['failed'])));
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user