دليل إعداد Google Sheets مع التنبيهات التلقائية

نظام متكامل لإدارة الشيكات والمدفوعات - كريستال باور للاستثمار

المقدمة - ما سيتم إعداده

Google Sheets مع 4 صفحات رئيسية

العقارات، المدفوعات، الشيكات، المستثمرين

تقارير يومية تلقائية

كل يوم في تمام الساعة 9 صباحاً بتوقيت القاهرة

تنبيهات فورية

للشيكات والمدفوعات المتأخرة

كود Apps Script كامل

جاهز للنسخ والاستخدام مباشرة

ميزات النظام:

  • حساب الغرامات تلقائياً (3.5% شهرياً متناسب يومياً)
  • نظام الشيكات الثلاثي (المستفيد، الساحب، المُحصّل)
  • تتبع ROI والضرائب المصرية
  • تنبيهات عبر البريد الإلكتروني
1

إنشاء Google Sheet جديد

خطوات الإنشاء:

  1. اذهب إلى sheets.google.com
  2. اضغط على "فارغ" لإنشاء ملف جديد
  3. اكتب اسم الملف: "نظام كريستال باور للاستثمار"
  4. احفظ الملف في Google Drive

نصيحة: تأكد من استخدام حساب Google الخاص بك ([email protected])

2

إنشاء هيكل الصفحات

صفحة 1: العقارات

انقر بجانب "Sheet1" في الأسفل → اختر "إعادة تسمية" → اكتب "العقارات"

العمود اسم الحقل مثال
Aرقم الأصلPROP-001
Bاسم العقارعقار لايف
Cالموقعالغرافة
Dنوع العقارسكني
Eالمساحة م²482
Fرقم الوحدة58
Gالمبلغ المستثمر11000000
Hتاريخ الشراء13/05/2023
Iالقيمة الحالية12100000
Jنسبة العائد10%
Kحالة العقارنشط
Lملاحظات-

صفحة 2: المدفوعات

اضغط على "+" في أسفل الصفحة → أنشئ صفحة جديدة → اسمها "المدفوعات"

العمود اسم الحقل مثال
Aرقم السندPAY-001
Bتاريخ الاستحقاق27/03/2024
Cالمبلغ469750
Dتاريخ التحصيل(فارغ)
Eأيام التأخير(صيغة)
Fنسبة الغرامة(صيغة)
Gمبلغ الغرامة(صيغة)
Hالمبلغ الإجمالي(صيغة)
Iحالة التحصيلقيد الانتظار
Jملاحظات-

صفحة 3: الشيكات (نظام ثلاثي)

العمود اسم الحقل مثال
Aرقم الشيك12345
Bالمستفيدمؤمن محمد
Cالساحبم. مصطفى
Dالمُحصّلأحمد خالد
Eالبنكبنك مصر
Fتاريخ الشيك15/10/2025
Gتاريخ الاستحقاق01/11/2025
Hقيمة الشيك50000
Iتاريخ التحصيل(فارغ)
Jأيام التأخير(صيغة)
Kالغرامة 3.5%(صيغة)
Lالمبلغ الإجمالي(صيغة)
Mحالة الشيكقيد الانتظار
Nملاحظات-

صفحة 4: المستثمرين

العمود اسم الحقل مثال
Aرقم المستثمرINV-001
Bالاسم الكاملمؤمن محمد
Cالبريد الإلكتروني[email protected]
Dرقم الهاتف+20 XXX XXXX
Eعدد العقارات3
Fإجمالي الاستثمارات13350000
Gمتوسط العائد12%
Hحالة الحسابنشط
3

إضافة الصيغ التلقائية

صيغ صفحة المدفوعات:

الخلية E2 (أيام التأخير):

=IF(D2="", IF(B2B2, D2-B2, 0))

الخلية F2 (نسبة الغرامة اليومية):

=3.5%/30

الخلية G2 (مبلغ الغرامة):

=IF(E2>0, C2*F2*E2, 0)

الخلية H2 (المبلغ الإجمالي):

=C2+G2

صيغ صفحة الشيكات:

