💰 מאור אישי
💚 הכנסות
₪0
0 הכנסות
🔴 הוצאות
₪0
0 הוצאות
💰 חיסכון נטו
₪0
הכנסות פחות הוצאות
📊 שיעור חיסכון
0%
מסך ההכנסות
🔴 הוצאות לפי קטגוריה
💚 הכנסות לפי קטגוריה
📈 תזרים לאורך זמן
הכנסות
הוצאות
🔥 5 ההוצאות הגדולות בתקופה
תאריךשם מקורקטגוריהסכום
טוען...
🕒 פעולות אחרונות
תאריךסוגשם/מקורתיאורקטגוריהסכום
טוען...
סה"כ הכנסות
₪0
ממוצע להכנסה
₪0
מספר הכנסות
0
רשימת ההכנסות
תאריךמקור הכנסהתיאורקטגוריהסכום כוללסטטוס
טוען...
💰 סה"כ חודשי
₪0
0 מקורות פעילים
📅 השקה שנתית משוערת
₪0
12 × הכנסה חודשית
🏷️ קטגוריה מובילה
רשימת הכנסות חוזרות
מקור הכנסהתיאורקטגוריהסכוםיום בחודששיטת תשלוםפעילה מ־
טוען...
סה"כ הוצאות
₪0
הוצאות קבועות
₪0
הוצאות משתנות
₪0
רשימת הוצאות
תאריךשם מקורתיאורקטגוריהסכוםסוג
טוען...
ממתין לתשלום
₪0
באיחור
₪0
שולם החודש
₪0
סה"כ פתוח
0
פריטים פתוחים
פריטים ממתינים לגבייה
תאריךמקור הכנסהתיאורסכוםתאריך יעדסטטוס
טוען...
⏳ ממתין להפקה החודש
₪0
0 חשבוניות
✅ הופקו החודש
₪0
0 חשבוניות
📊 סה"כ ממתין (כל הזמנים)
₪0
0 חשבוניות
📊 סה"כ הופק (כל הזמנים)
₪0
0 חשבוניות
⏳ ממתינות להפקת חשבונית
תאריךלקוחתיאורסכוםסטטוס תשלוםב-Drive
טוען...
✅ חשבוניות שהופקו החודש
תאריך עסקהלקוחתיאורסכוםשיטת תשלוםב-Drive
טוען...
סה"כ ממתין לחתימה
₪0
מספר הכנסות
0
ממתינים לחתימת חוזה
תאריךלקוחתיאורסכוםב-Drive
טוען...
⚙️ שיעורי המס:
💡 השיעורים נקבעים על פי הרווח (הכנסות לפני מע"מ פחות הוצאות לפני מע"מ)
מנויים פעילים
0
MRR
₪0
הכנסה חודשית חוזרת
ARR
₪0
הכנסה שנתית חוזרת
רשימת מנויים
לקוחחבילהסכוםתדירותחידוש הבאסטטוס
טוען...
💰 סה"כ מושקע (עלות)
₪0
0 השקעות
📊 שווי תיק נוכחי
₪0
0 השקעות פעילות
📈 רווח / הפסד
₪0
0%
🔁 הפקדות חודשיות
₪0
סה"כ יורד מהעו"ש בכל חודש
📅 השקעה שנתית משוערת
₪0
12 × הפקדות חודשיות
📊 פירוט לפי סוג השקעה
📋 רשימת ההשקעות שלי
שםסוגעלותשווי נוכחירווח/הפסד%הפקדה חודשיתעדכון אחרון
טוען...
סה"כ חודשי
₪0
סה"כ שנתי
₪0
מספר פריטים
0
רשימת הוצאות קבועות
שםקטגוריהסכוםתדירותתאריך חיובשיטה📦 Voliסטטוס
טוען...
⚙️ כללי תזמון
1-15
עסקאות מה-1 עד ה-15 בחודש
מועברות לחשבון ב-2 לחודש הבא
16-31
עסקאות מה-16 עד סוף החודש
מועברות לחשבון ב-9 לחודש הבא
💡 הכללים חלים אוטומטית על כל הכנסה שסומנה בשיטת תשלום "כרטיס אשראי" — תאריך הכניסה בפועל לתזרים מחושב לפי הכלל, לא לפי תאריך העסקה.
⏳ תקבולי אשראי בהמתנה לסליקה
לקוחסכוםתאריך עסקהתאריך כניסה לחשבון
טוען...
🏦 יתרת פתיחה (עו"ש)
יתרה נוכחית בחשבון — ממנה מחשבים את המצב היומי
תקבולים החודש
₪0
תשלומים החודש
₪0
מקסימום תזרים
₪0
הנקודה הגבוהה ביותר בטווח המוצג
מינימום תזרים
₪0
הנקודה הנמוכה ביותר בטווח המוצג
ימים ביתרה שלילית
0
בסה"כ על פני כל הזמנים
📅 תזרים יומי — מאי 2026
תאריך יום תקבולים סעיף תקבול תשלומים סעיף תשלום מצב חשבון
טוען...
☁️ סנכרון Google Sheets
לא מחובר
📖 הוראות התקנה — לחץ להצגה
💡 אם כבר התקנת קודם עם הקוד הישן — פשוט העתק את הקוד החדש למטה, החלף את הישן ב-Apps Script, ולחץ Deploy ← Manage deployments ← ✏️ ← "New version" ← Deploy. ה-URL נשאר אותו דבר!

שלב 1: פתח Google Sheet חדש. תן לו שם, למשל "כספי סטודיו".

שלב 2: בגיליון החדש, פתח Extensions ← Apps Script.

שלב 3: מחק את הקוד שיש שם, והדבק את הקוד הבא (לחץ 📋 להעתקה):

// ============================================================

// דשבורד כספים — Google Sheets Backend (טבלאות מסודרות)

// ============================================================


const SHEETS_MAP = {

  income:            'הכנסות',

  expenses:          'הוצאות',

  fixed:             'הוצאות קבועות',

  investments:       'השקעות',

  collection:        'גבייה',

  invoices:          'חשבוניות',

  contracts:         'חוזים',

  supplierPayments:  'ספקים עתידיים',

  subscriptions:     'מנויים',

  vendorPresets:     'ספקים שמורים'

};

const META_SHEET = '_מטא';

const SETTINGS_SHEET = 'הגדרות';


function doGet(e) {

  try {

    const ss = SpreadsheetApp.getActiveSpreadsheet();

    const result = {};

    Object.keys(SHEETS_MAP).forEach(k => {

      result[k] = readSheet(ss, SHEETS_MAP[k]);

    });

    result.settings = readSettings(ss);

    const meta = readMeta(ss);

    return respond({ ok: true, data: result, lastUpdated: meta.lastUpdated || null });

  } catch (err) {

    return respond({ ok: false, error: err.toString() });

  }

}


function doPost(e) {

  try {

    const ss = SpreadsheetApp.getActiveSpreadsheet();

    const body = e && e.postData ? e.postData.contents : '{}';

    const data = JSON.parse(body);

    Object.keys(SHEETS_MAP).forEach(k => {

      writeSheet(ss, SHEETS_MAP[k], data[k] || []);

    });

    writeSettings(ss, data.settings || {});

    const now = new Date().toISOString();

    writeMeta(ss, now, data);

    return respond({ ok: true, lastUpdated: now });

  } catch (err) {

    return respond({ ok: false, error: err.toString() });

  }

}


function respond(obj) {

  return ContentService.createTextOutput(JSON.stringify(obj))

    .setMimeType(ContentService.MimeType.JSON);

}


function getOrCreateSheet(ss, name) {

  let s = ss.getSheetByName(name);

  if (!s) s = ss.insertSheet(name);

  return s;

}


function readSheet(ss, name) {

  const sheet = ss.getSheetByName(name);

  if (!sheet) return [];

  const values = sheet.getDataRange().getValues();

  if (values.length < 2) return [];

  const headers = values[0].map(h => String(h || '').trim());

  return values.slice(1).map(row => {

    const obj = {};

    headers.forEach((h, i) => {

      if (!h) return;

      let v = row[i];

      if (v === '' || v === null || v === undefined) return;

      if (typeof v === 'string') {

        const s = v.trim();

        if ((s.startsWith('{') && s.endsWith('}')) || (s.startsWith('[') && s.endsWith(']'))) {

          try { v = JSON.parse(s); } catch (e) {}

        }

      }

      obj[h] = v;

    });

    return obj;

  }).filter(o => Object.keys(o).length > 0);

}


function readSettings(ss) {

  const sheet = ss.getSheetByName(SETTINGS_SHEET);

  if (!sheet) return {};

  const values = sheet.getDataRange().getValues();

  const out = {};

  values.slice(1).forEach(row => {

    if (!row[0]) return;

    let v = row[1];

    if (typeof v === 'string') {

      const s = v.trim();

      if ((s.startsWith('{') && s.endsWith('}')) || (s.startsWith('[') && s.endsWith(']'))) {

        try { v = JSON.parse(s); } catch (e) {}

      }

    }

    if (v !== '' && v !== null && v !== undefined) out[String(row[0])] = v;

  });

  return out;

}


function readMeta(ss) {

  const sheet = ss.getSheetByName(META_SHEET);

  if (!sheet) return {};

  const values = sheet.getDataRange().getValues();

  const out = {};

  values.slice(1).forEach(row => {

    if (row[0]) out[String(row[0])] = row[1];

  });

  return out;

}


function writeSheet(ss, name, records) {

  const sheet = getOrCreateSheet(ss, name);

  sheet.clear();

  if (!records || records.length === 0) {

    sheet.getRange(1, 1).setValue('(אין נתונים)')

      .setFontColor('#9CA3AF').setFontStyle('italic');

    return;

  }

  const fieldSet = {};

  records.forEach(r => Object.keys(r).forEach(k => fieldSet[k] = true));

  const fields = Object.keys(fieldSet);

  const maxCols = sheet.getMaxColumns();

  if (fields.length > maxCols) {

    sheet.insertColumnsAfter(maxCols, fields.length - maxCols);

  }

  const headerRange = sheet.getRange(1, 1, 1, fields.length);

  headerRange.setValues([fields]);

  headerRange.setFontWeight('bold').setBackground('#1F2937').setFontColor('#FFFFFF');

  const rows = records.map(r => fields.map(f => valueToCell(r[f])));

  sheet.getRange(2, 1, rows.length, fields.length).setValues(rows);

  sheet.setFrozenRows(1);

  try { sheet.autoResizeColumns(1, fields.length); } catch (e) {}

}


function valueToCell(v) {

  if (v === undefined || v === null) return '';

  if (typeof v === 'object') return JSON.stringify(v);

  return v;

}


function writeSettings(ss, settings) {

  const sheet = getOrCreateSheet(ss, SETTINGS_SHEET);

  sheet.clear();

  const header = sheet.getRange(1, 1, 1, 2);

  header.setValues([['שדה', 'ערך']]);

  header.setFontWeight('bold').setBackground('#1F2937').setFontColor('#FFFFFF');

  const keys = Object.keys(settings || {});

  if (keys.length > 0) {

    const rows = keys.map(k => [k, valueToCell(settings[k])]);

    sheet.getRange(2, 1, rows.length, 2).setValues(rows);

  }

  sheet.setFrozenRows(1);

  try { sheet.autoResizeColumns(1, 2); } catch (e) {}

}


function writeMeta(ss, lastUpdated, data) {

  const sheet = getOrCreateSheet(ss, META_SHEET);

  sheet.clear();

  const rows = [

    ['שדה', 'ערך'],

    ['עדכון אחרון', new Date(lastUpdated).toLocaleString('he-IL')],

    ['ISO Timestamp', lastUpdated]

  ];

  Object.keys(SHEETS_MAP).forEach(k => {

    const arr = data[k];

    if (Array.isArray(arr)) rows.push([SHEETS_MAP[k], arr.length + ' רשומות']);

  });

  sheet.getRange(1, 1, rows.length, 2).setValues(rows);

  sheet.getRange(1, 1, 1, 2).setFontWeight('bold').setBackground('#1F2937').setFontColor('#FFFFFF');

  sheet.setFrozenRows(1);

  try { sheet.autoResizeColumns(1, 2); } catch (e) {}

}

שלב 4: לחץ Deploy ← New deployment. בחר Web app.

שלב 5: הגדר:

  • Execute as: Me
  • Who has access: Anyone (חשוב!)

שלב 6: לחץ Deploy. אשר את ההרשאות (Advanced ← Go to project).

שלב 7: העתק את ה-Web app URL והדבק כאן למטה 👇

הודבק את ה-URL שהעתקת מ-Apps Script (מסתיים ב-/exec)
💡 כשמופעל, כל שינוי יסונכרן לענן ברקע. באייפון תמיד תראה את המצב העדכני אחרי טעינה.
💡 מומלץ אם אתה משתמש גם במחשב וגם באייפון. יבטיח שאתה תמיד רואה את הגרסה העדכנית.
💤 עוד לא סונכרן בסשן זה
💾 גיבוי ידני
💡 השתמש בגיבוי הידני כפתרון חלופי לסנכרון בענן, או כגיבוי בטחון לפני שינויים גדולים.
ℹ️ מידע על המערכת
גרסה 1.0.0
מפתח מאור אישי
מקום שמירה מקומי localStorage (financeData_v1)
גודל דאטה מקומי