403Webshell
Server IP : 188.114.96.3  /  Your IP : 216.73.216.143
Web Server : Apache
System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64
User :  ( 3194769)
PHP Version : 8.5.12
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : OFF
Directory :  /polana/includes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /polana/includes/schedule.php
<?php
declare(strict_types=1);

function ensure_work_shifts_schema(): void
{
    db()->exec(
        "CREATE TABLE IF NOT EXISTS work_shifts (
            id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            code VARCHAR(32) NOT NULL,
            name VARCHAR(80) NOT NULL,
            start_time TIME DEFAULT NULL,
            end_time TIME DEFAULT NULL,
            active TINYINT(1) NOT NULL DEFAULT 1,
            sort_order INT NOT NULL DEFAULT 0,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            UNIQUE KEY uq_work_shift_code (code),
            INDEX idx_work_shift_active_sort (active, sort_order)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci"
    );

    db()->exec("UPDATE work_shifts SET active=0 WHERE code<>'all_day'");
    $stmt = db()->prepare(
        "INSERT INTO work_shifts (code,name,start_time,end_time,active,sort_order)
         VALUES ('all_day','Cały dzień',NULL,NULL,1,10)
         ON DUPLICATE KEY UPDATE name='Cały dzień',start_time=NULL,end_time=NULL,active=1,sort_order=10"
    );
    $stmt->execute();
}

function work_shifts(bool $activeOnly = true): array
{
    ensure_work_shifts_schema();
    $stmt = db()->query("SELECT * FROM work_shifts WHERE code='all_day' LIMIT 1");
    $row = $stmt->fetch();
    return $row ? [$row] : [];
}

function work_shift_by_code(string $code): ?array
{
    ensure_work_shifts_schema();
    if (trim($code) !== 'all_day') return null;
    $stmt = db()->query("SELECT * FROM work_shifts WHERE code='all_day' LIMIT 1");
    return $stmt->fetch() ?: null;
}

function work_shift_default_code(): string { return 'all_day'; }
function work_shift_label(string $code): string { return 'Cały dzień'; }
function work_shift_resolve(string $value, ?string $fallback = null): string { return 'all_day'; }
function work_shift_slug(string $value): string { return 'all_day'; }

function work_shift_save(?int $id, string $name, ?string $start, ?string $end, bool $active, int $sortOrder): array
{
    ensure_work_shifts_schema();
    return work_shift_by_code('all_day') ?: [];
}

function ensure_schedule_schema(): void
{
    ensure_work_shifts_schema();

    db()->exec(
        "CREATE TABLE IF NOT EXISTS staff_schedule (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            work_date DATE NOT NULL,
            user_id INT UNSIGNED NOT NULL,
            shift_code VARCHAR(32) NOT NULL DEFAULT 'all_day',
            start_time TIME DEFAULT NULL,
            end_time TIME DEFAULT NULL,
            created_by_user_id INT UNSIGNED DEFAULT NULL,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            UNIQUE KEY uq_staff_schedule_day_user (work_date, user_id),
            INDEX idx_staff_schedule_date (work_date),
            INDEX idx_staff_schedule_date_shift (work_date, shift_code),
            INDEX idx_staff_schedule_user (user_id, work_date),
            CONSTRAINT fk_staff_schedule_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
            CONSTRAINT fk_staff_schedule_created_by FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci"
    );

    $columns = db()->query('SHOW COLUMNS FROM staff_schedule')->fetchAll();
    $names = array_map(static fn(array $row): string => (string)$row['Field'], $columns);

    if (!in_array('shift_code',$names,true)) db()->exec("ALTER TABLE staff_schedule ADD COLUMN shift_code VARCHAR(32) NOT NULL DEFAULT 'all_day' AFTER user_id");
    if (!in_array('start_time',$names,true)) db()->exec('ALTER TABLE staff_schedule ADD COLUMN start_time TIME DEFAULT NULL AFTER shift_code');
    if (!in_array('end_time',$names,true)) db()->exec('ALTER TABLE staff_schedule ADD COLUMN end_time TIME DEFAULT NULL AFTER start_time');
    if (!in_array('updated_at',$names,true)) db()->exec('ALTER TABLE staff_schedule ADD COLUMN updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at');

    $indexes = db()->query('SHOW INDEX FROM staff_schedule')->fetchAll();
    $indexNames = array_values(array_unique(array_column($indexes,'Key_name')));
    if (!in_array('idx_staff_schedule_date_shift',$indexNames,true)) db()->exec('ALTER TABLE staff_schedule ADD INDEX idx_staff_schedule_date_shift (work_date, shift_code)');
}

