function createEmployee(token, data) { const admin = requireUser_(token,['ADMIN','MANAGER']); const email = emailNorm_(data.email); if (!email) throw new Error('E-mail je povinný.'); if (findOne_(CFG.SHEETS.EMPLOYEES, r => emailNorm_(r.email) === email)) { throw new Error('Zaměstnanec s tímto e-mailem už existuje.'); } const role = String(data.role || 'EMPLOYEE'); if (!['EMPLOYEE','MANAGER','ACCOUNTANT','ADMIN'].includes(role)) { throw new Error('Neplatná role.'); } const hourlyRate = data.hourly_rate === '' || data.hourly_rate === undefined ? '' : Number(data.hourly_rate); if (hourlyRate !== '' && (isNaN(hourlyRate) || hourlyRate < 0)) { throw new Error('Hodinová sazba není platná.'); } const pin = String(data.terminal_pin || '').trim(); if (pin && !/^\d{4,6}$/.test(pin)) { throw new Error('PIN musí mít 4 až 6 číslic.'); } const emp = { employee_id: uuid_('EMP'), first_name: String(data.first_name || '').trim(), last_name: String(data.last_name || '').trim(), email: email, phone: String(data.phone || '').trim(), company_id: String(data.company_id || 'COMPANY_MAIN'), location_id: String(data.location_id || 'LOCATION_MAIN'), position: String(data.position || '').trim(), employment_type: String(data.employment_type || '').trim(), terminal_pin: pin, start_date: data.start_date || isoDate_(now_()), end_date: '', active: true }; if (!emp.first_name || !emp.last_name) { throw new Error('Jméno a příjmení jsou povinné.'); } append_(CFG.SHEETS.EMPLOYEES, emp); const user = { user_id: uuid_('USR'), email: email, role: role, employee_id: emp.employee_id, password_salt: '', password_hash: '', active: true, created_at: now_(), last_login: '' }; append_(CFG.SHEETS.USERS, user); append_(CFG.SHEETS.USER_COMPANIES, { user_id: user.user_id, company_id: emp.company_id }); if (hourlyRate !== '') { append_(CFG.SHEETS.PAY_RATES, { pay_rate_id: uuid_('RATE'), employee_id: emp.employee_id, valid_from: emp.start_date, valid_to: '', hourly_rate: hourlyRate, created_by: admin.user_id, created_at: now_() }); } audit_(admin.user_id,'EMPLOYEE_CREATED','EMPLOYEE',emp.employee_id,'',emp); requestActivation(email); return { ok: true, employee_id: emp.employee_id }; } function listEmployees(token) { requireUser_( token, ['ADMIN','MANAGER','ACCOUNTANT'] ); const employees = rows_(CFG.SHEETS.EMPLOYEES); const users = rows_(CFG.SHEETS.USERS); const rates = rows_(CFG.SHEETS.PAY_RATES); return employees.map(function(employee) { const user = users.find(function(u) { return String(u.employee_id) === String(employee.employee_id); }); const employeeRates = rates.filter(function(rate) { return String(rate.employee_id) === String(employee.employee_id); }); employeeRates.sort(function(a,b) { const dateA = a.valid_from ? new Date(a.valid_from).getTime() : 0; const dateB = b.valid_from ? new Date(b.valid_from).getTime() : 0; return dateB - dateA; }); let hourlyRate = 0; if (employeeRates.length > 0) { hourlyRate = Number( employeeRates[0].hourly_rate || 0 ); } const active = employee.active === true || String(employee.active) .toUpperCase() === 'TRUE'; return { employee_id: String(employee.employee_id || ''), first_name: String(employee.first_name || ''), last_name: String(employee.last_name || ''), name: ( String(employee.first_name || '') + ' ' + String(employee.last_name || '') ).trim(), email: String(employee.email || ''), phone: String(employee.phone || ''), company_id: String(employee.company_id || ''), location_id: String(employee.location_id || ''), position: String(employee.position || ''), employment_type: String(employee.employment_type || ''), terminal_pin: String(employee.terminal_pin || ''), start_date: employee.start_date ? String(employee.start_date) : '', end_date: employee.end_date ? String(employee.end_date) : '', active: active, role: user ? String(user.role || 'EMPLOYEE') : 'EMPLOYEE', hourly_rate: hourlyRate }; }); } function setEmployeeActive(token, employeeId, active) { const admin = requireUser_(token,['ADMIN']); const emp = findOne_( CFG.SHEETS.EMPLOYEES, r => String(r.employee_id) === String(employeeId) ); if (!emp) throw new Error('Zaměstnanec nebyl nalezen.'); const user = findOne_( CFG.SHEETS.USERS, r => String(r.employee_id) === String(employeeId) ); updateBy_( CFG.SHEETS.EMPLOYEES, 'employee_id', employeeId, { active: !!active, end_date: active ? '' : isoDate_(now_()) } ); if (user) { updateBy_( CFG.SHEETS.USERS, 'user_id', user.user_id, { active: !!active } ); } audit_( admin.user_id, 'EMPLOYEE_ACTIVE_CHANGED', 'EMPLOYEE', employeeId, emp.active, !!active ); return { ok: true }; } function getCompaniesAndLocations(token) { requireUser_(token,['ADMIN','MANAGER']); return { companies: rows_(CFG.SHEETS.COMPANIES) .filter(x => x.active === true || String(x.active).toUpperCase() === 'TRUE' ), locations: rows_(CFG.SHEETS.LOCATIONS) .filter(x => x.active === true || String(x.active).toUpperCase() === 'TRUE' ) }; }