148 Commits

Author SHA1 Message Date
6fdfc9ec97 Fix Zammad sync overwriting local ticket status
Local statuses (odottaa vastausta, kasittelyssa, odottaa) set in the
intra UI were being overwritten by Zammad's state on every sync.
Now these statuses are preserved unless Zammad explicitly closes the ticket.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:27:35 +02:00
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