2024-12-18 12:56:16 +07:00
< ? php
namespace App\Http\Controllers\Forms ;
use Illuminate\Http\Request ;
use App\Http\Controllers\Controller ;
use App\Models\FormEntertaimentPresentation ;
use App\Models\Rayon ;
use Illuminate\Support\Facades\DB ;
use App\Models\Region ;
use App\Models\Cabang ;
use App\Models\UserHasCabang ;
use App\Models\Kategori ;
use App\Helpers\NextCloudHelper ;
use Illuminate\Support\Str ;
2024-12-24 15:12:24 +07:00
use Illuminate\Support\Facades\Mail ;
use App\Mail\ExpenseApproved ;
2025-01-16 15:56:22 +07:00
use App\Mail\ExpenseApproved2 ;
2024-12-24 15:12:24 +07:00
use App\Mail\ExpenseRejected ;
2024-12-26 13:49:00 +07:00
use App\Mail\FinalApprove ;
2025-01-09 13:22:56 +07:00
use App\Mail\ExpenseCreated ;
2024-12-25 13:37:49 +07:00
use App\Helpers\WhatsappHelper ;
2025-01-01 17:14:46 +07:00
use App\Helpers\AuditTrailHelper ;
2025-01-09 13:22:56 +07:00
use App\Models\Admin ;
2025-01-09 14:17:28 +07:00
use App\Helpers\BudgetHelper ;
2025-01-17 16:17:53 +07:00
use App\Helpers\NotificationHelper ;
2025-10-13 00:14:28 +07:00
use App\Helpers\OutstandingHelper ;
2025-01-23 17:46:38 +07:00
use Illuminate\Support\Facades\Log ;
2025-03-05 13:44:58 +07:00
use App\Services\BrevoService ;
2025-10-13 17:28:34 +07:00
use App\Services\AttachmentService ;
use App\Enums\AttachmentTableName ;
use App\Models\AttachmentForm ;
use Illuminate\Support\Facades\Validator ;
use Illuminate\Validation\ValidationException ;
2025-05-05 19:40:38 +07:00
use Carbon\Carbon ;
2024-12-18 12:56:16 +07:00
class FormEntertainmentPresentationController extends Controller
{
2025-10-13 17:28:34 +07:00
protected AttachmentService $attachmentService ;
/**
* Attachment categories for Entertainment & Presentation.
*
* @var array<string,string>
*/
protected array $attachmentCategories = [
'bukti_total' => 'Bukti Total' ,
];
/**
* Blocked extensions to avoid dangerous uploads.
*
* @var array<int,string>
*/
protected array $blockedExtensions = [ 'exe' , 'dll' , 'sh' , 'bat' , 'cmd' , 'msi' ];
/**
* Supported image extensions for inline preview.
*
* @var array<int,string>
*/
protected array $imageExtensions = [ 'jpg' , 'jpeg' , 'png' , 'gif' , 'bmp' , 'webp' ];
public function __construct ( AttachmentService $attachmentService )
{
$this -> attachmentService = $attachmentService ;
view () -> share ( 'entertainmentAttachmentCategories' , $this -> attachmentCategories );
view () -> share ( 'entertainmentAttachmentCategoryKeys' , array_keys ( $this -> attachmentCategories ));
view () -> share ( 'entertainmentBlockedExtensions' , $this -> blockedExtensions );
}
2024-12-18 12:56:16 +07:00
public function index ()
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.view' ]);
2024-12-23 10:10:09 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2025-05-05 19:40:38 +07:00
$startDay = ( int ) env ( 'STARTING_DATE' , 11 );
2026-06-01 15:01:03 +07:00
$closingDay = ( int ) env ( 'CLOSING_DATE' , 13 );
2025-10-13 00:14:28 +07:00
$userRegionData = auth () -> user () -> getMyCabangAndRegionId ();
$regionContextId = $userRegionData [ 'region' ] !== '-' ? $userRegionData [ 'region' ] : null ;
$cabangContextId = $userRegionData [ 'cabang' ] !== '-' ? $userRegionData [ 'cabang' ] : null ;
2025-05-05 19:40:38 +07:00
$now = Carbon :: now ();
2025-06-19 19:39:20 +07:00
// Determine the period
2025-05-05 19:40:38 +07:00
if ( $now -> day >= $startDay ) {
2025-06-19 19:39:20 +07:00
$currentPeriodStartDate = Carbon :: create ( $now -> year , $now -> month , $startDay ) -> startOfDay ();
$currentPeriodClosingDate = Carbon :: create ( $now -> copy () -> addMonth () -> year , $now -> copy () -> addMonth () -> month , $closingDay ) -> endOfDay ();
2025-05-05 19:40:38 +07:00
} else {
2025-06-19 19:39:20 +07:00
$currentPeriodStartDate = Carbon :: create ( $now -> copy () -> subMonth () -> year , $now -> copy () -> subMonth () -> month , $startDay ) -> startOfDay ();
$currentPeriodClosingDate = Carbon :: create ( $now -> year , $now -> month , $closingDay ) -> endOfDay ();
2025-05-05 19:40:38 +07:00
}
2025-01-18 15:01:28 +07:00
2025-06-19 19:39:20 +07:00
// Calculate the actual start date for data retrieval (1 month before currentPeriodStartDate)
2025-06-26 19:47:57 +07:00
$dataRetrievalStartDate = $currentPeriodStartDate -> copy () -> subMonth () -> subDay () -> startOfDay ();
2025-05-25 15:14:23 +07:00
2025-06-19 19:39:20 +07:00
$forms = FormEntertaimentPresentation :: whereBetween ( 'tanggal' , [ $dataRetrievalStartDate , $currentPeriodClosingDate ])
2025-01-12 13:34:35 +07:00
-> orderBy ( 'tanggal' , 'desc' )
2025-10-13 17:28:34 +07:00
-> with ([ 'user' , 'attachments' ])
2025-01-12 13:34:35 +07:00
-> get ();
2025-01-11 13:31:44 +07:00
$availableBudget = null ;
2025-06-19 19:39:20 +07:00
$periodeMonth = strtolower ( $currentPeriodStartDate -> format ( 'F' ));
$periodeYear = ( int ) $currentPeriodStartDate -> format ( 'Y' );
2024-12-26 18:37:34 +07:00
2025-05-25 15:14:23 +07:00
if ( $role == 'Admin Region' || $role == 'Marketing Operational Manager Region' ) {
2025-10-13 00:14:28 +07:00
if ( $regionContextId ) {
$users = UserHasCabang :: whereHas ( 'cabang' , function ( $query ) use ( $regionContextId ) {
$query -> where ( 'region_id' , $regionContextId );
2025-05-25 15:14:23 +07:00
}) -> get ();
2024-12-26 18:37:34 +07:00
2025-05-25 15:14:23 +07:00
$userIds = $users -> pluck ( 'user_id' ) -> toArray ();
$forms = $forms -> whereIn ( 'user_id' , $userIds );
}
} elseif ( $role == 'Area Manager Cabang' ) {
2025-10-13 00:14:28 +07:00
if ( $cabangContextId ) {
$users = UserHasCabang :: where ( 'cabang_id' , $cabangContextId ) -> get ();
2025-01-01 15:33:16 +07:00
$userIds = $users -> pluck ( 'user_id' ) -> toArray ();
$forms = $forms -> whereIn ( 'user_id' , $userIds );
2025-05-25 15:14:23 +07:00
2025-10-13 00:14:28 +07:00
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabangContextId , $periodeMonth , $periodeYear );
2025-01-01 15:33:16 +07:00
}
2025-05-25 15:14:23 +07:00
} elseif ( $role == 'Medical Representatif' ) {
2024-12-26 18:37:34 +07:00
$forms = $forms -> where ( 'user_id' , auth () -> user () -> id );
2025-05-25 15:14:23 +07:00
$cabang_id = UserHasCabang :: where ( 'user_id' , auth () -> user () -> id ) -> first () ? -> cabang_id ;
if ( $cabang_id ) {
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang_id , $periodeMonth , $periodeYear );
}
2024-12-23 10:10:09 +07:00
}
2025-10-13 00:14:28 +07:00
$outstandingForms = OutstandingHelper :: filterForms (
$forms ,
$role ,
$currentPeriodStartDate ,
$currentPeriodClosingDate ,
[
'region_id' => $regionContextId ,
'cabang_id' => $cabangContextId ,
]
);
2025-10-12 20:47:43 +07:00
2024-12-26 13:28:06 +07:00
session () -> put ( 'redirect_url' , route ( 'forms.entertainment' ));
2025-10-13 17:28:34 +07:00
$forms -> each ( function ( FormEntertaimentPresentation $form ) {
$form -> setAttribute ( 'formatted_attachments' , $this -> formatAttachmentCollection ( $form ));
});
2025-05-25 15:14:23 +07:00
return view ( 'backend.pages.forms.entertainment.index' , [
'forms' => $forms ,
2025-01-11 13:31:44 +07:00
'availableBudget' => $availableBudget ,
2025-10-12 20:47:43 +07:00
'outstandingCount' => $outstandingForms -> count (),
'outstandingTotal' => $outstandingForms -> sum ( 'total' ),
2025-10-13 00:14:28 +07:00
'outstandingIds' => $outstandingForms -> pluck ( 'id' ) -> toArray (),
2025-05-25 15:14:23 +07:00
]);
}
2024-12-18 12:56:16 +07:00
2025-01-06 16:00:51 +07:00
public function detail ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.view' ]);
2025-10-13 17:28:34 +07:00
$form = FormEntertaimentPresentation :: with ([ 'user' , 'attachments' ]) -> findOrFail ( $id );
$attachments = $this -> formatAttachmentCollection ( $form );
$form -> setAttribute ( 'formatted_attachments' , $attachments );
$form -> bukti_total = $this -> getAttachmentDownloadUrlByCategory ( $attachments , 'bukti_total' );
2025-01-06 16:00:51 +07:00
return response () -> json ( $form );
}
public function view ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.view' ]);
2025-10-13 17:28:34 +07:00
$form = FormEntertaimentPresentation :: with ([ 'user' , 'attachments' ]) -> findOrFail ( $id );
$attachments = $this -> formatAttachmentCollection ( $form );
$form -> setAttribute ( 'formatted_attachments' , $attachments );
2025-01-06 16:00:51 +07:00
return view ( 'backend.pages.forms.entertainment.view' , [
'form' => $form ,
2025-10-13 17:28:34 +07:00
'attachments' => $attachments ,
2025-01-06 16:00:51 +07:00
]);
}
2026-06-01 15:01:03 +07:00
public function create ()
{
2024-12-18 12:56:16 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.create' ]);
2026-06-01 15:01:03 +07:00
return view ( 'backend.pages.forms.entertainment.create' );
}
2024-12-18 12:56:16 +07:00
2026-06-01 15:01:03 +07:00
public function store ( Request $request )
{
2024-12-18 12:56:16 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.create' ]);
2025-10-13 17:28:34 +07:00
$validator = Validator :: make ( $request -> all (), [
'tanggal' => 'required|date' ,
2024-12-18 12:56:16 +07:00
'jenis' => 'required|in:entertainment,presentation,sponsorship' ,
2025-10-13 17:28:34 +07:00
'keterangan' => 'required|string' ,
2025-01-19 14:57:01 +07:00
'total' => 'required|numeric|min:1' ,
2025-10-13 17:28:34 +07:00
'name' => 'required|string' ,
'alamat' => 'required|string' ,
'nik_or_npwp' => 'required|string' ,
2026-06-01 15:01:03 +07:00
'jabatan' => 'required|string' ,
'nama_perusahaan' => 'required|string' ,
'jenis_usaha' => 'required|string' ,
2025-10-13 17:28:34 +07:00
'attachments' => 'nullable|array' ,
'attachments.*.file_category' => 'nullable|string|in:' . implode ( ',' , array_keys ( $this -> attachmentCategories )),
'attachments.*.file_path' => 'nullable|file|max:10240' ,
2024-12-18 12:56:16 +07:00
]);
2025-10-13 17:28:34 +07:00
$this -> addAttachmentValidationRules ( $validator , $request );
$validator -> validate ();
2024-12-18 12:56:16 +07:00
2025-05-05 19:40:38 +07:00
$tanggal = Carbon :: parse ( $request -> tanggal );
2026-06-01 15:01:03 +07:00
// PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE
2025-05-05 19:40:38 +07:00
$startDay = ( int ) env ( 'STARTING_DATE' , 11 );
2026-06-01 15:01:03 +07:00
$inputMaxDay = ( int ) env ( 'INPUT_MAX_DATE' , 10 );
2025-05-05 19:40:38 +07:00
// Ambil tanggal sekarang untuk tentukan periode berjalan
$now = Carbon :: now ();
if ( $now -> day >= $startDay ) {
2026-06-01 15:01:03 +07:00
// Jendela Aktif: Tgl 11 Bulan Ini s/d Tgl 10 Bulan Depan
$startDate = Carbon :: create ( $now -> year , $now -> month , $startDay ) -> startOfDay ();
$maxInputDate = Carbon :: create ( $now -> copy () -> addMonth () -> year , $now -> copy () -> addMonth () -> month , $inputMaxDay ) -> endOfDay ();
2025-05-05 19:40:38 +07:00
} else {
2026-06-01 15:01:03 +07:00
// Jendela Aktif: Tgl 11 Bulan Lalu s/d Tgl 10 Bulan Ini
$startDate = Carbon :: create ( $now -> copy () -> subMonth () -> year , $now -> copy () -> subMonth () -> month , $startDay ) -> startOfDay ();
$maxInputDate = Carbon :: create ( $now -> year , $now -> month , $inputMaxDay ) -> endOfDay ();
2025-05-05 19:40:38 +07:00
}
2025-01-18 15:01:28 +07:00
2026-06-01 15:01:03 +07:00
if ( $tanggal -> lt ( $startDate ) || $tanggal -> gt ( $maxInputDate )) {
session () -> flash ( 'error' , " Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: { $startDate -> format ( 'd M Y' ) } - { $maxInputDate -> format ( 'd M Y' ) } " );
2025-10-13 17:28:34 +07:00
return redirect () -> back () -> withInput ();
2025-01-18 15:01:28 +07:00
}
2025-05-25 15:14:23 +07:00
// Hitung periode budget dari tanggal form
$periodeMonth = strtolower ( $startDate -> format ( 'F' ));
$periodeYear = ( int ) $startDate -> format ( 'Y' );
2024-12-18 12:56:16 +07:00
$cabang = UserHasCabang :: with ( 'cabang' ) -> where ( 'user_id' , auth () -> user () -> id ) -> first () -> cabang ;
2025-05-25 15:14:23 +07:00
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang -> id , $periodeMonth , $periodeYear );
2025-10-13 17:28:34 +07:00
if ( $request -> total > $availableBudget ) {
2025-01-19 14:57:01 +07:00
session () -> flash ( 'error' , 'Alokasi budget bulanan cabang tidak mencukupi,silahkan ajukan pada periode expense berikutnya' );
2025-10-13 17:28:34 +07:00
return redirect () -> back () -> withInput ();
2025-01-19 14:57:01 +07:00
}
2024-12-18 12:56:16 +07:00
$region = Region :: where ( 'id' , $cabang -> region_id ) -> first ();
2025-10-13 00:29:49 +07:00
2026-06-01 15:01:03 +07:00
$kategori = null ;
2025-10-13 00:29:49 +07:00
if ( strtolower ( $request -> jenis ) == 'entertainment' || $request -> jenis == 'entertainment' ) {
2026-06-01 15:01:03 +07:00
$kategori = Kategori :: where ( 'name' , 'LIKE' , '%Entertainment%' ) -> first ();
2025-10-13 17:28:34 +07:00
} elseif ( strtolower ( $request -> jenis ) == 'presentation' || $request -> jenis == 'presentation' ) {
2026-06-01 15:01:03 +07:00
$kategori = Kategori :: where ( 'name' , 'LIKE' , '%Presentation%' ) -> first ();
2025-10-13 17:28:34 +07:00
} elseif ( strtolower ( $request -> jenis ) == 'sponsorship' || $request -> jenis == 'sponsorship' ) {
2026-06-01 15:01:03 +07:00
$kategori = Kategori :: where ( 'name' , 'LIKE' , '%Sponsorship%' ) -> first ();
2025-10-13 00:29:49 +07:00
}
2024-12-18 12:56:16 +07:00
$folderPath = '/expense/' . $region -> code . '/' . $cabang -> code . '/entertainment' ;
// Generate sequence number and expense number
2026-06-01 15:01:03 +07:00
$sequence_number = FormEntertaimentPresentation :: withTrashed () -> where ( 'expense_number' , 'like' , $region -> code . '-' . $cabang -> code . '-ETY-' . date ( 'ym' ) . '%' ) -> count () + 1 ;
2024-12-18 12:56:16 +07:00
$formatted_sequence_number = str_pad ( $sequence_number , 4 , '0' , STR_PAD_LEFT );
$expense_number = $region -> code . '-' . $cabang -> code . '-ETY-' . date ( 'ym' ) . $formatted_sequence_number ;
2025-10-13 17:28:34 +07:00
DB :: beginTransaction ();
try {
$attachmentsPayload = $this -> buildAttachmentPayload ( $request , $folderPath );
if ( empty ( $attachmentsPayload )) {
throw ValidationException :: withMessages ([
'attachments.0.file_path' => 'Minimal satu lampiran diperlukan.' ,
]);
}
$primaryAttachmentPath = $attachmentsPayload [ 0 ][ 'file_path' ] ? ? null ;
$form = FormEntertaimentPresentation :: create ([
'expense_number' => $expense_number ,
'user_id' => auth () -> user () -> id ,
2026-06-01 15:01:03 +07:00
'cabang_id' => $cabang -> id , // <--- REPARASI SUNTIK CABANG_ID BARU
2025-10-13 17:28:34 +07:00
'tanggal' => $request -> tanggal ,
'jenis' => $request -> jenis ,
'keterangan' => $request -> keterangan ,
'total' => $request -> total ,
'name' => $request -> name ,
'alamat' => $request -> alamat ,
'nik_or_npwp' => $request -> nik_or_npwp ,
2026-06-01 15:01:03 +07:00
'jabatan' => $request -> jabatan ,
'nama_perusahaan' => $request -> nama_perusahaan ,
'jenis_usaha' => $request -> jenis_usaha ,
2025-10-13 17:28:34 +07:00
'bukti_total' => $primaryAttachmentPath ,
2026-06-01 15:01:03 +07:00
'account_number' => $kategori -> account_number ? ? null ,
'kategori_id' => $kategori -> id ? ? null ,
2025-10-13 17:28:34 +07:00
'status' => 'On Progress' ,
]);
$this -> attachmentService -> addMultipleAttachments (
$form -> id ,
AttachmentTableName :: FORM_ENTERTAINMENT_PRESENTATION ,
$attachmentsPayload
);
DB :: commit ();
} catch ( ValidationException $exception ) {
DB :: rollBack ();
throw $exception ;
} catch ( \Throwable $th ) {
DB :: rollBack ();
Log :: error ( 'Failed to create Entertainment Presentation form' , [
'user_id' => auth () -> id (),
'message' => $th -> getMessage (),
'trace' => $th -> getTraceAsString (),
]);
session () -> flash ( 'error' , 'Terjadi kesalahan saat menyimpan data, silakan coba lagi.' );
return redirect () -> back () -> withInput ();
}
2024-12-18 12:56:16 +07:00
2025-01-09 13:22:56 +07:00
$name = auth () -> user () -> name ;
2025-01-16 16:42:06 +07:00
$tanggal = $form -> created_at ;
2025-01-09 13:22:56 +07:00
$total = $form -> total ;
2025-01-16 16:42:06 +07:00
// Send notification to MIS and Admin Region and Area Manager Cabang
2025-03-03 20:13:30 +07:00
$roles = [ 'Marketing Information System' , 'Admin Region' , 'Area Manager Cabang' ];
2025-01-16 16:42:06 +07:00
$recipients = [ $form -> user -> email , auth () -> user () -> email ];
$phoneNumbers = [ $form -> user -> phone , auth () -> user () -> phone ];
2025-01-17 16:17:53 +07:00
$receiver_ids = [];
2025-01-16 16:42:06 +07:00
2025-01-09 13:22:56 +07:00
// Collect all recipients (emails and phone numbers) for the specified roles
2025-03-04 12:24:36 +07:00
$cabang = UserHasCabang :: where ( 'user_id' , $form -> user_id ) -> first ();
$cabang_id = $cabang ? $cabang -> cabang_id : null ;
$cabangData = $cabang_id ? Cabang :: where ( 'id' , $cabang_id ) -> first () : null ;
$region_id = $cabangData ? $cabangData -> region -> id : null ;
2025-01-09 13:22:56 +07:00
foreach ( $roles as $role ) {
2025-03-04 12:24:36 +07:00
$users = Admin :: getUserByRoleName ( $role ) -> filter ( function ( $user ) use ( $role , $cabang_id , $region_id ) {
2025-01-16 16:42:06 +07:00
if ( $role === 'Marketing Information System' ) {
2025-03-04 12:24:36 +07:00
return true ; // Tidak ada filter cabang untuk peran ini
}
2025-10-13 17:28:34 +07:00
2025-03-04 12:24:36 +07:00
if ( ! $user -> cabang || ! $user -> cabang -> cabang ) {
return false ; // Pastikan user memiliki cabang
2025-01-16 16:42:06 +07:00
}
2025-03-04 12:24:36 +07:00
if ( $role === 'Admin Region' || $role === 'Marketing Operational Manager Region' ) {
return $user -> cabang -> cabang -> region -> id === $region_id ;
}
return $user -> cabang -> cabang -> id === $cabang_id ;
2025-01-16 16:42:06 +07:00
});
2025-01-09 13:22:56 +07:00
foreach ( $users as $user ) {
$recipients [] = $user -> email ;
$phoneNumbers [] = $user -> phone ;
2025-01-17 16:17:53 +07:00
$receiver_ids [] = $user -> id ;
2025-01-09 13:22:56 +07:00
}
}
// Remove duplicate email addresses, if any
$recipients = array_unique ( $recipients );
2025-01-16 16:42:06 +07:00
$phoneNumbers = array_unique ( $phoneNumbers );
2025-01-17 16:17:53 +07:00
$receiver_ids = array_unique ( $receiver_ids );
2025-01-09 13:22:56 +07:00
// Generate URL and send WhatsApp notification
$url = route ( 'forms.entertainment.view' , $form -> id );
2025-01-16 15:56:22 +07:00
WhatsappHelper :: newExpense ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name );
2025-01-09 13:22:56 +07:00
// Send bulk email
2025-03-05 13:44:58 +07:00
$brevoService = app ( BrevoService :: class );
2025-01-23 17:46:38 +07:00
foreach ( $recipients as $recipient ) {
try {
2025-03-05 13:44:58 +07:00
$mail = new ExpenseCreated (
2025-01-23 17:46:38 +07:00
$name ,
$expense_number ,
$tanggal ,
$total ,
$url
2025-03-05 13:44:58 +07:00
);
$response = $brevoService -> expenseCreated ( $recipient , $name , $mail );
if ( isset ( $response [ 'success' ]) && $response [ 'success' ] === false ) {
Log :: warning ( " Email to { $recipient } failed: " . ( $response [ 'message' ] ? ? 'Unknown error' ));
}
2025-01-23 17:46:38 +07:00
} catch ( \Exception $e ) {
Log :: error ( " Failed to send email to { $recipient } : " . $e -> getMessage ());
continue ;
}
}
2025-01-09 13:22:56 +07:00
2025-01-23 17:46:38 +07:00
NotificationHelper :: newExpense ( 'entertainment-presentation' , $form -> id , $form -> user_id , $expense_number , $receiver_ids );
2025-01-17 16:17:53 +07:00
2025-01-01 17:14:46 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Insert' , 'Insert New Expense at Form Entertainment Presentation (' . $expense_number . ')' );
2024-12-18 12:56:16 +07:00
session () -> flash ( 'success' , 'Form has been created.' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
2026-06-01 15:01:03 +07:00
}
2024-12-18 12:56:16 +07:00
2026-06-01 15:01:03 +07:00
public function edit ( $id )
{
2024-12-18 12:56:16 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.edit' ]);
2025-01-07 17:23:42 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2024-12-18 12:56:16 +07:00
2025-10-13 17:28:34 +07:00
$form = FormEntertaimentPresentation :: with ( 'attachments' ) -> findOrFail ( $id );
2025-01-07 17:23:42 +07:00
if ( $form -> status != 'On Progress' && $role == 'Medical Representatif' ) {
session () -> flash ( 'error' , 'You cannot edit this form because it has been approved or rejected.' );
2024-12-24 11:23:27 +07:00
return redirect () -> back ();
}
2025-10-13 17:28:34 +07:00
$attachments = $this -> formatAttachmentCollection ( $form );
2026-06-01 15:01:03 +07:00
return view ( 'backend.pages.forms.entertainment.edit' , [
2025-10-13 17:28:34 +07:00
'form' => $form ,
'attachments' => $attachments ,
2026-06-01 15:01:03 +07:00
]);
}
2024-12-18 12:56:16 +07:00
2026-06-01 15:01:03 +07:00
public function update ( Request $request , $id )
{
2024-12-18 12:56:16 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.edit' ]);
2025-01-07 17:23:42 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2025-10-13 17:28:34 +07:00
$form = FormEntertaimentPresentation :: with ( 'attachments' ) -> findOrFail ( $id );
2026-06-01 15:01:03 +07:00
$cabang = UserHasCabang :: with ( 'cabang' ) -> where ( 'user_id' , $form -> user_id ) -> first () ? -> cabang ;
2025-05-25 15:14:23 +07:00
2026-06-01 15:01:03 +07:00
if ( ! $cabang ) {
2025-05-25 15:14:23 +07:00
return redirect () -> back () -> with ( 'error' , 'Cabang tidak ditemukan.' );
}
$tanggal = Carbon :: parse ( $request -> tanggal );
2026-06-01 15:01:03 +07:00
// PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE (Konsisten dengan fungsi Store)
2025-05-25 15:14:23 +07:00
$startDay = ( int ) env ( 'STARTING_DATE' , 11 );
2026-06-01 15:01:03 +07:00
$inputMaxDay = ( int ) env ( 'INPUT_MAX_DATE' , 10 );
$now = Carbon :: now ();
2025-05-25 15:14:23 +07:00
2026-06-01 15:01:03 +07:00
if ( $now -> day >= $startDay ) {
$startDate = Carbon :: create ( $now -> year , $now -> month , $startDay ) -> startOfDay ();
$maxInputDate = Carbon :: create ( $now -> copy () -> addMonth () -> year , $now -> copy () -> addMonth () -> month , $inputMaxDay ) -> endOfDay ();
2025-05-25 15:14:23 +07:00
} else {
2026-06-01 15:01:03 +07:00
$startDate = Carbon :: create ( $now -> copy () -> subMonth () -> year , $now -> copy () -> subMonth () -> month , $startDay ) -> startOfDay ();
$maxInputDate = Carbon :: create ( $now -> year , $now -> month , $inputMaxDay ) -> endOfDay ();
2025-05-25 15:14:23 +07:00
}
2026-06-01 15:01:03 +07:00
if ( $tanggal -> lt ( $startDate ) || $tanggal -> gt ( $maxInputDate )) {
session () -> flash ( 'error' , " Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: { $startDate -> format ( 'd M Y' ) } - { $maxInputDate -> format ( 'd M Y' ) } " );
return redirect () -> back () -> withInput ();
}
2025-05-25 15:14:23 +07:00
2026-06-01 15:01:03 +07:00
// Hitung periode berdasarkan tanggal form untuk validasi budget
$periodeMonth = strtolower ( $startDate -> format ( 'F' ));
$periodeYear = ( int ) $startDate -> format ( 'Y' );
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang -> id , $periodeMonth , $periodeYear );
2024-12-18 12:56:16 +07:00
2025-10-13 17:28:34 +07:00
$validator = Validator :: make ( $request -> all (), [
'tanggal' => 'required|date' ,
2024-12-18 12:56:16 +07:00
'jenis' => 'required|in:entertainment,presentation,sponsorship' ,
2025-10-13 17:28:34 +07:00
'keterangan' => 'required|string' ,
2025-05-25 15:14:23 +07:00
'total' => 'required|numeric|min:1|max:' . $availableBudget ,
2025-10-13 17:28:34 +07:00
'name' => 'required|string' ,
'alamat' => 'required|string' ,
'nik_or_npwp' => 'required|string' ,
2026-06-01 15:01:03 +07:00
'jabatan' => 'required|string' ,
'nama_perusahaan' => 'required|string' ,
'jenis_usaha' => 'required|string' ,
2025-10-13 17:28:34 +07:00
'attachments' => 'nullable|array' ,
'attachments.*.file_category' => 'nullable|string|in:' . implode ( ',' , array_keys ( $this -> attachmentCategories )),
'attachments.*.file_path' => 'nullable|file|max:10240' ,
2024-12-18 12:56:16 +07:00
]);
2025-10-13 17:28:34 +07:00
$this -> addAttachmentValidationRules ( $validator , $request );
$validator -> validate ();
2024-12-18 12:56:16 +07:00
2025-10-13 17:28:34 +07:00
if ( $form -> status != 'On Progress' && $role == 'Medical Representatif' ) {
2025-01-07 17:23:42 +07:00
session () -> flash ( 'error' , 'You cannot edit this form because it has been approved or rejected.' );
2024-12-24 11:23:27 +07:00
return redirect () -> back ();
}
2024-12-18 12:56:16 +07:00
$region = Region :: where ( 'id' , $cabang -> region_id ) -> first ();
2025-10-13 00:29:49 +07:00
2026-06-01 15:01:03 +07:00
$kategori = null ;
2025-10-13 00:29:49 +07:00
if ( strtolower ( $request -> jenis ) == 'entertainment' || $request -> jenis == 'entertainment' ) {
2026-06-01 15:01:03 +07:00
$kategori = Kategori :: where ( 'name' , 'LIKE' , '%Entertainment%' ) -> first ();
2025-10-13 17:28:34 +07:00
} elseif ( strtolower ( $request -> jenis ) == 'presentation' || $request -> jenis == 'presentation' ) {
2026-06-01 15:01:03 +07:00
$kategori = Kategori :: where ( 'name' , 'LIKE' , '%Presentation%' ) -> first ();
2025-10-13 17:28:34 +07:00
} elseif ( strtolower ( $request -> jenis ) == 'sponsorship' || $request -> jenis == 'sponsorship' ) {
2026-06-01 15:01:03 +07:00
$kategori = Kategori :: where ( 'name' , 'LIKE' , '%Sponsorship%' ) -> first ();
2025-10-13 00:29:49 +07:00
}
2024-12-18 12:56:16 +07:00
2024-12-19 10:51:27 +07:00
$folderPath = '/expense/' . $region -> code . '/' . $cabang -> code . '/entertainment' ;
2024-12-18 12:56:16 +07:00
2025-10-13 17:28:34 +07:00
DB :: beginTransaction ();
try {
$attachmentsPayload = $this -> buildAttachmentPayload ( $request , $folderPath );
$primaryAttachmentPath = ! empty ( $attachmentsPayload )
? $attachmentsPayload [ 0 ][ 'file_path' ]
: $form -> bukti_total ;
$form -> update ([
2026-06-01 15:01:03 +07:00
'cabang_id' => $cabang -> id , // <--- REPARASI SUNTIK CABANG_ID TERBARU
2025-10-13 17:28:34 +07:00
'tanggal' => $request -> tanggal ,
'jenis' => $request -> jenis ,
'keterangan' => $request -> keterangan ,
'total' => $request -> total ,
'name' => $request -> name ,
'alamat' => $request -> alamat ,
'nik_or_npwp' => $request -> nik_or_npwp ,
2026-06-01 15:01:03 +07:00
'jabatan' => $request -> jabatan ,
'nama_perusahaan' => $request -> nama_perusahaan ,
'jenis_usaha' => $request -> jenis_usaha ,
2025-10-13 17:28:34 +07:00
'bukti_total' => $primaryAttachmentPath ,
2026-06-01 15:01:03 +07:00
'account_number' => $kategori -> account_number ? ? $form -> account_number ,
'kategori_id' => $kategori -> id ? ? null ,
2025-10-13 17:28:34 +07:00
]);
if ( ! empty ( $attachmentsPayload )) {
$this -> attachmentService -> addMultipleAttachments (
$form -> id ,
AttachmentTableName :: FORM_ENTERTAINMENT_PRESENTATION ,
$attachmentsPayload
);
}
2024-12-18 12:56:16 +07:00
2025-10-13 17:28:34 +07:00
DB :: commit ();
} catch ( ValidationException $exception ) {
DB :: rollBack ();
throw $exception ;
} catch ( \Throwable $th ) {
DB :: rollBack ();
Log :: error ( 'Failed to update Entertainment Presentation form' , [
'form_id' => $form -> id ,
'message' => $th -> getMessage (),
'trace' => $th -> getTraceAsString (),
]);
session () -> flash ( 'error' , 'Terjadi kesalahan saat memperbarui data, silakan coba lagi.' );
return redirect () -> back () -> withInput ();
2024-12-18 12:56:16 +07:00
}
2025-01-01 17:14:46 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Update' , 'Update Expense at Form Entertainment Presentation (' . $form -> expense_number . ')' );
2024-12-18 12:56:16 +07:00
session () -> flash ( 'success' , 'Form has been updated.' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
2026-06-01 15:01:03 +07:00
}
2024-12-18 12:56:16 +07:00
2025-10-13 17:28:34 +07:00
public function deleteAttachment ( Request $request , $formId , $attachmentId )
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.edit' ]);
$form = FormEntertaimentPresentation :: findOrFail ( $formId );
$role = auth () -> user () -> getRoleNames ()[ 0 ];
if (( $form -> status !== 'On Progress' && $form -> status !== 'Rejected' ) && $role === 'Medical Representatif' ) {
return response () -> json ([ 'message' => 'You cannot modify attachments once the form is approved or closed.' ], 403 );
}
$attachment = AttachmentForm :: where ( 'id' , $attachmentId )
-> where ( 'form_id' , $form -> id )
-> where ( 'table_name' , AttachmentTableName :: FORM_ENTERTAINMENT_PRESENTATION )
-> first ();
if ( ! $attachment ) {
return response () -> json ([ 'message' => 'Attachment not found.' ], 404 );
}
$this -> attachmentService -> deleteAttachment ( $attachment -> id );
AuditTrailHelper :: AddAuditTrail ( 'Delete Attachment' , 'Delete Attachment at Form Entertainment Presentation (' . $form -> expense_number . ')' );
return response () -> json ([ 'message' => 'Attachment deleted successfully.' ]);
}
2025-01-16 15:56:22 +07:00
public function approve ( $id )
2024-12-23 10:10:09 +07:00
{
$this -> checkAuthorization ( auth () -> user (), [ 'approval.approve' ]);
$form = FormEntertaimentPresentation :: findOrfail ( $id );
$form -> update ([
'approved_at' => now (),
'approved_by' => auth () -> user () -> id ,
2025-01-16 15:56:22 +07:00
'status' => 'Approved 1' ,
2025-01-23 17:46:38 +07:00
'approved_total' => $form -> total ,
2024-12-23 10:10:09 +07:00
]);
2024-12-24 15:12:24 +07:00
$name = $form -> user -> name ;
$expense_number = $form -> expense_number ;
2025-01-16 15:56:22 +07:00
$tanggal = $form -> created_at ;
$total = $form -> total ;
2024-12-24 15:12:24 +07:00
// Collect all recipients
2025-03-03 20:13:30 +07:00
$roles = [ 'Marketing Information System' , 'Admin Region' , 'Area Manager Cabang' , 'Marketing Operational Manager Region' ];
2025-01-17 16:17:53 +07:00
$receiver_ids = [];
2024-12-26 13:40:17 +07:00
$recipients = [ $form -> user -> email , auth () -> user () -> email ];
$phoneNumbers = [ $form -> user -> phone , auth () -> user () -> phone ];
2024-12-24 15:12:24 +07:00
2025-03-04 12:24:36 +07:00
// Collect all recipients (emails and phone numbers) for the specified roles
$cabang = UserHasCabang :: where ( 'user_id' , $form -> user_id ) -> first ();
$cabang_id = $cabang ? $cabang -> cabang_id : null ;
$cabangData = $cabang_id ? Cabang :: where ( 'id' , $cabang_id ) -> first () : null ;
$region_id = $cabangData ? $cabangData -> region -> id : null ;
2025-01-16 16:42:06 +07:00
foreach ( $roles as $role ) {
2025-03-04 12:24:36 +07:00
$users = Admin :: getUserByRoleName ( $role ) -> filter ( function ( $user ) use ( $role , $cabang_id , $region_id ) {
2025-01-16 16:42:06 +07:00
if ( $role === 'Marketing Information System' ) {
2025-03-04 12:24:36 +07:00
return true ; // Tidak ada filter cabang untuk peran ini
}
if ( ! $user -> cabang || ! $user -> cabang -> cabang ) {
return false ; // Pastikan user memiliki cabang
2025-01-16 16:42:06 +07:00
}
2024-12-24 15:12:24 +07:00
2025-03-04 12:24:36 +07:00
if ( $role === 'Admin Region' || $role === 'Marketing Operational Manager Region' ) {
return $user -> cabang -> cabang -> region -> id === $region_id ;
}
return $user -> cabang -> cabang -> id === $cabang_id ;
2025-01-16 16:42:06 +07:00
});
foreach ( $users as $user ) {
$recipients [] = $user -> email ;
$phoneNumbers [] = $user -> phone ;
2025-01-17 16:17:53 +07:00
$receiver_ids [] = $user -> id ;
2025-01-16 16:42:06 +07:00
}
2024-12-24 15:12:24 +07:00
}
2025-01-16 15:56:22 +07:00
// Remove duplicate data, if any
2024-12-24 15:12:24 +07:00
$recipients = array_unique ( $recipients );
2024-12-25 13:37:49 +07:00
$phoneNumbers = array_unique ( $phoneNumbers );
2025-01-17 16:17:53 +07:00
$receiver_ids = array_unique ( $receiver_ids );
2024-12-24 15:12:24 +07:00
2025-01-16 15:56:22 +07:00
// send whatsapp message
$url = route ( 'forms.entertainment.view' , $form -> id );
WhatsappHelper :: approveExpense ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name );
2024-12-25 13:37:49 +07:00
2025-03-05 13:44:58 +07:00
// Send bulk email
$brevoService = app ( BrevoService :: class );
2025-01-23 17:46:38 +07:00
foreach ( $recipients as $recipient ) {
try {
2025-03-05 13:44:58 +07:00
$mail = new ExpenseApproved (
2025-01-23 17:46:38 +07:00
$name ,
$expense_number ,
$tanggal ,
$total ,
$url
2025-03-05 13:44:58 +07:00
);
$response = $brevoService -> expenseApproved ( $recipient , $name , $mail );
if ( isset ( $response [ 'success' ]) && $response [ 'success' ] === false ) {
Log :: warning ( " Email to { $recipient } failed: " . ( $response [ 'message' ] ? ? 'Unknown error' ));
}
2025-01-23 17:46:38 +07:00
} catch ( \Exception $e ) {
Log :: error ( " Failed to send email to { $recipient } : " . $e -> getMessage ());
continue ;
}
}
2025-01-09 13:22:56 +07:00
2025-01-23 17:46:38 +07:00
NotificationHelper :: approveExpense ( 'entertainment-presentation' , $form -> id , $form -> user_id , $expense_number , $receiver_ids );
2025-01-17 16:17:53 +07:00
2025-01-16 15:56:22 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Approve' , 'Approve Expense at Form Entertainment (' . $form -> expense_number . ')' );
session () -> flash ( 'success' , 'Form has been approved.' );
return redirect () -> back ();
}
public function approve2 ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'approval2.approve' ]);
$form = FormEntertaimentPresentation :: findOrfail ( $id );
$form -> update ([
'approved2_at' => now (),
'approved2_by' => auth () -> user () -> id ,
'status' => 'Approved 2' ,
]);
$name = $form -> user -> name ;
$expense_number = $form -> expense_number ;
$tanggal = $form -> created_at ;
$total = $form -> total ;
2025-03-03 20:13:30 +07:00
$roles = [ 'Marketing Information System' , 'Area Manager Cabang' , 'Marketing Operational Manager Region' , 'Head of Sales Marketing' ];
2025-01-17 16:17:53 +07:00
$receiver_ids = [];
2025-01-16 15:56:22 +07:00
$recipients = [ $form -> user -> email , auth () -> user () -> email ];
$phoneNumbers = [ $form -> user -> phone , auth () -> user () -> phone ];
2025-03-04 12:24:36 +07:00
// Collect all recipients (emails and phone numbers) for the specified roles
$cabang = UserHasCabang :: where ( 'user_id' , $form -> user_id ) -> first ();
$cabang_id = $cabang ? $cabang -> cabang_id : null ;
$cabangData = $cabang_id ? Cabang :: where ( 'id' , $cabang_id ) -> first () : null ;
$region_id = $cabangData ? $cabangData -> region -> id : null ;
2025-01-16 16:42:06 +07:00
foreach ( $roles as $role ) {
2025-03-04 12:24:36 +07:00
$users = Admin :: getUserByRoleName ( $role ) -> filter ( function ( $user ) use ( $role , $cabang_id , $region_id ) {
if ( $role === 'Marketing Information System' || $role === 'Head of Sales Marketing' ) {
return true ; // Tidak ada filter cabang untuk peran ini
}
if ( ! $user -> cabang || ! $user -> cabang -> cabang ) {
return false ; // Pastikan user memiliki cabang
}
if ( $role === 'Admin Region' || $role === 'Marketing Operational Manager Region' ) {
return $user -> cabang -> cabang -> region -> id === $region_id ;
2025-01-16 16:42:06 +07:00
}
2025-03-04 12:24:36 +07:00
return $user -> cabang -> cabang -> id === $cabang_id ;
2025-01-16 16:42:06 +07:00
});
2025-01-16 15:56:22 +07:00
2025-01-16 16:42:06 +07:00
foreach ( $users as $user ) {
$recipients [] = $user -> email ;
$phoneNumbers [] = $user -> phone ;
2025-01-17 16:17:53 +07:00
$receiver_ids [] = $user -> id ;
2025-01-16 16:42:06 +07:00
}
2025-01-16 15:56:22 +07:00
}
// Remove duplicate data, if any
$recipients = array_unique ( $recipients );
$phoneNumbers = array_unique ( $phoneNumbers );
2025-01-17 16:17:53 +07:00
$receiver_ids = array_unique ( $receiver_ids );
2025-01-16 15:56:22 +07:00
// send whatsapp message
$url = route ( 'forms.entertainment.view' , $form -> id );
WhatsappHelper :: approve2Expense ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name );
2025-03-05 13:44:58 +07:00
// Send bulk email
$brevoService = app ( BrevoService :: class );
2025-01-23 17:46:38 +07:00
foreach ( $recipients as $recipient ) {
try {
2025-03-05 13:44:58 +07:00
$mail = new ExpenseApproved2 (
2025-01-23 17:46:38 +07:00
$name ,
$expense_number ,
$tanggal ,
$total ,
$url
2025-03-05 13:44:58 +07:00
);
$response = $brevoService -> expenseApproved2 ( $recipient , $name , $mail );
if ( isset ( $response [ 'success' ]) && $response [ 'success' ] === false ) {
Log :: warning ( " Email to { $recipient } failed: " . ( $response [ 'message' ] ? ? 'Unknown error' ));
}
2025-01-23 17:46:38 +07:00
} catch ( \Exception $e ) {
Log :: error ( " Failed to send email to { $recipient } : " . $e -> getMessage ());
continue ;
}
}
2025-01-16 15:56:22 +07:00
2025-01-23 17:46:38 +07:00
NotificationHelper :: approve2Expense ( 'entertainment-presentation' , $form -> id , $form -> user_id , $expense_number , $receiver_ids );
2025-01-17 16:17:53 +07:00
2025-01-16 15:56:22 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Approve' , 'Approve Expense at Form Entertainment (' . $form -> expense_number . ')' );
2024-12-23 10:10:09 +07:00
session () -> flash ( 'success' , 'Form has been approved.' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
2024-12-23 10:10:09 +07:00
}
2026-06-01 15:01:03 +07:00
// <--- REPARASI SUNTIKAN REQUEST DARI JAVASCRIPT MODAL AGAR DATA BISA DIAMBIL
public function finalApprove ( Request $request , $id )
2024-12-23 10:10:09 +07:00
{
$this -> checkAuthorization ( auth () -> user (), [ 'final_approval.approve' ]);
2025-05-25 15:14:23 +07:00
$form = FormEntertaimentPresentation :: findOrFail ( $id );
2024-12-23 10:10:09 +07:00
2025-05-25 15:14:23 +07:00
if ( $form -> approved_at == null || $form -> approved2_at == null ) {
2025-01-16 15:56:22 +07:00
session () -> flash ( 'error' , 'Form must be approved by both approvers before final approval.' );
return redirect () -> back ();
}
2025-05-25 15:14:23 +07:00
$cabang_id = UserHasCabang :: where ( 'user_id' , $form -> user_id ) -> first () ? -> cabang_id ;
if ( ! $cabang_id ) {
session () -> flash ( 'error' , 'Data cabang tidak ditemukan.' );
return redirect () -> back ();
}
2026-06-01 15:01:03 +07:00
// Hitung periode berdasarkan tanggal form
2025-05-25 15:14:23 +07:00
$tanggal = Carbon :: parse ( $form -> tanggal );
$startDay = ( int ) env ( 'STARTING_DATE' , 11 );
2026-06-01 15:01:03 +07:00
2025-05-25 15:14:23 +07:00
if ( $tanggal -> day >= $startDay ) {
$periodeDate = Carbon :: create ( $tanggal -> year , $tanggal -> month , $startDay );
} else {
$periodeDate = Carbon :: create ( $tanggal -> copy () -> subMonth () -> year , $tanggal -> copy () -> subMonth () -> month , $startDay );
}
$periodeMonth = strtolower ( $periodeDate -> format ( 'F' ));
$periodeYear = ( int ) $periodeDate -> format ( 'Y' );
2026-06-01 15:01:03 +07:00
// <--- REPARASI: Penangkapan nilai payload dari checkbox JavaScript
$approvedTotal = $request -> input ( 'approved_total' , $form -> total );
2025-05-25 15:14:23 +07:00
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang_id , $periodeMonth , $periodeYear );
2026-06-01 15:01:03 +07:00
if ( $approvedTotal > $availableBudget ) { // <--- DISINKRONKAN DENGAN NILAI BARU HASIL CHECKLIST
2025-01-23 17:46:38 +07:00
session () -> flash ( 'error' , 'Budget cabang untuk periode ini tidak mencukupi, silahkan ajukan pada periode expense berikutnya' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
2024-12-23 10:10:09 +07:00
}
$form -> update ([
2026-06-01 15:01:03 +07:00
'approved_total' => $approvedTotal , // <--- SUNTIK DATA UPDATE HASIL CHECKLIST KE DATABASE
2024-12-23 10:10:09 +07:00
'final_approved_at' => now (),
'final_approved_by' => auth () -> user () -> id ,
2025-01-16 15:56:22 +07:00
'status' => 'Closed' ,
2024-12-23 10:10:09 +07:00
]);
2024-12-26 13:49:00 +07:00
$name = $form -> user -> name ;
$expense_number = $form -> expense_number ;
2025-01-16 15:56:22 +07:00
$tanggal = $form -> created_at ;
2026-06-01 15:01:03 +07:00
$total = $form -> approved_total ; // <--- TOTAL BARU YANG DIKIRIMKAN KE EMAIL / WHATSAPP
2024-12-26 13:49:00 +07:00
2025-01-16 16:42:06 +07:00
$roles = [ 'Head of Sales Marketing' , 'Marketing Operational Manager Region' ];
2024-12-26 13:49:00 +07:00
$recipients = [ $form -> user -> email , auth () -> user () -> email ];
$phoneNumbers = [ $form -> user -> phone , auth () -> user () -> phone ];
2025-03-04 12:24:36 +07:00
$cabang = UserHasCabang :: where ( 'user_id' , $form -> user_id ) -> first ();
$cabang_id = $cabang ? $cabang -> cabang_id : null ;
$cabangData = $cabang_id ? Cabang :: where ( 'id' , $cabang_id ) -> first () : null ;
$region_id = $cabangData ? $cabangData -> region -> id : null ;
2025-01-16 16:42:06 +07:00
foreach ( $roles as $role ) {
2025-03-04 12:24:36 +07:00
$users = Admin :: getUserByRoleName ( $role ) -> filter ( function ( $user ) use ( $role , $cabang_id , $region_id ) {
if ( $role === 'Marketing Information System' || $role === 'Head of Sales Marketing' ) {
return true ; // Tidak ada filter cabang untuk peran ini
}
if ( ! $user -> cabang || ! $user -> cabang -> cabang ) {
return false ; // Pastikan user memiliki cabang
}
if ( $role === 'Admin Region' || $role === 'Marketing Operational Manager Region' ) {
return $user -> cabang -> cabang -> region -> id === $region_id ;
2025-01-16 16:42:06 +07:00
}
2025-03-04 12:24:36 +07:00
return $user -> cabang -> cabang -> id === $cabang_id ;
2025-01-16 16:42:06 +07:00
});
2024-12-26 13:49:00 +07:00
2025-01-16 16:42:06 +07:00
foreach ( $users as $user ) {
$recipients [] = $user -> email ;
$phoneNumbers [] = $user -> phone ;
}
2024-12-26 13:49:00 +07:00
}
// Remove duplicate email addresses, if any
$recipients = array_unique ( $recipients );
$phoneNumbers = array_unique ( $phoneNumbers );
// send whatsapp message
2025-01-06 16:00:51 +07:00
$url = route ( 'forms.entertainment.view' , $form -> id );
2025-01-16 15:56:22 +07:00
WhatsappHelper :: finalApprove ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name );
2024-12-26 13:49:00 +07:00
2025-01-09 13:22:56 +07:00
// Send bulk email
2025-03-05 13:44:58 +07:00
$brevoService = app ( BrevoService :: class );
2025-01-23 17:46:38 +07:00
foreach ( $recipients as $recipient ) {
try {
2025-03-05 13:44:58 +07:00
$mail = new FinalApprove (
2025-01-23 17:46:38 +07:00
$name ,
$expense_number ,
$tanggal ,
$total ,
$url
2025-03-05 13:44:58 +07:00
);
$response = $brevoService -> expenseFinalApproved ( $recipient , $name , $mail );
if ( isset ( $response [ 'success' ]) && $response [ 'success' ] === false ) {
Log :: warning ( " Email to { $recipient } failed: " . ( $response [ 'message' ] ? ? 'Unknown error' ));
}
2025-01-23 17:46:38 +07:00
} catch ( \Exception $e ) {
Log :: error ( " Failed to send email to { $recipient } : " . $e -> getMessage ());
continue ;
}
}
2025-01-09 13:22:56 +07:00
2025-01-16 15:56:22 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Final Approve' , 'Final Approve Expense at Form Entertainment (' . $form -> expense_number . ')' );
2024-12-23 10:10:09 +07:00
session () -> flash ( 'success' , 'Form has been final approved.' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
2024-12-23 10:10:09 +07:00
}
2025-10-12 20:47:43 +07:00
public function reject ( Request $request , $id )
2024-12-23 10:10:09 +07:00
{
2026-06-01 15:01:03 +07:00
// Otorisasi khusus MOM Region dan Atasan
if ( ! auth () -> user () -> hasAnyPermission ([ 'approval.reject' , 'approval2.reject' , 'final_approval.approve' ])) {
abort ( 403 , 'Akses Ditolak: Anda tidak memiliki izin untuk melakukan Reject.' );
}
2024-12-23 10:10:09 +07:00
2025-10-12 20:47:43 +07:00
$request -> validate ([
2026-06-01 15:01:03 +07:00
'remarks' => 'required|string|max:500' ,
2025-10-12 20:47:43 +07:00
]);
2026-06-01 15:01:03 +07:00
// Menggunakan Model Entertainment & Presentation
2024-12-23 10:10:09 +07:00
$form = FormEntertaimentPresentation :: findOrfail ( $id );
2026-06-01 15:01:03 +07:00
2024-12-23 10:10:09 +07:00
$form -> update ([
2026-06-01 15:01:03 +07:00
'status' => 'Rejected' ,
'remarks' => $request -> remarks ,
'rejected_by' => auth () -> id (), // Kolom baru
'rejected_at' => now (), // Kolom baru
2024-12-23 10:10:09 +07:00
]);
2024-12-24 15:12:24 +07:00
$heads = $form -> user -> getCabangAndRegionHead ();
$name = $form -> user -> name ;
$expense_number = $form -> expense_number ;
2026-06-01 15:01:03 +07:00
$tanggal = $form -> tanggal ;
2024-12-24 15:12:24 +07:00
$total = $form -> total ;
2024-12-26 13:40:17 +07:00
$recipients = [ $form -> user -> email , auth () -> user () -> email ];
$phoneNumbers = [ $form -> user -> phone , auth () -> user () -> phone ];
2024-12-24 15:12:24 +07:00
if ( $heads [ 'cabang_head' ]) {
2026-06-01 15:01:03 +07:00
$recipients [] = $heads [ 'cabang_head' ][ 'email' ];
$phoneNumbers [] = $heads [ 'cabang_head' ][ 'phone' ];
2024-12-24 15:12:24 +07:00
}
if ( $heads [ 'region_head' ]) {
2026-06-01 15:01:03 +07:00
$recipients [] = $heads [ 'region_head' ][ 'email' ];
$phoneNumbers [] = $heads [ 'region_head' ][ 'phone' ];
2024-12-24 15:12:24 +07:00
}
$recipients = array_unique ( $recipients );
2024-12-25 13:37:49 +07:00
$phoneNumbers = array_unique ( $phoneNumbers );
2024-12-24 15:12:24 +07:00
2026-06-01 15:01:03 +07:00
// Route khusus Entertainment
2025-01-06 16:00:51 +07:00
$url = route ( 'forms.entertainment.view' , $form -> id );
2026-06-01 15:01:03 +07:00
// Notifikasi WhatsApp
$this -> rejectExpenseWithNotification ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name , $request -> remarks , $recipients );
2024-12-25 13:37:49 +07:00
2026-06-01 15:01:03 +07:00
// Audit Trail Khusus Form Entertainment
AuditTrailHelper :: AddAuditTrail ( 'Reject' , 'Reject Expense at Form Entertainment Presentation (' . $form -> expense_number . ') oleh ' . auth () -> user () -> name );
session () -> flash ( 'success' , 'Form has been rejected.' );
return redirect () -> back ();
}
2025-03-05 13:44:58 +07:00
2026-06-01 15:01:03 +07:00
private function rejectExpenseWithNotification ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name , $remarks , $recipients )
{
WhatsappHelper :: rejectExpense ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name , $remarks );
2025-03-05 13:44:58 +07:00
2026-06-01 15:01:03 +07:00
// Notifikasi Email (Brevo)
$brevoService = app ( BrevoService :: class );
foreach ( $recipients as $recipient ) {
try {
$mail = new ExpenseRejected ( $name , $expense_number , $tanggal , $total , $url , $remarks );
$brevoService -> expenseRejected ( $recipient , $name , $mail );
} catch ( \Exception $e ) {
Log :: error ( " Failed to send email to { $recipient } : " . $e -> getMessage ());
continue ;
2025-03-05 13:44:58 +07:00
}
2025-01-23 17:46:38 +07:00
}
2024-12-23 10:10:09 +07:00
}
2025-01-16 15:56:22 +07:00
public function open ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'final_approval.open' ]);
$form = FormEntertaimentPresentation :: findOrfail ( $id );
$form -> update ([
'final_approved_at' => null ,
'final_approved_by' => null ,
'status' => 'Approved'
]);
AuditTrailHelper :: AddAuditTrail ( 'Open' , 'Open Expense at Form Entertainment Presentation (' . $form -> expense_number . ')' );
session () -> flash ( 'success' , 'Form has been opened.' );
return redirect () -> back ();
}
2026-06-01 15:01:03 +07:00
public function destroy ( $id )
{
2024-12-18 12:56:16 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.entertainment.delete' ]);
2025-01-07 17:23:42 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2024-12-18 12:56:16 +07:00
2026-06-01 15:01:03 +07:00
$form = FormEntertaimentPresentation :: findOrfail ( $id );
2025-01-07 17:23:42 +07:00
if ( $form -> status != 'On Progress' && $role == 'Medical Representatif' ) {
session () -> flash ( 'error' , 'You cannot edit this form because it has been approved or rejected.' );
return redirect () -> back ();
}
2025-01-01 17:14:46 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Delete' , 'Delete Record at Form Entertainment Presentation (' . $form -> expense_number . ')' );
2026-06-01 15:01:03 +07:00
$form -> delete ();
2024-12-18 12:56:16 +07:00
2026-06-01 15:01:03 +07:00
session () -> flash ( 'success' , 'Form has been deleted.' );
return redirect () -> back ();
}
2025-10-13 17:28:34 +07:00
protected function addAttachmentValidationRules ( \Illuminate\Validation\Validator $validator , Request $request ) : void
{
$validator -> after ( function ( $validator ) use ( $request ) {
$inputAttachments = $request -> input ( 'attachments' , []);
$fileAttachments = $request -> file ( 'attachments' , []);
foreach ( $inputAttachments as $index => $input ) {
$category = $input [ 'file_category' ] ? ? null ;
$file = $fileAttachments [ $index ][ 'file_path' ] ? ? null ;
if ( $file && ! $category ) {
$validator -> errors () -> add ( " attachments. { $index } .file_category " , 'Attachment category is required.' );
}
if ( $category && ! $file ) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'Attachment file is required.' );
continue ;
}
if ( ! $file ) {
continue ;
}
$extension = strtolower (( string ) $file -> getClientOriginalExtension ());
if ( $this -> isBlockedExtension ( $extension )) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'File type is not allowed.' );
continue ;
}
$mimeType = ( string ) $file -> getMimeType ();
if ( in_array ( $extension , $this -> imageExtensions , true ) && strpos ( $mimeType , 'image/' ) !== 0 ) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'Invalid image file.' );
}
if ( $extension === 'pdf' && $mimeType !== 'application/pdf' ) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'Invalid PDF file.' );
}
}
foreach ( $fileAttachments as $index => $files ) {
$file = $files [ 'file_path' ] ? ? null ;
if ( $file && ! isset ( $inputAttachments [ $index ])) {
$validator -> errors () -> add ( " attachments. { $index } .file_category " , 'Attachment category is required.' );
}
}
});
}
protected function buildAttachmentPayload ( Request $request , string $folderPath ) : array
{
$payload = [];
$inputs = $request -> input ( 'attachments' , []);
$files = $request -> file ( 'attachments' , []);
foreach ( $inputs as $index => $input ) {
$category = $input [ 'file_category' ] ? ? null ;
$file = $files [ $index ][ 'file_path' ] ? ? null ;
if ( ! $category || ! $file ) {
continue ;
}
$extension = strtolower (( string ) $file -> getClientOriginalExtension ());
if ( $this -> isBlockedExtension ( $extension )) {
throw ValidationException :: withMessages ([
" attachments. { $index } .file_path " => 'File type is not allowed.' ,
]);
}
$filename = Str :: uuid () . '.' . $extension ;
$filePath = rtrim ( $folderPath , '/' ) . '/' . $filename ;
$uploadResponse = NextCloudHelper :: uploadFile (
$folderPath ,
$file -> getContent (),
$filename
);
if ( is_array ( $uploadResponse ) && isset ( $uploadResponse [ 'success' ]) && $uploadResponse [ 'success' ] === false ) {
throw ValidationException :: withMessages ([
" attachments. { $index } .file_path " => $uploadResponse [ 'message' ] ? ? 'Failed to upload attachment.' ,
]);
}
$payload [] = [
'file_category' => $category ,
'file_path' => $filePath ,
];
}
return $payload ;
}
protected function formatAttachmentCollection ( FormEntertaimentPresentation $form ) : array
{
if ( ! $form -> relationLoaded ( 'attachments' )) {
$form -> load ( 'attachments' );
}
$attachments = $form -> attachments -> map ( function ( AttachmentForm $attachment ) {
if ( ! $attachment -> file_path ) {
return null ;
}
return $this -> formatAttachmentData (
$attachment -> id ,
$attachment -> file_category ,
$attachment -> file_path
);
}) -> filter () -> values () -> all ();
$hasLegacyPath = collect ( $attachments ) -> contains ( function ( $attachment ) use ( $form ) {
return isset ( $attachment [ 'file_path' ]) && $attachment [ 'file_path' ] === $form -> bukti_total ;
});
if ( $form -> bukti_total && ! $hasLegacyPath ) {
$attachments [] = $this -> formatAttachmentData ( null , 'bukti_total' , $form -> bukti_total , false );
}
return $attachments ;
}
protected function formatAttachmentData ( ? int $id , ? string $category , string $filePath , bool $canDelete = true ) : array
{
$extension = strtolower ( pathinfo ( $filePath , PATHINFO_EXTENSION ));
$previewType = $this -> determinePreviewType ( $extension );
return [
'id' => $id ,
'file_category' => $category ,
'category_label' => $this -> getCategoryLabel ( $category ),
'file_path' => $filePath ,
'download_url' => NextCloudHelper :: getFileUrl ( $filePath ),
'preview_url' => in_array ( $previewType , [ 'image' , 'pdf' ], true ) ? NextCloudHelper :: getPreviewUrl ( $filePath ) : null ,
'preview_type' => $previewType ,
'filename' => basename ( $filePath ),
'extension' => $extension ,
'can_delete' => $canDelete && ! is_null ( $id ),
];
}
protected function determinePreviewType ( ? string $extension ) : string
{
$extension = strtolower (( string ) $extension );
if ( in_array ( $extension , $this -> imageExtensions , true )) {
return 'image' ;
}
if ( $extension === 'pdf' ) {
return 'pdf' ;
}
return 'other' ;
}
protected function isBlockedExtension ( ? string $extension ) : bool
{
return in_array ( strtolower (( string ) $extension ), $this -> blockedExtensions , true );
}
protected function getAttachmentDownloadUrlByCategory ( array $attachments , string $category ) : ? string
{
foreach ( $attachments as $attachment ) {
if (( $attachment [ 'file_category' ] ? ? null ) === $category ) {
return $attachment [ 'download_url' ] ? ? null ;
}
}
return null ;
}
protected function getCategoryLabel ( ? string $category ) : string
{
if ( ! $category ) {
return 'Lampiran' ;
}
if ( isset ( $this -> attachmentCategories [ $category ])) {
return $this -> attachmentCategories [ $category ];
}
return ucwords ( str_replace ( '_' , ' ' , $category ));
}
2026-06-01 15:01:03 +07:00
}