diff --git a/api.php b/api.php index 29f36c2..949447f 100644 --- a/api.php +++ b/api.php @@ -1443,6 +1443,19 @@ switch ($action) { } } + // Todennäköinen saatavuus: postinumero saatavuuslistalla + $probable = false; + if (!$found) { + $saatInteg = dbGetIntegration($matchedCompany['id'], 'saatavuus_api'); + $probablePostcodes = ($saatInteg && $saatInteg['config']) ? ($saatInteg['config']['probable_postcodes'] ?? []) : []; + if (in_array($queryPostinumero, $probablePostcodes)) { + $probable = true; + } + } + + // Tulos: true, "todennäköinen", tai false + $result = $found ? true : ($probable ? 'todennäköinen' : false); + // Tallenna kysely tietokantaan (ohita duplikaatit: sama osoite+postinumero+kaupunki+yritys) try { $rawOsoite = $_GET['osoite'] ?? ''; @@ -1467,6 +1480,8 @@ switch ($action) { $org = $ipData['org'] ?? $ipData['isp'] ?? ''; } } catch (\Throwable $e) { /* IP-haku ei saa kaataa API:a */ } + // saatavilla: 1=kyllä, 2=todennäköinen, 0=ei + $saatavillaDb = $found ? 1 : ($probable ? 2 : 0); _dbExecute( "INSERT INTO availability_queries (company_id, osoite, postinumero, kaupunki, saatavilla, ip_address, hostname, org, user_agent, referer, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", @@ -1475,7 +1490,7 @@ switch ($action) { $rawOsoite, $rawPostinumero, $rawKaupunki, - $found ? 1 : 0, + $saatavillaDb, $ip, $hostname, $org, @@ -1487,7 +1502,7 @@ switch ($action) { } } catch (\Throwable $e) { /* logitus ei saa kaataa API-vastausta */ } - echo json_encode(['saatavilla' => $found]); + echo json_encode(['saatavilla' => $result]); break; // ---------- SAATAVUUSKYSELYT ---------- @@ -1523,9 +1538,12 @@ switch ($action) { // Telegram: yrityskohtainen config (bot_token + chat_id), globaali fallback $teleInteg = dbGetIntegration($companyId, 'telegram'); $teleConf = ($teleInteg && $teleInteg['config']) ? $teleInteg['config'] : []; + $saatavuusInteg = dbGetIntegration($companyId, 'saatavuus_api'); + $saatavuusConf = ($saatavuusInteg && $saatavuusInteg['config']) ? $saatavuusInteg['config'] : []; echo json_encode([ 'api_key' => dbGetCompanyApiKey($companyId), 'cors_origins' => dbGetCompanyCorsOrigins($companyId), + 'probable_postcodes' => $saatavuusConf['probable_postcodes'] ?? [], 'telegram_bot_token' => $teleConf['bot_token'] ?? ($globalConf['telegram_bot_token'] ?? ''), 'telegram_chat_id' => $teleConf['chat_id'] ?? '', ]); @@ -1556,6 +1574,15 @@ switch ($action) { } dbSaveIntegration($companyId, 'telegram', $teleEnabled, $teleConfig); } + // Todennäköinen saatavuus -postinumerot + if (isset($input['probable_postcodes'])) { + $postcodes = array_filter(array_map('trim', explode("\n", $input['probable_postcodes']))); + $saatInteg = dbGetIntegration($companyId, 'saatavuus_api'); + $saatConfig = ($saatInteg && $saatInteg['config']) ? $saatInteg['config'] : []; + $saatEnabled = $saatInteg ? $saatInteg['enabled'] : true; + $saatConfig['probable_postcodes'] = array_values($postcodes); + dbSaveIntegration($companyId, 'saatavuus_api', $saatEnabled, $saatConfig); + } dbAddLog($companyId, currentUser(), 'config_update', '', '', 'Päivitti asetuksia'); echo json_encode([ 'api_key' => dbGetCompanyApiKey($companyId), diff --git a/index.html b/index.html index ffb704c..3df35ca 100644 --- a/index.html +++ b/index.html @@ -1760,6 +1760,11 @@ +
+ + + Jos osoitetta ei löydy asiakasrekisteristä, mutta postinumero on tällä listalla, palautetaan "todennäköinen". +
@@ -1777,7 +1782,9 @@
Esimerkki:
api.php?action=saatavuus&key=AVAIN&osoite=Esimerkkikatu+1&postinumero=00100&kaupunki=Helsinki
Vastaus:
- {"saatavilla":true} tai {"saatavilla":false}
+ {"saatavilla":true} — löytyy asiakasrekisteristä
+ {"saatavilla":"todennäköinen"} — postinumero saatavuusalueella
+ {"saatavilla":false} — ei löydy

