| 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 : |
<?php
declare(strict_types=1);
function tips_import_normalize_header(string $value): string
{
$value = mb_strtolower(trim($value),'UTF-8');
$value = str_replace(['ą','ć','ę','ł','ń','ó','ś','ź','ż'],['a','c','e','l','n','o','s','z','z'],$value);
return preg_replace('/[^a-z0-9]+/','',$value) ?? '';
}
function tips_import_date(string $value): ?string
{
$value = trim($value);
if ($value === '') return null;
if (is_numeric($value)) {
$serial=(int)floor((float)$value);
if ($serial > 20000 && $serial < 100000) {
return (new DateTimeImmutable('1899-12-30'))->modify('+' . $serial . ' days')->format('Y-m-d');
}
}
foreach (['Y-m-d','d.m.Y','d/m/Y','d-m-Y'] as $format) {
$dt=DateTimeImmutable::createFromFormat('!'.$format,$value);
if ($dt && $dt->format($format)===$value) return $dt->format('Y-m-d');
}
return null;
}
function tips_import_rows_from_matrix(array $matrix, string $fallbackShift): array
{
if (!$matrix) return [];
$header=array_shift($matrix);
$map=[];
foreach ($header as $i=>$name) {
$key=tips_import_normalize_header((string)$name);
$map[$key]=(int)$i;
}
$find = static function(array $keys) use ($map): ?int {
foreach ($keys as $key) if (array_key_exists($key,$map)) return $map[$key];
return null;
};
$idx=[
'date'=>$find(['data','date']),
'cash'=>$find(['gotowka','cash']),
'card'=>$find(['karta','terminal','kartaterminal','card']),
'other'=>$find(['inne','other']),
'note'=>$find(['notatka','uwagi','note']),
];
if ($idx['date']===null) throw new RuntimeException('Brakuje kolumny Data.');
if ($idx['cash']===null && $idx['card']===null && $idx['other']===null) {
throw new RuntimeException('Brakuje kolumn z kwotami: Gotówka / Karta / Inne.');
}
$out=[];
foreach ($matrix as $line=>$row) {
$row=array_values($row);
$rawDate=(string)($row[$idx['date']]??'');
if (trim(implode('',array_map('strval',$row)))==='') continue;
$date=tips_import_date($rawDate);
$shift='all_day';
$cash=tips_parse_amount($idx['cash']!==null ? (string)($row[$idx['cash']]??'0') : '0');
$card=tips_parse_amount($idx['card']!==null ? (string)($row[$idx['card']]??'0') : '0');
$other=tips_parse_amount($idx['other']!==null ? (string)($row[$idx['other']]??'0') : '0');
$note=$idx['note']!==null ? trim((string)($row[$idx['note']]??'')) : '';
$errors=[];
if (!$date) $errors[]='Nieprawidłowa data';
if (($cash+$card+$other)<=0) $errors[]='Brak kwoty';
$staffIds=$date ? schedule_user_ids_for_date($date) : [];
if ($date && !$staffIds) $errors[]='Brak obsady tego dnia w grafiku';
$out[]=[
'row'=>$line+2,'date'=>$date ?: $rawDate,'shift_code'=>'all_day','shift_name'=>'Cały dzień',
'cash'=>$cash,'card'=>$card,'other'=>$other,'note'=>$note,'staff_count'=>count($staffIds),'errors'=>$errors,
];
}
return $out;
}
function tips_import_csv_matrix(string $path): array
{
$raw=file_get_contents($path);
if ($raw===false) throw new RuntimeException('Nie udało się odczytać CSV.');
$raw=preg_replace('/^\\xEF\\xBB\\xBF/','',$raw) ?? $raw;
$first=strtok($raw,"\r\n") ?: '';
$delims=[';'=>substr_count($first,';'),','=>substr_count($first,','),"\t"=>substr_count($first,"\t")];
arsort($delims); $delimiter=(string)array_key_first($delims);
$fh=fopen($path,'rb');
if (!$fh) throw new RuntimeException('Nie udało się otworzyć CSV.');
$matrix=[];
while (($row=fgetcsv($fh,0,$delimiter))!==false) {
if (isset($row[0])) $row[0]=preg_replace('/^\\xEF\\xBB\\xBF/','',(string)$row[0]) ?? (string)$row[0];
$matrix[]=$row;
}
fclose($fh);
return $matrix;
}
function tips_import_xlsx_matrix(string $path): array
{
if (!class_exists('ZipArchive')) {
throw new RuntimeException('Na serwerze brak obsługi ZIP potrzebnej do XLSX. Zapisz arkusz jako CSV i wgraj ponownie.');
}
if (!class_exists('DOMDocument')) {
throw new RuntimeException('Na serwerze brak obsługi XML potrzebnej do XLSX. Zapisz arkusz jako CSV i wgraj ponownie.');
}
$zip = new ZipArchive();
if ($zip->open($path) !== true) {
throw new RuntimeException('Nie udało się otworzyć pliku XLSX.');
}
$shared = [];
$sharedXml = $zip->getFromName('xl/sharedStrings.xml');
if ($sharedXml !== false && trim($sharedXml) !== '') {
$dom = new DOMDocument();
$previous = libxml_use_internal_errors(true);
if ($dom->loadXML($sharedXml)) {
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('x', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
foreach ($xpath->query('//x:si') as $si) {
$parts = [];
foreach ($xpath->query('.//x:t', $si) as $t) {
$parts[] = $t->textContent;
}
$shared[] = implode('', $parts);
}
}
libxml_clear_errors();
libxml_use_internal_errors($previous);
}
$sheetXml = $zip->getFromName('xl/worksheets/sheet1.xml');
$zip->close();
if ($sheetXml === false || trim($sheetXml) === '') {
throw new RuntimeException('Nie znaleziono pierwszego arkusza w XLSX.');
}
$dom = new DOMDocument();
$previous = libxml_use_internal_errors(true);
if (!$dom->loadXML($sheetXml)) {
libxml_clear_errors();
libxml_use_internal_errors($previous);
throw new RuntimeException('Nie udało się odczytać arkusza XLSX.');
}
libxml_clear_errors();
libxml_use_internal_errors($previous);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('x', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
$matrix = [];
foreach ($xpath->query('//x:sheetData/x:row') as $row) {
$cells = [];
foreach ($xpath->query('./x:c', $row) as $cell) {
$ref = (string)$cell->getAttribute('r');
preg_match('/^[A-Z]+/', $ref, $m);
$letters = $m[0] ?? 'A';
$col = 0;
for ($i = 0; $i < strlen($letters); $i++) {
$col = $col * 26 + (ord($letters[$i]) - 64);
}
$col--;
$type = (string)$cell->getAttribute('t');
$value = '';
if ($type === 'inlineStr') {
$parts = [];
foreach ($xpath->query('.//x:is//x:t', $cell) as $t) {
$parts[] = $t->textContent;
}
$value = implode('', $parts);
} else {
$v = $xpath->query('./x:v', $cell)->item(0);
$raw = $v ? $v->textContent : '';
$value = $type === 's' ? ($shared[(int)$raw] ?? '') : $raw;
}
$cells[$col] = $value;
}
if ($cells) {
$max = max(array_keys($cells));
$line = [];
for ($i = 0; $i <= $max; $i++) {
$line[] = $cells[$i] ?? '';
}
$matrix[] = $line;
}
}
return $matrix;
}
function tips_import_parse(string $path, string $originalName, string $fallbackShift): array
{
$ext=strtolower(pathinfo($originalName,PATHINFO_EXTENSION));
$matrix=match($ext) {
'csv','txt' => tips_import_csv_matrix($path),
'xlsx' => tips_import_xlsx_matrix($path),
default => throw new RuntimeException('Obsługiwane pliki: XLSX i CSV.'),
};
return tips_import_rows_from_matrix($matrix,$fallbackShift);
}