added new fcp statistics
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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) : [];
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
53
db/migrations/20250912080000_add_rimo_fcp_index.php
Normal file
53
db/migrations/20250912080000_add_rimo_fcp_index.php
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user