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/admin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /polana/admin/tips.php
<?php
declare(strict_types=1);
require_once dirname(__DIR__).'/includes/functions.php';
$user=require_tips_access();
ensure_schedule_schema(); ensure_tips_schema();
$isManager=in_array((string)($user['role']??''),tips_manager_roles(),true);
$isMonthManager=in_array((string)($user['role']??''),tips_month_manager_roles(),true);
$isStaff=in_array((string)($user['role']??''),tip_staff_roles(),true);
if($isStaff){ redirect('/pracownik/napiwki.php'); }
$month=trim((string)($_GET['month']??$_POST['month']??date('Y-m')));
if(!preg_match('/^\d{4}-\d{2}$/',$month)) $month=date('Y-m');
$monthStart=$month.'-01'; $monthEnd=(new DateTimeImmutable($monthStart))->modify('last day of this month')->format('Y-m-d');
$split=tip_split_settings();

if($_SERVER['REQUEST_METHOD']==='POST') {
    verify_csrf();
    $action=(string)($_POST['action']??'');
    try {
        if($action==='acknowledge' && $isStaff) {
            $poolId=(int)($_POST['pool_id']??0);
            tip_pool_acknowledge_for_user($poolId,(int)$user['id'],(string)($_SERVER['REMOTE_ADDR']??''));
            flash('success','Potwierdzono odbiór napiwku.');
            redirect('/admin/tips.php?month='.rawurlencode($month));
        }
        if(in_array($action,['close_month','reopen_month'],true) && !$isMonthManager) { http_response_code(403); exit('Zamknięcie miesiąca jest dostępne dla managera lub administratora.'); }
        if(!$isManager) { http_response_code(403); exit('Brak dostępu do edycji napiwków.'); }

        if($action==='close_month') {
            $count=tip_month_close($month,(int)$user['id'],trim((string)($_POST['note']??'')));
            admin_log('tips_month_close','Zamknięto miesiąc '.$month);
            flash('success','Miesiąc zamknięty. Zamknięto otwartych dni: '.$count.'.');
            redirect('/admin/tips.php?month='.rawurlencode($month));
        }
        if($action==='reopen_month') {
            $reason=trim((string)($_POST['reason']??''));
            tip_month_reopen($month,(int)$user['id'],$reason);
            admin_log('tips_month_reopen','Otwarto miesiąc '.$month);
            flash('success','Miesiąc ponownie otwarty do korekt.');
            redirect('/admin/tips.php?month='.rawurlencode($month));
        }
        if($action==='save_pool') {
            $date=trim((string)($_POST['tip_date']??''));
            $shiftCode='all_day';
            $cash=tips_parse_amount((string)($_POST['cash_amount']??'0'));
            $card=tips_parse_amount((string)($_POST['card_amount']??'0'));
            $other=tips_parse_amount((string)($_POST['other_amount']??'0'));
            $note=trim((string)($_POST['note']??''));
            $correctionReason=trim((string)($_POST['correction_reason']??''));
            $selected=$_POST['person_selected']??[];
            if(!is_array($selected)) $selected=[];
            $ids=array_values(array_unique(array_filter(array_map('intval',$selected))));
            $pool=tip_pool_save($date,$cash,$card,$other,$note,$ids,(int)$user['id'],$correctionReason,$shiftCode,['type'=>'manual']);
            admin_log('tip_pool_save','Napiwki '.$date);
            flash('success','Rozliczenie dnia zapisane.');
            redirect('/admin/tips.php?month='.rawurlencode(substr($date,0,7)).'&edit='.(int)$pool['id']);
        }
        if(in_array($action,['settle','reopen','delete'],true)) {
            $poolId=(int)($_POST['pool_id']??0); $pool=tip_pool_by_id($poolId);
            if(!$pool) throw new RuntimeException('Nie znaleziono rozliczenia.');
            if($action==='settle') { tip_pool_set_status($poolId,'settled',(int)$user['id']); flash('success','Dzień został zamknięty.'); }
            elseif($action==='reopen') { if(tip_month_is_closed(substr((string)$pool['tip_date'],0,7))) throw new RuntimeException('Najpierw otwórz cały miesiąc.'); $reason=trim((string)($_POST['reason']??'')); tip_pool_set_status($poolId,'open',(int)$user['id'],$reason); flash('success','Dzień został ponownie otwarty.'); }
            else {
                if($pool['status']==='settled') throw new RuntimeException('Najpierw otwórz rozliczenie.');
                $reason=trim((string)($_POST['reason']??'')); if(mb_strlen($reason,'UTF-8')<3) throw new RuntimeException('Podaj powód usunięcia.');
                db()->prepare('DELETE FROM tip_pools WHERE id=?')->execute([$poolId]); flash('success','Rozliczenie usunięte.');
            }
            redirect('/admin/tips.php?month='.rawurlencode($month));
        }
        if(in_array($action,['mark_paid','mark_unpaid','mark_all_paid'],true)) {
            $poolId=(int)($_POST['pool_id']??0); $participantId=(int)($_POST['participant_id']??0); $note=trim((string)($_POST['note']??''));
            if($action==='mark_paid') { tip_pool_mark_paid($poolId,$participantId,(int)$user['id'],$note); flash('success','Wypłata potwierdzona.'); }
            elseif($action==='mark_unpaid') { tip_pool_mark_unpaid($poolId,$participantId,(int)$user['id'],$note); flash('success','Cofnięto potwierdzenie wypłaty.'); }
            else { $count=tip_pool_mark_all_paid($poolId,(int)$user['id'],$note); flash('success','Oznaczono wypłaty: '.$count.'.'); }
            redirect('/admin/tips.php?month='.rawurlencode($month).'&edit='.$poolId.'#wyplaty');
        }
    } catch(Throwable $e) { flash('error',$e->getMessage()); redirect('/admin/tips.php?month='.rawurlencode($month)); }
}

