WIP Snopp Order

This commit is contained in:
Frank Schubert
2026-01-22 17:07:51 +01:00
parent 225e14c74d
commit 976844d623
7 changed files with 334 additions and 47 deletions

View File

@@ -587,9 +587,6 @@
<?php if($product->preorder_id): ?> <?php if($product->preorder_id): ?>
<input type="hidden" name="products[<?=$i?>][preorder_id]" value="<?=$product->preorder_id?>" /> <input type="hidden" name="products[<?=$i?>][preorder_id]" value="<?=$product->preorder_id?>" />
<?php endif; ?> <?php endif; ?>
<?php if($product->oaid): ?>
<input type="hidden" name="products[<?=$i?>][oaid]" value="<?=$product->oaid?>" />
<?php endif; ?>
<div class="col-md-1 product-<?=$i?>"> <div class="col-md-1 product-<?=$i?>">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
@@ -649,6 +646,16 @@
</div> </div>
<?php if($product->product->getAttributeValue("oaid_enabled")): ?>
<div class="row mt-1 mb-2" id="oaid-<?=$i?>-line">
<?php else: ?>
<div class="row mt-1 mb-2 hidden" id="oaid-<?=$i?>-line">
<?php endif; ?>
<div class="col-4">
<label class="form-label" for="oaid-<?=$i?>">OAID</label>
<input type="text" name="products[<?=$i?>][oaid]" id="oaid-<?=$i?>" class="form-control" value="<?=$product->oaid?>" placeholder="optional">
</div>
</div>
<?php if( <?php if(
(is_array($product->product->attributes) && count($product->product->attributes)) (is_array($product->product->attributes) && count($product->product->attributes))
&& (array_key_exists(TT_ATTRIB_TERMINATION_REQUIRED_NAME, $product->product->attributes) && (array_key_exists(TT_ATTRIB_TERMINATION_REQUIRED_NAME, $product->product->attributes)
@@ -877,6 +884,13 @@
</div> </div>
</div> </div>
<div class="row mt-1 mb-2 hidden" id="oaid-<?=$i?>-line">
<div class="col-4">
<label class="form-label" for="oaid-<?=$i?>">OAID</label>
<input type="text" name="products[<?=$i?>][oaid]" id="oaid-<?=$i?>" class="form-control" value="<?=$product->oaid?>" placeholder="optional">
</div>
</div>
<div class="row mt-1 mb-2 hidden" id="termination_id-<?=$i?>-line"> <div class="row mt-1 mb-2 hidden" id="termination_id-<?=$i?>-line">
<!-- line to choose termination --> <!-- line to choose termination -->
<div class="col-12"> <div class="col-12">
@@ -1390,7 +1404,14 @@
}); });
} else { } else {
$('#termination_id-' + id + '-line').hide(); $('#termination_id-' + id + '-line').hide();
//$('#termination_id-' + id + '-line').hide(); }
if(typeof p.attributes === 'object' && "oaid_enabled" in p.attributes && p.attributes.oaid_enabled == 1) {
console.log("oaid_enabled");
$('#oaid-' + id + '-line').show();
console.log($('#oaid-' + id).val());
} else {
$('#oaid-' + id + '-line').hide();
} }
if(typeof p.attributes === 'object' && "needs_number" in p.attributes && p.attributes.needs_number == 1) { if(typeof p.attributes === 'object' && "needs_number" in p.attributes && p.attributes.needs_number == 1) {
@@ -1980,7 +2001,14 @@
<input type="text" class="form-control" name="products[' + i +'][price_setup]" id="price_setup-' + i +'" value="" placeholder="Preis Setup" /> \ <input type="text" class="form-control" name="products[' + i +'][price_setup]" id="price_setup-' + i +'" value="" placeholder="Preis Setup" /> \
</div> \ </div> \
</div> \ </div> \
\ \
<div class="row mt-1 mb-2 hidden" id="oaid-<?=$i?>-line"> \
<div class="col-4"> \
<label class="form-label" for="oaid-<?=$i?>">OAID</label> \
<input type="text" name="products[<?=$i?>][oaid]" id="oaid-<?=$i?>" class="form-control" value="" placeholder="optional"> \
</div> \
</div> \
\
<div class="row mt-1 mb-2 hidden" id="termination_id-' + i +'-line"> \ <div class="row mt-1 mb-2 hidden" id="termination_id-' + i +'-line"> \
<!-- line to choose termination --> \ <!-- line to choose termination --> \
<div class="col-12"> \ <div class="col-12"> \

View File

@@ -290,7 +290,6 @@
<td onclick="toggleOrder(<?=$order->id?>)" class="text-monospace"><?=date("d.m.Y", $order->edit)?></td> <td onclick="toggleOrder(<?=$order->id?>)" class="text-monospace"><?=date("d.m.Y", $order->edit)?></td>
<td onclick="toggleOrder(<?=$order->id?>)" class="text-monospace"><?=$order->editor->name?></td> <td onclick="toggleOrder(<?=$order->id?>)" class="text-monospace"><?=$order->editor->name?></td>
<td style="text-align: left; letter-spacing: 4px; font-size: 1.1em;"> <td style="text-align: left; letter-spacing: 4px; font-size: 1.1em;">
<!--a href="<?=self::getUrl("Order", "createSnoppOrder", ["id" => $order->id, "filter" => $filter, "noTermProducts" => 1])?>" class="mr-2"><img src="<?=self::getResourcePath()?>img/snop-logo.png" /></a-->
<?php if($order->waiting == 1): ?> <?php if($order->waiting == 1): ?>
<a href="<?=self::getUrl("Order", "setwaiting", ["id" => $order->id, "unset" => "1", "filter" => $filter, "s" => $pagination['start']])?>"><i class="fas fa-eye-slash" title="Einblenden"></i></a> <a href="<?=self::getUrl("Order", "setwaiting", ["id" => $order->id, "unset" => "1", "filter" => $filter, "s" => $pagination['start']])?>"><i class="fas fa-eye-slash" title="Einblenden"></i></a>
<?php else: ?> <?php else: ?>
@@ -300,6 +299,10 @@
<a href="<?=self::getUrl("Order", "sendServicePin", ["id" => $order->id])?>" onclick="if(!confirm('Soll der Service-PIN an den Vertragsinhaber gesendet werden?')) return false;"><i class="fas fa-paper-plane" title="Service PIN als PDF per Email an Vertragsinhaber"></i></a> <a href="<?=self::getUrl("Order", "sendServicePin", ["id" => $order->id])?>" onclick="if(!confirm('Soll der Service-PIN an den Vertragsinhaber gesendet werden?')) return false;"><i class="fas fa-paper-plane" title="Service PIN als PDF per Email an Vertragsinhaber"></i></a>
<a href="<?=self::getUrl("Order", "edit", ["id" => $order->id, "filter" => $filter, "noTermProducts" => 1])?>"><i class="far fa-edit" title="Bearbeiten"></i></a> <a href="<?=self::getUrl("Order", "edit", ["id" => $order->id, "filter" => $filter, "noTermProducts" => 1])?>"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a href="<?=self::getUrl("Order", "delete", ["id" => $order->id])?>" onclick="if(!confirm('Bestellung wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash"></i></a> <a href="<?=self::getUrl("Order", "delete", ["id" => $order->id])?>" onclick="if(!confirm('Bestellung wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash"></i></a>
<?php if(!$order->getSnoppProduct() && ($order->getPreorderProduct() || $order->getOaidProduct())): ?>
<a href="<?=self::getUrl("Order", "createSnoppOrder", ["id" => $order->id, "filter" => $filter, "noTermProducts" => 1])?>" class="ml-2"><img src="<?=self::getResourcePath()?>img/snop-logo.png" style="width:24px;height:auto;" /></a>
<?php endif; ?>
</td> </td>
</tr> </tr>
<tr class="building-list-tr <?=($order_count % 2 == 0) ? "table-bg-even" : ""?>" id="order-dates-<?=$order->id?>"> <tr class="building-list-tr <?=($order_count % 2 == 0) ? "table-bg-even" : ""?>" id="order-dates-<?=$order->id?>">
@@ -544,7 +547,10 @@
<td class="text-right"><?=$product->pos?></td> <td class="text-right"><?=$product->pos?></td>
<td class="text-right"><?=$product->formatAmount()?></td> <td class="text-right"><?=$product->formatAmount()?></td>
<td> <td>
<?=$product->product->name?> <?php if($product->snopp_order_id): ?>
<img src="<?=self::getResourcePath()?>/img/snop-logo.png" style="width:24px;height:auto;" title="Bestellung in Snopp">
<?php endif; ?>
<?=$product->product->name?> <?=$product->oaid ? "<span class='text-pink font-italic'>{$product->oaid}</span>" : ""?>
<?php <?php
if( if(
(is_array($product->product->attributes) && count($product->product->attributes)) (is_array($product->product->attributes) && count($product->product->attributes))

View File

@@ -68,6 +68,27 @@ class Order extends mfBaseModel {
return $terminations; return $terminations;
} }
public function getSnoppProduct() {
foreach($this->getProperty("products") as $product) {
if($product->snopp_order_id) return $product;
}
return null;
}
public function getOaidProduct() {
foreach($this->getProperty("products") as $product) {
if($product->oaid) return $product;
}
return null;
}
public function getPreorderProduct() {
foreach($this->getProperty("products") as $product) {
if($product->preorder_id) return $product;
}
return null;
}
public function getShippingdate() { public function getShippingdate() {
if(!$this->id) { if(!$this->id) {
return false; return false;

View File

@@ -1373,6 +1373,141 @@ class OrderController extends mfBaseController {
$this->returnJson(["status" => "OK", "order" => ['id' => $order_id]]); $this->returnJson(["status" => "OK", "order" => ['id' => $order_id]]);
} }
protected function createSnoppOrderAction() {
$order_id = $this->request->id;
if(!$order_id || $order_id < 1) {
$this->layout()->setFlash("Bestellung nicht gefunden.", "error");
$this->redirect("Order");
}
$order = new Order($order_id);
if(!$order->id) {
$this->layout()->setFlash("Bestellung nicht gefunden.", "error");
$this->redirect("Order");
}
$order_product = false;
$product_snopp_id = false;
$products_noterm = false;
// find product
foreach($order->products as $op) {
// check for valid internet access product
if(!in_array($op->product->producttech_id, TT_PRODUCTTECH_IDS_INTERNET_ACCESSS)) {
continue;
}
if($op->oaid) {
$order_product = $op;
break;
}
if($op->preorder_id) {
$order_product = $op;
break;
}
// if product has a snopp product_id, then this must be it
if($op->product->getAttributeValue("oan_pid_snopp")) {
$order_product = $op;
break;
}
}
if(!$order_product) {
$this->layout()->setFlash("Kein für SNOPP-Bestellungen geeignetes Produkt in dieser Bestellung gefunden.", "error");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
// order in snopp
$snopp_prod_id = $op->product->getAttributeValue("oan_pid_snopp");
if(!$snopp_prod_id) {
$this->layout()->setFlash("SNOPP Product ID fehlt im Produkt (".$order_product->product->name.").", "error");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
// find snopp api credentials
$api_creds = $order_product->product->getOwnerSnoppApiCredentials();
$this->log->debug(__METHOD__.": Snopp Api Creds: ".print_r($api_creds, true));
if(!$api_creds) {
$this->layout()->setFlash("Produktbesitzer hat keinen SNOPP Api Key", "error");
$this->redirect("Order");
}
$baseurl = $api_creds["prod"]["url"];
$apikey = $api_creds["prod"]["key"];
$snopp = new Snoppapi($baseurl, $apikey);
$ext_id = false;
if($order_product->oaid) {
$ext_id = $order_product->oaid;
} elseif($order_product->preorder_id) {
$preorder = new Preorder($order_product->preorder_id);
if($preorder->id && $preorder->campaign->fulfillment == "citycom_oan") {
$ext_id = "SDIHome_xtc{$preorder->adb_wohneinheit_id}_1700000000";
} elseif($preorder->id) {
if($preorder->adb_wohneinheit->oaid) {
$ext_id = $preorder->adb_wohneinheit->oaid;
} elseif($preorder->adb_wohneinheit->extref) {
$ext_id = $preorder->adb_wohneinheit->extref;
}
}
} else {
// search for address in snopp
$search_data = [
"street" => $order->owner->street,
"zip" => $order->owner->zip,
"city" => $order->owner->city,
];
$homes = $snopp->searchAddress($search_data);
if(!$homes) {
$this->layout()->setFlash("Home in Snopp nicht gefunden", "error");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
$home = reset($homes);
$ext_id = $home->oan_id;
}
if(!$ext_id) {
$this->layout()->setFlash("Konnte keine OAID oder External ID zur Adresse finden.", "error");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
$data = [
"oan_id" => ($order_product->oaid) ?: $ext_id,
"product_id" => $snopp_prod_id,
"extref" => ($op->order->partner_number) ?: $op->order->owner->customer_number,
"execution_date" => (new DateTime("now"))->setTimezone(new DateTimeZone("Europe/Vienna"))->format("c"),
"name" => $order->owner->getCompanyOrName(),
"street" => $order->owner->street,
"zip" => $order->owner->zip,
"city" => $order->owner->city,
"phone" => $order->owner->phone,
"mobile" => $order->owner->mobile,
"email" => $order->owner->email,
];
$resp = $snopp->submitOrder($data);
if(!$resp) {
$this->layout()->setFlash("Fehler beim Bestellen im Snopp.", "error");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
if($resp->status != "Created") {
$this->layout()->setFlash("Konnte nicht bestellt werden: '{$resp->result->message}'", "error");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
$order_product->snopp_order_id = 1;
$order_product->save();
$this->layout()->setFlash("Bestellung erfolgreich in SNOPP erstellt.", "success");
$this->redirect("Order", "Index", ["id" => $order->id]);
}
protected function deleteAction() { protected function deleteAction() {
if(!$this->me->is(["Admin","salespartner"])) { if(!$this->me->is(["Admin","salespartner"])) {
$this->layout()->setFlash("Keine Berechtigung", "error"); $this->layout()->setFlash("Keine Berechtigung", "error");

View File

@@ -54,6 +54,28 @@ class Product extends mfBaseModel {
} }
public function getAttributeValue($name) {
$attributes = $this->getProperty("attributes");
if(!array_key_exists($name, $attributes) || !$attributes[$name]->value) {
return null;
}
return $attributes[$name]->value;
}
public function getOwnerSnoppApiCredentials() {
$owner = $this->getProperty("owner");
if(!$owner) return false;
foreach(TT_SNOPP_API_CREDS as $api_creds) {
if($api_creds["address_id"] == $owner->id) {
return $api_creds;
}
}
return null;
}
public function getProperty($name) { public function getProperty($name) {
if($this->$name == null) { if($this->$name == null) {

View File

@@ -1,36 +0,0 @@
<?php
/**
* Represents the SNOPP API.
*/
class SNOPP {
/**
* SNOPP constructor.
*/
public function __construct() {}
/**
* Search for support tickets using the SNOPP API.
* @return array - The search results.
*/
function getSNOPPTickets(): array {
$SNOPP_API_URL = SNOPP_API_URL;
$SNOPP_API_KEY = SNOPP_API_KEY;
$ctx_opts = [
'http' => [
'method' => 'GET',
'header' => "X-Api-Key: $SNOPP_API_KEY\r\n",
]
];
$snopp_output = file_get_contents("$SNOPP_API_URL/ticket/find?provider_id=all&status=open", false, stream_context_create($ctx_opts));
$ticket_obj = json_decode($snopp_output);
$tickets = $ticket_obj->result->tickets;
return $tickets;
}
}
?>

111
lib/Snoppapi/Snoppapi.php Normal file
View File

@@ -0,0 +1,111 @@
<?php
/**
* Represents the SNOPP API.
*/
class Snoppapi {
private $log;
private $baseurl;
private $apikey;
public function __construct($baseurl, $apikey) {
$this->baseurl = $baseurl;
$this->apikey = $apikey;
$this->log = mfLoghandler::singleton();
}
public function searchAddress(Array $address_data) {
$street = trim($address_data['street']);
$zip = trim($address_data['zip']);
$city = trim($address_data['city']);
$ctx_opts = [
'http' => [
'method' => 'GET',
'ignore_errors' => true,
'header' => [
"Accept: application/json",
"X-Api-Key: {$this->apikey}",
]
]
];
$getHomesEp = $this->baseurl.SNOPP_API_EP_GET_HOMES;
//$url = $getHomesEp."?".$qs;
$url = $getHomesEp;
$ctx = stream_context_create($ctx_opts);
$this->log->debug(__METHOD__.": Getting SNOPP homes: $url");
$response = file_get_contents($url, false, $ctx);
//var_dump($response);exit;
if($response === false) {
$this->log->error("Fehler beim Homes abfragen in SNOPP");
return false;
}
$resp_data = json_decode($response);
$homes = $resp_data->result->homes;
$results = [];
foreach($homes as $home) {
if(trim($home->street) == $street
&& trim($home->zip) == $zip
&& trim($home->city) == $city
) {
$results[] = $home;
}
}
return $results;
}
public function submitOrder(Array $order_data) {
$data = [];
foreach(["oan_id", "execution_date", "product_id", "name", "street", "zip", "city"] as $field) {
if(!array_key_exists($field, $order_data) || !$order_data[$field]) {
$this->log->error(__METHOD__.": Mandatory field '$field' missing");
return false;
}
$data[$field] = $order_data[$field];
}
foreach(["phone", "mobile", "email"] as $field) {
if(array_key_exists($field, $order_data) && $order_data[$field]) {
$data[$field] = $order_data[$field];
}
}
$ctx_opts = [
'http' => [
'method' => 'POST',
'content' => json_encode($data),
'ignore_errors' => true,
'header' => [
"Accept: application/json",
"Content-type: application/json",
"X-Api-Key: {$this->apikey}",
]
]
];
$url = $this->baseurl.SNOPP_API_EP_SUBMIT_ORDER;
$ctx = stream_context_create($ctx_opts);
$this->log->debug(__METHOD__.": Ordering Snopp product: $url\n".print_r($data, true));
$response = file_get_contents($url, false, $ctx);
$this->log->debug(__METHOD__.": ".print_r($response, true));
if($response === false) {
$this->log->error("Fehler beim Bestellen in SNOPP ".print_r($response, true));
return false;
}
$resp_data = json_decode($response);
return $resp_data;
}
}