الخلية J2 (أيام التأخير):

=IF(I2="", IF(G2G2, I2-G2, 0))

الخلية K2 (الغرامة 3.5%):

=IF(J2>0, H2*(3.5%/30)*J2, 0)

الخلية L2 (المبلغ الإجمالي):

=H2+K2

مهم: بعد إدخال كل صيغة في الخلية المحددة، اضغط Enter، ثم انسخ الصيغة لجميع الصفوف الأخرى بالسحب أو Ctrl+C ثم Ctrl+V.

4

فتح محرر Apps Script

خطوات فتح المحرر:

  1. في Google Sheet، اضغط على قائمة "ملحقات" (Extensions)
  2. اختر "Apps Script"
  3. ستفتح صفحة جديدة مع ملف Code.gs
  4. احذف أي كود موجود
  5. انسخ والصق الكود من الخطوة التالية

سيتم فتح محرر Apps Script في تاب جديد. احتفظ بصفحة Google Sheet مفتوحة أيضاً.

5

كود Apps Script الكامل

انسخ هذا الكود كاملاً والصقه في محرر Apps Script:

// ============================================
// CRYSTAL POWER INVESTMENTS - AUTOMATED NOTIFICATIONS
// Email: [email protected]
// ============================================

// CONFIGURATION
const CONFIG = {
  primaryEmail: "[email protected]",
  backupEmail: "[email protected]",
  companyName: "Crystal Power Investments",
  timezone: "Africa/Cairo",
  dailyReportHour: 9, // 9 AM
  alerts: {
    highPenaltyThreshold: 500,  // EGP
    urgentOverdueDays: 7,
    upcomingDueDays: 3
  }
};

// ============================================
// MAIN FUNCTIONS
// ============================================

/**
 * Send daily email report at 9 AM Cairo time
 */
function sendDailyReport() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const checksSheet = ss.getSheetByName('الشيكات');
  const paymentsSheet = ss.getSheetByName('المدفوعات');
  
  if (!checksSheet || !paymentsSheet) {
    Logger.log('Error: Required sheets not found');
    return;
  }
  
  const today = new Date();
  const data = analyzeData(checksSheet, paymentsSheet, today);
  
  // Only send if there are alerts or upcoming items
  if (data.overdueChecks.length > 0 || data.overduePayments.length > 0 || 
      data.upcomingChecks.length > 0 || data.upcomingPayments.length > 0) {
    
    const emailBody = buildDailyEmail(data, today);
    
    MailApp.sendEmail({
      to: CONFIG.primaryEmail,
      cc: CONFIG.backupEmail,
      subject: "🔔 تقرير الشيكات والمدفوعات اليومي - " + formatDate(today),
      body: emailBody,
      name: CONFIG.companyName
    });
    
    Logger.log('Daily report sent successfully');
  } else {
    Logger.log('No alerts to send today');
  }
}

/**
 * Check for overdue items when sheet is edited
 */
function onEditTrigger(e) {
  const sheet = e.source.getActiveSheet();
  const sheetName = sheet.getName();
  
  // Only check الشيكات and المدفوعات sheets
  if (sheetName !== 'الشيكات' && sheetName !== 'المدفوعات') return;
  
  const range = e.range;
  const row = range.getRow();
  
  // Skip header row
  if (row === 1) return;
  
  checkForInstantAlert(sheet, row);
}

/**
 * Setup time-based trigger for daily reports
 */
function setupDailyTrigger() {
  // Delete existing triggers
  const triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(trigger => {
    if (trigger.getHandlerFunction() === 'sendDailyReport') {
      ScriptApp.deleteTrigger(trigger);
    }
  });
  
  // Create new trigger at 9 AM Cairo time
  ScriptApp.newTrigger('sendDailyReport')
    .timeBased()
    .atHour(CONFIG.dailyReportHour)
    .everyDays(1)
    .inTimezone(CONFIG.timezone)
    .create();
  
  Logger.log('Daily trigger set up successfully for ' + CONFIG.dailyReportHour + ' AM ' + CONFIG.timezone);
}