// PRACOWNIK: tylko własne rekordy. Nie ma endpointu ani przełącznika do cudzej zmiany.
if($isStaff) {
    $myRows=tip_user_month_rows((int)$user['id'],$month);
    $total=$open=$paid=$toPay=0.0;
    foreach($myRows as $r){$a=(float)$r['share_amount'];$total+=$a;if($r['status']==='open')$open+=$a;elseif($r['payout_status']==='paid')$paid+=$a;else$toPay+=$a;}
    render_admin_header('Moje napiwki'); ?>
    <div class="admin-title-row"><div><h1>Moje napiwki</h1></div><a class="btn btn-light" href="/pracownik/napiwki.php">Widok mobilny</a></div>
    <div class="grid-cards tip-stats"><div class="card"><h3>Ten miesiąc</h3><div class="stat tip-stat-money"><?= e(tip_money($total)) ?></div></div><div class="card"><h3>Wypłacone</h3><div class="stat tip-stat-money"><?= e(tip_money($paid)) ?></div></div><div class="card"><h3>Do wypłaty</h3><div class="stat tip-stat-money"><?= e(tip_money($toPay)) ?></div></div></div>
    <form class="form-card form-grid" method="get"><label>Miesiąc<input type="month" name="month" value="<?= e($month) ?>"></label><div class="full"><button class="btn btn-light">Pokaż</button></div></form>
    <div class="tips-mobile-own">
    <?php if(!$myRows): ?><div class="form-card muted">Brak wpisów w tym miesiącu.</div><?php endif; ?>
    <?php foreach($myRows as $r): ?><article class="tip-own-card"><div><strong><?= e((new DateTimeImmutable($r['tip_date']))->format('d.m.Y')) ?></strong><small>Cały dzień</small></div><b><?= e(tip_money((float)$r['share_amount'])) ?></b><span class="tip-payout-badge <?= $r['payout_status']==='paid'?'is-paid':($r['status']==='settled'?'is-pending':'is-waiting') ?>"><?= $r['payout_status']==='paid'?(!empty($r['acknowledged_at'])?'Odebrane':'Wypłacono'):($r['status']==='settled'?'Do wypłaty':'W trakcie') ?></span><?php if($r['payout_status']==='paid' && empty($r['acknowledged_at'])): ?><form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="acknowledge"><input type="hidden" name="pool_id" value="<?= (int)$r['pool_id'] ?>"><button class="btn btn-small btn-green">Potwierdzam odbiór</button></form><?php endif; ?></article><?php endforeach; ?>
    </div>
    <?php render_admin_footer(); exit;
}

