المقدمة – ما سيتم إعداده
- Google Sheets مع 4 صفحات: العقارات، المدفوعات، الشيكات، المستثمرين
- تقارير يومية تلقائية كل يوم 9 صباحاً بتوقيت القاهرة
- تنبيهات فورية للشيكات والمدفوعات المتأخرة
- حساب غرامات 3.5% شهرياً (متناسب يومياً)
- نظام شيكات ثلاثي: المستفيد – الساحب – المُحصّل
- تتبع ROI والضرائب المصرية + تنبيهات بريدية
1) إنشاء Google Sheet جديد
- اذهب إلى sheets.google.com
- اضغط “فارغ” لإنشاء ملف جديد
- اسم الملف: “نظام كريستال باور للاستثمار”
- احفظه في Google Drive. استخدم حسابك: mmaisara@crystalpowerinvestment.com
2) إنشاء هيكل الصفحات
الصفحة: العقارات
| A رقم الأصل | B اسم العقار | C الموقع | D نوع العقار | E المساحة م² | F رقم الوحدة | G المبلغ المستثمر | H تاريخ الشراء | I القيمة الحالية | J نسبة العائد | K حالة العقار | L ملاحظات |
|---|---|---|---|---|---|---|---|---|---|---|---|
| PROP-001 | عقار لايف | الغرافة | سكني | 482 | 58 | 11000000 | 13/05/2023 | 12100000 | 10% | نشط | - |
الصفحة: المدفوعات
| A رقم السند | B تاريخ الاستحقاق | C المبلغ | D تاريخ التحصيل | E أيام التأخير | F نسبة الغرامة | G مبلغ الغرامة | H المبلغ الإجمالي | I حالة التحصيل | J ملاحظات |
|---|---|---|---|---|---|---|---|---|---|
| PAY-001 | 27/03/2024 | 469750 | (صيغة) | (صيغة) | (صيغة) | (صيغة) | قيد الانتظار | - |
الصفحة: الشيكات (نظام ثلاثي)
| A رقم الشيك | B المستفيد | C الساحب | D المُحصّل | E البنك | F تاريخ الشيك | G تاريخ الاستحقاق | H قيمة الشيك | I تاريخ التحصيل | J أيام التأخير | K الغرامة 3.5% | L المبلغ الإجمالي | M حالة الشيك | N ملاحظات |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12345 | مؤمن محمد | م. مصطفى | أحمد خالد | بنك مصر | 15/10/2025 | 01/11/2025 | 50000 | (صيغة) | (صيغة) | (صيغة) | قيد الانتظار | - |
الصفحة: المستثمرين
| A رقم المستثمر | B الاسم الكامل | C البريد الإلكتروني | D رقم الهاتف | E عدد العقارات | F إجمالي الاستثمارات | G متوسط العائد | H حالة الحساب |
|---|---|---|---|---|---|---|---|
| INV-001 | مؤمن محمد | mmaisara@crystalpowerinvestment.com | +20 XXX XXXX | 3 | 13350000 | 12% | نشط |
نسخة 2.0 – تعليمات التنفيذ السريع
- إنشاء ملف Google Sheets: اسم الملف: Crystal Power Investments System
- إنشاء الصفحات بالأسماء الدقيقة: العقارات، المدفوعات، الشيكات، المستثمرين
- إنشاء الأعمدة: استخدم الجداول أدناه (عربي/إنجليزي)
1) أعمدة الصفحات (مطابقة لنسخة 2.0)
المدفوعات (Payments)
| Column | Arabic | English |
|---|---|---|
| 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)
| Column | Arabic | English |
|---|---|---|
| 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 – المشغلات والاختبار
- افتح Extensions → Apps Script
- احذف الكود الافتراضي والصق الكود الأساسي (بالقسم أدناه) ثم شغّل: setupDailyTrigger و setupEditTrigger
- شغّل sendTestEmail للتحقق من البريد
- تحقق من قائمة Triggers أن المشغّل اليومي ظاهر
4) ترقية محسّنة (Enhanced)
- استبدل الكود الأساسي بالكود المحسّن (Enhanced)
- شغّل setupAllTriggers لإنشاء لوحة: 📊 Dashboard
- تحقق من تحديث البيانات كل 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
- الملحقات (Extensions) ← Apps Script
- احذف أي كود موجود
- انسخ الكود الكامل أدناه والصقه في 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) حفظ وتفعيل الأذونات
- احفظ (Ctrl+S) وأعد تسمية المشروع: Crystal Power Notifications
- من القائمة اختر:
setupDailyTrigger← تشغيل (Run) - راجع الأذونات واختر حسابك ثم “السماح”
- تأكد من رسالة السجل: Daily trigger set up successfully…
رسالة “unsafe” طبيعية لأن المشروع خاص وغير مُراجع من Google.
7) اختبار النظام
- اختر الدالة
sendTestEmail← تشغيل - تحقق من البريد: mmaisara@crystalpowerinvestment.com (وأيضاً Spam)
- ينبغي وصول رسالة اختبار خلال دقيقة
8) إدخال البيانات الحقيقية
- العقارات: أدخل 6 أصول كما في الأمثلة
- المدفوعات: 11 دفعة (تأكد تنسيق التاريخ Date)
- الشيكات: أضف الأعمدة بالضبط كما بالأعلى لتعمل الصيغ والكود
كيف يعمل النظام
- التقارير اليومية 9 صباحاً بتوقيت القاهرة – تُرسل فقط عند وجود تنبيهات أو استحقاقات قريبة
- تنبيه فوري إذا تجاوزت غرامة الشيك 500 جنيه أثناء التحرير
- المستلمون: المالك + نسخة إلى: maisaramoamen@gmail.com
التخصيص والإعدادات
- تغيير وقت الإرسال: dailyReportHour: 9 ← غيّر إلى ساعة 0–23
- حدود التنبيهات:
alerts: { highPenaltyThreshold: 500, urgentOverdueDays: 7, upcomingDueDays: 3 } - إضافة مستلمين إضافيين: غيّر cc في MailApp.sendEmail
حل المشكلات الشائعة
- لا توجد رسائل: تفقد Spam + تحقق من Triggers + شغّل sendTestEmail
- الصيغ: تأكد من تنسيق التاريخ كـ Date وعدم وجود مسافات زائدة
- أسماء الصفحات يجب أن تكون: “العقارات”، “المدفوعات”، “الشيكات”
ملاحظة حول مشروع Asset Management (GitHub)
المزايا مثل تسجيل الدخول، الأدوار، قاعدة بيانات، وواجهات برمجية تتطلب خادم Backend. هذا الدليل والصفحة الحالية واجهة ثابتة فقط. يمكننا بناء واجهة أولية وبيانات باستخدام RESTful Table API هنا، ثم تسليم مواصفات لـ Backend منفصل. إن رغبت، أقدم لك خطة مراحل وتقدير زمني/مالي كمستند منفصل.
