Crystal Power Investments Logo

نظام متكامل لإدارة الشيكات والمدفوعات – Crystal Power Investments

دليل عملي لإعداد Google Sheets + Apps Script مع تقارير يومية 9 صباحاً وتنبيهات فورية

البريد: mmaisara@crystalpowerinvestment.com

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

1) إنشاء Google Sheet جديد

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

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

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

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

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

A رقم السندB تاريخ الاستحقاقC المبلغD تاريخ التحصيلE أيام التأخيرF نسبة الغرامةG مبلغ الغرامةH المبلغ الإجماليI حالة التحصيلJ ملاحظات
PAY-00127/03/2024469750(صيغة)(صيغة)(صيغة)(صيغة)قيد الانتظار-

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

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

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

A رقم المستثمرB الاسم الكاملC البريد الإلكترونيD رقم الهاتفE عدد العقاراتF إجمالي الاستثماراتG متوسط العائدH حالة الحساب
INV-001مؤمن محمدmmaisara@crystalpowerinvestment.com+20 XXX XXXX31335000012%نشط

نسخة 2.0 – تعليمات التنفيذ السريع

  1. إنشاء ملف Google Sheets: اسم الملف: Crystal Power Investments System
  2. إنشاء الصفحات بالأسماء الدقيقة: العقارات، المدفوعات، الشيكات، المستثمرين
  3. إنشاء الأعمدة: استخدم الجداول أدناه (عربي/إنجليزي)

1) أعمدة الصفحات (مطابقة لنسخة 2.0)

المدفوعات (Payments)

ColumnArabicEnglish
Aرقم المستندDocument Number
Bتاريخ الاستحقاقDue Date
CالمبلغAmount
Dتاريخ التحصيلCollection Date
Eأيام التأخيرDelay Days
Fمعدل الجزاءPenalty Rate
Gقيمة الجزاءPenalty Amount
HالإجماليTotal Due
IالحالةStatus

الشيكات (Checks)

ColumnArabicEnglish
Aرقم الشيكCheck Number
BالمستفيدBeneficiary
CالساحبDrawer
DالمحصلCollector
EالبنكBank
FالتاريخDate
Gتاريخ الاستحقاقDue Date
HالقيمةValue
Iتاريخ التحصيلCollection Date
Jأيام التأخيرDelay Days
Kجزاء 3.5%3.5% Penalty Amount
LالإجماليTotal Due
MالحالةStatus
NملاحظاتNotes

2) صيغ نسخة 2.0 (انسخ-ألصق من الأسفل)

المدفوعات

E2:
=IF(D2="", IF(B2D2, D2-B2, 0))
F2:
=3.5%/30
G2:
=IF(E2>0, C2*F2*E2, 0)
H2:
=C2+G2

انسخ لعدد 100 صف على الأقل.

الشيكات

J2:
=IF(I2="", IF(G2I2, I2-G2, 0))
K2:
=IF(J2>0, H2*(3.5%/30)*J2, 0)
L2:
=H2+K2

3) أتمتة نسخة 2.0 – المشغلات والاختبار

  1. افتح Extensions → Apps Script
  2. احذف الكود الافتراضي والصق الكود الأساسي (بالقسم أدناه) ثم شغّل: setupDailyTrigger و setupEditTrigger
  3. شغّل sendTestEmail للتحقق من البريد
  4. تحقق من قائمة Triggers أن المشغّل اليومي ظاهر

4) ترقية محسّنة (Enhanced)

  1. استبدل الكود الأساسي بالكود المحسّن (Enhanced)
  2. شغّل setupAllTriggers لإنشاء لوحة: 📊 Dashboard
  3. تحقق من تحديث البيانات كل 30 دقيقة والتقارير اليومية/الأسبوعية

5) دمج WhatsApp (اختياري – CallMeBot)

CONFIG.whatsapp مثال:
whatsapp: {
  enabled: true,
  apiKey: "YOUR_API_KEY_HERE",
  phoneNumber: "201012345678"
}

أرسل النص “I allow callmebot to send me messages” إلى +34 644 24 93 58 لتحصل على API Key.

6) الكود المحسّن V2.0 (انسخه كاملاً إلى Apps Script)

// ============================================
// CRYSTAL POWER INVESTMENTS - ENHANCED AUTOMATION V2.0
// Email: mmaisara@crystalpowerinvestment.com
// ============================================

