1634 lines
64 KiB
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();
|
|
}
|
|
}
|