Merge branch 'development' into test

This commit is contained in:
“VeLiTi”
2026-02-05 10:14:22 +01:00
19 changed files with 229 additions and 45 deletions

View File

@@ -272,7 +272,7 @@ $view .= '<div class="tabs">
//Dropdown
$partner_data = json_decode($_SESSION['authorization']['partnerhierarchy']);
$soldto_dropdown = listPartner('soldto',$_SESSION['authorization']['permission'],$accounthierarchy->soldto,'');
$soldto_dropdown = listPartner('soldto',$accounthierarchy->soldto,'',$_SESSION['authorization']['permission']);
$view .= '<div class="tabs">
<a href="#">'.$tab3.'</a>

View File

@@ -13,6 +13,8 @@ if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} el
//default whereclause
$whereclause = '';
$whereclause_alt = '';
$whereclause_alt2 = '';
list($whereclause,$condition) = getWhereclause('products',$permission,$partner,'get');

View File

@@ -52,7 +52,7 @@ if(isset($get_content) && $get_content!=''){
//Filter system roles for users without delete permission on user_roles
if (isAllowed('user_roles', $profile, $permission, 'D') !== 1) {
$clause .= ' AND r.is_system != 1';
$clause .= ' AND r.is_system != 1 AND r.role_hierarchy >= '.$permission;
}
//Build WHERE clause

View File

@@ -357,6 +357,7 @@ CREATE TABLE `user_roles` (
`updated` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`updatedby` varchar(255) DEFAULT NULL,
`is_system` tinyint(1) DEFAULT NULL,
`role_hierarchy` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`rowID`),
UNIQUE KEY `unique_role_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@@ -10,6 +10,51 @@ let deviceVersion = "";
let deviceHwVersion = "";
let selectedSoftwareUrl = "";
// Restricted hardware versions that require warning
const RESTRICTED_HW_VERSIONS = ['r06', 'r06a', 'r07', 'r07a', 'r07b'];
// Helper function to normalize hardware version for comparison
function normalizeHwVersion(hwVersion) {
if (!hwVersion) return '';
// Convert to lowercase and trim
let normalized = hwVersion.toLowerCase().trim();
// Remove leading zeros (e.g., "0000070" -> "70", "0000060" -> "60")
normalized = normalized.replace(/^0+/, '');
// If it starts with 'r', keep it as is (e.g., "r06a")
if (normalized.startsWith('r')) {
return normalized;
}
// Extract numeric part and suffix (e.g., "7a" -> "7" + "a", "70" -> "70" + "")
const match = normalized.match(/^(\d+)([a-z]*)$/);
if (!match) return 'r' + normalized; // Fallback
let numPart = match[1];
const suffix = match[2];
// Pad single digit to 2 digits (e.g., "7" -> "07", "6" -> "06")
if (numPart.length === 1) {
numPart = '0' + numPart;
} else if (numPart.length === 2 && numPart.endsWith('0') && numPart[0] !== '0') {
// "70" -> "07", "60" -> "06" (swap if needed)
numPart = '0' + numPart[0];
}
// Add 'r' prefix (e.g., "07" -> "r07", "07a" -> "r07a", "07b" -> "r07b")
return 'r' + numPart + suffix;
}
// Check if hardware version is restricted
function isRestrictedHardware(hwVersion) {
const normalized = normalizeHwVersion(hwVersion);
const isRestricted = RESTRICTED_HW_VERSIONS.includes(normalized);
console.log(`[HW Check] Original: "${hwVersion}" -> Normalized: "${normalized}" -> Restricted: ${isRestricted}`);
return isRestricted;
}
// Helper function to generate country select options
function generateCountryOptions(selectedCountry = '') {
if (typeof COUNTRIES === 'undefined' || !COUNTRIES) {
@@ -242,7 +287,7 @@ async function connectDeviceForSoftware() {
// TEST MODE: Use mock device data
deviceSerialNumber = "22110095";
deviceVersion = "03e615af";
deviceHwVersion = "0000080";
deviceHwVersion = "0000070";
document.getElementById("Device_output").style.display = "block";
serialResultsDiv.innerHTML = `<strong style="color: #ff9800;">DEBUG MODE - Simulated Device Data:</strong><br>SN=${deviceSerialNumber}<br>FW=${deviceVersion}<br>HW=${deviceHwVersion}`;
@@ -624,15 +669,24 @@ async function fetchSoftwareOptions() {
const customerDataShownThisSession = sessionStorage.getItem('customerDataShownThisSession');
// Show user info modal unless:
// 1. We're in debug mode
// 2. We're returning from payment
// 3. We already showed the modal in this session
if ((typeof DEBUG === 'undefined' || !DEBUG || typeof DEBUG_ID === 'undefined' || !DEBUG_ID)
&& !isReturningFromPayment
&& !customerDataShownThisSession) {
// 1. We're returning from payment
// 2. We already showed the modal in this session
if (!isReturningFromPayment && !customerDataShownThisSession) {
// Always show userInfoModal (even in debug mode)
showUserInfoModal();
} else {
// Debug mode, returning from payment, or already shown this session - reveal software options immediately
// Returning from payment or already shown this session
console.log('[HW Check] Skipping userInfoModal - checking hardware version...');
console.log('[HW Check] deviceHwVersion:', deviceHwVersion);
// Check if hardware version is restricted
if (isRestrictedHardware(deviceHwVersion)) {
// Show hardware warning modal
console.log('[HW Check] Hardware is restricted - showing warning modal');
showHwWarningModal();
} else {
// Reveal software options immediately
console.log('[HW Check] Hardware is not restricted - revealing options');
const softwareOptions = document.getElementById("softwareOptions");
if (softwareOptions) {
softwareOptions.style.filter = "none";
@@ -640,6 +694,7 @@ async function fetchSoftwareOptions() {
softwareOptions.style.pointerEvents = "auto";
}
}
}
} catch (error) {
await logCommunication(`Software options error: ${error.message}`, 'error');
@@ -1014,6 +1069,11 @@ function showUserInfoModal() {
// Close modal
document.body.removeChild(modal);
// Check if hardware version is restricted
if (isRestrictedHardware(deviceHwVersion)) {
// Show hardware warning modal
showHwWarningModal();
} else {
// Reveal software options by removing blur
const softwareOptions = document.getElementById("softwareOptions");
if (softwareOptions) {
@@ -1021,6 +1081,7 @@ function showUserInfoModal() {
softwareOptions.style.opacity = "1";
softwareOptions.style.pointerEvents = "auto";
}
}
};
}
@@ -1064,6 +1125,82 @@ async function sendUserInfoToAPI(customerData) {
}
}
function showHwWarningModal() {
console.log(`[HW Check] Showing hardware warning modal for HW version: ${deviceHwVersion}`);
// Create modal overlay - matching userInfoModal
const modal = document.createElement("div");
modal.id = "hwWarningModal";
modal.style.cssText = `
display: flex;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 2000;
align-items: center;
justify-content: center;
`;
// Create modal content - matching userInfoModal
const modalContent = document.createElement("div");
modalContent.style.cssText = `
background: white;
border-radius: 12px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
margin: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
`;
modalContent.innerHTML = `
<div style="padding: 30px; border-bottom: 2px solid #e0e0e0;">
<h3 style="margin: 0; color: #333; font-size: 24px;">${typeof TRANS_HW_WARNING_TITLE !== 'undefined' ? TRANS_HW_WARNING_TITLE : 'Hardware Compatibility Notice'}</h3>
<p style="margin: 10px 0 0 0; color: #666; font-size: 14px;">${typeof TRANS_HW_WARNING_SUBTITLE !== 'undefined' ? TRANS_HW_WARNING_SUBTITLE : 'Please read the following information carefully'}</p>
</div>
<div style="padding: 30px;">
<div style="margin-bottom: 20px; padding: 20px; background: #fff3cd; border-left: 4px solid #ff9800; border-radius: 4px;">
<div style="display: flex; align-items: flex-start; gap: 15px;">
<i class="fa-solid fa-exclamation-triangle" style="font-size: 32px; color: #ff9800; margin-top: 5px;"></i>
<div>
<p style="margin: 0 0 10px 0; color: #333; font-size: 14px; font-weight: 600;">
${typeof TRANS_HW_WARNING_DETECTED !== 'undefined' ? TRANS_HW_WARNING_DETECTED.replace('{hw_version}', deviceHwVersion) : `Hardware version ${deviceHwVersion} detected`}
</p>
<p style="margin: 0; color: #666; font-size: 14px; line-height: 1.6;">
${typeof TRANS_HW_WARNING_TEXT !== 'undefined' ? TRANS_HW_WARNING_TEXT : 'This hardware version requires special attention. Please ensure you select the correct software version for your device.'}
</p>
</div>
</div>
</div>
<button id="hwWarningContinue" style="width: 100%; padding: 15px; background: linear-gradient(135deg, #04AA6D 0%, #038f5a 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 16px rgba(4,170,109,0.3)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'">
${typeof TRANS_CONTINUE !== 'undefined' ? TRANS_CONTINUE : 'Continue'}
</button>
</div>
`;
modal.appendChild(modalContent);
document.body.appendChild(modal);
// Handle continue button click
document.getElementById("hwWarningContinue").onclick = () => {
// Close modal
document.body.removeChild(modal);
// Reveal software options by removing blur
const softwareOptions = document.getElementById("softwareOptions");
if (softwareOptions) {
softwareOptions.style.filter = "none";
softwareOptions.style.opacity = "1";
softwareOptions.style.pointerEvents = "auto";
}
};
}
async function selectUpgrade(option) {
const price = parseFloat(option.price || 0);
const isFree = price === 0;

View File

@@ -248,10 +248,10 @@ $view .=' </div>
$partner_data = json_decode($contract['accounthierarchy']);
//BUID UP DROPDOWNS
$salesid_dropdown = listPartner('salesid',$_SESSION['authorization']['permission'],$partner_data->salesid,'');
$soldto_dropdown = listPartner('soldto',$_SESSION['authorization']['permission'],$partner_data->soldto,'');
$shipto_dropdown = listPartner('shipto',$_SESSION['authorization']['permission'],$partner_data->shipto,'');
$location_dropdown = listPartner('location',$_SESSION['authorization']['permission'],$partner_data->location,'');
$salesid_dropdown = listPartner('salesid',$partner_data->salesid,'',$_SESSION['authorization']['permission']);
$soldto_dropdown = listPartner('soldto',$partner_data->soldto,'',$_SESSION['authorization']['permission']);
$shipto_dropdown = listPartner('shipto',$partner_data->shipto,'',$_SESSION['authorization']['permission']);
$location_dropdown = listPartner('location',$partner_data->location,'',$_SESSION['authorization']['permission']);
//DISPLAY
$view .= '<div class="tabs">

View File

@@ -216,10 +216,10 @@ $view .= '<div class="tabs">
//GET PARTNERDATA
$partner_data = json_decode($equipment['accounthierarchy']);
//BUID UP DROPDOWNS
$salesid_dropdown = listPartner('salesid',$_SESSION['authorization']['permission'],$partner_data->salesid,'');
$soldto_dropdown = listPartner('soldto',$_SESSION['authorization']['permission'],$partner_data->soldto,'');
$shipto_dropdown = listPartner('shipto',$_SESSION['authorization']['permission'],$partner_data->shipto,'');
$location_dropdown = listPartner('location',$_SESSION['authorization']['permission'],$partner_data->location,'');
$salesid_dropdown = listPartner('salesid',$partner_data->salesid,'',$_SESSION['authorization']['permission']);
$soldto_dropdown = listPartner('soldto',$partner_data->soldto,'',$_SESSION['authorization']['permission']);
$shipto_dropdown = listPartner('shipto',$partner_data->shipto,'',$_SESSION['authorization']['permission']);
$location_dropdown = listPartner('location',$partner_data->location,'',$_SESSION['authorization']['permission']);
if (isset($partner_data->section)){$section = getPartnerName($partner_data->section) ?? 'Not specified';} else {$section = 'Not specified';}

View File

@@ -248,10 +248,12 @@ if (isset($partner_data->section)){$section = getPartnerName($partner_data->sect
//GET PATH OF ASSIGNED MEDIA
$full_path = '';
foreach ($media_responses as $media){
if (!empty($media_responses) && is_array($media_responses)){
foreach ($media_responses as $media){
if($response->product_media == $media['rowID']){
$full_path = $media['full_path'];
}
}
}
$indicators = overviewIndicators($response->warranty_date,$response->service_date,$response->sw_version, $response->sw_version_latest);

View File

@@ -200,10 +200,10 @@ $view .= '<form action="" method="post" id="mass_update_form">
//BUID UP DROPDOWNS
$partner = json_decode($_SESSION['authorization']['partnerhierarchy'],true);
$salesid_dropdown = listPartner('salesid',$_SESSION['authorization']['permission'],$partner['salesid'],'yes');
$soldto_dropdown = listPartner('soldto',$_SESSION['authorization']['permission'],'','yes');
$shipto_dropdown = listPartner('shipto',$_SESSION['authorization']['permission'],'','');
$location_dropdown = listPartner('location',$_SESSION['authorization']['permission'],'','');
$salesid_dropdown = listPartner('salesid',$partner['salesid'],'yes',$_SESSION['authorization']['permission']);
$soldto_dropdown = listPartner('soldto','','yes',$_SESSION['authorization']['permission']);
$shipto_dropdown = listPartner('shipto','','',$_SESSION['authorization']['permission']);
$location_dropdown = listPartner('location','','',$_SESSION['authorization']['permission']);
$view .='<div class="content-block" style="max-height: 450px;">
<div class="block-header">

View File

@@ -163,8 +163,8 @@ $view .= '</div>
//GET PARTNERDATA
$partner_data = json_decode($partner['salesID'])?? json_decode($_SESSION['authorization']['partnerhierarchy']) ;
//BUID UP DROPDOWNS
$salesid_dropdown = listPartner('salesid',$_SESSION['authorization']['permission'],$partner_data->salesid,'');
$soldto_dropdown = listPartner('soldto',$_SESSION['authorization']['permission'],$partner_data->soldto,'');
$salesid_dropdown = listPartner('salesid',$partner_data->salesid,'',$_SESSION['authorization']['permission']);
$soldto_dropdown = listPartner('soldto',$partner_data->soldto,'',$_SESSION['authorization']['permission']);
//DISPLAY
$view .= '<div class="tabs">

View File

@@ -829,4 +829,8 @@ $user_information_description = 'Bitte geben Sie Ihre Daten ein, um mit Software
$general_continue = 'Fortfahren';
$payment_method_1 = 'Debit/Credit';
$payment_method_3 = 'PayPal';
$hw_warning_title = 'Hardware-Kompatibilitätshinweis';
$hw_warning_subtitle = 'Bitte lesen Sie die folgenden Informationen sorgfältig durch';
$hw_warning_detected = 'Hardware-Version {hw_version} erkannt';
$hw_warning_text = 'Ihre Hardware-Version ist nicht mit der verbesserten Batterietemperatur-Warnfunktion kompatibel. Bitte kontaktieren Sie Ihren Händler oder sales@totalsafetysolutions.nl für weitere Informationen zu unserem Austausch- und Upgrade-Programm.';
?>

View File

@@ -835,4 +835,8 @@ $user_information_description = 'Por favor proporcione su información para cont
$general_continue = 'Continuar';
$payment_method_1 = 'Debit/Credit';
$payment_method_3 = 'PayPal';
$hw_warning_title = 'Aviso de Compatibilidad de Hardware';
$hw_warning_subtitle = 'Por favor lea la siguiente información cuidadosamente';
$hw_warning_detected = 'Versión de hardware {hw_version} detectada';
$hw_warning_text = 'Su versión de hardware no es compatible con la función mejorada de advertencia de temperatura de batería. Póngase en contacto con su distribuidor o sales@totalsafetysolutions.nl para obtener más información sobre nuestro programa de reemplazo y actualización.';
?>

View File

@@ -1038,5 +1038,9 @@ $dealer_type_2 = 'Bedrijf';
$user_information_required = 'Gebruikersinformatie Vereist';
$user_information_description = 'Geef uw gegevens op om door te gaan met software-updates';
$general_continue = 'Doorgaan';
$hw_warning_title = 'Hardware Compatibiliteit Melding';
$hw_warning_subtitle = 'Lees de volgende informatie zorgvuldig door';
$hw_warning_detected = 'Hardware versie {hw_version} gedetecteerd';
$hw_warning_text = 'Uw hardwareversie is niet compatibel met de verbeterde waarschuwingsfunctie voor batterijtemperatuur. Neem contact op met uw dealer of sales@totalsafetysolutions.nl voor meer informatie over ons vervangings- en upgradeprogramma.';
$payment_method_3 = 'PayPal';
?>

View File

@@ -769,4 +769,8 @@ $user_information_description = 'Podaj swoje dane, aby kontynuować aktualizacje
$general_continue = 'Kontynuuj';
$payment_method_1 = 'Debit/Credit';
$payment_method_3 = 'PayPal';
$hw_warning_title = 'Powiadomienie o Kompatybilności Sprzętu';
$hw_warning_subtitle = 'Proszę uważnie przeczytać poniższe informacje';
$hw_warning_detected = 'Wykryto wersję sprzętu {hw_version}';
$hw_warning_text = 'Twoja wersja sprzętu nie jest kompatybilna z ulepszoną funkcją ostrzegania o temperaturze baterii. Skontaktuj się ze swoim dealerem lub sales@totalsafetysolutions.nl, aby uzyskać więcej informacji na temat naszego programu wymiany i aktualizacji.';
?>

View File

@@ -835,4 +835,8 @@ $user_information_description = 'Por favor, forneça suas informações para con
$general_continue = 'Continuar';
$payment_method_1 = 'Debit/Credit';
$payment_method_3 = 'PayPal';
$hw_warning_title = 'Aviso de Compatibilidade de Hardware';
$hw_warning_subtitle = 'Por favor, leia as seguintes informações cuidadosamente';
$hw_warning_detected = 'Versão de hardware {hw_version} detectada';
$hw_warning_text = 'A sua versão de hardware não é compatível com a função aprimorada de aviso de temperatura da bateria. Entre em contato com seu revendedor ou sales@totalsafetysolutions.nl para obter mais informações sobre nosso programa de substituição e atualização.';
?>

View File

@@ -1052,5 +1052,9 @@ $dealer_type_2 = 'Corporate';
$user_information_required = 'User Information Required';
$user_information_description = 'Please provide your information to continue with software updates';
$general_continue = 'Continue';
$hw_warning_title = 'Hardware Compatibility Notice';
$hw_warning_subtitle = 'Please read the following information carefully';
$hw_warning_detected = 'Hardware version {hw_version} detected';
$hw_warning_text = 'Your hardware version is not compatible with the enhanced battery temperature warning function. Please contact your dealer or sales@totalsafetysolutions.nl for more information on our replacement and upgrade program.';
$payment_method_3 = 'PayPal';
?>

View File

@@ -330,6 +330,10 @@ echo '
var TRANS_USER_INFO_REQUIRED = "'.($user_information_required ?? 'User Information Required').'";
var TRANS_USER_INFO_DESCRIPTION = "'.($user_information_description ?? 'Please provide your information to continue with software updates').'";
var TRANS_CONTINUE = "'.($general_continue ?? 'Continue').'";
var TRANS_HW_WARNING_TITLE = "'.($hw_warning_title ?? 'Hardware Compatibility Notice').'";
var TRANS_HW_WARNING_SUBTITLE = "'.($hw_warning_subtitle ?? 'Please read the following information carefully').'";
var TRANS_HW_WARNING_DETECTED = "'.($hw_warning_detected ?? 'Hardware version {hw_version} detected').'";
var TRANS_HW_WARNING_TEXT = "'.($hw_warning_text ?? 'This hardware version requires special attention. Please ensure you select the correct software version for your device.').'";
// Countries data
var COUNTRIES = '.json_encode($countries ?? []).';

View File

@@ -494,8 +494,8 @@ $view .= '<div class="content-block">
<table>';
if ($hierarchyLevel == 0 || $hierarchyLevel == 1){
$salesid_dropdown = listPartner('salesid', $_SESSION['authorization']['permission'], $partner_data->salesid ?? '', '');
$soldto_dropdown = listPartner('soldto', $_SESSION['authorization']['permission'], $partner_data->soldto ?? '', '');
$salesid_dropdown = listPartner('salesid', $partner_data->salesid ?? '', '', $_SESSION['authorization']['permission']);
$soldto_dropdown = listPartner('soldto', $partner_data->soldto ?? '', '', $_SESSION['authorization']['permission']);
$view .= '<tr>
<td style="width:25%;">'.($general_salesid ?? 'Sales ID').'</td>
@@ -513,8 +513,8 @@ if ($hierarchyLevel == 0 || $hierarchyLevel == 1){
</tr>';
}
$shipto_dropdown = listPartner('shipto', $_SESSION['authorization']['permission'], $partner_data->shipto ?? '', '');
$location_dropdown = listPartner('location', $_SESSION['authorization']['permission'], $partner_data->location ?? '', '');
$shipto_dropdown = listPartner('shipto', $partner_data->shipto ?? '', '', $_SESSION['authorization']['permission']);
$location_dropdown = listPartner('location', $partner_data->location ?? '', '', $_SESSION['authorization']['permission']);
$view .= '<tr>
<td style="width:25%;">'.($general_shipto ?? 'Ship To').'</td>

View File

@@ -84,12 +84,13 @@ if (!empty($all_roles)){$all_roles = json_decode($all_roles);}else{$all_roles =
// Handle POST for inline edit
//------------------------------
if (isset($_POST['save_permissions']) && $update_allowed_edit === 1) {
// Update role info (name, description, status, system role)
// Update role info (name, description, status, system role, hierarchy)
$role_data_array = [
'rowID' => $role_id,
'name' => $_POST['name'] ?? '',
'description' => $_POST['description'] ?? '',
'is_active' => $_POST['is_active'] ?? 1
'is_active' => $_POST['is_active'] ?? 1,
'role_hierarchy' => $_POST['role_hierarchy'] ?? 0
];
// Only allow is_system to be changed if user has delete permission on user_roles
if ($system_role_allowed === 1) {
@@ -286,6 +287,19 @@ $view .= ' <div class="content-block order-details">
</label>' : '').'
</p>
</div>
<div class="order-detail">
<h3>'.($role_hierarchy ?? 'Role Hierarchy Level').'</h3>
<p>
<span class="view-mode">'.($responses->role_hierarchy ?? '0').'</span>
<select class="edit-mode" name="role_hierarchy" style="display:none;">
<option value="0"'.($responses->role_hierarchy == 0 ? ' selected' : '').'>0 - '.($hierarchy_level_0 ?? 'No restrictions (all access)').'</option>
<option value="1"'.($responses->role_hierarchy == 1 ? ' selected' : '').'>1 - '.($hierarchy_level_1 ?? 'Sales ID level').'</option>
<option value="2"'.($responses->role_hierarchy == 2 ? ' selected' : '').'>2 - '.($hierarchy_level_2 ?? 'Sold To level').'</option>
<option value="3"'.($responses->role_hierarchy == 3 ? ' selected' : '').'>3 - '.($hierarchy_level_3 ?? 'Ship To level').'</option>
<option value="4"'.($responses->role_hierarchy == 4 ? ' selected' : '').'>4 - '.($hierarchy_level_4 ?? 'Location level').'</option>
</select>
</p>
</div>
<div class="order-detail edit-mode-block" style="display:none;">
<h3>'.($copy_from_role ?? 'Copy Permissions From').'</h3>
<p>