Chrono je višestanarska aplikacija za praćenje vremena i upravljanje odsustvima — Laravel API iza Vue single-page aplikacije, gdje timovi bilježe sate po aktivnostima, traže i odobravaju odsustva te prepuštaju sustavu računanje datuma. Teški dijelovi nisu obrasci, nego pravila ispod njih: brojanje stvarnih radnih dana, održavanje svakog stanja odsustava usklađivim iz predznačenih transakcija te zaključavanje dovršenih razdoblja evidencije kako bi prošlost ostala prošlost.
Chrono je krenuo iz jednostavne frustracije: alati za evidenciju i odsustva ili pohranjuju stanja kao promjenjive brojače koji tiho odlaze u krivu vrijednost, ili tretiraju “pet dana slobodno” kao pet kalendarskih dana bez obzira na vikende i praznike. Stoga je projektna odluka ispod cijele aplikacije ta da se zanimljivi brojevi nikad ne pohranjuju — oni se računaju. Stanje odsustava je dodjela uvećana za predznačeni zbroj knjige transakcija. Trajanje odsustva je prolazak kroz raspon datuma koji preskače vikende i pravi skup neradnih dana. Oboje se može u svakom trenutku ponovno izvesti iz izvora, što ih čini revidiranima umjesto tajanstvenima.
Iznad te domenske jezgre sjedi prilično običan, namjerno dosadan oblik: Laravel 13 API osiguran Sanctumom i Vue 3 TypeScript SPA koji s njim komunicira preko TanStack Queryja, zapakiran kao instalabilna PWA. Višestanarska je od prve migracije — organizacije ograničavaju sve, uloge štite pristup, a ljudi se uvode kroz tokenizirane pozivnice s istekom. Doista mišljenima dijelovi — izračun radnih dana, mehanizam stanja i prijenosa, zaključavanje razdoblja te idempotentna predaja zasebnom servisu za tiketiranje — izolirani su u tankom sloju servisa, pa kontroleri ostaju tanki, a pravila ostaju ispitiva.
Živa okosnica — unos vremena, tijek odsustava, stanja i kalendar radnih dana — izgrađena je i radi s kraja na kraj. Sloj zaključavanja razdoblja modeliran je i migriran, uz provedbu koja se još uvijek povezuje u put pisanja.
Svaka organizacija bira kako njezini ljudi evidentiraju vrijeme — rasponi početka/kraja ili obična trajanja — a obrazac za unos i validacija prilagođavaju se tom načinu. Batch endpoint prihvaća cijeli tjedan unosa u jednom zahtjevu, pa klijent može spremiti mrežu u jednom prolazu.
Članovi prijavljuju odsustvo po vrsti odsustva za raspon datuma; administratori odobravaju ili odbijaju iz reda na razini cijele organizacije. Pri odobrenju sustav razvija raspon u stvarne radne dane i bilježi oduzimanje, pa se zahtjev i stanje nikad ne razilaze.
Korisnikovo stanje za vrstu odsustva u godini jest godišnja dodjela uvećana za predznačeni zbroj svake transakcije odsustva — pripisi pozitivni, korištenje negativno. Ništa se ne mijenja na mjestu, pa se svako stanje može ponovno izvesti i revidirati iz povijesti transakcija.
Kalkulator dana prolazi rasponom datuma preskačući vikende i svaki neradni dan koji se odnosi na korisnika — prilagođena zatvaranja njegove organizacije te državni praznici njegove zemlje, razriješeni preko koda zemlje. Ista mapa pokreće i oduzimanja i kalendarsko sučelje.
Svaki zapis ograničen je na organizaciju. Pristup je temeljen na ulogama, uvođenje teče kroz tokenizirane pozivnice s istekom, a zaključavanja razdoblja po organizaciji i mjesecu postoje za zamrzavanje predanih evidencija — s eksplicitnim zapisima otključavanja kada zatvoreno razdoblje opravdano treba ponovno otvoriti.
Povratna informacija predana u Chronou šalje se zasebnoj aplikaciji za tiketiranje (TickIt) putem autenticiranog HTTP poziva. Zahtjev nosi Idempotency-Key koji dostavlja klijent, pa ponovljeno slanje nikad ne može stvoriti duplicirani tiket među dvama sustavima.
"Koliko dana traje ovo odsustvo?" nema fiksni odgovor — ovisi o vikendima, vlastitim zatvaranjima organizacije i državnim praznicima zemlje u kojoj ta organizacija posluje. Pogrešan izračun tiho kvari svako stanje nizvodno.
Jedan LeaveDaysCalculator jednom razrješava primjenjive neradne dane — specifične za organizaciju plus državne praznike usklađene po kodu zemlje — pa zatim prolazi rasponom preskačući vikende i taj skup. Potpuno ista mapa hrani kalendarsko sučelje, pa se broj koji korisnik vidi i broj koji se oduzima računaju na isti način.
Pohranjeni brojač "preostalih dana" odlazi u krivu vrijednost čim se propusti odobrenje, prilagodba ili ispravak, a nema načina objasniti kako je do toga došlo.
Stanja se nikad ne pohranjuju. Svako se računa kao godišnja dodjela uvećana za predznačeni zbroj svake transakcije odsustva za tog korisnika i vrstu, s decimalnim iznosima za pola dana. Knjiga je izvor istine, pa se svako stanje može ponovno izvesti i revidirati iz svoje povijesti.
Dani preneseni iz prošle godine ne bi smjeli živjeti zauvijek — istječu na određeni datum, i samo dani koji još nisu iskorišteni trebaju i dalje vrijediti.
Prijenos se drži na dodjeli s datumom isteka; servis vraća nulu kada taj datum prođe, a inače obračunava preneseni iznos s godišnjim korištenjem tako da ostaju samo stvarno neiskorišteni dani — ograničeno tako da korištenje nikad ne može gurnuti vrijednost u negativno.
Povratne informacije Chronoa hrane zasebni servis za tiketiranje preko mreže. Izgubljeni odgovor ili korisnikov dvostruki klik inače bi mogli prijaviti istu pritužbu dvaput, u sustavu koji Chrono ne posjeduje.
Endpoint za povratne informacije zahtijeva Idempotency-Key (validiran u strogi format), a zatim ga prosljeđuje kao zaglavlje na Bearer-autenticiranom POST-u prema TickIt-ovom integracijskom API-ju. Ponovljeni pokušaji koriste isti ključ, pa primateljska strana može sažeti duplikate; greške veze i 5xx odgovori podižu se kao zasebne, ponovljive greške umjesto da se progutaju.