added new fcp statistics

This commit is contained in:
2025-09-12 09:18:15 +02:00
parent 900b9232b4
commit 8e6ab15fef
5 changed files with 218 additions and 16 deletions

View File

@@ -1076,20 +1076,73 @@ $pagination_entity_name = "Vorbestellungen";
}
async function getFCPs(map) {
var fcp = await $.get("<?=self::getUrl("Preorder", "Api")?>", {
const fcpResponse = await $.get("<?=self::getUrl("Preorder", "Api")?>", {
do: "getFCPsForCampaign",
campaign_id: "<?=$campaign->id?>"
});
if(fcp.status == "OK") {
fcp.result.forEach((fcp) => {
var icon = L.MakiMarkers.icon({icon: "viewpoint", color: "yellow", size: "m"});
var marker = L.marker([fcp.lat, fcp.lng], {icon: icon}).addTo(map);
var google_maps_link = "https://www.google.com/maps/search/?api=1&query=" + fcp.lat + "," + fcp.lng;
var popup_content = "<a href='" + google_maps_link + "' target='_blank'>Google Maps</a><br />" + fcp.text;
marker.bindPopup(popup_content);
});
}
if (fcpResponse.status !== "OK" || !fcpResponse.result?.length) return;
const fcpIds = fcpResponse.result.map(fcp => fcp.real_id);
const statsResponse = await $.ajax({
url: "<?=self::getUrl("Preorder", "Api")?>?do=getRimoFcpStats",
type: 'POST',
contentType: 'application/json', // 1. Set the content type to JSON
data: JSON.stringify({ fcp_ids: fcpIds }) // 2. Stringify the data object
});
const stats = statsResponse.status === "OK" ? statsResponse.result : [];
fcpResponse.result.forEach(fcp => {
const icon = L.MakiMarkers.icon({ icon: "viewpoint", color: "yellow", size: "m" });
const marker = L.marker([fcp.lat, fcp.lng], { icon }).addTo(map);
const fcpStat = stats.find(s => parseInt(s.fcp_id) === parseInt(fcp.real_id));
const googleMapsLink = `https://www.google.com/maps/search/?api=1&query=${fcp.lat},${fcp.lng}`;
const statsHtml = !fcpStat ? `<p>Keine Statistiken gefunden.</p>` : `
<div style="margin-bottom: 15px;">
<strong style="display: block; margin-bottom: 5px; color: #555;">Zusammenfassung:</strong>
<span>Hausnummern Gesamt: <b>${fcpStat.total_hausnummer_count}</b></span><br>
<span>Wohneinheiten Gesamt: <b>${fcpStat.total_wohneinheit_count}</b></span><br>
<span>Aktive Vorbestellungen: <b>${fcpStat.total_active_preorders}</b></span>
</div>
<strong style="display: block; margin-bottom: 5px; color: #555;">Details nach RIMO-Typ:</strong>
<table style="width: 100%; border-collapse: collapse; font-size: 12px;">
<thead>
<tr style="background-color: #f2f2f2; text-align: left;">
<th style="padding: 8px; border: 1px solid #ddd;">Typ</th>
<th style="padding: 8px; border: 1px solid #ddd;">HN</th>
<th style="padding: 8px; border: 1px solid #ddd;">WE</th>
<th style="padding: 8px; border: 1px solid #ddd;">VB</th>
</tr>
</thead>
<tbody>
${Object.entries(fcpStat.counts_by_rimo_type || {}).length ?
Object.entries(fcpStat.counts_by_rimo_type).map(([type, counts], index) => `
<tr style="${index % 2 === 0 ? 'background-color: #ffffff;' : 'background-color: #f9f9f9;'}">
<td style="padding: 8px; border: 1px solid #ddd;">${type}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${counts.hausnummer_count}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${counts.wohneinheit_count}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${counts.preorder_count}</td>
</tr>
`).join('') :
'<tr><td colspan="4" style="padding: 8px; text-align: center; border: 1px solid #ddd;">Keine detaillierten Statistiken verfügbar.</td></tr>'
}
</tbody>
</table>
`;
const popupContent = `
<div style="font-family: Arial, sans-serif; width: 320px; padding: 5px;">
<h3 style="margin-bottom: 10px; color: #333; border-bottom: 1px solid #ddd; padding-bottom: 5px;">
${fcp.text}
</h3>
<a href='${googleMapsLink}' target='_blank' style="color: #007bff; text-decoration: none; margin-bottom: 15px; display: inline-block;">In Google Maps anzeigen</a>
${statsHtml}
</div>
`;
marker.bindPopup(popupContent);
});
}
function centerMap() {

View File

@@ -1,6 +1,7 @@
<?php
class ADBRimoFcp extends TTCrudBaseModel {
class ADBRimoFcp extends TTCrudBaseModel
{
public int $id;
public int $netzgebiet_id;
public ?string $name;
@@ -13,4 +14,67 @@ class ADBRimoFcp extends TTCrudBaseModel {
public ?float $gps_long;
public int $create;
public int $edit;
public static function getRimoFcpStatistics(): array {
$db = self::getDB();
$fronkDbName = defined('FRONKDB_DBNAME') ? FRONKDB_DBNAME : 'thetool';
$addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb';
$sql = "
-- Use a Common Table Expression (CTE) to pre-calculate counts for each combination of FCP and rimo_type.
WITH RimoTypeCounts AS (
SELECT
hn.fcp_id,
-- Group NULL rimo_types into an 'UNKNOWN' category for clarity.
COALESCE(hn.rimo_type, 'UNKNOWN') AS rimo_type,
COUNT(DISTINCT hn.id) AS hausnummer_count,
COUNT(DISTINCT we.id) AS wohneinheit_count,
COUNT(DISTINCT CASE WHEN ps.code < 899 THEN p.id ELSE NULL END) AS preorder_count
FROM
`{$addressDbName}`.`Hausnummer` AS hn
LEFT JOIN
`{$addressDbName}`.`Wohneinheit` AS we ON hn.id = we.hausnummer_id
LEFT JOIN
`{$fronkDbName}`.`Preorder` AS p ON hn.id = p.adb_hausnummer_id
LEFT JOIN
`{$fronkDbName}`.`Preorderstatus` AS ps ON p.status_id = ps.id
WHERE
hn.fcp_id IS NOT NULL
GROUP BY
hn.fcp_id,
COALESCE(hn.rimo_type, 'UNKNOWN')
)
-- Final SELECT statement to assemble the data for each FCP.
SELECT
fcp.id AS fcp_id,
fcp.name AS fcp_name,
fcp.rimo_id AS fcp_rimo_id,
-- Aggregate total counts for the entire FCP.
SUM(rtc.hausnummer_count) AS total_hausnummer_count,
SUM(rtc.wohneinheit_count) AS total_wohneinheit_count,
SUM(rtc.preorder_count) AS total_active_preorders,
-- Create a single JSON object from all the rimo_type groups for the current FCP.
JSON_OBJECTAGG(
rtc.rimo_type,
JSON_OBJECT(
'hausnummer_count', rtc.hausnummer_count,
'wohneinheit_count', rtc.wohneinheit_count,
'preorder_count', rtc.preorder_count
)
) AS counts_by_rimo_type
FROM
`{$addressDbName}`.`RimoFcp` AS fcp
LEFT JOIN
RimoTypeCounts AS rtc ON fcp.id = rtc.fcp_id
WHERE
rtc.fcp_id IS NOT NULL
GROUP BY
fcp.id, fcp.name, fcp.rimo_id
ORDER BY
fcp.name;
";
$result = $db->query($sql);
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
}
}

View File

@@ -136,14 +136,10 @@ class ADBRimoFcpController extends TTCrud {
public function getAllFCPsAction() {
$input = json_decode(file_get_contents('php://input'), true);
$fcpList = ADBRimoFcp::getAll();
$fcpData = array_map(function ($fcp) {
return [
'id' => $fcp->id,
// 'rimo_ex_state' => $fcp->rimo_ex_state,
// 'rimo_op_state' => $fcp->rimo_op_state,
'gps_lat' => $fcp->gps_lat,
'gps_long' => $fcp->gps_long
];
@@ -151,4 +147,20 @@ class ADBRimoFcpController extends TTCrud {
self::returnJson(['success' => true, 'data' => $fcpData]);
}
public function getRimoFcpStatsAction() {
$stats = ADBRimoFcp::getRimoFcpStatistics();
if (!empty($this->postData->fcp_ids)) {
$fcpIds = (array) $this->postData->fcp_ids;
$stats = array_filter($stats, fn($item) => in_array($item['fcp_id'], $fcpIds));
}
foreach ($stats as &$item)
if (isset($item['counts_by_rimo_type']) && is_string($item['counts_by_rimo_type']))
$item['counts_by_rimo_type'] = json_decode($item['counts_by_rimo_type']);
unset($item);
self::returnJson(array_values($stats));
}
}

View File

@@ -1090,6 +1090,9 @@ class PreorderController extends mfBaseController {
case "saveStatusJournal":
$return = $this->saveStatusJournalApi();
break;
case "getRimoFcpStats":
$return = $this->getRimoFcpStatsApi();
break;
default:
$return = false;
}
@@ -1221,11 +1224,28 @@ class PreorderController extends mfBaseController {
if (!$campaign->id) return [];
return array_map(
fn($fcp) => ["id" => $fcp->name ?? null, "text" => $fcp->name ?? null, 'lat' => $fcp->gps_lat ?? null, 'lng' => $fcp->gps_long ?? null],
fn($fcp) => ["real_id" => $fcp->id, "id" => $fcp->name ?? null, "text" => $fcp->name ?? null, 'lat' => $fcp->gps_lat ?? null, 'lng' => $fcp->gps_long ?? null],
ADBRimoFcp::getAll(["netzgebiet_id" => intval($campaign->network->adb_netzgebiet_id)]) ?? []
);
}
public function getRimoFcpStatsApi() {
$this->postData = json_decode(file_get_contents("php://input"));
$stats = ADBRimoFcp::getRimoFcpStatistics();
if (!empty($this->postData->fcp_ids)) {
$fcpIds = (array) $this->postData->fcp_ids;
$stats = array_filter($stats, fn($item) => in_array($item['fcp_id'], $fcpIds));
}
foreach ($stats as &$item)
if (isset($item['counts_by_rimo_type']) && is_string($item['counts_by_rimo_type']))
$item['counts_by_rimo_type'] = json_decode($item['counts_by_rimo_type']);
unset($item);
return array_values($stats);
}
private function setBilledApi() {
$preorder_id = $this->request->id;

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class AddRimoFcpIndex extends AbstractMigration
{
public function up(): void
{
if ($this->getEnvironment() == "addressdb") {
$table = $this->table('Hausnummer');
if (!$table->hasIndexByName('idx_fcp_id_rimo_type')) {
$table->addIndex(['fcp_id', 'rimo_type'], ['name' => 'idx_fcp_id_rimo_type'])
->update();
}
}
if ($this->getEnvironment() == "thetool") {
$table = $this->table('Preorder');
if (!$table->hasIndexByName('idx_adb_hausnummer_id')) {
$table->addIndex(['adb_hausnummer_id'], ['name' => 'idx_adb_hausnummer_id']);
}
if (!$table->hasIndexByName('idx_status_id')) {
$table->addIndex(['status_id'], ['name' => 'idx_status_id']);
}
$table->update();
}
}
public function down(): void
{
if ($this->getEnvironment() == "addressdb") {
$table = $this->table('Hausnummer');
if ($table->hasIndexByName('idx_fcp_id_rimo_type')) {
$table->removeIndexByName('idx_fcp_id_rimo_type')
->update();
}
}
if ($this->getEnvironment() == "thetool") {
$table = $this->table('Preorder');
if ($table->hasIndexByName('idx_adb_hausnummer_id')) {
$table->removeIndexByName('idx_adb_hausnummer_id');
}
if ($table->hasIndexByName('idx_status_id')) {
$table->removeIndexByName('idx_status_id');
}
$table->update();
}
}
}