$staff=tip_pool_staff(true); $waiters=array_values(array_filter($staff,fn($p)=>$p['role']==='waiter')); $kitchen=array_values(array_filter($staff,fn($p)=>$p['role']==='kitchen'));
$pools=tip_pool_month_rows($month); $personSummary=tip_person_month_summary($month); $monthClosed=tip_month_is_closed($month); $monthClosure=tip_month_closure($month);
$editId=(int)($_GET['edit']??0); $editing=$editId?tip_pool_by_id($editId):null; $editingPeople=$editing?tip_pool_people((int)$editing['id']):[];
$formDate=$editing?(string)$editing['tip_date']:trim((string)($_GET['date']??date('Y-m-d'))); if(!preg_match('/^\d{4}-\d{2}-\d{2}$/',$formDate))$formDate=date('Y-m-d');
$formShift='all_day';
$selectedIds=$editing?array_map('intval',array_column($editingPeople,'user_id')):schedule_user_ids_for_date($formDate);
$events=$editing?tip_pool_events((int)$editing['id']):[];
$monthTotal=$monthOpen=0.0; foreach($pools as $p){$v=tip_pool_total($p);$monthTotal+=$v;if($p['status']==='open')$monthOpen+=$v;}
render_admin_header('Napiwki'); ?>
<div class="admin-title-row"><div><h1>Rozliczenie napiwków</h1><p>Jedno rozliczenie na cały dzień.</p></div><div class="actions"><a class="btn btn-light" href="/admin/tips-photo.php">📷 Z aparatu</a><a class="btn btn-light" href="/admin/tips-import.php">Import Excel / CSV</a></div></div>
<?php if($monthClosed): ?>
<div class="flash flash-success"><strong>Miesiąc <?= e($month) ?> zamknięty.</strong></div>
<?php endif; ?>
<div class="grid-cards tip-stats"><div class="card"><h3>Miesiąc</h3><div class="stat tip-stat-money"><?= e(tip_money($monthTotal)) ?></div></div><div class="card"><h3>Otwarte</h3><div class="stat tip-stat-money"><?= e(tip_money($monthOpen)) ?></div></div><div class="card"><h3>Rozliczeń</h3><div class="stat"><?= count($pools) ?></div></div></div>
<?php if($isMonthManager): ?>
<details class="form-card tips-month-tools" style="margin-top:12px">
<summary><strong>Operacje miesięczne · <?= e($month) ?></strong></summary>
<div style="margin-top:14px">
<?php if($monthClosed): ?>
<form method="post" class="form-grid"><?= csrf_field() ?><input type="hidden" name="action" value="reopen_month"><label class="full">Powód ponownego otwarcia<input name="reason" required minlength="3"></label><div class="full"><button class="btn btn-light">Otwórz miesiąc do korekt</button></div></form>
<?php else: ?>
<form method="post" class="form-grid"><?= csrf_field() ?><input type="hidden" name="action" value="close_month"><label class="full">Notatka<input name="note" placeholder="opcjonalnie"></label><div class="full"><button class="btn btn-green" onclick="return confirm('Zamknąć wszystkie rozliczenia za <?= e($month) ?>?')">Zamknij miesiąc</button></div></form>
<?php endif; ?>
</div></details>
<?php endif; ?>
<div class="tip-layout tip-layout-wide">
<section class="form-card"><div class="admin-section-title"><h2><?= $editing?'Edytuj dzień':'Nowe rozliczenie dnia' ?></h2></div>
<form class="form-grid" method="post" id="tip-pool-form"><?= csrf_field() ?><input type="hidden" name="action" value="save_pool"><input type="hidden" name="month" value="<?= e($month) ?>">
<label>Data<input type="date" name="tip_date" value="<?= e($formDate) ?>" <?= $editing?'readonly':'' ?> required></label>
<label>Gotówka<input class="tip-pool-money" name="cash_amount" inputmode="decimal" value="<?= $editing?e(number_format((float)$editing['cash_amount'],2,',','')):'' ?>" placeholder="0,00"></label>
<label>Karta / terminal<input class="tip-pool-money" name="card_amount" inputmode="decimal" value="<?= $editing?e(number_format((float)$editing['card_amount'],2,',','')):'' ?>" placeholder="0,00"></label>
<label>Inne<input class="tip-pool-money" name="other_amount" inputmode="decimal" value="<?= $editing?e(number_format((float)$editing['other_amount'],2,',','')):'' ?>" placeholder="0,00"></label>
<label class="full">Notatka<textarea name="note" rows="2"><?= e($editing['note']??'') ?></textarea></label>
<?php if($editing): ?><label class="full">Powód korekty<input name="correction_reason" placeholder="np. korekta raportu terminala" required minlength="3"></label><?php endif; ?>
<div class="full tip-pool-total-box"><span>Pula dnia</span><strong id="tip-pool-total">0,00 zł</strong></div>
<div class="full tips-shift-staff-head"><div><h3>Obsada z grafiku</h3><small id="tips-schedule-note">Automatycznie podstawiamy osoby przypisane do tego dnia w grafiku.</small></div><button class="btn btn-small btn-light" type="button" id="reload-schedule">Pobierz z grafiku</button></div>
<div class="full tip-people-grid" id="tip-people-grid">
<?php foreach($staff as $person): $id=(int)$person['id']; $checked=in_array($id,$selectedIds,true); ?>
<label class="tip-person-card <?= $checked?'is-selected':'' ?>" data-role="<?= e($person['role']) ?>"><div class="tip-person-head"><input class="tip-person-check" type="checkbox" name="person_selected[]" value="<?= $id ?>" <?= $checked?'checked':'' ?>><div><strong><?= e($person['name']?:$person['username']) ?></strong><small><?= e(tip_role_label($person['role'])) ?></small></div></div><div class="tip-person-share"><span>Udział</span><strong data-share>0,00 zł</strong></div></label>
<?php endforeach; ?>
</div>
<div class="full actions"><?php if($monthClosed): ?><button class="btn btn-light" type="button" disabled>Miesiąc zamknięty</button><?php else: ?><button class="btn btn-primary" type="submit">Zapisz rozliczenie</button><?php endif; ?></div></form></section>
<section class="form-card"><h2>Podział</h2><div class="tip-split-big"><strong><?= e(number_format($split['waiter'],0)) ?>%</strong><span>Kelnerzy</span><strong><?= e(number_format($split['kitchen'],0)) ?>%</strong><span>Kuchnia</span></div><p class="muted">Split zmieniasz w Ustawieniach.</p><?php if($editing && $editing['source_type']==='photo'): ?><a class="btn btn-light" target="_blank" href="/admin/tips-photo-file.php?id=<?= (int)$editing['id'] ?>">Pokaż zdjęcie rozliczenia</a><?php endif; ?></section>
</div>
<section class="form-card" style="margin-top:20px"><div class="admin-section-title"><h2>Dni w <?= e($month) ?></h2></div><div class="table-wrap"><table class="admin-table"><thead><tr><th>Data</th><th>Pula</th><th>Osób</th><th>Status</th><th>Źródło</th><th></th></tr></thead><tbody>
<?php if(!$pools): ?><tr><td colspan="6" class="muted">Brak rozliczeń.</td></tr><?php endif; ?>
<?php foreach($pools as $p): ?><tr><td><?= e((new DateTimeImmutable($p['tip_date']))->format('d.m.Y')) ?></td><td><strong><?= e(tip_money(tip_pool_total($p))) ?></strong></td><td><?= (int)$p['people_count'] ?></td><td><span class="tip-status <?= $p['status']==='settled'?'is-settled':'is-open' ?>"><?= $p['status']==='settled'?'Zamknięta':'Otwarta' ?></span></td><td><?= e(match($p['source_type']){'photo'=>'Zdjęcie','import'=>'Import','kiosk'=>'Terminal',default=>'Ręcznie'}) ?></td><td><div class="tip-actions"><a class="btn btn-small btn-light" href="/admin/tips.php?month=<?= e($month) ?>&edit=<?= (int)$p['id'] ?>">Otwórz</a><?php if($p['status']==='open'): ?><form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="settle"><input type="hidden" name="pool_id" value="<?= (int)$p['id'] ?>"><input type="hidden" name="month" value="<?= e($month) ?>"><button class="btn btn-small btn-green">Zamknij</button></form><?php endif; ?></div></td></tr><?php endforeach; ?>
</tbody></table></div></section>