const CONFIG = {
  primaryEmail: "mmaisara@crystalpowerinvestment.com",
  backupEmail: "maisaramoamen@gmail.com",
  companyName: "Crystal Power Investments",
  timezone: "Africa/Cairo",
  dailyReportHour: 9,
  weeklyReportHour: 9,
  weeklyReportDay: 'MONDAY', // MONDAY..SUNDAY
  alerts: {
    highPenaltyThreshold: 500,
    upcomingDueDays: 3
  },
  schedule: {
    dashboardMinutes: 30 // 1,5,10,15,30 allowed by Apps Script
  },
  whatsapp: {
    enabled: false,
    apiKey: "",
    phoneNumber: "" // 2010XXXXXXX without +
  }
};

function setupAllTriggers(){
  const triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(t=>ScriptApp.deleteTrigger(t));
  ScriptApp.newTrigger('sendDailyReport')
    .timeBased().atHour(CONFIG.dailyReportHour).everyDays(1)
    .inTimezone(CONFIG.timezone).create();
  ScriptApp.newTrigger('sendWeeklyReport')
    .timeBased().atHour(CONFIG.weeklyReportHour)
    .onWeekDay(ScriptApp.WeekDay[CONFIG.weeklyReportDay])
    .inTimezone(CONFIG.timezone).create();
  ScriptApp.newTrigger('refreshDashboard')
    .timeBased().everyMinutes(CONFIG.schedule.dashboardMinutes).create();
  ScriptApp.newTrigger('onEditTrigger')
    .forSpreadsheet(SpreadsheetApp.getActive()).onEdit().create();
  Logger.log('All triggers set (daily, weekly, dashboard, edit)');
}

function sendDailyReport(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const checks = ss.getSheetByName('الشيكات');
  const pays = ss.getSheetByName('المدفوعات');
  if(!checks || !pays){ Logger.log('Sheets missing'); return; }
  const today = new Date();
  const data = analyzeData(checks, pays, today);
  if(data.overdueChecks.length||data.overduePayments.length||data.upcomingChecks.length||data.upcomingPayments.length){
    const body = buildDailyEmail(data, today);
    MailApp.sendEmail({to:CONFIG.primaryEmail, cc:CONFIG.backupEmail, subject:'🔔 تقرير يومي - '+formatDate(today), body, name:CONFIG.companyName});
  }
}

function sendWeeklyReport(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const checks = ss.getSheetByName('الشيكات');
  const pays = ss.getSheetByName('المدفوعات');
  if(!checks || !pays){ return; }
  const today = new Date();
  const data = analyzeData(checks, pays, today);
  const body = 'تقرير أسبوعي\n\n'+buildDailyEmail(data, today);
  MailApp.sendEmail({to:CONFIG.primaryEmail, cc:CONFIG.backupEmail, subject:'📅 التقرير الأسبوعي - '+formatDate(today), body, name:CONFIG.companyName});
}

function onEditTrigger(e){
  const sheet = e.source.getActiveSheet();
  const name = sheet.getName();
  if(name!=='الشيكات' && name!=='المدفوعات') return;
  const row = e.range.getRow(); if(row===1) return;
  checkForInstantAlert(sheet, row);
}

function refreshDashboard(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const checks = ss.getSheetByName('الشيكات');
  const pays = ss.getSheetByName('المدفوعات');
  if(!checks || !pays){ return; }
  const today = new Date();
  const data = analyzeData(checks, pays, today);
  const dashName = '📊 Dashboard';
  let dash = ss.getSheetByName(dashName);
  if(!dash){ dash = ss.insertSheet(dashName); dash.getRange('A1:B1').setValues([["KPI","Value"]]); dash.getRange('A1:B1').setFontWeight('bold'); }
  const rows = [
    ['Overdue Checks', data.overdueChecks.length],
    ['Overdue Payments', data.overduePayments.length],
    ['Upcoming Checks (≤'+CONFIG.alerts.upcomingDueDays+'d)', data.upcomingChecks.length],
    ['Upcoming Payments (≤'+CONFIG.alerts.upcomingDueDays+'d)', data.upcomingPayments.length],
    ['Total Penalties (EGP)', Number(data.totalPenalties.toFixed(2))],
    ['Last Refresh', formatDate(today)+' '+Utilities.formatDate(today, CONFIG.timezone, 'HH:mm')]
  ];
  dash.clearContents();
  dash.getRange(1,1,1,2).setValues([["KPI","Value"]]).setFontWeight('bold');
  dash.getRange(2,1,rows.length,2).setValues(rows);
}

