diff --git a/Layout/default/Callcenterstats/Index.php b/Layout/default/Callcenterstats/Index.php new file mode 100644 index 000000000..761daf63f --- /dev/null +++ b/Layout/default/Callcenterstats/Index.php @@ -0,0 +1,165 @@ + + + + +
+
+
+
+ +
+

Callcenter Auswertung

+
+
+
+ + +
+
+ +
+
+

Callcenter Auswertung

+ +
"> + +
+ +
+ + ESTMK +
+
+ +
+ +
+ " /> +
+
+ +
+ +
+ "/> +
+
+ +
+
+
+ +
+
+ +
+ + +
+
+ + + +
+
+
+
+

Eingehend

+ + + + + + + + + + + + + + + + + + +
Anzahl eingehende Calls:
Sekunden gesamt:
Verrechenbare Minuten:
Kosten:
+ + +
+
+

Ausgehend

+ + + + + + + + + + + + + + + + + + +
Anzahl ausgehende Calls:
Sekunden gesamt:
Verrechenbare Minuten:
Kosten:
+ +
+
+ +
+
+ + + + +
+
+ + + \ No newline at end of file diff --git a/Layout/default/menu.php b/Layout/default/menu.php index 0324b0d51..2c076da33 100644 --- a/Layout/default/menu.php +++ b/Layout/default/menu.php @@ -113,10 +113,11 @@
  • "> Vorbestellkampagnen Verrechnung
  • isAdmin() || !empty(json_decode((new WorkerFlag($me->id, "constructionConsent_projects"))->value() ?? '[]'))): ?> -
  • "> Zustimmungserklärungen
  • +
  • "> Zustimmungserklärungen
  • is(["Admin"])): ?>
  • "> Emailaussendungen
  • + is(["Admin"])): ?>
  • "> Callcenter Auswertung
  • is(["Admin"])): ?>
  • "> Asset Management
  • diff --git a/application/CallcenterIdentity/CallcenterIdentity.php b/application/CallcenterIdentity/CallcenterIdentity.php new file mode 100644 index 000000000..b7acd7f91 --- /dev/null +++ b/application/CallcenterIdentity/CallcenterIdentity.php @@ -0,0 +1,198 @@ +$name == null) { + + if($name == "inumbers") { + $inumbers = CallcenterIdentityIncomingnumber::search(["callcenteridentity_id" => $this->id]); + if($inumbers) { + $this->inumbers = $inumbers; + } + return $this->inumbers; + } + + $classname = ucfirst($name); + $idfield = $name . "_id"; + $this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-" . $this->$idfield); + if(!$this->$name) { + $this->$name = new $classname($this->$idfield); + } + + if($this->$name->id) { + mfValuecache::singleton()->set("mfObjectmodel-$name-" . $this->$name->id, $this->$name); + return $this->$name; + } else { + return null; + } + } + + return $this->$name; + + } + + /******************************** + * Begin static Model functions + */ + + public static function create(Array $data) { + $model = new CallcenterIdentity(); + + $table_fields = [ + "name", "number", "display", + "create_by","edit_by","create","edit" + ]; + + foreach($data as $field => $value) { + if(in_array($field, $table_fields)) { + $model->$field = $value; + } + } + + $me = new User(); + $me->loadMe(); + + if($model->create_by === null) { + $model->create_by = $me->id; + } + if($model->edit_by === null) { + $model->edit_by = $me->id; + } + + return $model; + } + + + public static function getAll() { + $items = []; + + $db = FronkDB::singleton(); + + $res = $db->select("CallcenterIdentity", "*", "1 = 1 ORDER BY `order`"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new CallcenterIdentity($data); + } + } + return $items; + + } + + public static function getFirst($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM CallcenterIdentity + WHERE $where + ORDER BY `order` LIMIT 1"; + //var_dump($sql);exit; + mfLoghandler::singleton()->debug($sql); + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new CallcenterIdentity($data); + if($item->id) { + return $item; + } else { + return null; + } + } + return null; + } + + public static function count($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT COUNT(*) as cnt FROM CallcenterIdentity + WHERE $where"; + + //mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + return 0; + } + + public static function search($filter, $limit = false, $order = false) { + //var_dump($filter);exit; + $items = []; + + if(!$order) { + $order = "`order` ASC"; + } + + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM CallcenterIdentity + WHERE $where + ORDER BY $order"; + + if(is_array($limit) && count($limit)) { + if(is_numeric($limit['start']) && is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['start'].", ".$limit['count']; + } elseif(is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['count']; + } + } + + mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[$data->id] = new CallcenterIdentity($data); + } + } + + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + if(array_key_exists("order", $filter)) { + $order = $filter['order']; + if(is_numeric($order)) { + $where .= " AND CallcenterIdentity.order=$order"; + } + } + + if(array_key_exists("number", $filter)) { + $number = FronkDB::singleton()->escape($filter['number']); + if($number) { + $where .= " AND CallcenterIdentity.number LIKE '$number'"; + } + } + + if(array_key_exists("name", $filter)) { + $name = FronkDB::singleton()->escape($filter['name']); + if($name) { + $where .= " AND CallcenterIdentity.name LIKE '$name'"; + } + } + + if(array_key_exists("display", $filter)) { + $display = FronkDB::singleton()->escape($filter['display']); + if($display) { + $where .= " AND CallcenterIdentity.display LIKE '$display'"; + } + } + + if(array_key_exists("add-where", $filter)) { + $where .= " ".$filter['add-where']; + } + + //var_dump($filter, $where);exit; + return $where; + } + +} \ No newline at end of file diff --git a/application/CallcenterIdentity/CallcenterIdentityController.php b/application/CallcenterIdentity/CallcenterIdentityController.php new file mode 100644 index 000000000..3cc5ffea0 --- /dev/null +++ b/application/CallcenterIdentity/CallcenterIdentityController.php @@ -0,0 +1,26 @@ +needlogin = true; + $me = new User(); + $me->loadMe(); + $this->me = $me; + $this->layout()->set("me", $me); + + if(!$me->is("Admin")) { + $this->redirect("Dashboard"); + } + } + + protected function indexAction() { + $this->layout()->setTemplate("Callcenterstats/Index"); + + $identities = CallcenterIdentity::getAll(); + $this->layout()->set("identities", $identities); + + + } + +} \ No newline at end of file diff --git a/application/CallcenterIdentityIncomingnumber/CallcenterIdentityIncomingnumber.php b/application/CallcenterIdentityIncomingnumber/CallcenterIdentityIncomingnumber.php new file mode 100644 index 000000000..c44f969c6 --- /dev/null +++ b/application/CallcenterIdentityIncomingnumber/CallcenterIdentityIncomingnumber.php @@ -0,0 +1,190 @@ +$name == null) { + + + $classname = ucfirst($name); + $idfield = $name . "_id"; + $this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-" . $this->$idfield); + if(!$this->$name) { + $this->$name = new $classname($this->$idfield); + } + + if($this->$name->id) { + mfValuecache::singleton()->set("mfObjectmodel-$name-" . $this->$name->id, $this->$name); + return $this->$name; + } else { + return null; + } + } + + return $this->$name; + + } + + /******************************** + * Begin static Model functions + */ + + public static function create(Array $data) { + $model = new CallcenterIdentityIncomingnumber(); + + $table_fields = [ + "callcenteridentity_id", "number", "display", + "create_by","edit_by","create","edit" + ]; + + foreach($data as $field => $value) { + if(in_array($field, $table_fields)) { + $model->$field = $value; + } + } + + $me = new User(); + $me->loadMe(); + + if($model->create_by === null) { + $model->create_by = $me->id; + } + if($model->edit_by === null) { + $model->edit_by = $me->id; + } + + return $model; + } + + + public static function getAll() { + $items = []; + + $db = FronkDB::singleton(); + + $res = $db->select("CallcenterIdentityIncomingnumber", "*", "1 = 1 ORDER BY `number`"); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[] = new CallcenterIdentityIncomingnumber($data); + } + } + return $items; + + } + + public static function getFirst($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM CallcenterIdentityIncomingnumber + ORDER BY `number` LIMIT 1"; + //var_dump($sql);exit; + mfLoghandler::singleton()->debug($sql); + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + $item = new CallcenterIdentityIncomingnumber($data); + if($item->id) { + return $item; + } else { + return null; + } + } + return null; + } + + public static function count($filter) { + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT COUNT(*) as cnt FROM CallcenterIdentityIncomingnumber + WHERE $where"; + + //mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + return 0; + } + + public static function search($filter, $limit = false, $order = false) { + //var_dump($filter);exit; + $items = []; + + if(!$order) { + $order = "`number` ASC"; + } + + $db = FronkDB::singleton(); + + $where = self::getSqlFilter($filter); + $sql = "SELECT * FROM CallcenterIdentityIncomingnumber + WHERE $where + ORDER BY $order"; + + if(is_array($limit) && count($limit)) { + if(is_numeric($limit['start']) && is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['start'].", ".$limit['count']; + } elseif(is_numeric($limit['count'])) { + $sql .= " LIMIT ".$limit['count']; + } + } + + mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if($db->num_rows($res)) { + while($data = $db->fetch_object($res)) { + $items[$data->id] = new CallcenterIdentityIncomingnumber($data); + } + } + + return $items; + } + + private static function getSqlFilter($filter) { + $where = "1=1 "; + + if(array_key_exists("callcenteridentity_id", $filter)) { + $callcenteridentity_id = $filter['callcenteridentity_id']; + if(is_numeric($callcenteridentity_id)) { + $where .= " AND CallcenterIdentityIncomingnumber.callcenteridentity_id=$callcenteridentity_id"; + } + } + + if(array_key_exists("number", $filter)) { + $number = FronkDB::singleton()->escape($filter['number']); + if($number) { + $where .= " AND CallcenterIdentityIncomingnumber.number LIKE '$number'"; + } + } + + if(array_key_exists("name", $filter)) { + $name = FronkDB::singleton()->escape($filter['name']); + if($name) { + $where .= " AND CallcenterIdentityIncomingnumber.name LIKE '$name'"; + } + } + + if(array_key_exists("display", $filter)) { + $display = FronkDB::singleton()->escape($filter['display']); + if($display) { + $where .= " AND CallcenterIdentityIncomingnumber.display LIKE '$display'"; + } + } + + if(array_key_exists("add-where", $filter)) { + $where .= " ".$filter['add-where']; + } + + //var_dump($filter, $where);exit; + return $where; + } + +} \ No newline at end of file diff --git a/application/Callcenterstats/CallcenterstatsController.php b/application/Callcenterstats/CallcenterstatsController.php new file mode 100644 index 000000000..f81df557e --- /dev/null +++ b/application/Callcenterstats/CallcenterstatsController.php @@ -0,0 +1,158 @@ +needlogin = true; + $me = new User(); + $me->loadMe(); + $this->me = $me; + $this->layout()->set("me", $me); + + if(!$me->is("Admin")) { + $this->redirect("Dashboard"); + } + } + + protected function indexAction() { + $this->layout()->setTemplate("Callcenterstats/Index"); + + $identities = CallcenterIdentity::getAll(); + $this->layout()->set("identities", $identities); + + $firstOfLastMonth = new DateTime("now"); + $firstOfLastMonth->setTime(0,0,0); + $firstOfLastMonth->modify("first day of this month"); + $firstOfLastMonth->modify("-1 month"); + + $lastOfLastMonth = clone($firstOfLastMonth); + $lastOfLastMonth->modify("last day of this month"); + + $today = new DateTime("now"); + $today->setTime(23,59,59); + + $this->layout()->set("from", $firstOfLastMonth); + $this->layout()->set("to", $lastOfLastMonth); + $this->layout()->set("today", $today); + $this->layout()->set("selected_identities", []); + + if($this->request->run) { + return $this->runAction(); + } + } + + protected function runAction() { + $identities = $this->request->identities; + $from = $this->request->from; + $to = $this->request->to; + $direction = $this->request->direction; + + $from_ts = self::dateToTimestamp($from); + $to_ts = self::dateToTimestamp($to); + + $from_date = new DateTime("@".$from_ts); + $from_date->setTimezone(new DateTimeZone("Europe/Vienna")); + $to_date = new DateTime("@".$to_ts); + $to_date->setTimezone(new DateTimeZone("Europe/Vienna")); + + $this->layout()->set("from", $from_date); + $this->layout()->set("to", $to_date); + + $this->layout()->set("selected_identities", $identities); + + $calls = [ + "in" => [ + "count" => 0, + "seconds" => 0, + "billable" => 0, + "cost" => 0, + ], + "out" => [ + "count" => 0, + "seconds" => 0, + "billable" => 0, + "cost" => 0, + ], + ]; + + $perMinute = 1; + + // Eingehend + $destinations = []; + foreach($identities as $num) { + $ident = CallcenterIdentity::getFirst(["number" => $num]); + foreach($ident->inumbers as $inumber) { + $inum = $inumber->number; + $destinations[] = $inum; + $destinations[] = substr($inum, 2); + $destinations[] = "0".substr($inum, 2); + } + } + + + $sql = "SELECT * FROM VoiceCallHistory WHERE + `start` >= '".date("Y-m-d", $from_ts)."' AND `start` <= '".date("Y-m-d", $to_ts)." 23:59:59' + AND `destination` IN ('".join("','", $destinations)."') + "; + //echo "$sql\n
    "; + $res = $this->db->query($sql); + while($data = $this->db->fetch_object($res)) { + //var_dump($data); + $calls["in"]["count"]++; + $calls["in"]["seconds"] += (int)$data->duration; + + // 1 euro pro minute + $cost = (int)($data->duration / 60); + if($data->duration % 60) { + // wenn nächste Minute angefangen hat, +1 euro + $cost++; + } + //echo "cost: $cost\n
    "; + $calls["in"]["billable"] += $cost; + $calls["in"]["cost"] += $cost * $perMinute; + } + $this->layout()->set("in", $calls["in"]); + //var_dump($calls["in"]); + + + // ausgehend + $sources = []; + foreach($identities as $num) { + $ident = CallcenterIdentity::getFirst(["number" => $num]); + $sources[] = $ident->number; + $sources[] = substr($ident->number, 2); + $sources[] = "0".substr($ident->number, 2); + } + + + $sql = "SELECT * FROM VoiceCallHistory WHERE + `start` >= '".date("Y-m-d", $from_ts)."' AND `start` <= '".date("Y-m-d", $to_ts)." 23:59:59' + AND `source` IN ('".join("','", $sources)."') + "; + //echo "$sql\n
    "; + + $res = $this->db->query($sql); + while($data = $this->db->fetch_object($res)) { + $calls["out"]["count"]++; + $calls["out"]["seconds"] += $data->duration; + + // 1 euro pro minute + $cost = (int)($data->duration / 60); + if($data->duration % 60) { + // wenn nächste Minute angefangen hat, +1 euro + $cost++; + } + $calls["out"]["billable"] += $cost; + $calls["out"]["cost"] += $cost*$perMinute; + } + $this->layout()->set("out", $calls["out"]); + //var_dump($calls["out"]); + + //exit; + + + + + } + +} \ No newline at end of file diff --git a/db/migrations/20250717135228_callcenter_identity.php b/db/migrations/20250717135228_callcenter_identity.php new file mode 100644 index 000000000..b13daa243 --- /dev/null +++ b/db/migrations/20250717135228_callcenter_identity.php @@ -0,0 +1,51 @@ +getEnvironment() == "thetool") { + $ci = $this->table("CallcenterIdentity"); + $ci->addColumn("name", "string", ["null" => false, "length" => 64]); + $ci->addColumn("order", "integer", ["null" => true]); + $ci->addColumn("number", "string", ["null" => false, "length" => 64]); + $ci->addColumn("display", "string", ["null" => false, "length" => 64]); + $ci->addColumn("color", "string", ["null" => false, "length" => 64]); + $ci->addColumn("create_by", "integer", ["null" => false]); + $ci->addColumn("edit_by", "integer", ["null" => false]); + $ci->addColumn("create", "integer", ["null" => false]); + $ci->addColumn("edit", "integer", ["null" => false]); + $ci->create(); + + $ciin = $this->table("CallcenterIdentityIncomingnumber"); + $ciin->addColumn("callcenteridentity_id", "integer", ["null" => false]); + $ciin->addColumn("number", "string", ["null" => false, "length" => 64]); + $ciin->addColumn("display", "string", ["null" => false, "length" => 64]); + $ciin->addColumn("create_by", "integer", ["null" => false]); + $ciin->addColumn("edit_by", "integer", ["null" => false]); + $ciin->addColumn("create", "integer", ["null" => false]); + $ciin->addColumn("edit", "integer", ["null" => false]); + $ciin->create(); + + } + + if($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void + { + if($this->getEnvironment() == "thetool") { + $this->table("CallcenterIdentityIncomingnumber")->drop()->save(); + $this->table("CallcenterIdentity")->drop()->save(); + } + + if($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/lib/mvcfronk/mfBase/mfBaseController.php b/lib/mvcfronk/mfBase/mfBaseController.php index 29a34f171..6def4f80c 100644 --- a/lib/mvcfronk/mfBase/mfBaseController.php +++ b/lib/mvcfronk/mfBase/mfBaseController.php @@ -7,13 +7,18 @@ class mfBaseController { protected $log; protected $needlogin = false; + /** @var mfRequest */ protected $request; private $mfAction; + /** @var FronkDB */ private $mfDBI; + /** @var Layout */ private $mfLayout; private $mfMenu; private $mfUser; + /** @var mixed|null Called module (Controller) */ protected $mod; + /** @var mixed|null Called action method in Controller */ protected $action; public function __construct($params = NULL)