moduleUtil->isModuleInstalled('Essentials')) { abort(403, 'Unauthorized action.'); } } protected function getSettings(int $business_id): array { $business = Business::find($business_id); $settings = $business?->essentials_settings; return ! empty($settings) ? json_decode($settings, true) : []; } protected function parseDate(?string $date): ?string { if (empty($date)) { return null; } return Carbon::parse($date)->format('Y-m-d'); } public function dashboard(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $user_id = $user->id; $is_admin = $this->moduleUtil->is_admin($user, $business_id); $users = User::where('business_id', $business_id)->user()->get(); $departments = Category::where('business_id', $business_id) ->where('category_type', 'hrm_department') ->get(); $users_by_dept = $users->groupBy('essentials_department_id'); $today = Carbon::today(); $one_month_from_today = Carbon::now()->addMonth(); $leaves = EssentialsLeave::where('business_id', $business_id) ->where('status', 'approved') ->whereDate('end_date', '>=', $today->format('Y-m-d')) ->whereDate('start_date', '<=', $one_month_from_today->format('Y-m-d')) ->with(['user', 'leave_type']) ->orderBy('start_date', 'asc') ->get(); $todays_leaves = []; $upcoming_leaves = []; $users_leaves = []; foreach ($leaves as $leave) { $leave_start = Carbon::parse($leave->start_date); $leave_end = Carbon::parse($leave->end_date); if ($today->gte($leave_start) && $today->lte($leave_end)) { $todays_leaves[] = $this->formatLeave($leave); if ($leave->user_id == $user_id) { $users_leaves[] = $this->formatLeave($leave); } } elseif ($today->lt($leave_start) && $leave_start->lte($one_month_from_today)) { $upcoming_leaves[] = $this->formatLeave($leave); if ($leave->user_id == $user_id) { $users_leaves[] = $this->formatLeave($leave); } } } $holidays_query = EssentialsHoliday::where('business_id', $business_id) ->whereDate('end_date', '>=', $today->format('Y-m-d')) ->whereDate('start_date', '<=', $one_month_from_today->format('Y-m-d')) ->orderBy('start_date', 'asc') ->with(['location']); $permitted_locations = $user->permitted_locations(); if ($permitted_locations != 'all') { $holidays_query->where(function ($query) use ($permitted_locations) { $query->whereIn('location_id', $permitted_locations) ->orWhereNull('location_id'); }); } $holidays = $holidays_query->get(); $todays_holidays = []; $upcoming_holidays = []; foreach ($holidays as $holiday) { $holiday_start = Carbon::parse($holiday->start_date); $holiday_end = Carbon::parse($holiday->end_date); $formatted = $this->formatHoliday($holiday); if ($today->gte($holiday_start) && $today->lte($holiday_end)) { $todays_holidays[] = $formatted; } elseif ($today->lt($holiday_start) && $holiday_start->lte($one_month_from_today)) { $upcoming_holidays[] = $formatted; } } $todays_attendances = []; if ($is_admin) { $attendances = EssentialsAttendance::where('business_id', $business_id) ->whereDate('clock_in_time', Carbon::now()->format('Y-m-d')) ->with(['employee']) ->orderBy('clock_in_time', 'asc') ->get(); foreach ($attendances as $attendance) { $todays_attendances[] = [ 'id' => $attendance->id, 'employee' => $attendance->employee?->user_full_name ?? '', 'clock_in_time' => $attendance->clock_in_time, 'clock_out_time' => $attendance->clock_out_time, 'clock_in_note' => $attendance->clock_in_note, 'clock_out_note' => $attendance->clock_out_note, ]; } } $settings = $this->getSettings($business_id); $sales_targets = EssentialsUserSalesTarget::where('user_id', $user_id)->get(); $start_date = Carbon::today()->startOfMonth()->format('Y-m-d'); $end_date = Carbon::today()->endOfMonth()->format('Y-m-d'); $sale_totals = $this->transactionUtil->getUserTotalSales($business_id, $user_id, $start_date, $end_date); $target_achieved_this_month = ! empty($settings['calculate_sales_target_commission_without_tax']) ? $sale_totals['total_sales_without_tax'] : $sale_totals['total_sales']; $start_date = Carbon::parse('first day of last month')->format('Y-m-d'); $end_date = Carbon::parse('last day of last month')->format('Y-m-d'); $sale_totals = $this->transactionUtil->getUserTotalSales($business_id, $user_id, $start_date, $end_date); $target_achieved_last_month = ! empty($settings['calculate_sales_target_commission_without_tax']) ? $sale_totals['total_sales_without_tax'] : $sale_totals['total_sales']; $department_stats = $departments->map(function ($dept) use ($users_by_dept) { return [ 'id' => $dept->id, 'name' => $dept->name, 'count' => isset($users_by_dept[$dept->id]) ? count($users_by_dept[$dept->id]) : 0, ]; }); return $this->success([ 'is_admin' => $is_admin, 'users_count' => $users->count(), 'departments' => $department_stats, 'users_leaves' => $users_leaves, 'todays_leaves' => $todays_leaves, 'upcoming_leaves' => $upcoming_leaves, 'todays_holidays' => $todays_holidays, 'upcoming_holidays' => $upcoming_holidays, 'todays_attendances' => $todays_attendances, 'sales_targets' => $sales_targets, 'target_achieved_this_month' => $target_achieved_this_month, 'target_achieved_last_month' => $target_achieved_last_month, 'stats' => [ 'holidays' => EssentialsHoliday::where('business_id', $business_id)->count(), 'leave' => EssentialsLeave::where('business_id', $business_id)->count(), 'todo' => ToDo::where('business_id', $business_id)->count(), 'payroll' => Transaction::where('business_id', $business_id)->where('type', 'payroll')->count(), ], ]); } protected function formatLeave(EssentialsLeave $leave): array { return [ 'id' => $leave->id, 'start_date' => $leave->start_date, 'end_date' => $leave->end_date, 'leave_type' => $leave->leave_type?->leave_type, 'status' => $leave->status, 'user' => $leave->user?->user_full_name, ]; } protected function formatHoliday(EssentialsHoliday $holiday): array { return [ 'id' => $holiday->id, 'name' => $holiday->name, 'start_date' => $holiday->start_date, 'end_date' => $holiday->end_date, 'note' => $holiday->note, 'location' => $holiday->location?->name, ]; } public function salesTargetsReport(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $this_month_start = Carbon::today()->startOfMonth()->format('Y-m-d'); $this_month_end = Carbon::today()->endOfMonth()->format('Y-m-d'); $last_month_start = Carbon::parse('first day of last month')->format('Y-m-d'); $last_month_end = Carbon::parse('last day of last month')->format('Y-m-d'); $settings = $this->getSettings($business_id); $query = User::where('users.business_id', $business_id) ->join('transactions as t', 't.commission_agent', '=', 'users.id') ->where('t.type', 'sell') ->whereDate('transaction_date', '>=', $last_month_start) ->where('t.status', 'final'); if (! empty($settings['calculate_sales_target_commission_without_tax'])) { $query->select( 'users.id', DB::raw("CONCAT(COALESCE(surname, ''), ' ', COALESCE(first_name, ''), ' ', COALESCE(last_name, '')) as full_name"), DB::raw("SUM(IF(DATE(transaction_date) BETWEEN '{$last_month_start}' AND '{$last_month_end}', total_before_tax - shipping_charges - (SELECT SUM(item_tax*quantity) FROM transaction_sell_lines as tsl WHERE tsl.transaction_id=t.id), 0)) as total_sales_last_month"), DB::raw("SUM(IF(DATE(transaction_date) BETWEEN '{$this_month_start}' AND '{$this_month_end}', total_before_tax - shipping_charges - (SELECT SUM(item_tax*quantity) FROM transaction_sell_lines as tsl WHERE tsl.transaction_id=t.id), 0)) as total_sales_this_month") ); } else { $query->select( 'users.id', DB::raw("CONCAT(COALESCE(surname, ''), ' ', COALESCE(first_name, ''), ' ', COALESCE(last_name, '')) as full_name"), DB::raw("SUM(IF(DATE(transaction_date) BETWEEN '{$last_month_start}' AND '{$last_month_end}', final_total, 0)) as total_sales_last_month"), DB::raw("SUM(IF(DATE(transaction_date) BETWEEN '{$this_month_start}' AND '{$this_month_end}', final_total, 0)) as total_sales_this_month") ); } $rows = $query->groupBy('users.id')->get(); return $this->success($rows); } public function leaves(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $can_all = $user->can('essentials.crud_all_leave'); $can_own = $user->can('essentials.crud_own_leave'); if (! $can_all && ! $can_own) { abort(403, 'Unauthorized action.'); } $query = EssentialsLeave::where('essentials_leaves.business_id', $business_id) ->join('users as u', 'u.id', '=', 'essentials_leaves.user_id') ->join('essentials_leave_types as lt', 'lt.id', '=', 'essentials_leaves.essentials_leave_type_id') ->select([ 'essentials_leaves.id', DB::raw("CONCAT(COALESCE(u.surname, ''), ' ', COALESCE(u.first_name, ''), ' ', COALESCE(u.last_name, '')) as user"), 'lt.leave_type', 'start_date', 'end_date', 'ref_no', 'essentials_leaves.status', 'reason', 'status_note', ]) ->orderByDesc('essentials_leaves.id'); if ($request->filled('user_id')) { $query->where('essentials_leaves.user_id', $request->input('user_id')); } if (! $can_all && $can_own) { $query->where('essentials_leaves.user_id', $user->id); } if ($request->filled('status')) { $query->where('essentials_leaves.status', $request->input('status')); } if ($request->filled('leave_type')) { $query->where('essentials_leaves.essentials_leave_type_id', $request->input('leave_type')); } return $this->paginated($query->paginate($request->input('per_page', 25))); } public function showLeave(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $leave = EssentialsLeave::where('business_id', $business_id) ->with(['user', 'leave_type']) ->findOrFail($id); return $this->success($leave); } public function storeLeave(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $can_all = $user->can('essentials.crud_all_leave'); $can_own = $user->can('essentials.crud_own_leave'); if (! $can_all && ! $can_own) { abort(403, 'Unauthorized action.'); } $request->validate([ 'essentials_leave_type_id' => 'required|integer', 'start_date' => 'required|date', 'end_date' => 'required|date', ]); $input = $request->only(['essentials_leave_type_id', 'start_date', 'end_date', 'reason']); $input['business_id'] = $business_id; $input['status'] = 'pending'; $input['start_date'] = $this->parseDate($input['start_date']); $input['end_date'] = $this->parseDate($input['end_date']); $settings = $this->getSettings($business_id); $prefix = $settings['leave_ref_no_prefix'] ?? ''; $ref_count = $this->moduleUtil->setAndGetReferenceCount('leave'); $input['ref_no'] = $this->moduleUtil->generateReferenceNumber('leave', $ref_count, null, $prefix); if ($can_all && ! empty($request->input('employees'))) { $created = []; foreach ((array) $request->input('employees') as $user_id) { $row = $input; $row['user_id'] = $user_id; $created[] = EssentialsLeave::create($row); } return $this->success($created, '', 201); } $input['user_id'] = $user->id; $leave = EssentialsLeave::create($input); return $this->success($leave, '', 201); } public function updateLeaveStatus(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.approve_leave') && ! $user->can('essentials.crud_all_leave')) { abort(403, 'Unauthorized action.'); } $request->validate([ 'status' => 'required|in:pending,approved,cancelled', ]); $leave = EssentialsLeave::where('business_id', $business_id)->findOrFail($id); $leave->status = $request->input('status'); $leave->status_note = $request->input('status_note'); $leave->changed_by = $user->id; $leave->save(); return $this->success($leave); } public function destroyLeave(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.crud_all_leave')) { abort(403, 'Unauthorized action.'); } EssentialsLeave::where('business_id', $business_id)->where('id', $id)->delete(); return $this->success(null, 'Deleted'); } public function leaveTypes(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $query = EssentialsLeaveType::where('business_id', $business_id)->orderBy('leave_type'); return $this->paginated($query->paginate($request->input('per_page', 25))); } public function storeLeaveType(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.crud_leave_type')) { abort(403, 'Unauthorized action.'); } $request->validate(['leave_type' => 'required']); $input = $request->only(['leave_type', 'max_leave_count', 'leave_count_interval']); $input['business_id'] = $business_id; $leaveType = EssentialsLeaveType::create($input); return $this->success($leaveType, '', 201); } public function updateLeaveType(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.crud_leave_type')) { abort(403, 'Unauthorized action.'); } $leaveType = EssentialsLeaveType::where('business_id', $business_id)->findOrFail($id); $leaveType->update($request->only(['leave_type', 'max_leave_count', 'leave_count_interval'])); return $this->success($leaveType); } public function destroyLeaveType(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.crud_leave_type')) { abort(403, 'Unauthorized action.'); } EssentialsLeaveType::where('business_id', $business_id)->where('id', $id)->delete(); return $this->success(null, 'Deleted'); } public function holidays(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $query = EssentialsHoliday::where('essentials_holidays.business_id', $business_id) ->leftJoin('business_locations as bl', 'bl.id', '=', 'essentials_holidays.location_id') ->select([ 'essentials_holidays.id', 'essentials_holidays.name', 'bl.name as location', 'essentials_holidays.start_date', 'essentials_holidays.end_date', 'essentials_holidays.note', 'essentials_holidays.location_id', ]) ->orderByDesc('essentials_holidays.start_date'); $permitted_locations = $user->permitted_locations(); if ($permitted_locations != 'all') { $query->where(function ($q) use ($permitted_locations) { $q->whereIn('essentials_holidays.location_id', $permitted_locations) ->orWhereNull('essentials_holidays.location_id'); }); } if ($request->filled('location_id')) { $query->where('essentials_holidays.location_id', $request->input('location_id')); } return $this->paginated($query->paginate($request->input('per_page', 25))); } public function showHoliday(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $holiday = EssentialsHoliday::where('business_id', $business_id)->findOrFail($id); return $this->success($holiday); } public function storeHoliday(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $request->validate([ 'name' => 'required', 'start_date' => 'required|date', 'end_date' => 'required|date', ]); $input = $request->only(['name', 'start_date', 'end_date', 'location_id', 'note']); $input['start_date'] = $this->parseDate($input['start_date']); $input['end_date'] = $this->parseDate($input['end_date']); $input['business_id'] = $business_id; if (empty($input['location_id'])) { $input['location_id'] = null; } $holiday = EssentialsHoliday::create($input); return $this->success($holiday, '', 201); } public function updateHoliday(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $holiday = EssentialsHoliday::where('business_id', $business_id)->findOrFail($id); $input = $request->only(['name', 'start_date', 'end_date', 'location_id', 'note']); if (! empty($input['start_date'])) { $input['start_date'] = $this->parseDate($input['start_date']); } if (! empty($input['end_date'])) { $input['end_date'] = $this->parseDate($input['end_date']); } if (array_key_exists('location_id', $input) && empty($input['location_id'])) { $input['location_id'] = null; } $holiday->update($input); return $this->success($holiday); } public function destroyHoliday(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } EssentialsHoliday::where('business_id', $business_id)->where('id', $id)->delete(); return $this->success(null, 'Deleted'); } public function attendances(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $can_all = $user->can('essentials.crud_all_attendance'); $can_own = $user->can('essentials.view_own_attendance'); if (! $can_all && ! $can_own) { abort(403, 'Unauthorized action.'); } $query = EssentialsAttendance::where('essentials_attendances.business_id', $business_id) ->join('users as u', 'u.id', '=', 'essentials_attendances.user_id') ->leftJoin('essentials_shifts as es', 'es.id', '=', 'essentials_attendances.essentials_shift_id') ->select([ 'essentials_attendances.id', 'essentials_attendances.user_id', DB::raw("CONCAT(COALESCE(u.surname, ''), ' ', COALESCE(u.first_name, ''), ' ', COALESCE(u.last_name, '')) as user"), 'clock_in_time', 'clock_out_time', 'clock_in_note', 'clock_out_note', 'es.name as shift_name', ]) ->orderByDesc('clock_in_time'); if ($request->filled('employee_id')) { $query->where('essentials_attendances.user_id', $request->input('employee_id')); } if ($request->filled('start_date') && $request->filled('end_date')) { $query->whereDate('clock_in_time', '>=', $request->input('start_date')) ->whereDate('clock_in_time', '<=', $request->input('end_date')); } if (! $can_all && $can_own) { $query->where('essentials_attendances.user_id', $user->id); } return $this->paginated($query->paginate($request->input('per_page', 25))); } public function payrolls(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $can_view_all = $user->can('essentials.view_all_payroll'); $query = $this->essentialsUtil->getPayrollQuery($business_id)->orderByDesc('transaction_date'); if ($can_view_all) { if ($request->filled('user_id')) { $query->where('transactions.expense_for', $request->input('user_id')); } } else { $query->where('transactions.expense_for', $user->id); } if ($request->filled('location_id')) { $query->where('u.location_id', $request->input('location_id')); } return $this->paginated($query->paginate($request->input('per_page', 25))); } public function showPayroll(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $payroll = Transaction::where('business_id', $business_id) ->where('type', 'payroll') ->with(['transaction_for', 'payment_lines']) ->findOrFail($id); if (! $user->can('essentials.view_all_payroll') && $payroll->expense_for != $user->id) { abort(403, 'Unauthorized action.'); } $employee = $payroll->transaction_for; $transaction_date = Carbon::parse($payroll->transaction_date); $start_of_month = $transaction_date->copy()->startOfMonth(); $end_of_month = $transaction_date->copy()->endOfMonth(); $department = $employee ? Category::where('category_type', 'hrm_department')->find($employee->essentials_department_id) : null; $designation = $employee ? Category::where('category_type', 'hrm_designation')->find($employee->essentials_designation_id) : null; $location = $employee ? BusinessLocation::where('business_id', $business_id)->find($employee->location_id) : null; $leaves = $employee ? EssentialsLeave::where('business_id', $business_id) ->where('user_id', $employee->id) ->whereDate('start_date', '>=', $start_of_month) ->whereDate('end_date', '<=', $end_of_month) ->get() : collect(); $total_leaves = 0; foreach ($leaves as $leave) { $total_leaves += Carbon::parse($leave->start_date)->diffInDays(Carbon::parse($leave->end_date)) + 1; } $total_days_present = $employee ? $this->essentialsUtil->getTotalDaysWorkedForGivenDateOfAnEmployee( $business_id, $employee->id, $start_of_month->format('Y-m-d'), $end_of_month->format('Y-m-d') ) : 0; $total_work_duration = $employee ? $this->essentialsUtil->getTotalWorkDuration( 'hour', $employee->id, $business_id, $start_of_month->format('Y-m-d'), $end_of_month->format('Y-m-d') ) : 0; return $this->success([ 'payroll' => $payroll, 'month_name' => format_jalali_month($transaction_date), 'year' => format_jalali_year($transaction_date), 'allowances' => ! empty($payroll->essentials_allowances) ? json_decode($payroll->essentials_allowances, true) : [], 'deductions' => ! empty($payroll->essentials_deductions) ? json_decode($payroll->essentials_deductions, true) : [], 'bank_details' => $employee && ! empty($employee->bank_details) ? json_decode($employee->bank_details, true) : null, 'department' => $department?->name, 'designation' => $designation?->name, 'location' => $location?->name, 'total_leaves' => $total_leaves, 'days_in_a_month' => $start_of_month->daysInMonth, 'total_days_present' => $total_days_present, 'total_work_duration' => $total_work_duration, ]); } public function payrollGroups(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.view_all_payroll')) { abort(403, 'Unauthorized action.'); } $query = PayrollGroup::where('business_id', $business_id) ->with('businessLocation') ->orderByDesc('created_at'); return $this->paginated($query->paginate($request->input('per_page', 25))); } public function showPayrollGroup(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.view_all_payroll')) { abort(403, 'Unauthorized action.'); } $group = PayrollGroup::where('business_id', $business_id) ->with(['businessLocation', 'payrollGroupTransactions.transaction_for']) ->findOrFail($id); return $this->success($group); } public function allowanceDeductions(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.add_allowance_and_deduction') && ! $user->can('essentials.view_allowance_and_deduction')) { abort(403, 'Unauthorized action.'); } $query = EssentialsAllowanceAndDeduction::where('business_id', $business_id) ->with('employees') ->orderByDesc('id'); return $this->paginated($query->paginate($request->input('per_page', 25))); } public function storeAllowanceDeduction(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.add_allowance_and_deduction')) { abort(403, 'Unauthorized action.'); } $request->validate([ 'description' => 'required', 'type' => 'required|in:allowance,deduction', 'amount' => 'required|numeric', 'amount_type' => 'required|in:fixed,percent', ]); $input = $request->only(['description', 'type', 'amount', 'amount_type', 'applicable_date']); $input['business_id'] = $business_id; if (! empty($input['applicable_date'])) { $input['applicable_date'] = $this->parseDate($input['applicable_date']); } $item = EssentialsAllowanceAndDeduction::create($input); if ($request->filled('employees')) { $item->employees()->sync($request->input('employees')); } return $this->success($item->load('employees'), '', 201); } public function updateAllowanceDeduction(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.add_allowance_and_deduction')) { abort(403, 'Unauthorized action.'); } $item = EssentialsAllowanceAndDeduction::where('business_id', $business_id)->findOrFail($id); $input = $request->only(['description', 'type', 'amount', 'amount_type', 'applicable_date']); if (! empty($input['applicable_date'])) { $input['applicable_date'] = $this->parseDate($input['applicable_date']); } $item->update($input); if ($request->has('employees')) { $item->employees()->sync($request->input('employees', [])); } return $this->success($item->load('employees')); } public function destroyAllowanceDeduction(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); if (! Auth::user()->can('essentials.add_allowance_and_deduction')) { abort(403, 'Unauthorized action.'); } EssentialsAllowanceAndDeduction::where('business_id', $business_id)->where('id', $id)->delete(); return $this->success(null, 'Deleted'); } public function salesTargetUsers(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.access_sales_target') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $users = User::where('business_id', $business_id) ->user() ->where('allow_login', 1) ->select([ 'id', DB::raw("CONCAT(COALESCE(surname, ''), ' ', COALESCE(first_name, ''), ' ', COALESCE(last_name, '')) as full_name"), ]) ->orderBy('first_name') ->get(); return $this->success($users); } public function userSalesTargets(Request $request, $userId) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.access_sales_target') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } User::where('business_id', $business_id)->findOrFail($userId); $targets = EssentialsUserSalesTarget::where('user_id', $userId)->get(); return $this->success($targets); } public function saveUserSalesTargets(Request $request, $userId) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.access_sales_target') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } User::where('business_id', $business_id)->findOrFail($userId); $request->validate(['targets' => 'required|array']); EssentialsUserSalesTarget::where('user_id', $userId)->delete(); foreach ($request->input('targets') as $target) { if (empty($target['target_start']) && empty($target['target_end'])) { continue; } EssentialsUserSalesTarget::create([ 'user_id' => $userId, 'target_start' => $target['target_start'], 'target_end' => $target['target_end'], 'commission_percent' => $target['commission_percent'] ?? 0, ]); } return $this->success(EssentialsUserSalesTarget::where('user_id', $userId)->get()); } public function todos(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $is_admin = $this->moduleUtil->is_admin($user, $business_id); $query = ToDo::where('business_id', $business_id) ->with(['users', 'assigned_by']) ->orderByDesc('id'); if (! $is_admin) { $query->where(function ($q) use ($user) { $q->where('created_by', $user->id) ->orWhereHas('users', fn ($sub) => $sub->where('user_id', $user->id)); }); } if ($request->filled('status')) { $query->where('status', $request->input('status')); } if ($request->filled('priority')) { $query->where('priority', $request->input('priority')); } return $this->paginated($query->paginate($request->input('per_page', 25))); } public function storeTodo(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $request->validate([ 'task' => 'required', 'date' => 'required|date', ]); $input = $request->only(['task', 'date', 'description', 'priority', 'status']); $input['business_id'] = $business_id; $input['created_by'] = Auth::id(); $input['date'] = $this->parseDate($input['date']); $input['status'] = $input['status'] ?? 'new'; $input['priority'] = $input['priority'] ?? 'medium'; $todo = ToDo::create($input); if ($request->filled('users')) { $todo->users()->sync($request->input('users')); } return $this->success($todo->load('users'), '', 201); } public function updateTodo(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $todo = ToDo::where('business_id', $business_id)->findOrFail($id); $input = $request->only(['task', 'date', 'description', 'priority', 'status']); if (! empty($input['date'])) { $input['date'] = $this->parseDate($input['date']); } $todo->update(array_filter($input, fn ($v) => $v !== null)); if ($request->has('users')) { $todo->users()->sync($request->input('users', [])); } return $this->success($todo->load('users')); } public function destroyTodo(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); ToDo::where('business_id', $business_id)->where('id', $id)->delete(); return $this->success(null, 'Deleted'); } public function settings(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } return $this->success($this->getSettings($business_id)); } public function updateSettings(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $input = $request->only([ 'leave_ref_no_prefix', 'leave_instructions', 'payroll_ref_no_prefix', 'essentials_todos_prefix', 'grace_before_checkin', 'grace_after_checkin', 'grace_before_checkout', 'grace_after_checkout', ]); $input['is_location_required'] = $request->boolean('is_location_required') ? 1 : 0; $input['calculate_sales_target_commission_without_tax'] = $request->boolean('calculate_sales_target_commission_without_tax') ? 1 : 0; $business = Business::findOrFail($business_id); $business->essentials_settings = json_encode($input); $business->save(); return $this->success($this->getSettings($business_id)); } public function leaveResources(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); return $this->success([ 'leave_types' => EssentialsLeaveType::where('business_id', $business_id)->get(['id', 'leave_type']), 'leave_statuses' => ['pending', 'approved', 'cancelled'], ]); } protected function ensureShiftAdmin(int $business_id): void { $user = Auth::user(); if (! $user->can('superadmin') && ! $this->moduleUtil->hasThePermissionInSubscription($business_id, 'essentials_module') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } } public function shifts(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); $shifts = Shift::where('business_id', $business_id) ->select(['id', 'name', 'type', 'start_time', 'end_time', 'holidays', 'is_allowed_auto_clockout', 'auto_clockout_time']) ->orderBy('name') ->get() ->map(function ($shift) { return [ 'id' => $shift->id, 'name' => $shift->name, 'type' => $shift->type, 'start_time' => $shift->start_time, 'end_time' => $shift->end_time, 'holidays' => $shift->holidays ?? [], ]; }); return $this->success($shifts); } public function showShift(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); $shift = Shift::where('business_id', $business_id)->findOrFail($id); return $this->success($shift); } public function storeShift(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); $request->validate([ 'name' => 'required|string|max:191', 'type' => 'required|in:fixed,flexible_shift', ]); $input = $request->only(['name', 'type', 'holidays']); if ($input['type'] !== 'flexible_shift') { $input['start_time'] = $this->moduleUtil->uf_time($request->input('start_time')); $input['end_time'] = $this->moduleUtil->uf_time($request->input('end_time')); } $input['is_allowed_auto_clockout'] = $request->boolean('is_allowed_auto_clockout') ? 1 : 0; if ($request->filled('auto_clockout_time')) { $input['auto_clockout_time'] = $this->moduleUtil->uf_time($request->input('auto_clockout_time')); } $input['business_id'] = $business_id; $shift = Shift::create($input); return $this->success($shift, __('lang_v1.added_success'), 201); } public function updateShift(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); $shift = Shift::where('business_id', $business_id)->findOrFail($id); $input = $request->only(['name', 'type', 'holidays']); if (($input['type'] ?? $shift->type) !== 'flexible_shift') { if ($request->filled('start_time')) { $input['start_time'] = $this->moduleUtil->uf_time($request->input('start_time')); } if ($request->filled('end_time')) { $input['end_time'] = $this->moduleUtil->uf_time($request->input('end_time')); } } else { $input['start_time'] = null; $input['end_time'] = null; } $input['is_allowed_auto_clockout'] = $request->boolean('is_allowed_auto_clockout') ? 1 : 0; if ($request->filled('auto_clockout_time')) { $input['auto_clockout_time'] = $this->moduleUtil->uf_time($request->input('auto_clockout_time')); } $shift->update($input); return $this->success($shift->fresh(), __('lang_v1.updated_success')); } public function destroyShift(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); Shift::where('business_id', $business_id)->where('id', $id)->delete(); return $this->success(null, 'Deleted'); } public function shiftUsers(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); $shift = Shift::where('business_id', $business_id) ->with('user_shifts') ->findOrFail($id); $users = User::forDropdown($business_id, false); $assigned = []; foreach ($shift->user_shifts as $user_shift) { $assigned[$user_shift->user_id] = [ 'start_date' => $user_shift->start_date, 'end_date' => $user_shift->end_date, ]; } return $this->success([ 'shift' => $shift, 'users' => $users, 'assigned' => $assigned, ]); } public function assignShiftUsers(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureShiftAdmin($business_id); Shift::where('business_id', $business_id)->findOrFail($id); $user_shifts = $request->input('user_shift', []); $user_ids = []; foreach ($user_shifts as $user_id => $value) { if (! empty($value['is_added'])) { $user_ids[] = $user_id; EssentialsUserShift::updateOrCreate( ['essentials_shift_id' => $id, 'user_id' => $user_id], [ 'start_date' => ! empty($value['start_date']) ? $this->parseDate($value['start_date']) : null, 'end_date' => ! empty($value['end_date']) ? $this->parseDate($value['end_date']) : null, ] ); } } EssentialsUserShift::where('essentials_shift_id', $id) ->whereNotIn('user_id', $user_ids) ->delete(); return $this->success(null, __('lang_v1.added_success')); } protected function ensureHrmAdmin(int $business_id): void { $user = Auth::user(); if (! $user->can('superadmin') && ! $this->moduleUtil->hasThePermissionInSubscription($business_id, 'essentials_module') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } } public function attendanceEmployees(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $can_all = $user->can('essentials.crud_all_attendance'); $can_own = $user->can('essentials.view_own_attendance'); if (! $can_all && ! $can_own) { abort(403, 'Unauthorized action.'); } $query = User::where('business_id', $business_id)->user(); if (! $can_all && $can_own) { $query->where('id', $user->id); } $employees = $query->get(['id', 'first_name', 'last_name', 'surname']) ->map(fn ($u) => [ 'id' => $u->id, 'name' => trim("{$u->surname} {$u->first_name} {$u->last_name}"), ]); return $this->success($employees); } public function storeAttendance(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureHrmAdmin($business_id); $request->validate([ 'user_id' => 'required|integer', 'clock_in_time' => 'required', ]); $ip = $this->moduleUtil->getUserIpAddr(); $attendance = EssentialsAttendance::create([ 'business_id' => $business_id, 'user_id' => $request->input('user_id'), 'clock_in_time' => $this->moduleUtil->uf_date($request->input('clock_in_time'), true), 'clock_out_time' => $request->filled('clock_out_time') ? $this->moduleUtil->uf_date($request->input('clock_out_time'), true) : null, 'clock_in_note' => $request->input('clock_in_note'), 'clock_out_note' => $request->input('clock_out_note'), 'essentials_shift_id' => $request->input('essentials_shift_id'), 'ip_address' => $request->input('ip_address', $ip), ]); return $this->success($attendance, __('lang_v1.added_success'), 201); } public function importAttendance(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureHrmAdmin($business_id); $request->validate(['attendance' => 'required|file|mimes:xls,xlsx']); try { ini_set('max_execution_time', 0); $parsed = Excel::toArray([], $request->file('attendance')); $imported_data = array_splice($parsed[0], 1); $formated_data = []; $ip_address = $this->moduleUtil->getUserIpAddr(); DB::beginTransaction(); foreach ($imported_data as $key => $value) { $row_no = $key + 2; if (empty($value[0])) { throw new \Exception("Email is required in row $row_no"); } $user = User::where('business_id', $business_id)->where('email', trim($value[0]))->first(); if (! $user) { throw new \Exception("User not found in row $row_no"); } if (empty($value[1])) { throw new \Exception("Clock in time is required in row $row_no"); } $row = [ 'business_id' => $business_id, 'user_id' => $user->id, 'clock_in_time' => trim($value[1]), 'clock_out_time' => ! empty($value[2]) ? trim($value[2]) : null, 'clock_in_note' => ! empty($value[4]) ? trim($value[4]) : null, 'clock_out_note' => ! empty($value[5]) ? trim($value[5]) : null, 'ip_address' => ! empty($value[6]) ? trim($value[6]) : $ip_address, ]; if (! empty($value[3])) { $shift = Shift::where('business_id', $business_id)->where('name', trim($value[3]))->first(); if (! $shift) { throw new \Exception("Shift not found in row $row_no"); } $row['essentials_shift_id'] = $shift->id; } $formated_data[] = $row; } if (! empty($formated_data)) { EssentialsAttendance::insert($formated_data); } DB::commit(); return $this->success(['imported' => count($formated_data)], __('product.file_imported_successfully')); } catch (\Exception $e) { DB::rollBack(); return $this->error($e->getMessage() ?: __('messages.something_went_wrong'), 422); } } public function payrollEmployees(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.create_payroll') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $query = User::where('business_id', $business_id)->user(); if ($request->filled('location_id')) { $query->where('location_id', $request->input('location_id')); } $employees = $query->get(['id', 'first_name', 'last_name', 'surname', 'essentials_salary', 'essentials_pay_period']) ->map(fn ($u) => [ 'id' => $u->id, 'name' => trim("{$u->surname} {$u->first_name} {$u->last_name}"), 'essentials_salary' => $u->essentials_salary, 'essentials_pay_period' => $u->essentials_pay_period, ]); return $this->success($employees); } public function payrollPreview(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.create_payroll') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $request->validate([ 'employee_ids' => 'required|array|min:1', 'month_year' => 'required|string', 'location_id' => 'nullable|integer', ]); $employee_ids = $request->input('employee_ids'); $parts = explode('/', $request->input('month_year')); $month = $parts[0]; $year = $parts[1]; $transaction_date = $year.'-'.$month.'-01'; $start_date = $transaction_date; $end_date = Carbon::parse($start_date)->endOfMonth()->format('Y-m-d'); $existing = Transaction::where('business_id', $business_id) ->where('type', 'payroll') ->whereIn('expense_for', $employee_ids) ->whereDate('transaction_date', $transaction_date) ->pluck('expense_for') ->all(); $add_for = array_diff($employee_ids, $existing); $payrolls = []; foreach (User::where('business_id', $business_id)->whereIn('id', $add_for)->get() as $employee) { $row = [ 'expense_for' => $employee->id, 'name' => $employee->user_full_name, 'essentials_salary' => $employee->essentials_salary, 'essentials_pay_period' => $employee->essentials_pay_period, 'essentials_duration' => 1, 'essentials_duration_unit' => $employee->essentials_pay_period, 'essentials_amount_per_unit_duration' => $employee->essentials_salary, 'total_leaves' => $this->essentialsUtil->getTotalLeavesForGivenDateOfAnEmployee($business_id, $employee->id, $start_date, $end_date), 'total_days_worked' => $this->essentialsUtil->getTotalDaysWorkedForGivenDateOfAnEmployee($business_id, $employee->id, $start_date, $end_date), 'total_work_duration' => $this->essentialsUtil->getTotalWorkDuration('hour', $employee->id, $business_id, $start_date, $end_date), 'allowance_names' => [], 'allowance_types' => [], 'allowance_percent' => [], 'allowance_amounts' => [], 'deduction_names' => [], 'deduction_types' => [], 'deduction_percent' => [], 'deduction_amounts' => [], ]; $base = (float) $employee->essentials_salary; $row['final_total'] = $base; $ads = $this->essentialsUtil->getEmployeeAllowancesAndDeductions($business_id, $employee->id, $start_date, $end_date); foreach ($ads as $ad) { if ($ad->type === 'allowance') { $amt = $ad->amount_type === 'fixed' ? $ad->amount : ($base * $ad->amount / 100); $row['allowance_names'][] = $ad->description; $row['allowance_types'][] = $ad->amount_type; $row['allowance_percent'][] = $ad->amount_type === 'percent' ? $ad->amount : 0; $row['allowance_amounts'][] = $amt; $row['final_total'] += $amt; } else { $amt = $ad->amount_type === 'fixed' ? $ad->amount : ($base * $ad->amount / 100); $row['deduction_names'][] = $ad->description; $row['deduction_types'][] = $ad->amount_type; $row['deduction_percent'][] = $ad->amount_type === 'percent' ? $ad->amount : 0; $row['deduction_amounts'][] = $amt; $row['final_total'] -= $amt; } } $payrolls[$employee->id] = $row; } return $this->success([ 'transaction_date' => $transaction_date, 'month_name' => format_jalali_month(Carbon::parse($transaction_date)), 'year' => format_jalali_year(Carbon::parse($transaction_date)), 'payrolls' => $payrolls, 'skipped_employee_ids' => array_values($existing), ]); } public function storePayrollGroup(Request $request) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); if (! $user->can('essentials.create_payroll') && ! $this->moduleUtil->is_admin($user, $business_id)) { abort(403, 'Unauthorized action.'); } $request->validate([ 'transaction_date' => 'required|date', 'payroll_group_name' => 'required|string', 'payroll_group_status' => 'required|in:draft,final', 'payrolls' => 'required|array|min:1', ]); try { DB::beginTransaction(); $group = PayrollGroup::create([ 'business_id' => $business_id, 'name' => $request->input('payroll_group_name'), 'status' => $request->input('payroll_group_status'), 'gross_total' => $this->transactionUtil->num_uf($request->input('total_gross_amount', 0)), 'location_id' => $request->input('location_id'), 'created_by' => $user->id, ]); $settings = $this->getSettings($business_id); $prefix = $settings['payroll_ref_no_prefix'] ?? ''; $transaction_ids = []; foreach ($request->input('payrolls') as $payroll) { $ad = $this->payrollAllowanceDeductionJson($payroll); $ref_count = $this->moduleUtil->setAndGetReferenceCount('payroll'); $transaction = Transaction::create([ 'business_id' => $business_id, 'created_by' => $user->id, 'type' => 'payroll', 'status' => 'final', 'payment_status' => 'due', 'transaction_date' => $request->input('transaction_date'), 'expense_for' => $payroll['expense_for'], 'final_total' => $this->moduleUtil->num_uf($payroll['final_total'] ?? 0), 'total_before_tax' => $this->moduleUtil->num_uf($payroll['final_total'] ?? 0), 'essentials_duration' => $payroll['essentials_duration'] ?? 1, 'essentials_duration_unit' => $payroll['essentials_duration_unit'] ?? 'month', 'essentials_amount_per_unit_duration' => $this->moduleUtil->num_uf($payroll['essentials_amount_per_unit_duration'] ?? 0), 'essentials_allowances' => $ad['essentials_allowances'], 'essentials_deductions' => $ad['essentials_deductions'], 'staff_note' => $payroll['staff_note'] ?? null, 'ref_no' => $this->moduleUtil->generateReferenceNumber('payroll', $ref_count, null, $prefix), ]); $transaction_ids[] = $transaction->id; } $group->payrollGroupTransactions()->sync($transaction_ids); DB::commit(); return $this->success($group->load('payrollGroupTransactions'), __('lang_v1.added_success'), 201); } catch (\Exception $e) { DB::rollBack(); return $this->error(__('messages.something_went_wrong'), 500); } } public function payPayrollGroup(Request $request, $id) { $this->ensureEssentials(); $business_id = $this->contextService->getBusinessId($request); $this->ensureHrmAdmin($business_id); $request->validate(['payments' => 'required|array']); try { DB::beginTransaction(); foreach ($request->input('payments') as $payment) { if (empty($payment['transaction_id']) || empty($payment['method']) || empty($payment['final_total'])) { continue; } $transaction = Transaction::where('business_id', $business_id)->findOrFail($payment['transaction_id']); if ($transaction->payment_status === 'paid') { continue; } $transaction_before = $transaction->replicate(); $input = [ 'method' => $payment['method'], 'business_id' => $business_id, 'paid_on' => $this->transactionUtil->uf_date($payment['paid_on'] ?? now(), true), 'transaction_id' => $payment['transaction_id'], 'amount' => $this->transactionUtil->num_uf($payment['final_total']), 'created_by' => Auth::id(), ]; if (! empty($payment['account_id'])) { $input['account_id'] = $payment['account_id']; } $ref_count = $this->transactionUtil->setAndGetReferenceCount('purchase_payment'); $input['payment_ref_no'] = $this->transactionUtil->generateReferenceNumber('purchase_payment', $ref_count); $tp = TransactionPayment::create($input); $input['transaction_type'] = $transaction->type; event(new TransactionPaymentAdded($tp, $input)); $transaction->payment_status = $this->transactionUtil->updatePaymentStatus($payment['transaction_id']); $transaction->save(); $this->transactionUtil->activityLog($transaction, 'payment_edited', $transaction_before); } $this->updatePayrollGroupPaymentStatus($id, $business_id); DB::commit(); return $this->success(null, __('purchase.payment_added_success')); } catch (\Exception $e) { DB::rollBack(); return $this->error(__('messages.something_went_wrong'), 500); } } protected function payrollAllowanceDeductionJson(array $payroll): array { $allowance_names = []; $allowance_amounts = []; $allowance_types = $payroll['allowance_types'] ?? []; $allowance_percents = []; foreach ($payroll['allowance_amounts'] ?? [] as $key => $value) { $name = $payroll['allowance_names'][$key] ?? null; if (! empty($name)) { $allowance_names[] = $name; $allowance_amounts[] = $this->moduleUtil->num_uf($value); $allowance_percents[] = $this->moduleUtil->num_uf($payroll['allowance_percent'][$key] ?? 0); } } $deduction_names = []; $deduction_amounts = []; $deduction_types = $payroll['deduction_types'] ?? []; $deduction_percents = []; foreach ($payroll['deduction_amounts'] ?? [] as $key => $value) { $name = $payroll['deduction_names'][$key] ?? null; if (! empty($name)) { $deduction_names[] = $name; $deduction_amounts[] = $this->moduleUtil->num_uf($value); $deduction_percents[] = $this->moduleUtil->num_uf($payroll['deduction_percent'][$key] ?? 0); } } return [ 'essentials_allowances' => json_encode([ 'allowance_names' => $allowance_names, 'allowance_amounts' => $allowance_amounts, 'allowance_types' => $allowance_types, 'allowance_percents' => $allowance_percents, ]), 'essentials_deductions' => json_encode([ 'deduction_names' => $deduction_names, 'deduction_amounts' => $deduction_amounts, 'deduction_types' => $deduction_types, 'deduction_percents' => $deduction_percents, ]), ]; } protected function updatePayrollGroupPaymentStatus($payroll_group_id, $business_id): void { $group = PayrollGroup::where('business_id', $business_id) ->with('payrollGroupTransactions') ->findOrFail($payroll_group_id); $total = $group->payrollGroupTransactions->count(); $paid = $group->payrollGroupTransactions->where('payment_status', 'paid')->count(); $due = $group->payrollGroupTransactions->where('payment_status', '!=', 'paid')->count(); if ($total === $paid) { $status = 'paid'; } elseif ($total === $due) { $status = 'due'; } else { $status = 'partial'; } $group->payment_status = $status; $group->save(); } }