/**
 * Setup edit trigger
 */
function setupEditTrigger() {
  // Delete existing edit triggers
  const triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(trigger => {
    if (trigger.getHandlerFunction() === 'onEditTrigger') {
      ScriptApp.deleteTrigger(trigger);
    }
  });
  
  // Create new edit trigger
  ScriptApp.newTrigger('onEditTrigger')
    .forSpreadsheet(SpreadsheetApp.getActive())
    .onEdit()
    .create();
  
  Logger.log('Edit trigger set up successfully');
}

// ============================================
// ANALYSIS FUNCTIONS
// ============================================

/**
 * Analyze data from sheets
 */
function analyzeData(checksSheet, paymentsSheet, today) {
  const checksData = checksSheet.getDataRange().getValues();
  const paymentsData = paymentsSheet.getDataRange().getValues();
  
  let overdueChecks = [];
  let upcomingChecks = [];
  let overduePayments = [];
  let upcomingPayments = [];
  let totalPenalties = 0;
  
  // Analyze checks (skip header row)
  for (let i = 1; i < checksData.length; i++) {
    const row = checksData[i];
    const checkNumber = row[0];
    const dueDate = new Date(row[6]);
    const amount = row[7];
    const collectionDate = row[8];
    const status = row[12];
    
    if (!collectionDate && checkNumber) {
      const daysDiff = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));
      
      if (daysDiff > 0) {
        const penalty = amount * (3.5/100/30) * daysDiff;
        totalPenalties += penalty;
        overdueChecks.push({
          number: checkNumber,
          days: daysDiff,
          amount: amount,
          penalty: penalty,
          beneficiary: row[1],
          drawer: row[2],
          collector: row[3]
        });
      } else if (daysDiff >= -CONFIG.alerts.upcomingDueDays && daysDiff <= 0) {
        upcomingChecks.push({
          number: checkNumber,
          dueDate: dueDate,
          amount: amount,
          daysUntilDue: Math.abs(daysDiff)
        });
      }
    }
  }
  
  // Analyze payments (skip header row)
  for (let i = 1; i < paymentsData.length; i++) {
    const row = paymentsData[i];
    const paymentId = row[0];
    const dueDate = new Date(row[1]);
    const amount = row[2];
    const collectionDate = row[3];
    const status = row[8];
    
    if (!collectionDate && paymentId) {
      const daysDiff = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));
      
      if (daysDiff > 0) {
        const penalty = amount * (3.5/100/30) * daysDiff;
        totalPenalties += penalty;
        overduePayments.push({
          id: paymentId,
          days: daysDiff,
          amount: amount,
          penalty: penalty
        });
      } else if (daysDiff >= -CONFIG.alerts.upcomingDueDays && daysDiff <= 0) {
        upcomingPayments.push({
          id: paymentId,
          dueDate: dueDate,
          amount: amount,
          daysUntilDue: Math.abs(daysDiff)
        });
      }
    }
  }
  
  return {
    overdueChecks,
    upcomingChecks,
    overduePayments,
    upcomingPayments,
    totalPenalties
  };
}

/**
 * Build daily email content
 */