<?php if($editing): ?><section class="form-card" id="wyplaty" style="margin-top:20px"><div class="admin-section-title"><h2>Wypłaty · <?= e((new DateTimeImmutable($editing['tip_date']))->format('d.m.Y')) ?></h2></div>
<?php if($editing['status']!=='settled'): ?><div class="flash flash-error">Najpierw zamknij dzień.</div><?php else: ?><form method="post" class="tip-pay-all-form"><?= csrf_field() ?><input type="hidden" name="action" value="mark_all_paid"><input type="hidden" name="pool_id" value="<?= (int)$editing['id'] ?>"><input type="hidden" name="month" value="<?= e($month) ?>"><input name="note" placeholder="Notatka (opcjonalnie)"><button class="btn btn-green">Wypłacono wszystkim</button></form><?php endif; ?>
<div class="tip-payout-grid"><?php foreach($editingPeople as $person): ?><article class="tip-payout-card <?= $person['payout_status']==='paid'?'is-paid':'' ?>"><div><strong><?= e($person['name']?:$person['username']) ?></strong><small><?= e(tip_role_label($person['role'])) ?></small></div><div class="tip-payout-amount"><?= e(tip_money((float)$person['share_amount'])) ?></div><?php if($person['payout_status']==='paid'): ?><span class="tip-payout-badge is-paid"><?= !empty($person['acknowledged_at']) ? 'Odebrane przez pracownika' : 'Wypłacono · czeka na potwierdzenie' ?></span><form method="post" class="tip-inline-form"><?= csrf_field() ?><input type="hidden" name="action" value="mark_unpaid"><input type="hidden" name="pool_id" value="<?= (int)$editing['id'] ?>"><input type="hidden" name="participant_id" value="<?= (int)$person['id'] ?>"><input type="hidden" name="month" value="<?= e($month) ?>"><input name="note" required minlength="3" placeholder="Powód cofnięcia"><button class="btn btn-small btn-light">Cofnij</button></form><?php elseif($editing['status']==='settled'): ?><span class="tip-payout-badge is-pending">Do wypłaty</span><form method="post" class="tip-pay-person-form"><?= csrf_field() ?><input type="hidden" name="action" value="mark_paid"><input type="hidden" name="pool_id" value="<?= (int)$editing['id'] ?>"><input type="hidden" name="participant_id" value="<?= (int)$person['id'] ?>"><input type="hidden" name="month" value="<?= e($month) ?>"><input name="note" placeholder="gotówka / przelew"><button class="btn btn-small btn-green">Wypłacono</button></form><?php else: ?><span class="tip-payout-badge is-waiting">Dzień otwarty</span><?php endif; ?></article><?php endforeach; ?></div></section>
<section class="form-card" style="margin-top:20px"><div class="admin-section-title"><h2>Historia</h2></div><div class="tip-audit-list"><?php foreach($events as $event): ?><article><div><strong><?= e(match($event['event_type']){'pool_created'=>'Utworzono','pool_corrected'=>'Korekta','pool_settled'=>'Zamknięto dzień','pool_reopened'=>'Otwarto ponownie','payout_paid'=>'Wypłata','payout_reopened'=>'Cofnięto wypłatę','payout_all_paid'=>'Wypłacono wszystkim','payout_acknowledged'=>'Pracownik potwierdził odbiór','month_closed'=>'Zamknięto miesiąc',default=>$event['event_type']}) ?></strong><small><?= e($event['name']?:$event['username']?:'system') ?></small></div><time><?= e((new DateTimeImmutable($event['created_at']))->format('d.m.Y H:i')) ?></time><?php if($event['event_note']): ?><p><?= e($event['event_note']) ?></p><?php endif; ?></article><?php endforeach; ?></div></section><?php endif; ?>

