Files
crmPRO/autoload/Domain/Users/VacationRepository.php
Jacek Pyziak a4a35c8d62 feat: Implement module permissions system with database-driven access control
- Added `users_permissions` table for managing user permissions.
- Created `PermissionRepository` for handling permission logic.
- Refactored `controls\Users::permissions()` to utilize the new database structure.
- Introduced AJAX endpoint for saving user permissions.
- Enhanced user management UI with permission checkboxes.
- Added vacation management template for handling employee absences.
- Implemented tests for `PermissionRepository`.
2026-02-26 20:17:03 +01:00

353 lines
9.0 KiB
PHP

<?php
namespace Domain\Users;
class VacationRepository
{
private $mdb;
const TYPE_VACATION = 'urlop_wypoczynkowy';
const TYPE_SICK = 'chorobowe';
const TYPE_OTHER = 'inna_nieobecnosc';
const TYPES = [
self::TYPE_VACATION => 'Urlop wypoczynkowy',
self::TYPE_SICK => 'Chorobowe',
self::TYPE_OTHER => 'Inna nieobecność'
];
const DEFAULT_DAYS_LIMIT = 26;
private static bool $tables_checked = false;
public function __construct( $mdb )
{
$this -> mdb = $mdb;
if ( !self::$tables_checked )
{
$this -> ensureTables();
self::$tables_checked = true;
}
}
private function ensureTables(): void
{
$pdo = $this -> mdb -> pdo;
$pdo -> exec( "
CREATE TABLE IF NOT EXISTS `users_vacations` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`user_id` INT UNSIGNED NOT NULL,
`date_from` DATE NOT NULL,
`date_to` DATE NOT NULL,
`type` VARCHAR(30) NOT NULL DEFAULT 'urlop_wypoczynkowy',
`comment` TEXT,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_user_year` (`user_id`, `date_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
" );
$pdo -> exec( "
CREATE TABLE IF NOT EXISTS `users_vacation_limits` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`user_id` INT UNSIGNED NOT NULL,
`year` SMALLINT UNSIGNED NOT NULL,
`days_limit` TINYINT UNSIGNED NOT NULL DEFAULT 26,
UNIQUE KEY `uq_user_year` (`user_id`, `year`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
" );
}
public function allByYear( int $year, ?int $user_id = null ): array
{
$where = [
'AND' => [
'date_from[>=]' => $year . '-01-01',
'date_from[<=]' => $year . '-12-31'
],
'ORDER' => [ 'date_from' => 'DESC' ]
];
if ( $user_id )
$where['AND']['user_id'] = $user_id;
return $this -> mdb -> select( 'users_vacations', [
'[>]users' => [ 'user_id' => 'id' ]
], [
'users_vacations.id',
'users_vacations.user_id',
'users_vacations.date_from',
'users_vacations.date_to',
'users_vacations.type',
'users_vacations.comment',
'users.name',
'users.surname'
], $where );
}
public function add( int $user_id, string $date_from, string $date_to, string $type, string $comment = '' )
{
if ( !isset( self::TYPES[ $type ] ) )
return false;
if ( $date_from > $date_to )
return false;
$this -> mdb -> insert( 'users_vacations', [
'user_id' => $user_id,
'date_from' => $date_from,
'date_to' => $date_to,
'type' => $type,
'comment' => $comment
] );
return $this -> mdb -> id();
}
public function getById( int $id ): ?array
{
$row = $this -> mdb -> get( 'users_vacations', '*', [ 'id' => $id ] );
return $row ?: null;
}
public function update( int $id, int $user_id, string $date_from, string $date_to, string $type, string $comment = '' ): bool
{
if ( !isset( self::TYPES[ $type ] ) )
return false;
if ( $date_from > $date_to )
return false;
$this -> mdb -> update( 'users_vacations', [
'user_id' => $user_id,
'date_from' => $date_from,
'date_to' => $date_to,
'type' => $type,
'comment' => $comment
], [ 'id' => $id ] );
return true;
}
public function delete( int $id ): bool
{
$this -> mdb -> delete( 'users_vacations', [ 'id' => $id ] );
return true;
}
public function getLimit( int $user_id, int $year ): int
{
$row = $this -> mdb -> get( 'users_vacation_limits', 'days_limit', [
'user_id' => $user_id,
'year' => $year
] );
return $row !== null ? (int) $row : self::DEFAULT_DAYS_LIMIT;
}
public function setLimit( int $user_id, int $year, int $days_limit ): bool
{
$exists = $this -> mdb -> has( 'users_vacation_limits', [
'user_id' => $user_id,
'year' => $year
] );
if ( $exists )
{
$this -> mdb -> update( 'users_vacation_limits', [
'days_limit' => $days_limit
], [
'user_id' => $user_id,
'year' => $year
] );
}
else
{
$this -> mdb -> insert( 'users_vacation_limits', [
'user_id' => $user_id,
'year' => $year,
'days_limit' => $days_limit
] );
}
return true;
}
public function getLimitsForYear( int $year ): array
{
$rows = $this -> mdb -> select( 'users_vacation_limits', '*', [
'year' => $year
] );
$limits = [];
foreach ( $rows as $row )
$limits[ (int) $row['user_id'] ] = (int) $row['days_limit'];
return $limits;
}
public static function countBusinessDays( string $date_from, string $date_to ): int
{
$start = new \DateTime( $date_from );
$end = new \DateTime( $date_to );
$years = range( (int) $start -> format( 'Y' ), (int) $end -> format( 'Y' ) );
$holidays = self::getPolishHolidays( $years );
$days = 0;
while ( $start <= $end )
{
$dow = (int) $start -> format( 'N' );
if ( $dow <= 5 && !isset( $holidays[ $start -> format( 'Y-m-d' ) ] ) )
$days++;
$start -> modify( '+1 day' );
}
return $days;
}
private static function getPolishHolidays( array $years ): array
{
$holidays = [];
foreach ( $years as $y )
{
// Stałe święta ustawowe
$fixed = [
"$y-01-01", // Nowy Rok
"$y-01-06", // Trzech Króli
"$y-05-01", // Święto Pracy
"$y-05-03", // Święto Konstytucji
"$y-08-15", // Wniebowzięcie NMP
"$y-11-01", // Wszystkich Świętych
"$y-11-11", // Święto Niepodległości
"$y-12-25", // Boże Narodzenie
"$y-12-26", // Drugi dzień Bożego Narodzenia
];
foreach ( $fixed as $d )
$holidays[ $d ] = true;
// Wielkanoc i święta ruchome
$easter = new \DateTime( date( 'Y-m-d', easter_date( $y ) ) );
$easter_monday = ( clone $easter ) -> modify( '+1 day' );
$corpus_christi = ( clone $easter ) -> modify( '+60 days' );
$holidays[ $easter -> format( 'Y-m-d' ) ] = true;
$holidays[ $easter_monday -> format( 'Y-m-d' ) ] = true;
$holidays[ $corpus_christi -> format( 'Y-m-d' ) ] = true;
}
return $holidays;
}
public function usedDaysByYear( int $year, ?string $type = null ): array
{
$where = [
'AND' => [
'date_from[>=]' => $year . '-01-01',
'date_from[<=]' => $year . '-12-31'
]
];
if ( $type )
$where['AND']['type'] = $type;
$rows = $this -> mdb -> select( 'users_vacations', [ 'user_id', 'date_from', 'date_to' ], $where );
$totals = [];
foreach ( $rows as $row )
{
$uid = (int) $row['user_id'];
if ( !isset( $totals[ $uid ] ) )
$totals[ $uid ] = 0;
$totals[ $uid ] += self::countBusinessDays( $row['date_from'], $row['date_to'] );
}
return $totals;
}
public function summaryByYear( int $year, array $users ): array
{
$limits = $this -> getLimitsForYear( $year );
$used_map = $this -> usedDaysByYear( $year, self::TYPE_VACATION );
$summary = [];
foreach ( $users as $u )
{
$uid = (int) $u['id'];
$limit = $limits[ $uid ] ?? self::DEFAULT_DAYS_LIMIT;
$used = $used_map[ $uid ] ?? 0;
$summary[] = [
'user_id' => $uid,
'name' => $u['name'] . ' ' . $u['surname'],
'limit' => $limit,
'used' => $used,
'remaining' => $limit - $used
];
}
return $summary;
}
public function carryoverByYear( int $year, array $users ): array
{
// Znajdź najwcześniejszy rok z danymi (urlopy lub limity)
$min_vacation_year = $this -> mdb -> get( 'users_vacations', 'date_from', [
'ORDER' => [ 'date_from' => 'ASC' ],
'LIMIT' => 1
] );
$min_limit_year = $this -> mdb -> get( 'users_vacation_limits', 'year', [
'ORDER' => [ 'year' => 'ASC' ],
'LIMIT' => 1
] );
$start_year = $year - 1; // domyślnie sprawdzaj przynajmniej rok wstecz
if ( $min_vacation_year )
$start_year = min( $start_year, (int) substr( $min_vacation_year, 0, 4 ) );
if ( $min_limit_year )
$start_year = min( $start_year, (int) $min_limit_year );
if ( $start_year >= $year )
return [];
$carryover = [];
foreach ( $users as $u )
{
$uid = (int) $u['id'];
$total_remaining = 0;
$year_details = [];
for ( $y = $start_year; $y < $year; $y++ )
{
$limit = $this -> getLimit( $uid, $y );
$used_map = $this -> usedDaysByYear( $y, self::TYPE_VACATION );
$used = $used_map[ $uid ] ?? 0;
$remaining = $limit - $used;
if ( $remaining > 0 )
{
$total_remaining += $remaining;
$year_details[] = [
'year' => $y,
'remaining' => $remaining
];
}
}
if ( $total_remaining > 0 )
{
$carryover[] = [
'user_id' => $uid,
'name' => $u['name'] . ' ' . $u['surname'],
'total' => $total_remaining,
'years' => $year_details
];
}
}
return $carryover;
}
}