Commit Graph

147 Commits

Author SHA1 Message Date
f24123be81 Convert iRedMail from REST API to direct MySQL (vmail DB) + per-company integration
- Replace IRedMailClient REST API class with direct MySQL/PDO connection to vmail database
- Move iRedMail config from global config table to per-company integrations (like Zammad)
- Add iRedMail integration card to API settings with DB host/name/user/password/port fields
- Add iRedMail checkbox to integrations section in company settings
- Change Hallinta tab visibility: show for admins (not just superadmins) when module enabled
- API endpoints now use requireCompany() + requireSuperAdmin() and get config from integrations
- Password hashing uses SSHA512 (iRedMail default)
- Mask db_password in API responses (like token masking for Zammad)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:31:23 +02:00
50f34ac37b Add module visibility fix and configurable postal codes for saatavuus API
- Add 'hallinta' to ALL_MODULES so it appears in company settings
- Change fallback from ALL_MODULES to DEFAULT_MODULES (new modules not enabled by default)
- Hallinta tab requires both module enabled + superadmin role
- Add per-company configurable postal codes for "todennäköinen" availability
- Saatavuus API returns true/todennäköinen/false based on customer data + postal codes
- Show "Todennäköinen" badge in availability queries list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 20:10:21 +02:00
6ea62b075f Add Hallinta module with iRedMail email management
New "Hallinta" main tab (superadmin only) with "Sähköposti" sub-tab for
managing email via iRedAdmin-Pro REST API. Features:
- IRedMailClient PHP class with cookie-based session auth + auto-retry
- Domain CRUD (list, create, delete)
- Mailbox CRUD (list, create, delete, password change)
- Alias CRUD (list, create, delete)
- Configuration modal (API URL, admin credentials, connection test)
- Search/filter for mailboxes
- 13 new API endpoints, all requireSuperAdmin()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:58:10 +02:00
b3d8b7e067 Simplify timer: checkbox + delay days, execute at 10:00
Timer is now a simple checkbox + days field. When enabled, rule actions
are scheduled for X days after ticket creation at 10:00 AM instead of
applying immediately. Removed no_activity condition in favor of simple
time-based scheduling stored on the ticket (delay_rule_id + delay_execute_at).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:35:45 +02:00
95434a42fe Add timer/scheduler to ticket automation rules
Adds optional delay_days + delay_condition (no_activity) to ticket rules.
When a ticket has no activity for X days, timed rules automatically apply
actions (e.g., escalate priority to urgent). Checked on each ticket list load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:24:57 +02:00
1a97e07768 Fix undefined function dbGetCompany → use dbLoadCompanies
sendTelegramAlert and telegram_test used non-existent dbGetCompany().
Replaced with dbLoadCompanies() + loop to find company name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:11:48 +02:00
acd3591544 Make bot token also per-company with global fallback
Both bot_token and chat_id are now per-company. If a company
doesn't set its own bot_token, falls back to the global one.
Removed parenthetical hints from labels, all admins can now
configure their own Telegram bot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:08:58 +02:00
20a2b78782 Make Telegram chat_id per-company, bot token stays global
Each company can now have its own Telegram channel/group for alerts.
- Bot token: global (superadmin only, shared across companies)
- Chat ID: per-company (stored in integrations table config)
- sendTelegramAlert reads chat_id from company integration
- Test message shows company name
- Non-superadmin users can't see/edit bot token

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:05:57 +02:00
bbfff2f8b5 Mobile responsiveness + IP allow list comments
Mobile improvements:
- Fix overflow issues with global max-width/overflow-x hidden
- Stack ticket detail selects on mobile instead of one long row
- Compact header: hide less important buttons, scrollable right side
- Stat cards in 2-column grid on mobile
- Force flex-wrap on inline-styled flex containers
- Hide subtitle and user-info on small phones
- Ticket selects full-width on small phones

IP allow list:
- Support # comments after IP addresses (e.g. "192.168.1.1 # Office VPN")
- Updated placeholder with comment examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:49:07 +02:00
45de863d07 Security: defense-in-depth company isolation for all operations
Critical fixes:
- company_logo_upload: validate user has access to target company
- All delete functions (db.php): accept optional company_id parameter
  for defense-in-depth filtering (customers, devices, ipam, guides,
  leads, tickets, archives, mailboxes, rules, templates, todos)
