diff --git a/app/Http/Controllers/Admin/Finances/TuitionInvoiceController.php b/app/Http/Controllers/Admin/Finances/TuitionInvoiceController.php index e515f1d..a70cd2b 100644 --- a/app/Http/Controllers/Admin/Finances/TuitionInvoiceController.php +++ b/app/Http/Controllers/Admin/Finances/TuitionInvoiceController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Finances; +use App\Enums\StudentStatus; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Finances\TuitionInvoiceRequest; use App\Http\Requests\PaginatedRequest; @@ -28,7 +29,7 @@ public function index(PaginatedRequest $request): Response ...$request->validatedWithDefaults(), academicTermId: $request->validated('academic_term_id'), ), - 'students' => $this->studentService->getAllForSelect(), + 'students' => $this->studentService->getAllForSelect(status: StudentStatus::Active->value), 'academicTerms' => $this->academicTermService->getAllForSelect(), 'filters' => $request->only(['academic_term_id']), ]); @@ -36,7 +37,7 @@ public function index(PaginatedRequest $request): Response public function store(TuitionInvoiceRequest $request): RedirectResponse { - $this->service->create($request->validated()); + $this->service->createMany($request->validated('student_ids'), $request->validated()); Inertia::flash('toast', ['type' => 'success', 'message' => 'Tagihan berhasil ditambahkan.']); diff --git a/app/Http/Requests/Admin/Finances/TuitionInvoiceRequest.php b/app/Http/Requests/Admin/Finances/TuitionInvoiceRequest.php index 6bbce5f..e034f33 100644 --- a/app/Http/Requests/Admin/Finances/TuitionInvoiceRequest.php +++ b/app/Http/Requests/Admin/Finances/TuitionInvoiceRequest.php @@ -14,11 +14,19 @@ public function authorize(): bool public function rules(): array { - return [ - 'student_id' => ['required', 'integer', Rule::exists('students', 'id')], + $rules = [ 'academic_term_id' => ['required', 'integer', Rule::exists('academic_terms', 'id')], 'amount_due' => ['required', 'numeric', 'min:0'], - 'due_date' => ['nullable', 'date'], + 'due_date' => ['required', 'date'], ]; + + if ($this->isMethod('post')) { + $rules['student_ids'] = ['required', 'array', 'min:1']; + $rules['student_ids.*'] = ['integer', Rule::exists('students', 'id')]; + } else { + $rules['student_id'] = ['required', 'integer', Rule::exists('students', 'id')]; + } + + return $rules; } } diff --git a/app/Services/Admin/Finances/TuitionInvoiceService.php b/app/Services/Admin/Finances/TuitionInvoiceService.php index cde7f14..6965c2f 100644 --- a/app/Services/Admin/Finances/TuitionInvoiceService.php +++ b/app/Services/Admin/Finances/TuitionInvoiceService.php @@ -4,6 +4,7 @@ use App\Models\TuitionInvoice; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Support\Facades\DB; class TuitionInvoiceService { @@ -21,14 +22,18 @@ public function paginated(int $perPage = 25, string $search = '', ?int $academic ->paginate($perPage); } - public function create(array $data): TuitionInvoice + public function createMany(array $studentIds, array $data): void { - return TuitionInvoice::create([ - 'student_id' => $data['student_id'], - 'academic_term_id' => $data['academic_term_id'], - 'amount_due' => $data['amount_due'], - 'due_date' => $data['due_date'] ?? null, - ]); + DB::transaction(function () use ($studentIds, $data) { + foreach ($studentIds as $studentId) { + TuitionInvoice::create([ + 'student_id' => $studentId, + 'academic_term_id' => $data['academic_term_id'], + 'amount_due' => $data['amount_due'], + 'due_date' => $data['due_date'], + ]); + } + }); } public function update(TuitionInvoice $invoice, array $data): TuitionInvoice diff --git a/app/Services/Admin/Users/StudentService.php b/app/Services/Admin/Users/StudentService.php index cf1651c..3844401 100644 --- a/app/Services/Admin/Users/StudentService.php +++ b/app/Services/Admin/Users/StudentService.php @@ -12,7 +12,7 @@ class StudentService { - public function getAllForSelect(): Collection + public function getAllForSelect(?string $status = null): Collection { return Student::select(['id', 'user_id', 'student_number', 'department_id']) ->with([ @@ -20,6 +20,7 @@ public function getAllForSelect(): Collection 'user.profile:id,user_id,full_name', 'department:id,name', ]) + ->when($status, fn ($q) => $q->where('status', $status)) ->get(); } diff --git a/resources/js/pages/admin/finances/tuition-invoices/index.tsx b/resources/js/pages/admin/finances/tuition-invoices/index.tsx index d9b7365..ee7dc73 100644 --- a/resources/js/pages/admin/finances/tuition-invoices/index.tsx +++ b/resources/js/pages/admin/finances/tuition-invoices/index.tsx @@ -13,6 +13,18 @@ import InputError from '@/components/input-error'; import { PageHeader } from '@/components/page-header'; import { RupiahInput } from '@/components/rupiah-input'; import { Button } from '@/components/ui/button'; +import { + Combobox, + ComboboxChip, + ComboboxChips, + ComboboxChipsInput, + ComboboxContent, + ComboboxEmpty, + ComboboxInput, + ComboboxItem, + ComboboxList, + useComboboxAnchor, +} from '@/components/ui/combobox'; import { Label } from '@/components/ui/label'; import { Select, @@ -209,6 +221,15 @@ function CreateForm({ academicTerms: AcademicTermOption[]; }) { const [dueDate, setDueDate] = useState(); + const [selectedStudents, setSelectedStudents] = useState< + TuitionInvoiceStudent[] + >([]); + const studentAnchor = useComboboxAnchor(); + + function reset() { + setDueDate(undefined); + setSelectedStudents([]); + } return ( { onOpenChange(false); - setDueDate(undefined); + reset(); }} > {({ errors }) => (
- - - + ))} + a.id === b.id} + > + + {selectedStudents.map((student) => ( + {studentLabel(student)} - + ))} - - - + + + + + Mahasiswa tidak ditemukan. + + + {students.map((student) => ( + + {studentLabel(student)} + + ))} + + + +
- + ( editing?.due_date ? new Date(editing.due_date) : undefined, ); + const [student, setStudent] = useState( + students.find((s) => s.id === editing?.student_id) ?? null, + ); return ( * - + + + + Mahasiswa tidak ditemukan. + + + {students.map((option) => ( + + {studentLabel(option)} + + ))} + + +
@@ -396,7 +488,10 @@ function EditForm({
- +