diff --git a/Modules/HrmAddon/App/Exports/ExportAttendanceReport.php b/Modules/HrmAddon/App/Exports/ExportAttendanceReport.php new file mode 100644 index 00000000..df22df48 --- /dev/null +++ b/Modules/HrmAddon/App/Exports/ExportAttendanceReport.php @@ -0,0 +1,20 @@ +where('business_id', auth()->user()->business_id) + ->latest() + ->get(); + + return view('hrmaddon::reports.attendances.excel', compact('attendances')); + } +} diff --git a/Modules/HrmAddon/App/Exports/ExportLeaveReport.php b/Modules/HrmAddon/App/Exports/ExportLeaveReport.php new file mode 100644 index 00000000..32d50c22 --- /dev/null +++ b/Modules/HrmAddon/App/Exports/ExportLeaveReport.php @@ -0,0 +1,20 @@ +where('business_id', auth()->user()->business_id) + ->latest() + ->get(); + + return view('hrmaddon::reports.leaves.excel', compact('leaves')); + } +} diff --git a/Modules/HrmAddon/App/Exports/ExportPayrollReport.php b/Modules/HrmAddon/App/Exports/ExportPayrollReport.php new file mode 100644 index 00000000..25494e4f --- /dev/null +++ b/Modules/HrmAddon/App/Exports/ExportPayrollReport.php @@ -0,0 +1,21 @@ +where('business_id', auth()->user()->business_id) + ->latest() + ->get(); + + return view('hrmaddon::reports.payrolls.excel', compact('payrolls')); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/.gitkeep b/Modules/HrmAddon/App/Http/Controllers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceController.php new file mode 100644 index 00000000..976fa2e4 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceController.php @@ -0,0 +1,168 @@ +middleware('check.permission:attendances.read')->only(['index']); + $this->middleware('check.permission:attendances.create')->only(['store']); + $this->middleware('check.permission:attendances.update')->only(['update']); + $this->middleware('check.permission:attendances.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + + $attendances = Attendance::with(['employee:id,name', 'shift:id,name']) + ->where('business_id', auth()->user()->business_id) + + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->whereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('shift', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }) + + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::attendances.datas', compact('attendances'))->render() + ]); + } + + return view('hrmaddon::attendances.index', compact('attendances', 'employees')); + } + + public function store(Request $request) + { + $request->validate([ + 'employee_id' => 'required|integer|exists:employees,id', + 'shift_id' => 'required|integer|exists:shifts,id', + 'date' => 'required|date', + 'time_in' => 'required|date_format:H:i', + 'time_out' => 'required|date_format:H:i', + 'duration' => 'nullable|date_format:H:i', + 'note' => 'nullable|string', + ]); + + $business = Business::findOrFail(auth()->user()->business_id); + $employee = Employee::findOrFail($request->employee_id); + + $timeIn = Carbon::parse($request->time_in); + $timeOut = Carbon::parse($request->time_out); + + $durationInMinutes = $timeOut->diffInMinutes($timeIn); + $hours = floor($durationInMinutes / 60); + $minutes = $durationInMinutes % 60; + $formattedDuration = sprintf('%02d:%02d', $hours, $minutes); + $month = strtolower(Carbon::parse($request->date)->format('F')); + + $attendance = Attendance::create($request->except(['business_id', 'duration', 'month']) + [ + 'business_id' => $business->id, + 'duration' => $formattedDuration, + 'month' => $month, + ]); + + if (moduleCheck('MarketingAddon')) { + $this->attendanceMessage($business, $attendance, $employee); + } + + return response()->json([ + 'message' => __('Attendance created successfully'), + 'redirect' => route('hrm.attendances.index') + ]); + } + + public function update(Request $request, $id) + { + $request->validate([ + 'employee_id' => 'required|integer|exists:employees,id', + 'shift_id' => 'required|integer|exists:shifts,id', + 'date' => 'required|date', + 'time_in' => 'required', + 'time_out' => 'required', + 'duration' => 'nullable', + 'note' => 'nullable|string', + ]); + + $attendance = Attendance::findOrFail($id); + + $timeIn = Carbon::parse($request->time_in); + $timeOut = Carbon::parse($request->time_out); + + $durationInMinutes = $timeOut->diffInMinutes($timeIn); + $hours = floor($durationInMinutes / 60); + $minutes = $durationInMinutes % 60; + $formattedDuration = sprintf('%02d:%02d', $hours, $minutes); + $month = strtolower(Carbon::parse($request->date)->format('F')); + + $attendance->update($request->except(['business_id', 'duration', 'month']) + [ + 'business_id' => auth()->user()->business_id, + 'duration' => $formattedDuration, + 'month' => $month, + ]); + + return response()->json([ + 'message' => __('Attendance Updated successfully'), + 'redirect' => route('hrm.attendances.index') + ]); + } + + public function destroy($id) + { + $attendance = Attendance::findOrFail($id); + $attendance->delete(); + return response()->json([ + 'message' => __('Attendance Deleted Successfully'), + 'redirect' => route('hrm.attendances.index') + ]); + } + + public function deleteAll(Request $request) + { + Attendance::whereIn('id', $request->input('ids'))->delete(); + + return response()->json([ + 'message' => __('All Attendances deleted successfully'), + 'redirect' => route('hrm.attendances.index') + ]); + } + + public function getShift() + { + $employee = Employee::findOrFail(request('employee_id')); + $shift = Shift::findOrFail($employee->shift_id); + + return response()->json([ + 'data' => [ + 'id' => $shift->id, + 'name' => $shift->name + ] + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceReportController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceReportController.php new file mode 100644 index 00000000..a901d962 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceReportController.php @@ -0,0 +1,72 @@ +middleware('check.permission:attendance-reports.read')->only(['index']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + $attendances = Attendance::with(['employee:id,name', 'shift:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->whereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('shift', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::reports.attendances.datas', compact('attendances'))->render() + ]); + } + + return view('hrmaddon::reports.attendances.index', compact('attendances', 'employees')); + } + + public function exportExcel() + { + return Excel::download(new ExportAttendanceReport, 'attendance-report.xlsx'); + } + + public function exportCsv() + { + return Excel::download(new ExportAttendanceReport, 'attendance-report.csv'); + } + + public function exportPdf(Request $request) + { + $attendances = Attendance::with(['employee:id,name', 'shift:id,name']) + ->where('business_id', auth()->user()->business_id) + ->latest() + ->get(); + + return PdfService::render('hrmaddon::reports.attendances.pdf', compact('attendances'),'attendance-report.pdf'); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceReportHistoryController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceReportHistoryController.php new file mode 100644 index 00000000..780cb71a --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooAttendanceReportHistoryController.php @@ -0,0 +1,49 @@ +middleware('check.permission:attendance-reports.read')->only(['index']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + $attendances = Attendance::with(['employee:id,name', 'shift:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->whereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('shift', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::reports.attendances.datas', compact('attendances'))->render() + ]); + } + + return view('hrmaddon::reports.attendances.index', compact('attendances', 'employees')); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooDepartmentController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooDepartmentController.php new file mode 100644 index 00000000..e1d07e3c --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooDepartmentController.php @@ -0,0 +1,97 @@ +middleware('check.permission:dashboard.read')->only(['index']); + $this->middleware('check.permission:deparment.create')->only(['store']); + $this->middleware('check.permission:department.update')->only(['update', 'status']); + $this->middleware('check.permission:department.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $departments = Department::where('business_id', auth()->user()->business_id) + ->when(request('search'), function($q) use($request) { + $q->where(function($q) use($request) { + $q->where('name', 'like', '%'.$request->search.'%') + ->orWhere('description', 'like', '%'.$request->search.'%'); + }); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if($request->ajax()){ + return response()->json([ + 'data' => view('hrmaddon::department.datas',compact('departments'))->render() + ]); + } + return view('hrmaddon::department.index', compact('departments')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + ]); + + Department::create($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id, + ]); + + return response()->json([ + 'message' => __('Department created cuccessfully'), + 'redirect' => route('hrm.department.index'), + ]); + } + + public function update(Request $request, $id) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + ]); + + Department::where('id', $id)->update($request->except(['_token', '_method'])); + + return response()->json([ + 'message' => __('Department updated successfully'), + 'redirect' => route('hrm.department.index'), + ]); + } + + public function destroy($id) + { + Department::where('id', $id)->delete(); + + return response()->json([ + 'message' => __('Department deleted successfully'), + 'redirect' => route('hrm.department.index') + ]); + } + + public function status(Request $request, $id) + { + $department = Department::findOrFail($id); + $department->update(['status' => $request->status]); + return response()->json(['message' => __('Department')]); + } + + public function deleteAll(Request $request) + { + Department::whereIn('id', $request->input('ids'))->delete(); + + return response()->json([ + 'message' => __('Selected Departments deleted successfully'), + 'redirect' => route('hrm.department.index') + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooDesignationController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooDesignationController.php new file mode 100644 index 00000000..819befc1 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooDesignationController.php @@ -0,0 +1,101 @@ +middleware('check.permission:designations.read')->only(['index']); + $this->middleware('check.permission:designations.create')->only(['store']); + $this->middleware('check.permission:designations.update')->only(['update', 'status']); + $this->middleware('check.permission:designations.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $designations = Designation::where('business_id', auth()->user()->business_id) + ->when(request('search'), function($q) use($request) { + $q->where(function($q) use($request) { + $q->where('name', 'like', '%'.$request->search.'%') + ->orWhere('description', 'like', '%'.$request->search.'%'); + }); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if($request->ajax()){ + return response()->json([ + 'data' => view('hrmaddon::designations.datas',compact('designations'))->render() + ]); + } + return view('hrmaddon::designations.index', compact('designations')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + ]); + + Designation::create($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id, + ]); + + return response()->json([ + 'message' => __('Designation created cuccessfully'), + 'redirect' => route('hrm.designations.index'), + ]); + } + + public function update(Request $request, $id) + { + $designation = Designation::findOrFail($id); + + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + ]); + + $designation->update($request->all()); + + return response()->json([ + 'message' => __('Designation updated successfully'), + 'redirect' => route('hrm.designations.index'), + ]); + } + + public function destroy($id) + { + $designation = Designation::findOrFail($id); + + $designation->delete(); + + return response()->json([ + 'message' => __('Designation deleted successfully'), + 'redirect' => route('hrm.designations.index') + ]); + } + + public function status(Request $request, $id) + { + $designation = Designation::findOrFail($id); + $designation->update(['status' => $request->status]); + return response()->json(['message' => __('Designation')]); + } + + public function deleteAll(Request $request) + { + Designation::whereIn('id', $request->input('ids'))->delete(); + + return response()->json([ + 'message' => __('Selected Designations deleted successfully'), + 'redirect' => route('hrm.designations.index') + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooEmployeeController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooEmployeeController.php new file mode 100644 index 00000000..3e97817e --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooEmployeeController.php @@ -0,0 +1,201 @@ +middleware('check.permission:employees.read')->only(['index']); + $this->middleware('check.permission:employees.create')->only(['create', 'store']); + $this->middleware('check.permission:employees.update')->only(['edit', 'update']); + $this->middleware('check.permission:employees.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $branches = Branch::withTrashed()->where('business_id', auth()->user()->business_id)->latest()->get(); + + $employees = Employee::with('department', 'designation', 'shift', 'branch:id,name') + ->where('business_id', auth()->user()->business_id) + ->when(request('search'), function ($q) { + $q->where('phone', 'like', '%' . request('search') . '%') + ->orWhere('amount', 'like', '%' . request('search') . '%') + ->orWhere('name', 'like', '%' . request('search') . '%'); + $q->orwhereHas('department', function ($d) { + $d->where('name', 'like', '%' . request('search') . '%'); + }); + $q->orwhereHas('designation', function ($r) { + $r->where('name', 'like', '%' . request('search') . '%'); + }); + $q->orwhereHas('shift', function ($s) { + $s->where('name', 'like', '%' . request('search') . '%'); + }); + $q->orwhereHas('branch', function ($r) { + $r->where('name', 'like', '%' . request('search') . '%'); + }); + }) + ->when($request->branch_id, function ($q) use ($request) { + $q->where('branch_id', $request->branch_id); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::employees.datas', compact('employees', 'branches'))->render() + ]); + } + + return view('hrmaddon::employees.index', compact('employees', 'branches')); + } + + public function create() + { + $designations = Designation::where('business_id', auth()->user()->business_id)->where('status', 1)->get(); + $departments = Department::where('business_id', auth()->user()->business_id)->where('status', 1)->get(); + $shifts = Shift::where('business_id', auth()->user()->business_id)->where('status', 1)->get(); + + return view('hrmaddon::employees.create', compact('designations', 'departments', 'shifts')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'designation_id' => 'required|exists:designations,id', + 'department_id' => 'required|exists:departments,id', + 'shift_id' => 'required|exists:shifts,id', + 'amount' => 'numeric|required', + 'phone' => 'required|string|max:20', + 'email' => 'nullable|email|max:255', + 'gender' => 'required|in:male,female,others', + 'country' => 'nullable|string|max:100', + 'birth_date' => 'nullable|date', + 'join_date' => 'nullable|date', + 'image' => 'nullable|image|mimes:jpg,png,jpeg,svg', + 'status' => 'required|in:active,terminated,suspended', + ]); + + Employee::create($request->except('business_id', 'image') + [ + 'business_id' => auth()->user()->business_id, + 'image' => $request->image ? $this->upload($request, 'image') : NULL + ]); + + return response()->json([ + 'message' => __('Employee created Successfully'), + 'redirect' => route('hrm.employees.index') + ]); + } + + + public function edit(string $id) + { + $employee = Employee::findOrFail($id); + $designations = Designation::where('business_id', auth()->user()->business_id)->where('status', 1)->get(); + $departments = Department::where('business_id', auth()->user()->business_id)->where('status', 1)->get(); + $shifts = Shift::where('business_id', auth()->user()->business_id)->where('status', 1)->get(); + + return view('hrmaddon::employees.edit', compact('employee', 'designations', 'departments', 'shifts')); + } + + public function update(Request $request, string $id) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'designation_id' => 'required|exists:designations,id', + 'department_id' => 'required|exists:departments,id', + 'shift_id' => 'required|exists:shifts,id', + 'amount' => 'numeric|required', + 'phone' => 'required|string|max:20', + 'email' => 'nullable|email|max:255', + 'gender' => 'required|in:male,female,others', + 'country' => 'nullable|string|max:100', + 'birth_date' => 'nullable|date', + 'join_date' => 'nullable|date', + 'image' => 'nullable|image|mimes:jpg,png,jpeg,svg', + 'status' => 'required|in:active,terminated,suspended', + ]); + + $employee = Employee::findOrFail($id); + + $employee->update($request->except('business_id', 'image') + [ + 'business_id' => auth()->user()->business_id, + 'image' => $request->image ? $this->upload($request, 'image', $employee->image) : $employee->image + ]); + + return response()->json([ + 'message' => __('Employee updated Successfully'), + 'redirect' => route('hrm.employees.index') + ]); + } + + public function destroy(string $id) + { + $employee = Employee::findOrFail($id); + if (file_exists($employee->image)) { + Storage::delete($employee->image); + } + $employee->delete(); + + return response()->json([ + 'message' => __('Employee deleted successfully'), + 'redirect' => route('hrm.employees.index') + ]); + } + + public function deleteAll(Request $request) + { + $employees = Employee::whereIn('id', $request->ids)->get(); + + foreach ($employees as $employee) { + if (file_exists($employee->image)) { + Storage::delete($employee->image); + } + } + Employee::whereIn('id', $request->ids)->delete(); + return response()->json([ + 'message' => __('Selected employees deleted successfully'), + 'redirect' => route('hrm.employees.index') + ]); + } + + public function bulkStore(Request $request) + { + $request->validate([ + 'file' => 'required|file|mimes:xlsx,xls,csv' + ]); + + $businessId = auth()->user()->business_id; + $import = new EmployeeImport($businessId); + + try { + Excel::import($import, $request->file('file')); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => $e->getMessage(), + ], 422); + } + + return response()->json([ + 'success' => true, + 'message' => __('Bulk upload successfully.'), + 'redirect' => route('hrm.employees.index') + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooHolidayController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooHolidayController.php new file mode 100644 index 00000000..0ef67e8a --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooHolidayController.php @@ -0,0 +1,107 @@ +middleware('check.permission:holidays.read')->only(['index']); + $this->middleware('check.permission:holidays.create')->only(['store']); + $this->middleware('check.permission:holidays.update')->only(['update']); + $this->middleware('check.permission:holidays.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $branches = Branch::withTrashed()->where('business_id', auth()->user()->business_id)->latest()->get(); + + $holidays = Holiday::with('branch:id,name') + ->where('business_id', auth()->user()->business_id) + ->when($request->branch_id, function ($q) use ($request) { + $q->where('branch_id', $request->branch_id); + }) + ->when($request->search, function ($q) use ($request) { + $search = $request->search; + $q->where(function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%') + ->orWhere('description', 'like', '%' . $search . '%') + ->orWhereHas('branch', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::holidays.datas', compact('holidays'))->render() + ]); + } + + return view('hrmaddon::holidays.index', compact('holidays', 'branches')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'description' => 'nullable|string', + ]); + + Holiday::create($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => __('Holiday created successfully'), + 'redirect' => route('hrm.holidays.index') + ]); + } + + public function update(Request $request, Holiday $holiday) + { + $request->validate([ + 'name' => 'nullable|string|max:255', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'description' => 'nullable|string', + ]); + + $holiday->update($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => __('Holiday updated successfully'), + 'redirect' => route('hrm.holidays.index') + ]); + } + + public function destroy(Holiday $holiday) + { + $holiday->delete(); + return response()->json([ + 'message' => __('Holidays Deleted Successfully'), + 'redirect' => route('hrm.holidays.index') + ]); + } + + public function deleteAll(Request $request) + { + Holiday::whereIn('id', $request->ids)->delete(); + return response()->json([ + 'message' => __('All Holiday deleted successfully.'), + 'redirect' => route('hrm.holidays.index') + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveController.php new file mode 100644 index 00000000..a1902504 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveController.php @@ -0,0 +1,167 @@ +middleware('check.permission:leaves.read')->only(['index']); + $this->middleware('check.permission:leaves.create')->only(['store']); + $this->middleware('check.permission:leaves.update')->only(['update', 'status']); + $this->middleware('check.permission:leaves.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->latest()->get(); + $departments = Department::where('business_id', auth()->user()->business_id)->whereStatus(1)->latest()->get(); + $leave_types = LeaveType::where('business_id', auth()->user()->business_id)->whereStatus(1)->latest()->get(); + + $leaves = Leave::with(['employee:id,name', 'branch:id,name', 'leave_type:id,name', 'department:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->branch_id, function ($q) use ($request) { + $q->where('branch_id', $request->branch_id); + }) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->where(function ($query) use ($search) { + $query->where('description', 'like', '%' . $search . '%') + ->orWhere('start_date', 'like', '%' . $search . '%') + ->orWhere('end_date', 'like', '%' . $search . '%') + ->orWhere('leave_duration', 'like', '%' . $search . '%') + ->orWhere('month', 'like', '%' . $search . '%') + ->orWhere('status', 'like', '%' . $search . '%') + ->orWhereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('department', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('branch', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('leave_type', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::leaves.datas', compact('leaves'))->render() + ]); + } + + $branches = Branch::withTrashed()->where('business_id', auth()->user()->business_id)->latest()->get(); + + return view('hrmaddon::leaves.index', compact('leaves', 'employees', 'departments', 'leave_types', 'branches')); + } + + public function store(Request $request) + { + $request->validate([ + 'employee_id' => 'required|exists:employees,id', + 'department_id' => 'required|exists:departments,id', + 'leave_type_id' => 'required|exists:leave_types,id', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'leave_duration' => 'required|numeric|min:0.5', + 'month' => 'required|string', + 'status' => 'required|in:pending,approved,rejected', + 'description' => 'nullable|string', + ]); + + Leave::create($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id, + ]); + + return response()->json([ + 'message' => __('Leave created successfully'), + 'redirect' => route('hrm.leaves.index') + ]); + } + + public function update(Request $request, $id) + { + $request->validate([ + 'employee_id' => 'required|exists:employees,id', + 'department_id' => 'required|exists:departments,id', + 'leave_type_id' => 'required|exists:leave_types,id', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'leave_duration' => 'required|numeric|min:0.5', + 'month' => 'required|string', + 'status' => 'required|in:pending,approved,rejected', + 'description' => 'nullable|string', + ]); + + $leave = Leave::findOrFail($id); + + $leave->update($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id, + ]); + + return response()->json([ + 'message' => __('Leave Updated successfully'), + 'redirect' => route('hrm.leaves.index') + ]); + } + + public function destroy($id) + { + $leave = Leave::findOrFail($id); + $leave->delete(); + return response()->json([ + 'message' => __('Leave Deleted Successfully'), + 'redirect' => route('hrm.leaves.index') + ]); + } + + public function deleteAll(Request $request) + { + Leave::whereIn('id', $request->input('ids'))->delete(); + + return response()->json([ + 'message' => __('All Leaves deleted successfully'), + 'redirect' => route('hrm.leaves.index') + ]); + } + + public function status(Request $request, $id) + { + $order = Leave::findOrFail($id); + $order->update(['status' => $request->status]); + return response()->json(['message' => 'Leave']); + } + + public function getDepartment() + { + $employee = Employee::findOrFail(request('employee_id')); + $department = Department::findOrFail($employee->department_id); + + return response()->json([ + 'data' => [ + 'id' => $department->id, + 'name' => $department->name + ] + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveReportController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveReportController.php new file mode 100644 index 00000000..3a142a96 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveReportController.php @@ -0,0 +1,84 @@ +middleware('check.permission:leave-reports.read')->only(['index']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + + $leaves = Leave::with(['employee:id,name', 'leave_type:id,name', 'department:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->where(function ($query) use ($search) { + $query->where('description', 'like', '%' . $search . '%') + ->orWhere('start_date', 'like', '%' . $search . '%') + ->orWhere('end_date', 'like', '%' . $search . '%') + ->orWhere('leave_duration', 'like', '%' . $search . '%') + ->orWhere('month', 'like', '%' . $search . '%') + ->orWhere('status', 'like', '%' . $search . '%') + ->orWhereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('department', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('leave_type', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::reports.leaves.datas', compact('leaves'))->render() + ]); + } + + return view('hrmaddon::reports.leaves.index', compact('leaves', 'employees')); + } + + public function exportExcel() + { + return Excel::download(new ExportLeaveReport, 'leave-report.xlsx'); + } + + public function exportCsv() + { + return Excel::download(new ExportLeaveReport, 'leave-report.csv'); + } + + public function exportPdf(Request $request) + { + $leaves = Leave::with(['employee:id,name', 'leave_type:id,name', 'department:id,name']) + ->where('business_id', auth()->user()->business_id) + ->latest() + ->get(); + + return PdfService::render('hrmaddon::reports.leaves.pdf', compact('leaves'),'leave-report.pdf'); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveReportHistoryController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveReportHistoryController.php new file mode 100644 index 00000000..8811bb68 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveReportHistoryController.php @@ -0,0 +1,61 @@ +middleware('check.permission:leave-reports.read')->only(['index']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + + $leaves = Leave::with(['employee:id,name', 'leave_type:id,name', 'department:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->where(function ($query) use ($search) { + $query->where('description', 'like', '%' . $search . '%') + ->orWhere('start_date', 'like', '%' . $search . '%') + ->orWhere('end_date', 'like', '%' . $search . '%') + ->orWhere('leave_duration', 'like', '%' . $search . '%') + ->orWhere('month', 'like', '%' . $search . '%') + ->orWhere('status', 'like', '%' . $search . '%') + ->orWhereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('department', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('leave_type', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::reports.leaves.datas', compact('leaves'))->render() + ]); + } + + return view('hrmaddon::reports.leaves.index', compact('leaves', 'employees')); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveTypeController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveTypeController.php new file mode 100644 index 00000000..de900b8a --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooLeaveTypeController.php @@ -0,0 +1,100 @@ +middleware('check.permission:leave-types.read')->only(['index']); + $this->middleware('check.permission:leave-types.create')->only(['store']); + $this->middleware('check.permission:leave-types.update')->only(['update', 'status']); + $this->middleware('check.permission:leave-types.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $leave_types = LeaveType::where('business_id', auth()->user()->business_id) + ->when(request('search'), function($q) use($request) { + $q->where(function($q) use($request) { + $q->where('name', 'like', '%'.$request->search.'%') + ->orWhere('description', 'like', '%'.$request->search.'%'); + }); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if($request->ajax()){ + return response()->json([ + 'data' => view('hrmaddon::leave-types.datas',compact('leave_types'))->render() + ]); + } + return view('hrmaddon::leave-types.index',compact('leave_types')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + ]); + + LeaveType::create($request->except('business_id') +[ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => 'Leave Type created cuccessfully.', + 'redirect' => route('hrm.leave-types.index'), + ]); + } + + public function update(Request $request, LeaveType $leaveType) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + ]); + + $leaveType->update($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => 'Leave Type updated successfully.', + 'redirect' => route('hrm.leave-types.index'), + ]); + } + + public function destroy(LeaveType $leaveType) + { + $leaveType->delete(); + + return response()->json([ + 'message' => 'Leave Type deleted successfully', + 'redirect' => route('hrm.leave-types.index') + ]); + } + + public function status(Request $request, $id) + { + $leaveType = LeaveType::findOrFail($id); + $leaveType->update(['status' => $request->status]); + return response()->json(['message' => 'Leave Type']); + } + + public function deleteAll(Request $request) + { + $leave_type = LeaveType::whereIn('id', $request->input('ids')); + $leave_type->delete(); + + return response()->json([ + 'message' => __('All Leave Types deleted successfully.'), + 'redirect' => route('hrm.leave-types.index') + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollController.php new file mode 100644 index 00000000..f587fa4f --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollController.php @@ -0,0 +1,219 @@ +middleware('check.permission:payrolls.read')->only(['index']); + $this->middleware('check.permission:payrolls.create')->only(['store']); + $this->middleware('check.permission:payrolls.update')->only(['update']); + $this->middleware('check.permission:payrolls.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + $payment_types = PaymentType::where('business_id', auth()->user()->business_id)->whereStatus(1)->latest()->get(); + + $payrolls = Payroll::with(['employee:id,name', 'payment_type:id,name', 'branch:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->branch_id, function ($q) use ($request) { + $q->where('branch_id', $request->branch_id); + }) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->where(function ($query) use ($search) { + $query->where('puid', 'like', '%' . $search . '%') + ->orWhere('amount', 'like', '%' . $search . '%') + ->orWhereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('branch', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('payment_type', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::payrolls.datas', compact('payrolls'))->render() + ]); + } + + $branches = Branch::withTrashed()->where('business_id', auth()->user()->business_id)->latest()->get(); + + return view('hrmaddon::payrolls.index', compact('payrolls', 'employees', 'payment_types', 'branches')); + } + + public function store(Request $request) + { + $request->validate([ + 'employee_id' => 'required|integer|exists:employees,id', + 'payment_type_id' => 'required', + 'month' => 'required|string', + 'date' => 'nullable|date', + 'amount' => 'required|numeric', + 'note' => 'nullable|string', + 'payemnt_year' => 'required|string', + ]); + + DB::beginTransaction(); + try { + + $exists = Payroll::where('employee_id', $request->employee_id) + ->where('month', $request->month) + ->where('payemnt_year', $request->payemnt_year) + ->where('business_id', auth()->user()->business_id) + ->exists(); + + if ($exists) { + return response()->json([ + 'message' => __('This employee has already been paid for this month and year.'), + ], 422); + } + + updateBalance($request->amount, 'decrement'); + + $payroll = Payroll::create($request->except('business_id', 'payment_type_id') + [ + 'business_id' => auth()->user()->business_id, + 'payment_type_id' => $request->payment_type_id != 'cash' ? $request->payment_type_id : NULL, + ]); + + $payments = [['amount' => $request->amount, 'type' => $request->payment_type_id]]; + + + // MultiPaymentProcessed Event + event(new MultiPaymentProcessed( + $payments, + $payroll->id, + 'payroll', + $total_amount ?? 0, + )); + + DB::commit(); + + return response()->json([ + 'message' => __('Payroll created successfully'), + 'redirect' => route('hrm.payrolls.index') + ]); + } catch (\Exception $e) { + DB::rollback(); + return response()->json(['message' => __('Somethings went wrong!')], 404); + } + } + + public function update(Request $request, $id) + { + $request->validate([ + 'employee_id' => 'required|integer|exists:employees,id', + 'payment_type_id' => 'required', + 'date' => 'nullable|date', + 'month' => 'required|string', + 'amount' => 'required|numeric', + 'note' => 'nullable|string', + 'payemnt_year' => 'required|string', + ]); + + DB::beginTransaction(); + try { + + $business_id = auth()->user()->business_id; + $payroll = Payroll::where('business_id', $business_id)->findOrFail($id); + + updateBalance($request->amount - $payroll->amount, 'decrement'); + + $payroll->update($request->except(['business_id', 'amount', 'payment_type_id']) + [ + 'payment_type_id' => $request->payment_type_id != 'cash' ? $request->payment_type_id : NULL + ]); + + // Delete old transactions + $transaction = Transaction::where('business_id', $business_id) + ->where([ + 'platform' => 'payroll', + 'reference_id' => $payroll->id + ]) + ->first(); + + if ($transaction->bank_payment) { + PaymentType::find($transaction->payment_type_id)->decrement('balance', $transaction->amount); + } + $transaction->delete(); + + $payments = [['amount' => $request->amount, 'type' => $request->payment_type_id]]; + + // Process new payments + event(new MultiPaymentProcessed( + $payments, + $payroll->id, + 'payroll', + $request->amount, + )); + + DB::commit(); + + return response()->json([ + 'message' => __('Payroll Updated successfully'), + 'redirect' => route('hrm.payrolls.index') + ]); + } catch (\Exception $e) { + DB::rollback(); + return response()->json(['message' => $e->getMessage()], 404); + } + } + + public function destroy($id) + { + $payroll = Payroll::findOrFail($id); + + $payroll->delete(); + return response()->json([ + 'message' => __('Payroll Deleted Successfully'), + 'redirect' => route('hrm.payrolls.index') + ]); + } + + public function deleteAll(Request $request) + { + Payroll::whereIn('id', $request->input('ids'))->delete(); + + return response()->json([ + 'message' => __('All Payrolls deleted successfully'), + 'redirect' => route('hrm.payrolls.index') + ]); + } + + public function getEmpAmount() + { + $employee = Employee::findOrFail(request('employee_id')); + + return response()->json([ + 'data' => [ + 'amount' => $employee->amount + ] + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollReportController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollReportController.php new file mode 100644 index 00000000..5f0b03d7 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollReportController.php @@ -0,0 +1,77 @@ +middleware('check.permission:payroll-reports.read')->only(['index']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + + $payrolls = Payroll::with(['employee:id,name', 'payment_type:id,name', 'branch:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->where(function ($query) use ($search) { + $query->where('puid', 'like', '%' . $search . '%') + ->orWhere('amount', 'like', '%' . $search . '%') + ->orWhereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('payment_type', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::reports.payrolls.datas', compact('payrolls'))->render() + ]); + } + + return view('hrmaddon::reports.payrolls.index', compact('payrolls', 'employees')); + } + + public function exportExcel() + { + return Excel::download(new ExportPayrollReport, 'payroll-report.xlsx'); + } + + public function exportCsv() + { + return Excel::download(new ExportPayrollReport, 'payroll-report.csv'); + } + + public function exportPdf(Request $request) + { + $payrolls = Payroll::with(['employee:id,name', 'payment_type:id,name', 'branch:id,name']) + ->where('business_id', auth()->user()->business_id) + ->latest() + ->get(); + + return PdfService::render('hrmaddon::reports.payrolls.pdf', compact('payrolls'),'payroll-report.pdf'); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollReportHistoryController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollReportHistoryController.php new file mode 100644 index 00000000..d56d1d3f --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooPayrollReportHistoryController.php @@ -0,0 +1,54 @@ +middleware('check.permission:payroll-reports.read')->only(['index']); + } + + public function index(Request $request) + { + $employees = Employee::where('business_id', auth()->user()->business_id)->whereStatus('active')->latest()->get(); + + $payrolls = Payroll::with(['employee:id,name', 'payment_type:id,name', 'branch:id,name']) + ->where('business_id', auth()->user()->business_id) + ->when($request->search, function ($query) use ($request) { + $search = $request->search; + $query->where(function ($query) use ($search) { + $query->where('puid', 'like', '%' . $search . '%') + ->orWhere('amount', 'like', '%' . $search . '%') + ->orWhereHas('employee', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }) + ->orWhereHas('payment_type', function ($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%'); + }); + }); + }) + ->when($request->employee, function ($query) use ($request) { + $employeeId = $request->employee; + $query->where('employee_id', $employeeId); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $query->where('month', $request->month); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::reports.payrolls.datas', compact('payrolls'))->render() + ]); + } + + return view('hrmaddon::reports.payrolls.index', compact('payrolls', 'employees')); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/AcnooShiftController.php b/Modules/HrmAddon/App/Http/Controllers/AcnooShiftController.php new file mode 100644 index 00000000..65a3dee1 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/AcnooShiftController.php @@ -0,0 +1,135 @@ +middleware('check.permission:shifts.read')->only(['index']); + $this->middleware('check.permission:shifts.create')->only(['store']); + $this->middleware('check.permission:shifts.update')->only(['update', 'status']); + $this->middleware('check.permission:shifts.delete')->only(['destroy', 'deleteAll']); + } + + public function index(Request $request) + { + $shifts = Shift::where('business_id', auth()->user()->business_id) + ->when(request('search'), function ($q) use ($request) { + $q->where(function ($q) use ($request) { + $q->where('name', 'like', '%' . $request->search . '%') + ->orWhere('start_time', 'like', '%' . $request->search . '%') + ->orWhere('end_time', 'like', '%' . $request->search . '%'); + }); + }) + ->latest() + ->paginate($request->per_page ?? 20)->appends($request->query()); + + if ($request->ajax()) { + return response()->json([ + 'data' => view('hrmaddon::shifts.datas', compact('shifts'))->render() + ]); + } + return view('hrmaddon::shifts.index', compact('shifts')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|in:Morning,Day,Evening,Night', + 'start_time' => 'required|date_format:H:i', + 'end_time' => 'required|date_format:H:i', + 'start_break_time' => 'nullable|date_format:H:i', + 'end_break_time' => 'nullable|date_format:H:i', + 'break_status' => 'nullable|in:yes,no', + ]); + + if ($request->filled('start_break_time') && $request->filled('end_break_time')) { + $startTime = Carbon::parse($request->input('start_break_time')); + $endTime = Carbon::parse($request->input('end_break_time')); + + $hoursDiff = $endTime->diffInHours($startTime); + $minutesDiff = $endTime->diffInMinutes($startTime) % 60; + + $breakTime = sprintf('%02d:%02d', $hoursDiff, $minutesDiff); + } else { + $breakTime = null; + } + + Shift::create($request->except('status','break_time', 'business_id')+[ + 'business_id' => auth()->user()->business_id, + 'break_time' => $breakTime, + ]); + + return response()->json([ + 'message' => __('Shift created successfully.'), + 'redirect' => route('hrm.shifts.index') + ]); + } + + public function update(Request $request, Shift $shift) + { + $request->validate([ + 'name' => 'required|in:Morning,Day,Evening,Night', + 'start_time' => 'required', + 'end_time' => 'required', + 'start_break_time' => 'nullable', + 'end_break_time' => 'nullable', + 'break_status' => 'nullable|in:yes,no', + ]); + + if ($request->filled('start_break_time') && $request->filled('end_break_time')) { + $startTime = Carbon::parse($request->input('start_break_time')); + $endTime = Carbon::parse($request->input('end_break_time')); + + $hoursDiff = $endTime->diffInHours($startTime); + $minutesDiff = $endTime->diffInMinutes($startTime) % 60; + + $breakTime = sprintf('%02d:%02d', $hoursDiff, $minutesDiff); + } else { + $breakTime = null; + } + + $shift->update($request->except('status','break_time', 'business_id')+[ + 'business_id' => auth()->user()->business_id, + 'break_time' => $breakTime, + ]); + + return response()->json([ + 'message' => __('Shift Updated successfully.'), + 'redirect' => route('hrm.shifts.index') + ]); + } + + public function destroy($id) + { + $shift = Shift::findOrFail($id); + $shift->delete(); + return response()->json([ + 'message' => __('Shift deleted successfully.'), + 'redirect' => route('hrm.shifts.index') + ]); + } + + public function deleteAll(Request $request) + { + Shift::whereIn('id', $request->input('ids'))->delete(); + + return response()->json([ + 'message' => __('All shifts deleted successfully'), + 'redirect' => route('hrm.shifts.index') + ]); + } + + public function status(Request $request, $id) + { + $shift = Shift::findOrFail($id); + $shift->update(['status' => $request->status]); + return response()->json(['message' => 'Shift']); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/AttendanceController.php b/Modules/HrmAddon/App/Http/Controllers/Api/AttendanceController.php new file mode 100644 index 00000000..dd535c7f --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/AttendanceController.php @@ -0,0 +1,176 @@ +where('business_id', auth()->user()->business_id) + ->when($request->employee_id, function ($query) use ($request) { + $query->where('employee_id', $request->employee_id); + }) + ->when($request->duration, function ($query) use ($request) { + $today = Carbon::today(); + + if ($request->duration === 'today') { + $query->whereDate('date', $today); + } elseif ($request->duration === 'yesterday') { + $query->whereDate('date', Carbon::yesterday()); + } elseif ($request->duration === 'last_seven_days') { + $startDate = Carbon::now()->subDays(7)->format('Y-m-d'); + $endDate = Carbon::now()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'last_thirty_days') { + $startDate = Carbon::now()->subDays(30)->format('Y-m-d'); + $endDate = Carbon::now()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'current_month') { + $startDate = Carbon::now()->startOfMonth()->format('Y-m-d'); + $endDate = Carbon::now()->endOfMonth()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'last_month') { + $startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d'); + $endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'current_year') { + $startDate = Carbon::now()->startOfYear()->format('Y-m-d'); + $endDate = Carbon::now()->endOfYear()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'custom_date' && $request->from_date && $request->to_date) { + $startDate = Carbon::parse($request->from_date)->format('Y-m-d'); + $endDate = Carbon::parse($request->to_date)->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } + }) + ->latest() + ->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'employee_id' => 'required|exists:employees,id', + 'time_in' => 'required|date_format:H:i', + 'time_out' => 'required|date_format:H:i', + 'date' => 'required|date', + 'note' => 'nullable|string', + ]); + + $timeIn = Carbon::parse($request->time_in); + $timeOut = Carbon::parse($request->time_out); + + $durationInMinutes = $timeOut->diffInMinutes($timeIn); + $hours = floor($durationInMinutes / 60); + $minutes = $durationInMinutes % 60; + $formattedDuration = sprintf('%02d:%02d', $hours, $minutes); + $month = strtolower(Carbon::parse($request->date)->format('F')); + + $business = Business::findOrFail(auth()->user()->business_id); + $employee = Employee::findOrFail($request->employee_id); + + $data = Attendance::create($request->except(['business_id', 'duration', 'month', 'shift_id']) + [ + 'business_id' => auth()->user()->business_id, + 'duration' => $formattedDuration, + 'month' => $month, + 'shift_id' => $employee->shift_id, + ]); + + if (moduleCheck('MarketingAddon')) { + $this->attendanceMessage($business, $data, $employee); + } + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function show($id) + { + $data = Attendance::with(['employee:id,name', 'shift:id,name']) + ->where('business_id', auth()->user()->business_id) + ->findOrFail($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, $id) + { + $attendance = Attendance::where('business_id', auth()->user()->business_id)->find($id); + + if (!$attendance) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $request->validate([ + 'employee_id' => 'required|exists:employees,id', + 'time_in' => 'required|date_format:H:i', + 'time_out' => 'required|date_format:H:i', + 'date' => 'required|date', + 'note' => 'nullable|string', + ]); + + $timeIn = Carbon::parse($request->time_in); + $timeOut = Carbon::parse($request->time_out); + + $durationInMinutes = $timeOut->diffInMinutes($timeIn); + $hours = floor($durationInMinutes / 60); + $minutes = $durationInMinutes % 60; + $formattedDuration = sprintf('%02d:%02d', $hours, $minutes); + $month = strtolower(Carbon::parse($request->date)->format('F')); + $employee = Employee::findOrFail($request->employee_id); + + $attendance->update($request->except(['duration', 'month']) + [ + 'duration' => $formattedDuration, + 'month' => $month, + 'shift_id' => $employee->shift_id, + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $attendance, + ]); + } + + public function destroy($id) + { + $attendance = Attendance::where('business_id', auth()->user()->business_id)->find($id); + + if (!$attendance) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $attendance->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/AttendanceReportController.php b/Modules/HrmAddon/App/Http/Controllers/Api/AttendanceReportController.php new file mode 100644 index 00000000..fab896b9 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/AttendanceReportController.php @@ -0,0 +1,66 @@ +where('business_id', auth()->user()->business_id) + ->when($request->employee_id, function ($query) use ($request) { + $query->where('employee_id', $request->employee_id); + }) + ->when($request->duration, function ($query) use ($request) { + $today = Carbon::today(); + + if ($request->duration === 'today') { + $query->whereDate('date', $today); + } elseif ($request->duration === 'yesterday') { + $query->whereDate('date', Carbon::yesterday()); + } elseif ($request->duration === 'last_seven_days') { + $startDate = Carbon::now()->subDays(7)->format('Y-m-d'); + $endDate = Carbon::now()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'last_thirty_days') { + $startDate = Carbon::now()->subDays(30)->format('Y-m-d'); + $endDate = Carbon::now()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'current_month') { + $startDate = Carbon::now()->startOfMonth()->format('Y-m-d'); + $endDate = Carbon::now()->endOfMonth()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'last_month') { + $startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d'); + $endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'current_year') { + $startDate = Carbon::now()->startOfYear()->format('Y-m-d'); + $endDate = Carbon::now()->endOfYear()->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } elseif ($request->duration === 'custom_date' && $request->from_date && $request->to_date) { + $startDate = Carbon::parse($request->from_date)->format('Y-m-d'); + $endDate = Carbon::parse($request->to_date)->format('Y-m-d'); + $query->whereDate('date', '>=', $startDate) + ->whereDate('date', '<=', $endDate); + } + }) + ->latest() + ->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/DepartmentController.php b/Modules/HrmAddon/App/Http/Controllers/Api/DepartmentController.php new file mode 100644 index 00000000..79ad8427 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/DepartmentController.php @@ -0,0 +1,96 @@ +user()->business_id) + ->when(request('status'), function ($query) { + $query->where('status', request('status') == 'active' ? 1 : 0); + }) + ->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|unique:departments,name,NULL,id,business_id,' . auth()->user()->business_id, + 'description' => 'nullable|string', + 'status' => 'nullable|boolean', + ]); + + $data = Department::create($request->all() + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function show($id) + { + $data = Department::where('business_id', auth()->user()->business_id)->findOrFail($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, $id) + { + $department = Department::where('business_id', auth()->user()->business_id) + ->find($id); + + if (!$department) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $request->validate([ + 'name' => 'required|unique:departments,name,' . $id . ',id,business_id,' . auth()->user()->business_id, + 'description' => 'nullable|string', + 'status' => 'nullable|boolean', + ]); + + $department->update($request->except('business_id')); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $department, + ]); + } + + public function destroy($id) + { + $department = Department::where('business_id', auth()->user()->business_id) + ->find($id); + + if (!$department) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $department->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/DesignationController.php b/Modules/HrmAddon/App/Http/Controllers/Api/DesignationController.php new file mode 100644 index 00000000..0211d3ac --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/DesignationController.php @@ -0,0 +1,95 @@ +user()->business_id) + ->when(request('status'), function ($query) { + $query->where('status', request('status') == 'active' ? 1 : 0); + }) + ->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|unique:designations,name,NULL,id,business_id,' . auth()->user()->business_id, + 'description' => 'nullable|string', + 'status' => 'nullable|boolean', + ]); + + $data = Designation::create($request->all() + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function show($id) + { + $data = Designation::where('business_id', auth()->user()->business_id)->findOrFail($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, $id) + { + $designation = Designation::where('business_id', auth()->user()->business_id)->find($id); + + if (!$designation) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $request->validate([ + 'name' => 'required|unique:designations,name,' . $id . ',id,business_id,' . auth()->user()->business_id, + 'description' => 'nullable|string', + 'status' => 'nullable|boolean', + ]); + + $designation->update($request->except('business_id')); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $designation, + ]); + } + + public function destroy($id) + { + $designation = Designation::where('business_id', auth()->user()->business_id) + ->find($id); + + if (!$designation) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $designation->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/EmployeeController.php b/Modules/HrmAddon/App/Http/Controllers/Api/EmployeeController.php new file mode 100644 index 00000000..53f822ea --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/EmployeeController.php @@ -0,0 +1,126 @@ +where('business_id', auth()->user()->business_id) + ->when(request('status'), function ($query) { + $status = request('status'); + if (in_array($status, ['active', 'terminated', 'suspended'])) { + $query->where('status', $status); + } + }) + ->latest() + ->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + + } + + public function store(Request $request) + { + $request->validate($this->validationRules()); + + $data = Employee::create($request->except('business_id', 'image') + [ + 'business_id' => auth()->user()->business_id, + 'image' => $request->image ? $this->upload($request, 'image') : NULL + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function show($id) + { + $data = Employee::with('department:id,name', 'designation:id,name', 'shift:id,name', 'branch:id,name') + ->where('business_id', auth()->user()->business_id) + ->findOrFail($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, $id) + { + $employee = Employee::where('business_id', auth()->user()->business_id) + ->find($id); + + if (!$employee) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $request->validate($this->validationRules($id)); + + $data = $employee->update($request->except('business_id', 'image') + [ + 'image' => $request->image ? $this->upload($request, 'image', $employee->image) : $employee->image + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function destroy($id) + { + $employee = Employee::where('business_id', auth()->user()->business_id) + ->find($id); + + if (!$employee) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + if (file_exists($employee->image)) { + Storage::delete($employee->image); + } + + $employee->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } + + // Common validation for store/update. + protected function validationRules($id = null) + { + return [ + 'name' => 'required|string|max:255', + 'designation_id' => 'required|exists:designations,id', + 'department_id' => 'required|exists:departments,id', + 'shift_id' => 'required|exists:shifts,id', + 'amount' => 'required|numeric', + 'phone' => 'required|string|max:20', + 'email' => 'nullable|email|max:255', + 'gender' => 'required|in:male,female,others', + 'country' => 'nullable|string|max:100', + 'birth_date' => 'nullable|date', + 'join_date' => 'nullable|date', + 'image' => 'nullable|image|mimes:jpg,png,jpeg,svg', + 'status' => 'required|in:active,terminated,suspended', + ]; + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/HolidayController.php b/Modules/HrmAddon/App/Http/Controllers/Api/HolidayController.php new file mode 100644 index 00000000..f75d2ea8 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/HolidayController.php @@ -0,0 +1,86 @@ +where('business_id', auth()->user()->business_id)->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $holidays, + ]); + } + + public function show($id) + { + $data = Holiday::where('business_id', auth()->user()->business_id)->findOrFail($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'start_date' => 'required|date', + 'end_date' => 'required|date', + 'description' => 'nullable|string', + ]); + + $data = Holiday::create($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, string $id) + { + $request->validate([ + 'name' => 'nullable|string|max:255', + 'start_date' => 'required|date', + 'end_date' => 'required|date', + 'description' => 'nullable|string', + ]); + + $holiday = Holiday::where('business_id', auth()->user()->business_id)->findOrFail($id); + + $holiday->update($request->except('business_id')); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $holiday, + ]); + } + + public function destroy(string $id) + { + $holiday = Holiday::where('business_id', auth()->user()->business_id)->findOrFail($id); + + if (!$holiday) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $holiday->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } + +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/LeaveController.php b/Modules/HrmAddon/App/Http/Controllers/Api/LeaveController.php new file mode 100644 index 00000000..b8ddd132 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/LeaveController.php @@ -0,0 +1,117 @@ +where('business_id', auth()->user()->business_id) + ->when($request->employee_id, function ($query) use ($request) { + $query->where('employee_id', $request->employee_id); + }) + ->when($request->month, function ($query) use ($request) { + $month = strtolower($request->month); + $query->whereRaw("LOWER(month) = ?", [$month]); + }) + ->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $leaves, + ]); + } + + public function show($id) + { + $data = Leave::with(['employee:id,name', 'branch:id,name', 'leave_type:id,name', 'department:id,name'])->where('business_id', auth()->user()->business_id)->find($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'employee_id' => 'required|exists:employees,id', + 'leave_type_id' => 'required|exists:leave_types,id', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'leave_duration' => 'required|numeric|min:0.5', + 'month' => 'required|string', + 'status' => 'required|in:pending,approved,rejected', + 'description' => 'nullable|string', + ]); + + $employee = Employee::where('business_id', auth()->user()->business_id)->findOrFail($request->employee_id); + + $data = Leave::create($request->except(['business_id', 'department_id']) + [ + 'business_id' => auth()->user()->business_id, + 'department_id' => $employee->department_id, + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, string $id) + { + $request->validate([ + 'employee_id' => 'required|exists:employees,id', + 'leave_type_id' => 'required|exists:leave_types,id', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'leave_duration' => 'required|numeric|min:0.5', + 'month' => 'required|string', + 'status' => 'required|in:pending,approved,rejected', + 'description' => 'nullable|string', + ]); + + $leave = Leave::find($id); + + if (!$leave) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $employee = Employee::where('business_id', auth()->user()->business_id)->findOrFail($request->employee_id); + + $leave->update($request->except('business_id','department_id') + [ + 'department_id' => $employee->department_id, + ]); + + return response()->json([ + 'message' => __('Data update successfully.'), + 'data' => $leave, + ]); + } + + public function destroy(string $id) + { + $leave = Leave::find($id); + + if (!$leave) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $leave->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/LeaveReportController.php b/Modules/HrmAddon/App/Http/Controllers/Api/LeaveReportController.php new file mode 100644 index 00000000..b49dcdfe --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/LeaveReportController.php @@ -0,0 +1,31 @@ +where('business_id', auth()->user()->business_id) + ->when($request->employee_id, function ($query) use ($request) { + $query->where('employee_id', $request->employee_id); + }) + ->when($request->month, function ($query) use ($request) { + $month = strtolower($request->month); + $query->whereRaw("LOWER(month) = ?", [$month]); + }) + ->latest() + ->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/LeaveTypeController.php b/Modules/HrmAddon/App/Http/Controllers/Api/LeaveTypeController.php new file mode 100644 index 00000000..c86a43f8 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/LeaveTypeController.php @@ -0,0 +1,86 @@ +user()->business_id)->when(request('search'), function ($query) { + $query->where('name', 'like', '%' . request('search') . '%') + ->orwhere('description', 'like', '%' . request('search') . '%'); + })->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $types, + ]); + } + + public function show($id) + { + $data = LeaveType::where('business_id', auth()->user()->business_id)->find($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'status' => 'nullable|boolean', + ]); + + $data = LeaveType::create($request->except('business_id') + [ + 'business_id' => auth()->user()->business_id + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, string $id) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'status' => 'nullable|boolean', + ]); + + $leaveType = LeaveType::where('business_id', auth()->user()->business_id)->find($id); + + $leaveType->update($request->except('business_id')); + + return response()->json([ + 'message' => __('Data update successfully.'), + 'data' => $leaveType, + ]); + } + + public function destroy(string $id) + { + $leaveType = LeaveType::where('business_id', auth()->user()->business_id)->find($id); + + if (!$leaveType) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $leaveType->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/PayrollController.php b/Modules/HrmAddon/App/Http/Controllers/Api/PayrollController.php new file mode 100644 index 00000000..1fa6fd53 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/PayrollController.php @@ -0,0 +1,137 @@ +where('business_id', auth()->user()->business_id)->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $payrolls, + ]); + } + + public function show($id) + { + $data = Payroll::with(['employee:id,name', 'branch:id,name', 'transactions:id,platform,transaction_type,amount,date,invoice_no,reference_id,payment_type_id,meta', 'transactions.paymentType:id,name'])->where('business_id', auth()->user()->business_id)->find($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'employee_id' => 'required|integer|exists:employees,id', + 'month' => 'required|string', + 'date' => 'nullable|date', + 'note' => 'nullable|string', + 'payemnt_year' => 'required|string', + ]); + + $exists = Payroll::where('employee_id', $request->employee_id) + ->where('month', $request->month) + ->where('payemnt_year', $request->payemnt_year) + ->where('business_id', auth()->user()->business_id) + ->exists(); + + if ($exists) { + return response()->json([ + 'message' => __('This employee has already been paid for this month and year.'), + ], 422); + } + + $employee = Employee::where('business_id', auth()->user()->business_id)->findOrFail($request->employee_id); + + $data = Payroll::create($request->except(['business_id', 'amount']) + [ + 'business_id' => auth()->user()->business_id, + 'amount' => $employee->amount, + ]); + + // Handle payments + $payments = collect($request->payments ?? [])->map(function ($payment) use ($employee) { + $payment['amount'] = $employee->amount; + return $payment; + })->toArray(); + + // Trigger multipayment event + event(new MultiPaymentProcessed( + $payments, + $data->id, + 'payroll', + $employee->amount ?? 0, + )); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, string $id) + { + $request->validate([ + 'employee_id' => 'required|integer|exists:employees,id', + 'date' => 'nullable|date', + 'month' => 'required|string', + 'note' => 'nullable|string', + 'payemnt_year' => 'required|string', + ]); + + $Payroll = Payroll::find($id); + + if (!$Payroll) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $exists = Payroll::where('employee_id', $request->employee_id) + ->where('month', $request->month) + ->where('payemnt_year', $request->payemnt_year) + ->where('business_id', auth()->user()->business_id) + ->where('id', '!=', $id) + ->exists(); + + if ($exists) { + return response()->json([ + 'message' => __('This employee has already been paid for this month and year.'), + ], 422); + } + + $Payroll->update($request->except(['business_id', 'amount'])); + + return response()->json([ + 'message' => __('Data update successfully.'), + 'data' => $Payroll, + ]); + } + + public function destroy(string $id) + { + $Payroll = Payroll::where('business_id', auth()->user()->business_id)->find($id); + + if (!$Payroll) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $Payroll->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/PayrollReportController.php b/Modules/HrmAddon/App/Http/Controllers/Api/PayrollReportController.php new file mode 100644 index 00000000..934f0579 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/PayrollReportController.php @@ -0,0 +1,30 @@ +where('business_id', auth()->user()->business_id) + ->when($request->employee_id, function ($query) use ($request) { + $query->where('employee_id', $request->employee_id); + }) + ->when($request->filled('month'), function ($query) use ($request) { + $search = $request->month; + $query->where('month', 'like', '%' . $search . '%'); + }) + ->latest() + ->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } +} diff --git a/Modules/HrmAddon/App/Http/Controllers/Api/ShiftController.php b/Modules/HrmAddon/App/Http/Controllers/Api/ShiftController.php new file mode 100644 index 00000000..02c6a627 --- /dev/null +++ b/Modules/HrmAddon/App/Http/Controllers/Api/ShiftController.php @@ -0,0 +1,150 @@ +user()->business_id) + ->when(request('status'), function ($query) { + $query->where('status', request('status') == 'active' ? 1 : 0); + }) + ->latest()->get(); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|in:Morning,Day,Evening,Night', + 'start_time' => 'required|date_format:H:i', + 'end_time' => 'required|date_format:H:i', + 'start_break_time' => 'nullable|date_format:H:i', + 'end_break_time' => 'nullable|date_format:H:i', + 'break_status' => 'nullable|in:yes,no', + ]); + + if ($request->start_break_time && $request->end_break_time) { + $startTime = Carbon::parse($request->input('start_break_time')); + $endTime = Carbon::parse($request->input('end_break_time')); + + $hoursDiff = $endTime->diffInHours($startTime); + $minutesDiff = $endTime->diffInMinutes($startTime) % 60; + + $breakTime = sprintf('%02d:%02d', $hoursDiff, $minutesDiff); + } else { + $breakTime = null; + } + + $data = Shift::create($request->except('break_time', 'business_id') + [ + 'business_id' => auth()->user()->business_id, + 'break_time' => $breakTime, + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $data, + ]); + } + + public function show($id) + { + $data = Shift::where('business_id', auth()->user()->business_id)->findOrFail($id); + + return response()->json([ + 'message' => __('Data fetched successfully.'), + 'data' => $data, + ]); + } + + public function update(Request $request, $id) + { + $shift = Shift::where('business_id', auth()->user()->business_id)->find($id); + + if (!$shift) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $request->validate([ + 'name' => 'required|in:Morning,Day,Evening,Night', + 'start_time' => 'required|date_format:H:i', + 'end_time' => 'required|date_format:H:i', + 'start_break_time' => 'nullable|date_format:H:i', + 'end_break_time' => 'nullable|date_format:H:i', + 'break_status' => 'nullable|in:yes,no', + ]); + + if ($request->start_break_time && $request->end_break_time) { + $startTime = Carbon::parse($request->input('start_break_time')); + $endTime = Carbon::parse($request->input('end_break_time')); + + $hoursDiff = $endTime->diffInHours($startTime); + $minutesDiff = $endTime->diffInMinutes($startTime) % 60; + + $breakTime = sprintf('%02d:%02d', $hoursDiff, $minutesDiff); + } else { + $breakTime = null; + } + + $shift->update($request->except('break_time', 'business_id') + [ + 'break_time' => $breakTime, + ]); + + return response()->json([ + 'message' => __('Data saved successfully.'), + 'data' => $shift, + ]); + } + + public function destroy($id) + { + $shift = Shift::where('business_id', auth()->user()->business_id) + ->find($id); + + if (!$shift) { + return response()->json([ + 'message' => __('Data not found.'), + ], 404); + } + + $shift->delete(); + + return response()->json([ + 'message' => __('Data deleted successfully.'), + ]); + } + + public function status(Request $request, $id) + { + $request->validate([ + 'status' => 'required|boolean' + ]); + + $shift = Shift::where('business_id', auth()->user()->business_id)->find($id); + + if (!$shift) { + return response()->json([ + 'message' => 'Shift not found.' + ], 404); + } + + $shift->update(['status' => $request->status]); + + return response()->json([ + 'message' => 'Shift status updated successfully.', + 'data' => $shift + ]); + } +} diff --git a/Modules/HrmAddon/App/Models/Attendance.php b/Modules/HrmAddon/App/Models/Attendance.php new file mode 100644 index 00000000..9582868b --- /dev/null +++ b/Modules/HrmAddon/App/Models/Attendance.php @@ -0,0 +1,69 @@ +belongsTo(Branch::class)->withTrashed(); + } + + protected static function booted() + { + static::addGlobalScope(new BranchScope); + + if (auth()->check() && auth()->user()->accessToMultiBranch()) { + static::addGlobalScope('withBranch', function ($builder) { + $builder->with('branch:id,name'); + }); + } + } + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $model->branch_id = auth()->user()->branch_id ?? auth()->user()->active_branch_id; + }); + } + + public function employee() + { + return $this->belongsTo(Employee::class); + } + + public function shift() + { + return $this->belongsTo(Shift::class); + } + + protected $casts = [ + 'business_id' => 'integer', + 'branch_id' => 'integer', + 'employee_id' => 'integer', + 'shift_id' => 'integer', + ]; +} diff --git a/Modules/HrmAddon/App/Models/Department.php b/Modules/HrmAddon/App/Models/Department.php new file mode 100644 index 00000000..4350922f --- /dev/null +++ b/Modules/HrmAddon/App/Models/Department.php @@ -0,0 +1,18 @@ + 'integer', + 'status' => 'integer', + ]; +} diff --git a/Modules/HrmAddon/App/Models/Designation.php b/Modules/HrmAddon/App/Models/Designation.php new file mode 100644 index 00000000..5aa4199d --- /dev/null +++ b/Modules/HrmAddon/App/Models/Designation.php @@ -0,0 +1,21 @@ + 'integer', + 'status' => 'integer', + ]; +} diff --git a/Modules/HrmAddon/App/Models/Employee.php b/Modules/HrmAddon/App/Models/Employee.php new file mode 100644 index 00000000..52b4edc9 --- /dev/null +++ b/Modules/HrmAddon/App/Models/Employee.php @@ -0,0 +1,84 @@ +belongsTo(Branch::class)->withTrashed(); + } + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $model->branch_id = auth()->user()->branch_id ?? auth()->user()->active_branch_id; + }); + } + + protected static function booted() + { + static::addGlobalScope(new BranchScope); + + if (auth()->check() && auth()->user()->accessToMultiBranch()) { + static::addGlobalScope('withBranch', function ($builder) { + $builder->with('branch:id,name'); + }); + } + } + + public function department() + { + return $this->belongsTo(Department::class); + } + + public function designation() + { + return $this->belongsTo(Designation::class); + } + + public function shift() + { + return $this->belongsTo(Shift::class); + } + + protected $casts = [ + 'business_id' => 'integer', + 'designation_id' => 'integer', + 'department_id' => 'integer', + 'shift_id' => 'integer', + 'amount' => 'double', + ]; + + protected $imageFields = ['image']; + +} diff --git a/Modules/HrmAddon/App/Models/Holiday.php b/Modules/HrmAddon/App/Models/Holiday.php new file mode 100644 index 00000000..2abe8197 --- /dev/null +++ b/Modules/HrmAddon/App/Models/Holiday.php @@ -0,0 +1,41 @@ +belongsTo(Branch::class)->withTrashed(); + } + + protected static function booted() + { + static::addGlobalScope(new BranchScope); + + if (auth()->check() && auth()->user()->accessToMultiBranch()) { + static::addGlobalScope('withBranch', function ($builder) { + $builder->with('branch:id,name'); + }); + } + } + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $model->branch_id = auth()->user()->branch_id ?? auth()->user()->active_branch_id; + }); + } +} diff --git a/Modules/HrmAddon/App/Models/Leave.php b/Modules/HrmAddon/App/Models/Leave.php new file mode 100644 index 00000000..99b75507 --- /dev/null +++ b/Modules/HrmAddon/App/Models/Leave.php @@ -0,0 +1,56 @@ +belongsTo(Branch::class)->withTrashed(); + } + + protected static function booted() + { + static::addGlobalScope(new BranchScope); + + if (auth()->check() && auth()->user()->accessToMultiBranch()) { + static::addGlobalScope('withBranch', function ($builder) { + $builder->with('branch:id,name'); + }); + } + } + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $model->branch_id = auth()->user()->branch_id ?? auth()->user()->active_branch_id; + }); + } + + public function employee() + { + return $this->belongsTo(Employee::class); + } + + public function leave_type() + { + return $this->belongsTo(LeaveType::class); + } + + public function department() + { + return $this->belongsTo(Department::class); + } +} diff --git a/Modules/HrmAddon/App/Models/LeaveType.php b/Modules/HrmAddon/App/Models/LeaveType.php new file mode 100644 index 00000000..0a12ecd1 --- /dev/null +++ b/Modules/HrmAddon/App/Models/LeaveType.php @@ -0,0 +1,13 @@ +belongsTo(Branch::class)->withTrashed(); + } + + protected static function booted() + { + static::addGlobalScope(new BranchScope); + + if (auth()->check() && auth()->user()->accessToMultiBranch()) { + static::addGlobalScope('withBranch', function ($builder) { + $builder->with('branch:id,name'); + }); + } + } + + public function employee() + { + return $this->belongsTo(Employee::class); + } + + public function payment_type() + { + return $this->belongsTo(PaymentType::class); + } + + public function transactions() + { + return $this->hasMany(Transaction::class, 'reference_id') + ->where('platform', 'payroll'); + } + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $id = Payroll::where('business_id', auth()->user()->business_id)->count() + 1; + $model->puid = 'Ps_' . str_pad($id, 4, '0', STR_PAD_LEFT); + $model->branch_id = auth()->user()->branch_id ?? auth()->user()->active_branch_id; + }); + } +} diff --git a/Modules/HrmAddon/App/Models/Shift.php b/Modules/HrmAddon/App/Models/Shift.php new file mode 100644 index 00000000..114cb685 --- /dev/null +++ b/Modules/HrmAddon/App/Models/Shift.php @@ -0,0 +1,21 @@ + 'integer', + 'status' => 'integer', + ]; +} diff --git a/Modules/HrmAddon/App/Providers/.gitkeep b/Modules/HrmAddon/App/Providers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/App/Providers/HrmAddonServiceProvider.php b/Modules/HrmAddon/App/Providers/HrmAddonServiceProvider.php new file mode 100644 index 00000000..5be15c38 --- /dev/null +++ b/Modules/HrmAddon/App/Providers/HrmAddonServiceProvider.php @@ -0,0 +1,111 @@ +registerCommands(); + $this->registerCommandSchedules(); + $this->registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations')); + } + + /** + * Register the service provider. + */ + public function register(): void + { + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register commands in the format of Command::class + */ + protected function registerCommands(): void + { + // + } + + /** + * Register command Schedules. + */ + protected function registerCommandSchedules(): void + { + // + } + + /** + * Register translations. + */ + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/'.$this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang')); + } + } + + /** + * Register config. + */ + protected function registerConfig(): void + { + $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config'); + $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower); + } + + /** + * Register views. + */ + public function registerViews(): void + { + $viewPath = resource_path('views/modules/'.$this->moduleNameLower); + $sourcePath = module_path($this->moduleName, 'resources/views'); + + $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + + $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path')); + Blade::componentNamespace($componentNamespace, $this->moduleNameLower); + } + + /** + * Get the services provided by the provider. + */ + public function provides(): array + { + return []; + } + + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (config('view.paths') as $path) { + if (is_dir($path.'/modules/'.$this->moduleNameLower)) { + $paths[] = $path.'/modules/'.$this->moduleNameLower; + } + } + + return $paths; + } +} diff --git a/Modules/HrmAddon/App/Providers/RouteServiceProvider.php b/Modules/HrmAddon/App/Providers/RouteServiceProvider.php new file mode 100644 index 00000000..4e9a800b --- /dev/null +++ b/Modules/HrmAddon/App/Providers/RouteServiceProvider.php @@ -0,0 +1,59 @@ +mapApiRoutes(); + + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web') + ->namespace($this->moduleNamespace) + ->group(module_path('HrmAddon', '/routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->moduleNamespace) + ->group(module_path('HrmAddon', '/routes/api.php')); + } +} diff --git a/Modules/HrmAddon/Database/Seeders/.gitkeep b/Modules/HrmAddon/Database/Seeders/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/Database/Seeders/HrmAddonDatabaseSeeder.php b/Modules/HrmAddon/Database/Seeders/HrmAddonDatabaseSeeder.php new file mode 100644 index 00000000..7a9525d0 --- /dev/null +++ b/Modules/HrmAddon/Database/Seeders/HrmAddonDatabaseSeeder.php @@ -0,0 +1,16 @@ +id(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->string('name')->nullable(); + $table->text('description')->nullable(); + $table->boolean('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('departments'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_12_130921_create_designations_table.php b/Modules/HrmAddon/Database/migrations/2025_05_12_130921_create_designations_table.php new file mode 100644 index 00000000..9acaf54f --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_12_130921_create_designations_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('name')->nullable(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->text('description')->nullable(); + $table->boolean('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('designations'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_12_152226_create_shifts_table.php b/Modules/HrmAddon/Database/migrations/2025_05_12_152226_create_shifts_table.php new file mode 100644 index 00000000..4b2d8cb5 --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_12_152226_create_shifts_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('name'); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->time('start_time'); + $table->time('end_time'); + $table->time('start_break_time')->nullable(); + $table->time('end_break_time')->nullable(); + $table->time('break_time')->nullable(); + $table->string('break_status')->nullable(); + $table->boolean('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('shifts'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_13_102231_create_employees_table.php b/Modules/HrmAddon/Database/migrations/2025_05_13_102231_create_employees_table.php new file mode 100644 index 00000000..d4332cac --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_13_102231_create_employees_table.php @@ -0,0 +1,41 @@ +id(); + $table->string('name'); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->foreignId('designation_id')->nullable()->constrained()->nullOnDelete(); + $table->foreignId('department_id')->nullable()->constrained()->nullOnDelete(); + $table->foreignId('shift_id')->nullable()->constrained()->nullOnDelete(); + $table->double('amount', 10, 2)->default(0); + $table->string('image')->nullable(); + $table->string('phone'); + $table->string('email')->nullable(); + $table->string('gender'); + $table->string('country')->nullable(); + $table->date('birth_date')->nullable(); + $table->date('join_date')->nullable(); + $table->string('status')->default('active'); // active || terminated || suspended + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('employees'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_13_152009_create_leave_types_table.php b/Modules/HrmAddon/Database/migrations/2025_05_13_152009_create_leave_types_table.php new file mode 100644 index 00000000..0e3df1c1 --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_13_152009_create_leave_types_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->text('description')->nullable(); + $table->boolean('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('leave_types'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_14_090651_create_leaves_table.php b/Modules/HrmAddon/Database/migrations/2025_05_14_090651_create_leaves_table.php new file mode 100644 index 00000000..380b9d77 --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_14_090651_create_leaves_table.php @@ -0,0 +1,37 @@ +id(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->foreignId('employee_id')->constrained()->cascadeOnDelete(); + $table->foreignId('leave_type_id')->nullable()->constrained()->nullOnDelete(); + $table->foreignId('department_id')->nullable()->constrained()->nullOnDelete(); + $table->date('start_date'); + $table->date('end_date'); + $table->double('leave_duration'); + $table->string('month'); + $table->string('status')->default('pending'); // rejected, pending, approved + $table->text('description')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('leaves'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_14_124928_create_holidays_table.php b/Modules/HrmAddon/Database/migrations/2025_05_14_124928_create_holidays_table.php new file mode 100644 index 00000000..4e45533c --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_14_124928_create_holidays_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->date('start_date'); + $table->date('end_date'); + $table->text('description')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('holidays'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_14_162716_create_attendances_table.php b/Modules/HrmAddon/Database/migrations/2025_05_14_162716_create_attendances_table.php new file mode 100644 index 00000000..f5db1d5f --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_14_162716_create_attendances_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->foreignId('employee_id')->constrained()->cascadeOnDelete(); + $table->foreignId('shift_id')->nullable()->constrained()->nullOnDelete(); + $table->time('time_in'); + $table->time('time_out'); + $table->date('date'); + $table->time('duration'); + $table->string('month'); + $table->longText('note')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('attendances'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_05_15_105035_create_payrolls_table.php b/Modules/HrmAddon/Database/migrations/2025_05_15_105035_create_payrolls_table.php new file mode 100644 index 00000000..52ec5cb9 --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_05_15_105035_create_payrolls_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('business_id')->constrained()->cascadeOnDelete(); + $table->foreignId('employee_id')->constrained()->cascadeOnDelete(); + $table->foreignId('payment_type_id')->nullable()->constrained()->nullOnDelete(); + $table->string('month'); + $table->string('puid'); + $table->date('date')->nullable(); + $table->double('amount', 10, 2); + $table->string('payemnt_year'); + $table->longText('note')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('payrolls'); + } +}; diff --git a/Modules/HrmAddon/Database/migrations/2025_08_19_105036_add_new_fields_to_multiple_table.php b/Modules/HrmAddon/Database/migrations/2025_08_19_105036_add_new_fields_to_multiple_table.php new file mode 100644 index 00000000..79f8121c --- /dev/null +++ b/Modules/HrmAddon/Database/migrations/2025_08_19_105036_add_new_fields_to_multiple_table.php @@ -0,0 +1,30 @@ +foreignId('branch_id')->nullable()->after('business_id')->constrained('branches')->nullOnDelete(); + }); + } + } + + public function down(): void + { + // 'holidays', 'attendances', 'leaves', 'payrolls', 'employees' + foreach (['holidays', 'attendances', 'leaves', 'payrolls', 'employees'] as $tableName) { + Schema::table($tableName, function (Blueprint $table) { + $table->dropColumn('branch_id'); + }); + } + } +}; diff --git a/Modules/HrmAddon/composer.json b/Modules/HrmAddon/composer.json new file mode 100644 index 00000000..6577ed4e --- /dev/null +++ b/Modules/HrmAddon/composer.json @@ -0,0 +1,31 @@ +{ + "name": "nwidart/hrmaddon", + "description": "", + "authors": [ + { + "name": "Nicolas Widart", + "email": "n.widart@gmail.com" + } + ], + "extra": { + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "Modules\\HrmAddon\\": "", + "Modules\\HrmAddon\\App\\": "app/", + "Modules\\HrmAddon\\Database\\Factories\\": "database/factories/", + "Modules\\HrmAddon\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\HrmAddon\\Tests\\": "tests/" + } + } +} diff --git a/Modules/HrmAddon/config/.gitkeep b/Modules/HrmAddon/config/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/config/config.php b/Modules/HrmAddon/config/config.php new file mode 100644 index 00000000..c4984f06 --- /dev/null +++ b/Modules/HrmAddon/config/config.php @@ -0,0 +1,5 @@ + 'HrmAddon', +]; diff --git a/Modules/HrmAddon/module.json b/Modules/HrmAddon/module.json new file mode 100644 index 00000000..859b40f3 --- /dev/null +++ b/Modules/HrmAddon/module.json @@ -0,0 +1,10 @@ +{ + "name": "HrmAddon", + "alias": "hrmaddon", + "version": "1.4", + "description": "", + "keywords": [], + "priority": 0, + "providers": ["Modules\\HrmAddon\\App\\Providers\\HrmAddonServiceProvider"], + "files": [] +} diff --git a/Modules/HrmAddon/package.json b/Modules/HrmAddon/package.json new file mode 100644 index 00000000..d6fbfc83 --- /dev/null +++ b/Modules/HrmAddon/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "axios": "^1.1.2", + "laravel-vite-plugin": "^0.7.5", + "sass": "^1.69.5", + "postcss": "^8.3.7", + "vite": "^4.0.0" + } +} diff --git a/Modules/HrmAddon/resources/assets/js/app.js b/Modules/HrmAddon/resources/assets/js/app.js new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/resources/assets/sass/app.scss b/Modules/HrmAddon/resources/assets/sass/app.scss new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/resources/views/.gitkeep b/Modules/HrmAddon/resources/views/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/HrmAddon/resources/views/attendances/create.blade.php b/Modules/HrmAddon/resources/views/attendances/create.blade.php new file mode 100644 index 00000000..d1a9c4fe --- /dev/null +++ b/Modules/HrmAddon/resources/views/attendances/create.blade.php @@ -0,0 +1,71 @@ + +
|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Month') }} | +{{ __('Shift') }} | +{{ __('date') }} | +{{ __('Time In') }} | +{{ __('Time Out') }} | +{{ __('Duration') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($attendances->currentPage() - 1) * $attendances->perPage() + $loop->iteration }} | + +{{ $attendance->employee->name ?? '' }} | +{{ ucfirst($attendance->month ?? '') }} | +{{ $attendance->shift->name ?? '' }} | +{{ formatted_date($attendance->date) }} | +{{ formatted_time($attendance->time_in) }} | +{{ formatted_time($attendance->time_out) }} | +{{ formatTimeToWords($attendance->duration) }} | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Name') }} | +{{ __('Description') }} | +{{ __('Status') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($departments->currentPage() - 1) * $departments->perPage() + $loop->iteration }} | + +{{ $department->name }} | +{{ Str::limit($department->description, 20, '...') }} | ++ + | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Name') }} | +{{ __('Description') }} | +{{ __('Status') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($designations->currentPage() - 1) * $designations->perPage() + $loop->iteration }} | + +{{ $designation->name }} | +{{ Str::limit($designation->description, 20, '...') }} | ++ + | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Image') }}. | + @if (auth()->user()->accessToMultiBranch()) +{{ __('Branch') }} | + @endif +{{ __('Name') }} | +{{ __('Department') }} | +{{ __('Designation') }} | +{{ __('shift') }} | +{{ __('Phone') }} | +{{ __('Salary') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($employees->currentPage() - 1) * $employees->perPage() + $loop->iteration }} | + +
+
+ |
+
+ @if (auth()->user()->accessToMultiBranch())
+ {{ $employee->branch->name ?? '' }} | + @endif + +{{ $employee->name }} | +{{ $employee->department->name ?? '' }} | +{{ $employee->designation->name ?? '' }} | +{{ $employee->shift->name ?? '' }} | +{{ $employee->phone }} | +{{ currency_format($employee->amount, 'icon', 2, business_currency()) }} | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Name') }} | + @if (auth()->user()->accessToMultiBranch()) +{{ __('Branch') }} | + @endif +{{ __('Start Date') }} | +{{ __('End Date') }} | +{{ __('Description') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($holidays->currentPage() - 1) * $holidays->perPage() + $loop->iteration }} | +{{ $holiday->name }} | + @if (auth()->user()->accessToMultiBranch()) +{{ $holiday->branch->name ?? '' }} | + @endif +{{ formatted_date($holiday->start_date) }} | +{{ formatted_date($holiday->end_date) }} | +{{ Str::limit($holiday->description, 20, '...') }} | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Name') }} | +{{ __('Description') }} | +{{ __('Status') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($leave_types->currentPage() - 1) * $leave_types->perPage() + $loop->iteration }} | + +{{ $leave_type->name }} | +{{ Str::limit($leave_type->description, 20, '...') }} | ++ + | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Employee') }} | + @if (auth()->user()->accessToMultiBranch()) +{{ __('Branch') }} | + @endif +{{ __('Month') }} | +{{ __('Department') }} | +{{ __('Start Date') }} | +{{ __('End Date') }} | +{{ __('Leave Type') }} | +{{ __('Leave Duration') }} | +{{ __('Status') }} | +{{ __('Action') }} | +||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($leaves->currentPage() - 1) * $leaves->perPage() + $loop->iteration }} | +{{ $leave->employee->name ?? '' }} | + @if (auth()->user()->accessToMultiBranch()) +{{ $leave->branch->name ?? '' }} | + @endif +{{ ucfirst($leave->month) }} | +{{ $leave->department->name ?? '' }} | +{{ formatted_date($leave->start_date) }} | +{{ formatted_date($leave->end_date) }} | +{{ $leave->leave_type->name ?? '' }} | +{{ $leave->leave_duration }} {{ __('Days') }} | + @if ($leave->status == 'pending') +
+ Pending
+ |
+ @elseif ($leave->status == 'approved')
+
+ Approved
+ |
+ @elseif ($leave->status == 'rejected')
+
+ Rejected
+ |
+ @endif
+
+
+
+
+
+ |
+
{{ __('items show') }}
+ +|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Employee') }} | + @if (auth()->user()->accessToMultiBranch()) +{{ __('Branch') }} | + @endif +{{ __('Payment Year') }} | +{{ __('Month') }} | +{{ __('Receipt No') }} | +{{ __('date') }} | +{{ __('Amount') }} | +{{ __('Payment Type') }} | +{{ __('Status') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($payrolls->currentPage() - 1) * $payrolls->perPage() + $loop->iteration }} | +{{ $payroll->employee->name ?? '' }} | + @if (auth()->user()->accessToMultiBranch()) +{{ $payroll->branch->name ?? '' }} | + @endif +{{ $payroll->payemnt_year }} | +{{ ucfirst($payroll->month) }} | +{{ $payroll->puid }} | +{{ formatted_date($payroll->date) }} | +{{ currency_format($payroll->amount, 'icon', 2, business_currency()) }} | +{{ $payroll->payment_type->name ?? 'Cash' }} | +
+ Paid
+ |
+
+
+
+
+
+ |
+
{{ __('items show') }}
+ +| {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Date') }} | +{{ __('Month') }} | +{{ __('Shift') }} | +{{ __('Time In') }} | +{{ __('Time Out') }} | +{{ __('Duration') }} | +
|---|---|---|---|---|---|---|---|
| {{ ($attendances->currentPage() - 1) * $attendances->perPage() + $loop->iteration }} | +{{ $attendance->employee->name ?? '' }} | +{{ formatted_date($attendance->date) }} | +{{ ucfirst($attendance->month) }} | +{{ $attendance->shift->name ?? '' }} | +{{ formatted_time($attendance->time_in) }} | +{{ formatted_time($attendance->time_out) }} | +{{ formatTimeToWords($attendance->duration) }} | +
| {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Date') }} | +{{ __('Month') }} | +{{ __('Shift') }} | +{{ __('Time In') }} | +{{ __('Time Out') }} | +{{ __('Duration') }} | +
|---|---|---|---|---|---|---|---|
| {{ $loop->iteration }} | +{{ $attendance->employee->name ?? '' }} | +{{ formatted_date($attendance->date) }} | +{{ ucfirst($attendance->month) }} | +{{ $attendance->shift->name ?? '' }} | +{{ formatted_time($attendance->time_in) }} | +{{ formatted_time($attendance->time_out) }} | +{{ formatTimeToWords($attendance->duration) }} | +
| {{ __('Employee') }} | +{{ __('Date') }} | +{{ __('Shift') }} | +{{ __('Time In') }} | +{{ __('Time Out') }} | +{{ __('Duration') }} | +
|---|---|---|---|---|---|
| + {{ $attendance->employee->name ?? '' }} + | ++ {{ formatted_date($attendance->date) }} + | ++ {{ $attendance->shift->name ?? '' }} + | ++ {{ formatted_time($attendance->time_in) }} + | ++ {{ formatted_time($attendance->time_out) }} + | ++ {{ formatTimeToWords($attendance->duration) }} + | +
| {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Month') }} | +{{ __('Department') }} | +{{ __('Start Date') }} | +{{ __('End Date') }} | +{{ __('Leave Type') }} | +{{ __('Leave Duration') }} | +{{ __('Status') }} | +||
|---|---|---|---|---|---|---|---|---|---|---|
| {{ ($leaves->currentPage() - 1) * $leaves->perPage() + $loop->iteration }} | +{{ $leave->employee->name ?? '' }} | +{{ ucfirst($leave->month) }} | +{{ $leave->department->name ?? '' }} | +{{ formatted_date($leave->start_date) }} | +{{ formatted_date($leave->end_date) }} | +{{ $leave->leave_type->name ?? '' }} | +{{ $leave->leave_duration }} {{ __('Days') }} | + @if ($leave->status == 'pending') +
+ {{__('Pending')}}
+ |
+ @elseif ($leave->status == 'approved')
+
+ {{__('Approved')}}
+ |
+ @elseif ($leave->status == 'rejected')
+
+ {{__('Rejected')}}
+ |
+ @endif
+
| {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Month') }} | +{{ __('Department') }} | +{{ __('Start Date') }} | +{{ __('End Date') }} | +{{ __('Leave Type') }} | +{{ __('Leave Duration') }} | +{{ __('Status') }} | +||
|---|---|---|---|---|---|---|---|---|---|---|
| {{ $loop->iteration }} | +{{ $leave->employee->name ?? '' }} | +{{ ucfirst($leave->month) }} | +{{ $leave->department->name ?? '' }} | +{{ formatted_date($leave->start_date) }} | +{{ formatted_date($leave->end_date) }} | +{{ $leave->leave_type->name ?? '' }} | +{{ $leave->leave_duration }} {{ __('Days') }} | + @if ($leave->status == 'pending') +
+ {{__('Pending')}}
+ |
+ @elseif ($leave->status == 'approved')
+
+ {{__('Approved')}}
+ |
+ @elseif ($leave->status == 'rejected')
+
+ {{__('Rejected')}}
+ |
+ @endif
+
| {{ __('Employee') }} | +{{ __('Month') }} | +{{ __('Start Date') }} | +{{ __('End Date') }} | +{{ __('Leave Type') }} | +{{ __('Duration') }} | +{{ __('Status') }} | +
|---|---|---|---|---|---|---|
| + {{ $leave->employee?->name }} + | ++ {{ ucfirst($leave->month) }} + | ++ {{ formatted_date($leave->start_date) }} + | ++ {{ formatted_date($leave->end_date) }} + | ++ {{ $leave->leave_type?->name }} + | ++ {{ $leave->leave_duration }} {{ __('Days') }} + | ++ @if ($leave->status == 'pending') + {{__('Pending')}} + @elseif ($leave->status == 'approved') + {{__('Approved')}} + @elseif ($leave->status == 'rejected') + {{__('Rejected')}} + @endif + | +
| {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Date') }} | +{{ __('Month') }} | +{{ __('Slip No') }} | +{{ __('Amount') }} | +{{ __('Payment Type') }} | +{{ __('Status') }} | +|
|---|---|---|---|---|---|---|---|---|
| {{ ($payrolls->currentPage() - 1) * $payrolls->perPage() + $loop->iteration }} | +{{ $payroll->employee->name ?? '' }} | +{{ formatted_date($payroll->date) }} | +{{ ucfirst($payroll->month) }} | +{{ $payroll->puid }} | +{{ currency_format($payroll->amount, currency: business_currency()) }} | +{{ $payroll->payment_type->name ?? 'Cash' }} | + @if ($payroll->status == 'unpaid') +
+ Unpaid
+ |
+ @else
+
+ Paid
+ |
+ @endif
+
| {{__('Total')}} | ++ | + | + | + | {{ currency_format($payrolls->sum('amount'), currency: business_currency()) }} | ++ | + |
| {{ __('SL') }}. | +{{ __('Employee') }} | +{{ __('Date') }} | +{{ __('Month') }} | +{{ __('Slip No') }} | +{{ __('Amount') }} | +{{ __('Payment Type') }} | +{{ __('Status') }} | +|
|---|---|---|---|---|---|---|---|---|
| {{ $loop->iteration }} | +{{ $payroll->employee->name ?? '' }} | +{{ formatted_date($payroll->date) }} | +{{ ucfirst($payroll->month) }} | +{{ $payroll->puid }} | +{{ currency_format($payroll->amount, currency: business_currency()) }} | +{{ $payroll->payment_type->name ?? 'Cash' }} | + @if ($payroll->status == 'unpaid') +
+ Unpaid
+ |
+ @else
+
+ Paid
+ |
+ @endif
+
| {{__('Total')}} | ++ | + | + | + | {{ currency_format($payrolls->sum('amount'), currency: business_currency()) }} | ++ | + |
| {{ __('Employee') }} | +{{ __('Date') }} | +{{ __('Slip No') }} | +{{ __('Amount') }} | +{{ __('Payment') }} | +{{ __('Status') }} | +
|---|---|---|---|---|---|
| + {{ $payroll->employee->name ?? '' }} + | ++ {{ formatted_date($payroll->date) }} + | ++ {{ $payroll->puid }} + | ++ {{ currency_format($payroll->amount, currency: business_currency()) }} + | ++ {{ $payroll->payment_type->name ?? 'Cash' }} + | ++ {{ $payroll->status == 'unpaid' ? 'Unpaid' : 'Paid' }} + | +
| + {{ __('Total') }} + | ++ | + | + {{ currency_format($payrolls->sum('amount'), currency: business_currency()) }} + | ++ |
|
+
+
+
+ |
+ @endusercan
+ {{ __('SL') }}. | +{{ __('Name') }} | +{{ __('Start Time') }} | +{{ __('End Time') }} | +{{ __('Break Time') }} | +{{ __('Break Duration') }} | +{{ __('Status') }} | +{{ __('Action') }} | +
|---|---|---|---|---|---|---|---|---|
| + + | + @endusercan +{{ ($shifts->currentPage() - 1) * $shifts->perPage() + $loop->iteration }} | +{{ $shift->name }} | +{{ formatted_time($shift->start_time) }} | +{{ formatted_time($shift->end_time) }} | +{{ formatted_time($shift->start_break_time ?? '') }} - + {{ formatted_time($shift->end_break_time ?? '') }} | +{{ formatTimeToWords($shift->break_time) }} | ++ + | +
+
+
+
+
+ |
+
{{ __('items show') }}
+ +