feat: Implement invoice generation and emailing functionality

- Added invoice generation logic using DomPDF.
- Integrated invoice data retrieval from the API.
- Implemented language determination for invoices based on customer data.
- Added options to email invoices to customers and admin.
- Included HTML output option for direct viewing in the browser.
- Ensured proper redirection and error handling throughout the process.
This commit is contained in:
“VeLiTi”
2026-01-07 14:36:48 +01:00
parent 543f0b3cac
commit 08263c7933
46 changed files with 4982 additions and 151 deletions

BIN
.DS_Store vendored

Binary file not shown.

8
.gitignore vendored
View File

@@ -15,3 +15,11 @@ settings/soveliti/soveliti_settings.php
assets/database/dev_schema.sql
assets/database/migration.sql
assets/database/prod_schema.sql
migration.sql
assets/database/migration_triggers.sql
assets/database/migration_v2.sql
assets/database/migration_v3.sql
.DS_Store
api/.DS_Store
api/v1/.DS_Store
api/v2/.DS_Store

BIN
api/.DS_Store vendored

Binary file not shown.

BIN
api/v1/.DS_Store vendored

Binary file not shown.

BIN
api/v2/.DS_Store vendored

Binary file not shown.

BIN
api/v2/get/.DS_Store vendored

Binary file not shown.

View File