function analyzeData(checksSheet, paymentsSheet, today){
  const checksData = checksSheet.getDataRange().getValues();
  const paymentsData = paymentsSheet.getDataRange().getValues();
  let overdueChecks=[], upcomingChecks=[], overduePayments=[], upcomingPayments=[], totalPenalties=0;
  for(let i=1;i0){ const p = amt*(3.5/100/30)*days; totalPenalties+=p; overdueChecks.push({number:num,days,amount:amt,penalty:p,beneficiary:r[1]}); }
      else if(days>=-CONFIG.alerts.upcomingDueDays){ upcomingChecks.push({number:num,amount:amt,dueDate:due,daysUntilDue:Math.abs(days)}); }
    }
  }
  for(let i=1;i0){ const p=amt*(3.5/100/30)*days; totalPenalties+=p; overduePayments.push({id,days,amount:amt,penalty:p}); }
      else if(days>=-CONFIG.alerts.upcomingDueDays){ upcomingPayments.push({id,amount:amt,dueDate:due,daysUntilDue:Math.abs(days)}); }
    }
  }
  return {overdueChecks, upcomingChecks, overduePayments, upcomingPayments, totalPenalties};
}

function buildDailyEmail(data, today){
  let email = 'مرحباً مؤمن،\n\n';
  email += '📊 تقرير يوم '+formatDate(today)+'\n\n';
  if(data.overdueChecks.length||data.overduePayments.length){
    email += '🔴 تنبيهات عاجلة:\n';
    if(data.overdueChecks.length) email += '• '+data.overdueChecks.length+' شيك متأخر\n';
    if(data.overduePayments.length) email += '• '+data.overduePayments.length+' دفعة متأخرة\n';
    email += '• إجمالي الغرامات: '+formatCurrency(data.totalPenalties)+' جنيه\n\n';
  }
  if(data.overdueChecks.length){ email+='📋 الشيكات المتأخرة:\n'; data.overdueChecks.forEach(c=>{ email+='• شيك #'+c.number+' — '+formatCurrency(c.amount)+'ج — '+c.days+' يوم\n'; }); email+='\n'; }
  if(data.overduePayments.length){ email+='💰 المدفوعات المتأخرة:\n'; data.overduePayments.forEach(p=>{ email+='• سند #'+p.id+' — '+formatCurrency(p.amount)+'ج — '+p.days+' يوم\n'; }); email+='\n'; }
  if(data.upcomingChecks.length){ email+='⏰ شيكات مستحقة قريباً:\n'; data.upcomingChecks.forEach(c=>{ email+='• #'+c.number+' — '+formatCurrency(c.amount)+'ج — خلال '+c.daysUntilDue+' يوم\n'; }); email+='\n'; }
  if(data.upcomingPayments.length){ email+='📅 دفعات مستحقة قريباً:\n'; data.upcomingPayments.forEach(p=>{ email+='• #'+p.id+' — '+formatCurrency(p.amount)+'ج — خلال '+p.daysUntilDue+' يوم\n'; }); email+='\n'; }
  email += '\nفتح الملف: '+SpreadsheetApp.getActiveSpreadsheet().getUrl();
  email += '\nتم الإرسال تلقائياً من نظام '+CONFIG.companyName;
  return email;
}

function checkForInstantAlert(sheet, row){
  const name = sheet.getName(); const today = new Date();
  if(name==='الشيكات'){
    const num = sheet.getRange(row,1).getValue();
    const due = new Date(sheet.getRange(row,7).getValue());
    const amt = sheet.getRange(row,8).getValue();
    const collected = sheet.getRange(row,9).getValue();
    if(!collected && today>due){
      const days = Math.floor((today-due)/(1000*60*60*24));
      const penalty = amt*(3.5/100/30)*days;
      if(penalty>=CONFIG.alerts.highPenaltyThreshold){
        const msg = '🚨 شيك #'+num+' تأخر '+days+' يوم. غرامة حالية: '+formatCurrency(penalty)+'ج';
        MailApp.sendEmail({to:CONFIG.primaryEmail, subject:'🚨 تنبيه: غرامة شيك عالية!', body: msg+'\n\n'+SpreadsheetApp.getActiveSpreadsheet().getUrl(), name:CONFIG.companyName});
        if(CONFIG.whatsapp.enabled) notifyWhatsApp(msg);
      }
    }
  }
}

