Add shift planning, month closure + payroll, and a dark modern redesign
Ports two modules from the friend's Google Apps Script build (kept as reference in gscript/) onto the TypeScript stack, rewritten cleanly against this app's own data model rather than copied 1:1: - Shift planning: weekly template (Sun-Thu evening, Fri/Sat two slots), lazy idempotent generation per period (no cron needed), employee signup/cancel with collision + capacity checks, admin calendar view with slot editing and manual assignment. - Month closure + payroll: employee confirms the month (blocked while a shift is still open), admin locks and finalizes pay (base hours * rate + tips/bonus/other), reopen to undo a premature lock, mark paid. Pay rates are versioned by date, defaulting the first-ever rate to apply retroactively to the employee's whole history. - A shift left open more than 12h (forgotten clock-out) is auto-closed at clock_in + 12h, checked lazily on read instead of a background job. - Full dark, sharp-edged modern restyle (theme.css replaces tui.css) with an amber accent, keeping every existing class name so no component logic needed to change. Backend test coverage (jest) for all three workflows: shift planning, closure/payroll, and the forgotten-clock-out auto-close.
This commit is contained in:
33
gscript/Automation.js
Normal file
33
gscript/Automation.js
Normal file
@@ -0,0 +1,33 @@
|
||||
function nightlyMaintenance() {
|
||||
const cutoff = Date.now() - 14*3600000;
|
||||
rows_(CFG.SHEETS.SHIFTS)
|
||||
.filter(s => String(s.status)==='OPEN' && new Date(s.clock_in).getTime() < cutoff)
|
||||
.forEach(s => {
|
||||
append_(CFG.SHEETS.NOTIFICATIONS,{
|
||||
notification_id:uuid_('NOT'),
|
||||
user_id:'',
|
||||
type:'OPEN_SHIFT_WARNING',
|
||||
title:'Pravděpodobně chybí odchod',
|
||||
message:'Směna '+s.shift_id+' je otevřená déle než 14 hodin.',
|
||||
url:'',
|
||||
read_at:'',
|
||||
created_at:now_(),
|
||||
email_sent_at:''
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function monthlyMaintenance() {
|
||||
const d = new Date();
|
||||
d.setMonth(d.getMonth()-1);
|
||||
const p = Utilities.formatDate(d,CFG.TZ,'yyyy-MM');
|
||||
rows_(CFG.SHEETS.EMPLOYEES)
|
||||
.filter(e => e.active === true || String(e.active).toUpperCase()==='TRUE')
|
||||
.forEach(e => {
|
||||
const exists = findOne_(CFG.SHEETS.MONTH_CLOSURES,r=>String(r.employee_id)===String(e.employee_id)&&String(r.period)===p);
|
||||
if (!exists) append_(CFG.SHEETS.MONTH_CLOSURES,{
|
||||
closure_id:uuid_('CLOSE'),employee_id:e.employee_id,period:p,
|
||||
employee_confirmed_at:'',manager_approved_at:'',manager_approved_by:'',locked_at:'',status:'WAITING_EMPLOYEE'
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user