• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות

    השנה ההתרמה שלכם תראה אחרת!!!

    מתוזמן נעוץ נעול הועבר עזרה הדדית למשתמשים מתקדמים
    28 פוסטים 11 כותבים 417 צפיות 4 עוקבים
    טוען פוסטים נוספים
    • מהישן לחדש
    • מהחדש לישן
    • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • א מחובר
      אA @BEN ZION
      נערך לאחרונה על ידי

      @BEN-ZION
      מוזר מאוד.
      הורדתי שוב ונפתח מעולה.
      ניסית לפתוח בקןבץ html?

      B תגובה 1 תגובה אחרונה תגובה ציטוט 0
      • B מנותק
        BEN ZION @אA
        נערך לאחרונה על ידי

        @אA הורדתי את הקובץ EXE והפעלתי אותו
        לקחתי את הקוד ויצרתי HTML אצלי הוא עובד רק התוכנה לא עובדת

        א תגובה 1 תגובה אחרונה תגובה ציטוט 0
        • א מחובר
          אA @BEN ZION
          נערך לאחרונה על ידי

          @BEN-ZION
          לא ממש יודע מה לומר לך.
          ניסית להוריד גם מפורום כאן?

          א תגובה 1 תגובה אחרונה תגובה ציטוט 0
          • א מחובר
            אA @אA
            נערך לאחרונה על ידי

            אולי אנסה לשלוח כשזה מקופל אבל זאת נראת בעיה במחשב שלך

            תגובה 1 תגובה אחרונה תגובה ציטוט 0
            • ג מחובר
              גלאט מערכות
              נערך לאחרונה על ידי גלאט מערכות

              @אa

              יש מצב שאתה בונה גם משהו לניהול מכירות לפי מק"ט ?

              B א 2 תגובות תגובה אחרונה תגובה ציטוט 0
              • B מנותק
                BEN ZION @גלאט מערכות
                נערך לאחרונה על ידי

                @אa מי שרוצה כתבתי קוד PHP שמקבל וובהוק מנדרים ומוסיף למערכת בהערות צריך לכתוב את שם המתרים

                <?php
                // הגדרות API
                $token = "{token}"; 
                $api_url = "https://www.call2all.co.il/ym/api/";
                
                // נתיבים
                $path_names = 'ivr2:Points/EnterIDValName.ini';
                $path_total = 'ivr2:Points/points_total.ymgr';
                $path_log   = 'ivr2:Points/manual_log.ini';
                
                // קבצי דיבוג מקומיים בשרת ה-PHP
                $debug_file = 'debug_webhook.log';
                $unassigned_csv = 'unassigned_donations.csv';
                
                // 1. קבלת הנתונים ושמירה לדיבוג ראשוני
                $json_data = file_get_contents('php://input');
                $data = json_decode($json_data, true);
                
                // רישום כל כניסה לקובץ דיבוג כדי שלא יאבד כלום
                $timestamp_log = date("Y-m-d H:i:s");
                file_put_contents($debug_file, "[$timestamp_log] Raw Data: " . $json_data . PHP_EOL, FILE_APPEND);
                
                if (!$data) die("No data received");
                
                $comment_name = trim($data['Comments'] ?? '');
                $amount = floatval($data['Amount'] ?? 0);
                
                function call_yemot($method, $path, $token, $contents = null) {
                    global $api_url;
                    $url = $api_url . $method . "?token=" . $token . "&what=" . $path;
                    if ($contents !== null) $url .= "&contents=" . urlencode($contents);
                    $res = file_get_contents($url);
                    return json_decode($res, true);
                }
                
                // 2. חיפוש גמיש (Fuzzy Matching)
                $fundraiser_id = null;
                $best_score = -1;
                $res_names = call_yemot('GetTextFile', $path_names, $token);
                
                if (isset($res_names['contents']) && !empty($comment_name)) {
                    $lines = explode("\n", $res_names['contents']);
                    foreach ($lines as $line) {
                        $parts = explode('=', $line);
                        if (count($parts) === 2) {
                            $current_id = trim($parts[0]);
                            $current_name = trim($parts[1]);
                
                            // חישוב דמיון
                            similar_text($comment_name, $current_name, $percent);
                            
                            if ($percent > $best_score) {
                                $best_score = $percent;
                                $fundraiser_id = $current_id;
                            }
                        }
                    }
                }
                
                // 3. ביצוע עדכון או שמירה ל-CSV במקרה של כישלון
                // הורדתי את הסף ל-50% בגלל תארים כמו "הרב" ו"שליט"א"
                if ($fundraiser_id && $best_score > 50) {
                    
                    // א. עדכון YMGR
                    $res_total = call_yemot('GetTextFile', $path_total, $token);
                    $t_lines = explode("\n", trim($res_total['contents'] ?? ""));
                    $found = false;
                    foreach ($t_lines as &$line) {
                        if (strpos($line, "EnterId#$fundraiser_id") !== false) {
                            $found = true;
                            preg_match('/PointsTotalAll#([\d.]+)/', $line, $matches);
                            $new_val = (isset($matches[1]) ? floatval($matches[1]) : 0) + $amount;
                            $line = preg_replace('/PointsTotalAll#[\d.]+/', "PointsTotalAll#$new_val", $line);
                            $line = preg_replace('/PointsTotal#[\d.]+/', "PointsTotal#$new_val", $line);
                        }
                    }
                    if (!$found) $t_lines[] = "EnterId#{$fundraiser_id}%PointsTotalAll#{$amount}%PointsTotal#{$amount}";
                    call_yemot('UploadTextFile', $path_total, $token, implode("\n", $t_lines));
                
                    // ב. רישום ללוג ה-HTML
                    $res_log = call_yemot('GetTextFile', $path_log, $token);
                    $time_ms = round(microtime(true) * 1000);
                    $time_hi = date("H:i");
                    $new_entry = "{$time_ms}|{$fundraiser_id}|{$amount}|{$time_hi}";
                    call_yemot('UploadTextFile', $path_log, $token, $new_entry . "\n" . ($res_log['contents'] ?? ""));
                
                    file_put_contents($debug_file, "[$timestamp_log] SUCCESS: Matched $comment_name to ID $fundraiser_id ($best_score%)" . PHP_EOL, FILE_APPEND);
                    echo "Success";
                
                } else {
                    // שמירה ל-CSV עבור תרומות שלא זוהו
                    $is_new = !file_exists($unassigned_csv);
                    $fp = fopen($unassigned_csv, 'a');
                    if ($is_new) {
                        fputs($fp, chr(0xEF) . chr(0xBB) . chr(0xBF)); // BOM לעברית
                        fputcsv($fp, ['תאריך', 'שם מהערה', 'סכום', 'טלפון', 'אחוז התאמה הכי קרוב']);
                    }
                    fputcsv($fp, [$timestamp_log, $comment_name, $amount, $data['Phone'] ?? '', $best_score . "%"]);
                    fclose($fp);
                
                    file_put_contents($debug_file, "[$timestamp_log] FAILED: No match for $comment_name (Best: $best_score%)" . PHP_EOL, FILE_APPEND);
                    echo "Saved to CSV";
                }
                

                מי שרוצה שידרגתי את שתי הקודים כך שיש גם יעד אישי לכל מתרים
                מסך ניהול

                <!DOCTYPE html>
                <html lang="he" dir="rtl">
                <head>
                <meta charset="UTF-8">
                <title>מנהל קמפיין - Points Sync</title>
                <style>
                :root { --primary: #0ea5e9; --success: #22c55e; --bg: #0f172a; --card: #1e293b; --border: #334155; --gold: #f59e0b; }
                body { background: var(--bg); color: #f1f5f9; font-family: system-ui, sans-serif; margin: 0; padding: 20px; text-align: right; }
                .manual-entry-box { max-width: 600px; margin: 50px auto; background: var(--card); padding: 30px; border-radius: 20px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); border: 1px solid var(--border); }
                h1 { text-align: center; color: var(--primary); }
                label { display: block; margin-top: 15px; font-weight: bold; }
                input { width: 100%; padding: 15px; margin: 10px 0; border-radius: 10px; border: 1px solid var(--border); background: #0f172a; color: white; font-size: 1.2rem; box-sizing: border-box; }
                .main-btn { background: var(--success); color: white; border: none; padding: 15px; border-radius: 10px; cursor: pointer; font-weight: bold; width: 100%; font-size: 1.1rem; margin-top: 20px; transition: 0.3s; }
                .main-btn:hover { filter: brightness(1.1); transform: translateY(-2px); }
                .settings-trigger { position: fixed; top: 20px; left: 20px; background: #334155; color: white; border: none; padding: 12px 20px; border-radius: 50px; cursor: pointer; font-weight: bold; display: flex; align-items: center; gap: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); }
                .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); z-index: 1000; backdrop-filter: blur(5px); }
                .modal-content { background: var(--card); width: 90%; max-width: 800px; margin: 40px auto; border-radius: 20px; padding: 25px; border: 1px solid var(--border); position: relative; max-height: 85vh; overflow-y: auto; }
                .close-btn { position: absolute; top: 15px; right: 20px; font-size: 1.5rem; cursor: pointer; }
                .tabs { display: flex; gap: 5px; border-bottom: 2px solid var(--border); margin-bottom: 20px; }
                .tab-btn { background: none; border: none; color: #8b949e; padding: 10px 15px; cursor: pointer; font-size: 0.95rem; }
                .tab-btn.active { color: var(--primary); border-bottom: 2px solid var(--primary); font-weight: bold; }
                .tab-pane { display: none; }
                .tab-pane.active { display: block; }
                .log-list { background: #0f172a; padding: 10px; border-radius: 8px; max-height: 350px; overflow-y: auto; }
                .log-item { display: flex; justify-content: space-between; padding: 12px; border-bottom: 1px solid var(--border); align-items: center; }
                .log-item label { display: flex; align-items: center; cursor: pointer; width: 100%; gap: 15px; }
                .badge { font-size: 0.75rem; padding: 3px 8px; border-radius: 5px; color: white; font-weight: bold; }
                .badge-manual { background: var(--primary); }
                .badge-phone { background: var(--success); }
                .status { margin-top: 15px; font-size: 0.9rem; text-align: center; font-weight: bold; }
                </style>
                </head>
                <body>
                
                <button class="settings-trigger" onclick="openModal()"><span>⚙️</span> הגדרות קמפיין</button>
                
                <div class="manual-entry-box">
                <h1>💰 הזנת תרומה ידנית</h1>
                <label>מספר אישי של המתרים:</label>
                <input type="number" id="manualId" placeholder="למשל 215" autofocus>
                <label>סכום התרומה (₪):</label>
                <input type="number" id="manualAmount" placeholder="0.00">
                <button class="main-btn" onclick="executeManualDonation()">✅ אשר והוסף למערכת</button>
                <div id="mainStatus" class="status"></div>
                </div>
                
                <div id="settingsModal" class="modal">
                <div class="modal-content">
                <span class="close-btn" onclick="closeModal()">✖️</span>
                <h2 style="border-bottom: 1px solid var(--border); padding-bottom: 10px;">⚙️ ניהול והגדרות</h2>
                <div class="tabs">
                <button class="tab-btn active" onclick="switchTab(event, 'tabNames')">👥 שמות ויעדים</button>
                <button class="tab-btn" onclick="switchTab(event, 'tabGoal')">📈 יעד ותאריך</button>
                <button class="tab-btn" onclick="switchTab(event, 'tabTotals')">📊 עריכת סכומים</button>
                <button class="tab-btn" onclick="switchTab(event, 'tabLogs')">🗑️ מחיקת לוג</button>
                </div>
                
                    <div id="tabNames" class="tab-pane active">
                        <div style="display:flex; gap:10px;">
                            <input type="text" id="editId" placeholder="מספר אישי">
                            <button onclick="findUser()" style="width:100px; background:var(--primary); border:none; border-radius:8px; color:white; cursor:pointer;">חפש</button>
                        </div>
                        <input type="text" id="editName" placeholder="שם המתרים">
                        <input type="number" id="editGoal" placeholder="יעד אישי (₪) - אופציונלי">
                        <button class="main-btn" style="background:var(--primary)" onclick="saveUser()">שמור פרטים ויעד</button>
                    </div>
                 
                    <div id="tabGoal" class="tab-pane">
                        <label>יעד הקמפיין הכולל (₪):</label><input type="number" id="goalVal">
                        <label>תאריך ושעת סיום:</label><input type="datetime-local" id="dateVal">
                        <button class="main-btn" onclick="saveConfig()">🔄 עדכן יעד ותאריך</button>
                        <hr style="border:0; border-top:1px solid var(--border); margin:25px 0;">
                        <label style="color:var(--gold)">מפתח אבטחה (Token) API:</label>
                        <input type="password" id="tokenInput" placeholder="הכנס טוקן חדש">
                        <button class="main-btn" style="background:#6366f1" onclick="updateToken()">🔄 עדכן מפתח Token</button>
                    </div>
                 
                    <div id="tabTotals" class="tab-pane"><div id="totalsList" class="log-list">טוען נתונים...</div></div>
                    <div id="tabLogs" class="tab-pane">
                        <div id="logsList" class="log-list">טוען לוג פעולות מהשרת...</div>
                        <button class="main-btn" style="background:#ef4444" onclick="deleteLogs()">❌ מחק פעולות וסנכרן שרת</button>
                    </div>
                    <div id="modalStatus" class="status"></div>
                </div>
                </div>
                
                <script>
                let CURRENT_TOKEN = localStorage.getItem('campaign_token') || 'YOUR_TOKEN_HERE';
                let nameMap = {};
                let goalMap = {};
                
                // נתיבים בשרת - הכל בתיקיית Points
                const PATH_TOTAL = 'ivr2:Points/points_total.ymgr';
                const PATH_PHONE_LOG = 'ivr2:Points/points_log.2026-02.ymgr';
                const PATH_MANUAL_LOG = 'ivr2:Points/manual_log.ini';
                const PATH_CONFIG = 'ivr2:Points/config.ini';
                const PATH_NAMES = 'ivr2:Points/EnterIDValName.ini';
                const PATH_GOALS = 'ivr2:Points/EnterIDGoal.ini';
                 
                document.getElementById('tokenInput').value = CURRENT_TOKEN;
                 
                function updateToken() {
                    const newToken = document.getElementById('tokenInput').value.trim();
                    if(!newToken) return alert("נא להזין טוקן");
                    CURRENT_TOKEN = newToken;
                    localStorage.setItem('campaign_token', newToken);
                    alert("הטוקן עודכן במחשב זה!");
                    location.reload();
                }
                 
                function openModal() { document.getElementById('settingsModal').style.display = 'block'; }
                function closeModal() { document.getElementById('settingsModal').style.display = 'none'; }
                 
                async function callApi(cmd, path, content = "") {
                    let url = `https://www.call2all.co.il/ym/api/${cmd}?token=${CURRENT_TOKEN}&what=${path}`;
                    if(content) url += `&contents=${encodeURIComponent(content)}`;
                    return fetch(url).then(r => r.json());
                }
                 
                async function switchTab(e, id) {
                    document.querySelectorAll('.tab-pane, .tab-btn').forEach(el => el.classList.remove('active'));
                    document.getElementById(id).classList.add('active');
                    e.currentTarget.classList.add('active');
                    await fetchNames();
                    if(id === 'tabGoal') loadCurrentConfig();
                    if(id === 'tabTotals') fetchTotals();
                    if(id === 'tabLogs') fetchLogs();
                }
                 
                async function fetchNames() {
                    const [resNames, resGoals] = await Promise.all([
                        callApi('GetTextFile', PATH_NAMES),
                        callApi('GetTextFile', PATH_GOALS)
                    ]);
                    
                    nameMap = {}; 
                    goalMap = {};
                    
                    if (resNames.contents) {
                        resNames.contents.split(/[\r\n]+/).forEach(line => {
                            const parts = line.split('=');
                            if (parts.length === 2) nameMap[parts[0].trim()] = parts[1].trim();
                        });
                    }
                    if (resGoals.contents) {
                        resGoals.contents.split(/[\r\n]+/).forEach(line => {
                            const parts = line.split('=');
                            if (parts.length === 2) goalMap[parts[0].trim()] = parseFloat(parts[1].trim());
                        });
                    }
                }
                
                async function findUser() {
                    const id = document.getElementById('editId').value;
                    if (!id) return alert("נא להזין מספר אישי");
                    await fetchNames();
                    document.getElementById('editName').value = nameMap[id] || "";
                    document.getElementById('editGoal').value = goalMap[id] || "";
                }
                
                async function saveUser() {
                    const id = document.getElementById('editId').value;
                    const name = document.getElementById('editName').value;
                    const goal = document.getElementById('editGoal').value;
                    
                    if (!id) return alert("חובה להזין מספר אישי");
                
                    let [resNames, resGoals] = await Promise.all([
                        callApi('GetTextFile', PATH_NAMES),
                        callApi('GetTextFile', PATH_GOALS)
                    ]);
                
                    let linesNames = (resNames.contents || "").split('\n').filter(l => l.trim() && !l.startsWith(`${id}=`));
                    if (name) linesNames.push(`${id}=${name}`);
                
                    let linesGoals = (resGoals.contents || "").split('\n').filter(l => l.trim() && !l.startsWith(`${id}=`));
                    if (goal) linesGoals.push(`${id}=${goal}`);
                
                    await Promise.all([
                        callApi('UploadTextFile', PATH_NAMES, linesNames.join('\n')),
                        callApi('UploadTextFile', PATH_GOALS, linesGoals.join('\n'))
                    ]);
                
                    alert("פרטי המתרים והיעד האישי עודכנו בהצלחה!");
                    await fetchNames();
                }
                 
                async function executeManualDonation() {
                    const id = document.getElementById('manualId').value;
                    const amt = parseFloat(document.getElementById('manualAmount').value);
                    if(!id || !amt) return alert("מלא מספר וסכום");
                    document.getElementById('mainStatus').innerText = "⏳ מעדכן שרת...";
                 
                    let resT = await callApi('GetTextFile', PATH_TOTAL);
                    let tLines = (resT.contents || "").trim().split('\n');
                    let found = false;
                    let updatedT = tLines.map(l => {
                        if(l.includes(`EnterId#${id}`)) {
                            found = true;
                            let old = parseFloat((l.match(/PointsTotalAll#([\d.]+)/) || [0,0])[1]);
                            let sum = old + amt;
                            return l.replace(/PointsTotalAll#[\d.]+/, `PointsTotalAll#${sum}`).replace(/PointsTotal#[\d.]+/, `PointsTotal#${sum}`);
                        }
                        return l;
                    });
                    if(!found) updatedT.push(`EnterId#${id}%PointsTotalAll#${amt}%PointsTotal#${amt}`);
                    
                    let resL = await callApi('GetTextFile', PATH_MANUAL_LOG);
                    let currentManualLog = resL.contents || "";
                    const timeStr = new Date().toLocaleTimeString('he-IL', {hour:'2-digit', minute:'2-digit'});
                    const newEntry = `${Date.now()}|${id}|${amt}|${timeStr}`;
                    const updatedManualLog = newEntry + "\n" + currentManualLog;
                 
                    await Promise.all([
                        callApi('UploadTextFile', PATH_TOTAL, updatedT.join('\n')),
                        callApi('UploadTextFile', PATH_MANUAL_LOG, updatedManualLog)
                    ]);
                 
                    document.getElementById('mainStatus').innerText = "✅ נרשם וסונכרן לשרת!";
                    document.getElementById('manualAmount').value = "";
                }
                 
                async function fetchLogs() {
                    let [resPhone, resManual] = await Promise.all([
                        callApi('GetTextFile', PATH_PHONE_LOG),
                        callApi('GetTextFile', PATH_MANUAL_LOG)
                    ]);
                 
                    let allEntries = [];
                    const todayStr = new Date().toISOString().split('T')[0];
                 
                    if (resPhone.contents) {
                        resPhone.contents.trim().split('\n').forEach(l => {
                            let id = (l.match(/id#(\d+)/) || [])[1];
                            let pts = (l.match(/Points#(\d+)/) || [])[1];
                            let timeM = (l.match(/(\d{2}:\d{2})/) || [""])[0];
                            if (id) allEntries.push({id, pts, time: timeM, type: 'phone', fullLine: l, ts: timeM ? new Date(`${todayStr}T${timeM}:00`).getTime() : 0});
                        });
                    }
                 
                    if (resManual.contents) {
                        resManual.contents.trim().split('\n').forEach(l => {
                            let [ts, id, amt, time] = l.split('|');
                            if (id) allEntries.push({id, pts: amt, time, type: 'manual', fullLine: l, ts: parseInt(ts)});
                        });
                    }
                 
                    allEntries.sort((a, b) => b.ts - a.ts);
                 
                    let html = allEntries.map(entry => {
                        let name = nameMap[entry.id] || entry.id;
                        return `<div class="log-item"><label><input type="checkbox" class="log-cb" data-id="${entry.id}" data-amt="${entry.pts}" data-type="${entry.type}" data-line="${entry.fullLine}"><span><span class="badge ${entry.type === 'manual' ? 'badge-manual' : 'badge-phone'}">${entry.type === 'manual' ? 'ידני' : 'טלפון'}</span> [${entry.time}] <b>${name}</b> - ₪${entry.pts}</span></label></div>`;
                    }).join('');
                    document.getElementById('logsList').innerHTML = html || "אין היסטוריה.";
                }
                 
                async function deleteLogs() {
                    const checkboxes = document.querySelectorAll('.log-cb:checked');
                    if (checkboxes.length === 0) return alert("בחר פעולות");
                    if (!confirm("זה ימחק את השורות מכל המחשבים ויקזז סכומים. המשך?")) return;
                 
                    document.getElementById('modalStatus').innerText = "⏳ מסנכרן שרת...";
                    let [resP, resM, resT] = await Promise.all([
                        callApi('GetTextFile', PATH_PHONE_LOG),
                        callApi('GetTextFile', PATH_MANUAL_LOG),
                        callApi('GetTextFile', PATH_TOTAL)
                    ]);
                 
                    let phoneLines = resP.contents ? resP.contents.trim().split('\n') : [];
                    let manualLines = resM.contents ? resM.contents.trim().split('\n') : [];
                    let totalLines = resT.contents ? resT.contents.trim().split('\n') : [];
                 
                    checkboxes.forEach(cb => {
                        const id = cb.getAttribute('data-id'), amt = parseFloat(cb.getAttribute('data-amt')), type = cb.getAttribute('data-type'), line = cb.getAttribute('data-line');
                        if (type === 'phone') phoneLines = phoneLines.filter(l => l !== line);
                        else manualLines = manualLines.filter(l => l !== line);
                 
                        totalLines = totalLines.map(tL => {
                            if(tL.includes(`EnterId#${id}`)) {
                                let old = parseFloat((tL.match(/PointsTotalAll#([\d.]+)/) || [0,0])[1]);
                                let sum = Math.max(0, old - amt);
                                return tL.replace(/PointsTotalAll#[\d.]+/, `PointsTotalAll#${sum}`).replace(/PointsTotal#[\d.]+/, `PointsTotal#${sum}`);
                            } return tL;
                        });
                    });
                 
                    await Promise.all([
                        callApi('UploadTextFile', PATH_PHONE_LOG, phoneLines.join('\n')),
                        callApi('UploadTextFile', PATH_MANUAL_LOG, manualLines.join('\n')),
                        callApi('UploadTextFile', PATH_TOTAL, totalLines.join('\n'))
                    ]);
                    
                    document.getElementById('modalStatus').innerText = "✅ הסנכרון הושלם בכל המחשבים!";
                    fetchLogs();
                }
                 
                async function loadCurrentConfig() {
                    const res = await callApi('GetTextFile', PATH_CONFIG);
                    if(res.contents) {
                        const goal = (res.contents.match(/goal=(.*)/) || [])[1];
                        const date = (res.contents.match(/date=(.*)/) || [])[1];
                        if(goal) document.getElementById('goalVal').value = goal;
                        if(date) document.getElementById('dateVal').value = date;
                    }
                }
                async function saveConfig() {
                    const content = `goal=${document.getElementById('goalVal').value}\ndate=${document.getElementById('dateVal').value}`;
                    await callApi('UploadTextFile', PATH_CONFIG, content);
                    alert("עודכן בשרת!");
                }
                async function fetchTotals() {
                    let res = await callApi('GetTextFile', PATH_TOTAL);
                    if(!res.contents) return;
                    let html = res.contents.trim().split('\n').map(l => {
                        let id = (l.match(/EnterId#(\d+)/) || [])[1];
                        let val = (l.match(/PointsTotalAll#([\d.]+)/) || [])[1];
                        if(!id) return '';
                        let name = nameMap[id] || id;
                        return `<div class="log-item"><span><b>${name}</b> (${id})</span><div style="display:flex; gap:5px;"><input type="number" id="inp-${id}" value="${val}" style="width:110px;"><button onclick="updateTotal('${id}')" style="background:var(--primary); color:white; border:none; padding:8px 12px; border-radius:8px; cursor:pointer;">עדכן</button></div></div>`;
                    }).join('');
                    document.getElementById('totalsList').innerHTML = html;
                }
                async function updateTotal(id) {
                    const newVal = document.getElementById(`inp-${id}`).value;
                    let res = await callApi('GetTextFile', PATH_TOTAL);
                    let updated = res.contents.trim().split('\n').map(l => l.includes(`EnterId#${id}`) ? l.replace(/PointsTotalAll#[\d.]+/, `PointsTotalAll#${newVal}`).replace(/PointsTotal#[\d.]+/, `PointsTotal#${newVal}`) : l);
                    await callApi('UploadTextFile', PATH_TOTAL, updated.join('\n'));
                    alert("סכום עודכן בשרת!");
                }
                </script>
                </body>
                </html>
                

                מסך התרמה

                <!DOCTYPE html>
                <html lang="he" dir="rtl">
                <head>
                <meta charset="UTF-8">
                <title>מערכת התרמה LIVE - Points Sync</title>
                <style>
                :root { --accent: #0ea5e9; --gold: #fbbf24; --bg: #020617; }
                body { background: var(--bg); color: #f8fafc; font-family: system-ui, sans-serif; margin: 0; padding: 10px; overflow: hidden; }
                .container { display: grid; grid-template-columns: 320px 1fr 320px; gap: 15px; height: 95vh; }
                .card { background: rgba(30, 41, 59, 0.6); border: 1px solid rgba(255,255,255,0.1); border-radius: 20px; padding: 15px; backdrop-filter: blur(10px); display: flex; flex-direction: column; overflow: hidden; }
                .center-card { display: flex; flex-direction: column; justify-content: space-between; align-items: center; text-align: center; padding: 40px 20px; }
                h2 { font-size: 1.3rem; border-bottom: 2px solid var(--accent); padding-bottom: 8px; margin: 0 0 10px 0; color: white; text-align: center; }
                
                    .countdown-timer { display: flex; gap: 15px; margin-bottom: 20px; }
                    .time-unit { background: rgba(14, 165, 233, 0.2); padding: 10px 15px; border-radius: 12px; border: 1px solid var(--accent); min-width: 60px; }
                    .time-num { display: block; font-size: 2rem; font-weight: bold; color: white; }
                    .time-label { font-size: 0.8rem; opacity: 0.7; }
                 
                    .total-amount { font-size: 8rem; font-weight: 900; color: var(--accent); text-shadow: 0 0 30px rgba(14, 165, 233, 0.4); margin: 10px 0; line-height: 1; }
                    
                    .progress-wrapper { width: 90%; }
                    .progress-bg { background: #1e293b; height: 40px; border-radius: 50px; border: 2px solid #334155; overflow: hidden; position: relative; }
                    .progress-fill { height: 100%; background: linear-gradient(90deg, #0ea5e9, #22d3ee); width: 0%; transition: width 2s; }
                    
                    .list-box { flex-grow: 1; overflow: hidden; display: flex; flex-direction: column; padding-right: 5px; }
                    .item { display: flex; flex-direction: column; align-items: stretch; padding: 10px; background: rgba(255,255,255,0.05); margin-bottom: 8px; border-radius: 10px; border-right: 4px solid var(--accent); flex-shrink: 0; transition: all 0.5s; }
                    .item-header { display: flex; justify-content: space-between; align-items: center; }
                    .item-info { display: flex; flex-direction: column; }
                    .item-name { font-weight: bold; font-size: 1.1rem; display: flex; align-items: center; gap: 6px; }
                    .item-time { font-size: 0.8rem; color: #94a3b8; font-weight: bold; }
                    .item-val { font-weight: 900; color: var(--gold); font-size: 1.3rem; }
                    
                    .badge { font-size: 0.6rem; padding: 2px 5px; border-radius: 4px; font-weight: bold; }
                    .badge-manual { background: #0ea5e9; color: white; }
                    .badge-phone { background: #22c55e; color: white; }
                 
                    .live-dot { height: 10px; width: 10px; background: #ef4444; border-radius: 50%; display: inline-block; margin-left: 8px; animation: pulse 1s infinite; }
                    @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
                
                    /* עיצוב לאנימציה של הגעה ליעד */
                    @keyframes goldGlow {
                        0% { box-shadow: 0 0 5px rgba(251, 191, 36, 0.2); border-color: rgba(251, 191, 36, 0.5); }
                        50% { box-shadow: 0 0 20px rgba(251, 191, 36, 0.8); border-color: rgba(251, 191, 36, 1); }
                        100% { box-shadow: 0 0 5px rgba(251, 191, 36, 0.2); border-color: rgba(251, 191, 36, 0.5); }
                    }
                    .goal-reached {
                        animation: goldGlow 2s infinite;
                        background: rgba(251, 191, 36, 0.1) !important;
                        border-right-color: var(--gold) !important;
                    }
                    .goal-badge {
                        background: var(--gold); color: black; font-size: 0.75rem; padding: 2px 8px; border-radius: 12px; font-weight: bold; margin-right: 10px; display: inline-block;
                    }
                </style>
                </head>
                <body>
                
                <div class="container">
                <div class="card">
                <h2>🏆 מובילים</h2>
                <div id="leaderboard" class="list-box" style="overflow-y: auto;"></div>
                </div>
                
                <div class="card center-card">
                    <div>
                        <div style="margin-bottom: 10px; font-size: 1.2rem; font-weight: bold;"><span class="live-dot"></span>הקמפיין מסתיים בעוד:</div>
                        <div id="countdown" class="countdown-timer">
                            <div class="time-unit"><span id="days" class="time-num">00</span><span class="time-label">ימים</span></div>
                            <div class="time-unit"><span id="hours" class="time-num">00</span><span class="time-label">שעות</span></div>
                            <div class="time-unit"><span id="minutes" class="time-num">00</span><span class="time-label">דקות</span></div>
                            <div class="time-unit"><span id="seconds" class="time-num">00</span><span class="time-label">שניות</span></div>
                        </div>
                    </div>
                 
                    <div>
                        <h1 style="margin:0; font-size: 1.8rem; opacity: 0.9;">סך הכל נאסף:</h1>
                        <div id="main-total" class="total-amount">₪0</div>
                    </div>
                 
                    <div class="progress-wrapper">
                        <div class="progress-bg"><div id="progress-bar" class="progress-fill"></div></div>
                        <div id="percent-label" style="font-size: 3rem; color: var(--gold); font-weight: bold; margin-top: 10px;">0%</div>
                        <div id="goal-text" style="font-size: 1.5rem; opacity: 0.6;">מחשב נתונים...</div>
                    </div>
                </div>
                 
                <div class="card">
                    <h2>🔔 תרומות אחרונות</h2>
                    <div id="recent-log" class="list-box" style="overflow-y: auto;"></div>
                </div>
                </div>
                
                <script>
                // הגדרות שלוחה - הכל תחת תיקיית Points
                const TOKEN = 'WU1BUElL.apik_MmtjfW7MhNRZGw4h--R54Q.zPUwopxRh5JxRpUutkWFKUhyDWtLfvYwWKQ30PTuXZw';
                const PATH_TOTAL = 'ivr2:Points/points_total.ymgr';
                const PATH_PHONE_LOG = 'ivr2:Points/points_log.2026-02.ymgr';
                const PATH_MANUAL_LOG = 'ivr2:Points/manual_log.ini';
                const PATH_CONFIG = 'ivr2:Points/config.ini';
                const PATH_NAMES = 'ivr2:Points/EnterIDValName.ini';
                const PATH_GOALS = 'ivr2:Points/EnterIDGoal.ini';
                
                let GOAL = 500000;
                let DEADLINE = new Date(); 
                let nameMap = {};
                let goalMap = {};
                 
                function updateCountdown() {
                    const now = new Date();
                    const diff = DEADLINE - now;
                    if (diff <= 0) {
                        document.querySelectorAll('.time-num').forEach(el => el.innerText = "00");
                        return;
                    }
                    document.getElementById('days').innerText = String(Math.floor(diff / (1000 * 60 * 60 * 24))).padStart(2, '0');
                    document.getElementById('hours').innerText = String(Math.floor((diff / (1000 * 60 * 60)) % 24)).padStart(2, '0');
                    document.getElementById('minutes').innerText = String(Math.floor((diff / 1000 / 60) % 60)).padStart(2, '0');
                    document.getElementById('seconds').innerText = String(Math.floor((diff / 1000) % 60)).padStart(2, '0');
                }
                 
                async function update() {
                    try {
                        const [resTotal, resLogPhone, resNames, resConfig, resLogManual, resGoals] = await Promise.all([
                            fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_TOTAL}`).then(r => r.json()),
                            fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_PHONE_LOG}`).then(r => r.json()),
                            fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_NAMES}`).then(r => r.json()),
                            fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_CONFIG}`).then(r => r.json()),
                            fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_MANUAL_LOG}`).then(r => r.json()),
                            fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_GOALS}`).then(r => r.json())
                        ]);
                 
                        if (resConfig.contents) {
                            const g = resConfig.contents.match(/goal=(.*)/);
                            const d = resConfig.contents.match(/date=(.*)/);
                            if (g) {
                                GOAL = parseFloat(g[1]);
                                document.getElementById('goal-text').innerText = `מתוך יעד של ${GOAL.toLocaleString()} ₪`;
                            }
                            if (d) DEADLINE = new Date(d[1]);
                        }
                 
                        if (resNames.contents) {
                            resNames.contents.split(/[\r\n]+/).forEach(line => {
                                const parts = line.split('=');
                                if (parts.length === 2) nameMap[parts[0].trim()] = parts[1].trim();
                            });
                        }
                
                        if (resGoals.contents) {
                            goalMap = {};
                            resGoals.contents.split(/[\r\n]+/).forEach(line => {
                                const parts = line.split('=');
                                if (parts.length === 2) goalMap[parts[0].trim()] = parseFloat(parts[1].trim());
                            });
                        }
                 
                        if (resTotal.contents) {
                            const lines = resTotal.contents.trim().split(/[\r\n]+/);
                            let totalSum = 0; let users = [];
                            lines.forEach(l => {
                                const idMatch = l.match(/EnterId#(\d+)/);
                                const scoreMatch = l.match(/PointsTotalAll#([\d.]+)/);
                                if (idMatch && scoreMatch) {
                                    const val = parseFloat(scoreMatch[1]);
                                    totalSum += val;
                                    users.push({id: idMatch[1], val: val});
                                }
                            });
                            document.getElementById('main-total').innerText = '₪' + totalSum.toLocaleString();
                            document.getElementById('progress-bar').style.width = Math.min((totalSum/GOAL)*100, 100) + '%';
                            document.getElementById('percent-label').innerText = ((totalSum/GOAL)*100).toFixed(1) + '%';
                            
                            users.sort((a,b) => b.val - a.val);
                            
                            document.getElementById('leaderboard').innerHTML = users.map((u,i) => {
                                const personalGoal = goalMap[u.id];
                                let goalHtml = '';
                                let reachedClass = '';
                                let goalBadgeHtml = '';
                                
                                if (personalGoal && personalGoal > 0) {
                                    const rawPct = (u.val / personalGoal) * 100;
                                    const pct = Math.min(rawPct, 100).toFixed(0);
                                    
                                    if (rawPct >= 100) {
                                        reachedClass = 'goal-reached';
                                        goalBadgeHtml = `<span class="goal-badge">הגיע ליעד! 👑</span>`;
                                    }
                
                                    goalHtml = `
                                        <div style="font-size: 0.8rem; color: #94a3b8; display: flex; align-items: center; gap: 8px; margin-top: 8px;">
                                            <div style="background: rgba(255,255,255,0.1); height: 6px; flex-grow: 1; border-radius: 5px; overflow: hidden;">
                                                <div style="background: var(--gold); height: 100%; width: ${pct}%; transition: width 1s;"></div>
                                            </div>
                                            <span style="min-width: 40px; text-align: left; font-weight: bold; color: ${rawPct >= 100 ? 'var(--gold)' : 'inherit'}">${pct}%</span>
                                        </div>
                                    `;
                                }
                
                                return `
                                    <div class="item ${reachedClass}">
                                        <div class="item-header">
                                            <span class="item-name"><b>${i+1}.</b> ${nameMap[u.id] || u.id} ${goalBadgeHtml}</span>
                                            <span style="color:var(--accent); font-weight:bold;">₪${u.val.toLocaleString()}</span>
                                        </div>
                                        ${goalHtml}
                                    </div>
                                `;
                            }).join('');
                        }
                 
                        let allItems = [];
                        const todayStr = new Date().toISOString().split('T')[0];
                 
                        if (resLogPhone.contents) {
                            resLogPhone.contents.trim().split(/[\r\n]+/).forEach((line, idx) => {
                                const idM = line.match(/id#(\d+)/);
                                const ptM = line.match(/Points#(\d+)/);
                                const timeM = line.match(/(\d{2}:\d{2})/);
                                if (idM && ptM) {
                                    let ts = timeM ? new Date(`${todayStr}T${timeM[1]}:00`).getTime() : (Date.now() - 500000);
                                    allItems.push({ id: idM[1], amount: ptM[1], type: 'phone', timestamp: ts, timeStr: timeM ? timeM[1] : "--:--" });
                                }
                            });
                        }
                 
                        if (resLogManual.contents) {
                            resLogManual.contents.trim().split('\n').forEach(l => {
                                let [ts, id, amt, time] = l.split('|');
                                if (id) allItems.push({ id, amount: amt, type: 'manual', timestamp: parseInt(ts), timeStr: time });
                            });
                        }
                 
                        allItems.sort((a, b) => b.timestamp - a.timestamp);
                 
                        document.getElementById('recent-log').innerHTML = allItems.map(item => `
                            <div class="item" style="flex-direction: row; justify-content: space-between; align-items: center;">
                                <div class="item-info">
                                    <span class="item-name">
                                        <span class="badge ${item.type === 'manual' ? 'badge-manual' : 'badge-phone'}">${item.type === 'manual' ? 'ידני' : 'טלפון'}</span>
                                        ${nameMap[item.id] || 'מתרים ' + item.id}
                                    </span>
                                    <span class="item-time">${item.timeStr}</span>
                                </div>
                                <span class="item-val">₪${parseFloat(item.amount).toLocaleString()}</span>
                            </div>
                        `).join('');
                 
                    } catch (e) { console.error("Update Error:", e); }
                }
                 
                setInterval(updateCountdown, 1000);
                setInterval(update, 5000);
                updateCountdown();
                update();
                </script>
                </body>
                </html>
                

                קרדיט ל @אa על הפיתוח המועיל

                ב תגובה 1 תגובה אחרונה תגובה ציטוט 0
                • ב מנותק
                  בוס @BEN ZION
                  נערך לאחרונה על ידי בוס

                  @BEN-ZION
                  אשמח לפירוט על הקוד הראשון, עם איזה מודול זה מתממשק ואיך זה עובד.
                  דבר נוסף, האם תוכל לעשות ששם המתרים ילקח מהקטגוריה ולא מההערות?

                  B א 2 תגובות תגובה אחרונה תגובה ציטוט 0
                  • B מנותק
                    BEN ZION @בוס
                    נערך לאחרונה על ידי

                    @בוס הקוד אתה שם אותו בשרת שלך מבקש מנדרים לשלוח וובהוק לURL והקוד מעביר למערכת של ימות עשיתי את זה אם בינה תתן לה את זה ותגיד לה להחליף מהערות ל קטגוריה או מה שאתה רוצה

                    תגובה 1 תגובה אחרונה תגובה ציטוט 0
                    • א מחובר
                      אA @גלאט מערכות
                      נערך לאחרונה על ידי

                      @גלאט-מערכות
                      בשמחה רבה.
                      רק אשמח אם יש לך אפיון או לכל הפחות כיון איך זה יראה כדי שאדע איך לבנות את זה כמו שצריך.
                      (חשבתי כבר על הכיוון אבל לא ניהלתי מעולם מערכת מכירות ואני לא ממש יודע מה נצרך לכזו תוכנה).

                      תגובה 1 תגובה אחרונה תגובה ציטוט 0
                      • א מחובר
                        אA @בוס
                        נערך לאחרונה על ידי

                        @בוס
                        זה בנוי על מודול הוספת ניקוד.
                        וזה עובד ע"י הוספת טוקן של המערכת בלשונית תאריך ויעד בתוכנת הניהול.

                        תגובה 1 תגובה אחרונה תגובה ציטוט 0
                        • פוסט ראשון
                          פוסט אחרון