Satura rādītājs
Darbo grafikas gyvena Google skaičiuoklėje. Komanda gyvena Google kalendoriuje. Kažkas jau dvejus metus kiekvieną penktadienį perrašo vieną į kitą, ir tą vieną savaitę, kai jis buvo atostogose, dvi pamainos liko nepadengtos. Mūsų r/GoogleAppsScript analizėje tai vienas iš dviejų dažniausiai užduodamų automatizavimo klausimų (imtyje apie 250 diskusijų), ir maždaug trečdalis šių diskusijų taip ir nesulaukė veikiančio atsakymo. Priežastis ne ta, kad tai sudėtinga. Priežastis ta, kad nepavyksta keturi atskiri dalykai, o kiekviena instrukcija aprašo tik vieną iš jų.
Toliau: skriptas, kurį galima saugiai paleisti pakartotinai, laiko juostų problema, dėl kurios įvykiai atsiduria dieną per anksti, kaip atsiranda dublikatai ir kaip daugumos jų išvengti, ir kada to apskritai nereikėtų daryti skriptu. Patikrinta pagal Google kalendoriaus ir Apps Script dokumentaciją 2026 m. rugsėjo 9 d. Išbandykite tai lentelės kopijoje ir testiniame kalendoriuje, prieš nukreipdami į tikrus.
Ką reiškia „teisingai”
Skriptas iš lentelės į kalendorių naudingas tik tada, jei jį galima paleisti dar kartą. Tai reiškia, kad jis negali sukurti to paties įvykio du kartus, turi atnaujinti įvykį, kai eilutė pasikeičia, negali tyliai pastumti laikų ir turi sustoti su aiškia klaida, o ne pusiaukelėje. Dauguma iš forumų nukopijuotų skriptų neatitinka visų keturių kriterijų. Veikiantis sprendimas paprastas: vienas lentelės stulpelis saugo kalendoriaus įvykio ID, kuris įrašomas įvykio sukūrimo momentu, o ne pabaigoje. Nėra ID – sukuria; yra ID – atnaujina. Užraktas neleidžia dviem vykdymams persidengti, o būsenos stulpelis apdoroja atšaukimus.
Lentelės išdėstymas
| A: Pavadinimas | B: Pradžia | C: Pabaiga | D: Aprašymas | E: Įvykio ID | F: Būsena |
|---|---|---|---|---|---|
| Rytinė pamaina, Anna | 2026-09-14 08:00 | 2026-09-14 16:00 | Registratūra | užpildo skriptas | |
| Pristatymas, klientas X | 2026-09-15 10:30 | 2026-09-15 11:30 | Užsakymas 4471 | užpildo skriptas | Cancelled |
Pradžia ir Pabaiga turi būti tikros datos ir laiko reikšmės, o ne tekstas, panašus į datą. Stulpelio formatavimas kaip „Data ir laikas” esamo teksto nepaverčia, todėl patikrinkite langelį su =ISDATE(B2): jis turi grąžinti TRUE. Pavyzdyje naudojama metai-mėnuo-diena tvarka, kokia yra Lietuvos lokalėje (daugumoje kitų Europos lokalių – diena.mėnuo.metai); jei jūsų skaičiuoklės lokalė yra JAV, rašykite 09/14/2026 08:00. Toliau pateiktas skriptas praleidžia eilutes, kurių datos nėra tikros datos, ir apie jas praneša, o ne tyliai sužlunga.
Skriptas
Atidarykite Extensions, Apps Script. Jei projekte jau yra kodo, jo neperrašykite: pridėkite naują skripto failą (pliuso ženklas prie Files) ir įklijuokite ten toliau pateiktą kodą. Užpildykite tris konstantas viršuje: skaičiuoklės ID (iš lentelės URL), lapo (skirtuko) pavadinimą ir komandos kalendoriaus ID (Calendar settings, Integrate calendar). Skriptas savo lentelę ir kalendorių nurodo tiesiogiai, o ne „tą, kuri aktyvi”, nes planuotam vykdymui aktyvios lentelės nėra.
const SPREADSHEET_ID = 'ikelkite-id-is-lenteles-url';
const SHEET_NAME = 'Roster';
const CALENDAR_ID = 'team@yourdomain.com';
function syncSheetToCalendar() {
const lock = LockService.getScriptLock();
if (!lock.tryLock(30000)) { Logger.log('Kitas vykdymas dar tesiasi; praleidziame.'); return; }
try {
const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
const cal = CalendarApp.getCalendarById(CALENDAR_ID);
if (!sheet) throw new Error('Lapas "' + SHEET_NAME + '" nerastas');
if (!cal) throw new Error('Kalendorius nerastas arba nera prieigos: ' + CALENDAR_ID);
const lastRow = sheet.getLastRow();
if (lastRow < 2) return; // tik antraste
const rows = sheet.getRange(2, 1, lastRow - 1, 6).getValues(); // A:F, vienas nuskaitymas
const problems = [];
rows.forEach((row, i) => {
const r = i + 2; // eilutes numeris lenteleje
const [title, start, end, desc, id, status] = row;
if (String(status).trim().toLowerCase() === 'cancelled') {
if (id) {
try { const ev = cal.getEventById(id); if (ev) ev.deleteEvent(); }
catch (e) { problems.push('Eilute ' + r + ': nepavyko istrinti: ' + e.message); return; }
sheet.getRange(r, 5).setValue(''); // isvalo ID, eilute lieka atsaukta
}
return;
}
if (!title) return; // tuscia eilute
if (!(start instanceof Date) || !(end instanceof Date) || end <= start) {
problems.push('Eilute ' + r + ': pradzia/pabaiga nera tinkamas datu intervalas'); return;
}
try {
let ev = id ? cal.getEventById(id) : null;
if (ev) {
ev.setTitle(title); ev.setTime(start, end); ev.setDescription(desc || '');
} else {
ev = cal.createEvent(title, start, end, { description: desc || '' });
sheet.getRange(r, 5).setValue(ev.getId()); // iraso ID nedelsiant
}
} catch (e) {
problems.push('Eilute ' + r + ': ' + e.message);
}
});
if (problems.length) {
Logger.log(problems.join('\n'));
// Pasirinktinai: MailApp.sendEmail('it@yourdomain.com', 'Grafiko sinchronizavimo problemos', problems.join('\n'));
}
} finally {
lock.releaseLock();
}
}
Pirmiausia paleiskite jį vieną kartą iš redaktoriaus (paleidimo mygtukas prie syncSheetToCalendar) lentelės kopijoje ir testiniame kalendoriuje. Google paprašys suteikti prieigą prie skaičiuoklių ir kalendoriaus. Kadangi paleidžiate skriptą, kurį ką tik patys įklijavote į savo projektą, šis autorizavimas skirtas jūsų pačių kodui; vis dėlto perskaitykite prašomų leidimų sąrašą, o jei kada nors pamatysite įspėjimą „unverified app” skriptui, kurio nerašėte, sustokite ir išsiaiškinkite, kam jis priklauso. Patikrinkite kalendorių, tada patikrinkite, ar E stulpelis užsipildė ID. Paleiskite dar kartą: niekas neturi dubliuotis. Tada išbandykite klaidos kelią: įrašykite Pradžios langelyje tekstą ir įsitikinkite, kad vykdymas baigiasi, įrašo problemos eilutę į žurnalą ir kitų įvykių neliečia.
Kodėl ID įrašomas kiekvienai eilutei atskirai
Daug forumų skriptų surenka ID į masyvą ir įrašo juos visus pabaigoje. Jei vykdymas nutrūksta pusiaukelėje (kvota, leidimų klaida, 6 minučių limitas), iki tol sukurti įvykiai kalendoriuje egzistuoja, bet jų ID į lentelę nepatenka, ir kitas vykdymas sukuria juos dar kartą. Kiekvieno ID įrašymas tuoj pat po createEvent kainuoja vieną papildomą iškvietimą kiekvienai naujai eilutei ir uždaro didžiąją šio lango dalį. Tai nepaverčia skaičiuoklių ir kalendoriaus viena transakcija; klaida tarp createEvent ir setValue vis dar galima, tik reta. Jei dublikatas kada nors vis tiek atsiranda, ID stulpelis pasako, kurį įvykį lentelė pažįsta, o kitas yra našlaitis.
Laiko juostų klaida: įvykiai pasislinkę per dieną ar valandą
Tai ta dalis, kuri žmonėms kainuoja visą popietę. Dalyvauja trys laiko juostos nustatymai, ir jei nežinote, ką kiekvienas daro, jie nesutaps:
- Skaičiuoklės laiko juosta. File, Settings, Time zone. Langeliuose įrašytos datos interpretuojamos čia.
- Skripto projekto laiko juosta. Apps Script redaktoriuje Project Settings, pažymėkite Show appsscript.json; laukas
timeZone. Naujiems projektams numatytoji reikšmė kartais būnaAmerica/New_York, nepriklausomai nuo to, kur esate. - Kalendoriaus laiko juosta. Konkretaus kalendoriaus nustatymuose.
Datos ir laiko langelis skaičiuoklėje saugomas kaip momentas laike, interpretuojamas skaičiuoklės juostoje. „2026-09-14 08:00” Europe/Vilnius lentelėje yra 05:00 UTC. Kalendorius saugo tą patį momentą ir rodo jį kiekvieno žiūrovo juostoje, todėl kolega Niujorke teisingai mato 01:00; tai ne klaida. Klaidos atsiranda, kai skriptas tekstą paverčia datomis naudodamas skripto juostą, kai reikšmė be laiko (vidurnaktis lentelės juostoje) sukuriama kaip įvykis su laiku ir kitoje juostoje rodoma kaip ankstesnis vakaras, arba kai paties kalendoriaus juosta skiriasi ir visos dienos įvykiai pasislenka per dieną. Praktiškai: nustatykite skaičiuoklei ir skripto projektui įmonės namų juostą, laikykite lentelėje datas kaip tikras datos reikšmes ir, prieš pasitikėdami, išbandykite vieną įvykį abipus vasaros/žiemos laiko ribos.
Visos dienos įvykiams (data be laiko) naudokite cal.createAllDayEvent(title, date), o ne createEvent. Jis ima reikšmės kalendorinę datą, todėl įsitikinkite, kad lentelės juostoje reikšmė iš tikrųjų yra ši data. Vėlesniam visos dienos įvykio atnaujinimui reikia setAllDayDate, o ne setTime; aukščiau pateiktas skriptas apdoroja tik įvykius su laiku.
Automatinis paleidimas
Redaktoriuje spustelėkite Triggers (laikrodžio piktograma), Add trigger, pasirinkite syncSheetToCalendar, įvykio šaltinis Time-driven, pavyzdžiui, kas valandą arba dienos langas, tarkime, nuo 6:00 iki 7:00 (Google planuoja valandos ribose, o ne minutės tikslumu). Nenaudokite tam paprasto onEdit aktyviklio (trigger): paprasti aktyvikliai negali pasiekti kalendoriaus, nes veikia be autorizacijos. Įdiegiamas „on edit” aktyviklis veikia, bet pagal laiką planuojamas sinchronizavimas yra ramesnis ir lengviau derinamas, o skripto užraktas persidengiančius vykdymus padaro nekenksmingus.
Dvi tvarkos taisyklės. Kurkite aktyviklį iš paskyros, kuri egzistuos ir kitais metais, idealiu atveju iš atskiro IT valdomo automatizavimo naudotojo, nes įdiegiami aktyvikliai sustoja, kai jų savininko paskyra sustabdoma, ir niekas kitas jų nemato. Ir nustatykite aktyviklio klaidų pranešimus į „immediately” adresu, kurį žmonės skaito, kad apie sugedusį sinchronizavimą sužinotumėte anksčiau nei komanda.
Įvykių šalinimas ir perkėlimas
Jei eilutė iš lentelės tiesiog ištrinama, įvykis kalendoriuje lieka ir niekas apie jį nebežino, todėl eilučių netrinkite: vietoj to F stulpelyje nustatykite Cancelled. Skriptas tada įvykį ištrina, išvalo ID ir toliau praleidžia šią eilutę kituose vykdymuose. Norėdami perkelti įvykį į kitą kalendorių, pažymėkite jį kaip atšauktą, leiskite vykdymui jį pašalinti, tada grąžinkite eilutę atgal ir nukreipkite skriptą į naują kalendorių (arba išplėskite skriptą kalendoriaus stulpeliu, jei sinchronizuojate kelis).
Kada to nereikėtų daryti skriptu
- Klientų rezervacijos. Naudokite Google kalendoriaus integruotus rezervacijų puslapius; klientas pasirenka laiką, ir lentelės nereikia.
- Pamainų planavimas, kur žmonės keičiasi, prašo ir tvirtina. Grafikų įrankiai su pamainų mainais, patvirtinimais ir pranešimais egzistuoja ne veltui. Lentelė plius skriptas tampa tuo, ką visi kaltina.
- Bet kas, kur kalendorius yra tiesos šaltinis. Aukščiau pateiktas skriptas yra vienpusis. Jei žmonės redaguoja įvykius kalendoriuje ir tikisi, kad lentelė seks, reikia ir atvirkštinio sinchronizavimo, ir dabar turite sinchronizavimo problemą, o tai jau tikra problema.
Dažniausiai užduodami klausimai
Kodėl mano Google kalendoriaus įvykiai iš Google skaičiuoklių sukuriami diena per anksti?
Paprastai reikšmė be laiko (vidurnaktis skaičiuoklės juostoje) sukurta kaip įvykis su laiku ir rodoma kitoje juostoje, arba tekstinės datos išanalizuotos skripto projekto juostoje. Nustatykite skaičiuoklei ir skriptui įmonės juostą, laikykite lentelėje tikras datos reikšmes, o eilutėms be laiko naudokite createAllDayEvent.
Kaip pasiekti, kad skriptas kiekvieno vykdymo metu nekurtų dubliuotų įvykių?
Saugokite įvykio ID stulpelyje tuoj pat po kiekvieno įvykio sukūrimo, kituose vykdymuose atnaujinkite esamus įvykius, o ne kurkite naujus, ir naudokite užraktą, kad du vykdymai negalėtų persidengti. Aukščiau pateiktas skriptas daro visus tris dalykus. Jis negali padaryti dublikatų neįmanomų kiekvienos klaidos atveju, bet padaro juos retus ir lengvai atpažįstamus.
Ar skriptas gali pasileisti automatiškai, kai lentelė pasikeičia?
Taip, su įdiegiamu „on edit” aktyvikliu, bet pagal laiką planuojamas aktyviklis (kas valandą ar kasdien) yra nuspėjamesnis. Paprasti onEdit aktyvikliai kalendoriaus pasiekti negali.
Susiję straipsniai
- Jūsų Google skaičiuoklių automatizavimas nustojo veikti: 6 minučių limitas, leidimų klaidos ir kada atsisakyti Apps Script
- Google kalendoriaus rezervacijų puslapiai: leiskite klientams patiems pasirinkti laiką
- Valdomos IT paslaugos įmonėms, naudojančioms Google Workspace
Jei norėtumėte, kad tai nustatytų ir prižiūrėtų kas nors kitas, paprastas vieno kalendoriaus sinchronizavimas, kaip aprašyta aukščiau, mums yra trumpas darbas, o jo priežiūra yra valdomų IT paslaugų dalis.
Automatizavimas sugedo, ir niekas už jį neatsako? Mes įvertiname darbo eigą, jos aktyviklius ir vykdymo paskyras, tada nustatome remonto apimtį ir priežiūros planą su konkrečiu atsakingu asmeniu. Užsiregistruokite nemokamam 30 minučių auditui arba skambinkite +371 22 30 50 90.


