- Sääntöjen kenttänimet: DB käyttää type_set/status_set mutta
API lähetti set_type/set_status → nyt dbSaveTicketRule hyväksyy
molemmat ja matching lukee oikeat DB-kenttänimet
- Migraatio: täytä fetched_message_ids olemassaolevien tikettien
message_id:illä niin poistetut viestit eivät tule takaisin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Korjaa implode()-bugi: $email['to'] on string, ei array
- Lisää fetched_message_ids-taulu joka estää poistettujen
tikettien uudelleenluonnin seuraavassa haussa
- Viestit haetaan postilaatikosta vain kertaalleen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vastauspohjat, Säännöt ja Asetukset siirretty omiksi alinaveikseen
tikettilistan overlay-napeista. Säännöt-välilehdelle lisätty
tikettityyppien hallinta (lisää/poista). Tyypit tallennetaan
tietokantaan yrityskohtaisesti ja populoidaan dynaamisesti
kaikkiin dropdown-valikoihin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lisää Gateway-sarake ja -valitsin NetAdmin-näkymään (devices-linkitys)
- VLAN ja IP näytetään IPAM:sta automaattisesti asiakkaan nimellä
- Muokkausmodaalissa asiakkaan IPAM VLANit/IP:t näkyvät ensimmäisinä
- DB: gateway_device_id LEFT JOIN devices, IPAM-enrichment API:ssa
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Vanhat kansiot (customer_id = NULL) saavat customer_id:n dokumenttiensa perusteella
- Tyhjät/orvot kansiot poistetaan automaattisesti
- Migraatio on idempotenssi: ei tee mitään kun orphaneja ei ole
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lisää customer_id sarake document_folders-tauluun (ALTER TABLE migraatio)
- dbLoadFolders() tukee nyt customer_id suodatusta
- dbSaveFolder() tallentaa customer_id:n kansioon
- API document_folders endpoint vastaanottaa customer_id parametrin
- JS: kansiot ladataan ja luodaan asiakaskohtaisesti (currentDocCustomerId)
- Jokaisen asiakkaan kansiorakenne on nyt itsenäinen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ongelma: migraatio ajautui joka API-kutsulla ja kopioi sites-taulun
rivit takaisin laitetilat-tauluun, joten poistettua laitetilaa ei
voinut oikeasti poistaa — se ilmestyi aina takaisin.
Korjaus: DELETE FROM sites migraation jälkeen, ja tarkista ensin
onko sites-taulussa rivejä (vältetään turha ajaminen).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- dbDeleteLaitetila nollaa devices.laitetila_id, devices.site_id
ja ipam.site_id ennen laitetilan poistoa
- API: parempi virhekäsittely (\Throwable), logi, tyhjä ID tarkistus
- Tiedostojen poisto: @-suppression ja GLOB_BRACE hidden-tiedostoille
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sijainnit (sites) ja Laitetilat olivat käytännössä sama asia.
Nyt kaikki hallitaan Laitetilat-välilehdeltä:
- DB-migraatio kopioi vanhat sites → laitetilat (sama ID säilyy)
- Laitteiden site_id päivitetty automaattisesti laitetila_id:ksi
- IPAM JOINaa nyt laitetilat-taulua sites:n sijaan
- Sijainnit sub-tab poistettu Tekniikasta
- Laiteformissa yksi "Sijainti / Laitetila" dropdown
- Sites API-endpointit poistettu (sites palauttaa laitetilat)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Poisto-nappi näkyy dokumentin luojalle (ei enää vain admin)
- API: document_delete sallii poiston adminille tai luojalle
- Uusi max_versions-sarake documents-tauluun (oletus 10)
- Versioiden automaattinen pruning: uuden version tallennuksen yhteydessä
poistetaan vanhimmat versiot jos yli max_versions (tiedostot levyltä myös)
- Valittavissa per dokumentti: 5, 10, 20, 50 tai rajaton
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Uusi dokumenttityyppi "kokousmuistio" jolla inline tekstieditori (ei tiedostopohjainen)
- document_versions.content -sarake kokousmuistioiden tekstin tallennukseen
- Sub-tabit Dokumentit-välilehdelle (Kaikki / Kokoukset) Tekniikka-mallin mukaan
- Kansiorakenne: document_folders-taulu, kansionavigaatio breadcrumbsilla
- Uudet API-endpointit: document_folders, document_folder_save/delete, document_content_save, document_move
- Asiakasprofiilin Dokumentit-osio: näyttää linkitetyt dokumentit + pikanapit luontiin
- Asiakasprofiilista voi avata dokumentin suoraan tai luoda uuden linkitettynä asiakkaaseen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lisää role-sarake user_companies-tauluun (admin/user per yritys)
- Migraatio: kopioi vanhat admin-roolit user_companies-tauluun, muuta globaali admin → user
- Päivitä dbSaveUser/dbLoadUsers/dbGetUser/dbGetUserByUsername käsittelemään company_roles
- isCompanyAdmin() tarkistaa nyt yrityskohtaisen roolin (session company_role)
- requireAdmin() käyttää isCompanyAdmin():ia
- requireCompany() tarkistaa IP-rajoituksen (siirretty login/check_auth:sta)
- Login ei enää estä kirjautumista IP:n perusteella, vaan merkitsee ip_blocked
- check_auth näyttää kaikki yritykset, IP-estetyt merkitään ip_blocked:lla
- company_switch palauttaa company_role ja päivittää session
- Frontend: käyttäjälomakkeessa yrityskohtaiset rooli-dropdownit (admin/käyttäjä)
- Frontend: yritysvaihto päivittää admin-näkyvyyden company_rolen mukaan
- Frontend: yritysvalitsimessa IP-estetyt yritykset näkyvät "(IP-rajoitus)" -tekstillä
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Liittymärivin klikkaus avaa modal-ikkunan jossa voi muokata kaikkia
kenttiä (osoite, nopeus, VLAN, laite, portti, IP). Yhteysnopeus
muutettu dropdown-valikoksi sekä NetAdmin-modalissa että asiakkaan
liittymälomakkeessa. Vakionopeudet: 10/10 - 10000/10000.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kokoaa kaikki asiakkaiden liittymät yhteen näkymään haulla ja suodattimilla.
Sarakkeet: asiakas, osoite, kaupunki, nopeus, VLAN, laite, portti, IP, hinta.
Suodattimet: kaupunki, nopeus, laite. Laitetietojen ping-status näkyvissä.
Klikkaus avaa asiakkaan muokkaukseen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uusi todo_subtasks-taulu + 3 API-endpointtia (add/toggle/delete).
Tehtävän lukunäkymässä checkbox-lista osatehtäville, lisäys
Enter-näppäimellä tai Lisää-napilla. Valmiit yliviivataan.
Tehtävälistassa näkyy edistyminen (esim. ☑ 2/5).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uusi category-sarake todosiin. Näkyy listassa badgena, lomakkeessa
dropdownina ja lukunäkymässä. Tyypillä voi myös suodattaa listaa.
Värikoodatut badget kullekin tyypille.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Talon sisäinen tehtävienhallinta kahdella alatabilla:
Tehtävät (admin luo):
- Prioriteetti (normaali/tärkeä/kiireellinen), status, deadline
- Vastuuhenkilö-osoitus, inline-muokkaus lukunäkymässä
- Aikakirjaukset: pvm, tunnit, kuvaus - kaikki voivat kirjata
- Myöhästyneet = punainen reunus, lähestyvät = keltainen
- Kommentointi kaikille käyttäjille
Kehitysehdotukset (kaikki voivat luoda):
- Status: ehdotettu → harkinnassa → toteutettu/hylätty (admin muuttaa)
- Kommentointi kaikille
- Ehdottaja voi muokata omia
Tietokanta: 3 taulua (todos, todo_comments, todo_time_entries)
API: 10 endpointtia oikeustarkistuksineen
Frontend: Sub-tab navigointi, kortti-grid, 3-näkymämalli per alatabi
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uusi moduuli "Ohjeet" jossa ylläpitäjä voi kirjoittaa ohjeita
asiakaspalvelijoille miten asioita tehdään.
Ominaisuudet:
- Korttipohjainen listanäkymä (grid) hakutoiminnolla ja kategoriasuodatuksella
- Markdown-editori toolbarilla (B, I, H2, H3, listat, linkit, koodi, lainaukset)
- Esikatselu-toggle muokkausnäkymässä
- Artikkelien lukunäkymä renderoitulla Markdownilla
- Kategorioiden hallinta (lisää/poista)
- Tagit ja kiinnitys (pinned) -toiminto
- Oikeushallinta: kaikki lukevat, admin luo/muokkaa/poistaa
- Moduuli näkyy/piiloutuu yrityskohtaisista asetuksista
- Muutokset kirjautuvat muutoslokiin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lisätty allowed_ips kenttä yrityksiin. Tyhjä = ei rajoitusta,
muuten vain listatut IP:t/CIDR-alueet pääsevät kirjautumaan.
Superadmin ohittaa aina IP-tarkistuksen (backdoor).
Tarkistus tehdään login, check_auth ja company_switch -endpointeissa.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tikettinumero:
- Uudet tiketit saavat juoksevan numeron VVNKKNN-formaatissa
(vuosi+kuukausi sekoitettu sekvenssiin, esim. 2600301)
- Numero näkyy tikettilistassa ja detail-näkymässä (#-merkillä)
- Sähköpostin aihe muotoa "Tiketti #2600301: Alkuperäinen aihe"
- Vastaukset ketjuuntuvat automaattisesti
Autoreply:
- Postilaatikkokohtainen asetus: checkbox + viestisisältö
- Uusi tiketti lähettää automaattisen vastauksen asiakkaalle
- Autoreply näkyy tiketin viestiketjussa (⚡ Automaattinen vastaus)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Aiemmin sähköpostit lähetettiin PHP mail()-funktiolla, mikä
ei toimi kunnolla useimmilla palvelimilla (SPF/DKIM-ongelmat).
Nyt mailboxiin voi konfiguroida SMTP-asetukset (host, port,
user, pass, encryption), ja lähetys tapahtuu suoraan
SMTP-palvelimen kautta socket-yhteydellä. Fallback PHP
mail():iin jos SMTP-asetuksia ei ole asetettu.
- db.php: smtp_host/port/user/password/encryption sarakkeet
- api.php: sendViaSMTP() socket-pohjainen SMTP-client
- index.html: SMTP-kentät mailbox-lomakkeeseen
- script.js: SMTP-kenttien luku/kirjoitus lomakkeessa
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Korvattu yleinen lisätiedot-tekstikenttä neljällä erillisellä
kentällä (vlan, laite, portti, ip) jotta tiedoista voi hakea
ja filtteröidä tarkemmin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Moduulijärjestelmä: yrityskohtaiset tabit (customers, support, leads, archive,
changelog, settings) valittavissa checkboxeina yrityksen asetuksissa
- Käyttäjäroolit: superadmin (pääkäyttäjä), admin (yritysadmin), user (käyttäjä)
- Superadmin: kaikki oikeudet kuten ennen
- Yritysadmin: muokkaa oman yrityksen asetuksia, moduuleita, postilaatikoita
- Käyttäjä: peruskäyttö ilman hallintaoikeuksia
- Päivitetty-kenttä näyttää suhteellista aikaa (15min sitten, 2h sitten, 3pv sitten)
- DB: enabled_modules sarake companies-tauluun, role ENUM laajennettu
- Automaattinen migraatio: vanhat admin → superadmin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Priority emails are now per-customer, not per-company.
Each customer can have a list of email addresses that
automatically elevate ticket priority to "tärkeä" when
they send email. Field added to customer form under
"Lisätiedot" section.
Removed separate priority_emails settings from API/rules tabs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reply form:
- Mailbox/sender selection dropdown (choose which email to reply from)
- CC field (auto-filled from incoming email CC, editable)
- Reply templates dropdown (quick insert pre-made responses)
Priority system:
- Three levels: normaali, tärkeä, urgent
- Priority dropdown in ticket detail view
- Priority-based sorting (urgent/tärkeä always on top)
- Visual indicators in ticket list (colored rows, emoji badges)
- Priority emails: per-company email list that auto-sets "tärkeä"
Response templates:
- CRUD management in Settings tab
- Dropdown selector in reply form
- Templates insert into textarea
Telegram alerts:
- Bot token + chat ID configuration in Settings
- Test button to verify connection
- Auto-alert on urgent tickets (both manual and from email fetch)
- Alert on priority email matches
Database changes:
- New tables: reply_templates, customer_priority_emails
- New columns: tickets.cc, tickets.priority
- ALTER TABLE migration in initDatabase()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Column name yhteyshenkilö contains ö which \w doesn't match
without the /u flag. This caused SQL syntax errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Foreign key constraints fail if charset doesn't match between
referencing and referenced tables. Added utf8mb4 to user_companies,
reset_tokens, and login_attempts tables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PDO extension was not available on CloudLinux/alt-php84 server.
MySQLi is available, so rewrote entire database layer to use it.
Added helper functions (_dbRun, _dbFetchAll, _dbFetchOne, etc.)
that handle named parameter conversion and type binding automatically.
All public db*() function signatures remain identical.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MySQL/PDO doesn't support multiple CREATE TABLE statements in a single
exec() call. Split into an array of individual statements with a loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>