Timeline — ER dijagram
Ponuda ima više grupa, grupa ima jedan timeline, timeline ima više stavki. Postojeće tablice prikazane su sa stvarnim stupcima iz baze.
Napomene
Zašto offer_groups, a ne groups
GROUPS je rezervirana riječ u MySQL-u 8, a ime prati postojeće offer_days / offer_day_items.
"Sve grupe" i shared_key
Svaka grupa ima svoje fizičke retke. Kopije iste stavke u različitim grupama dijele shared_key; izmjena u "Sve grupe" ažurira sve retke s tim ključem u grupama čiji timeline ima is_edited_separately = 0.
Aktivnosti: activities
Aktivnosti (doručak, ukrcaj u bus, prijava u hotel…) su nova tablica koja se uređuje u CMS-u (vlastiti CRUD), a timeline ih nudi za povlačenje. name je translatable (json, kao ostala prevodiva polja), pa stavka timelinea s type = activity pamti samo activity_id, a naziv na PDF-u ide na jeziku klijenta. Dodavanje s timelinea ("+ Aktivnost") sprema novi zapis u tu tablicu (samo hrvatski naziv, ostali jezici u CMS-u). Brisanje je soft delete, pa stavke koje već koriste aktivnost ostaju.
Labava veza na itinerar
offer_day_item_id je nullable s ON DELETE SET NULL. Brisanje usluge iz itinerara ostavlja stavku timelinea; type = service bez offer_day_item_id znači "obrisana usluga".
Po grupi: pax, vodiči, vozač, status
Vozači postoje samo na grupi (offer_group_drivers, jedan bus može imati više vozača), ne na stavkama timelinea. Ime i telefon nisu obavezni i nisu preduvjet za najavu ni voucher. pax, status i vodiči (pivot timeline_item_tour_guide) ne sinkroniziraju se kroz "Sve grupe". Raster uz ponudu prikazuje sve jedinstvene vodiče svih stavki (usluge i aktivnosti), a uz svaku uslugu jedinstvene vodiče stavki vezanih uz tu uslugu; ako nijedan nije dodijeljen, prikazuje "Potreban vodič" — to je zadano stanje usluge bez vodiča, ne posebno polje.
Status po grupi
status = null znači da stavka prati status usluge iz Rastera usluga (raster_services.status). Kad grupa postavi drugačiji status, Raster uz uslugu ispisuje "Grupa X – drugačiji status". Odabir istog statusa kao u Rasteru vraća null.
IN / OUT cijene
IN / OUT cijene nisu na stavkama timelinea nego u raster_services: jedna cijena po usluzi za cijelu ponudu (ne po grupi). To nisu cijene stavke ponude nego analitičke cijene: unose se ručno (prazne dok se ne unesu) i uspoređuju s nabavnom / prodajnom cijenom iz ponude (offer_day_items) radi ispravka kalkulacije. Ne mijenjaju ponudu.
Raster usluga: raster_services
Jedan red po usluzi ponude, zajednički za sve grupe: status, IN / OUT i najava partneru. Ne kopira se itinerar: red nastaje tek kad se na rasteru nešto postavi (status, cijena, najava, račun), a naziv, datum i cijene ponude čitaju se iz offer_day_items. Bez reda raster prikazuje zadano (Neobrađeno, prazno). Brisanje usluge: deleting hook na OfferDayItem — ako ponuda ima timeline, red se kreira ako ne postoji, u item_snapshot se kopira stavka, a offer_day_item_id postaje null; raster je i dalje prikazuje kao "Obrisana usluga" radi poziva i storna. Ako ponuda nema timeline, red se briše zajedno sa stavkom. offer_id zato stoji na tablici. Računi usluge su datoteke preko MediaStorage (media kolekcija na ovom modelu), ne stupci; ostali računi su po ponudi, na Offer.
Financije: PDF-ovi ponude
Tab "Financije" je popis PDF-ova po ponudi (jedan PDF = jedan red). Nema nove tablice: datoteke idu preko MediaStorage (Spatie tablica media), a naziv i vrijeme učitavanja dolaze iz nje. PDF-ovi se učitavaju samo s Rastera: "Račun usluga" (po usluzi) ide u kolekciju service_invoices na RasterService (red se kreira ako ne postoji), a "Ostali računi" (jedan gumb po ponudi, uz Vodiče) u kolekciju other_invoices na Offer. Financije prikazuju oboje po ponudi, s vrstom računa i nazivom usluge. Svaki PDF ima status Novo → Završeno (gumb), spremljen u custom_properties media zapisa (completed_at, null = Novo). Učitava se kroz modal (više PDF-ova odjednom), a svaki PDF dobiva svoju napomenu, također u custom_properties (note), koja se prikazuje u Financijama.
Napomene za najavu
timelines.notes je rich text po grupi (rooming lista, prehrana, smještaj vodiča, ostali izleti…). Ne sinkronizira se kroz "Sve grupe" i ide u najavu vodiču, ne na timeline PDF.
Najave
Najava vodiču (po grupi, timelines.announced_at): timeline PDF + timelines.notes + vaučeri svih usluga grupe u privitku. Najava partneru (raster_services.partner_announced_at): tekstualni sažetak usluge iz timelinea uz molbu za pisanu potvrdu, bez vaučera. Šalje se s Rastera usluga, jednom po usluzi za sve grupe. Slanjem se status usluge automatski postavlja na "Najavljeno"; inače se status mijenja ručno. Sadržaj po grupi: dan i vrijeme, broj gostiju (+ vodič + vozač), vodič, vozač; uz to slobodni tekst. Slobodni tekst se sprema u raster_services.partner_announcement_text.