function buildDailyEmail(data, today) {
  let email = "مرحباً مؤمن،\n\n";
  email += "📊 تقرير يوم " + formatDate(today) + ":\n\n";
  
  // Urgent alerts
  if (data.overdueChecks.length > 0 || data.overduePayments.length > 0) {
    email += "🔴 تنبيهات عاجلة:\n";
    if (data.overdueChecks.length > 0) {
      email += "• " + data.overdueChecks.length + " شيك متأخر\n";
    }
    if (data.overduePayments.length > 0) {
      email += "• " + data.overduePayments.length + " دفعة متأخرة\n";
    }
    email += "• إجمالي الغرامات: " + formatCurrency(data.totalPenalties) + " جنيه\n\n";
  }
  
  // Overdue checks details
  if (data.overdueChecks.length > 0) {
    email += "📋 الشيكات المتأخرة:\n";
    data.overdueChecks.forEach(check => {
      email += "• شيك #" + check.number + "\n";
      email += "  المستفيد: " + check.beneficiary + "\n";
      email += "  المبلغ: " + formatCurrency(check.amount) + " جنيه\n";
      email += "  متأخر: " + check.days + " يوم\n";
      email += "  الغرامة: " + formatCurrency(check.penalty) + " جنيه\n\n";
    });
  }
  
  // Overdue payments details
  if (data.overduePayments.length > 0) {
    email += "💰 المدفوعات المتأخرة:\n";
    data.overduePayments.forEach(payment => {
      email += "• سند #" + payment.id + " - " + formatCurrency(payment.amount) + " جنيه\n";
      email += "  متأخر: " + payment.days + " يوم - الغرامة: " + formatCurrency(payment.penalty) + " جنيه\n\n";
    });
  }
  
  // Upcoming checks
  if (data.upcomingChecks.length > 0) {
    email += "⏰ شيكات مستحقة قريباً:\n";
    data.upcomingChecks.forEach(check => {
      email += "• شيك #" + check.number + " - " + formatCurrency(check.amount) + " جنيه\n";
      email += "  الاستحقاق: " + formatDate(check.dueDate) + " (خلال " + check.daysUntilDue + " يوم)\n\n";
    });
  }
  
  // Upcoming payments
  if (data.upcomingPayments.length > 0) {
    email += "📅 دفعات مستحقة قريباً:\n";
    data.upcomingPayments.forEach(payment => {
      email += "• سند #" + payment.id + " - " + formatCurrency(payment.amount) + " جنيه\n";
      email += "  الاستحقاق: " + formatDate(payment.dueDate) + " (خلال " + payment.daysUntilDue + " يوم)\n\n";
    });
  }
  
  // Footer
  email += "\n━━━━━━━━━━━━━━━━━━━━━━\n";
  email += "فتح الملف: " + SpreadsheetApp.getActiveSpreadsheet().getUrl() + "\n\n";
  email += "تم الإرسال تلقائياً من نظام " + CONFIG.companyName;
  
  return email;
}

/**
 * Check for instant alerts when data is edited
 */
function checkForInstantAlert(sheet, row) {
  const sheetName = sheet.getName();
  const today = new Date();
  
  if (sheetName === 'الشيكات') {
    const checkNumber = sheet.getRange(row, 1).getValue();
    const dueDate = new Date(sheet.getRange(row, 7).getValue());
    const amount = sheet.getRange(row, 8).getValue();
    const collectionDate = sheet.getRange(row, 9).getValue();
    
    if (!collectionDate && today > dueDate) {
      const daysDiff = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));
      const penalty = amount * (3.5/100/30) * daysDiff;
      
      // Send instant alert if penalty exceeds threshold
      if (penalty >= CONFIG.alerts.highPenaltyThreshold) {
        MailApp.sendEmail({
          to: CONFIG.primaryEmail,
          subject: "🚨 تنبيه: غرامة شيك عالية!",
          body: "⚠️ شيك #" + checkNumber + "\n\n" +
                "المبلغ: " + formatCurrency(amount) + " جنيه\n" +
                "متأخر: " + daysDiff + " يوم\n" +
                "الغرامة الحالية: " + formatCurrency(penalty) + " جنيه\n" +
                "المبلغ الإجمالي: " + formatCurrency(amount + penalty) + " جنيه\n\n" +
                "راجع الشيك: " + SpreadsheetApp.getActiveSpreadsheet().getUrl(),
          name: CONFIG.companyName
        });
      }
    }
  }
}

// ============================================
// HELPER FUNCTIONS
// ============================================

/**
 * Format number as Egyptian currency
 */
function formatCurrency(amount) {
  if (!amount || isNaN(amount)) return "0.00";
  return amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

/**
 * Format date in Arabic
 */
function formatDate(date) {
  if (!date) return "";
  const months = ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو',
                  'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'];
  return date.getDate() + ' ' + months[date.getMonth()] + ' ' + date.getFullYear();
}

/**
 * Test function - send test email
 */