Testaa API

@@ -1965,6 +1972,9 @@ + diff --git a/script.js b/script.js index b21b4f0..47d1dcc 100644 --- a/script.js +++ b/script.js @@ -2744,6 +2744,7 @@ async function loadSettings() { const config = await apiCall('config'); document.getElementById('settings-api-key').value = config.api_key || ''; document.getElementById('settings-cors').value = (config.cors_origins || []).join('\n'); + document.getElementById('settings-probable-postcodes').value = (config.probable_postcodes || []).join('\n'); // Näytä yrityksen nimi API-otsikossa const apiTitle = document.getElementById('api-company-name'); if (apiTitle && currentCompany) apiTitle.textContent = currentCompany.nimi + ' — '; @@ -2831,6 +2832,7 @@ document.getElementById('btn-save-settings').addEventListener('click', async () const config = await apiCall('config_update', 'POST', { api_key: document.getElementById('settings-api-key').value, cors_origins: document.getElementById('settings-cors').value, + probable_postcodes: document.getElementById('settings-probable-postcodes').value, }); alert('Asetukset tallennettu!'); } catch (e) { alert(e.message); } @@ -3737,9 +3739,11 @@ async function loadAvailabilityQueries(page = 0) { } else { tbody.innerHTML = data.queries.map(q => { const date = q.created_at ? q.created_at.replace('T', ' ').substring(0, 16) : ''; - const found = q.saatavilla == 1; - const badge = found + const saatVal = parseInt(q.saatavilla); + const badge = saatVal === 1 ? 'Saatavilla' + : saatVal === 2 + ? 'Todennäköinen' : 'Ei saatavilla'; let source = ''; if (q.referer) { @@ -6719,7 +6723,7 @@ document.getElementById('laitetila-edit-form')?.addEventListener('submit', async // ==================== MODUULIT ==================== -const ALL_MODULES = ['customers', 'support', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'netadmin', 'archive', 'changelog', 'settings']; +const ALL_MODULES = ['customers', 'support', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'netadmin', 'hallinta', 'archive', 'changelog', 'settings']; const DEFAULT_MODULES = ['customers', 'support', 'archive', 'changelog', 'settings']; function applyModules(modules, hasIntegrations) { @@ -6727,8 +6731,8 @@ function applyModules(modules, hasIntegrations) { if (modules && modules.includes('devices') && !modules.includes('tekniikka')) { modules = modules.map(m => m === 'devices' ? 'tekniikka' : m); } - // Jos tyhjä array → kaikki moduulit päällä (fallback) - const enabled = (modules && modules.length > 0) ? modules : ALL_MODULES; + // Jos tyhjä array → oletusmoduulit (admin aktivoi uudet erikseen) + const enabled = (modules && modules.length > 0) ? modules : DEFAULT_MODULES; const isAdminUser = isCurrentUserAdmin(); const isSuperAdmin = currentUser?.role === 'superadmin'; ALL_MODULES.forEach(mod => { @@ -6738,14 +6742,14 @@ function applyModules(modules, hasIntegrations) { if (mod === 'settings') { const showSettings = enabled.includes(mod) && isAdminUser && (isSuperAdmin || hasIntegrations === true); tabBtn.style.display = showSettings ? '' : 'none'; + // hallinta: vain superadmineille + pitää olla moduulina päällä + } else if (mod === 'hallinta') { + tabBtn.style.display = (enabled.includes(mod) && isSuperAdmin) ? '' : 'none'; } else { tabBtn.style.display = enabled.includes(mod) ? '' : 'none'; } } }); - // Hallinta-tabi: aina näkyvä superadmineille, ei moduuliriippuvuutta - const hallintaTab = document.getElementById('tab-hallinta'); - if (hallintaTab) hallintaTab.style.display = isSuperAdmin ? '' : 'none'; // Jos aktiivinen tabi on piilotettu → vaihda ensimmäiseen näkyvään const activeTab = document.querySelector('.tab.active'); if (activeTab && activeTab.style.display === 'none') {