function notifyWhatsApp(message){
  if(!CONFIG.whatsapp.enabled) return;
  if(!CONFIG.whatsapp.apiKey || !CONFIG.whatsapp.phoneNumber) return;
  const url = 'https://api.callmebot.com/whatsapp.php?phone=' + CONFIG.whatsapp.phoneNumber + '&text=' + encodeURIComponent(message) + '&apikey=' + CONFIG.whatsapp.apiKey;
  try{ UrlFetchApp.fetch(url, {muteHttpExceptions:true}); } catch(err){ Logger.log('WA error: '+err); }
}

function sendWhatsAppTest(){
  notifyWhatsApp('اختبار نظام التنبيهات – Crystal Power Investments');
}

function formatCurrency(n){ if(!n || isNaN(n)) return '0.00'; return Number(n).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','); }
function formatDate(d){ const m=['يناير','فبراير','مارس','أبريل','مايو','يونيو','يوليو','أغسطس','سبتمبر','أكتوبر','نوفمبر','ديسمبر']; return d.getDate()+' '+m[d.getMonth()]+' '+d.getFullYear(); }

6) قائمة التحقق (نسخة 2.0)

3) إضافة الصيغ التلقائية (منسوخة وجاهزة)

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

E2 (أيام التأخير):
=IF(D2="", IF(ISNUMBER(B2), MAX(TODAY()-B2, 0), 0), IF(AND(ISNUMBER(D2), ISNUMBER(B2)), MAX(D2-B2, 0), 0))
F2 (نسبة الغرامة اليومية):
=3.5%/30
G2 (مبلغ الغرامة):
=IF(E2>0, C2*F2*E2, 0)
H2 (المبلغ الإجمالي):
=C2+G2

بعد إدخال كل صيغة اضغط Enter ثم اسحب لأسفل لنسخها لباقي الصفوف.

صفحة الشيكات

J2 (أيام التأخير):
=IF(I2="", IF(ISNUMBER(G2), MAX(TODAY()-G2, 0), 0), IF(AND(ISNUMBER(I2), ISNUMBER(G2)), MAX(I2-G2, 0), 0))
K2 (الغرامة 3.5%):
=IF(J2>0, H2*(3.5%/30)*J2, 0)
L2 (المبلغ الإجمالي):
=H2+K2

4) فتح محرر Apps Script

  1. الملحقات (Extensions) ← Apps Script
  2. احذف أي كود موجود
  3. انسخ الكود الكامل أدناه والصقه في Code.gs

5) كود Apps Script الكامل

// ============================================
// CRYSTAL POWER INVESTMENTS - AUTOMATED NOTIFICATIONS
// Email: mmaisara@crystalpowerinvestment.com
// ============================================

// CONFIGURATION
const CONFIG = {
  primaryEmail: "mmaisara@crystalpowerinvestment.com",
  backupEmail: "maisaramoamen@gmail.com",
  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');
}

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

  1. احفظ (Ctrl+S) وأعد تسمية المشروع: Crystal Power Notifications
  2. من القائمة اختر: setupDailyTrigger ← تشغيل (Run)
  3. راجع الأذونات واختر حسابك ثم “السماح”
  4. تأكد من رسالة السجل: Daily trigger set up successfully…

رسالة “unsafe” طبيعية لأن المشروع خاص وغير مُراجع من Google.

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

  1. اختر الدالة sendTestEmail ← تشغيل
  2. تحقق من البريد: mmaisara@crystalpowerinvestment.com (وأيضاً Spam)
  3. ينبغي وصول رسالة اختبار خلال دقيقة

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

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

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

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

ملاحظة حول مشروع Asset Management (GitHub)

المزايا مثل تسجيل الدخول، الأدوار، قاعدة بيانات، وواجهات برمجية تتطلب خادم Backend. هذا الدليل والصفحة الحالية واجهة ثابتة فقط. يمكننا بناء واجهة أولية وبيانات باستخدام RESTful Table API هنا، ثم تسليم مواصفات لـ Backend منفصل. إن رغبت، أقدم لك خطة مراحل وتقدير زمني/مالي كمستند منفصل.