function sendTestEmail() {
  MailApp.sendEmail({
    to: CONFIG.primaryEmail,
    subject: "✅ اختبار نظام التنبيهات",
    body: "تم إعداد نظام التنبيهات بنجاح!\n\n" +
          "سيتم إرسال التقارير اليومية في تمام الساعة " + CONFIG.dailyReportHour + " صباحاً بتوقيت القاهرة.\n\n" +
          "Crystal Power Investments",
    name: CONFIG.companyName
  });
  
  Logger.log('Test email sent');
}

هذا الكود مُحدّث ليعمل مع بياناتك الحقيقية ويرسل التنبيهات على البريدين: [email protected] و [email protected]

6

حفظ وتفعيل الأذونات

خطوات الحفظ والتفعيل:

  1. اضغط على أيقونة "حفظ" (أو Ctrl+S)
  2. اكتب اسم المشروع: "Crystal Power Notifications"
  3. من القائمة المنسدلة، اختر "setupDailyTrigger"
  4. اضغط على "تشغيل" (Run)
  5. اضغط على "مراجعة الأذونات" (Review permissions)
  6. اختر حساب Google الخاص بك
  7. اضغط على "متقدم" (Advanced)
  8. اضغط على "Go to Crystal Power Notifications (unsafe)"
  9. اضغط على "السماح" (Allow)

بعد الانتهاء، تحقق من سجل التنفيذ (Execution log) - يجب أن ترى رسالة: "Daily trigger set up successfully for 9 AM Africa/Cairo"

أمان: رسالة "unsafe" طبيعية لأن هذا مشروع شخصي. Google يحذر فقط لأنه لم يراجع الكود، لكنه آمن تماماً.

7

اختبار النظام

خطوات الاختبار:

  1. في محرر Apps Script، اختر "sendTestEmail" من القائمة المنسدلة
  2. اضغط على "تشغيل" (Run)
  3. راجع بريدك الإلكتروني ([email protected])
  4. يجب أن تستلم رسالة اختبار خلال دقيقة واحدة

نص رسالة الاختبار المتوقعة:

الموضوع: ✅ اختبار نظام التنبيهات
النص: تم إعداد نظام التنبيهات بنجاح!
سيتم إرسال التقارير اليومية في تمام الساعة 9 صباحاً بتوقيت القاهرة.

Crystal Power Investments

لم تستلم الرسالة؟ تحقق من مجلد البريد العشوائي (Spam) أولاً. إذا لم تجدها، راجع قسم "حل المشاكل" أدناه.

8

إدخال البيانات الحقيقية

العقارات (6 عقارات):

  • • عقار لايف - الغرافة - 482م² - 11,000,000 جنيه
  • • عقار لايف - 385م² - 2,000,000 جنيه
  • • عقار لايف النرجس - 214م² - 350,000 جنيه
  • • عقار لايف الرياض - 351م² - 10,000,000 جنيه
  • • قطعة أرض مدينة داب - 400م² - 250,000 جنيه
  • • قطعة أرض مدينة داب - 225م² - 225,000 جنيه

المدفوعات (11 دفعة):

  • • 27/03/2024 - 469,750 جنيه
  • • 27/04/2024 - 459,750 جنيه
  • • 27/05/2024 - 459,750 جنيه
  • • 27/06/2024 - 542,504 جنيه
  • • 27/03/2028 - 488,350 جنيه
  • • + 6 دفعات أخرى من ملفاتك

مثال على الشيكات:

رقم الشيك المستفيد الساحب المُحصّل المبلغ تاريخ الاستحقاق
12345 مؤمن محمد م. مصطفى أحمد خالد 50,000 01/11/2025
67890 م. مصطفى مؤمن محمد محمد علي 30,000 15/11/2025

يمكنك نسخ البيانات من النظام التفاعلي الذي أنشأناه سابقاً أو إدخالها يدوياً. تأكد من تطابق أسماء الأعمدة مع الكود.

كيف يعمل النظام

التقارير اليومية

  • • تُرسل تلقائياً في 9 صباحاً
  • • بتوقيت القاهرة يومياً
  • • فقط عند وجود تنبيهات
  • • تشمل الشيكات والمدفوعات المتأخرة

