233 lines
8.8 KiB
PHP
233 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace App\Commands;
|
|
|
|
use CodeIgniter\CLI\BaseCommand;
|
|
use CodeIgniter\CLI\CLI;
|
|
use App\Models\PayPalPaymentModel;
|
|
use App\Models\PaymentModel;
|
|
use App\Models\UserModel;
|
|
use App\Models\ConfigurationModel;
|
|
use App\Models\InvoiceModel;
|
|
use App\Models\StudentModel;
|
|
use App\Models\EnrollmentModel;
|
|
|
|
class SyncPaypalPayments extends BaseCommand
|
|
{
|
|
protected $group = 'Payments';
|
|
protected $name = 'payments:sync-paypal';
|
|
protected $description = 'Sync PayPal payments to internal payments table from paypal_payments';
|
|
protected $configModel;
|
|
protected $semester;
|
|
protected $schoolYear;
|
|
protected $paypalModel;
|
|
protected $paymentModel;
|
|
protected $userModel;
|
|
protected $invoiceModel;
|
|
protected $studentModel;
|
|
protected $enrollmentModel;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->configModel = new ConfigurationModel();
|
|
$this->paypalModel = new PayPalPaymentModel();
|
|
$this->paymentModel = new PaymentModel();
|
|
$this->userModel = new UserModel();
|
|
$this->invoiceModel = new InvoiceModel();
|
|
$this->studentModel = new StudentModel();
|
|
$this->enrollmentModel = new EnrollmentModel();
|
|
|
|
$this->semester = $this->configModel->getConfig('semester');
|
|
$this->schoolYear = $this->configModel->getConfig('school_year');
|
|
}
|
|
|
|
public function run(array $params)
|
|
{
|
|
$dryRun = CLI::getOption('dry-run');
|
|
$reportOnly = CLI::getOption('report-only');
|
|
$mode = $reportOnly ? 'REPORT-ONLY' : ($dryRun ? 'DRY-RUN' : 'LIVE');
|
|
|
|
$paypalEntries = $this->paypalModel
|
|
->where('status', 'COMPLETED')
|
|
->where('synced', 0)
|
|
->where('sync_attempts <', 3)
|
|
->where('transaction_id IS NOT NULL')
|
|
->findAll();
|
|
|
|
$syncedCount = 0;
|
|
$failed = [];
|
|
|
|
foreach ($paypalEntries as $entry) {
|
|
$parentId = null;
|
|
$invoiceId = 0;
|
|
|
|
$users = $this->userModel->getUsersBySchoolId($entry['parent_school_id']);
|
|
$user = $users[0] ?? null;
|
|
|
|
// Always increment sync_attempts unless report-only
|
|
if (!$reportOnly) {
|
|
$this->paypalModel->update($entry['id'], [
|
|
'sync_attempts' => $entry['sync_attempts'] + 1
|
|
]);
|
|
}
|
|
|
|
if ($user) {
|
|
$parentId = $user['id'];
|
|
|
|
$invoice = $this->invoiceModel->getInvoicesByParentId($parentId, $this->schoolYear);
|
|
|
|
if (!$reportOnly && !$dryRun) {
|
|
if ($invoice) {
|
|
$invoiceId = $invoice['id'];
|
|
|
|
$success = $this->processPayment(
|
|
$invoiceId,
|
|
$entry['amount'],
|
|
'PayPal',
|
|
null,
|
|
$entry['transaction_id'],
|
|
date('Y-m-d', strtotime($entry['created_at'])),
|
|
$this->schoolYear,
|
|
$this->semester
|
|
);
|
|
|
|
if (!$success) {
|
|
$failed[] = $entry['transaction_id'];
|
|
continue;
|
|
}
|
|
} else {
|
|
$this->paymentModel->insert([
|
|
'parent_id' => $parentId,
|
|
'invoice_id' => 0,
|
|
'total_amount' => $entry['amount'],
|
|
'paid_amount' => $entry['amount'],
|
|
'balance' => 0.00,
|
|
'number_of_installments' => 1,
|
|
'transaction_id' => $entry['transaction_id'],
|
|
'payment_method' => 'PayPal',
|
|
'payment_date' => date('Y-m-d', strtotime($entry['created_at'])),
|
|
'school_year' => $this->schoolYear,
|
|
'semester' => $this->semester,
|
|
'status' => 'Completed',
|
|
'updated_by' => null,
|
|
]);
|
|
}
|
|
|
|
// Mark as synced only in LIVE mode
|
|
$this->paypalModel->update($entry['id'], ['synced' => 1]);
|
|
}
|
|
|
|
$syncedCount++;
|
|
} else {
|
|
log_message('error', "[PAYPAL SYNC FAILED] No user found for parent_school_id: {$entry['parent_school_id']}");
|
|
$failed[] = $entry['transaction_id'];
|
|
}
|
|
}
|
|
|
|
// === Logging ===
|
|
log_message('info', "[$mode] PAYPAL SYNC: $syncedCount processed.");
|
|
if (!empty($failed)) {
|
|
log_message('error', "[$mode] PAYPAL SYNC Failed: " . implode(', ', $failed));
|
|
}
|
|
|
|
// === CLI Output ===
|
|
CLI::write("[$mode] $syncedCount PayPal payments processed.", 'green');
|
|
if (!empty($failed)) {
|
|
CLI::error("[$mode] Failed transactions: " . implode(', ', $failed));
|
|
}
|
|
|
|
// === Email Report: Only if there's any update ===
|
|
if ($syncedCount > 0 || !empty($failed)) {
|
|
helper('email');
|
|
$email = \Config\Services::email();
|
|
$email->setTo('support@alrahmaisgl.org');
|
|
$email->setFrom('no-parentsreply@alrahmaisgl.org', 'PayPal Sync Report');
|
|
$email->setSubject("[$mode] PayPal Sync Report - " . date('Y-m-d H:i'));
|
|
|
|
$body = "PayPal Sync Mode: $mode\n\n";
|
|
$body .= "$syncedCount PayPal payments processed.\n\n";
|
|
|
|
if (!empty($failed)) {
|
|
$body .= count($failed) . " failed transactions:\n";
|
|
$body .= implode("\n", $failed);
|
|
} else {
|
|
$body .= "No failed transactions.\n";
|
|
}
|
|
|
|
$email->setMessage(nl2br($body));
|
|
|
|
if ($email->send()) {
|
|
CLI::write("[$mode] Email report sent successfully.", 'yellow');
|
|
} else {
|
|
CLI::error("[$mode] Failed to send email report.");
|
|
log_message('error', 'Email send error: ' . $email->printDebugger(['headers']));
|
|
}
|
|
} else {
|
|
log_message('info', "[$mode] No PayPal sync updates. Email not sent.");
|
|
CLI::write("[$mode] No changes to report. Email not sent.", 'blue');
|
|
}
|
|
}
|
|
|
|
private function processPayment($invoiceId, $amount, $paymentMethod, $checkFile = null, $transactionId = null, $paymentDate = null, $schoolYear = null, $semester = null)
|
|
{
|
|
$invoice = $this->invoiceModel->find($invoiceId);
|
|
if (!$invoice) {
|
|
return false;
|
|
}
|
|
|
|
$transactionId = $transactionId ?? 'INV-' . $invoiceId . '-' . time();
|
|
$paymentDate = $paymentDate ?? date('Y-m-d');
|
|
|
|
$newPaid = $invoice['paid_amount'] + $amount;
|
|
$newBalance = $invoice['balance'] - $amount;
|
|
|
|
$invoiceUpdateData = [
|
|
'paid_amount' => $newPaid,
|
|
'balance' => $newBalance,
|
|
'status' => ($newBalance <= 0) ? 'Paid' : $invoice['status'],
|
|
];
|
|
|
|
if (!$this->invoiceModel->update($invoiceId, $invoiceUpdateData)) {
|
|
return false;
|
|
}
|
|
|
|
$this->paymentModel->insert([
|
|
'parent_id' => $invoice['parent_id'],
|
|
'invoice_id' => $invoiceId,
|
|
'total_amount' => $invoice['total_amount'],
|
|
'paid_amount' => $amount,
|
|
'balance' => $newBalance,
|
|
'number_of_installments' => 1,
|
|
'transaction_id' => $transactionId,
|
|
'payment_method' => $paymentMethod,
|
|
'payment_date' => $paymentDate,
|
|
'status' => ($newBalance <= 0) ? 'Full' : 'Partial',
|
|
'check_file' => $checkFile,
|
|
'updated_by' => null, // Avoid using session()->get() in CLI
|
|
'school_year' => $schoolYear,
|
|
'semester' => $semester
|
|
]);
|
|
|
|
$this->updateEnrollmentStatusIfPaid($invoiceId, $schoolYear);
|
|
return true;
|
|
}
|
|
|
|
private function updateEnrollmentStatusIfPaid($invoiceId, $schoolYear)
|
|
{
|
|
$invoice = $this->invoiceModel->find($invoiceId);
|
|
if (!$invoice || $invoice['balance'] > 0) {
|
|
return;
|
|
}
|
|
|
|
$students = $this->studentModel->where('parent_id', $invoice['parent_id'])
|
|
->where('school_year', $schoolYear)
|
|
->findAll();
|
|
|
|
foreach ($students as $student) {
|
|
$this->enrollmentModel->set(['enrollment_status' => 'enrolled'])
|
|
->where('student_id', $student['id'])
|
|
->update();
|
|
}
|
|
}
|
|
}
|