- All API delete calls now pass company_id to db layer
- ticket_bulk_delete: per-ticket company_id filtering
- todo_comment/time/subtask operations: verify todo belongs to company
- dbGetMailbox: optional company_id scoping, used in smtp_test
- requireCompanyOrParam: no longer mutates session permanently
- Fix _dbFetch typo in zammad_attachment (was runtime error)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:27:13 +02:00
a73cce678e Sync ticket status to Zammad + prevent closed tickets reopening
- When changing ticket status in intra, now syncs to Zammad too
  (close in intra → close in Zammad, etc.)
- Prevent Zammad sync from reopening locally closed tickets
  unless Zammad has genuinely new activity (updated_at changed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:15:16 +02:00
b4a85a28a5 Group users by company + allow admins to set user/admin role
- Superadmin sees users grouped by company with header rows
- Admins can now set user or admin role when creating/editing users
- Admin role change restricted to own company only
- Prevents admin from modifying superadmin roles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:16:04 +02:00
a94d1edee0 Add SSL certificate provisioning button for superadmin
- New provision_ssl API endpoint runs certbot for new domains
- SSL button appears next to domain textarea for superadmin
- Shell script on server handles Apache config + Let's Encrypt
- DNS check skips domains without resolution to avoid certbot errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:09:34 +02:00
d4e06fd586 Optimize Zammad sync: skip closed tickets in incremental sync
- Incremental sync now excludes closed/merged/removed tickets from query
- Full sync still fetches everything
- Reopened tickets (closed→new/open in Zammad) are handled correctly:
  existing ticket is updated instead of creating duplicate

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:56:38 +02:00
15223760f5 Auto-update ticket status to käsittelyssä on first reply
- zammad_reply endpoint now sets intra status uusi→kasittelyssa
- Also updates Zammad ticket state to open
- Fixed stateMap: Zammad open now maps to kasittelyssa instead of avoin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:53:59 +02:00
6bbd224104 Poista ticket_fetch changelog-kirjaus
Sähköpostien haku ei ole muutostoiminto eikä kuulu muutoslokiin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:06:45 +02:00
a69fed75e4 Zammad sync performance + liitteiden näyttö tikettilistassa
- Rajoita artikkelien haku max 10 tikettiin per sync (loput on-demand)
- Curl timeout 15s + connect timeout 5s
- Frontend: IMAP ja Zammad haetaan rinnakkain (Promise.allSettled)
- Auto-refresh: Zammad sync ei blokkaa tikettien latausta
- Hakaneula-ikoni (📎) tikettilistassa kun viestissä on liitteitä
- has_attachments, source, zammad_group, ticket_number tikettilistaan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:56:13 +02:00
dc0ed5c75c Liitteiden näyttö Zammad-viesteissä + download proxy
- dbLoadTickets: attachments & zammad_article_id mukaan viestidataan
- ticket_detail on-demand: liitteiden metadata talteen (sama kuin sync)
- Uusi zammad_attachment proxy-endpoint liitteiden lataukseen
- JS: liitteet näkyvät tikettiviesteissä (ikoni, nimi, koko, latauslinkki)
- CSS: .msg-attachments ja .msg-attachment-link tyylit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 03:14:15 +02:00
376912b9ff Liitetiedostot tikettivastausten sähköposteihin + muistiinpanojen piilotus
- Lisää liitetiedostojen tuki vastauslomakkeeseen (📎 Liitä tiedosto -nappi)
- Tuki max 25 MB tiedostoille, useita liitteitä kerralla
- Zammad API: liitteet base64-muodossa attachments-kentässä
- SMTP: multipart/mixed MIME (boundary, Content-Disposition: attachment)
- Sisäiset muistiinpanot (note) suodatetaan pois quoted threadista
  (eivät näy asiakkaille lähtevissä sähköposteissa)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 03:00:30 +02:00
6f1d9ed5d4 BCC-kenttä tiketteihin + To/CC/BCC tallennus + allekirjoituskorjaus
- Lisää BCC-kenttä vastauslomakkeeseen (HTML, JS, API)
- To/CC/BCC tallentuvat tiketille pysyvästi (seuraava vastaus muistaa muutokset)
- Lisää to_email ja bcc sarakkeet tickets-tauluun
- BCC-tuki SMTP-lähetykseen (RCPT TO ilman headeria)
- Korjaa allekirjoitukset: buildSignaturesWithDefaults() generoi nyt oletukset
  myös Zammad-sähköposteille (support@web1.fi ym.), ei pelkille mailboxeille
- Allekirjoituksiin lisätty puhelinnumero

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:54:57 +02:00
3d66319d89 Korjaa sähköpostivastausten välilyönnit: nl2br + &nbsp; pre-wrap:n sijaan
white-space:pre-wrap ei toimi kaikissa sähköpostiohjelmissa/Zammadissa.
Vaihdettu käyttämään nl2br() rivinvaihdoille ja &nbsp; peräkkäisille
välilyönneille, jotka toimivat universaalisti HTML-sähköposteissa.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:46:28 +02:00
8d5ef864f9 Saatavuuskyselyt: IP-organisaatio, siirrä API-tabiin + sähköpostien formatointi + tikettiviestivärit
- Lisää IP-organisaatio/ISP-kenttä saatavuuskyselyihin (ip-api.com haku)
- Siirrä saatavuuskyselyt-taulukko Asiakkaat-tabista API-asetussivulle
- Korjaa rivinvaihdot ja välilyönnit Zammad-sähköpostivastauksissa (white-space:pre-wrap)
- Korjaa quoted thread: plain-text viestit muunnetaan HTML:ksi oikein
- Tikettiviestiketjun värit selkeämmiksi (sininen=saapuva, vihreä=lähtevä)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:40:32 +02:00
74380a3176 Saatavuuskyselyt: IP/hostname, duplikaattien esto
- Reverse DNS -haku tallentaa hostnamen IP:n rinnalle (paljastaa
  operaattorin ja alueen, esim. dsl-hel-123.elisa.fi)
- Duplikaattikyselyn (sama osoite+postinumero+kaupunki) ei tallenneta
  uudelleen samalle yritykselle
- IP/hostname -sarake lisätty taulukkoon

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:26:16 +02:00
64dc02f432 Korjaa saatavuuskyselyt: näytä kaikkien yritysten kyselyt
Endpoint näytti vain aktiivisen yrityksen kyselyt, mutta kyselyt
tallennetaan API-avaimen yrityksen alle. Nyt näytetään kaikkien
käyttäjän yritysten kyselyt + yrityssarake taulukkoon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:23:40 +02:00
a38c5f4808 Lisää saatavuuskyselyjen keräys ja listausnäkymä
Jokainen nettisivujen kautta tehty saatavuustarkistus tallennetaan
tietokantaan (osoite, postinumero, kaupunki, tulos, IP, referer).
Kyselyt näkyvät Asiakkaat > Saatavuuskyselyt -välilehdellä
sivutettuna taulukkona.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:19:24 +02:00
9ba239478c Lisää viestiketju (quoted thread) tikettivastausten loppuun
Sekä Zammad- että SMTP-vastaukset sisältävät nyt koko viestiketjun
vastauksessa. Zammad-vastauksissa HTML-blockquoteilla, SMTP:ssä
plain-text > -quoting-muodossa. Vain uusi viesti tallennetaan
tietokantaan, ketju näkyy vain lähetettävässä sähköpostissa.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:08:03 +02:00
4086409f99 Välitä To/CC-kentät Zammad-vastauksissa
JS ei lähettänyt käyttäjän muokkaamaa To/CC-kenttää Zammad-vastauksen
mukana — backend käytti aina alkuperäistä lähettäjää tietokannasta.
Nyt käyttäjän syöttämä To/CC välitetään API:lle ja Zammadille.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:02:11 +02:00
6e0596959b Korjaa Zammad-vastauksen sähköpostilähetys
Lisätty subtype: 'reply' Zammad-artikkelin luontiin — ilman tätä
Zammad luo artikkelin mutta ei lähetä sähköpostia vastaanottajalle.
Myös plain-text muunnetaan HTML:ksi ja subject saa Re: -etuliitteen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:51:35 +02:00
7a20345701 Lisää puhelinnumero yrityksen asetuksiin ja allekirjoituksiin
Puhelinnumero-kenttä yrityksen asetuksissa tallennetaan tietokantaan
ja näkyy automaattisesti kaikissa oletusallekirjoituksissa viimeisenä
rivinä (sekä SMTP- että Zammad-postilaatikoille).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:30:11 +02:00
890b5d932e Lisää allekirjoitukset Zammad-sähköposteille
Asetukset-sivulla näytetään nyt myös Zammad-sähköpostiosoitteet
(esim. support@web1.fi) allekirjoitusten alla. Allekirjoitus liitetään
automaattisesti Zammad-vastauksiin. Tallennetaan avaimella
"zammad:email@osoite.fi". Uusi ticket_zammad_emails API-endpoint
hakee uniikit vastaanotto-osoitteet tietokannasta.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:12:38 +02:00
02a5c08164 Zammad-tiketit: vastaus Zammad API:n kautta + to-osoite + HTML-viestit
1. Zammad-tiketeille näytetään vastaanotto-osoite (esim. support@web1.fi)
   lähettäjäkentässä eikä SMTP-postilaatikkolistaa — vastaus menee
   Zammad API:n kautta.
2. Ensimmäisen artikkelin to-osoite tallennetaan zammad_to_email kenttään
   on-demand artikkelien haussa.
3. Korjattu _dbFetchRow → _dbFetchOne zammad_reply endpointissa.
4. sanitizeHtml() renderöi viestien HTML turvallisesti.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 01:06:43 +02:00
109dce3f26 Nopeuta Zammad-sync: artikkelit haetaan vasta kun tiketti avataan
Sync hakee nyt vain tikettien metatiedot (status, aihe, ryhmä) —
ei enää satoja getArticles-kutsuja jokaiselle tiketille. Artikkelit
haetaan on-demand ticket_detail-endpointissa kun käyttäjä avaa
tiketin. Nopeuttaa synkkausta dramaattisesti.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:54:11 +02:00
cbcfdaa2a3 Optimoi Zammad-sync: inkrementaalinen haku oletuksena
Auto-refresh hakee nyt vain viimeisen synkkauksen jälkeen muuttuneet
tiketit (updated_at + 5min marginaali). Artikkelit haetaan vain
uusille tai muuttuneille tiketeille. "Hae postit" -nappi tekee
edelleen full syncin (full=true). Nopeuttaa autopäivitystä merkittävästi.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:39:24 +02:00
8c950bbe8f Hae postit -nappi synkkaa myös Zammadin + suljettu tiketti avautuu uutena
1. Hae postit -nappi ajaa nyt myös zammad_sync automaattisesti
   sähköpostien haun jälkeen (ohitetaan hiljaa jos Zammad ei käytössä).
2. Kun suljettuun/ratkaistuun tikettiin tulee uusi viesti,
   tiketti avautuu uudelleen "uusi"-tilaan (aiemmin "käsittelyssä").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:19:51 +02:00
b9a76d76e5 Korjaa Zammad-ryhmien näkyvyys asetuksissa — hae API:sta eikä muistista
Zammad-ryhmät eivät näkyneet postilaatikoiden näkyvyydessä koska ne
haettiin tickets-globaalista joka on tyhjä ennen tiketti-tabin avaamista.
Lisätty ticket_zammad_groups API-endpoint joka hakee uniikit ryhmät
suoraan tietokannasta.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:15:55 +02:00
056de5054d Vaihda brändäys: Noxus Intra → Noxus HUB © Empor Oy 2026
Kaikki viittaukset "Noxus Intra" vaihdettu muotoon "Noxus HUB" koko
koodikannassa. Footer ja login-sivu näyttävät nyt "Noxus HUB © Empor Oy 2026".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:49:00 +02:00
08bce71b5b Paginointi asiakaspalveluun + Zammad-ryhmät näkyvyysasetuksiin
- Paginointi: 100 tikettiä/sivu, navigointipalkki sivujen välillä
- Filtterit resetoivat sivunumeron 1:ksi
- Select All valitsee vain nykyisen sivun tiketit
- Zammad-synkronointi tallentaa source=zammad ja zammad_group
- Postilaatikoiden näkyvyysasetuksissa Zammad-ryhmät (Zammad)-merkinnällä
- Zammad-ryhmien piilotus filtteröi tiketit samalla tavalla kuin postilaatikot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:20:25 +02:00
cc6e5c2653 Noxus-logo + API-tabi piiloon ilman integraatioita
- Luotu Noxus SVG-logo (violetti heksagoni + N)
- Demo-yritys nimetty uudelleen Noxukseksi violetilla värillä
- API-tabi piilotettuna ellei yrityksellä ole integraatioita päällä
  (superadmin näkee aina)
- check_auth palauttaa has_integrations-lipun

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:14:17 +02:00
1752397161 Fix: ZammadClient lisää https:// automaattisesti URL:iin
Kun Zammad-URL syötettiin ilman protokollaa (esim. desk.web1.fi),
cURL ei osannut muodostaa oikeaa osoitetta ja synkronointi epäonnistui.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:56:37 +02:00
6a12da858f Fix: superadmin pääsy kaikkiin yrityksiin
Superadmin-käyttäjällä oli user_companies-taulussa vain yksi yritys,
joten hän ei voinut vaihtaa tai muokata muita yrityksiä (esim. intra.web1.fi).
Nyt superadmin saa kaikki yritykset sessioon loginissa ja check_auth:ssa,
ja requireCompany() + company_switch ohittavat company-tarkistuksen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:24:03 +02:00
dbf2d2b89a Korjaa integraatio-endpointit: $companyId = requireCompany() kaikissa caseissa
Kaikki integraatio/zammad-endpointit kutsuivat requireCompany() ilman
return-arvon talteenottoa, jolloin $companyId oli null ja aiheutti
Fatal error. Korjattu: integrations, integration_save, integration_test,
zammad_groups, zammad_sync, zammad_reply.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:19:35 +02:00
bfa30009a9 Integraatiot: checkboxit vain päälle/pois Yrityksissä, kaikki asetukset API-tabissa
- Yritykset-tab: integraatio-checkboxit tallentavat vain enabled-tilan
- API-tab: Zammad/Saatavuus/Telegram kortit näkyvät kun integraatio päällä
- Zammad-asetukset (URL, token, ryhmät, synkronointi) kokonaan API-tabissa
- integration_save: tyhjä config ei ylikirjoita olemassaolevia asetuksia

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:11:31 +02:00
4a46ce56f3 Zammad-asetukset API-välilehdelle, integraatio-checkboxit erilliseksi kortiksi, korjaa tyyppi puuttuu -virhe
- Integraatiot erillinen table-card yrityksen asetuksissa (vain superadmin)
- Zammad-konfiguraatio (URL, token, ryhmät, synkronointi) siirretty API-tabiin
- Saatavuus-API, Telegram ja Zammad kortit näkyvät API-tabissa kun integraatio on enabloitu
- Korjattu integration_save ja integration_test: puuttuva $input json_decode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:00:36 +02:00
648cb949ac Superadmin-rajoitus: moduulit, integraatiot ja IP-asetukset vain pääkäyttäjälle + captcha-sessiokorjaus
- Moduulit, integraatiot ja IP-rajoitukset piilotetaan yritysadminilta (vain superadmin näkee)
- Saatavuus-API ja Telegram checkboxit tallentavat tilan heti muutoksessa
- session_regenerate_id(false) estää race conditionin kirjautumisen jälkeen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:49:57 +02:00
fa8aaed11e Lisää Zammad-integraatio ja modulaarinen integraatiot-hallinta
- Uusi integrations-taulu tietokantaan (moduulimalli: type, enabled, config)
- ZammadClient-luokka: tiketit, artikkelit, vastaukset, ryhmät
- API-endpointit: integration_save, integration_test, zammad_sync, zammad_reply, zammad_groups
- Synkronointi: Zammad-tiketit → intran tiketit, artikkelit → viestit
- Vastaukset: Zammad-tiketteihin vastaus kulkee Zammad API:n kautta (→ O365)
- UI: Integraatiot-osio API-välilehdellä, toggle-kytkimet, Zammad-konfiguraatio
- tickets.zammad_ticket_id ja ticket_messages.zammad_article_id linkitys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:25:51 +02:00
1aea4bde20 Tiukempi IP-rajoitus: estä kirjautuminen ja suojaa asetukset
- Login: jos kaikki käyttäjän yritykset IP-estetty → estä
  kirjautuminen kokonaan. Valitsee automaattisesti ei-estetyn
  yrityksen aktiiviseksi.
- check_auth: jos aktiivinen yritys IP-estetty → vaihda
  sallittuun. Jos kaikki estetty → kirjaa ulos.
- company_update: vain superadmin saa muuttaa allowed_ips-kenttää.
  Estää adminia poistamasta IP-rajoitusta itseltään.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:49:24 +02:00
771d288338 Käyttäjän poisto: admin poistaa vain yrityksestä, ei kokonaan
Admin poistaa käyttäjän vain nykyisestä yrityksestä (user_companies).
Käyttäjä poistetaan kokonaan vasta kun ei kuulu enää yhteenkään
yritykseen. Superadmin poistaa edelleen kokonaan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:13:23 +02:00
df2d6d2d83 Korjaa automaattisäännöt ja viestien duplikaattiesto
- 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>
2026-03-12 14:52:22 +02:00
0b04fd1d5a Korjaa ticket_fetch ja sähköpostien duplikaattiongelma
- 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>
2026-03-12 14:21:27 +02:00
656b5042e4 Asiakaspalvelu: alinavi-uudelleenjärjestely + tikettityyppien hallinta
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>
2026-03-12 12:52:54 +02:00