recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
@@ -0,0 +1,150 @@
<?php
namespace App\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use App\Models\ConfigurationModel;
use App\Models\InvoiceModel;
use App\Models\PaymentNotificationLogModel;
use App\Models\UserModel;
use App\Models\FamilyGuardianModel;
use App\Services\EmailService;
class SendTestPaymentNotification extends BaseCommand
{
protected $group = 'Payments';
protected $name = 'payments:send-test';
protected $description = 'Send a single test non-payment or installment reminder to a specific email.';
public function run(array $params)
{
$email = CLI::getOption('email');
$type = CLI::getOption('type') ?? 'no_payment';
if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
CLI::write('Usage: php spark payments:send-test --email=addr@example.com [--type=no_payment|installment]', 'yellow');
return;
}
$config = new ConfigurationModel();
$user = new UserModel();
$invoice= new InvoiceModel();
$logs = new PaymentNotificationLogModel();
$fam = new FamilyGuardianModel();
$mailer = new EmailService();
$tzName = (string) (config('School')->attendance['timezone'] ?? 'UTC');
$tz = new \DateTimeZone($tzName);
$now = new \DateTime('now', $tz);
$year = (int)$now->format('Y');
$month= (int)$now->format('n');
$schoolYear = (string) ($config->getConfig('school_year') ?? $year);
$userRow = $user->where('email', $email)->first();
$parentId = (int)($userRow['id'] ?? 0);
$invRows = $parentId ? $invoice->getAllInvoicesByUserIds([$parentId], $schoolYear) : [];
$totalBalance = 0.0; $latestInvoiceId = null;
foreach ($invRows as $ir) {
$totalBalance += (float)($ir['balance'] ?? 0);
if ($latestInvoiceId === null) $latestInvoiceId = (int)($ir['id'] ?? 0);
}
// Secondary guardian if available
$ccEmail = null;
if ($parentId) {
$row = $fam->where('user_id', $parentId)->first();
if ($row && !empty($row['family_id'])) {
$others = $fam->where('family_id', (int)$row['family_id'])
->where('user_id !=', $parentId)
->where('receive_emails', 1)->findAll();
foreach ($others as $g) {
$ccEmail = $user->select('email')->find((int)$g['user_id'])['email'] ?? null;
if ($ccEmail && filter_var($ccEmail, FILTER_VALIDATE_EMAIL)) break;
$ccEmail = null;
}
}
}
// Compose body (reuse layout)
$parentName = trim(($userRow['firstname'] ?? '') . ' ' . ($userRow['lastname'] ?? ''));
$monthYear = $now->format('F Y');
$subject = sprintf('Monthly Tuition Reminder — %s', $schoolYear);
$greeting = $parentName !== '' ? "Dear {$parentName}," : 'Dear Parent,';
$balanceFmt = '$' . number_format($totalBalance, 2);
$intro = ($type === 'no_payment')
? "We noticed there are outstanding tuition charges for {$schoolYear} but no payment has been recorded yet."
: "This is your monthly installment reminder for {$schoolYear}.";
$bodyHtml = <<<HTML
<div style="font-family:Arial,Helvetica,sans-serif; font-size:14px; color:#212529; line-height:1.5;">
<h2 style="margin:0 0 10px; font-size:18px;">Monthly Tuition Reminder (Test)</h2>
<p>{$greeting}</p>
<p>{$intro}</p>
<p><strong>Current Outstanding Balance:</strong> {$balanceFmt}</p>
<p>
As a friendly reminder, our tuition installments are due at the beginning of each month.
You can review your invoice and payment options by logging in to your parent portal.
</p>
<p>Thank you for your prompt attention.</p>
<p><em>Reminder Period:</em> {$monthYear}</p>
</div>
HTML;
// Compute remaining and max installments similar to Manual Payment UI
$installmentEndRaw = (string)$config->getConfig('installment_date');
$tz = new \DateTimeZone($tzName);
$today = new \DateTimeImmutable('today', $tz);
$end = null; try { if ($installmentEndRaw) { $end = new \DateTimeImmutable($installmentEndRaw, $tz); } } catch (\Throwable $e) { $end = null; }
$remMonths = 0;
if ($end) {
$y = (int)$end->format('Y') - (int)$today->format('Y');
$m = (int)$end->format('n') - (int)$today->format('n');
$remMonths = $y * 12 + $m;
if ((int)$end->format('j') > (int)$today->format('j')) $remMonths += 1;
if ($remMonths < 0) $remMonths = 0;
}
$remainingInst = max(1, $remMonths);
$maxInst = max(($remMonths ?: 0), ($totalBalance > 0 ? 2 : 0));
$instDueFmt = '$' . number_format($remainingInst > 0 ? ($totalBalance / $remainingInst) : 0, 2);
$bodyHtml .= "<ul>"
. "<li><strong>Remaining installments:</strong> {$remainingInst}</li>"
. "<li><strong>Suggested installment this month:</strong> {$instDueFmt}</li>"
. "<li><strong>Maximum installments available:</strong> {$maxInst}</li>"
. "</ul>";
$body = view('emails/_wrap_layout', [ 'title' => 'Monthly Tuition Reminder', 'body_html' => $bodyHtml ], ['saveData' => true]);
$ok = $mailer->send($email, $subject, $body, 'finance');
if ($ok && $ccEmail && strcasecmp($ccEmail, $email) !== 0) {
$mailer->send($ccEmail, $subject, $body, 'finance');
}
$existing = $logs->where('parent_id', $parentId)
->where('period_year', $year)
->where('period_month', $month)
->where('type', $type)
->first();
$payload = [
'parent_id' => $parentId,
'invoice_id' => $latestInvoiceId,
'school_year' => $schoolYear,
'period_year' => $year,
'period_month' => $month,
'type' => $type,
'to_email' => $email,
'cc_email' => $ccEmail,
'head_fa_notified' => 0,
'subject' => $subject,
'body' => $body,
'status' => $ok ? 'sent' : 'failed',
'error_message' => $ok ? null : 'Email send failed (see logs)',
'balance_snapshot' => $totalBalance,
'sent_at' => $now->format('Y-m-d H:i:s'),
];
if ($existing) $logs->update($existing['id'], $payload); else $logs->insert($payload);
CLI::write(($ok ? 'Sent' : 'Failed') . " test reminder to {$email}", $ok ? 'green' : 'red');
}
}