2026-04-08 07:22:13 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
|
use Carbon\Carbon;
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
|
use Illuminate\Support\Facades\Cookie;
|
|
|
|
|
|
use Illuminate\Support\Facades\Redis;
|
|
|
|
|
|
use Illuminate\Console\View\Components\Task;
|
|
|
|
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
|
|
use Illuminate\Support\Facades\Response;
|
|
|
|
|
|
use App\Models\Attendance;
|
|
|
|
|
|
use \App\Models\AttendanceBreak;
|
|
|
|
|
|
use App\Models\ai;
|
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
|
use Morilog\Jalali\Jalalian;
|
|
|
|
|
|
use GuzzleHttp\Client;
|
|
|
|
|
|
use App\Models\Tasks;
|
|
|
|
|
|
use App\Models\Pivot_Tasks;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminController extends Controller
|
|
|
|
|
|
{
|
2026-06-02 22:17:47 +03:30
|
|
|
|
private function redisIsAvailable(): bool
|
2026-06-02 18:45:01 +03:30
|
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
|
|
|
Redis::connection()->ping();
|
2026-06-02 22:17:47 +03:30
|
|
|
|
|
|
|
|
|
|
return true;
|
2026-06-02 18:45:01 +03:30
|
|
|
|
} catch (\Throwable) {
|
2026-06-02 22:17:47 +03:30
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function appendDecodedRequest(array &$target, ?string $raw): void
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!$raw) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$request = json_decode($raw);
|
|
|
|
|
|
if ($request && ($request->function ?? null) !== 'rejected') {
|
|
|
|
|
|
$target[] = $request;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function appendDecodedRequestWithAttendance(array &$target, ?string $raw, ?Attendance $attendance = null): void
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!$raw) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$request = json_decode($raw);
|
|
|
|
|
|
if ($request && ($request->function ?? null) !== 'rejected') {
|
|
|
|
|
|
$target[] = $this->enrichRequestForView($request, $attendance);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function buildFallbackRequestFromAttendance(Attendance $attendance): object
|
|
|
|
|
|
{
|
|
|
|
|
|
$request = (object) [
|
|
|
|
|
|
'function' => 'edit',
|
|
|
|
|
|
'userid' => $attendance->user_identity,
|
|
|
|
|
|
'id' => $attendance->id,
|
|
|
|
|
|
'oldenter' => $attendance->entered_at ? Carbon::parse($attendance->entered_at)->format('H:i') : '',
|
|
|
|
|
|
'newenter' => '',
|
|
|
|
|
|
'oldexit' => $attendance->exited_at ? Carbon::parse($attendance->exited_at)->format('H:i') : '',
|
|
|
|
|
|
'newexit' => '',
|
|
|
|
|
|
'date' => $attendance->entered_at
|
|
|
|
|
|
? Jalalian::fromCarbon(Carbon::parse($attendance->entered_at))->format('Y/m/d')
|
|
|
|
|
|
: null,
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
$request->display_date = $this->formatRequestDisplayDate($request, $attendance);
|
|
|
|
|
|
|
|
|
|
|
|
return $request;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function formatRequestDisplayDate(object $request, ?Attendance $attendance = null): string
|
|
|
|
|
|
{
|
|
|
|
|
|
$candidates = array_filter([
|
|
|
|
|
|
$attendance?->entered_at,
|
|
|
|
|
|
(!empty($request->oldenter) && str_contains((string) $request->oldenter, ' '))
|
|
|
|
|
|
? $request->oldenter
|
|
|
|
|
|
: null,
|
|
|
|
|
|
$request->date ?? null,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($candidates as $value) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (is_string($value) && preg_match('/^\d{4}\/\d{2}\/\d{2}$/', $value)) {
|
|
|
|
|
|
$year = (int) substr($value, 0, 4);
|
|
|
|
|
|
|
|
|
|
|
|
if ($year >= 1700) {
|
|
|
|
|
|
return Jalalian::fromCarbon(Carbon::createFromFormat('Y/m/d', $value))->format('Y/m/d');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($year >= 1300) {
|
|
|
|
|
|
return Jalalian::fromFormat('Y/m/d', $value)->format('Y/m/d');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$carbon = Carbon::parse($value);
|
|
|
|
|
|
if ($carbon->year >= 1000) {
|
|
|
|
|
|
return Jalalian::fromCarbon($carbon)->format('Y/m/d');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (\Throwable) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return '-';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function enrichRequestForView(object $request, ?Attendance $attendance = null): object
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isset($request->display_date)) {
|
|
|
|
|
|
$request->display_date = $this->formatRequestDisplayDate($request, $attendance);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $request;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function resolveRequestTime(?string $preferred, ?string $fallback, $recordDefault = null): ?string
|
|
|
|
|
|
{
|
|
|
|
|
|
$value = ($preferred !== null && $preferred !== '') ? $preferred : $fallback;
|
|
|
|
|
|
if ($value === null || $value === '') {
|
|
|
|
|
|
if ($recordDefault) {
|
|
|
|
|
|
return Carbon::parse($recordDefault)->format('H:i:s');
|
2026-06-02 18:45:01 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
return null;
|
2026-06-02 18:45:01 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
return Carbon::parse($value)->format('H:i:s');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function buildAttendanceTimestamp(string $date, ?string $time): ?string
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!$time) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $date . ' ' . $time;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function decrementUserRequestCount(?string $userName): void
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!$userName) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
User::where('user_name', $userName)
|
|
|
|
|
|
->where('req_counts', '>', 0)
|
|
|
|
|
|
->decrement('req_counts');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function loadPendingRequests($users): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$userNames = $users->pluck('user_name')->filter()->values();
|
|
|
|
|
|
$redisAvailable = $this->redisIsAvailable();
|
|
|
|
|
|
|
|
|
|
|
|
$pendingByUser = Attendance::query()
|
|
|
|
|
|
->whereIn('user_identity', $userNames)
|
|
|
|
|
|
->whereNotNull('requested')
|
|
|
|
|
|
->where('requested', '!=', '')
|
|
|
|
|
|
->get(['id', 'user_identity', 'entered_at', 'exited_at'])
|
|
|
|
|
|
->groupBy('user_identity');
|
|
|
|
|
|
|
|
|
|
|
|
$records = [];
|
|
|
|
|
|
|
2026-06-02 18:45:01 +03:30
|
|
|
|
foreach ($users as $user) {
|
|
|
|
|
|
$rec = [
|
|
|
|
|
|
'user_name' => $user->user_name,
|
|
|
|
|
|
'name' => $user->name,
|
|
|
|
|
|
'attendances' => [],
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$pendingForUser = $pendingByUser->get($user->user_name, collect());
|
|
|
|
|
|
$hasRequestHint = ($user->req_counts ?? 0) > 0 || $pendingForUser->isNotEmpty();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$hasRequestHint) {
|
|
|
|
|
|
$records[] = $rec;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$loadedIds = [];
|
|
|
|
|
|
|
|
|
|
|
|
if ($redisAvailable) {
|
|
|
|
|
|
foreach (Redis::lrange("att-tempreq:creates:{$user->user_name}", 0, -1) as $reqJson) {
|
|
|
|
|
|
$this->appendDecodedRequestWithAttendance($rec['attendances'], $reqJson);
|
2026-06-02 18:45:01 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$this->appendDecodedRequestWithAttendance($rec['attendances'], Redis::get("att-tempreq:{$user->user_name}:new"));
|
2026-06-02 18:45:01 +03:30
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
if ($pendingForUser->isNotEmpty()) {
|
|
|
|
|
|
$redisKeys = $pendingForUser
|
|
|
|
|
|
->map(fn ($attendance) => "att-tempreq:{$user->user_name}:{$attendance->id}")
|
|
|
|
|
|
->values()
|
|
|
|
|
|
->all();
|
|
|
|
|
|
|
|
|
|
|
|
$rawValues = Redis::mget($redisKeys);
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($pendingForUser->values() as $index => $attendance) {
|
|
|
|
|
|
$raw = $rawValues[$index] ?? null;
|
|
|
|
|
|
if (!$raw) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$beforeCount = count($rec['attendances']);
|
|
|
|
|
|
$this->appendDecodedRequestWithAttendance($rec['attendances'], $raw, $attendance);
|
|
|
|
|
|
if (count($rec['attendances']) > $beforeCount) {
|
|
|
|
|
|
$loadedIds[(string) $attendance->id] = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-02 18:45:01 +03:30
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
foreach ($pendingForUser as $attendance) {
|
|
|
|
|
|
if (isset($loadedIds[(string) $attendance->id])) {
|
|
|
|
|
|
continue;
|
2026-06-02 18:45:01 +03:30
|
|
|
|
}
|
2026-06-02 22:17:47 +03:30
|
|
|
|
|
|
|
|
|
|
$rec['attendances'][] = $this->buildFallbackRequestFromAttendance($attendance);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$pendingById = $pendingForUser->keyBy('id');
|
|
|
|
|
|
foreach ($rec['attendances'] as $index => $request) {
|
|
|
|
|
|
$attendance = $pendingById->get($request->id ?? null);
|
|
|
|
|
|
$rec['attendances'][$index] = $this->enrichRequestForView($request, $attendance);
|
2026-06-02 18:45:01 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$records[] = $rec;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $records;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function enrichUsersForDashboard($users): void
|
|
|
|
|
|
{
|
|
|
|
|
|
$userNames = $users->pluck('user_name');
|
|
|
|
|
|
$now = Carbon::now();
|
|
|
|
|
|
$monthStart = $now->copy()->startOfMonth();
|
|
|
|
|
|
$monthEnd = $now->copy()->endOfMonth();
|
|
|
|
|
|
$today = $now->format('Y-m-d');
|
|
|
|
|
|
|
|
|
|
|
|
$monthAttendances = Attendance::query()
|
|
|
|
|
|
->whereIn('user_identity', $userNames)
|
|
|
|
|
|
->whereBetween('entered_at', [$monthStart, $monthEnd])
|
|
|
|
|
|
->whereNotNull('duration')
|
|
|
|
|
|
->select(['user_identity', 'duration'])
|
|
|
|
|
|
->get()
|
|
|
|
|
|
->groupBy('user_identity');
|
|
|
|
|
|
|
|
|
|
|
|
$openAttendances = Attendance::query()
|
|
|
|
|
|
->whereIn('user_identity', $userNames)
|
|
|
|
|
|
->whereNull('exited_at')
|
|
|
|
|
|
->select(['id', 'user_identity', 'entered_at'])
|
|
|
|
|
|
->get()
|
|
|
|
|
|
->groupBy('user_identity');
|
|
|
|
|
|
|
|
|
|
|
|
$activeBreakAttendanceIds = AttendanceBreak::query()
|
|
|
|
|
|
->whereIn('attendance_id', $openAttendances->flatten()->pluck('id'))
|
|
|
|
|
|
->whereNull('break_end')
|
|
|
|
|
|
->pluck('attendance_id')
|
|
|
|
|
|
->flip();
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($users as $user) {
|
|
|
|
|
|
$totalSeconds = 0;
|
|
|
|
|
|
foreach ($monthAttendances->get($user->user_name, []) as $attendance) {
|
|
|
|
|
|
if (!$attendance->duration) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$parts = explode(':', $attendance->duration);
|
|
|
|
|
|
if (count($parts) !== 3) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
[$h, $m, $s] = array_map('intval', $parts);
|
|
|
|
|
|
$totalSeconds += ($h * 3600) + ($m * 60) + $s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$hours = intdiv($totalSeconds, 3600);
|
|
|
|
|
|
$minutes = intdiv($totalSeconds % 3600, 60);
|
|
|
|
|
|
$user->totaltime = sprintf('%02d:%02d', $hours, $minutes);
|
|
|
|
|
|
$user->working = 0;
|
|
|
|
|
|
|
|
|
|
|
|
$openForUser = $openAttendances->get($user->user_name, collect());
|
|
|
|
|
|
if ($openForUser->isEmpty()) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$latestOpen = $openForUser->sortByDesc('entered_at')->first();
|
|
|
|
|
|
if ($latestOpen && Carbon::parse($latestOpen->entered_at)->format('Y-m-d') === $today) {
|
|
|
|
|
|
$user->working = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($openForUser as $openAttendance) {
|
|
|
|
|
|
if ($activeBreakAttendanceIds->has($openAttendance->id)) {
|
|
|
|
|
|
$user->working = 2;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 07:22:13 +00:00
|
|
|
|
public function dashboard()
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if (isset($_COOKIE['token-0'])) {
|
|
|
|
|
|
$uname = $_COOKIE['token-0'];
|
|
|
|
|
|
setcookie("user_identity", $_COOKIE['token-0'], time() + (60 * 60 * 24 * 365), '/');
|
|
|
|
|
|
Cookie::queue(Cookie::forget('token-0'));
|
2026-06-02 18:45:01 +03:30
|
|
|
|
} else {
|
|
|
|
|
|
$uname = $_COOKIE['user_identity'] ?? null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!$uname) {
|
|
|
|
|
|
return redirect('/')->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
'text' => 'ابتدا باید وارد شوید!'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 07:22:13 +00:00
|
|
|
|
$u = User::select('name', 'is_admin')
|
|
|
|
|
|
->where('user_name', $uname)
|
|
|
|
|
|
->first();
|
2026-06-02 18:45:01 +03:30
|
|
|
|
|
|
|
|
|
|
if (!$u) {
|
|
|
|
|
|
return redirect('/')->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
'text' => 'کاربر یافت نشد!'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-18 06:17:01 +00:00
|
|
|
|
if ($u->is_admin == 1) {
|
2026-04-08 09:22:17 +00:00
|
|
|
|
$provider = 'admin';
|
2026-04-18 06:17:01 +00:00
|
|
|
|
} elseif ($u->is_admin == 2) {
|
2026-04-08 09:22:17 +00:00
|
|
|
|
$provider = 'supervisor';
|
|
|
|
|
|
}
|
2026-06-02 18:45:01 +03:30
|
|
|
|
|
2026-04-08 09:22:17 +00:00
|
|
|
|
if ($u->is_admin == '1' || $u->is_admin == '2') {
|
2026-04-08 07:22:13 +00:00
|
|
|
|
setcookie('ad', '1', time() + (60 * 60 * 24 * 365 * 5), "/");
|
2026-06-02 18:45:01 +03:30
|
|
|
|
$users = User::whereNotNull('user_name')
|
|
|
|
|
|
->select(['id', 'name', 'user_name', 'req_counts', 'active_task'])
|
2026-04-08 07:22:13 +00:00
|
|
|
|
->get();
|
|
|
|
|
|
|
2026-06-02 18:45:01 +03:30
|
|
|
|
$this->enrichUsersForDashboard($users);
|
|
|
|
|
|
$users = $users->sortByDesc('working');
|
|
|
|
|
|
$records = $this->loadPendingRequests($users);
|
2026-04-08 07:22:13 +00:00
|
|
|
|
|
|
|
|
|
|
return view("admin..admin")->with(['message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
"text" => 'شما به پنل ادمین وارد شدید✔️'
|
|
|
|
|
|
],
|
|
|
|
|
|
"users" => $users,
|
2026-04-08 09:22:17 +00:00
|
|
|
|
'reqs' => $records,
|
|
|
|
|
|
"userType" => $provider
|
2026-04-08 07:22:13 +00:00
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
"text" => 'شما دسترسی به این صفحه را ندارید!'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function newUser()
|
|
|
|
|
|
{
|
|
|
|
|
|
// dd('hello world');
|
|
|
|
|
|
return view('admin.new');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function addUser(Request $request)
|
|
|
|
|
|
{
|
|
|
|
|
|
// dd($request);
|
|
|
|
|
|
try {
|
|
|
|
|
|
$user = new User();
|
|
|
|
|
|
$user->name = $request->name;
|
|
|
|
|
|
$user->email = $request->email;
|
|
|
|
|
|
$user->user_name = $request->username;
|
|
|
|
|
|
$user->chatid = $request->chatid;
|
|
|
|
|
|
$user->number = $request->number;
|
|
|
|
|
|
$user->save();
|
|
|
|
|
|
return redirect(route('admin.dashboard'))->with('message', [
|
|
|
|
|
|
'color' => '#009900ff',
|
|
|
|
|
|
'text' => 'کاربر با موفقیت ایجاد شد✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
} catch (\Exception $exception) {
|
|
|
|
|
|
return redirect(route('admin.dashboard'))->with('message', [
|
|
|
|
|
|
'color' => '#ff9900ff',
|
|
|
|
|
|
'text' => 'مقادیر وارد شده تکراری یا اشتباه است✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function edit(Request $request)
|
|
|
|
|
|
{
|
|
|
|
|
|
// dd($request);
|
|
|
|
|
|
$allData = array_merge($request->all(), $request->query());
|
|
|
|
|
|
$converted = $this->fa2en_numbers($allData);
|
|
|
|
|
|
$request->merge($converted);
|
|
|
|
|
|
|
|
|
|
|
|
$id = $request->id;
|
|
|
|
|
|
$nstartt = $request->starttime;
|
|
|
|
|
|
$nendt = $request->endtime;
|
|
|
|
|
|
// dd($request);
|
2026-04-08 09:22:17 +00:00
|
|
|
|
$user = User::where('user_name', $_COOKIE['user_identity'])->first();
|
2026-04-18 06:17:01 +00:00
|
|
|
|
if ($user->is_admin == 2) {
|
2026-04-08 09:22:17 +00:00
|
|
|
|
$attController = new AttendanceController();
|
|
|
|
|
|
return $attController->edit($request);
|
|
|
|
|
|
}
|
2026-04-08 07:22:13 +00:00
|
|
|
|
if ($id == 'new') {
|
|
|
|
|
|
$user_name = $request->uname;
|
2026-04-18 06:17:01 +00:00
|
|
|
|
if ($request->display_date == null) {
|
2026-04-08 07:22:13 +00:00
|
|
|
|
$date = Carbon::now()->format('Y-m-d');
|
2026-04-18 06:17:01 +00:00
|
|
|
|
} else {
|
2026-04-08 07:22:13 +00:00
|
|
|
|
// dump($date);
|
|
|
|
|
|
$date = \Morilog\Jalali\CalendarUtils::convertNumbers($request->display_date, true);
|
|
|
|
|
|
$date = str_replace('/', '-', $date);
|
|
|
|
|
|
// dump($date);
|
|
|
|
|
|
$date = Jalalian::fromFormat('Y-m-d', $date)->toCarbon()->format('Y-m-d');
|
|
|
|
|
|
// $date = \Morilog\Jalali\CalendarUtils::createCarbonFromFormat('Y-m-d', $date)->format('Y-m-d');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (($nendt > $nstartt) || ($nendt === null && $nstartt !== null)) {
|
|
|
|
|
|
$attendance = new Attendance();
|
|
|
|
|
|
$attendance->ip_address = 'admin-manual';
|
|
|
|
|
|
$attendance->user_identity = $user_name;
|
|
|
|
|
|
$attendance->entered_at = $nstartt ? $date . ' ' . $nstartt : null;
|
|
|
|
|
|
$attendance->exited_at = $nendt ? $date . ' ' . $nendt : null;
|
|
|
|
|
|
$attendance->duration = $nendt !== null ? $attendance->duration($nstartt, $nendt) : null;
|
|
|
|
|
|
$attendance->save();
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
"text" => 'تغییرات با موفقیت ذخیره شد.✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff4400ff',
|
|
|
|
|
|
"text" => 'مقادیر اشتباه وارد شده است✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$attendance = Attendance::find($id);
|
|
|
|
|
|
|
|
|
|
|
|
$date = Carbon::parse($attendance->entered_at ?? $attendance->exited_at)->format('Y-m-d');
|
|
|
|
|
|
|
|
|
|
|
|
$ostartt = Carbon::parse($attendance->entered_at)->format('H:i');
|
|
|
|
|
|
$oendt = Carbon::parse($attendance->exited_at)->format('H:i');
|
|
|
|
|
|
|
|
|
|
|
|
if (($ostartt != $nstartt || $oendt != $nendt) && ($nendt != null ? $nendt > $nstartt : true)) {
|
|
|
|
|
|
|
|
|
|
|
|
$attendance->entered_at = $date . ' ' . $nstartt;
|
|
|
|
|
|
$attendance->exited_at = $nendt ? $date . ' ' . $nendt : null;
|
|
|
|
|
|
$attendance->duration = $nendt ? $attendance->duration($nstartt, $nendt) : null;
|
|
|
|
|
|
|
|
|
|
|
|
$attendance->save();
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
"text" => 'تغییرات با موفقیت ذخیره شد.✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff4400ff',
|
|
|
|
|
|
"text" => 'مقادیر اشتباه وارد شده است✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete(Request $request)
|
|
|
|
|
|
{
|
|
|
|
|
|
// dd($request);
|
|
|
|
|
|
$id = $request->id;
|
|
|
|
|
|
$attendance = Attendance::find($id);
|
|
|
|
|
|
if ($attendance) {
|
|
|
|
|
|
$attendance->delete();
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
"text" => 'حذف با موفقیت انجام شد.✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff4400ff',
|
|
|
|
|
|
"text" => 'خطا در حذف رکورد✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function update(Request $request)
|
|
|
|
|
|
{
|
|
|
|
|
|
// dd($request);
|
|
|
|
|
|
|
|
|
|
|
|
$data = json_decode($request->data);
|
|
|
|
|
|
if ($request->update == 'approve') {
|
|
|
|
|
|
|
|
|
|
|
|
if ($data->function == 'delete') {
|
|
|
|
|
|
$st = Attendance::find($data->id);
|
|
|
|
|
|
if ($st) {
|
|
|
|
|
|
$st->delete();
|
|
|
|
|
|
if ($st) {
|
|
|
|
|
|
tel_notif($st->user_identity, "تایید");
|
2026-04-08 09:30:38 +00:00
|
|
|
|
// $create_requests_key = "att-tempreq:creates:{$data->userid}";
|
|
|
|
|
|
// Redis::connection()->lrem($create_requests_key, 1, json_encode($data));
|
2026-04-08 07:22:13 +00:00
|
|
|
|
|
2026-04-08 09:30:38 +00:00
|
|
|
|
Redis::connection()->del("att-tempreq:{$data->userid}:{$data->id}");
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$this->decrementUserRequestCount($data->userid);
|
2026-04-08 07:22:13 +00:00
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
'text' => 'رکورد با موفقیت حذف شد✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
'text' => 'حذف رکورد با شکست مواجه شد✖️️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
'text' => 'رکورد پیدا نشد✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
} elseif ($data->function == 'edit') {
|
|
|
|
|
|
$record = Attendance::find($data->id);
|
2026-06-02 22:17:47 +03:30
|
|
|
|
if (!$record) {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
'text' => 'رکورد پیدا نشد✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 07:22:13 +00:00
|
|
|
|
$attendance = new Attendance();
|
|
|
|
|
|
$date = Carbon::parse($record->entered_at)->format('Y-m-d');
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$enterTime = $this->resolveRequestTime($data->newenter ?? null, $data->oldenter ?? null, $record->entered_at);
|
|
|
|
|
|
$exitTime = $this->resolveRequestTime($data->newexit ?? null, $data->oldexit ?? null, $record->exited_at);
|
|
|
|
|
|
|
2026-04-08 07:22:13 +00:00
|
|
|
|
$record->ip_address = 'admin-edited';
|
|
|
|
|
|
$record->user_identity = $data->userid;
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$record->entered_at = $this->buildAttendanceTimestamp($date, $enterTime) ?? $record->entered_at;
|
2026-04-08 07:22:13 +00:00
|
|
|
|
|
2026-06-02 22:17:47 +03:30
|
|
|
|
if ($exitTime) {
|
|
|
|
|
|
$record->exited_at = $this->buildAttendanceTimestamp($date, $exitTime);
|
|
|
|
|
|
$duration = $attendance->duration($enterTime, $exitTime);
|
2026-04-08 07:22:13 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
$record->exited_at = null;
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$duration = null;
|
2026-04-08 07:22:13 +00:00
|
|
|
|
}
|
2026-06-02 22:17:47 +03:30
|
|
|
|
|
2026-04-18 06:17:01 +00:00
|
|
|
|
$break = AttendanceBreak::where('attendance_id', $data->id)->sum('duration');
|
2026-06-02 22:17:47 +03:30
|
|
|
|
if ($break && $duration) {
|
2026-04-18 06:17:01 +00:00
|
|
|
|
$breakSec = strtotime("1970-01-01 " . $break) - strtotime("1970-01-01 00:00:00");
|
|
|
|
|
|
$durationSec = strtotime("1970-01-01 " . $duration) - strtotime("1970-01-01 00:00:00");
|
|
|
|
|
|
$durationSec -= $breakSec;
|
2026-04-21 05:56:01 +00:00
|
|
|
|
$duration = gmdate("H:i:s", $durationSec);
|
2026-04-18 06:17:01 +00:00
|
|
|
|
}
|
2026-06-02 22:17:47 +03:30
|
|
|
|
|
2026-04-21 05:56:01 +00:00
|
|
|
|
$record->duration = $duration ?? $record->duration;
|
2026-04-08 07:22:13 +00:00
|
|
|
|
$record->requested = '';
|
|
|
|
|
|
$record->save();
|
|
|
|
|
|
|
2026-04-08 09:22:17 +00:00
|
|
|
|
Redis::connection()->del("att-tempreq:{$data->userid}:{$data->id}");
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$this->decrementUserRequestCount($data->userid);
|
2026-04-18 06:17:01 +00:00
|
|
|
|
|
2026-04-08 07:22:13 +00:00
|
|
|
|
tel_notif($record->user_identity, "تایید");
|
2026-04-18 06:17:01 +00:00
|
|
|
|
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
"text" => 'رکورد با موفقیت ذخیره شد.✔️'
|
|
|
|
|
|
]);
|
2026-04-08 07:22:13 +00:00
|
|
|
|
} elseif ($data->function == 'new') {
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$enteredAt = !empty($data->oldenter) ? Carbon::parse($data->oldenter) : null;
|
|
|
|
|
|
$exitedAt = !empty($data->oldexit) ? Carbon::parse($data->oldexit) : null;
|
|
|
|
|
|
|
|
|
|
|
|
if (!$enteredAt) {
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#ff8800ff',
|
|
|
|
|
|
'text' => 'زمان ورود در درخواست مشخص نیست✖️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$starttime = $enteredAt->format('H:i:s');
|
|
|
|
|
|
$endtime = $exitedAt?->format('H:i:s');
|
|
|
|
|
|
|
2026-04-08 07:22:13 +00:00
|
|
|
|
if (is_null($endtime) || $endtime > $starttime) {
|
|
|
|
|
|
$attendance = new Attendance();
|
|
|
|
|
|
$attendance->ip_address = 'admin-new';
|
|
|
|
|
|
$attendance->user_identity = $data->userid;
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$attendance->entered_at = $enteredAt->format('Y-m-d H:i:s');
|
|
|
|
|
|
$attendance->exited_at = $exitedAt?->format('Y-m-d H:i:s');
|
|
|
|
|
|
$attendance->duration = $endtime
|
|
|
|
|
|
? $attendance->duration($starttime, $endtime)
|
|
|
|
|
|
: null;
|
2026-04-08 07:22:13 +00:00
|
|
|
|
$attendance->save();
|
|
|
|
|
|
$create_requests_key = "att-tempreq:creates:{$data->userid}";
|
|
|
|
|
|
Redis::connection()->lrem($create_requests_key, 1, json_encode($data));
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$this->decrementUserRequestCount($data->userid);
|
2026-04-08 07:22:13 +00:00
|
|
|
|
|
|
|
|
|
|
tel_notif($data->userid, "تایید");
|
|
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
"text" => 'درخواست با موفقیت تایید شد.✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($request->update == 'reject') {
|
2026-04-18 06:17:01 +00:00
|
|
|
|
if ($data->function == 'delete' || $data->function == 'edit') {
|
2026-04-08 09:22:17 +00:00
|
|
|
|
Redis::connection()->del("att-tempreq:{$data->userid}:{$data->id}");
|
2026-04-18 06:17:01 +00:00
|
|
|
|
} elseif ($data->function == 'new') {
|
2026-04-08 09:22:17 +00:00
|
|
|
|
$create_requests_key = "att-tempreq:creates:{$data->userid}";
|
|
|
|
|
|
Redis::connection()->lrem($create_requests_key, 1, json_encode($data));
|
|
|
|
|
|
}
|
2026-04-08 07:22:13 +00:00
|
|
|
|
|
|
|
|
|
|
// dd($data->id);
|
|
|
|
|
|
if ($data->id !== "new") {
|
|
|
|
|
|
$att = Attendance::find($data->id);
|
|
|
|
|
|
// dd($att);
|
|
|
|
|
|
$att->requested = null;
|
|
|
|
|
|
$att->save();
|
|
|
|
|
|
}
|
|
|
|
|
|
tel_notif($data->userid, "رد");
|
2026-06-02 22:17:47 +03:30
|
|
|
|
$this->decrementUserRequestCount($data->userid);
|
2026-04-08 07:22:13 +00:00
|
|
|
|
return redirect()->back()->with('message', [
|
|
|
|
|
|
'color' => '#6aff00ff',
|
|
|
|
|
|
"text" => 'درخواست با موفقیت رد شد.✔️'
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-23 14:00:39 +03:30
|
|
|
|
// private function fa2en_numbers($input)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// if (is_string($input)) {
|
|
|
|
|
|
// $fa_numbers = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
|
|
|
|
|
// $en_numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
|
|
|
|
// return str_replace($fa_numbers, $en_numbers, $input);
|
|
|
|
|
|
// } elseif (is_array($input)) {
|
|
|
|
|
|
// return array_map([$this, __FUNCTION__], $input);
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// return $input;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
2026-04-08 07:22:13 +00:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function tel_notif($username, $status)
|
|
|
|
|
|
{
|
|
|
|
|
|
$message = "درخواست شما {$status} شد.";
|
|
|
|
|
|
$user = User::where('user_name', $username)->whereNotNull('chatid')->select('chatid', 'user_name')->first();
|
|
|
|
|
|
if ($user) {
|
2026-05-03 14:50:16 +00:00
|
|
|
|
try {
|
|
|
|
|
|
Http::withoutVerifying()->post('https://tapi.bale.ai/1482838710:sYbLzby0tuLRzHvP4d3LsbcNwg2sjSDLTAU/sendMessage', [
|
|
|
|
|
|
"chat_id" => $user->bale,
|
|
|
|
|
|
'text' => $message,
|
|
|
|
|
|
]);
|
|
|
|
|
|
} catch (\Exception $e) {}
|
2026-04-08 07:22:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|