add controllers, servoices
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\ExtraCharges;
|
||||
|
||||
use App\Models\AdditionalCharge;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ExtraChargesChargeService
|
||||
{
|
||||
public function __construct(
|
||||
private ExtraChargesContextService $context,
|
||||
private ExtraChargesInvoiceService $invoiceService
|
||||
) {}
|
||||
|
||||
public function createCharge(array $payload): array
|
||||
{
|
||||
$invoiceId = $payload['invoice_id'] ?? null;
|
||||
$chargeType = $payload['charge_type'];
|
||||
$amountAbs = round(abs((float) $payload['amount']), 2);
|
||||
$signedAmount = $chargeType === 'add' ? $amountAbs : -$amountAbs;
|
||||
$schoolYear = $payload['school_year'] ?? $this->context->getSchoolYear();
|
||||
$semester = $payload['semester'] ?? $this->context->getSemester();
|
||||
|
||||
$data = [
|
||||
'parent_id' => (int) $payload['parent_id'],
|
||||
'invoice_id' => $invoiceId,
|
||||
'school_year' => $schoolYear,
|
||||
'semester' => $semester,
|
||||
'charge_type' => $chargeType,
|
||||
'title' => trim($payload['title']),
|
||||
'description' => trim((string) ($payload['description'] ?? '')),
|
||||
'amount' => $signedAmount,
|
||||
'due_date' => $payload['due_date'] ?? null,
|
||||
'status' => $invoiceId ? 'applied' : 'pending',
|
||||
'created_by' => (int) ($payload['created_by'] ?? 0),
|
||||
'created_at' => $payload['created_at'] ?? $this->utcNow(),
|
||||
];
|
||||
|
||||
$invoiceBefore = $invoiceId ? Invoice::getInvoicesByParentId((int) $payload['parent_id'], $schoolYear) : [];
|
||||
$invoiceAfter = [];
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$chargeId = AdditionalCharge::query()->create($data)->id;
|
||||
|
||||
if ($invoiceId) {
|
||||
$this->invoiceService->applyToInvoice((int) $invoiceId, $chargeType, $amountAbs);
|
||||
}
|
||||
|
||||
$invoiceAfter = $invoiceId ? Invoice::getInvoicesByParentId((int) $payload['parent_id'], $schoolYear) : [];
|
||||
|
||||
DB::commit();
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Extra charge create failed: ' . $e->getMessage());
|
||||
return ['ok' => false, 'message' => 'Failed to save charge.'];
|
||||
}
|
||||
|
||||
$parentUser = User::getUserInfoById((int) $payload['parent_id']);
|
||||
$this->triggerExtraChargeEvent($parentUser, $data, (int) $chargeId, $invoiceId, $invoiceBefore, $invoiceAfter);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'id' => (int) $chargeId,
|
||||
'invoice_id' => $invoiceId ? (int) $invoiceId : null,
|
||||
'parent_id' => (int) $payload['parent_id'],
|
||||
];
|
||||
}
|
||||
|
||||
public function updateCharge(AdditionalCharge $charge, array $payload): bool
|
||||
{
|
||||
$chargeType = $payload['charge_type'] ?? $charge->charge_type ?? 'add';
|
||||
$amountRaw = isset($payload['amount']) ? (float) $payload['amount'] : (float) $charge->amount;
|
||||
$newAmount = $chargeType === 'add' ? abs($amountRaw) : -abs($amountRaw);
|
||||
$delta = $newAmount - (float) $charge->amount;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$charge->title = trim($payload['title'] ?? $charge->title);
|
||||
$charge->description = trim($payload['description'] ?? $charge->description);
|
||||
$charge->amount = $newAmount;
|
||||
$charge->due_date = $payload['due_date'] ?? $charge->due_date;
|
||||
$charge->charge_type = $chargeType;
|
||||
if (array_key_exists('updated_by', $payload)) {
|
||||
$charge->updated_by = $payload['updated_by'];
|
||||
}
|
||||
$charge->save();
|
||||
|
||||
if ($charge->status === 'applied' && !empty($charge->invoice_id) && abs($delta) > 0.00001) {
|
||||
$this->invoiceService->adjustDelta((int) $charge->invoice_id, $delta);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Extra charge update failed: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function voidCharge(AdditionalCharge $charge): bool
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$invoiceId = (int) ($charge->invoice_id ?? 0);
|
||||
$amountAbs = round(abs((float) ($charge->amount ?? 0)), 2);
|
||||
$chargeType = (string) ($charge->charge_type ?? 'add');
|
||||
$status = (string) ($charge->status ?? 'pending');
|
||||
|
||||
if ($status === 'applied' && $invoiceId > 0 && $amountAbs > 0) {
|
||||
$this->invoiceService->reverseOnInvoice($invoiceId, $chargeType, $amountAbs);
|
||||
}
|
||||
|
||||
$charge->status = 'void';
|
||||
$charge->save();
|
||||
|
||||
DB::commit();
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Extra charge void failed: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function reverseCharge(AdditionalCharge $charge): bool
|
||||
{
|
||||
$invoiceId = (int) ($charge->invoice_id ?? 0);
|
||||
$amountAbs = round(abs((float) ($charge->amount ?? 0)), 2);
|
||||
$chargeType = (string) ($charge->charge_type ?? 'add');
|
||||
$status = (string) ($charge->status ?? 'pending');
|
||||
|
||||
if ($status !== 'applied' || $invoiceId <= 0 || $amountAbs <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$this->invoiceService->reverseOnInvoice($invoiceId, $chargeType, $amountAbs);
|
||||
|
||||
$charge->status = 'pending';
|
||||
$charge->invoice_id = null;
|
||||
$charge->save();
|
||||
|
||||
DB::commit();
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Extra charge reverse failed: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function triggerExtraChargeEvent(?array $parentUser, array $payload, int $chargeId, ?int $invoiceId, array $invoiceBefore, array $invoiceAfter): void
|
||||
{
|
||||
if (!$parentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $parentUser['first_name'] ?? $parentUser['firstname'] ?? '';
|
||||
$last = $parentUser['last_name'] ?? $parentUser['lastname'] ?? '';
|
||||
$parentName = trim($first . ' ' . $last);
|
||||
|
||||
$before = $this->normalizeRow($invoiceBefore);
|
||||
$after = $this->normalizeRow($invoiceAfter);
|
||||
|
||||
$invoiceTotal = $after ? (float) ($after['total_amount'] ?? 0) : 0.0;
|
||||
$preBalance = $before ? (float) ($before['balance'] ?? 0) : 0.0;
|
||||
$postBalance = $after ? (float) ($after['balance'] ?? 0) : 0.0;
|
||||
|
||||
$eventData = [
|
||||
'user_id' => (int) ($parentUser['id'] ?? 0),
|
||||
'email' => $parentUser['email'] ?? null,
|
||||
'firstname' => $first,
|
||||
'lastname' => $last,
|
||||
'parentName' => $parentName,
|
||||
'school_year' => $payload['school_year'],
|
||||
'semester' => $payload['semester'],
|
||||
'invoice_id' => $invoiceId,
|
||||
'invoice_number' => $after['invoice_number'] ?? ($invoiceId ? 'INV-' . $invoiceId : null),
|
||||
'charge_id' => $chargeId,
|
||||
'charge_title' => $payload['title'],
|
||||
'charge_desc' => $payload['description'],
|
||||
'charge_type' => $payload['charge_type'],
|
||||
'amount_signed' => $payload['amount'],
|
||||
'amount_abs' => abs((float) $payload['amount']),
|
||||
'due_date' => $payload['due_date'],
|
||||
'created_at' => $payload['created_at'],
|
||||
'pre_balance' => $preBalance,
|
||||
'post_balance' => $postBalance,
|
||||
'invoice_total' => $invoiceTotal,
|
||||
'portal_link' => url('/login'),
|
||||
'invoice_link' => $invoiceId ? url('parent/invoices/view/' . $invoiceId) : url('parent/invoices'),
|
||||
];
|
||||
|
||||
$students = [];
|
||||
|
||||
try {
|
||||
if (class_exists(\CodeIgniter\Events\Events::class)) {
|
||||
\CodeIgniter\Events\Events::trigger('extraCharge', $eventData, $students);
|
||||
} elseif (function_exists('event')) {
|
||||
event('extraCharge', [$eventData, $students]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('extraCharge event failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeRow($row): ?array
|
||||
{
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
if (is_array($row) && isset($row[0]) && is_array($row[0])) {
|
||||
return $row[0];
|
||||
}
|
||||
return is_array($row) ? $row : null;
|
||||
}
|
||||
|
||||
private function utcNow(): string
|
||||
{
|
||||
if (function_exists('utc_now')) {
|
||||
return (string) utc_now();
|
||||
}
|
||||
return now('UTC')->toDateTimeString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user