التنبيهات الفورية

  • • عندما تتجاوز الغرامة 500 جنيه
  • • تُرسل فور تعديل الملف
  • • تفاصيل الشيك والغرامة
  • • رابط مباشر للملف

المستلمون

التخصيص والإعدادات

تغيير وقت الإرسال:

ابحث عن هذا السطر في الكود:

dailyReportHour: 9, // 9 AM

غيّر الرقم 9 إلى الساعة المطلوبة (0-23)

تغيير حدود التنبيهات:

alerts: {
  highPenaltyThreshold: 500,  // EGP
  urgentOverdueDays: 7,
  upcomingDueDays: 3
}
  • 500: حد الغرامة للتنبيه الفوري
  • 7: الأيام المتأخرة العاجلة
  • 3: الأيام القادمة للتذكير

إضافة مستلمين إضافيين:

ابحث عن هذا السطر:

cc: CONFIG.backupEmail,

غيّره إلى:

حل المشكلات الشائعة

لم أستلم أي رسائل

  1. تحقق من مجلد البريد العشوائي (Spam/Junk)
  2. في Apps Script، اذهب إلى قائمة "Triggers" وتأكد من وجود trigger نشط
  3. راجع سجل التنفيذ (Execution log) للأخطاء
  4. شغّل دالة sendTestEmail() للتأكد من عمل البريد

الصيغ لا تعمل بشكل صحيح

  1. تأكد من تنسيق أعمدة التاريخ كـ "تاريخ" وليس نص
  2. تحقق من تطابق حروف الأعمدة مع الصيغ
  3. تأكد من توفر دالة TODAY() في Google Sheets
  4. تحقق من عدم وجود مسافات إضافية في الخلايا

أخطاء في Apps Script

  1. تأكد من أن أسماء الصفحات هي بالضبط: "العقارات"، "المدفوعات"، "الشيكات"
  2. تحقق من مواقع الأعمدة مطابقة لتوقعات الكود
  3. راجع نص الخطأ في Execution transcript للتفاصيل
  4. تأكد من عدم وجود صفوف فارغة في البداية

الميزات المتقدمة

تشغيل التقرير يدوياً:

  1. 1. Apps Script → تشغيل → sendDailyReport
  2. 2. راجع بريدك خلال دقيقة واحدة

مراجعة سجل التنفيذ:

  1. 1. Apps Script → Executions
  2. 2. مراجعة جميع التشغيلات مع التواريخ
  3. 3. تشخيص الأخطاء

تعديل قالب البريد:

ابحث عن دالة buildDailyEmail واعدّل نص البريد حسب الحاجة

إيقاف/تشغيل النظام:

Apps Script → Triggers → احذف trigger لإيقاف النظام، أو شغّل setupDailyTrigger لإعادة التشغيل

التحميلات والأدوات

تحميل هذا الدليل

احفظ الدليل كملف PDF للمراجعة

نسخ الكود كاملاً

انسخ كود Apps Script بنقرة واحدة

النظام التفاعلي

ارجع للنظام الذي بنيناه سابقاً

تم إعداد النظام بنجاح!

ما حصلت عليه:

  • ✅ نظام Google Sheets متكامل
  • ✅ تقارير يومية تلقائية (9 صباحاً)
  • ✅ حساب الغرامات (3.5% شهرياً)
  • ✅ نظام الشيكات الثلاثي
  • ✅ تنبيهات فورية
  • ✅ متوافق مع القانون المصري

الخطوات التالية:

  • 📊 أدخل بياناتك الحقيقية
  • 📧 تأكد من وصول رسالة الاختبار
  • ⚙️ خصّص الإعدادات حسب احتياجك
  • 📱 شارك الملف مع فريقك
  • 📈 راقب الأداء والتحصيل
  • 🔄 راجع التقارير الشهرية

الدعم: إذا واجهت أي مشكلة، راجع قسم "حل المشكلات" أعلاه أو ارجع لفريق كريستال باور للمساعدة.