2024-12-20 11:07:07 +07:00
< ? php
namespace App\Http\Controllers\Forms ;
use Illuminate\Http\Request ;
use App\Http\Controllers\Controller ;
use App\Models\FormOthers ;
use App\Models\Region ;
use App\Models\UserHasCabang ;
use App\Models\Kategori ;
use Illuminate\Support\Str ;
use App\Helpers\NextCloudHelper ;
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-03-04 12:24:36 +07:00
use App\Models\Cabang ;
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 16:42:05 +07:00
use App\Services\AttachmentService ;
use App\Enums\AttachmentTableName ;
use App\Models\AttachmentForm ;
use Illuminate\Support\Facades\DB ;
use Illuminate\Support\Facades\Validator ;
use Illuminate\Validation\ValidationException ;
2025-05-05 19:40:38 +07:00
use Carbon\Carbon ;
2024-12-20 11:07:07 +07:00
class FormOtherController extends Controller
{
2025-10-13 16:42:05 +07:00
protected AttachmentService $attachmentService ;
2025-10-23 17:21:46 +07:00
/**
* Kategori yang tidak boleh dipilih pada Form Others.
*
* @var array<int,string>
*/
protected array $excludedKategoriNames = [
'Up Country' ,
'Vehicle Running Cost' ,
'Entertainment' ,
'Meeting / Seminar' ,
'Presentation' ,
'Sponsorship' ,
'Cash in Bank BCA' ,
'Donasi/Gift' ,
'PPn Masukan' ,
'Vehicle Maintenance' ,
'Bahan Promosi' ,
];
2025-10-13 16:42:05 +07:00
protected array $attachmentCategories = [
'bukti_total' ,
];
protected array $blockedExtensions = [ 'exe' , 'dll' , 'sh' , 'bat' , 'cmd' , 'msi' ];
public function __construct ( AttachmentService $attachmentService )
{
$this -> attachmentService = $attachmentService ;
view () -> share ( 'otherAttachmentCategories' , $this -> attachmentCategories );
}
2024-12-20 11:07:07 +07:00
public function index ()
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.view' ]);
2024-12-26 18:37:34 +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
2025-06-19 19:39:20 +07:00
// Referensi waktu sekarang
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 = FormOthers :: whereBetween ( 'tanggal' , [ $dataRetrievalStartDate , $currentPeriodClosingDate ])
2025-01-12 13:34:35 +07:00
-> orderBy ( 'tanggal' , 'desc' )
2025-01-18 15:01:28 +07:00
-> with ( 'user' )
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 ;
2025-01-12 13:34:35 +07:00
2025-05-25 15:14:23 +07:00
if ( $cabang_id ) {
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang_id , $periodeMonth , $periodeYear );
}
2024-12-23 10:10:09 +07:00
}
2025-03-04 13:22:45 +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.other' ));
2025-05-25 15:14:23 +07:00
return view ( 'backend.pages.forms.other.index' , [
'forms' => $forms ,
2025-10-12 20:47:43 +07:00
'availableBudget' => $availableBudget ,
'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-20 11:07:07 +07:00
2025-01-06 16:00:51 +07:00
public function detail ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.view' ]);
2025-10-13 16:42:05 +07:00
$form = FormOthers :: with ([ 'user' , 'kategori' , 'attachments' ]) -> findOrFail ( $id );
$attachments = $this -> formatAttachmentCollection ( $form );
2025-01-06 16:00:51 +07:00
$form -> bukti_total = $form -> bukti_total ? NextCloudHelper :: getFileUrl ( $form -> bukti_total ) : null ;
2025-10-13 16:42:05 +07:00
return response () -> json ( array_merge ( $form -> toArray (), [
'attachments' => $attachments ,
]));
2025-01-06 16:00:51 +07:00
}
public function view ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.view' ]);
2025-10-13 16:42:05 +07:00
$form = FormOthers :: with ([ 'user' , 'kategori' , 'attachments' ]) -> findOrFail ( $id );
$attachments = $this -> formatAttachmentCollection ( $form );
2025-01-06 16:00:51 +07:00
$form -> bukti_total = $form -> bukti_total ? NextCloudHelper :: getFileUrl ( $form -> bukti_total ) : null ;
return view ( 'backend.pages.forms.other.view' , [
'form' => $form ,
2025-10-23 17:21:46 +07:00
'kategori' => Kategori :: whereNotIn ( 'name' , $this -> excludedKategoriNames ) -> get (),
2025-10-13 16:42:05 +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-20 11:07:07 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.create' ]);
2026-06-01 15:01:03 +07:00
return view ( 'backend.pages.forms.other.create' , [
2025-10-23 17:21:46 +07:00
'kategori' => Kategori :: whereNotIn ( 'name' , $this -> excludedKategoriNames ) -> get (),
2025-10-13 16:42:05 +07:00
'attachmentCategories' => $this -> attachmentCategories ,
2024-12-20 11:07:07 +07:00
]);
2026-06-01 15:01:03 +07:00
}
2024-12-20 11:07:07 +07:00
2026-06-01 15:01:03 +07:00
public function store ( Request $request )
{
2024-12-20 11:07:07 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.create' ]);
2025-10-13 16:42:05 +07:00
$validator = Validator :: make ( $request -> all (), [
2024-12-20 11:07:07 +07:00
'kategori_id' => 'required' ,
'tanggal' => 'required|date' ,
'keterangan' => 'required' ,
2025-01-19 14:57:01 +07:00
'total' => 'required|numeric|min:1' ,
2025-10-13 16:42:05 +07:00
'attachments' => 'nullable|array' ,
'attachments.*.file_category' => 'required|string|in:' . implode ( ',' , $this -> attachmentCategories ),
'attachments.*.file_path' => [
'nullable' ,
'file' ,
'max:10240' ,
2026-06-01 15:01:03 +07:00
'mimetypes:image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain,text/csv,application/zip,application/x-zip-compressed,application/x-7z-compressed,application/vnd.rar,application/octet-stream'
2025-10-13 16:42:05 +07:00
],
2024-12-20 11:07:07 +07:00
]);
2025-10-13 16:42:05 +07:00
$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 ( $category && ! $file ) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'Attachment file is required.' );
continue ;
}
if ( $file && $this -> isBlockedExtension ( $file -> getClientOriginalExtension ())) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'File type is not allowed.' );
}
}
});
$validator -> validate ();
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
$now = Carbon :: now ();
2026-06-01 15:01:03 +07:00
// Tentukan periode aktif berdasarkan tanggal saat ini
2025-05-05 19:40:38 +07:00
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
// Validasi apakah tanggal pengajuan masuk ke periode input saat ini
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-01-18 15:01:28 +07:00
}
2025-05-25 15:14:23 +07:00
// Ambil cabang user
$cabang = UserHasCabang :: with ( 'cabang' ) -> where ( 'user_id' , auth () -> user () -> id ) -> first () ? -> cabang ;
if ( ! $cabang ) {
session () -> flash ( 'error' , 'Data cabang tidak ditemukan.' );
return redirect () -> back ();
}
2026-06-01 15:01:03 +07:00
// Tentukan periode budget berdasarkan tanggal input
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' );
// Cek budget
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang -> id , $periodeMonth , $periodeYear );
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' );
2026-06-01 15:01:03 +07:00
return redirect () -> back () -> withInput ();
2025-01-19 14:57:01 +07:00
}
2024-12-20 11:07:07 +07:00
$region = Region :: where ( 'id' , $cabang -> region_id ) -> first ();
$kategori = Kategori :: where ( 'id' , $request -> kategori_id ) -> first ();
$folderPath = '/expense/' . $region -> code . '/' . $cabang -> code . '/other' ;
2025-10-13 16:42:05 +07:00
$attachmentsPayload = $this -> buildAttachmentPayload ( $request , $folderPath );
$primaryAttachmentPath = $attachmentsPayload [ 0 ][ 'file_path' ] ? ? null ;
2024-12-20 11:07:07 +07:00
// Generate sequence number and expense number
2026-06-01 15:01:03 +07:00
$sequence_number = FormOthers :: withTrashed () -> where ( 'expense_number' , 'like' , $region -> code . '-' . $cabang -> code . '-OTH-' . date ( 'ym' ) . '%' ) -> count () + 1 ;
2024-12-20 11:07:07 +07:00
$formatted_sequence_number = str_pad ( $sequence_number , 4 , '0' , STR_PAD_LEFT );
$expense_number = $region -> code . '-' . $cabang -> code . '-OTH-' . date ( 'ym' ) . $formatted_sequence_number ;
2025-10-13 16:42:05 +07:00
DB :: beginTransaction ();
try {
$form = FormOthers :: 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 16:42:05 +07:00
'kategori_id' => $request -> kategori_id ,
'tanggal' => $request -> tanggal ,
'keterangan' => $request -> keterangan ,
'total' => $request -> total ,
'bukti_total' => $primaryAttachmentPath ,
'account_number' => $kategori -> account_number ,
'status' => 'On Progress' ,
]);
if ( ! empty ( $attachmentsPayload )) {
$this -> attachmentService -> addMultipleAttachments (
$form -> id ,
AttachmentTableName :: FORM_OTHERS ,
$attachmentsPayload
);
}
DB :: commit ();
} catch ( \Throwable $th ) {
DB :: rollBack ();
Log :: error ( 'Failed to store Form Others' , [
'message' => $th -> getMessage (),
'trace' => $th -> getTraceAsString (),
]);
session () -> flash ( 'error' , 'Terjadi kesalahan saat menyimpan data, silakan coba lagi.' );
return redirect () -> back () -> withInput ();
}
$form -> load ( 'user' );
2024-12-20 11:07:07 +07:00
2025-01-09 13:22:56 +07:00
$name = auth () -> user () -> name ;
$tanggal = $request -> tanggal ;
$total = $request -> 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 ) {
if ( $role === 'Marketing Information System' ) {
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-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
2025-02-16 20:18:36 +07:00
$url = route ( 'forms.other.view' , $form -> id );
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-17 16:17:53 +07:00
// Send notification to the user
2025-01-23 17:46:38 +07:00
NotificationHelper :: newExpense ( 'other' , $form -> id , $form -> user_id , $expense_number , $receiver_ids );
2025-01-17 16:17:53 +07:00
2025-01-09 13:22:56 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Insert' , 'Insert New Expense at Form Other (' . $expense_number . ')' );
2025-02-15 13:51:03 +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-20 11:07:07 +07:00
2026-06-01 15:01:03 +07:00
public function edit ( $id )
{
2024-12-20 11:07:07 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.edit' ]);
2025-01-07 17:23:42 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2024-12-20 11:07:07 +07:00
2025-10-13 16:42:05 +07:00
$form = FormOthers :: 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 ();
}
2026-06-01 15:01:03 +07:00
return view ( 'backend.pages.forms.other.edit' , [
2024-12-20 11:07:07 +07:00
'form' => $form ,
2025-10-23 17:21:46 +07:00
'kategori' => Kategori :: whereNotIn ( 'name' , $this -> excludedKategoriNames ) -> get (),
2025-10-13 16:42:05 +07:00
'attachments' => $this -> formatAttachmentCollection ( $form ),
'attachmentCategories' => $this -> attachmentCategories ,
]);
2026-06-01 15:01:03 +07:00
}
2024-12-20 11:07:07 +07:00
2026-06-01 15:01:03 +07:00
public function update ( Request $request , $id )
2025-05-25 15:14:23 +07:00
{
2024-12-20 11:07:07 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.edit' ]);
2025-01-07 17:23:42 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2025-10-13 16:42:05 +07:00
$form = FormOthers :: with ( 'attachments' ) -> findOrFail ( $id );
2025-05-25 15:14:23 +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 ();
}
$cabang = UserHasCabang :: with ( 'cabang' ) -> where ( 'user_id' , $form -> user_id ) -> first () ? -> cabang ;
if ( ! $cabang ) {
session () -> flash ( 'error' , 'Cabang tidak ditemukan.' );
return redirect () -> back ();
}
2024-12-20 11:07:07 +07:00
2025-05-25 15:14:23 +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-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 ();
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 ();
} else {
$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
// Validasi apakah tanggal pengajuan masuk ke periode input saat ini
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 ();
}
// Hitung periode budget dari tanggal input
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' );
// Ambil available budget
$availableBudget = BudgetHelper :: getAvailableBudget ( $cabang -> id , $periodeMonth , $periodeYear );
2025-10-13 16:42:05 +07:00
$validator = Validator :: make ( $request -> all (), [
2024-12-20 11:07:07 +07:00
'kategori_id' => 'required' ,
'tanggal' => 'required|date' ,
'keterangan' => 'required' ,
2025-05-25 15:14:23 +07:00
'total' => 'required|numeric|min:1|max:' . $availableBudget ,
2025-10-13 16:42:05 +07:00
'attachments' => 'nullable|array' ,
'attachments.*.file_category' => 'required|string|in:' . implode ( ',' , $this -> attachmentCategories ),
'attachments.*.file_path' => [
'nullable' ,
'file' ,
'max:10240' ,
2026-06-01 15:01:03 +07:00
'mimetypes:image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain,text/csv,application/zip,application/x-zip-compressed,application/x-7z-compressed,application/vnd.rar,application/octet-stream'
2025-10-13 16:42:05 +07:00
],
2024-12-20 11:07:07 +07:00
]);
2025-10-13 16:42:05 +07:00
$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 ( $category && ! $file ) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'Attachment file is required.' );
continue ;
}
if ( $file && $this -> isBlockedExtension ( $file -> getClientOriginalExtension ())) {
$validator -> errors () -> add ( " attachments. { $index } .file_path " , 'File type is not allowed.' );
}
}
});
$validator -> validate ();
2025-05-25 15:14:23 +07:00
$region = Region :: find ( $cabang -> region_id );
$kategori = Kategori :: find ( $request -> kategori_id );
2024-12-20 11:07:07 +07:00
$folderPath = '/expense/' . $region -> code . '/' . $cabang -> code . '/other' ;
2025-10-13 16:42:05 +07:00
$attachmentsPayload = $this -> buildAttachmentPayload ( $request , $folderPath );
2026-06-01 15:01:03 +07:00
$primaryAttachmentPath = ! empty ( $attachmentsPayload ) ? $attachmentsPayload [ 0 ][ 'file_path' ] : null ;
2025-10-13 16:42:05 +07:00
DB :: beginTransaction ();
try {
$form -> update ([
2026-06-01 15:01:03 +07:00
'cabang_id' => $cabang -> id , // <--- REPARASI SUNTIK CABANG_ID TERBARU
2025-10-13 16:42:05 +07:00
'kategori_id' => $request -> kategori_id ,
'tanggal' => $request -> tanggal ,
'keterangan' => $request -> keterangan ,
'total' => $request -> total ,
'bukti_total' => $primaryAttachmentPath ? ? $form -> bukti_total ,
'account_number' => $kategori -> account_number ,
]);
if ( ! empty ( $attachmentsPayload )) {
$this -> attachmentService -> addMultipleAttachments (
$form -> id ,
AttachmentTableName :: FORM_OTHERS ,
$attachmentsPayload
);
}
2024-12-20 11:07:07 +07:00
2025-10-13 16:42:05 +07:00
DB :: commit ();
} catch ( \Throwable $th ) {
DB :: rollBack ();
Log :: error ( 'Failed to update Form Others' , [
'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-20 11:07:07 +07:00
}
2025-01-09 13:22:56 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Update' , 'Update Record at Form Other (' . $form -> expense_number . ')' );
2024-12-20 11:07:07 +07:00
session () -> flash ( 'success' , 'Form has been updated.' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
2025-05-25 15:14:23 +07:00
}
2024-12-20 11:07:07 +07:00
2025-10-13 16:42:05 +07:00
public function deleteAttachment ( Request $request , $formId , $attachmentId )
{
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.edit' ]);
$form = FormOthers :: 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_OTHERS )
-> first ();
if ( ! $attachment ) {
return response () -> json ([ 'message' => 'Attachment not found.' ], 404 );
}
$this -> attachmentService -> deleteAttachment ( $attachment -> id );
if ( $form -> bukti_total === $attachment -> file_path ) {
$replacement = AttachmentForm :: where ( 'form_id' , $form -> id )
-> where ( 'table_name' , AttachmentTableName :: FORM_OTHERS )
-> whereNull ( 'deleted_at' )
-> orderBy ( 'id' )
-> first ();
$form -> update ([ 'bukti_total' => $replacement -> file_path ? ? null ]);
}
AuditTrailHelper :: AddAuditTrail ( 'Delete Attachment' , 'Delete Attachment at Form Other (' . $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 = FormOthers :: 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' ];
2024-12-26 13:40:17 +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 = [];
2024-12-24 15:12:24 +07:00
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' ) {
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-24 15:12:24 +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
}
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
2025-01-06 16:00:51 +07:00
$url = route ( 'forms.other.view' , $form -> id );
2025-01-16 15:56:22 +07:00
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 ( 'other' , $form -> id , $form -> user_id , $expense_number , $receiver_ids );
2025-01-17 16:17:53 +07:00
2025-01-09 13:22:56 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Approve' , 'Approve Expense at Form Other (' . $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
}
2025-01-16 15:56:22 +07:00
public function approve2 ( $id )
{
$this -> checkAuthorization ( auth () -> user (), [ 'approval2.approve' ]);
$form = FormOthers :: 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-16 15:56:22 +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 15:56:22 +07:00
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-01-16 15:56:22 +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
});
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.other.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 ( 'other' , $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 Other (' . $form -> expense_number . ')' );
session () -> flash ( 'success' , 'Form has been approved.' );
return redirect () -> back ();
}
2026-06-01 15:01:03 +07:00
// <--- REPARASI SUNTIKAN OBJECT REQUEST HASIL CHECKLIST JAVASCRIPT MODAL DILUAR
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 = FormOthers :: 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 ();
}
// Hitung periode dari tanggal form
$tanggal = Carbon :: parse ( $form -> tanggal );
$startDay = ( int ) env ( 'STARTING_DATE' , 11 );
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 );
// Ambil sisa budget dan sinkronkan validasinya dengan nominal dinamis terbaru
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 ) {
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 NOMINAL BARU HASIL CHECKLIST DARI JAVASCRIPT KE DB
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 ; // <--- SEKARANG MENGGUNAKAN DATA UPDATE TERBARU
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.other.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
AuditTrailHelper :: AddAuditTrail ( 'Final Approve' , 'Final Approve Expense at Form Other (' . $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
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
]);
2024-12-23 10:10:09 +07:00
$form = FormOthers :: 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
$url = route ( 'forms.others.view' , $form -> id );
2025-10-26 14:14:06 +07:00
WhatsappHelper :: rejectExpense ( $phoneNumbers , $expense_number , $url , $tanggal , $total , $name , $request -> remarks );
2024-12-25 13:37:49 +07:00
2025-03-05 13:44:58 +07:00
$brevoService = app ( BrevoService :: class );
2025-01-23 17:46:38 +07:00
foreach ( $recipients as $recipient ) {
2026-06-01 15:01:03 +07:00
try {
$mail = new ExpenseRejected ( $name , $expense_number , $tanggal , $total , $url , $request -> 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
}
2025-01-09 13:22:56 +07:00
2026-06-01 15:01:03 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Reject' , 'Reject Expense at Form Others (' . $form -> expense_number . ') oleh ' . auth () -> user () -> name );
2024-12-23 10:10:09 +07:00
session () -> flash ( 'success' , 'Form has been rejected.' );
2024-12-23 10:42:27 +07:00
return redirect () -> back ();
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 = FormOthers :: findOrfail ( $id );
$form -> update ([
'final_approved_at' => null ,
'final_approved_by' => null ,
'status' => 'Approved'
]);
AuditTrailHelper :: AddAuditTrail ( 'Open' , 'Open Expense at Form Other (' . $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-20 11:07:07 +07:00
$this -> checkAuthorization ( auth () -> user (), [ 'forms.other.delete' ]);
2025-01-07 17:23:42 +07:00
$role = auth () -> user () -> getRoleNames ()[ 0 ];
2024-12-20 11:07:07 +07:00
2026-06-01 15:01:03 +07:00
$form = FormOthers :: 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-09 13:22:56 +07:00
AuditTrailHelper :: AddAuditTrail ( 'Delete' , 'Delete Record at Form Other (' . $form -> expense_number . ')' );
2025-01-01 17:14:46 +07:00
2026-06-01 15:01:03 +07:00
$form -> delete ();
2024-12-20 11:07:07 +07:00
2026-06-01 15:01:03 +07:00
session () -> flash ( 'success' , 'Form has been deleted.' );
return redirect () -> back ();
}
2025-10-13 16:42:05 +07:00
protected function formatAttachmentCollection ( FormOthers $form ) : array
{
$attachments = $form -> attachments -> map ( function ( $attachment ) {
return $this -> formatAttachmentData (
$attachment -> id ,
$attachment -> file_category ,
$attachment -> file_path
);
});
if ( $attachments -> isEmpty () && $form -> bukti_total ) {
$attachments -> push ( $this -> formatAttachmentData ( null , 'bukti_total' , $form -> bukti_total ));
}
return $attachments -> values () -> all ();
}
protected function formatAttachmentData ( ? int $id , ? string $category , string $filePath ) : array
{
$extension = strtolower ( pathinfo ( $filePath , PATHINFO_EXTENSION ));
$previewType = $this -> determinePreviewType ( $extension );
return [
'id' => $id ,
'file_category' => $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' => ! is_null ( $id ),
];
}
protected function determinePreviewType ( ? string $extension ) : string
{
$extension = strtolower (( string ) $extension );
$imageExtensions = [ 'jpg' , 'jpeg' , 'png' , 'gif' , 'bmp' , 'webp' ];
if ( in_array ( $extension , $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 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 ( $file -> getClientOriginalExtension ());
if ( $this -> isBlockedExtension ( $extension )) {
throw ValidationException :: withMessages ([
" attachments. { $index } .file_path " => 'File type is not allowed.' ,
]);
}
$filename = Str :: uuid () . '.' . $extension ;
$filePath = $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 ;
}
2026-06-01 15:01:03 +07:00
}