@@ -143,6 +143,10 @@ if(isset($get_content) && $get_content!=''){
$clause .= ' AND e.serialnumber IN (:'.$v[0].')';
}
}
elseif ($v[0] == 'validate') {
// Set validation mode flag
$validation_mode = true;
}
elseif ($v[0] == 'firmware') {
//Assets with firmaware upgrade = 0 (1=latest version, 2=No software)
$clause .= ' AND e.status != 5 AND e.sw_version_latest = 0';
@@ -161,7 +165,7 @@ if(isset($get_content) && $get_content!=''){
}
}
if ($sw_version_latest_update == 1){
if ($sw_version_latest_update == 1 || $clause == ''){
//------------------------------------------
//UPDATE SW_STATUS
//------------------------------------------
@@ -175,6 +179,10 @@ if (isset($criterias['download']) && $criterias['download'] ==''){
//Request for download
$sql = 'SELECT e.rowID as equipmentID, e.*, p.productcode, p.productname from equipment e LEFT JOIN products p ON e.productrowid = p.rowID '.$whereclause.' ORDER BY equipmentID';
}
elseif (isset($validation_mode) && $validation_mode === true) {
// Validation mode - return count only for serial validation
$sql = "SELECT count(rowID) as rowID from equipment e $whereclause";
}
elseif (isset($criterias['totals']) && $criterias['totals'] =='' && !isset($criterias['type'])){
//Request for total rows
$sql = 'SELECT count(*) as count from equipment e LEFT JOIN products p ON e.productrowid = p.rowID '.$whereclause.'';
@@ -314,7 +322,19 @@ if (debug){
//------------------------------------------
//Add paging details
//------------------------------------------
if(isset($criterias['totals']) && $criterias['totals']==''){
if (isset($validation_mode) && $validation_mode === true) {
$stmt->execute();
$messages = $stmt->fetch();
if ($messages[0] == 1) {
echo json_encode(array('SN'=> TRUE));
}
else {
echo json_encode(array('SN'=> FALSE));
}
return;
}
elseif(isset($criterias['totals']) && $criterias['totals']==''){
$stmt->execute();
$messages = $stmt->fetch();
$messages = $messages[0];

View File

@@ -136,10 +136,22 @@ else {
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Clean up nested JSON in description fields before final encoding
if (!isset($criterias['totals']) || $criterias['totals'] != '') {
foreach ($messages as &$message) {
if (isset($message['description']) && is_string($message['description'])) {
$decoded = json_decode($message['description'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$message['description'] = json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
}
}
}
//------------------------------------------
//JSON_ENCODE
//------------------------------------------
$messages = json_encode($messages, JSON_UNESCAPED_UNICODE);
$messages = json_encode($messages, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
//Send results
echo $messages;

View File

@@ -51,7 +51,7 @@ elseif (isset($criterias['list']) && $criterias['list'] =='invoice'){
//SQL for Paging
$sql = 'SELECT tx.*, txi.item_id as item_id,txi.item_price as item_price, txi.item_quantity as item_quantity, txi.item_options as item_options, p.productcode, p.productname, inv.id as invoice, inv.created as invoice_created, i.language as user_language
FROM transactions tx
left join invoice inv ON tx.id = inv.txn_id
left join invoice inv ON tx.txn_id = inv.txn_id
left join transactions_items txi ON tx.id = txi.txn_id
left join products p ON p.rowID = txi.item_id
left join identity i ON i.userkey = tx.account_id '.$whereclause;

View File

@@ -161,6 +161,7 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
$debug['decision'] = 'No active software assignments found';
}
} else {
$available_upgrades = 0;
$has_priced_options = false;
$has_latest_version_different = false;
@@ -242,6 +243,8 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
}
if ($show_version) {
$available_upgrades++;
//Check if there's a valid license for this upgrade
if ($final_price > 0 && $sw_version_license) {
//Check if the license is valid
@@ -286,23 +289,18 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
}
}
// Apply the logic:
// 1. If there are priced options -> "yes"
// 2. If no priced options but current version != latest flagged version -> "yes"
// 3. Default -> "no"
if ($has_priced_options) {
// Simple logic: if any upgrades are available to show, return "yes"
if ($available_upgrades > 0) {
$software_available = "yes";
$availability_reason = "Has priced upgrade options available";
} elseif ($has_latest_version_different) {
$software_available = "yes";
$availability_reason = "Has free latest version available";
$availability_reason = "Software upgrades available";
} else {
$software_available = "no";
$availability_reason = "No upgrades available or already on latest";
$availability_reason = "No upgrades available";
}
if (debug) {
$debug['final_decision'] = [
'available_upgrades' => $available_upgrades,
'has_priced_options' => $has_priced_options,
'has_latest_version_different' => $has_latest_version_different,
'software_available' => $software_available,

View File

@@ -133,6 +133,7 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
JOIN products_software_versions psv ON psa.software_version_id = psv.rowID
WHERE psa.product_id = ?
AND psa.status = 1
AND psv.latest = 1
AND (psv.hw_version = ? OR psv.hw_version IS NULL OR psv.hw_version = "")';
$stmt = $pdo->prepare($sql);
@@ -212,16 +213,13 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
$decision_reason = 'Skipped - is current version but no upgrades scenario';
}
} else {
//Check if this is the current version and should be shown as disabled
if ($is_current_version && $has_paid_upgrade_from_current && $version['latest'] == 1) {
//Show current version as disabled only if it's the latest AND there's a paid upgrade available
//Check if this is the current version - always show it
if ($is_current_version) {
$show_version = true;
$is_current = true;
$final_price = '0.00';
$final_currency = '';
$decision_reason = 'Showing as CURRENT - is latest version with paid upgrade available';
} else if ($is_current_version && !($has_paid_upgrade_from_current && $version['latest'] == 1)) {
$decision_reason = 'Skipped - is current version but not (latest + has_paid_upgrade)';
$decision_reason = 'Showing as CURRENT - always show current version';
} else if (!$is_current_version) {
//Check if this version is part of ANY upgrade path system (either FROM or TO)
$sql = 'SELECT COUNT(*) as path_count

BIN
api/v2/post/.DS_Store vendored

Binary file not shown.

View File

@@ -1,5 +1,6 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// History
//------------------------------------------
@@ -209,9 +210,8 @@ if (isset($post_content['sn']) && isset($post_content['payload'])){
if ($equipmentUpdate == 1){
//get HW + SW from PortalAPI
if ($post_content['type'] == 'firmware'){
$test = json_decode($post_content['payload']);
$hw_version = $test->HW;
$sw_version = $test->HEX_FW;
$hw_version = $post_content['payload']['HW'];
$sw_version = $post_content['payload']['HEX_FW'];
}
else {
//GET HW + SW from object

View File

@@ -14,7 +14,7 @@ $post_content = json_decode($input,true);
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
list($whereclause,$condition) = getWhereclauselvl2("software_upgrade_paths",$permission,$partner,'');
list($whereclause,$condition) = getWhereclauselvl2("",$permission,$partner,'');
//SET PARAMETERS FOR QUERY
$id = $post_content['rowID'] ?? ''; //check for rowID

View File

@@ -1672,7 +1672,7 @@ function overviewIndicators($warranty, $service, $sw_version, $sw_version_latest
$indicator .= '<span class="dot" style="background-color: #13b368;">F</span>';
} else {
if ($sw_version == ''){
$indicator .= '<span class="dot" style="background-color: #81848a">F</span>';
$indicator .= '<span class="dot" style="background-color: #13b368;">F</span>';
} else {
$indicator .= '<span class="dot" style="background-color: #eb8a0d;">F</span>';
}
@@ -1777,7 +1777,7 @@ function availableFirmware($sw_version,$sw_version_latest){
break;
default:
$message ='<span class="status">Unknown</span>';
$message ='<span class="status"></span>';
break;
}
@@ -5339,10 +5339,12 @@ function generateSoftwareInvoice($invoice_data, $order_id, $language = 'US') {
$customer_country = $customer['address_country'] ?? '';
// Extract transaction data
$payment_amount = $invoice_data['payment_amount'] ?? 0;
$tax_amount = $invoice_data['tax_amount'] ?? 0;
$shipping_amount = $invoice_data['shipping_amount'] ?? 0;
$discount_amount = $invoice_data['discount_amount'] ?? 0;
$pricing = $invoice_data['pricing'] ?? [];
$payment_amount = $pricing['payment_amount'] ?? $invoice_data['payment_amount'] ?? 0;
$tax_amount = $pricing['tax_total'] ?? $invoice_data['tax_amount'] ?? 0;
$shipping_amount = $pricing['shipping_total'] ?? $invoice_data['shipping_amount'] ?? 0;
$discount_amount = $pricing['discount_total'] ?? $invoice_data['discount_amount'] ?? 0;
$subtotal_amount = $pricing['subtotal'] ?? 0;
$currency = 'EUR'; // Default currency
$invoice_date = $invoice_data['invoice_created'] ?? date('Y-m-d H:i:s');
@@ -5373,6 +5375,39 @@ function generateSoftwareInvoice($invoice_data, $order_id, $language = 'US') {
'serial_number' => $serial_number,
'license_key' => $license_key
];
} elseif (isset($invoice_data['products']) && is_array($invoice_data['products'])) {
// New format with products array
$pdo = dbConnect($dbname);
foreach ($invoice_data['products'] as $product) {
$product_code = $product['productcode'] ?? null;
$product_name = $product['product_name'] ?? null;
$product_options = $product['options'] ?? [];
$product_serial = $product_options['serial_number'] ?? null;
// Handle case where productcode and product_name are empty but serial_number exists
if ((empty($product_code) || $product_code === null) &&
(empty($product_name) || $product_name === null) &&
!empty($product_serial)) {
$product_code = 'License';
$product_name = 'software license for ' . $product_serial;
}
// Get license key from database
$sql = 'SELECT license_key FROM products_software_licenses WHERE transaction_id = ? LIMIT 1';
$stmt = $pdo->prepare($sql);
$stmt->execute([$order_id]);
$license_result = $stmt->fetch(PDO::FETCH_ASSOC);
$license_key = $license_result['license_key'] ?? 'Pending';
$items[] = [
'name' => $product_name ?? 'Software Upgrade',
'quantity' => $product['quantity'] ?? 1,
'price' => $product['price'] ?? 0,
'serial_number' => $product_serial ?? 'N/A',
'license_key' => $license_key
];
}
}
// Load language translations
@@ -5402,127 +5437,319 @@ function generateSoftwareInvoice($invoice_data, $order_id, $language = 'US') {
$lbl_license_key = $translations['license_key'] ?? 'License Key';
$lbl_license_expiry = $translations['license_expiry'] ?? 'License Expiry';
// Subtotal calculation - use from pricing data or calculate from items
if ($subtotal_amount > 0) {
$subtotal = $subtotal_amount;
} else {
// Calculate from items if not provided
$subtotal = 0;
foreach ($items as $item) {
$subtotal += $item['price'] * $item['quantity'];
}
}
// Build HTML invoice
$html = '<!DOCTYPE html>
<html>
<html lang="' . strtolower($language) . '">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>' . htmlspecialchars($lbl_invoice) . ' - Total Safety Solutions</title>
<style>
body { font-family: Arial, sans-serif; font-size: 12px; color: #333; }
.invoice-header { margin-bottom: 30px; }
.invoice-title { font-size: 24px; font-weight: bold; margin-bottom: 10px; }
.invoice-info { margin-bottom: 20px; }
.customer-info { margin-bottom: 20px; background: #f5f5f5; padding: 15px; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
th { background: #4CAF50; color: white; padding: 10px; text-align: left; }
td { padding: 10px; border-bottom: 1px solid #ddd; }
.text-right { text-align: right; }
.total-row { font-weight: bold; background: #f9f9f9; }
.license-info { background: #e3f2fd; padding: 15px; margin-top: 20px; border-left: 4px solid #2196F3; }
.footer { margin-top: 40px; text-align: center; font-size: 10px; color: #666; }
@page {margin: 220px 50px; }
body {
font-family: "DejaVu Sans", Arial, sans-serif;
color: #000;
font-size: 11px;
line-height: 1.4;
margin: 0;
padding: 0;
}
#header {
position: fixed;
left: -52px;
top: -220px;
right: -50px;
height: 200px;
text-align: center;
border-radius: 5px;
}
#header img {
width: 100%;
}
#footer {
position: fixed;
left: -50px;
bottom: -250px;
right: -50px;
height: 150px;
border-radius: 5px;
}
#footer img {
width: 100%;
}
.invoice-title {
font-size: 18px;
font-weight: bold;
color: #2c5f5d;
margin-bottom: 20px;
}
.company-header {
display: table;
width: 100%;
margin-bottom: 25px;
}
.company-info, .contact-details {
display: table-cell;
vertical-align: top;
width: 50%;
}
.company-info h3 {
font-weight: bold;
margin: 0 0 5px 0;
color: #2c5f5d;
font-size: 12px;
}
.company-info p, .contact-details p {
margin: 0;
line-height: 1.2;
}
.contact-details {
text-align: right;
}
.contact-details h3 {
font-weight: bold;
margin: 0 0 5px 0;
color: #2c5f5d;
font-size: 12px;
}
.invoice-details {
display: table;
width: 100%;
margin-bottom: 25px;
}
.invoice-left, .invoice-right {
display: table-cell;
vertical-align: top;
width: 50%;
}
.invoice-right {
text-align: left;
}
.detail-row {
margin-bottom: 2px;
display: table;
width: 100%;
}
.detail-label {
display: table-cell;
font-weight: normal;
width: 140px;
padding-right: 10px;
}
.detail-value {
display: table-cell;
}
.detail-value {
display: table-cell;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
border-bottom: 1px solid #999;
}
.items-table th {
padding: 8px 6px;
font-weight: bold;
text-align: left;
font-size: 10px;
}
.items-table td {
padding: 6px;
border-bottom: 1px solid #999;
font-size: 10px;
}
.items-table .qty-col {
text-align: center;
width: 70px;
}
.items-table .price-col, .items-table .total-col {
text-align: right;
width: 80px;
}
.items-table th.qty-col {
text-align: center;
}
.items-table th.price-col, .items-table th.total-col {
text-align: right;
}
.totals-section {
float: right;
width: 250px;
margin-top: 20px;
clear: both;
}
.total-row {
display: table;
width: 100%;
margin-bottom: 3px;
}
.total-label {
display: table-cell;
text-align: left;
width: 150px;
}
.total-amount {
display: table-cell;
text-align: right;
font-weight: normal;
}
.final-total {
border-top: 1px solid #000;
padding-top: 5px;
margin-top: 8px;
}
.final-total .total-amount {
font-weight: bold;
}
</style>
</head>
<body>
<div class="invoice-header">
<div id="header">
<img src="https://'.$portalURL.'/assets/images/TSS_invoice_header.png" alt="Invoice header">
</div>
<div id="footer">
<img src="https://'.$portalURL.'/assets/images/TSS_invoice_footer.png" alt="Invoice footer">
</div>
<div class="invoice-title">' . htmlspecialchars($lbl_invoice) . '</div>
<div class="invoice-info">
<strong>' . htmlspecialchars($lbl_invoice_number) . ':</strong> ' . htmlspecialchars($order_id) . '<br>
<strong>' . htmlspecialchars($lbl_invoice_date) . ':</strong> ' . htmlspecialchars(date('Y-m-d', strtotime($invoice_date))) . '
<div class="company-header">
<div class="company-info">
<h3>Total Safety Solutions B.V.</h3>
<p>Laarakkerweg 8</p>
<p>5061 JR OISTERWIJK</p>
<p>Nederland</p>
</div>
<div class="contact-details">
</div>
</div>
<div class="customer-info">
<strong>' . htmlspecialchars($lbl_customer) . ':</strong><br>
' . htmlspecialchars($customer_name) . '<br>';
if ($customer_address) {
$html .= htmlspecialchars($customer_address) . '<br>';
}
if ($customer_city || $customer_zip) {
$html .= htmlspecialchars($customer_zip . ' ' . $customer_city) . '<br>';
}
if ($customer_state) {
$html .= htmlspecialchars($customer_state) . '<br>';
}
if ($customer_country) {
$html .= htmlspecialchars($customer_country) . '<br>';
}
$html .= htmlspecialchars($customer_email) . '
<div class="invoice-details">
<div class="invoice-left">
<div class="detail-row">
<div class="detail-label">Invoice Date</div>
<div class="detail-value">: ' . htmlspecialchars(date('d-m-Y', strtotime($invoice_date))) . '</div>
</div>
<div class="detail-row">
<div class="detail-label">Invoice Number</div>
<div class="detail-value">: ' . htmlspecialchars($order_id) . '</div>
</div>
</div>
<div class="invoice-right">
<div class="detail-row">
<div class="detail-label">Reference</div>
<div class="detail-value">: Online order</div>
</div>
<div class="detail-row">
<div class="detail-label">Order number</div>
<div class="detail-value">: ' . htmlspecialchars($order_id) . '</div>
</div>
</div>
</div>
<table>
<table class="items-table">
<thead>
<tr>
<th>' . htmlspecialchars($lbl_product) . '</th>
<th class="text-right">' . htmlspecialchars($lbl_quantity) . '</th>
<th class="text-right">' . htmlspecialchars($lbl_price) . '</th>
<th>Item code</th>
<th>Description</th>
<th class="qty-col">Quantity</th>
<th class="price-col">Price</th>
<th class="total-col">Total</th>
</tr>
</thead>
<tbody>';
foreach ($items as $item) {
$line_total = $item['price'] * $item['quantity'];
$html .= '<tr>
<td>' . htmlspecialchars($item['name']) . '</td>
<td class="text-right">' . htmlspecialchars($item['quantity']) . '</td>
<td class="text-right">' . number_format($item['price'], 2) . ' ' . htmlspecialchars($currency) . '</td>
</tr>';
<td>SOFTWARE</td>
<td>' . htmlspecialchars($item['name']);
if ($item['serial_number'] !== 'N/A') {
$html .= '<br><small>Serial: ' . htmlspecialchars($item['serial_number']) . '</small>';
}
if ($item['license_key'] !== 'Pending') {
$html .= '<br><small>License: ' . htmlspecialchars($item['license_key']) . '</small>';
}
// Subtotal
$subtotal = $payment_amount - $tax_amount - $shipping_amount + $discount_amount;
$html .= '<tr>
<td colspan="2" class="text-right"><strong>' . htmlspecialchars($lbl_subtotal) . ':</strong></td>
<td class="text-right">' . number_format($subtotal, 2) . ' ' . htmlspecialchars($currency) . '</td>
</tr>';
// Tax
if ($tax_amount > 0) {
$html .= '<tr>
<td colspan="2" class="text-right"><strong>' . htmlspecialchars($lbl_tax) . ':</strong></td>
<td class="text-right">' . number_format($tax_amount, 2) . ' ' . htmlspecialchars($currency) . '</td>
$html .= '</td>
<td class="qty-col">' . htmlspecialchars($item['quantity']) . ' </td>
<td class="price-col">€ ' . number_format($item['price'], 2) . '</td>
<td class="total-col">€ ' . number_format($line_total, 2) . '</td>
</tr>';
}
// Shipping
if ($shipping_amount > 0) {
$html .= '<tr>
<td colspan="2" class="text-right"><strong>' . htmlspecialchars($lbl_shipping) . ':</strong></td>
<td class="text-right">' . number_format($shipping_amount, 2) . ' ' . htmlspecialchars($currency) . '</td>
</tr>';
}
// Discount
if ($discount_amount > 0) {
$html .= '<tr>
<td colspan="2" class="text-right"><strong>' . htmlspecialchars($lbl_discount) . ':</strong></td>
<td class="text-right">-' . number_format($discount_amount, 2) . ' ' . htmlspecialchars($currency) . '</td>
</tr>';
}
// Total
$html .= '<tr class="total-row">
<td colspan="2" class="text-right"><strong>' . htmlspecialchars($lbl_total) . ':</strong></td>
<td class="text-right"><strong>' . number_format($payment_amount, 2) . ' ' . htmlspecialchars($currency) . '</strong></td>
</tr>';
$html .= '</tbody>
</table>';
</table>
// License information
if ($license_key && $serial_number) {
$html .= '<div class="license-info">
<strong>Software License Information:</strong><br>
<strong>' . htmlspecialchars($lbl_device_serial) . ':</strong> ' . htmlspecialchars($serial_number) . '<br>
<strong>' . htmlspecialchars($lbl_license_key) . ':</strong> ' . htmlspecialchars($license_key) . '<br>
<strong>' . htmlspecialchars($lbl_license_expiry) . ':</strong> 2099-12-31
<div class="totals-section">
<div class="total-row">
<div class="total-label">' . htmlspecialchars($lbl_subtotal) . '</div>
<div class="total-amount">€ ' . number_format($subtotal, 2) . '</div>
</div>';
if ($tax_amount > 0) {
$html .= '<div class="total-row">
<div class="total-label">' . htmlspecialchars($lbl_tax) . '</div>
<div class="total-amount">€ ' . number_format($tax_amount, 2) . '</div>
</div>';
} else {
$html .= '<div class="total-row">
<div class="total-label">VAT</div>
<div class="total-amount">included</div>
</div>';
}
$html .= '<div class="footer">
Thank you for your purchase!<br>
This invoice was generated automatically.
$html .= '<div class="total-row final-total">
<div class="total-label">' . htmlspecialchars($lbl_total) . '</div>
<div class="total-amount">€ ' . number_format($payment_amount, 2) . '</div>
</div>
</div>
</body>
</html>';

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -119,7 +119,10 @@ async function connectDevice() {
// Log connection failure details
await logCommunication(`Serial connection failed: ${error.message || 'Unknown error'}`, 'disconnected');
if (openPort = 1){
// Check for specific "No port selected" error and show user-friendly message
if (error.message && error.message.includes('No port selected by the user')) {
progressBar("100", "No device selected, please try again", "#ff6666");
} else if (openPort = 1){
closePort();
console.log("Closing port");
alert("System is still trying to close the serial port. If this message continues to come up please refresh this page.");

View File

@@ -161,7 +161,13 @@ async function connectDeviceForSoftware() {
} catch (error) {
await logCommunication(`Connection error: ${error.message}`, 'error');
progressBar("0", "Error: " + error.message, "#ff6666");
// Check for specific "No port selected" error and show user-friendly message
if (error.message && error.message.includes('No port selected by the user')) {
progressBar("100", "No device selected, please try again", "#ff6666");
} else {
progressBar("100", "Error: " + error.message, "#ff6666");
}
}
}
@@ -1193,14 +1199,50 @@ async function downloadAndInstallSoftware(option, customerData = null) {
console.log("Click the 'Install Software' button to test if upload.js can handle the file");
alert("DEBUG MODE: Download complete!\n\nBlob size: " + blob.size + " bytes\n\nClick the 'Install Software' button to test upload.js");
} else {
// PRODUCTION MODE: Show upload button and automatically trigger
// PRODUCTION MODE: Hide button and show installation in progress
document.getElementById("uploadSection").style.display = "block";
const uploadBtn = document.getElementById("uploadSoftware");
uploadBtn.style.display = "block";
uploadBtn.style.display = "none";
// Hide device version information during installation
const softwareOptions = document.getElementById("softwareOptions");
if (softwareOptions) {
softwareOptions.style.display = "none";
}
// Create installation status indicator
const installStatus = document.createElement("div");
installStatus.id = "installationStatus";
installStatus.style.cssText = `
text-align: center;
padding: 20px;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 8px;
margin: 10px 0;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
`;
installStatus.innerHTML = `
<i class="fa-solid fa-spinner fa-spin" style="font-size: 32px; color: #04AA6D; margin-bottom: 10px;"></i>
<p style="margin: 0; font-size: 18px; font-weight: 600; color: #333;">Installing Software...</p>
<p style="margin: 5px 0 0 0; color: #666; font-size: 14px;">Please keep your device connected and do not close this page</p>
`;
// Insert status before the hidden upload section
document.getElementById("uploadSection").parentNode.insertBefore(installStatus, document.getElementById("uploadSection"));
progressBar("60", "Starting automatic installation...", "#04AA6D");
// Enable the upload button and automatically click it
setTimeout(() => {
uploadBtn.disabled = false;
progressBar("60", "Ready to install, starting upload...", "#04AA6D");
// Start monitoring for completion
if (typeof startUploadMonitoring === 'function') {
startUploadMonitoring();
}
uploadBtn.click();
}, 1000);
}
} catch (error) {

View File

@@ -19,7 +19,7 @@ $color_accent = '#2FAC66'; //'#ececec';
// Database settings
//------------------------------------------
require '/var/www/vhosts/morvalwatches.com/settings/soveliti_cloud_settings.php';
require '/var/www/vhosts/morvalwatches.com/settings/bewellwell_cloud_settings.php';
//------------------------------------------
// Menusetup & settings

View File

@@ -0,0 +1,102 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Content Reset Email
//------------------------------------------
$newuser_subject = 'SoVeLiTi account created';
$newuser_header = 'Dear user';
$newuser_text = 'Your administrator has provided access to SoVeLiTi.';
$newuser_credential_text_1 = 'Your account has been created with username ';
$newuser_credential_text_2 = 'Please click the button below to complete your registration.';
$newuser_closure = 'For security reasons this link is only active for 10 minutes.';
//------------------------------------------
// Content Reset Email
//------------------------------------------
$subject = $newuser_subject;
$message = '
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>' . $subject . '</title>
<style>
@media screen and (max-width: 600px) {
.content {
width: 100% !important;
display: block !important;
padding: 10px !important;
}
.header, .body, .footer {
padding: 20px !important;
}
}
</style>
</head>
<body style="font-family: Arial, sans-serif">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 20px;">
<table class="content" width="600" border="0" cellspacing="0" cellpadding="0" style="border-collapse: collapse; border: 1px solid #cccccc;">
<!-- Header -->
<tr>
<td class="header" style="background-color:'.color.'; padding: 40px; text-align: center; color: white; font-size: 24px;">
'.site_name.'
</td>
</tr>
<!-- Body -->
<tr>
<td class="body" style="padding: 40px; text-align: left; font-size: 16px; line-height: 1.6;">
' . $newuser_header . ',
<br>
<br>
'.$newuser_text.' '.$newuser_credential_text_1.'<b>'.$post_content['username'].'</b>
<br>
<br>
'.$newuser_credential_text_2.'
</td>
</tr>
<!-- Call to action Button -->
<tr>
<td style="padding: 0px 40px 0px 40px; text-align: center;">
<!-- CTA Button -->
<table cellspacing="0" cellpadding="0" style="margin: auto;">
<tr>
<td align="center" style="background-color: '.color_accent.'; padding: 10px 20px; border-radius: 5px;">
<a href="https://' . $portalURL . '/reset.php?resetkey='.$resetkey.'" target="_blank" style="color: #ffffff; text-decoration: none; font-weight: bold;">Reset Password</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="body" style="padding: 40px; text-align: left; font-size: 16px; line-height: 1.6;">
' . $newuser_closure . '
<br>
<br>
Kind regards,
<br>
<br>
Service team
<br>
<br>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="footer" style="background: url(\'https://'.$portalURL.emaillogo.'\');background-position: center center;background-repeat:no-repeat;background-size:contain;background-color: '.color.'; padding: 40px;">
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
';

View File

@@ -0,0 +1,99 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Content Reset Email
//------------------------------------------
$changeuser_subject = 'SoVeLiTi - password reset requested';
$changeuser_header = 'Dear user';
$changeuser_text = 'A password reset has been requested for your account.';
$changeuser_credential_text_1 = 'Please click the button below to reset the password of your SoVeLiTi account.';
$changeuser_closure = 'For security reasons this link is only active for 10 minutes.';
//------------------------------------------
// Content Reset Email
//------------------------------------------
$subject = $changeuser_subject;
$message = '
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>' . $subject . '</title>
<style>
@media screen and (max-width: 600px) {
.content {
width: 100% !important;
display: block !important;
padding: 10px !important;
}
.header, .body, .footer {
padding: 20px !important;
}
}
</style>
</head>
<body style="font-family: Arial, sans-serif">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 20px;">
<table class="content" width="600" border="0" cellspacing="0" cellpadding="0" style="border-collapse: collapse; border: 1px solid #cccccc;">
<!-- Header -->
<tr>
<td class="header" style="background-color:'.color.'; padding: 40px; text-align: center; color: white; font-size: 24px;">
'.site_title.'
</td>
</tr>
<!-- Body -->
<tr>
<td class="body" style="padding: 40px; text-align: left; font-size: 16px; line-height: 1.6;">
' . $changeuser_header . ',
<br>
<br>
'.$changeuser_text.'
<br>
<br>
'.$changeuser_credential_text_1 .'
</td>
</tr>
<!-- Call to action Button -->
<tr>
<td style="padding: 0px 40px 0px 40px; text-align: center;">
<!-- CTA Button -->
<table cellspacing="0" cellpadding="0" style="margin: auto;">
<tr>
<td align="center" style="background-color: '.color_accent.'; padding: 10px 20px; border-radius: 5px;">
<a href="https://' . $portalURL . '/reset.php?resetkey='.$resetkey.'" target="_blank" style="color: #ffffff; text-decoration: none; font-weight: bold;">Reset Password</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="body" style="padding: 40px; text-align: left; font-size: 16px; line-height: 1.6;">
' . $changeuser_closure . '
<br>
<br>
Kind regards,
<br>
<br>
Service team
<br>
<br>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="footer" style="background: url(\'https://'.$portalURL.emaillogo.'\');background-position: center center;background-repeat:no-repeat;background-size:contain;background-color: '.color.'; padding: 40px;">
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
';

View File

@@ -0,0 +1,55 @@
<?php
require 'settingsprofiles.php';
// This will change the title on the website
define('site_name','');
define('site_name_footer','SoVeLiTi Cloud');
// This will change the title on browser TAB
define('site_title','SoVeLiTi');
//Scriptversion
define('script_version','v1');
//Enable VeLiTi-issue mgt
define('veliti_cim',true);
//Enable VeLiTi-analytics
define('veliti_analytics',false);
//Rewrite rule
define('rewrite_url',true);
define('news','');
//maintenance_mode
define('maintenance_mode_communication',false);
define('maintenance_mode_notification','Notice: Portal not accessible due to maintenance on May 15th between 12.00 - 13.00 CET.');
define('maintenance_mode',false);
define('maintenance_mode_text','System in maintenance');
define('debug',true);
define('debug_id','1');
/*Security*/
// Page security
define('page_security_key','secure_admin_342642');
define('cronjob_number','25');
define('header_security',false);
/* Email */
// The from email that will appear on the customer's order details email
define('mail_from','SoVeLiTi');
// Your email
define('email','CustomerPortal@veliti.nl');
//Additional phpmailer-settings
define('email_host_name','veliti.nl');
define('email_reply_to','info@gewoonlekkerspaans.nl');
define('email_outgoing_pw','306yc%X5f');
define('email_outgoing_port','587');
define('email_outgoing_security','ssl');
/*Appearance*/
//Icon
define('icon_image','/custom/soveliti/style/VeLiTi-Logo2.png');
define('color','#527ee5');
define('color_accent','#527ee5');
define('emaillogo','/custom/soveliti/style/SoVeLiTi.png');
/*Default Users*/
define('software_update_user','EMP-updater');
define('software_update_pw','EMP-updater');
define('interface_user','interface@test.nl');
define('interface_pw','test1234');

View File

@@ -0,0 +1,112 @@
<?php
//------------------------------------------
//EXCEPTION LIST
//------------------------------------------
$serialnumber_exceptions = array("22050695","22110095");
//------------------------------------------
// Security
//------------------------------------------
$security_key = 'secure_34563$52';
//------------------------------------------
// Base color
//------------------------------------------
$color = '#005655';//'#0b1054';
$color_accent = '#2FAC66'; //'#ececec';
//------------------------------------------
// Database settings
//------------------------------------------
require '/var/www/vhosts/veliti.nl/settings/morvalwatches_cloud_settings.php';
//------------------------------------------
// Menusetup & settings
//------------------------------------------
require 'settingsmenu.php';
//------------------------------------------
// API BaseUrl
//------------------------------------------
$baseurl = 'https://'.$_SERVER['SERVER_NAME'].'/api.php'; //URL of API
$portalURL = $_SERVER['SERVER_NAME'];
//------------------------------------------
// Equipmentdetails
//------------------------------------------
$servicedate = date("Y-m-d", strtotime("-365 days"));
$warrantydate = date("Y-m-d", strtotime("-365 days"));
$warranty_extended = date("Y-m-d", strtotime("+365 days"));
$date = date('Y-m-d H:i:s');
$curYear = date("Y", time());
$curMonth = date("m", time());
$curQuarter = (int)ceil($curMonth / 3);
$curdateObj = DateTime::createFromFormat('!m', $curMonth);
$curMonth_name = $curdateObj->format('F');
//------------------------------------------
//History Type
//------------------------------------------
$type1 = 'General';
$type2 = 'Customer';
$type3 = 'Service';
$type4 = 'Testing';
$type5 = 'Data';
$type6 = 'Other';
$type7 = 'Internal';
$type8 = 'Ignore';
$type9 = 'Warranty';
$type10 = 'Contract';
$type11 = 'Warranty-Expired';
$type12 = 'Contract-Expired';
$type13 = "Order";
$type14 = "ServiceReport";
$type15 = "SRIncluded";
$type16 = "Notes";
$type17 = "Visual";
$HistoryType_1 = 'Bootloader';
$HistoryType_2 = 'Firmware';
$HistoryType_3 = 'SerialNumber';
$HistoryType_4 = 'Visual_Test';
$HistoryType_5 = 'Maintenance_Test';
$HistoryType_6 = 'Assembly_Test';
$HistoryType_7 = 'ProductNumber';
$HistoryType_8 = 'Visual';
$HistoryType_9 = 'ServiceReport';
//------------------------------------------
//Permissions CRUD
//------------------------------------------
$permission_4 = 'CRUD'; //Admin+
$permission_3 = 'CRUD'; //Admin
$permission_2 = 'CRU'; //SuperUser
$permission_1 = 'CRU'; //CreateUpdate
$permission_0 = 'R'; //Readonly
$permissionlabel1 = 'Permission';
$permission1 = 'Superuser'; #1
$permission2 = 'Create & Update'; #2
$permission3 = 'read-only'; // #3
$permission4 = 'Admin'; //#4
$permission5 = 'Admin+'; // #5
$settingslabel1 = 'profile';
$setting1 = 'firmware'; //Fix
$setting2 = 'service';
$setting3 = 'build'; //Fix
$setting4 = 'distribution';
$setting5 = '';
$setting6 = '';
$setting7 = ''; //Fix
$setting8 = 'interface';
//------------------------------------------
//Partners
//------------------------------------------
$partnertype1 = 'SalesID';
$partnertype2 = 'SoldTo';
$partnertype3 = 'ShipTo';
$partnertype4 = 'Location';
$partnertype5 = 'Section';

View File

@@ -0,0 +1,365 @@
<?php
//------------------------------------------
// Menusetup and settings
//
//
// Each Menu name in urls array requires reference
// into translation files and corresponding profile
//------------------------------------------
$main_menu = [
"dashboard" => [
"main_menu" => [
"url" => "dashboard",
"selected" => "dashboard",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_dashboard"
]
],
"sales" => [
"main_menu" => [
"url" => "contracts",
"selected" => "contracts",
"icon" => "fa-solid fa-bars",
"name" => "menu_sales"
],
"accounts" => [
"url" => "accounts",
"selected" => "accounts",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_sales_accounts"
],
"catalog" => [
"url" => "catalog",
"selected" => "catalog",
"icon" => "fa-solid fa-photo-film",
"name" => "menu_catalog"
],
"contracts" => [
"url" => "contracts",
"selected" => "contracts",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_sales_contracts"
],
"orders" => [
"url" => "orders",
"selected" => "orders",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_sales_orders"
],
"identity" => [
"url" => "identity",
"selected" => "identity",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_identity"
]
],
"dealers" => [
"main_menu" => [
"url" => "dealers",
"selected" => "dealers",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_dealers"
]
],
"buildtool" => [
"main_menu" => [
"url" => "buildtool",
"selected" => "buildtool",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_build"
]
],
"cartests" => [
"main_menu" => [
"url" => "cartests",
"selected" => "cartests",
"icon" => "fa-solid fa-car",
"name" => "menu_cartest"
]
],
"marketing" => [
"main_menu" => [
"url" => "marketing&product_group=Emergency_Plug&product_content=Images",
"selected" => "marketing",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_marketing"
]
],
"equipments" => [
"main_menu" => [
"url" => "equipments",
"selected" => "assets",
"icon" => "fa-solid fa-database",
"name" => "menu_assets"
],
"equipments" =>[
"url" => "equipments",
"selected" => "assets",
"icon" => "fa-solid fa-database",
"name" => "menu_assets"
],
"servicereports" => [
"url" => "servicereports",
"selected" => "servicereports",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_service_reports"
],
"rmas" => [
"url" => "rmas",
"selected" => "rmas",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_rmas"
],
"histories" => [
"url" => "histories",
"selected" => "histories",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_history"
],
"firmwaretool" => [
"url" => "firmwaretool",
"selected" => "firmwaretool",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_firmwaretool"
] ,
"equipments_mass_update" => [
"url" => "equipments_mass_update",
"selected" => "equipments_mass_update",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_equipments_mass_update"
]
],
"products" => [
"main_menu" => [
"url" => "products&status=1",
"selected" => "products",
"icon" => "fas fa-box-open",
"name" => "menu_products"
],
"products" => [
"url" => "products&status=1",
"selected" => "products",
"icon" => "fas fa-box-open",
"name" => "menu_products"
],
"products_software" => [
"url" => "products_software_versions",
"selected" => "products_software_versions",
"icon" => "fas fa-box-open",
"name" => "menu_products_software_versions"
],
"products_attributes" => [
"url" => "products_attributes",
"selected" => "products_attributes",
"icon" => "fas fa-box-open",
"name" => "menu_products_attributes"
],
"pricelists" => [
"url" => "pricelists",
"selected" => "pricelists",
"icon" => "fa-solid fa-coins",
"name" => "menu_pricelists"
]
],
"reporting" => [
"main_menu" => [
"url" => "report_build",
"selected" => "report_build",
"icon" => "fa-solid fa-magnifying-glass-chart",
"name" => "menu_report_main"
],
"report_build" => [
"url" => "report_build",
"selected" => "report_build",
"icon" => "fa-solid fa-magnifying-glass-chart",
"name" => "menu_report_build"
],
"report_contracts_billing" => [
"url" => "report_contracts_billing",
"selected" => "report_contracts_billing",
"icon" => "fa-solid fa-magnifying-glass-chart",
"name" => "menu_report_contracts_billing"
],
"report_healthindex" => [
"url" => "report_healthindex",
"selected" => "report_healthindex",
"icon" => "fa-solid fa-magnifying-glass-chart",
"name" => "menu_report_healthindex"
],
"report_usage" => [
"url" => "report_usage",
"selected" => "report_usage",
"icon" => "fa-solid fa-magnifying-glass-chart",
"name" => "menu_report_usage"
]
],
"admin" =>[
"main_menu" => [
"url" => "partners",
"selected" => "partners",
"icon" => "fa-solid fa-bars",
"name" => "menu_admin"
],
"partners" => [
"url" => "partners",
"selected" => "partners",
"icon" => "fa-solid fa-bars",
"name" => "menu_admin_partners"
],
"users" => [
"url" => "users",
"selected" => "users",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_admin_users"
],
"communications" => [
"url" => "communications",
"selected" => "communications",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_admin_communications"
],
"media" => [
"url" => "media",
"selected" => "media",
"icon" => "fa-solid fa-photo-film",
"name" => "menu_media"
],
"categories" => [
"url" => "categories",
"selected" => "categories",
"icon" => "fa-solid fa-photo-film",
"name" => "menu_categories"
],
"discounts" => [
"url" => "discounts",
"selected" => "discounts",
"icon" => "fa-solid fa-photo-film",
"name" => "menu_discounts"
],
"shipping" => [
"url" => "shipping",
"selected" => "shipping",
"icon" => "fa-solid fa-truck-fast",
"name" => "menu_shipping"
]
],
"settings" => [
"main_menu" => [
"url" => "settings",
"selected" => "settings",
"icon" => "fas fa-tools",
"name" => "menu_settings"
],
"config" => [
"url" => "settings",
"selected" => "settings",
"icon" => "fas fa-tools",
"name" => "menu_config"
],
"translations" => [
"url" => "translations",
"selected" => "translations",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_translations"
],
"uploader" => [
"url" => "uploader",
"selected" => "uploader",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_uploader"
],
"logfile" => [
"url" => "logfile",
"selected" => "logfile",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_logfile"
],
"maintenance" => [
"url" => "maintenance",
"selected" => "maintenance",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_maintenance"
],
"profiles" => [
"url" => "profiles",
"selected" => "profiles",
"icon" => "fas fa-tachometer-alt",
"name" => "menu_profiles"
]
]
];
$routes = array(
'/' => 'equipments.php',
'equipments' => 'equipments.php',
'products' => 'products.php',
'logout' => 'logout.php'
);
//------------------------------------------
// Paging
//------------------------------------------
$page_rows_equipment = 25; //list Equipment
$page_rows_history = 15; //list History
$page_rows_products = 25;//list producst
$page_rows_users = 15;//list users
$page_rows_partners = 15;//list partners
$page_rows_communication = 25; //list communications
$page_rows_accounts = 25 ;// list accounts
$page_rows_contracts = 25 ;// list contracts
$page_rows_cartest = 25 ;// list contracts
$page_rows_equipment_servicereporst = 5 ;// Number of service reports on equipment
$page_rows_changelog = 50 ;// Number of changelogs returned
$page_rows_rma = 25; // list RMA
$page_rows_translations = 50; //list translation variables
$page_rows_products_attributes = 50; //list product attributes
$page_rows_media = 25; // list media
$page_rows_pricelists = 50;//pricelists
$page_rows_categories = 25;//categories
$page_rows_discounts = 25;//discounts
$page_rows_shipping = 25;//discounts
$page_rows_transactions = 25; //transactions
$page_rows_invoice = 25; //invoices
$page_rows_dealers = 25; //dealers
$page_rows_software_versions = 50; //software versions
//------------------------------------------
// Languages supported
//------------------------------------------
$supportedLanguages = ['US', 'NL', 'DE', 'ES','PT'];
//------------------------------------------
// Pricing
//------------------------------------------
$supportedCurrencies = ["0" =>"euro", "1"=>"dollar"];
$supportedModifiers = ["0" =>"subtract", "1"=>"add"];
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// All individual views and APIs - Profile ++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
$all_profiles = [
"build",
"commerce",
"distribution",
"firmware",
"garage",
"interface",
"service",
"other"
];
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Marketing +++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
$main_marketing_dir = './marketing/';
$marketing_structure = array(
"Emergency_Plug" => array(
"Documents",
"Images",
"Video"
)
);

View File

@@ -0,0 +1,27 @@
<?php
/*Standard*/
define('standard_profile','dashboard,profile,equipments,equipment,histories,history,firmwaretool,products_software,servicereports,servicereport,application');
/*Superuser*/
define('superuser_profile','dashboard,profile,assets,equipments,equipment,equipment_manage,equipment_manage_edit,equipments_mass_update,histories,history,history_manage,firmwaretool,products,products_versions,products_software,product,product_manage,servicereports,servicereport,admin,partners,partner,users,user,user_manage,marketing,application');
/*Admin*/
define('admin_profile','dashboard,profile,buildtool,sales,accounts,account,contracts,contract,contract_manage,cartests,cartest,cartest_manage,assets,equipments,equipment,equipment_healthindex,equipment_data,equipment_manage,equipment_manage_edit,equipments_mass_update,histories,history,history_manage,firmwaretool,rmas,rma,rma_manage,rma_history,rma_history_manage,buildtool,products,products_versions,products_software,product,product_manage,servicereports,servicereport,admin,partners,partner,users,user,user_manage,communications,communication,communication_send,marketing,reporting,report_build,report_contracts_billing,report_healthindex,changelog,application');
/*AdminPlus*/
define('adminplus_profile','dashboard,profile,buildtool,sales,accounts,account,contracts,contract,contract_manage,billing,cartests,cartest,cartest_manage,assets,equipments,equipment,equipment_healthindex,equipment_data,equipment_manage,equipment_manage_edit,equipments_mass_update,histories,history,history_manage,firmwaretool,rmas,rma,rma_manage,rma_history,rma_history_manage,buildtool,products,products_versions,products_software,products_attributes,products_attributes_items,products_attributes_manage,products_configurations,products_categories,products_media,product,product_manage,pricelists,pricelists_items,pricelists_manage,catalog,categories,category,discounts,discount,shipping,shipping_manage,servicereports,servicereport,admin,partners,partner,users,user,user_manage,communications,communication,communication_send,marketing,reporting,report_build,report_contracts_billing,report_healthindex,report_usage,config,settings,logfile,changelog,language,translations,translations_details,translation_manage,media,media_manage,application,maintenance,uploader,profiles,vin,shopping_cart,checkout,placeorder,taxes,transactions,transactions_items,invoice,order,orders,identity');
/*Build*/
define('build','dashboard,profile,buildtool,firmwaretool,buildtool,products_software,application');
/*Commerce*/
define('commerce','dashboard,profile,products,products_versions,products_software,products_attributes,products_attributes_items,products_attributes_manage,products_configurations,products_categories,products_media,product,product_manage,pricelists,pricelists_items,pricelists_manage,catalog,categories,category,discounts,discount,shipping,shipping_manage,admin,partners,partner,users,user,user_manage,translations,translations_details,translation_manage,media,media_manage,application,uploader,shopping_cart,checkout,placeorder,taxes,transactions,transactions_items,invoice,order,orders,identity');
/*Distribution*/
define('distribution','dashboard,profile,assets,equipments,equipment,equipment_manage,equipment_manage_edit,equipments_mass_update,histories,history,history_manage,firmwaretool,products,products_versions,products_software,product,product_manage,servicereports,servicereport,admin,partners,partner,users,user,user_manage,marketing,application');
/*Firmware*/
define('firmware','firmwaretool,products_software,application');
/*Garage*/
define('garage','dashboard,profile,cartests,cartest,cartest_manage,products_versions,application');
/*Interface*/
define('interface','contracts,contract,equipments,equipment_manage,firmwaretool,products_versions,products_software,users,application');
/*Service*/
define('service','dashboard,profile,assets,equipments,equipment,equipment_manage,histories,history,history_manage,firmwaretool,products_software,servicereports,servicereport,admin,partners,partner,users,user,user_manage,marketing,application');
/*Other*/
define('other','dashboard,profile,assets,equipments,equipment,equipment_manage,histories,history,history_manage,firmwaretool,products_software,servicereports,servicereport,admin,partners,partner,marketing,application');
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1,231 @@
:root {
--color-white: #FFFFFF;
--color-light-blue: #344fd5c2;
--color-blue: #0b1054;
--color-red: #a75151;
--text-color: #333333;
--error-background: #f3c3c3;
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Open Sans", Helvetica, sans-serif;
accent-color: var(--color-blue);
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: var(--color-white);
padding: 20px;
}
.login-container {
display: flex;
width: 100%;
max-width: 1200px;
height: calc(100vh - 40px);
background-color: var(--color-white);
border-radius: 16px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
overflow: hidden;
}
.login-form {
width: 45%;
padding: 40px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.logo {
position: absolute;
top: 20px;
left: 20px;
background-image: url(VeLiTi-Logo2.png);
background-repeat: no-repeat;
opacity: inherit;
width: 85px;
height: 120px;
margin: 0 auto;
-webkit-filter: drop-shadow(5px 5px 5px #222);
filter: drop-shadow(5px 5px 5px #222);
}
.login-visual {
width: 55%;
position: relative;
overflow: hidden;
background-image: url(veliti_intro.png);
background-position: center center;
background-size: 100% 100%;
background-color: var(--color-light-blue);
background-repeat: no-repeat;
}
.login-visual img {
width: 100%;
height: 100%;
object-fit: cover;
}
.register-link {
position: absolute;
top: 20px;
right: 20px;
text-decoration: none;
color: var(--text-color);
}
.header {
margin-bottom: 30px;
}
.header h1 {
font-size: 24px;
margin-bottom: 10px;
}
.header p {
color: #666;
}
.input-group {
margin-bottom: 15px;
position: relative;
}
.input-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.input-group input[type="email"] {
padding-left: 40px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>') no-repeat left 10px center;
}
.input-group input[type="password"] {
padding-left: 40px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>') no-repeat left 10px center;
}
.forgot-password {
color: var(--text-color);
text-decoration: none;
text-align: right;
margin-top: 5px;
float: right;
font-size: 12px;
}
.remember-me {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.remember-me input {
margin-right: 10px;
}
.maintenance {
padding: 5px;
}
.message p {
margin-top: 5px;
background-color: var(--error-background);
border-left: 4px solid var(--color-red);
color: var(--color-red);
padding: 5px;
border-radius: 4px;
text-align: center;
}
.login-btn {
width: 100%;
padding: 12px;
background-color: var(--color-light-blue);
color: var(--color-white);
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.login-btn:hover {
background-color: var(--color-blue);
}
.trademark {
position: absolute;
bottom: 20px;
left: 20px;
color: var(--text-color);
font-size: 12px;
}
.language-selector {
position: absolute;
bottom: 20px;
right: 20px;
display: flex;
align-items: center;
color: var(--text-color);
}
.language-selector select {
margin-left: 10px;
border: none;
background: transparent;
}
/* Responsive Design */
@media screen and (max-width: 1024px) {
.login-container {
flex-direction: column;
height: auto;
max-width: 500px;
}
.login-form, .login-visual {
width: 100%;
height: auto;
}
.login-visual {
height: 300px;
}
.logo {
position: static;
margin: 20px auto;
margin-bottom: 0;
}
.trademark, .language-selector {
position: static;
text-align: center;
margin: 20px 0;
}
}
@media screen and (max-width: 480px) {
.login-form {
padding: 20px;
}
.header h1 {
font-size: 20px;
}
}

View File

@@ -0,0 +1,231 @@
:root {
--color-white: #FFFFFF;
--color-light-blue: #344fd5c2;
--color-blue: #0b1054;
--color-red: #a75151;
--text-color: #333333;
--error-background: #f3c3c3;
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Open Sans", Helvetica, sans-serif;
accent-color: var(--color-blue);
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: var(--color-white);
padding: 20px;
}
.login-container {
display: flex;
width: 100%;
max-width: 1200px;
height: calc(100vh - 40px);
background-color: var(--color-white);
border-radius: 16px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
overflow: hidden;
}
.login-form {
width: 45%;
padding: 40px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.logo {
position: absolute;
top: 20px;
left: 20px;
background-image: url(VeLiTi-Logo2.png);
background-repeat: no-repeat;
opacity: inherit;
width: 85px;
height: 120px;
margin: 0 auto;
-webkit-filter: drop-shadow(5px 5px 5px #222);
filter: drop-shadow(5px 5px 5px #222);
}
.login-visual {
width: 55%;
position: relative;
overflow: hidden;
background-image: url(veliti_intro.png);
background-position: center center;
background-size: 100% 100%;
background-color: var(--color-light-blue);
background-repeat: no-repeat;
}
.login-visual img {
width: 100%;
height: 100%;
object-fit: cover;
}
.register-link {
position: absolute;
top: 20px;
right: 20px;
text-decoration: none;
color: var(--text-color);
}
.header {
margin-bottom: 30px;
}
.header h1 {
font-size: 24px;
margin-bottom: 10px;
}
.header p {
color: #666;
}
.input-group {
margin-bottom: 15px;
position: relative;
}
.input-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.input-group input[type="email"] {
padding-left: 40px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>') no-repeat left 10px center;
}
.input-group input[type="password"] {
padding-left: 40px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>') no-repeat left 10px center;
}
.forgot-password {
color: var(--text-color);
text-decoration: none;
text-align: right;
margin-top: 5px;
float: right;
font-size: 12px;
}
.remember-me {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.remember-me input {
margin-right: 10px;
}
.maintenance {
padding: 5px;
}
.message p {
margin-top: 5px;
background-color: var(--error-background);
border-left: 4px solid var(--color-red);
color: var(--color-red);
padding: 5px;
border-radius: 4px;
text-align: center;
}
.login-btn {
width: 100%;
padding: 12px;
background-color: var(--color-light-blue);
color: var(--color-white);
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.login-btn:hover {
background-color: var(--color-blue);
}
.trademark {
position: absolute;
bottom: 20px;
left: 20px;
color: var(--text-color);
font-size: 12px;
}
.language-selector {
position: absolute;
bottom: 20px;
right: 20px;
display: flex;
align-items: center;
color: var(--text-color);
}
.language-selector select {
margin-left: 10px;
border: none;
background: transparent;
}
/* Responsive Design */
@media screen and (max-width: 1024px) {
.login-container {
flex-direction: column;
height: auto;
max-width: 500px;
}
.login-form, .login-visual {
width: 100%;
height: auto;
}
.login-visual {
height: 300px;
}
.logo {
position: static;
margin: 20px auto;
margin-bottom: 0;
}
.trademark, .language-selector {
position: static;
text-align: center;
margin: 20px 0;
}
}
@media screen and (max-width: 480px) {
.login-form {
padding: 20px;
}
.header h1 {
font-size: 20px;
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

151
factuur.php Normal file
View File

@@ -0,0 +1,151 @@
<?php
defined(page_security_key) or exit;
if (debug && debug_id == $_SESSION['id']) {
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
}
include_once './assets/functions.php';
include_once './settings/settings_redirector.php';
// Check if allowed
if (isAllowed('order', $_SESSION['profile'], $_SESSION['permission'], 'R') === 0) {
header('location: index.php');
exit;
}
// Get transaction ID from POST or GET (for HTML output)
$order_number = $_POST['txn_id'] ?? $_GET['txn_id'] ?? '';
if (empty($order_number)) {
header('Location: index.php?page=orders');
exit;
}
// Connect to database
$pdo = dbConnect($dbname);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Get detailed invoice data
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
$api_url = '/v2/invoice/list=invoice&txn_id=' . $order_number;
$invoice_data = ioServer($api_url, '');
$invoice_data = json_decode($invoice_data, true);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Determine invoice language
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
$available_languages = [
'NL' => 'NL',
'BE' => 'NL',
'US' => 'US',
'GB' => 'US',
'DE' => 'DE',
'FR' => 'FR',
'ES' => 'ES'
];
$invoice_language = 'US'; // Default
if (!empty($invoice_data['customer']['language'])) {
$invoice_language = strtoupper($invoice_data['customer']['language']);
} elseif (!empty($invoice_data['customer']['country']) && isset($available_languages[strtoupper($invoice_data['customer']['country'])])) {
$invoice_language = $available_languages[strtoupper($invoice_data['customer']['country'])];
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generate invoice HTML
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
var_dump($invoice_data);
list($data, $customer_email, $order_id) = generateSoftwareInvoice($invoice_data, $order_number, $invoice_language);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Check for HTML output request
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
if (isset($_GET['output']) && $_GET['output'] === 'html') {
// Output HTML directly to browser
echo $data;
exit;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Initialize DomPDF
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
use Dompdf\Dompdf;
use Dompdf\Options;
$options = new Options();
$options->set('isRemoteEnabled', true);
$dompdf = new Dompdf($options);
// Load HTML content
$dompdf->loadHtml($data);
// Setup paper size and orientation
$dompdf->setPaper('A4', 'portrait');
// Render the HTML as PDF
$dompdf->render();
$file_name = 'Factuur - ' . $order_id;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Handle different actions
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Email invoice to customer
if (isset($_POST['email_invoice'])) {
$to = $customer_email;
$subject = 'Factuur - ' . $order_id;
$message = $data;
$attachment = $dompdf->output();
$attachment_name = $file_name . '.pdf';
$header_redirect = 'Location: index.php?page=order&id=' . $order_id . '&success=invoice_sent';
// Send to PHPMailer
send_mail($to, $subject, $message, $attachment, $attachment_name);
header($header_redirect);
exit;
}
// Email invoice to admin/bookkeeping
if (isset($_POST['email_invoice_to_admin'])) {
$to = $customer_email;
$subject = 'Factuur - ' . $order_id;
$message = $data;
$attachment = $dompdf->output();
$attachment_name = $file_name . '.pdf';
$header_redirect = 'Location: index.php?page=order&id=' . $order_id . '&success=invoice_sent_admin';
// Send to bookkeeping if configured
if (defined('invoice_bookkeeping') && invoice_bookkeeping && defined('email_bookkeeping') && email_bookkeeping) {
send_mail(email_bookkeeping, $subject, $message, $attachment, $attachment_name);
}
header($header_redirect);
exit;
}
// Show invoice in browser
if (isset($_POST['show_invoice'])) {
// Clean output buffer to prevent corrupted PDF
if (ob_get_level()) {
ob_end_clean();
}
// Stream PDF to browser
$dompdf->stream($file_name . ".pdf", array("Attachment" => false));
exit;
}
// If no action specified, redirect back
header('Location: index.php?page=order&id=' . $order_number);
exit;
?>

View File

@@ -30,16 +30,16 @@ $url = 'index.php?page=histories'.$status.$search;
//GET Details from URL
$GET_VALUES = urlGETdetails($_GET) ?? '';
//CALL TO API
$api_url = '/v1/history/'.$GET_VALUES;
$api_url = '/v2/history/'.$GET_VALUES;
$responses = ioServer($api_url,'');
//Decode Payload
if (!empty($responses)){$responses = decode_payload($responses);}else{$responses = null;}
if (!empty($responses)){$responses = json_decode($responses);}else{$responses = null;}
//Return QueryTotal from API
$api_url = '/v1/history/'.$GET_VALUES.'&totals=';
$api_url = '/v2/history/'.$GET_VALUES.'&totals=';
$query_total = ioServer($api_url,'');
//Decode Payload
if (!empty($query_total)){$query_total = decode_payload($query_total);}else{$query_total = null;}
if (!empty($query_total)){$query_total = json_decode($query_total);}else{$query_total = null;}
template_header('Histories', 'histories','view');
$view = '
@@ -116,7 +116,7 @@ $view .= '
foreach ($responses as $response){
$description = json_decode($response->description) ?? $response->description;
$description = json_encode($description, JSON_PRETTY_PRINT);
$description = json_encode($description, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
//Replace JSON ITEMS
$search = ['{', '}', '"',':','[',']',','];
$replace = ['', '','','=','','',''];

View File

@@ -33,16 +33,16 @@ $url = 'index.php?page=history&equipmentID='.$_GET['equipmentID'].$status.$searc
//GET Details from URL
$GET_VALUES = urlGETdetails($_GET) ?? '';
//CALL TO API
$api_url = '/v1/history/'.$GET_VALUES;
$api_url = '/v2/history/'.$GET_VALUES;
$responses = ioServer($api_url,'');
//Decode Payload
if (!empty($responses)){$responses = decode_payload($responses);}else{$responses = null;}
if (!empty($responses)){$responses = json_decode($responses);}else{$responses = null;}
//Return QueryTotal from API
$api_url = '/v1/history/'.$GET_VALUES.'&totals=';
$api_url = '/v2/history/'.$GET_VALUES.'&totals=';
$query_total = ioServer($api_url,'');
//Decode Payload
if (!empty($query_total)){$query_total = decode_payload($query_total);}else{$query_total = null;}
if (!empty($query_total)){$query_total = json_decode($query_total);}else{$query_total = null;}
// Handle success messages
@@ -137,7 +137,7 @@ $view .= '
foreach ($responses as $response){
$description = json_decode($response->description) ?? $response->description;
$description = json_encode($description, JSON_PRETTY_PRINT);
$description = json_encode($description, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
//Replace JSON ITEMS
$search = ['{', '}', '"',':','[',']',','];

View File

@@ -34,10 +34,10 @@ if ($rowID !=''){
if (isset($_GET['rowID'])) {
// ID param exists, edit an existing product
//CALL TO API
$api_url = '/v1/history/historyid='.$rowID;
$api_url = '/v2/history/historyid='.$rowID;
$responses = ioServer($api_url,'');
//Decode Payload
if (!empty($responses)){$responses = decode_payload($responses);}else{$responses = null;}
if (!empty($responses)){$responses = json_decode($responses);}else{$responses = null;}
$history = json_decode(json_encode($responses[0]), true);

View File

@@ -319,6 +319,21 @@ $firmwaretool_step_5 = 'Wenn Firmware verfügbar ist: Die Statusleiste zeigt "<i
$firmwaretool_step_6 = 'Wenn Firmware verfügbar ist: Stellen Sie sicher, dass SN und HW vom Gerät gelesen werden und bestätigen Sie dies, indem Sie das Kontrollkästchen "Ich bestätige, dass SN und HW vom Gerät gelesen wurden" auswählen';
$firmwaretool_step_7 = 'Drücken Sie die <i>"Firmware aktualisieren"</i>-Schaltfläche, um den Firmware-Aktualisierungsdialog zu starten und folgen Sie den Anweisungen auf dem Bildschirm';
$firmwaretool_step_8 = '<b>Hinweis: Dieser Prozess kann nicht gestoppt werden und muss abgeschlossen werden.</b>';
$softwaretool_h2 = 'Software-Upgrade-Tool';
$softwaretool_p = 'Software-Upgrade-Optionen.';
$softwaretool_step = 'Anweisungen';
$softwaretool_step_1 = 'Verbinden Sie das Gerät über USB mit dem Computer. (USB befindet sich unter der Batterieabdeckung)';
$softwaretool_step_2 = 'Drücken Sie die "<i>Verbinden</i>"-Schaltfläche';
$softwaretool_step_3 = 'Ein Popup-Fenster erscheint, in dem Sie ein Gerät auswählen können. Wählen Sie das Gerät aus, indem Sie darauf klicken, und drücken Sie dann die Verbinden-Schaltfläche.';
$softwaretool_step_4 = 'Nach dem Verschwinden des Popups wird das Gerät gelesen, die Statusleiste zeigt den Fortschritt an';
$softwaretool_step_5 = 'Verfügbare Software-Upgrades werden mit Name, Beschreibung und Preis angezeigt';
$softwaretool_step_6 = 'Wählen Sie eine Software-Version';
$softwaretool_step_7 = 'Für kostenpflichtige Upgrades folgen Sie bitte dem Zahlungsprozess';
$softwaretool_step_8 = '<b>Hinweis: Dieser Prozess kann nicht gestoppt werden und muss abgeschlossen werden.</b>';
$softwaretool_no_updates = 'Keine Software-Updates gefunden';
$softwaretool_checking = 'Suche nach Software-Updates...';
$softwaretool_available = 'Software-Updates verfügbar';
$softwaretool_select_upgrade = 'Wählen Sie eine Upgrade-Option:';
$newuser_subject = 'CustomerPortal-Benutzer erstellt';
$newuser_header = 'Lieber CustomerPortal-Benutzer';
$newuser_text = 'Ihr CustomerPortal-Administrator hat Ihnen Zugriff auf das CustomerPortal gewährt. Um Ihr Konto abzuschließen, müssen Sie Ihr Passwort über den folgenden Link aktualisieren.';

View File

@@ -319,6 +319,21 @@ $firmwaretool_step_5 = 'Cuando el firmware esté disponible: La barra de estado
$firmwaretool_step_6 = 'Cuando el firmware esté disponible: Asegúrese de que el SN y el HW se lean del dispositivo y confirme esto seleccionando la casilla "Confirmo que el SN y el HW se han leído del dispositivo"';
$firmwaretool_step_7 = 'Presione el botón <i>"Actualizar firmware"</i> para iniciar el diálogo de actualización de firmware y siga las instrucciones en pantalla';
$firmwaretool_step_8 = '<b>Tenga en cuenta: Este proceso no se puede detener y debe finalizar.</b>';
$softwaretool_h2 = 'Herramienta de actualización de software';
$softwaretool_p = 'Opciones de actualización de software.';
$softwaretool_step = 'Instrucciones';
$softwaretool_step_1 = 'Conecte el dispositivo a la computadora por USB. (Se encuentra debajo de la tapa de la batería)';
$softwaretool_step_2 = 'Presione el botón "<i>conectar</i>"';
$softwaretool_step_3 = 'Aparecerá una ventana emergente pidiendo seleccionar un dispositivo. Seleccione el dispositivo haciendo clic en él y luego presione el botón conectar.';
$softwaretool_step_4 = 'Después de que desaparezca la ventana emergente, el dispositivo será leído, la barra de estado mostrará el progreso';
$softwaretool_step_5 = 'Las actualizaciones de software disponibles se mostrarán con Nombre, Descripción y Precio';
$softwaretool_step_6 = 'Seleccione una versión de software';
$softwaretool_step_7 = 'Para actualizaciones de pago, siga el proceso de pago';
$softwaretool_step_8 = '<b>Tenga en cuenta: Este proceso no se puede detener y debe finalizar.</b>';
$softwaretool_no_updates = 'No se encontraron actualizaciones de software';
$softwaretool_checking = 'Buscando actualizaciones de software...';
$softwaretool_available = 'Actualizaciones de software disponibles';
$softwaretool_select_upgrade = 'Seleccione una opción de actualización:';
$newuser_subject = 'Usuario de CustomerPortal creado';
$newuser_header = 'Estimado usuario de CustomerPortal';
$newuser_text = 'Su administrador de CustomerPortal ha proporcionado acceso al CustomerPortal. Para completar su cuenta, debe actualizar su contraseña a través del siguiente enlace.';

View File

@@ -319,6 +319,21 @@ $firmwaretool_step_5 = 'Wanneer software beschikbaar is zal de voortgang "<i>Fir
$firmwaretool_step_6 = 'Wanneer software beschikbaar is: Bevestig dat SW and HW zijn gelezen van het apparaat door op de checkbox te klikken bij <i> Ik bevestig dat SN en HW zijn uitgelezen van de activa</i>.';
$firmwaretool_step_7 = 'Druk op de <i>"Update firmware</i>" knop om de update te starten en de instructies op het scherm te volgen';
$firmwaretool_step_8 = '<b>Opgelet: Dit proces kan niet onderbroken worden.</b>';
$softwaretool_h2 = 'Software upgrade tool';
$softwaretool_p = 'Software upgrade opties.';
$softwaretool_step = 'Instructies';
$softwaretool_step_1 = 'Sluit het apparaat via USB aan op de computer.(USB bevindt zich onder de batterijklep)';
$softwaretool_step_2 = 'Druk "<i>connect</i>" knop';
$softwaretool_step_3 = 'In het popup venster selecteer het apparaat door er op te klikken. Druk daarna op de verbinding maken knop.';
$softwaretool_step_4 = 'Het apparaat wordt nu gelezen. Een voortgang status wordt getoond';
$softwaretool_step_5 = 'Beschikbare software upgrades worden weergegeven met Naam, Beschrijving en Prijs';
$softwaretool_step_6 = 'Selecteer een software versie';
$softwaretool_step_7 = 'Voor betaalde upgrades, volg het betalingsproces';
$softwaretool_step_8 = '<b>Opgelet: Dit proces kan niet onderbroken worden.</b>';
$softwaretool_no_updates = 'Geen software updates gevonden';
$softwaretool_checking = 'Controleren op software updates...';
$softwaretool_available = 'Software updates beschikbaar';
$softwaretool_select_upgrade = 'Selecteer een upgrade optie:';
$newuser_subject = 'CustomerPortal user created';
$newuser_header = 'Dear CustomerPortal user';
$newuser_text = 'Your CustomerPortal administrator has provided access to the CustomerPortal. To complete your account you need to update your password via the link below.';

View File

@@ -319,6 +319,21 @@ $firmwaretool_step_5 = 'Gdy oprogramowanie jest dostępne: Pasek stanu pokaże "
$firmwaretool_step_6 = 'Gdy oprogramowanie jest dostępne: Upewnij się, że SN i HW zostały odczytane z urządzenia i potwierdź to zaznaczając pole "Potwierdzam, że SN i HW zostały odczytane z urządzenia"';
$firmwaretool_step_7 = 'Naciśnij przycisk <i>"Aktualizuj oprogramowanie"</i>, aby uruchomić okno aktualizacji oprogramowania i postępuj zgodnie z instrukcjami na ekranie';
$firmwaretool_step_8 = '<b>Uwaga: Ten proces nie może zostać przerwany i musi się zakończyć.</b>';
$softwaretool_h2 = 'Narzędzie aktualizacji oprogramowania';
$softwaretool_p = 'Opcje aktualizacji oprogramowania.';
$softwaretool_step = 'Instrukcje';
$softwaretool_step_1 = 'Podłącz urządzenie do komputera przez USB. (Znajduje się pod pokrywą baterii)';
$softwaretool_step_2 = 'Naciśnij przycisk "<i>połącz</i>"';
$softwaretool_step_3 = 'Pojawi się okno popup z prośbą o wybranie urządzenia. Wybierz urządzenie klikając na nie, a następnie naciśnij przycisk połącz.';
$softwaretool_step_4 = 'Po zniknięciu okna popup urządzenie zostanie odczytane, pasek stanu pokaże postęp';
$softwaretool_step_5 = 'Dostępne aktualizacje oprogramowania zostaną wyświetlone z Nazwą, Opisem i Ceną';
$softwaretool_step_6 = 'Wybierz wersję oprogramowania';
$softwaretool_step_7 = 'W przypadku płatnych aktualizacji postępuj zgodnie z procesem płatności';
$softwaretool_step_8 = '<b>Uwaga: Ten proces nie może zostać przerwany i musi się zakończyć.</b>';
$softwaretool_no_updates = 'Nie znaleziono aktualizacji oprogramowania';
$softwaretool_checking = 'Sprawdzanie aktualizacji oprogramowania...';
$softwaretool_available = 'Dostępne aktualizacje oprogramowania';
$softwaretool_select_upgrade = 'Wybierz opcję aktualizacji:';
$newuser_subject = 'Użytkownik CustomerPortal utworzony';
$newuser_header = 'Szanowny użytkowniku CustomerPortal';
$newuser_text = 'Administrator CustomerPortal przyznał Ci dostęp do CustomerPortal. Aby ukończyć konfigurację konta, musisz zaktualizować hasło za pomocą poniższego linku.';

View File

@@ -319,6 +319,21 @@ $firmwaretool_step_5 = 'Quando o firmware estiver disponível: A barra de status
$firmwaretool_step_6 = 'Quando o firmware estiver disponível: Certifique-se de que o SN e o HW sejam lidos do dispositivo e confirme isso selecionando a caixa "Eu confirmo que o SN e o HW foram lidos do dispositivo"';
$firmwaretool_step_7 = 'Pressione o botão <i>"Atualizar firmware"</i> para iniciar o diálogo de atualização de firmware e siga as instruções na tela';
$firmwaretool_step_8 = '<b>Esteja ciente: Este processo não pode ser interrompido e precisa ser concluído.</b>';
$softwaretool_h2 = 'Ferramenta de atualização de software';
$softwaretool_p = 'Opções de atualização de software.';
$softwaretool_step = 'Instruções';
$softwaretool_step_1 = 'Conecte o dispositivo ao computador por USB. (Encontrado sob a tampa da bateria)';
$softwaretool_step_2 = 'Pressione o botão "<i>conectar</i>"';
$softwaretool_step_3 = 'Um popup aparecerá pedindo para selecionar um dispositivo. Selecione o dispositivo clicando nele e depois pressione o botão conectar.';
$softwaretool_step_4 = 'Após o popup desaparecer, o dispositivo será lido, a barra de status mostrará o progresso';
$softwaretool_step_5 = 'Atualizações de software disponíveis serão exibidas com Nome, Descrição e Preço';
$softwaretool_step_6 = 'Selecione uma versão de software';
$softwaretool_step_7 = 'Para atualizações pagas, siga o processo de pagamento';
$softwaretool_step_8 = '<b>Esteja ciente: Este processo não pode ser interrompido e precisa ser concluído.</b>';
$softwaretool_no_updates = 'Nenhuma atualização de software encontrada';
$softwaretool_checking = 'Verificando atualizações de software...';
$softwaretool_available = 'Atualizações de software disponíveis';
$softwaretool_select_upgrade = 'Selecione uma opção de atualização:';
$newuser_subject = 'Usuário do CustomerPortal criado';
$newuser_header = 'Caro usuário do CustomerPortal';
$newuser_text = 'Seu administrador do CustomerPortal forneceu acesso ao CustomerPortal. Para completar sua conta, você precisa atualizar sua senha através do link abaixo.';

View File

@@ -336,8 +336,8 @@ $softwaretool_step_2 = 'Press "<i>connect</i>" button"';
$softwaretool_step_3 = 'A popup will appear asking to select a device. Select the device by clicking on it and the press the connect button.';
$softwaretool_step_4 = 'After pop-up disappears the device will be read, status bar will show progress';
$softwaretool_step_5 = 'Available software upgrades will be displayed with Name, Description and Price';
$softwaretool_step_6 = 'Select a free upgrade (price = 0) to download and install';
$softwaretool_step_7 = 'For paid upgrades, please contact support';
$softwaretool_step_6 = 'Select a software version';
$softwaretool_step_7 = 'For paid upgrades, please follow the payment process';
$softwaretool_step_8 = '<b>Be aware: This process cannot be stopped and needs to finish.</b>';
$softwaretool_no_updates = 'No software updates found';
$softwaretool_checking = 'Checking for software updates...';

View File

@@ -122,7 +122,7 @@ $view .= '<div class="content-block">
<div id="connectdevice" style="display:flex; gap: 10px; margin-bottom: 20px;">
<button class="btn" id="connectButton" onClick="connectDeviceForSoftware()" style="min-width: 150px;">
<i class="fa-solid fa-plug"></i>Connect
<i class="fa-solid fa-plug"></i> Connect
</button>
<div id="readStatus" style="flex: 1; background-color: #f1f1f1; border-radius: 4px; overflow: hidden;">
<div id="readBar" style="height: 100%; transition: width 0.3s ease;"></div>
@@ -143,10 +143,10 @@ $view .= '<div class="content-block">
</div>
</div>
<div id="noUpdatesMessage" style="margin: 20px 0; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; text-align: center; display:none;">
<i class="fa-solid fa-check-circle" style="font-size: 48px; margin-bottom: 15px;"></i>
<p style="margin: 0; font-size: 18px; font-weight: 500;"><strong>'.$softwaretool_no_updates.'</strong></p>
<p style="margin: 10px 0 0 0; opacity: 0.9;">Your device is up to date</p>
<div id="noUpdatesMessage" style="margin: 20px 0; padding: 30px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 3px; text-align: center; display:none; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
<i class="fa-solid fa-check-circle" style="font-size: 48px; margin-bottom: 15px; color: #28a745;"></i>
<p style="margin: 0; font-size: 18px; font-weight: 600; color: #155724;"><strong>'.$softwaretool_no_updates.'</strong></p>
<p style="margin: 10px 0 0 0; color: #6c757d; font-size: 14px;">Your device is up to date</p>
</div>
<div id="uploadSection" style="margin-top: 10px; display:none;">
@@ -157,6 +157,8 @@ $view .= '<div class="content-block">
disabled>Install Software
<span class="upload-progress"></span>
</button>
<!-- Hidden checkbox that upload.js expects -->
<input type="checkbox" id="action_firmware" style="display:none;">
</div>
</div>
</div>
@@ -245,6 +247,108 @@ echo '
closePaymentModal();
}
});
// Monitor upload completion
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === "attributes" && mutation.attributeName === "checked") {
const actionFirmware = document.getElementById("action_firmware");
if (actionFirmware && actionFirmware.checked) {
console.log("Upload completion detected!");
// Upload completed successfully
handleUploadCompletion(true);
}
}
});
});
// Also monitor for readBar changes to detect completion
const readBarObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === "childList" || mutation.type === "characterData") {
const readBar = document.getElementById("readBar");
if (readBar && readBar.innerHTML && readBar.innerHTML.includes("Firmware update completed")) {
console.log("Firmware update completed detected via readBar!");
setTimeout(() => handleUploadCompletion(true), 500);
}
}
});
});
// Start observing when upload section is visible
function startUploadMonitoring() {
const actionFirmware = document.getElementById("action_firmware");
const readBar = document.getElementById("readBar");
if (actionFirmware) {
observer.observe(actionFirmware, {
attributes: true,
attributeFilter: ["checked"]
});
console.log("Started monitoring action_firmware checkbox");
}
if (readBar) {
readBarObserver.observe(readBar, {
childList: true,
subtree: true,
characterData: true
});
console.log("Started monitoring readBar for completion");
}
}
// Handle upload completion (success or failure)
window.handleUploadCompletion = function(success) {
console.log("handleUploadCompletion called with success:", success);
const installStatus = document.getElementById("installationStatus");
const readBar = document.getElementById("readBar");
if (success) {
// Success handling
console.log("Updating UI for successful completion");
if (installStatus) {
installStatus.innerHTML = \'\' +
\'<i class="fa-solid fa-check-circle" style="font-size: 48px; color: #28a745; margin-bottom: 15px;"></i>\' +
\'<p style="margin: 0; font-size: 18px; font-weight: 600; color: #155724;">Installation Complete!</p>\' +
\'<p style="margin: 5px 0 0 0; color: #666; font-size: 14px;">Your device has been successfully updated</p>\';
}
// Ensure progress bar is at 100%
if (readBar) {
readBar.style.width = "100%";
readBar.style.background = "#04AA6D";
readBar.innerHTML = "Firmware update completed - 100%";
console.log("Updated readBar to 100%");
}
} else {
// Failure handling
console.log("Updating UI for failed completion");
if (installStatus) {
installStatus.innerHTML = \'\' +
\'<i class="fa-solid fa-exclamation-circle" style="font-size: 48px; color: #dc3545; margin-bottom: 15px;"></i>\' +
\'<p style="margin: 0; font-size: 18px; font-weight: 600; color: #721c24;">Installation Failed</p>\' +
\'<p style="margin: 5px 0 0 0; color: #666; font-size: 14px;">Please try again or contact support</p>\';
}
if (readBar) {
readBar.style.width = "100%";
readBar.style.background = "#dc3545";
readBar.innerHTML = "Installation failed";
}
}
// Stop monitoring
observer.disconnect();
readBarObserver.disconnect();
console.log("Stopped monitoring observers");
};
// Monitor for upload errors
window.addEventListener("error", function(e) {
if (e.message && e.message.includes("upload")) {
handleUploadCompletion(false);
}
});
</script>';
template_footer();