ultimatepos/Modules/Connector/Http/Controllers/Api/V2/HrmExtendedController.php

1634 lines
64 KiB
PHP

<?php
namespace Modules\Connector\Http\Controllers\Api\V2;
use App\Business;
use App\BusinessLocation;
use App\Category;
use App\Services\Api\BusinessContextService;
use App\Transaction;
use App\TransactionPayment;
use App\User;
use App\Utils\BusinessUtil;
use App\Utils\ModuleUtil;
use App\Utils\TransactionUtil;
use App\Events\TransactionPaymentAdded;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Essentials\Entities\EssentialsAllowanceAndDeduction;
use Modules\Essentials\Entities\EssentialsAttendance;
use Modules\Essentials\Entities\EssentialsHoliday;
use Modules\Essentials\Entities\EssentialsLeave;
use Modules\Essentials\Entities\EssentialsLeaveType;
use Modules\Essentials\Entities\EssentialsUserSalesTarget;
use Modules\Essentials\Entities\EssentialsUserShift;
use Modules\Essentials\Entities\PayrollGroup;
use Modules\Essentials\Entities\Shift;
use Modules\Essentials\Entities\ToDo;
use Modules\Essentials\Utils\EssentialsUtil;
class HrmExtendedController extends BaseController
{
public function __construct(
protected BusinessContextService $contextService,
protected ModuleUtil $moduleUtil,
protected EssentialsUtil $essentialsUtil,
protected TransactionUtil $transactionUtil,
protected BusinessUtil $businessUtil
) {
}
protected function ensureEssentials(): void
{
if (! $this->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();
}
}