function schedule_manager_roles(): array { return ['admin','editor','manager']; }
function schedule_staff_roles(): array { return ['waiter','kitchen']; }
function schedule_is_manager(?array $user = null): bool
{
    $user ??= admin_user();
    return $user && in_array((string)$user['role'],schedule_manager_roles(),true);
}
function require_schedule_access(): array
{
    require_login();
    $user=admin_user();
    if(!$user || !in_array((string)$user['role'],array_merge(schedule_manager_roles(),schedule_staff_roles()),true)) {
        http_response_code(403); exit('Brak dostępu do grafiku.');
    }
    return $user;
}
function schedule_staff(bool $activeOnly = true): array
{
    ensure_schedule_schema();
    $sql="SELECT id,username,name,role,active FROM users WHERE role IN ('waiter','kitchen')";
    if($activeOnly) $sql.=' AND active=1';
    $sql.=" ORDER BY FIELD(role,'waiter','kitchen'),COALESCE(name,username),username";
    return db()->query($sql)->fetchAll();
}
function schedule_normalize_time(?string $value): ?string
{
    $value=trim((string)$value);
    if($value==='') return null;
    if(!preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/',$value)) throw new RuntimeException('Nieprawidłowa godzina: '.$value);
    return $value.':00';
}
function schedule_time_label(?string $start, ?string $end): string
{
    $start=$start?substr($start,0,5):''; $end=$end?substr($end,0,5):'';
    if($start!==''&&$end!=='') return $start.'–'.$end;
    if($start!=='') return 'od '.$start;
    if($end!=='') return 'do '.$end;
    return 'cały dzień';
}
function schedule_user_ids_for_date(string $date): array
{
    ensure_schedule_schema();
    $stmt=db()->prepare("SELECT s.user_id FROM staff_schedule s JOIN users u ON u.id=s.user_id WHERE s.work_date=? AND u.active=1 ORDER BY FIELD(u.role,'waiter','kitchen'),COALESCE(u.name,u.username),u.username");
    $stmt->execute([$date]);
    return array_map('intval',array_column($stmt->fetchAll(),'user_id'));
}
function schedule_user_ids_for_date_shift(string $date, string $shiftCode): array
{
    return schedule_user_ids_for_date($date);
}
function schedule_entries_for_date(string $date, ?int $onlyUserId = null, ?string $shiftCode = null): array
{
    ensure_schedule_schema();
    $params=[$date]; $where='s.work_date=? AND u.active=1';
    if($onlyUserId!==null){$where.=' AND s.user_id=?';$params[]=$onlyUserId;}
    $stmt=db()->prepare("SELECT s.user_id,'all_day' AS shift_code,s.start_time,s.end_time,u.username,u.name,u.role,'Cały dzień' AS shift_name FROM staff_schedule s JOIN users u ON u.id=s.user_id WHERE $where ORDER BY FIELD(u.role,'waiter','kitchen'),COALESCE(u.name,u.username),u.username");
    $stmt->execute($params); return $stmt->fetchAll();
}
function schedule_people_for_date(string $date): array { return schedule_entries_for_date($date); }
function schedule_month_map(string $month, ?int $onlyUserId = null): array
{
    ensure_schedule_schema();
    $from=$month.'-01'; $to=(new DateTimeImmutable($from))->modify('last day of this month')->format('Y-m-d');
    $params=[$from,$to]; $where='s.work_date BETWEEN ? AND ?';
    if($onlyUserId!==null){$where.=' AND s.user_id=?';$params[]=$onlyUserId;}
    $stmt=db()->prepare("SELECT s.work_date,s.user_id,'all_day' AS shift_code,s.start_time,s.end_time,u.username,u.name,u.role,'Cały dzień' AS shift_name,10 AS shift_sort FROM staff_schedule s JOIN users u ON u.id=s.user_id WHERE $where ORDER BY s.work_date,FIELD(u.role,'waiter','kitchen'),COALESCE(u.name,u.username),u.username");
    $stmt->execute($params); $map=[]; foreach($stmt->fetchAll() as $row)$map[$row['work_date']][]=$row; return $map;
}
function schedule_save_day(string $date, array $entries, int $actorUserId): void
{
    ensure_schedule_schema();
    $dateObj=DateTimeImmutable::createFromFormat('!Y-m-d',$date);
    if(!$dateObj||$dateObj->format('Y-m-d')!==$date) throw new RuntimeException('Nieprawidłowa data.');
    $normalized=[];
    foreach($entries as $entry){
        if(!is_array($entry))continue; $userId=(int)($entry['user_id']??0); if($userId<1)continue;
        $normalized[$userId]=['user_id'=>$userId,'shift_code'=>'all_day','start_time'=>schedule_normalize_time($entry['start_time']??null),'end_time'=>schedule_normalize_time($entry['end_time']??null)];
    }
    if($normalized){
        $ids=array_keys($normalized);$ph=implode(',',array_fill(0,count($ids),'?'));
        $stmt=db()->prepare("SELECT id FROM users WHERE active=1 AND role IN ('waiter','kitchen') AND id IN ($ph)");$stmt->execute($ids);$valid=array_map('intval',array_column($stmt->fetchAll(),'id'));
        foreach(array_keys($normalized) as $id)if(!in_array((int)$id,$valid,true))unset($normalized[$id]);
    }
    $pdo=db();$pdo->beginTransaction();
    try{
        $pdo->prepare('DELETE FROM staff_schedule WHERE work_date=?')->execute([$date]);
        if($normalized){
            $insert=$pdo->prepare('INSERT INTO staff_schedule (work_date,user_id,shift_code,start_time,end_time,created_by_user_id) VALUES (?,?,?,?,?,?)');
            foreach($normalized as $e)$insert->execute([$date,$e['user_id'],'all_day',$e['start_time'],$e['end_time'],$actorUserId]);
        }
        $pdo->commit();
    }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
}
function schedule_copy_day(string $sourceDate,string $targetDate,int $actorUserId): void
{
    $entries=[];foreach(schedule_entries_for_date($sourceDate) as $row)$entries[]=['user_id'=>(int)$row['user_id'],'start_time'=>$row['start_time']?substr((string)$row['start_time'],0,5):'','end_time'=>$row['end_time']?substr((string)$row['end_time'],0,5):''];
    schedule_save_day($targetDate,$entries,$actorUserId);
}
function schedule_copy_previous_week(string $dateInTargetWeek,int $actorUserId): void
{
    $target=new DateTimeImmutable($dateInTargetWeek);$targetMonday=$target->modify('monday this week');$sourceMonday=$targetMonday->modify('-7 days');
    for($i=0;$i<7;$i++)schedule_copy_day($sourceMonday->modify('+'.$i.' days')->format('Y-m-d'),$targetMonday->modify('+'.$i.' days')->format('Y-m-d'),$actorUserId);
}
function schedule_upcoming_for_user(int $userId,int $limit=8): array
{
    ensure_schedule_schema();$limit=max(1,min(31,$limit));$stmt=db()->prepare("SELECT work_date FROM staff_schedule WHERE user_id=? AND work_date>=CURDATE() ORDER BY work_date ASC LIMIT $limit");$stmt->execute([$userId]);return array_column($stmt->fetchAll(),'work_date');
}
function schedule_upcoming_shifts_for_user(int $userId,int $limit=14): array
{
    ensure_schedule_schema();$limit=max(1,min(62,$limit));$stmt=db()->prepare("SELECT work_date,'all_day' AS shift_code,start_time,end_time,'Cały dzień' AS shift_name FROM staff_schedule WHERE user_id=? AND work_date>=CURDATE() ORDER BY work_date ASC LIMIT $limit");$stmt->execute([$userId]);return $stmt->fetchAll();
}


function schedule_copy_week_to_next(string $dateInSourceWeek, int $actorUserId): int
{
    ensure_schedule_schema();
    $date = new DateTimeImmutable($dateInSourceWeek);
    $monday = $date->modify('monday this week');
    $copied = 0;
    for ($i=0; $i<7; $i++) {
        $source = $monday->modify('+' . $i . ' days')->format('Y-m-d');
        $target = $monday->modify('+' . ($i+7) . ' days')->format('Y-m-d');
        schedule_copy_day($source, $target, $actorUserId);
        $copied++;
    }
    return $copied;
}

function schedule_minimums(): array
{
    return [
        'waiter' => max(0, min(20, (int)setting('schedule_min_waiters','1'))),
        'kitchen' => max(0, min(20, (int)setting('schedule_min_kitchen','1'))),
    ];
}

function schedule_reservation_counts_for_month(string $month): array
{
    ensure_reservations_schema();
    $from = $month . '-01';
    $to = (new DateTimeImmutable($from))->modify('last day of this month')->format('Y-m-d');
    $stmt = db()->prepare(
        "SELECT reservation_date,COUNT(*) AS cnt
         FROM reservations
         WHERE reservation_date BETWEEN ? AND ? AND status NOT IN ('rejected','cancelled')
         GROUP BY reservation_date"
    );
    $stmt->execute([$from,$to]);
    $out=[];
    foreach($stmt->fetchAll() as $row) $out[(string)$row['reservation_date']] = (int)$row['cnt'];
    return $out;
}

function schedule_staffing_status(array $people, int $reservationCount = 0): array
{
    $min = schedule_minimums();
    $waiters = count(array_filter($people, static fn(array $p): bool => ($p['role'] ?? '') === 'waiter'));
    $kitchen = count(array_filter($people, static fn(array $p): bool => ($p['role'] ?? '') === 'kitchen'));
    $missing=[];
    if ($waiters < $min['waiter']) $missing[]='kelnerzy';
    if ($kitchen < $min['kitchen']) $missing[]='kuchnia';
    if (!$people && $reservationCount < 1) return ['level'=>'none','waiters'=>$waiters,'kitchen'=>$kitchen,'missing'=>[]];
    if (!$people && $reservationCount > 0) return ['level'=>'danger','waiters'=>0,'kitchen'=>0,'missing'=>['brak obsady']];
    return ['level'=>$missing ? 'warning' : 'ok','waiters'=>$waiters,'kitchen'=>$kitchen,'missing'=>$missing];
}

Youez - 2016 - github.com/yon3zu
LinuXploit