This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\FormUpCountry;
|
||||
use App\Models\FormVehicleRunningCost;
|
||||
use App\Models\FormEntertaimentPresentation;
|
||||
use App\Models\FormMeetingSeminar;
|
||||
use App\Models\FormOthers;
|
||||
use App\Helpers\AuditTrailHelper;
|
||||
|
||||
class AutoRejectExpense extends Command
|
||||
{
|
||||
/**
|
||||
* Nama perintah yang akan dijalankan di terminal/cron
|
||||
*/
|
||||
protected $signature = 'expense:auto-reject';
|
||||
|
||||
/**
|
||||
* Deskripsi perintah
|
||||
*/
|
||||
protected $description = 'Otomatis reject expense yang melewati batas waktu final approve (Tgl 13)';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$closingDay = (int) env('CLOSING_DATE', 13);
|
||||
$startDay = (int) env('STARTING_DATE', 11);
|
||||
|
||||
// Hanya jalankan eksekusi reject pada H+1 dari Closing Day (Misal: Tanggal 14 jam 00:01)
|
||||
if ($now->day !== ($closingDay + 1)) {
|
||||
$this->info("Hari ini bukan jadwal auto-reject. Jadwal berikutnya: Tanggal " . ($closingDay + 1));
|
||||
return;
|
||||
}
|
||||
|
||||
// Batas maksimal tanggal expense yang harus direject adalah H-1 dari Start Day (Misal: Tanggal 10 bulan ini)
|
||||
// Karena tanggal 11 sudah masuk ke periode bulan depannya.
|
||||
$batasTanggalPeriodeLalu = Carbon::create($now->year, $now->month, $startDay)->subDay()->endOfDay();
|
||||
|
||||
$models = [
|
||||
FormUpCountry::class,
|
||||
FormVehicleRunningCost::class,
|
||||
FormEntertaimentPresentation::class,
|
||||
FormMeetingSeminar::class,
|
||||
FormOthers::class
|
||||
];
|
||||
|
||||
// Daftar status yang dianggap menggantung / belum selesai
|
||||
$pendingStatuses = ['On Progress', 'Approved 1', 'Approved 2', 'Waiting Approval', 'Validated'];
|
||||
|
||||
$totalRejected = 0;
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($models as $model) {
|
||||
// Tentukan kolom tanggal yang digunakan
|
||||
$dateColumn = ($model === FormMeetingSeminar::class) ? 'created_at' : 'tanggal';
|
||||
|
||||
// Ambil semua data yang masih menggantung dari periode lalu
|
||||
$pendingExpenses = $model::whereIn('status', $pendingStatuses)
|
||||
->where($dateColumn, '<=', $batasTanggalPeriodeLalu)
|
||||
->get();
|
||||
|
||||
foreach ($pendingExpenses as $expense) {
|
||||
// Update status dan berikan alasan penolakan sesuai instruksi core business
|
||||
$expense->update([
|
||||
'status' => 'Rejected',
|
||||
'remarks' => 'auto reject by system - lewat periode cut off',
|
||||
'rejected_at' => Carbon::now(),
|
||||
'rejected_by' => 0 // Diisi 0 sebagai flag penanda sistem otomatis
|
||||
]);
|
||||
|
||||
// Tambahkan ke Audit Trail
|
||||
$noExpense = $expense->expense_number ?? 'Unknown';
|
||||
AuditTrailHelper::AddAuditTrail('System Update', "Auto Reject by System for Expense ({$noExpense})");
|
||||
|
||||
$totalRejected++;
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
$msg = "Auto Reject berhasil dijalankan. Total direject: {$totalRejected} dokumen.";
|
||||
$this->info($msg);
|
||||
Log::info($msg);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
$this->error("Gagal menjalankan Auto Reject: " . $e->getMessage());
|
||||
Log::error("Cron Auto Reject Failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class MigrateOldExpenseData extends Command
|
||||
{
|
||||
// Perintah yang akan dieksekusi di terminal
|
||||
protected $signature = 'expense:migrate-old';
|
||||
protected $description = 'Migrasi data transaksional paling update dari expense-old ke database expense (v2.0) secara aman';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Memulai Proses Sinkronisasi: Memindahkan data update dari [expense-old] ke [expense]...');
|
||||
|
||||
// Daftar tabel yang akan disinkronkan (Nama tabel diasumsikan sama pada kedua versi)
|
||||
$tablesToMigrate = [
|
||||
'form_up_country' => 'form_up_country',
|
||||
'form_vehicle_running_cost' => 'form_vehicle_running_cost',
|
||||
'form_entertainment_presentation' => 'form_entertainment_presentation',
|
||||
'form_meeting_seminar' => 'form_meeting_seminar',
|
||||
'form_others' => 'form_others',
|
||||
];
|
||||
|
||||
foreach ($tablesToMigrate as $oldTable => $newTable) {
|
||||
$this->comment("--------------------------------------------------");
|
||||
$this->comment("Memproses Tabel Target: {$newTable}");
|
||||
|
||||
// 1. Validasi eksistensi tabel di database tujuan (expense)
|
||||
if (!Schema::hasTable($newTable)) {
|
||||
$this->error("Tabel [{$newTable}] tidak ditemukan di database 'expense'. Melewati tabel ini.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Ambil list kolom lengkap yang ada di database tujuan (expense)
|
||||
$destinationColumns = Schema::getColumnListing($newTable);
|
||||
|
||||
// 3. Ambil data transaksional paling update dari database lama (expense-old)
|
||||
$oldData = DB::connection('mysql_lama')->table($oldTable)->get();
|
||||
|
||||
$insertedCount = 0;
|
||||
$skippedCount = 0;
|
||||
|
||||
foreach ($oldData as $row) {
|
||||
// 4. PROTEKSI ANTI-OVERWRITE: Cek apakah data sudah ada di database 'expense'
|
||||
$isExists = DB::table($newTable)
|
||||
->where('expense_number', $row->expense_number)
|
||||
->exists();
|
||||
|
||||
if (!$isExists) {
|
||||
$insertData = [];
|
||||
|
||||
// 5. MAPPING DATA SECARA DINAMIS
|
||||
foreach ($destinationColumns as $column) {
|
||||
// KUNCI PENYEMPURNAAN: Jangan bawa ID lama agar tidak tabrakan dengan Auto-Increment di DB baru
|
||||
if ($column === 'id') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Jika kolom dari database baru tersedia di data 'expense-old', ambil nilainya
|
||||
if (property_exists($row, $column) && !is_null($row->$column)) {
|
||||
$insertData[$column] = $row->$column;
|
||||
} else {
|
||||
// Jika kolom baru versi 2.0 tidak ada di database lama / bernilai NULL, isi dengan nilai default aman
|
||||
$insertData[$column] = $this->getDefaultValueForColumn($newTable, $column);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Jalankan amunisi Insert ke database utama (expense)
|
||||
DB::table($newTable)->insert($insertData);
|
||||
$insertedCount++;
|
||||
} else {
|
||||
$skippedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Tabel [{$newTable}] Selesai: Masuk {$insertedCount} data baru. Diabaikan (Sudah ada) {$skippedCount} data.");
|
||||
}
|
||||
|
||||
$this->info('==================================================');
|
||||
$this->info('Sukses! Seluruh data dari [expense-old] telah bermigrasi ke [expense] dengan aman.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback Nilai Default untuk Kolom Baru Struktur Aplikasi 2.0
|
||||
*/
|
||||
private function getDefaultValueForColumn($tableName, $columnName)
|
||||
{
|
||||
// POINT 1: Solusi otomatisasi penanganan cabang_id yang NULL pada tabel entertainment
|
||||
if ($tableName === 'form_entertainment_presentation' && $columnName === 'cabang_id') {
|
||||
return 1; // Ganti angka 1 dengan ID Cabang default existing Anda
|
||||
}
|
||||
|
||||
// Pengondisian khusus untuk kolom-kolom baru krusial di versi 2.0 Anda
|
||||
if ($columnName === 'account_number') {
|
||||
return '-';
|
||||
}
|
||||
if ($columnName === 'approved_total') {
|
||||
return 0;
|
||||
}
|
||||
if ($columnName === 'approved_at' || $columnName === 'approved2_at' || $columnName === 'final_approved_at') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Nilai bawaan jika tidak masuk kategori spesifik di atas
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user