function setupSystem() { const props = PropertiesService.getScriptProperties(); let id = props.getProperty('DB_SPREADSHEET_ID'); let ss; if (id) { ss = SpreadsheetApp.openById(id); } else { ss = SpreadsheetApp.create('MeStaff Database'); props.setProperty('DB_SPREADSHEET_ID', ss.getId()); } Object.keys(CFG.SHEETS).forEach(k => { const name = CFG.SHEETS[k]; let s = ss.getSheetByName(name); if (!s) s = ss.insertSheet(name); const headers = HEADERS[k]; if (s.getLastRow() === 0) s.getRange(1,1,1,headers.length).setValues([headers]); s.setFrozenRows(1); s.getRange(1,1,1,headers.length).setFontWeight('bold'); }); const defaultSheet = ss.getSheetByName('Sheet1') || ss.getSheetByName('List1'); if (defaultSheet && Object.values(CFG.SHEETS).indexOf(defaultSheet.getName()) === -1) { ss.deleteSheet(defaultSheet); } if (!props.getProperty('PASSWORD_PEPPER')) { props.setProperty('PASSWORD_PEPPER', Utilities.getUuid() + Utilities.getUuid()); } seedSettings_(); return {ok:true, spreadsheetId:ss.getId(), url:ss.getUrl()}; } function seedSettings_() { const current = rows_(CFG.SHEETS.SETTINGS); const map = {}; current.forEach(r => map[r.key] = r.value); const defaults = { APP_NAME: 'MeStaff', CURRENCY: 'CZK', TIMEZONE: CFG.TZ, NIGHT_START: '22:00', NIGHT_END: '06:00' }; Object.keys(defaults).forEach(k => { if (map[k] === undefined) append_(CFG.SHEETS.SETTINGS, {key:k, value:defaults[k]}); }); } function seedFirstAdmin(email, firstName, lastName) { email = emailNorm_(email); if (!email) throw new Error('Zadej e-mail.'); const lock = LockService.getScriptLock(); lock.waitLock(10000); try { let company = findOne_(CFG.SHEETS.COMPANIES, r => r.active === true || String(r.active).toUpperCase() === 'TRUE'); if (!company) { company = {company_id:'COMPANY_MAIN', name:'Hlavní společnost', ico:'', active:true}; append_(CFG.SHEETS.COMPANIES, company); } let loc = findOne_(CFG.SHEETS.LOCATIONS, r => String(r.company_id) === String(company.company_id)); if (!loc) { loc = {location_id:'LOCATION_MAIN', company_id:company.company_id, name:'Hlavní provozovna', active:true}; append_(CFG.SHEETS.LOCATIONS, loc); } let emp = findOne_(CFG.SHEETS.EMPLOYEES, r => emailNorm_(r.email) === email); if (!emp) { emp = { employee_id: uuid_('EMP'), first_name:firstName || 'Admin', last_name:lastName || '', email, company_id:company.company_id, location_id:loc.location_id, position:'Administrátor', employment_type:'', terminal_pin:'', start_date:isoDate_(now_()), end_date:'', active:true }; append_(CFG.SHEETS.EMPLOYEES, emp); } let user = findOne_(CFG.SHEETS.USERS, r => emailNorm_(r.email) === email); if (!user) { user = { user_id:uuid_('USR'), email, role:'ADMIN', employee_id:emp.employee_id, password_salt:'', password_hash:'', active:true, created_at:now_(), last_login:'' }; append_(CFG.SHEETS.USERS, user); } return {ok:true, email, message:'Admin založen. Teď použij „Aktivovat účet“ na přihlašovací stránce.'}; } finally { lock.releaseLock(); } } function createSystemTriggers() { ScriptApp.getProjectTriggers().forEach(t => { if (['nightlyMaintenance','monthlyMaintenance'].includes(t.getHandlerFunction())) { ScriptApp.deleteTrigger(t); } }); ScriptApp.newTrigger('nightlyMaintenance') .timeBased().everyDays(1).atHour(3).inTimezone(CFG.TZ).create(); ScriptApp.newTrigger('monthlyMaintenance') .timeBased().onMonthDay(1).atHour(4).inTimezone(CFG.TZ).create(); return {ok:true}; } function createMyAdmin() { return seedFirstAdmin( 'filip.thurrigl@gmail.com', 'Philipp', 'Thürrigl' ); }