This commit is contained in:
Saufi
2026-06-03 08:51:22 +08:00
commit a14d43fe34
347 changed files with 38197 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Services\Finance;
use App\Models\BankVerification;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class BankVerificationService
{
/**
* @param array{status: string, finance_note?: string|null} $data
*/
public function update(BankVerification $verification, User $actor, array $data): BankVerification
{
return DB::transaction(function () use ($verification, $actor, $data): BankVerification {
$before = $verification->only(['status', 'finance_note', 'verified_by_user_id', 'verified_at']);
$verification->forceFill([
'status' => $data['status'],
'finance_note' => $data['finance_note'] ?? null,
'verified_by_user_id' => $actor->id,
'verified_at' => now(),
])->save();
activity('finance')
->performedOn($verification)
->causedBy($actor)
->withProperties([
'before' => $before,
'after' => $verification->only(['status', 'finance_note', 'verified_by_user_id', 'verified_at']),
'application_id' => $verification->application_id,
])
->log('bank_verification_updated');
return $verification;
});
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Services\Finance;
use App\Exports\FinanceVerificationExport;
use App\Models\Application;
use App\Models\ExportLog;
use App\Models\User;
use Illuminate\Support\Facades\Storage;
class FinanceExportService
{
public function __construct(
private readonly FinanceVerificationQuery $verificationQuery,
private readonly FinanceVerificationExport $export,
) {}
/**
* @param array<string, mixed> $filters
*/
public function export(User $actor, array $filters): ExportLog
{
$verifications = $this->verificationQuery->build($filters)->get();
$fileName = 'finance-verification-'.now()->format('Ymd-His').'.xlsx';
$path = 'exports/finance/'.$fileName;
Storage::disk('local')->makeDirectory('exports/finance');
$absolutePath = Storage::disk('local')->path($path);
$this->export->write($verifications, $absolutePath);
$firstApplication = $verifications->first()?->application;
$electionId = $firstApplication instanceof Application ? $firstApplication->election_id : null;
$log = ExportLog::query()->create([
'election_id' => $electionId,
'generated_by_user_id' => $actor->id,
'generated_at' => now(),
'report_type' => 'finance_verification_list',
'filter_parameters' => $filters,
'file_name' => $fileName,
'disk' => 'local',
'path' => $path,
]);
activity('exports')
->performedOn($log)
->causedBy($actor)
->withProperties([
'report_type' => 'finance_verification_list',
'filters' => $filters,
'row_count' => $verifications->count(),
])
->log('finance_verification_export_generated');
return $log;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Services\Finance;
use App\Models\BankVerification;
use Illuminate\Database\Eloquent\Builder;
class FinanceVerificationQuery
{
/**
* @param array<string, mixed> $filters
* @return Builder<BankVerification>
*/
public function build(array $filters = []): Builder
{
return BankVerification::query()
->with([
'application.election',
'application.pusatMengundi',
'application.approvedPosition',
'application.requestedPosition',
'application.documents',
'application.staffAssignments.position',
'verifiedBy',
])
->whereHas('application', function (Builder $query) use ($filters): void {
$query->whereIn('status', ['approved', 'assigned'])
->when(! empty($filters['pusat_mengundi_id']), fn (Builder $nested) => $nested->where('pusat_mengundi_id', $filters['pusat_mengundi_id']))
->when(! empty($filters['role_id']), function (Builder $nested) use ($filters): void {
$nested->where(function (Builder $roleQuery) use ($filters): void {
$roleQuery->where('approved_position_id', $filters['role_id'])
->orWhere(function (Builder $fallback) use ($filters): void {
$fallback->whereNull('approved_position_id')
->where('requested_position_id', $filters['role_id']);
});
});
})
->when(! empty($filters['missing_account_number']), function (Builder $nested): void {
$nested->where(function (Builder $accountQuery): void {
$accountQuery->whereNull('bank_account_number')
->orWhere('bank_account_number', '');
});
})
->when(! empty($filters['missing_bank_statement']), function (Builder $nested): void {
$nested->whereDoesntHave('documents', fn (Builder $documentQuery) => $documentQuery->where('document_type', 'bank_statement'));
})
->when(! empty($filters['q']), function (Builder $nested) use ($filters): void {
$term = '%'.$filters['q'].'%';
$nested->where(function (Builder $keywordQuery) use ($term): void {
$keywordQuery->where('name', 'like', $term)
->orWhere('ic_number', 'like', $term)
->orWhere('email', 'like', $term);
});
});
})
->when(! empty($filters['status']), fn (Builder $query) => $query->where('status', $filters['status']));
}
}