<script>
(()=>{const form=document.getElementById('tip-pool-form');if(!form)return;const money=[...form.querySelectorAll('.tip-pool-money')],cards=[...form.querySelectorAll('.tip-person-card')],totalEl=document.getElementById('tip-pool-total'),date=form.querySelector('[name="tip_date"]');const wp=<?= json_encode((float)$split['waiter']) ?>,kp=<?= json_encode((float)$split['kitchen']) ?>;const num=v=>{const n=parseFloat(String(v||'').replace(/\s+/g,'').replace(',','.'));return Number.isFinite(n)?Math.max(0,n):0};const fmt=v=>new Intl.NumberFormat('pl-PL',{style:'currency',currency:'PLN'}).format(v);function recalc(){const total=money.reduce((s,i)=>s+num(i.value),0);totalEl.textContent=fmt(total);const groups={waiter:cards.filter(c=>c.dataset.role==='waiter'&&c.querySelector('input').checked),kitchen:cards.filter(c=>c.dataset.role==='kitchen'&&c.querySelector('input').checked)};cards.forEach(c=>{c.classList.toggle('is-selected',c.querySelector('input').checked);c.querySelector('[data-share]').textContent=fmt(0)});for(const [role,list] of Object.entries(groups)){if(!list.length)continue;const group=total*((role==='waiter'?wp:kp)/100);const cents=Math.round(group*100),base=Math.floor(cents/list.length);let rem=cents-base*list.length;list.forEach(c=>{const own=base+(rem-->0?1:0);c.querySelector('[data-share]').textContent=fmt(own/100)})}}async function loadSchedule(){if(!date?.value)return;try{const r=await fetch('/admin/schedule-day.php?date='+encodeURIComponent(date.value),{credentials:'same-origin',cache:'no-store'});const d=await r.json();if(!d.ok)return;const ids=new Set((d.user_ids||[]).map(String));cards.forEach(c=>c.querySelector('input').checked=ids.has(c.querySelector('input').value));document.getElementById('tips-schedule-note').textContent=ids.size?'Załadowano z grafiku: '+ids.size+' osób.':'Brak obsady tego dnia w grafiku.';recalc()}catch(e){}}form.addEventListener('input',recalc);form.addEventListener('change',recalc);date?.addEventListener('change',loadSchedule);document.getElementById('reload-schedule')?.addEventListener('click',loadSchedule);recalc()})();
</script>
<?php render_admin_footer(); ?>

Youez - 2016 - github.com/yon3zu
LinuXploit