Preorder campaign rework

This commit is contained in:
Luca Haid
2025-03-05 16:32:20 +00:00
parent c032929283
commit f42e472000
10 changed files with 465 additions and 291 deletions

View File

@@ -75,13 +75,13 @@ class ADBHausnummer extends mfBaseModel {
}
public function getBuildingType() {
$rimo_type = strtolower($this->rimo_type);
$rimo_type = $this->rimo_type === NULL ? '' : strtolower($this->rimo_type);
if(array_key_exists($this->rimo_type, TT_ADB_GDA_TYPES)) {
return TT_ADB_GDA_TYPES[$this->rimo_type];
}
$gdaeigenschaft = strtolower($this->gdaeigenschaft);
$gdaeigenschaft = $this->gdaeigenschaft === NULL ? '' : strtolower($this->gdaeigenschaft);
if(array_key_exists($gdaeigenschaft, TT_ADB_GDA_TYPES)) {
return TT_ADB_GDA_TYPES[$gdaeigenschaft];
}

View File

@@ -927,11 +927,72 @@ class PreorderModel
return 0;
}
public static function countHistoryStatus($filter = [], $status_code = null)
{
if ($status_code === null) {
die("Please select a status code");
public static function countActivePreorder($preorderCampaignId = null) {
$db = FronkDB::singleton();
$where = "1=1 ";
if ($preorderCampaignId) {
$where .= " AND p.preordercampaign_id = $preorderCampaignId";
}
$sql = "SELECT
COALESCE(SUM(CASE
WHEN LOWER(vh.gdaeigenschaft) IN ('multi dwelling', 'mehrparteienhaus', 'gebaeude mit 2 oder mehr wohnungen', 'gebäude mit 2 oder mehr wohnungen', 'wohngebaeude fuer gemeinschaften', 'wohngebäude für gemeinschaften', 'hotels und aehnliche gebaeude', 'hotels und ähnliche gebäude', 'wohngebäude mit 2 und mehr wohnungen')
OR LOWER(vh.rimo_type) IN ('multi dwelling', 'mehrparteienhaus', 'gebaeude mit 2 oder mehr wohnungen', 'gebäude mit 2 oder mehr wohnungen', 'wohngebaeude fuer gemeinschaften', 'wohngebäude für gemeinschaften', 'hotels und aehnliche gebaeude', 'hotels und ähnliche gebäude', 'wohngebäude mit 2 und mehr wohnungen')
THEN COALESCE(p.connection_count, 1)
ELSE 0
END), 0) AS md_count,
COALESCE(SUM(COALESCE(p.connection_count, 1)), 0) AS total_count
FROM
`".FRONKDB_DBNAME."`.Preorder p
LEFT JOIN `".ADDRESSDB_DBNAME."`.view_hausnummer vh ON p.adb_hausnummer_id = vh.hausnummer_id
LEFT JOIN `".FRONKDB_DBNAME."`.Preorderstatus tt_preorderstatus ON p.status_id = tt_preorderstatus.id
WHERE p.deleted = 0 AND tt_preorderstatus.code < 899";
$queryStart = microtime(true);
$res = $db->query($sql . $where);
mfLoghandler::singleton()->debug("[Query took: ".(microtime(true) - $queryStart)." seconds] " . $sql);
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return ['md_count' => $data->md_count, 'sd_count' => $data->total_count - $data->md_count, 'total_count' => $data->total_count];
}
return ['md_count' => 0, 'sd_count' => 0, 'total_count' => 0];
}
public static function countTotalUnits($preorderCampaignId = null) {
$db = FronkDB::singleton();
$where = "1=1 ";
if ($preorderCampaignId) {
$where .= " AND pc.id = $preorderCampaignId";
}
$sql = "SELECT
pc.id AS campaign_id,
COALESCE(SUM(n.unit_count), 0) AS total_unit_count,
COALESCE(SUM(n.unit_count_sd), 0) AS total_unit_count_sd,
COALESCE(SUM(n.unit_count_md), 0) AS total_unit_count_md
FROM Preordercampaign pc
LEFT JOIN `".FRONKDB_DBNAME."`.PreordercampaignSalescluster pcs ON pc.id = pcs.preordercampaign_id
LEFT JOIN `".ADDRESSDB_DBNAME."`.Netzgebiet n ON pcs.salescluster_id = n.id
WHERE $where
GROUP BY pc.id";
$queryStart = microtime(true);
$res = $db->query($sql);
mfLoghandler::singleton()->debug("[Query took: ".(microtime(true) - $queryStart)." seconds] " . $sql);
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return ['total_unit_count' => $data->total_unit_count, 'total_unit_count_sd' => $data->total_unit_count_sd, 'total_unit_count_md' => $data->total_unit_count_md];
}
return ['total_unit_count' => 0, 'total_unit_count_sd' => 0, 'total_unit_count_md' => 0];
}
public static function countHistoryStatus($filter = [], $status_code = null) {
if ($status_code === null) {
die("Please select a status code");
}
if (!is_array($filter)) return false;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
<?php /** @noinspection ALL */
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class AddPreorderDbImprovements extends AbstractMigration {
public function up(): void {
if ($this->getEnvironment() == "thetool") {
$this->table("PreordercampaignSalescluster")->addIndex(["preordercampaign_id","salescluster_id"])->save();
$this->table("RimoWorkorder")->addIndex(["adb_wohneinheit_id"])->save();
}
}
public function down(): void {
if ($this->getEnvironment() == "thetool") {
$this->table("PreordercampaignSalescluster")->removeIndex(["preordercampaign_id","salescluster_id"])->save();
$this->table("RimoWorkorder")->removeIndex(["adb_wohneinheit_id"])->save();
}
}
}

View File

@@ -1,17 +1,16 @@
<?php
class FronkDB {
public $link;
private $result;
private $lastError;
private $log;
public $insert_id;
private static $instances = [];
//private static $instance;
public $host;
public $user;
public $pass;
public $db;
public function __construct($host = false, $user = false, $pass = false, $db = false) {
$this->host = $host;

View File

@@ -0,0 +1,36 @@
.campaign-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.75rem;
align-items: center;
}
.campaign-icon,
.campaign-icon > i{
font-size: 24px !important;
padding: 0.25rem;
}
.campaign-icon:hover {
opacity: 0.8;
}
.progress-grid {
display: grid;
grid-template-columns: auto repeat(3, max-content);
gap: 0.4rem;
}
.progress-row > *:nth-child(n+2) {
text-align: right;
font-variant-numeric: tabular-nums;
}
.preorder-campaign-table-actions > a > i {
font-size: 17px !important;
}
.preorder-campaign-table-actions {
display: flex;
justify-content: space-between;
}

View File

@@ -0,0 +1,103 @@
Vue.component('Preordercampaign', {
template: `
<tt-card>
<tt-table :data="window['TT_CONFIG']['CAMPAIGNS']" :config="PreordercampaignTableConfig">
<template v-slot:top-buttons>
<button v-if="window['TT_CONFIG']['IS_ADMIN'] === '1'" type="button" class="btn btn-primary" @click="window.location = window['TT_CONFIG']['ADD_URL']">
<i class="fas fa-plus"></i> Vorbestellung hinzufügen
</button>
<a v-if="window['TT_CONFIG']['SHOW_MISC_BUTTONS'] === '1'" class="btn btn-purple" :href="window['TT_CONFIG']['PREORDER_DISCOUNT_IMPORT_URL']">
<i class="fas fa-tags fa-fw"></i> Gutscheincodes importieren</a>
<a v-if="window['TT_CONFIG']['SHOW_MISC_BUTTONS'] === '1'" class="btn btn-purple ml-1" :href="window['TT_CONFIG']['PREORDER_STATUS_UPDATE_IMPORT_URL']">
<i class="fas fa-retweet fa-fw"></i> Statusupdates importieren</a>
</template>
<template v-slot:status="{ row: campaign }">
<span class="fa-stack" title="Vorbestellkampagne aktiv" v-if="campaign.from <= Date.now()/1000 && campaign.to >= Date.now()/1000">
<i class="fas fa-alarm-clock fa-stack-1x text-success" style="z-index:20"></i>
<i class="fas fa-circle-check" style="color:Tomato;font-size:.7em"></i>
</span>
</template>
<template v-slot:add="{ row: campaign }">
<a class="btn btn-sm btn-outline-primary" :href="window['TT_CONFIG']['ADD_PREORDER_URL'] + '?preordercampaign_id=' + campaign.id">
<i class="fas fa-plus" title="Vorbestellung hinzufügen"></i></a>
</template>
<template v-slot:progress="{ row: campaign }">
<div class="campaign-grid">
<a :href="window['TT_CONFIG']['VIEW_URL'] + '?filter[preordercampaign_id]=' + campaign.id" class="campaign-icon">
<i class="fas fa-list-alt" title="Bestellungen anzeigen"></i></a>
<div class="progress-grid">
<template v-if="campaign.count_total_units.total_unit_count_sd > 0">
<span>EFH:</span><span>{{ campaign.active_preorder_count.sd_count }} /</span>
<span>{{ campaign.count_total_units.total_unit_count_sd }}</span>
<span>({{ ((campaign.active_preorder_count.sd_count / campaign.count_total_units.total_unit_count_sd) * 100 || 0).toFixed(2) }} %)</span>
</template>
<template v-if="campaign.count_total_units.total_unit_count_md > 0">
<span>MPH:</span><span>{{ campaign.active_preorder_count.md_count }} /</span>
<span>{{ campaign.count_total_units.total_unit_count_md }}</span>
<span>({{ ((campaign.active_preorder_count.md_count / campaign.count_total_units.total_unit_count_md) * 100 || 0).toFixed(2) }} %)</span>
</template>
<template v-if="campaign.count_total_units.total_unit_count > 0">
<span>Gesamt:</span><span>{{ campaign.active_preorder_count.total_count }} /</span>
<span>{{ campaign.count_total_units.total_unit_count * 2 }}</span>
<span>({{ ((campaign.active_preorder_count.total_count / (campaign.count_total_units.total_unit_count * 2)) * 100 || 0).toFixed(1) }} %)</span>
</template>
</div>
</div>
</template>
<template v-slot:from="{ row: campaign }">{{ formatDate(campaign.from) }}</template>
<template v-slot:to="{ row: campaign }">{{ formatDate(campaign.to) }}</template>
<template v-slot:rimo_workoders="{ row: campaign }">{{ campaign.rimo_workoders }} / {{ campaign.active_preorder_count.total_count }}</template>
<template v-slot:actions="{ row: campaign }">
<div class="preorder-campaign-table-actions">
<a :href="window['TT_CONFIG']['PREORDER_NOTIFICATION_URL'] + '?filter[preordercampaign_id]=' + campaign.id" title="Email Aussendungen"><i class="far fa-envelope"></i></a>
<template v-if="window['TT_CONFIG']['IS_ADMIN'] === '1'">
<a class="ml-2" :href="window['TT_CONFIG']['EDIT_URL'] + '?id=' + campaign.id"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a class="ml-2" :href="window['TT_CONFIG']['ADMIN_URL'] + '?id=' + campaign.id"><i class="far fa-a" title="Adminfunktionen"></i></a>
</template>
</div>
</template>
</tt-table>
</tt-card>
`,
data() {
return {
window: window,
PreordercampaignTableConfig: {
key: 'PreordercampaignTable',
tableHeader: 'Vorbestellkampagnen',
defaultPageSize: 25,
headers: [
{text: 'Status', key: 'status', class: 'text-center',sortable: false,filter:false, priority: 20},
{text: '', key: 'add', class: 'text-center',filter:false, sortable: false, priority: 18},
{text: 'Netzgebiet', key: 'network_name', priority: 19},
{text: 'Name', key: 'name', priority: 14},
{text: 'Fortschritt', key: 'progress', priority: 17,filter:false,sortable: false},
{text: 'Workorders', key: 'rimo_workoders', class: 'text-center', priority: 16,filter:false},
{text: 'Von', key: 'from', class: 'text-center', priority: 13,filter:false,sortable: false},
{text: 'Bis', key: 'to', class: 'text-center', priority: 12,filter:false,sortable: false},
{text: 'Aktionen', key: 'actions', class: 'text-center', sortable: false, priority: 21,filter:false}
],
customRowClass: (row) => {
if (row.from <= Date.now()/1000 && row.to >= Date.now()/1000) {
return 'active';
}
}
}
}
},
mounted() {
if (window.TT_CONFIG.IS_ADMIN === "1") {
this.PreordercampaignTableConfig.headers.splice(3, 0, {text: 'Netzbesitzer', key: 'network_owner_name', priority: 4, filter: 'select', filterOptions: window['TT_CONFIG']['NETWORK_OWNER_OPTIONS']});
}
},
methods: {
formatDate: date => window.moment(date * 1000).format('DD.MM.YYYY'),
}
});

View File

@@ -83,7 +83,7 @@
}
.tt-table.table-sm > tbody > tr > td * {
font-size: 13px;
font-size: 14px;
}
.tt-table.table-sm > tbody > tr {

View File

@@ -66,8 +66,9 @@ Vue.component('tt-table-crud', {
<tt-select v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'select'" v-model="crudModalData[column.key]" :label="column.text" :options="column.items" sm row/>
<tt-autocomplete v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'autocomplete'" v-model="crudModalData[column.key]" :label="column.text" :api-url="column.apiUrl" :items="column.items" sm row :return-text="column.returnText" />
<tt-date-picker v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'datepicker'" v-model="crudModalData[column.key]" :label="column.text" sm row :date-range="false" :ref="column.key.toLowerCase() + '-modal-input'"/>
<tt-icon-select v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'icon-select'" v-model="crudModalData[column.key]" :label="column.text" sm row/>
<tt-icon-select v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'icon-select'" v-model="crudModalData[column.key]" :options="column.items" :label="column.text" sm row/>
<tt-checkbox v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'checkbox'" v-model="crudModalData[column.key]" :label="column.text" sm row/>
<tt-positions-manager v-show="crudModalColumnVisibility[column.key]" v-else-if="column.type === 'positions-manager'" v-model="crudModalData[column.key]" :config="column.config" sm row/>
</slot>
<!-- @formatter:on -->
</template>

View File

@@ -825,16 +825,16 @@ Vue.component('tt-table', {
}
})
Vue.config.errorHandler = function (err, vm, info) {
// still log errors to the console
console.error(info, err, vm);
if (typeof vm.config.key === 'string') {
// check if document.querySelector table.tt-table exists aswell if it has atleast 3 <tr> elements
const table = document.querySelector('table.tt-table');
if (!table || !table.querySelectorAll('tr').length > 2) {
// localStorage.removeItem(`tt-table-${vm.config.key}`);
// window.location.reload();
}
}
}
// Vue.config.errorHandler = function (err, vm, info) {
// // still log errors to the console
// console.error(info, err, vm);
//
// if (typeof vm.config.key === 'string') {
// // check if document.querySelector table.tt-table exists aswell if it has atleast 3 <tr> elements
// const table = document.querySelector('table.tt-table');
// if (!table || !table.querySelectorAll('tr').length > 2) {
// // localStorage.removeItem(`tt-table-${vm.config.key}`);
// // window.location.reload();
// }
// }
// }