أتمتة فواتير Gmail إلى Sheets بدون نسخ ولصق
مستوى القارئ: متوسط
هتخرج من المقال ده بworkflow بسيط يوفر عليك حوالي 45 دقيقة أسبوعيًا: يقرأ فواتير Gmail، يحفظ مرفقات PDF في Drive، ويسجل صف واضح في Google Sheets.
المشكلة باختصار
لو عندك شركة صغيرة أو فريق مشتريات بيستقبل 20 إلى 40 فاتورة في الأسبوع، النسخ واللصق اليدوي بيعمل مشكلتين. الأولى إن فاتورة ممكن تضيع وسط البريد. الثانية إن نفس الفاتورة ممكن تتسجل مرتين لو أكثر من شخص فتحها.
الطريقة الشائعة هي تحميل كل PDF يدويًا ثم تسمية الملف وكتابة المورد والتاريخ في Sheet. الطريقة دي بتفشل لما البريد يزيد، أو لما المورد يرسل نفس الفاتورة مرة تانية بعد تعديل بسيط.
الفكرة الأساسية
ركز. إحنا مش بنبني نظام محاسبة. إحنا بنبني طبقة تنظيم أولى. Apps Script يشتغل كل ساعة، يبحث في Gmail عن رسائل عليها label اسمه Invoices، يأخذ أول مرفق PDF، يحفظه في Drive، ثم يضيف صف في Sheet.
الافتراض إن عندك حجم صغير أو متوسط: أقل من 50 رسالة فاتورة في التشغيل الواحد. لو عندك آلاف الفواتير يوميًا، انت محتاج queue ونظام معالجة مخصص، مش سكربت داخل Google Workspace.
المفهوم المهم هنا اسمه dedupe. يعني منع التكرار. مثال بسيط: لو عامل أمن بيسجل أسماء الزوار في دفتر، لازم يبص هل الاسم اتكتب قبل كده في نفس اليوم. في السكربت، هنستخدم messageId كرقم زيارة. لو موجود في Sheet، الرسالة تتساب.
الإعداد العملي
- اعمل label في Gmail اسمه
Invoicesوحط عليه رسائل الفواتير يدويًا أو بفلتر Gmail. - اعمل Google Sheet باسم
Invoice Registerواكتب الأعمدة: Date, Vendor, Subject, File URL, Message ID. - اعمل folder في Google Drive باسم
Invoices Archive. - افتح Apps Script من داخل Google Sheets، ثم الصق الكود التالي.
const DRIVE_FOLDER_ID = 'PUT_FOLDER_ID_HERE';
const SHEET_NAME = 'Invoices';
function importInvoicesFromGmail() {
const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const folder = DriveApp.getFolderById(DRIVE_FOLDER_ID);
const processedIds = new Set(
sheet.getRange(2, 5, Math.max(sheet.getLastRow() - 1, 1), 1)
.getValues().flat().filter(Boolean)
);
const threads = GmailApp.search('label:Invoices has:attachment filename:pdf newer_than:30d', 0, 50);
threads.forEach(thread => {
thread.getMessages().forEach(message => {
const messageId = message.getId();
if (processedIds.has(messageId)) return;
const pdf = message.getAttachments().find(a =>
a.getContentType() === 'application/pdf'
);
if (!pdf) return;
const vendor = message.getFrom().replace(/<.*>/, '').trim();
const file = folder.createFile(pdf.copyBlob())
.setName(`${message.getDate().toISOString().slice(0,10)}-${pdf.getName()}`);
sheet.appendRow([
message.getDate(), vendor, message.getSubject(), file.getUrl(), messageId
]);
processedIds.add(messageId);
});
});
}
شغلها تلقائيًا
من Apps Script افتح Triggers، واعمل trigger للدالة importInvoicesFromGmail كل ساعة. Google بتسمي النوع ده time-driven trigger. هو قريب من cron، لكنه داخل Google Workspace وبيشتغل بحساب الشخص اللي أنشأه.
اللي بيحصل فعلاً: كل ساعة السكربت يراجع آخر 30 يوم، يأخذ أول 50 thread فقط، ويتجاهل أي messageId اتسجل قبل كده. الرقم 50 مقصود. بتكسب تشغيل أسرع وحدود أوضح، وبتخسر إن backlog ضخم محتاج أكثر من تشغيل علشان يخلص.
الـ trade-off هنا
أفضل طريقة للبداية هي Apps Script لأنه قريب من Gmail وSheets وDrive، ومش محتاج سيرفر. المكسب: صفر بنية تحتية تقريبًا، وتنفيذ أولي في أقل من ساعة. التكلفة: صلاحيات Gmail واسعة، وحدود تشغيل Apps Script، وعدم وجود parsing ذكي لمحتوى PDF.
لو عايزها تدعم استخراج المبلغ ورقم الفاتورة من داخل PDF، أضف خطوة OCR أو استخدم Document AI. بس ده يزود التعقيد والتكلفة. ابدأ بتسجيل المرفق والرابط والمورد. بعد أسبوعين من الاستخدام، قرر هل استخراج البيانات يستحق.
متى لا تستخدم هذه الطريقة
ما تستخدمش الطريقة دي لو الفواتير فيها بيانات مالية حساسة جدًا ولازم تمر عبر نظام موافقات رسمي. ما تستخدمهاش كمان لو عندك أكثر من 500 فاتورة يوميًا، أو لو مطلوب audit trail كامل لا يعتمد على حساب شخص واحد. في الحالات دي، استخدم نظام محاسبة أو workflow engine فيه صلاحيات ومراجعات.
مصادر اعتمد عليها
- توثيق GmailApp الرسمي لاستخدام البحث وقراءة الرسائل والمرفقات.
- توثيق installable triggers لتشغيل الدوال تلقائيًا حسب الوقت.
- توثيق triggers في Apps Script لفهم الفرق بين simple وinstallable triggers.
الخطوة التالية
الخطوة التالية: اعمل label باسم Invoices على 5 رسائل فقط، شغّل السكربت يدويًا مرة واحدة، وتأكد إن Sheet سجلت 5 صفوف بدون تكرار. بعد كده فعّل trigger كل ساعة.