feat(cooperation): refactor workflow and implement payment system
- Refactored cooperation workflow to include manual status transitions. - Implemented payment tracking with CooperationPayment model and PayAction. - Updated UpdateCooperationStatusCommand for automatic assignment and verification transitions. - Removed deprecated date columns (assignment_deadline, verification_deadline). - Enhanced Filament UI with new actions (Complete, Proceed to Payment, Pay). - Added CooperationSeeder for testing purposes.
This commit is contained in:
parent
ad6335542e
commit
1d52463286
@ -40,7 +40,7 @@ public function handle()
|
|||||||
// 1. PENDING -> ASSIGNMENT (final_submission_date)
|
// 1. PENDING -> ASSIGNMENT (final_submission_date)
|
||||||
// Automatically reject media that haven't responded
|
// Automatically reject media that haven't responded
|
||||||
$rejectedPendingMedia = CooperationMedia::whereHas('cooperation', function ($query) use ($today) {
|
$rejectedPendingMedia = CooperationMedia::whereHas('cooperation', function ($query) use ($today) {
|
||||||
$query->pending()->whereDate('final_submission_date', '<=', $today);
|
$query->pending()->whereDate('final_submission_date', '<', $today);
|
||||||
})
|
})
|
||||||
->pending()
|
->pending()
|
||||||
->update(['status' => ApprovalStatus::REJECTED]);
|
->update(['status' => ApprovalStatus::REJECTED]);
|
||||||
@ -50,53 +50,37 @@ public function handle()
|
|||||||
}
|
}
|
||||||
|
|
||||||
$updatedPending = Cooperation::pending()
|
$updatedPending = Cooperation::pending()
|
||||||
->whereDate('final_submission_date', '<=', $today)
|
->whereDate('final_submission_date', '<', $today)
|
||||||
->update(['status' => CooperationStatus::ASSIGNMENT]);
|
->update(['status' => CooperationStatus::ASSIGNMENT]);
|
||||||
|
|
||||||
if ($updatedPending > 0) {
|
if ($updatedPending > 0) {
|
||||||
Log::info("Updated {$updatedPending} cooperations from PENDING to ASSIGNMENT.");
|
Log::info("Updated {$updatedPending} cooperations from PENDING to ASSIGNMENT.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. ASSIGNMENT -> VERIFICATION (assignment_deadline)
|
// 2. ASSIGNMENT -> VERIFICATION (taskAssignment->end_date)
|
||||||
// Automatically reject media that haven't responded
|
// Automatically reject media that haven't responded to the invitation
|
||||||
$rejectedMedia = CooperationMedia::whereHas('cooperation', function ($query) use ($today) {
|
$rejectedMedia = CooperationMedia::whereHas('cooperation', function ($query) use ($today) {
|
||||||
$query->assignment()->whereDate('assignment_deadline', '<=', $today);
|
$query->assignment()->whereHas('taskAssignment', function ($subQuery) use ($today) {
|
||||||
|
$subQuery->whereDate('end_date', '<', $today);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
->pending()
|
->pending()
|
||||||
->update(['status' => ApprovalStatus::REJECTED]);
|
->update(['status' => ApprovalStatus::REJECTED]);
|
||||||
|
|
||||||
if ($rejectedMedia > 0) {
|
if ($rejectedMedia > 0) {
|
||||||
Log::info("Updated {$rejectedMedia} media assignments to REJECTED because they didn't respond before the deadline.");
|
Log::info("Updated {$rejectedMedia} media assignments to REJECTED because they didn't respond before the task deadline.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$updatedAssignment = Cooperation::assignment()
|
$updatedAssignment = Cooperation::where('status', CooperationStatus::ASSIGNMENT)
|
||||||
->whereDate('assignment_deadline', '<=', $today)
|
->whereHas('taskAssignment', function ($query) use ($today) {
|
||||||
|
$query->whereDate('end_date', '<', $today);
|
||||||
|
})
|
||||||
->update(['status' => CooperationStatus::VERIFICATION]);
|
->update(['status' => CooperationStatus::VERIFICATION]);
|
||||||
|
|
||||||
if ($updatedAssignment > 0) {
|
if ($updatedAssignment > 0) {
|
||||||
Log::info("Updated {$updatedAssignment} cooperations from ASSIGNMENT to VERIFICATION.");
|
Log::info("Updated {$updatedAssignment} cooperations from ASSIGNMENT to VERIFICATION.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. VERIFICATION -> PAYMENT (verification_deadline)
|
|
||||||
// Automatically reject media that haven't responded
|
|
||||||
$rejectedVerificationMedia = CooperationMedia::whereHas('cooperation', function ($query) use ($today) {
|
|
||||||
$query->verification()->whereDate('verification_deadline', '<=', $today);
|
|
||||||
})
|
|
||||||
->pending()
|
|
||||||
->update(['status' => ApprovalStatus::REJECTED]);
|
|
||||||
|
|
||||||
if ($rejectedVerificationMedia > 0) {
|
|
||||||
Log::info("Updated {$rejectedVerificationMedia} media in VERIFICATION cooperations to REJECTED because they didn't respond before verification_deadline.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$updatedVerification = Cooperation::verification()
|
|
||||||
->whereDate('verification_deadline', '<=', $today)
|
|
||||||
->update(['status' => CooperationStatus::PAYMENT]);
|
|
||||||
|
|
||||||
if ($updatedVerification > 0) {
|
|
||||||
Log::info("Updated {$updatedVerification} cooperations from VERIFICATION to PAYMENT.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('Cooperation statuses updated successfully.');
|
$this->info('Cooperation statuses updated successfully.');
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Manage\Cooperations\Actions\Cooperation;
|
||||||
|
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Filament\Support\CheerfulNotification;
|
||||||
|
use App\Models\Cooperation;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
|
||||||
|
class CompleteCooperationAction extends Action
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->label('Selesaikan Kerjasama')
|
||||||
|
->icon(Heroicon::CheckBadge)
|
||||||
|
->color('success')
|
||||||
|
->requiresConfirmation()
|
||||||
|
->modalHeading('Selesaikan Kerja Sama')
|
||||||
|
->modalDescription('Apakah Anda yakin ingin menyelesaikan kerja sama ini? Pastikan semua pembayaran kepada media yang bekerja sama telah lunas.')
|
||||||
|
->action(function (Cooperation $record): void {
|
||||||
|
// Verify all accepted media are paid
|
||||||
|
$acceptedMedia = $record->cooperationMedia()->accepted()->get();
|
||||||
|
|
||||||
|
if ($acceptedMedia->isEmpty()) {
|
||||||
|
CheerfulNotification::danger(
|
||||||
|
'Tidak Ada Media! ❌',
|
||||||
|
'Tidak ada media yang menerima kerja sama ini. Tidak dapat diselesaikan.'
|
||||||
|
)->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unpaidMedia = $acceptedMedia->filter(function ($media) {
|
||||||
|
return ! $media->payment()->exists();
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($unpaidMedia->isNotEmpty()) {
|
||||||
|
CheerfulNotification::danger(
|
||||||
|
'Pembayaran Belum Lunas! ⚠️',
|
||||||
|
'Masih ada media yang belum dibayar. Harap selesaikan pembayaran untuk semua media yang menerima kerja sama.'
|
||||||
|
)->send();
|
||||||
|
|
||||||
|
$this->halt();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total payment from media payments
|
||||||
|
$totalPayment = $acceptedMedia->sum(fn ($media) => $media->payment->amount);
|
||||||
|
|
||||||
|
$record->update([
|
||||||
|
'payment_amount' => $totalPayment,
|
||||||
|
'payment_date' => now(),
|
||||||
|
'status' => CooperationStatus::COMPLETED,
|
||||||
|
]);
|
||||||
|
|
||||||
|
CheerfulNotification::success(
|
||||||
|
'Kerja Sama Selesai! 🎉✨',
|
||||||
|
'Alhamdulillah! Kerja sama ini telah resmi diselesaikan. Terima kasih atas kerja samanya! 🙌😊'
|
||||||
|
)->send();
|
||||||
|
})
|
||||||
|
->visible(function (Cooperation $record): bool {
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user && ! $user->hasRole('Perusahaan')
|
||||||
|
&& $record->status === CooperationStatus::PAYMENT;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,7 +14,6 @@
|
|||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Support\Enums\Width;
|
use Filament\Support\Enums\Width;
|
||||||
use Filament\Support\Icons\Heroicon;
|
use Filament\Support\Icons\Heroicon;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class CreateTaskAssignmentAction extends Action
|
class CreateTaskAssignmentAction extends Action
|
||||||
{
|
{
|
||||||
@ -34,8 +33,7 @@ protected function setUp(): void
|
|||||||
->displayFormat('l, d F Y')
|
->displayFormat('l, d F Y')
|
||||||
->locale('id')
|
->locale('id')
|
||||||
->required()
|
->required()
|
||||||
->minDate($record->final_submission_date)
|
->minDate($record->final_submission_date),
|
||||||
->autofocus(),
|
|
||||||
|
|
||||||
DatePicker::make('end_date')
|
DatePicker::make('end_date')
|
||||||
->label('Tanggal Selesai')
|
->label('Tanggal Selesai')
|
||||||
@ -103,8 +101,7 @@ protected function setUp(): void
|
|||||||
->modalHeading('Buat Penugasan')
|
->modalHeading('Buat Penugasan')
|
||||||
->modalSubmitActionLabel('Simpan')
|
->modalSubmitActionLabel('Simpan')
|
||||||
->visible(function (Cooperation $record): bool {
|
->visible(function (Cooperation $record): bool {
|
||||||
|
$user = auth()->user();
|
||||||
$user = Auth::user();
|
|
||||||
|
|
||||||
return $user && ! $user->hasRole('Perusahaan')
|
return $user && ! $user->hasRole('Perusahaan')
|
||||||
&& ! $record->taskAssignment()->exists()
|
&& ! $record->taskAssignment()->exists()
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Manage\Cooperations\Actions\Cooperation;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Filament\Support\CheerfulNotification;
|
||||||
|
use App\Models\Cooperation;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
|
||||||
|
class ProceedToPaymentAction extends Action
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->label('Lanjutkan ke Pembayaran')
|
||||||
|
->icon(Heroicon::Banknotes)
|
||||||
|
->color('success')
|
||||||
|
->requiresConfirmation()
|
||||||
|
->modalHeading('Lanjutkan ke Pembayaran?')
|
||||||
|
->modalDescription('Apakah Anda yakin ingin melanjutkan kerja sama ini ke tahap pembayaran? Pastikan semua laporan telah diverifikasi.')
|
||||||
|
->action(function (Cooperation $record): void {
|
||||||
|
$record->update(['status' => CooperationStatus::PAYMENT]);
|
||||||
|
|
||||||
|
CheerfulNotification::success(
|
||||||
|
'Tahap Pembayaran Dimulai! 💸✨',
|
||||||
|
'Kerja sama telah dipindahkan ke tahap pembayaran. Silakan proses pembayarannya ya! 😊'
|
||||||
|
)->send();
|
||||||
|
})
|
||||||
|
->visible(function (Cooperation $record): bool {
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
if (! $user || $user->hasRole('Perusahaan') || $record->status !== CooperationStatus::VERIFICATION) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if all reports are verified (either Accepted or Rejected, not Pending)
|
||||||
|
$reports = $record->reports;
|
||||||
|
|
||||||
|
if ($reports->isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reports->every(fn ($report) => $report->status !== ApprovalStatus::PENDING);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,7 +48,6 @@ protected function setUp(): void
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
->visible(function (Cooperation $record): bool {
|
->visible(function (Cooperation $record): bool {
|
||||||
|
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
if (! $user || ! $user->hasRole('Perusahaan')) {
|
if (! $user || ! $user->hasRole('Perusahaan')) {
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
use Filament\Support\Enums\Width;
|
use Filament\Support\Enums\Width;
|
||||||
use Filament\Support\Icons\Heroicon;
|
use Filament\Support\Icons\Heroicon;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class SendProposalAction extends Action
|
class SendProposalAction extends Action
|
||||||
@ -55,19 +54,19 @@ protected function setUp(): void
|
|||||||
$cooperationMedia = $record->cooperationMedia()
|
$cooperationMedia = $record->cooperationMedia()
|
||||||
->whereHas('partnerMedia', function (Builder $query): void {
|
->whereHas('partnerMedia', function (Builder $query): void {
|
||||||
$query->whereHas('company', function (Builder $subQuery): void {
|
$query->whereHas('company', function (Builder $subQuery): void {
|
||||||
$subQuery->where('user_id', Auth::id());
|
$subQuery->where('user_id', auth()->id());
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($cooperationMedia) {
|
if ($cooperationMedia) {
|
||||||
$existingProposal = $record->proposals()
|
$existingProposal = $record->proposal()
|
||||||
->where('partner_media_id', $cooperationMedia->partner_media_id)
|
->where('partner_media_id', $cooperationMedia->partner_media_id)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
$isRevision = (bool) $existingProposal;
|
$isRevision = (bool) $existingProposal;
|
||||||
|
|
||||||
$proposal = $record->proposals()->create([
|
$proposal = $record->proposal()->create([
|
||||||
'partner_media_id' => $cooperationMedia->partner_media_id,
|
'partner_media_id' => $cooperationMedia->partner_media_id,
|
||||||
'description' => $data['description'],
|
'description' => $data['description'],
|
||||||
'e_catalog' => $data['e_catalog'],
|
'e_catalog' => $data['e_catalog'],
|
||||||
@ -99,8 +98,8 @@ protected function setUp(): void
|
|||||||
User::superAdmin()
|
User::superAdmin()
|
||||||
->get()
|
->get()
|
||||||
->each(function ($admin) use ($isRevision, $record, $cooperationMedia): void {
|
->each(function ($admin) use ($isRevision, $record, $cooperationMedia): void {
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
$user = Auth::user();
|
|
||||||
$admin->notify(new BroadcastNotification([
|
$admin->notify(new BroadcastNotification([
|
||||||
'title' => $isRevision ? 'Pembaruan Proposal Kerja Sama ✨' : 'Ada Proposal Kerja Sama Baru! 🚀',
|
'title' => $isRevision ? 'Pembaruan Proposal Kerja Sama ✨' : 'Ada Proposal Kerja Sama Baru! 🚀',
|
||||||
'body' => $isRevision
|
'body' => $isRevision
|
||||||
@ -118,8 +117,7 @@ protected function setUp(): void
|
|||||||
->modalHeading('Proposal Kerja Sama')
|
->modalHeading('Proposal Kerja Sama')
|
||||||
->modalSubmitActionLabel('Kirim Proposal')
|
->modalSubmitActionLabel('Kirim Proposal')
|
||||||
->visible(function (Cooperation $record): bool {
|
->visible(function (Cooperation $record): bool {
|
||||||
|
$user = auth()->user();
|
||||||
$user = Auth::user();
|
|
||||||
|
|
||||||
if (! $user->hasRole('Perusahaan')) {
|
if (! $user->hasRole('Perusahaan')) {
|
||||||
return false;
|
return false;
|
||||||
@ -128,15 +126,14 @@ protected function setUp(): void
|
|||||||
$cooperationMedia = $record->cooperationMedia()
|
$cooperationMedia = $record->cooperationMedia()
|
||||||
->whereHas('partnerMedia', function (Builder $query): void {
|
->whereHas('partnerMedia', function (Builder $query): void {
|
||||||
$query->whereHas('company', function (Builder $subQuery): void {
|
$query->whereHas('company', function (Builder $subQuery): void {
|
||||||
$subQuery->where('user_id', Auth::id());
|
$subQuery->where('user_id', auth()->id());
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
return $cooperationMedia
|
return $cooperationMedia
|
||||||
&& $cooperationMedia->status === ApprovalStatus::ACCEPTED
|
&& $cooperationMedia->status === ApprovalStatus::ACCEPTED
|
||||||
&& $record->initial_submission_date->toDateString() === now()->toDateString()
|
&& ! $record->proposal()
|
||||||
&& ! $record->proposals()
|
|
||||||
->where('partner_media_id', $cooperationMedia->partner_media_id)
|
->where('partner_media_id', $cooperationMedia->partner_media_id)
|
||||||
->where(fn (Builder $q): Builder => $q->accepted()->orWhere(fn (Builder $q): Builder => $q->pending()))
|
->where(fn (Builder $q): Builder => $q->accepted()->orWhere(fn (Builder $q): Builder => $q->pending()))
|
||||||
->exists();
|
->exists();
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Manage\Cooperations\Actions\Media;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Filament\Support\CheerfulNotification;
|
||||||
|
use App\Models\CooperationMedia;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
|
||||||
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Support\Enums\Width;
|
||||||
|
use Filament\Support\Icons\Heroicon;
|
||||||
|
use Filament\Support\RawJs;
|
||||||
|
|
||||||
|
class PayAction extends Action
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->label('Bayar')
|
||||||
|
->icon(Heroicon::CreditCard)
|
||||||
|
->color('success')
|
||||||
|
->visible(fn (CooperationMedia $record): bool => $record->cooperation->status === CooperationStatus::PAYMENT && ! $record->payment()->exists() && $record->status === ApprovalStatus::ACCEPTED)
|
||||||
|
->schema([
|
||||||
|
TextInput::make('amount')
|
||||||
|
->label('Nominal Pembayaran')
|
||||||
|
->placeholder('1,000,000')
|
||||||
|
->autocomplete(false)
|
||||||
|
->autofocus()
|
||||||
|
->required()
|
||||||
|
->mask(RawJs::make('$money($input)'))
|
||||||
|
->prefix('Rp'),
|
||||||
|
|
||||||
|
SpatieMediaLibraryFileUpload::make('payment_proof')
|
||||||
|
->label('Bukti Pembayaran')
|
||||||
|
->disk(config('filesystems.default'))
|
||||||
|
->acceptedFileTypes(['image/*'])
|
||||||
|
->maxSize(1024 * 3)
|
||||||
|
->collection('cooperations')
|
||||||
|
->customProperties(fn (): array => [
|
||||||
|
'feature' => 'cooperations',
|
||||||
|
'date' => now()->toDateString(),
|
||||||
|
'doc_type' => 'payment-proof',
|
||||||
|
])
|
||||||
|
->image()
|
||||||
|
->required(),
|
||||||
|
|
||||||
|
Textarea::make('description')
|
||||||
|
->label('Keterangan')
|
||||||
|
->placeholder('...')
|
||||||
|
->autocomplete(false),
|
||||||
|
])
|
||||||
|
->action(function (CooperationMedia $record, array $data): void {
|
||||||
|
$record->payment()->create([
|
||||||
|
'amount' => str_replace(['Rp ', ','], '', $data['amount']),
|
||||||
|
'description' => $data['description'],
|
||||||
|
'payment_date' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
CheerfulNotification::success(
|
||||||
|
'Pembayaran Berhasil! 💸✨',
|
||||||
|
"Pembayaran untuk {$record->partnerMedia->name} telah berhasil dicatat."
|
||||||
|
)->send();
|
||||||
|
})
|
||||||
|
->modalWidth(Width::Large);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,8 @@
|
|||||||
namespace App\Filament\Resources\Manage\Cooperations\Actions\Report;
|
namespace App\Filament\Resources\Manage\Cooperations\Actions\Report;
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||||
use App\Models\Report;
|
use App\Models\Report;
|
||||||
use App\Notifications\BroadcastNotification;
|
use App\Notifications\BroadcastNotification;
|
||||||
@ -50,6 +52,10 @@ protected function setUp(): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
->visible(fn (Report $record): bool => ! auth()->user()->hasRole('Perusahaan') && $record->status === ApprovalStatus::PENDING);
|
->visible(
|
||||||
|
fn (Report $record): bool => $record->taskAssignment?->cooperation?->status === CooperationStatus::VERIFICATION &&
|
||||||
|
! auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) &&
|
||||||
|
$record->status === ApprovalStatus::PENDING
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
namespace App\Filament\Resources\Manage\Cooperations\Actions\Report;
|
namespace App\Filament\Resources\Manage\Cooperations\Actions\Report;
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||||
use App\Models\Report;
|
use App\Models\Report;
|
||||||
use App\Notifications\BroadcastNotification;
|
use App\Notifications\BroadcastNotification;
|
||||||
@ -60,7 +62,11 @@ protected function setUp(): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
->visible(fn (Report $record): bool => ! auth()->user()->hasRole('Perusahaan') && $record->status === ApprovalStatus::PENDING)
|
->visible(
|
||||||
|
fn (Report $record): bool => $record->taskAssignment?->cooperation?->status === CooperationStatus::VERIFICATION &&
|
||||||
|
! auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) &&
|
||||||
|
$record->status === ApprovalStatus::PENDING
|
||||||
|
)
|
||||||
->modalWidth(Width::Large);
|
->modalWidth(Width::Large);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,21 +106,16 @@ public static function infolist(Schema $schema): Schema
|
|||||||
->label('Tanggal Pengajuan Akhir')
|
->label('Tanggal Pengajuan Akhir')
|
||||||
->date('l, d F Y'),
|
->date('l, d F Y'),
|
||||||
|
|
||||||
TextEntry::make('assignment_deadline')
|
TextEntry::make('taskAssignment.start_date')
|
||||||
->label('Deadline Penugasan')
|
->label('Tanggal Mulai Penugasan')
|
||||||
->date('l, d F Y')
|
->date('l, d F Y')
|
||||||
->placeholder('Belum ditentukan'),
|
->placeholder('Belum ditentukan'),
|
||||||
|
|
||||||
TextEntry::make('verification_deadline')
|
TextEntry::make('taskAssignment.end_date')
|
||||||
->label('Deadline Verifikasi')
|
->label('Tanggal Selesai Penugasan')
|
||||||
->date('l, d F Y')
|
->date('l, d F Y')
|
||||||
->placeholder('Belum ditentukan'),
|
->placeholder('Belum ditentukan'),
|
||||||
|
|
||||||
TextEntry::make('payment_amount')
|
|
||||||
->label('Jumlah Pembayaran')
|
|
||||||
->money('IDR')
|
|
||||||
->placeholder('Belum ditentukan'),
|
|
||||||
|
|
||||||
TextEntry::make('payment_date')
|
TextEntry::make('payment_date')
|
||||||
->label('Tanggal Pembayaran')
|
->label('Tanggal Pembayaran')
|
||||||
->date('l, d F Y')
|
->date('l, d F Y')
|
||||||
@ -133,18 +128,19 @@ public static function infolist(Schema $schema): Schema
|
|||||||
->label('Template Proposal')
|
->label('Template Proposal')
|
||||||
->getStateUsing(fn (Cooperation $record): ?string => optional($record->getMedia('cooperations')->where('custom_properties.doc_type', 'proposal-template')->sortByDesc('created_at')->first())->getPathRelativeToRoot())
|
->getStateUsing(fn (Cooperation $record): ?string => optional($record->getMedia('cooperations')->where('custom_properties.doc_type', 'proposal-template')->sortByDesc('created_at')->first())->getPathRelativeToRoot())
|
||||||
->disk(config('filesystems.default'))
|
->disk(config('filesystems.default'))
|
||||||
->columnSpan(2),
|
->columnSpan(2)
|
||||||
|
->visible(fn (Cooperation $record): bool => $record->getMedia('cooperations')->where('custom_properties.doc_type', 'proposal-template')->isNotEmpty()),
|
||||||
|
|
||||||
ImageEntry::make('banner')
|
ImageEntry::make('banner')
|
||||||
->label('Banner')
|
->label('Banner')
|
||||||
->getStateUsing(fn (Cooperation $record): ?string => optional($record->getMedia('cooperations')->where('custom_properties.doc_type', 'banner')->sortByDesc('created_at')->first())->getPathRelativeToRoot())
|
->getStateUsing(fn (Cooperation $record): ?string => optional($record->getMedia('cooperations')->where('custom_properties.doc_type', 'banner')->sortByDesc('created_at')->first())->getPathRelativeToRoot())
|
||||||
->disk(config('filesystems.default'))
|
->disk(config('filesystems.default'))
|
||||||
->visible(fn (Cooperation $record): bool => $record->getMedia('cooperations')->isNotEmpty()),
|
->visible(fn (Cooperation $record): bool => $record->getMedia('cooperations')->where('custom_properties.doc_type', 'banner')->isNotEmpty()),
|
||||||
]),
|
])
|
||||||
|
->visible(fn (Cooperation $record): bool => $record->getMedia('cooperations')->where('custom_properties.doc_type', 'banner')->isNotEmpty() && $record->getMedia('cooperations')->where('custom_properties.doc_type', 'proposal-template')->isNotEmpty()),
|
||||||
|
|
||||||
TextEntry::make('description')
|
TextEntry::make('description')
|
||||||
->label('Deskripsi')
|
->label('Deskripsi'),
|
||||||
->markdown(),
|
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
->columns(1);
|
->columns(1);
|
||||||
|
|||||||
@ -2,263 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Filament\Resources\Manage\Cooperations\Pages;
|
namespace App\Filament\Resources\Manage\Cooperations\Pages;
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
|
||||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Infolists\Components\RepeatableEntry;
|
|
||||||
use Filament\Infolists\Components\TextEntry;
|
|
||||||
use Filament\Resources\Pages\ViewRecord;
|
use Filament\Resources\Pages\ViewRecord;
|
||||||
use Filament\Support\Enums\FontWeight;
|
|
||||||
|
|
||||||
class ViewCooperation extends ViewRecord
|
class ViewCooperation extends ViewRecord
|
||||||
{
|
{
|
||||||
protected static string $resource = CooperationResource::class;
|
protected static string $resource = CooperationResource::class;
|
||||||
|
|
||||||
protected function getWorkflowSteps()
|
|
||||||
{
|
|
||||||
$record = $this->getRecord();
|
|
||||||
|
|
||||||
$steps = [
|
|
||||||
[
|
|
||||||
'step' => 1,
|
|
||||||
'title' => 'Pengajuan Kerja Sama',
|
|
||||||
'description' => 'Admin mengajukan kerja sama ke media',
|
|
||||||
'status' => 'completed', // Always completed since we're viewing it
|
|
||||||
'date' => $record->created_at?->format('d/m/Y H:i'),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'step' => 2,
|
|
||||||
'title' => 'Respon Media',
|
|
||||||
'description' => 'Media menerima atau menolak pengajuan',
|
|
||||||
'status' => $this->getMediaResponseStatus($record),
|
|
||||||
'date' => $this->getMediaResponseDate($record),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'step' => 3,
|
|
||||||
'title' => 'Upload Proposal',
|
|
||||||
'description' => 'Media mengupload dokumen proposal',
|
|
||||||
'status' => $this->getProposalStatus($record),
|
|
||||||
'date' => $this->getProposalDate($record),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'step' => 4,
|
|
||||||
'title' => 'Review Proposal',
|
|
||||||
'description' => 'Admin mereview dan menyetujui proposal',
|
|
||||||
'status' => $this->getProposalReviewStatus($record),
|
|
||||||
'date' => $this->getProposalReviewDate($record),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'step' => 5,
|
|
||||||
'title' => 'Penugasan',
|
|
||||||
'description' => 'Admin membuat task assignment',
|
|
||||||
'status' => $this->getTaskStatus($record),
|
|
||||||
'date' => $this->getTaskDate($record),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'step' => 6,
|
|
||||||
'title' => 'Upload Laporan',
|
|
||||||
'description' => 'Media mengupload laporan',
|
|
||||||
'status' => $this->getReportStatus($record),
|
|
||||||
'date' => $this->getReportDate($record),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'step' => 7,
|
|
||||||
'title' => 'Selesai',
|
|
||||||
'description' => 'Kerja sama telah selesai',
|
|
||||||
'status' => $this->getCompletionStatus($record),
|
|
||||||
'date' => $this->getCompletionDate($record),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
return RepeatableEntry::make('workflow_steps')
|
|
||||||
->label('')
|
|
||||||
->schema([
|
|
||||||
TextEntry::make('step')
|
|
||||||
->label('Langkah')
|
|
||||||
->formatStateUsing(fn (int $state): string => "Langkah {$state}")
|
|
||||||
->weight(FontWeight::Bold),
|
|
||||||
|
|
||||||
TextEntry::make('title')
|
|
||||||
->label('Judul'),
|
|
||||||
|
|
||||||
TextEntry::make('description')
|
|
||||||
->label('Deskripsi'),
|
|
||||||
|
|
||||||
TextEntry::make('status')
|
|
||||||
->label('Status')
|
|
||||||
->badge()
|
|
||||||
->color(fn (string $state): string => match ($state) {
|
|
||||||
'completed' => 'success',
|
|
||||||
'in_progress' => 'warning',
|
|
||||||
'pending' => 'gray',
|
|
||||||
'rejected' => 'danger',
|
|
||||||
default => 'gray',
|
|
||||||
})
|
|
||||||
->formatStateUsing(fn (string $state): string => match ($state) {
|
|
||||||
'completed' => 'Selesai',
|
|
||||||
'in_progress' => 'Sedang Berjalan',
|
|
||||||
'pending' => 'Menunggu',
|
|
||||||
'rejected' => 'Ditolak',
|
|
||||||
default => $state,
|
|
||||||
}),
|
|
||||||
|
|
||||||
TextEntry::make('date')
|
|
||||||
->label('Tanggal')
|
|
||||||
->placeholder('Belum ada'),
|
|
||||||
])
|
|
||||||
->columns(5)
|
|
||||||
->state($steps);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMediaResponseStatus($record): string
|
|
||||||
{
|
|
||||||
$mediaStatuses = $record->cooperationMedia->pluck('status')->unique();
|
|
||||||
|
|
||||||
if ($mediaStatuses->contains(ApprovalStatus::ACCEPTED)) {
|
|
||||||
return 'completed';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($mediaStatuses->contains(ApprovalStatus::REJECTED)) {
|
|
||||||
return 'rejected';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMediaResponseDate($record)
|
|
||||||
{
|
|
||||||
$latestResponse = $record->cooperationMedia
|
|
||||||
->whereNotNull('updated_at')
|
|
||||||
->sortByDesc('updated_at')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return $latestResponse?->updated_at?->format('d/m/Y H:i');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProposalStatus($record): string
|
|
||||||
{
|
|
||||||
if ($record->proposals()->exists()) {
|
|
||||||
return 'completed';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProposalDate($record)
|
|
||||||
{
|
|
||||||
$latestProposal = $record->proposals->sortByDesc('created_at')->first();
|
|
||||||
|
|
||||||
return $latestProposal?->created_at?->format('d/m/Y H:i');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProposalReviewStatus($record): string
|
|
||||||
{
|
|
||||||
$proposals = $record->proposals;
|
|
||||||
|
|
||||||
if ($record->proposals()->accepted()->exists()) {
|
|
||||||
return 'completed';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($record->proposals()->rejected()->exists()) {
|
|
||||||
return 'rejected';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($proposals->isNotEmpty()) {
|
|
||||||
return 'in_progress';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProposalReviewDate($record)
|
|
||||||
{
|
|
||||||
$latestProposal = $record->proposals->sortByDesc('updated_at')->first();
|
|
||||||
|
|
||||||
return $latestProposal?->updated_at?->format('d/m/Y H:i');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTaskStatus($record): string
|
|
||||||
{
|
|
||||||
if ($record->taskAssignment()->exists()) {
|
|
||||||
return 'completed';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTaskDate($record)
|
|
||||||
{
|
|
||||||
return $record->taskAssignment?->created_at?->format('d/m/Y H:i');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getReportStatus($record): string
|
|
||||||
{
|
|
||||||
$task = $record->taskAssignment;
|
|
||||||
|
|
||||||
if (! $task) {
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
$totalReports = $task->report_amount;
|
|
||||||
$completedReports = $task->reports->count();
|
|
||||||
|
|
||||||
if ($completedReports >= $totalReports && $totalReports > 0) {
|
|
||||||
return 'completed';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($completedReports > 0) {
|
|
||||||
return 'in_progress';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getReportDate($record)
|
|
||||||
{
|
|
||||||
$latestReport = $record->taskAssignment?->reports
|
|
||||||
->sortByDesc('created_at')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return $latestReport?->created_at?->format('d/m/Y H:i');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCompletionStatus($record): string
|
|
||||||
{
|
|
||||||
// Logic for completion - could be based on all reports accepted, etc.
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCompletionDate($record)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
Action::make('back')
|
Action::make('back')
|
||||||
->label('Kembali')
|
->label('Kembali')
|
||||||
->url(fn (): string => static::getResource()::getUrl('index'))
|
->url(ListCooperations::getUrl())
|
||||||
->outlined()
|
->outlined()
|
||||||
->color('secondary'),
|
->color('secondary'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function canMarkAsCompleted(): bool
|
|
||||||
{
|
|
||||||
$record = $this->getRecord();
|
|
||||||
|
|
||||||
$task = $record->taskAssignment;
|
|
||||||
|
|
||||||
if (! $task) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$reportCount = $task->reports()->count();
|
|
||||||
if ($reportCount < $task->report_amount) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||||
|
|
||||||
use App\Filament\Resources\Manage\Partners\PartnerResource;
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Filament\Resources\Manage\Cooperations\Actions\Media\PayAction;
|
||||||
use App\Models\CooperationMedia;
|
use App\Models\CooperationMedia;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\ViewAction;
|
||||||
|
use Filament\Infolists\Components\ImageEntry;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
use Filament\Resources\RelationManagers\RelationManager;
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
use Filament\Support\Icons\Heroicon;
|
use Filament\Schemas\Components\Grid;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -30,12 +34,16 @@ public static function getBadge(Model $ownerRecord, string $pageClass): ?string
|
|||||||
public function table(Table $table): Table
|
public function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table
|
return $table
|
||||||
->recordTitleAttribute('partnerMedia.name')
|
|
||||||
->columns([
|
->columns([
|
||||||
TextColumn::make('partnerMedia.name')
|
TextColumn::make('partnerMedia.name')
|
||||||
->label('Nama')
|
->label('Nama')
|
||||||
->searchable(),
|
->searchable(),
|
||||||
|
|
||||||
|
TextColumn::make('payment.amount')
|
||||||
|
->label('Jumlah Pembayaran')
|
||||||
|
->money('IDR', decimalPlaces: 0)
|
||||||
|
->placeholder('Belum ditentukan'),
|
||||||
|
|
||||||
TextColumn::make('status')
|
TextColumn::make('status')
|
||||||
->label('Status')
|
->label('Status')
|
||||||
->badge(),
|
->badge(),
|
||||||
@ -43,15 +51,38 @@ public function table(Table $table): Table
|
|||||||
->filters([])
|
->filters([])
|
||||||
->headerActions([])
|
->headerActions([])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
Action::make('view')
|
PayAction::make('pay'),
|
||||||
->label('Lihat')
|
|
||||||
->icon(Heroicon::Eye)
|
ViewAction::make()
|
||||||
->url(function (CooperationMedia $record): string {
|
->visible(fn (CooperationMedia $record): bool => $record->status === ApprovalStatus::ACCEPTED && $record->payment()->exists()),
|
||||||
return PartnerResource::getUrl('view', [
|
|
||||||
'record' => $record->partnerMedia->company->user_id,
|
|
||||||
]);
|
|
||||||
}),
|
|
||||||
])
|
])
|
||||||
->recordAction(null);
|
->recordAction(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function infolist(Schema $infolist): Schema
|
||||||
|
{
|
||||||
|
return $infolist
|
||||||
|
->components([
|
||||||
|
Grid::make(2)
|
||||||
|
->schema([
|
||||||
|
TextEntry::make('payment.amount')
|
||||||
|
->label('Nominal')
|
||||||
|
->money('IDR'),
|
||||||
|
|
||||||
|
TextEntry::make('payment.payment_date')
|
||||||
|
->label('Tanggal Bayar')
|
||||||
|
->dateTime(),
|
||||||
|
]),
|
||||||
|
|
||||||
|
TextEntry::make('payment.description')
|
||||||
|
->label('Keterangan')
|
||||||
|
->visible(fn (CooperationMedia $record): bool => $record->description !== null),
|
||||||
|
|
||||||
|
ImageEntry::make('payment_proof')
|
||||||
|
->label('Bukti Pembayaran')
|
||||||
|
->getStateUsing(fn (CooperationMedia $record): ?string => optional($record->getMedia('cooperations')->where('custom_properties.doc_type', 'payment-proof')->sortByDesc('created_at')->first())->getPathRelativeToRoot())
|
||||||
|
->disk(config('filesystems.default')),
|
||||||
|
])
|
||||||
|
->columns(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Proposal\AcceptAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Proposal\AcceptAction;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Proposal\RejectAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Proposal\RejectAction;
|
||||||
use App\Models\CooperationProposal;
|
use App\Models\CooperationProposal;
|
||||||
@ -16,14 +17,36 @@
|
|||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Joaopaulolndev\FilamentPdfViewer\Infolists\Components\PdfViewerEntry;
|
use Joaopaulolndev\FilamentPdfViewer\Infolists\Components\PdfViewerEntry;
|
||||||
|
|
||||||
class CooperationProposalRelationManager extends RelationManager
|
class CooperationProposalRelationManager extends RelationManager
|
||||||
{
|
{
|
||||||
protected static string $relationship = 'proposals';
|
protected static string $relationship = 'proposal';
|
||||||
|
|
||||||
protected static ?string $title = 'Proposal';
|
protected static ?string $title = 'Proposal';
|
||||||
|
|
||||||
|
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
|
||||||
|
{
|
||||||
|
if (auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)) {
|
||||||
|
return $ownerRecord->cooperationMedia()
|
||||||
|
->whereHas('partnerMedia', function ($query) {
|
||||||
|
$query->whereHas('company', function ($query) {
|
||||||
|
$query->where('user_id', auth()->id());
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->where('status', ApprovalStatus::ACCEPTED)
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
|
||||||
|
{
|
||||||
|
return $ownerRecord->proposal()->count();
|
||||||
|
}
|
||||||
|
|
||||||
public function table(Table $table): Table
|
public function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table
|
return $table
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Report\AcceptAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Report\AcceptAction;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Report\RejectAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Report\RejectAction;
|
||||||
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
use App\Filament\Resources\Manage\Cooperations\CooperationResource;
|
||||||
@ -22,7 +24,6 @@
|
|||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class ReportRelationManager extends RelationManager
|
class ReportRelationManager extends RelationManager
|
||||||
{
|
{
|
||||||
@ -30,6 +31,22 @@ class ReportRelationManager extends RelationManager
|
|||||||
|
|
||||||
protected static ?string $title = 'Laporan';
|
protected static ?string $title = 'Laporan';
|
||||||
|
|
||||||
|
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
|
||||||
|
{
|
||||||
|
if (auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)) {
|
||||||
|
return $ownerRecord->cooperationMedia()
|
||||||
|
->whereHas('partnerMedia', function ($query) {
|
||||||
|
$query->whereHas('company', function ($query) {
|
||||||
|
$query->where('user_id', auth()->id());
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->where('status', ApprovalStatus::ACCEPTED)
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function isReadOnly(): bool
|
public function isReadOnly(): bool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -138,8 +155,8 @@ public function table(Table $table): Table
|
|||||||
User::superAdmin()
|
User::superAdmin()
|
||||||
->get()
|
->get()
|
||||||
->each(function ($admin) use ($record): void {
|
->each(function ($admin) use ($record): void {
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
$user = Auth::user();
|
|
||||||
$admin->notify(new BroadcastNotification([
|
$admin->notify(new BroadcastNotification([
|
||||||
'title' => 'Ada Laporan Baru! 📝✨',
|
'title' => 'Ada Laporan Baru! 📝✨',
|
||||||
'body' => "Halo Admin! {$user->name} baru saja mengirimkan laporan \"{$record->title}\" untuk kerja sama \"{$this->getOwnerRecord()->title}\". Yuk, dicek! 😊",
|
'body' => "Halo Admin! {$user->name} baru saja mengirimkan laporan \"{$record->title}\" untuk kerja sama \"{$this->getOwnerRecord()->title}\". Yuk, dicek! 😊",
|
||||||
@ -152,8 +169,7 @@ public function table(Table $table): Table
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
->visible(function (): bool {
|
->visible(function (): bool {
|
||||||
|
$user = auth()->user();
|
||||||
$user = Auth::user();
|
|
||||||
|
|
||||||
if (! $user || ! $user->hasRole('Perusahaan')) {
|
if (! $user || ! $user->hasRole('Perusahaan')) {
|
||||||
return false;
|
return false;
|
||||||
@ -165,9 +181,10 @@ public function table(Table $table): Table
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $taskAssignment->reports()
|
return $this->getOwnerRecord()->status === CooperationStatus::ASSIGNMENT
|
||||||
->where('status', '!=', ApprovalStatus::REJECTED)
|
&& $taskAssignment->reports()
|
||||||
->count() < $taskAssignment->report_amount;
|
->where('status', '!=', ApprovalStatus::REJECTED)
|
||||||
|
->count() < $taskAssignment->report_amount;
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
->toolbarActions([]);
|
->toolbarActions([]);
|
||||||
|
|||||||
@ -3,20 +3,17 @@
|
|||||||
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
namespace App\Filament\Resources\Manage\Cooperations\RelationManagers;
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
use App\Filament\Actions\Cheerful\CreateAction;
|
use App\Filament\Actions\Cheerful\CreateAction;
|
||||||
use App\Filament\Actions\Cheerful\DeleteAction;
|
use App\Filament\Actions\Cheerful\DeleteAction;
|
||||||
use App\Filament\Actions\Cheerful\EditAction;
|
use App\Filament\Actions\Cheerful\EditAction;
|
||||||
use Filament\Actions\BulkActionGroup;
|
use Filament\Actions\BulkActionGroup;
|
||||||
use Filament\Actions\DeleteBulkAction;
|
use Filament\Actions\DeleteBulkAction;
|
||||||
use Filament\Forms\Components\DatePicker;
|
|
||||||
use Filament\Forms\Components\Select;
|
|
||||||
use Filament\Forms\Components\Textarea;
|
|
||||||
use Filament\Forms\Components\TextInput;
|
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Resources\RelationManagers\RelationManager;
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
use Filament\Schemas\Schema;
|
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class TaskAssignmentRelationManager extends RelationManager
|
class TaskAssignmentRelationManager extends RelationManager
|
||||||
{
|
{
|
||||||
@ -24,58 +21,25 @@ class TaskAssignmentRelationManager extends RelationManager
|
|||||||
|
|
||||||
protected static ?string $title = 'Penugasan';
|
protected static ?string $title = 'Penugasan';
|
||||||
|
|
||||||
public function form(Schema $schema): Schema
|
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
|
||||||
{
|
{
|
||||||
return $schema
|
if (auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)) {
|
||||||
->components([
|
return $ownerRecord->cooperationMedia()
|
||||||
DatePicker::make('start_date')
|
->whereHas('partnerMedia', function ($query) {
|
||||||
->label('Tanggal Mulai')
|
$query->whereHas('company', function ($query) {
|
||||||
->required()
|
$query->where('user_id', auth()->id());
|
||||||
->native(false)
|
});
|
||||||
->autocomplete(false)
|
})
|
||||||
->autofocus(),
|
->where('status', ApprovalStatus::ACCEPTED)
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
DatePicker::make('end_date')
|
return true;
|
||||||
->label('Tanggal Selesai')
|
}
|
||||||
->required()
|
|
||||||
->native(false)
|
|
||||||
->after('start_date')
|
|
||||||
->autocomplete(false),
|
|
||||||
|
|
||||||
Textarea::make('task_description')
|
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
|
||||||
->label('Deskripsi Tugas')
|
{
|
||||||
->required()
|
return $ownerRecord->taskAssignment()->count();
|
||||||
->rows(4)
|
|
||||||
->maxLength(65535)
|
|
||||||
->autocomplete(false),
|
|
||||||
|
|
||||||
TextInput::make('report_amount')
|
|
||||||
->label('Jumlah Laporan')
|
|
||||||
->numeric()
|
|
||||||
->required()
|
|
||||||
->minValue(1)
|
|
||||||
->default(1)
|
|
||||||
->autocomplete(false),
|
|
||||||
|
|
||||||
Select::make('partner_media_ids')
|
|
||||||
->label('Media yang Ditugaskan')
|
|
||||||
->relationship('partnerMedia', 'name')
|
|
||||||
->multiple()
|
|
||||||
->preload()
|
|
||||||
->required()
|
|
||||||
->autocomplete(false),
|
|
||||||
|
|
||||||
Select::make('status')
|
|
||||||
->options(ApprovalStatus::options())
|
|
||||||
->required()
|
|
||||||
->default(ApprovalStatus::PENDING),
|
|
||||||
|
|
||||||
Textarea::make('rejection_reason')
|
|
||||||
->label('Alasan Penolakan')
|
|
||||||
->rows(3)
|
|
||||||
->columnSpanFull()
|
|
||||||
->autocomplete(false),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function table(Table $table): Table
|
public function table(Table $table): Table
|
||||||
@ -107,7 +71,7 @@ public function table(Table $table): Table
|
|||||||
->visible(function (): bool {
|
->visible(function (): bool {
|
||||||
$cooperation = $this->getOwnerRecord();
|
$cooperation = $this->getOwnerRecord();
|
||||||
|
|
||||||
return $cooperation->proposals()->accepted()->exists()
|
return $cooperation->proposal()->accepted()->exists()
|
||||||
&& ! $cooperation->taskAssignment()->exists();
|
&& ! $cooperation->taskAssignment()->exists();
|
||||||
})
|
})
|
||||||
->successNotification(
|
->successNotification(
|
||||||
|
|||||||
@ -8,7 +8,9 @@
|
|||||||
use App\Filament\Actions\DefaultBulkActions;
|
use App\Filament\Actions\DefaultBulkActions;
|
||||||
use App\Filament\Columns\TimestampColumns;
|
use App\Filament\Columns\TimestampColumns;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\AcceptAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\AcceptAction;
|
||||||
|
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\CompleteCooperationAction;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\CreateTaskAssignmentAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\CreateTaskAssignmentAction;
|
||||||
|
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\ProceedToPaymentAction;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\RejectAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\RejectAction;
|
||||||
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\SendProposalAction;
|
use App\Filament\Resources\Manage\Cooperations\Actions\Cooperation\SendProposalAction;
|
||||||
use App\Models\Cooperation;
|
use App\Models\Cooperation;
|
||||||
@ -61,22 +63,6 @@ public static function configure(Table $table): Table
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
TextColumn::make('assignment_deadline')
|
|
||||||
->label('Deadline Penugasan')
|
|
||||||
->searchable()
|
|
||||||
->sortable()
|
|
||||||
->date('l, d F Y')
|
|
||||||
->placeholder('Belum ditentukan')
|
|
||||||
->toggleable(isToggledHiddenByDefault: true),
|
|
||||||
|
|
||||||
TextColumn::make('verification_deadline')
|
|
||||||
->label('Deadline Verifikasi')
|
|
||||||
->searchable()
|
|
||||||
->sortable()
|
|
||||||
->date('l, d F Y')
|
|
||||||
->placeholder('Belum ditentukan')
|
|
||||||
->toggleable(isToggledHiddenByDefault: true),
|
|
||||||
|
|
||||||
TextColumn::make('status')
|
TextColumn::make('status')
|
||||||
->searchable()
|
->searchable()
|
||||||
->sortable()
|
->sortable()
|
||||||
@ -158,6 +144,10 @@ public static function configure(Table $table): Table
|
|||||||
|
|
||||||
CreateTaskAssignmentAction::make('createTaskAssignment'),
|
CreateTaskAssignmentAction::make('createTaskAssignment'),
|
||||||
|
|
||||||
|
ProceedToPaymentAction::make('proceedToPayment'),
|
||||||
|
|
||||||
|
CompleteCooperationAction::make('completeCooperation'),
|
||||||
|
|
||||||
EditAction::make()
|
EditAction::make()
|
||||||
->visible(fn (): bool => ! auth()->user()->hasRole('Perusahaan')),
|
->visible(fn (): bool => ! auth()->user()->hasRole('Perusahaan')),
|
||||||
|
|
||||||
|
|||||||
@ -26,8 +26,6 @@ protected function casts(): array
|
|||||||
return [
|
return [
|
||||||
'initial_submission_date' => 'date',
|
'initial_submission_date' => 'date',
|
||||||
'final_submission_date' => 'date',
|
'final_submission_date' => 'date',
|
||||||
'assignment_deadline' => 'date',
|
|
||||||
'verification_deadline' => 'date',
|
|
||||||
'payment_amount' => 'integer',
|
'payment_amount' => 'integer',
|
||||||
'payment_date' => 'date',
|
'payment_date' => 'date',
|
||||||
'status' => CooperationStatus::class,
|
'status' => CooperationStatus::class,
|
||||||
@ -64,9 +62,9 @@ protected function completed(Builder $query): void
|
|||||||
$query->where('status', CooperationStatus::COMPLETED);
|
$query->where('status', CooperationStatus::COMPLETED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function proposals(): HasMany
|
public function proposal(): HasOne
|
||||||
{
|
{
|
||||||
return $this->hasMany(CooperationProposal::class);
|
return $this->hasOne(CooperationProposal::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function taskAssignment(): HasOne
|
public function taskAssignment(): HasOne
|
||||||
|
|||||||
@ -7,12 +7,15 @@
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
|
||||||
class CooperationMedia extends Pivot
|
class CooperationMedia extends Pivot implements HasMedia
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory, InteractsWithMedia;
|
||||||
|
|
||||||
protected $table = 'cooperation_media';
|
protected $table = 'cooperation_media';
|
||||||
|
|
||||||
@ -57,4 +60,9 @@ public function rejectionReasons(): MorphMany
|
|||||||
{
|
{
|
||||||
return $this->morphMany(RejectionReason::class, 'rejectable');
|
return $this->morphMany(RejectionReason::class, 'rejectable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function payment(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(CooperationPayment::class, 'cooperation_media_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
app/Models/CooperationPayment.php
Normal file
29
app/Models/CooperationPayment.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
|
||||||
|
class CooperationPayment extends Model implements HasMedia
|
||||||
|
{
|
||||||
|
use HasFactory, InteractsWithMedia;
|
||||||
|
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'amount' => 'integer',
|
||||||
|
'payment_date' => 'datetime',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cooperationMedia(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(CooperationMedia::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,8 +17,6 @@ public function up(): void
|
|||||||
$table->string('title', 200);
|
$table->string('title', 200);
|
||||||
$table->date('initial_submission_date');
|
$table->date('initial_submission_date');
|
||||||
$table->date('final_submission_date');
|
$table->date('final_submission_date');
|
||||||
$table->date('assignment_deadline')->nullable();
|
|
||||||
$table->date('verification_deadline')->nullable();
|
|
||||||
$table->text('description');
|
$table->text('description');
|
||||||
$table->unsignedInteger('payment_amount')->nullable();
|
$table->unsignedInteger('payment_amount')->nullable();
|
||||||
$table->date('payment_date')->nullable();
|
$table->date('payment_date')->nullable();
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\CooperationMedia;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('cooperation_payments', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignIdFor(CooperationMedia::class)->constrained()->cascadeOnDelete();
|
||||||
|
$table->unsignedInteger('amount');
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->date('payment_date')->useCurrent();
|
||||||
|
$table->timestamp('created_at')->useCurrent();
|
||||||
|
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('cooperation_payments');
|
||||||
|
}
|
||||||
|
};
|
||||||
36
database/seeders/CooperationSeeder.php
Normal file
36
database/seeders/CooperationSeeder.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Models\Cooperation;
|
||||||
|
use App\Models\PartnerMedia;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class CooperationSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$cooperation = Cooperation::create([
|
||||||
|
'title' => 'Kerja Sama Media '.now()->format('Y'),
|
||||||
|
'description' => 'Kerja sama media untuk penyebarluasan informasi publik.',
|
||||||
|
'initial_submission_date' => now()->subDay(),
|
||||||
|
'final_submission_date' => now(),
|
||||||
|
'status' => CooperationStatus::PENDING,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$verifiedMedia = PartnerMedia::verified()->get();
|
||||||
|
|
||||||
|
foreach ($verifiedMedia as $media) {
|
||||||
|
$cooperation->partnerMedia()->attach($media->id, [
|
||||||
|
'status' => ApprovalStatus::PENDING,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user