الأبحاث · جولةُ البرمجة الأولى
CB-3-05 — قواعد بيانات وSQL · SQL · متوسّط
جولةُ البرمجة الأولى · 2026-10-17 · GAIBRA v2.31.03 · واجبُ التشغيل
| الترتيب | المتنافس | الرمز أمام المحكّم | الكلمات | الاختبارات | Opus | Grok | المجموع | الصدق (Opus · Grok) |
|---|---|---|---|---|---|---|---|---|
| 1 | GAIBRA | هـ | 1,458 | 15/15 | 97.0 | 100.0 | 98.5 | 12 · 12 |
| 2 | Claude Sonnet | ج | 1,167 | 15/15 | 93.0 | 91.0 | 92.0 | 12 · 10 |
| 3 | ChatGPT | أ | 1,143 | 15/15 | 85.0 | 94.0 | 89.5 | 7 · 11 |
| 4 | DeepSeek | د | 1,154 | 15/15 | 78.0 | 90.0 | 84.0 | 5 · 10 |
| 5 | Gemini | ب | 845 | 15/15 | 71.0 | 81.0 | 76.0 | 3 · 8 |
نصُّ السيناريو كما كتبه المولّد
عندنا 3 فروع لمطعم برجر، وكل فرع يرسل مبيعاته كصفوف orders وorder\_items إلى قاعدة مركزية SQLite. المالية تريد استعلاماً واحداً يخرج تقريراً يومياً لكل فرع بالشكل التالي:
| branch | day | gross\_sales | discounts | refunds | net\_sales | vat\_15 | top\_item | top\_item\_qty |
القواعد:
gross\_sales: مجموع unit\_price \* qty لكل البنود (السعر يُخزن شامل الضريبة في order\_items).
discounts: مجموع عمود discount\_amount من رأس الطلب.
refunds: البنود المرتبطة بمرتجع (order\_items.is\_return = 1) تُطرح من المبيعات الإجمالية وتظهر سطراً مستقلاً في عمود refunds بقيمتها المطلقة.
net\_sales = gross\_sales - discounts - refunds، ثم vat\_15 = net\_sales - net\_sales/1.15 مقرّباً إلى رقمين عشريين.
top\_item: الصنف الأعلى عدداً (مجموع qty، مع طرح الكميات المرتجعة) في ذلك اليوم والفرع؛ عند التعادل يُختار الاسم الأبجدي أولاً.
الطلبات بحالة 'cancelled' تُستبعد كلياً.
نريد النتائج حتى يوم معيّن يمرّر كمعامل في الاستعلام، ولكل فرع فقط أيام له فيها مبيعات فعلية.
هذا تعريف الجداول والبيانات الأولية (SQLite، أرقام عشرية REAL أو NUMERIC كما تراه مناسباً):
sql
CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  branch\_id INTEGER NOT NULL REFERENCES branches(id),
  ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM'
  status TEXT NOT NULL, -- 'completed' | 'cancelled'
  discount\_amount REAL NOT NULL DEFAULT 0
);
CREATE TABLE order\_items (
  id INTEGER PRIMARY KEY,
  order\_id INTEGER NOT NULL REFERENCES orders(id),
  item\_name TEXT NOT NULL,
  unit\_price REAL NOT NULL, -- شامل الضريبة
  qty INTEGER NOT NULL,
  is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع
);
البيانات (ألصقها في ملف واحد مع الاستعلام):
sql
INSERT INTO branches (id, name) VALUES (1,'العليا'), (2,'الروضة'), (3,'النرجس');
\-- العليا 2026-10-01: طلبان مكتملان، أحدهما فيه مرتجع 1 برجر كلاسيك، وخصم 5
\-- الروضة 2026-10-01: طلب مكتمل + طلب ملغى يجب أن لا يظهر
\-- النرجس 2026-10-01: طلب واحد مكتمل، تعادل في الكمية بين صنفين
\-- (اكتب INSERTs تفصيلية كافية لإبراز كل قاعدة أعلاه: بيانات 8-12 بنود كافية)
المطلوب: ملف report.sql واحد قابل للتشغيل بـ sqlite3 report.sql من البداية للنهاية (بناء الجداول + البيانات + الاستعلام النهائي المُعلّم بـ -- REPORT QUERY، مع قيمة يوم المعيّن مثبّتة داخله). لا دوال معرّفة منك خارج SQL القياسي لـ SQLite (أي CTEs و window functions مسموحة).
الردودُ الخمسة كما وصلت المحكّمين
هـGAIBRA
\-- report.sql
\-- تقرير المبيعات اليومي لكل فرع — SQLite 3.25 فأحدث (دوال النوافذ)
\-- التشغيل: sqlite3 :memory: < report.sql
.bail on
.headers on
.mode column
PRAGMA foreign\_keys = ON;
\-- ───────── المخطّط كما ورد في المواصفة ─────────
CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  branch\_id INTEGER NOT NULL REFERENCES branches(id),
  ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM'
  status TEXT NOT NULL, -- 'completed' | 'cancelled'
  discount\_amount REAL NOT NULL DEFAULT 0
);
CREATE TABLE order\_items (
  id INTEGER PRIMARY KEY,
  order\_id INTEGER NOT NULL REFERENCES orders(id),
  item\_name TEXT NOT NULL,
  unit\_price REAL NOT NULL, -- شامل الضريبة
  qty INTEGER NOT NULL,
  is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع
);
\-- فهارس لمسار التقرير: نطاق التاريخ أوّلاً ثمّ ربط البنود بطلباتها
CREATE INDEX idx\_orders\_ordered\_at ON orders (ordered\_at);
CREATE INDEX idx\_order\_items\_order\_id ON order\_items (order\_id);
\-- ───────── معاملات التقرير (مثبّتة داخل الملف) ─────────
\-- صفّ واحد فقط؛ التاريخ غير الصالح (مثل 2026-02-30) يكسر CHECK فيتوقّف الملف بخطأ صريح
CREATE TEMP TABLE report\_params (
  id INTEGER PRIMARY KEY CHECK (id = 1),
  report\_end\_day TEXT NOT NULL CHECK (report\_end\_day = date(report\_end\_day)),
  cancelled\_status TEXT NOT NULL,
  vat\_inclusive\_divisor REAL NOT NULL CHECK (vat\_inclusive\_divisor > 1)
);
INSERT INTO report\_params (id, report\_end\_day, cancelled\_status, vat\_inclusive\_divisor)
VALUES (1, '2026-10-02', 'cancelled', 1.15);
\-- ───────── البيانات ─────────
BEGIN TRANSACTION;
INSERT INTO branches (id, name) VALUES (1,'العليا'), (2,'الروضة'), (3,'النرجس');
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
  -- العليا 2026-10-01: طلبان مكتملان؛ الخصم 5 على طلب فيه بندان (يكشف تكرار الخصم عند الربط)
  (1, 1, '2026-10-01 12:15', 'completed', 5),
  (2, 1, '2026-10-01 19:40', 'completed', 0),
  -- الروضة 2026-10-01: طلب مكتمل + طلب ملغى بخصم وكمية كبيرة يجب ألّا يؤثّرا
  (3, 2, '2026-10-01 13:05', 'completed', 0),
  (4, 2, '2026-10-01 20:30', 'cancelled', 3),
  -- النرجس 2026-10-01: طلب واحد فيه تعادل كمّيّ بين صنفين
  (5, 3, '2026-10-01 14:10', 'completed', 0),
  -- العليا 2026-10-02: يوم ثانٍ داخل الحدّ
  (6, 1, '2026-10-02 13:00', 'completed', 0),
  -- النرجس 2026-10-03: بعد يوم التقرير، يجب ألّا يظهر
  (7, 3, '2026-10-03 12:00', 'completed', 0);
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (1, 1, 'برجر كلاسيك', 25.00, 2, 0),
  (2, 1, 'بطاطس', 9.00, 2, 0),
  (3, 2, 'برجر كلاسيك', 25.00, 1, 0),
  (4, 2, 'برجر دجاج', 22.00, 3, 0),
  (5, 2, 'برجر كلاسيك', 25.00, 1, 1), -- مرتجع: لولا طرحه لتصدّر الكلاسيك بدل الدجاج
  (6, 3, 'برجر كلاسيك', 25.00, 1, 0),
  (7, 3, 'مشروب غازي', 6.00, 2, 0),
  (8, 4, 'برجر دجاج', 22.00, 4, 0), -- داخل طلب ملغى
  (9, 5, 'بطاطس', 9.00, 2, 0),
  (10, 5, 'برجر كلاسيك', 25.00, 2, 0), -- تعادل مع البطاطس عند 2
  (11, 6, 'برجر كلاسيك', 25.00, 1, 0),
  (12, 7, 'برجر دجاج', 22.00, 1, 0); -- بعد يوم التقرير
COMMIT;
\-- REPORT QUERY
WITH
params AS (
  SELECT report\_end\_day,
  date(report\_end\_day, '+1 day') AS exclusive\_upper\_bound,
  cancelled\_status,
  vat\_inclusive\_divisor
  FROM report\_params
  WHERE id = 1
),
eligible\_orders AS (
  -- الطلبات غير الملغاة حتى نهاية يوم التقرير شاملاً
  SELECT o.id AS order\_id,
  o.branch\_id,
  date(o.ordered\_at) AS sales\_day,
  o.discount\_amount
  FROM orders AS o
  CROSS JOIN params AS p
  WHERE o.status <> p.cancelled\_status
  AND o.ordered\_at < p.exclusive\_upper\_bound
  AND date(o.ordered\_at) IS NOT NULL
),
order\_discounts AS (
  -- الخصم من رأس الطلب، مجموعاً قبل أيّ ربط بالبنود
  SELECT branch\_id, sales\_day, SUM(discount\_amount) AS discounts
  FROM eligible\_orders
  GROUP BY branch\_id, sales\_day
),
item\_lines AS (
  SELECT eo.branch\_id,
  eo.sales\_day,
  oi.item\_name,
  CASE WHEN oi.is\_return = 1 THEN 0 ELSE oi.unit\_price \* oi.qty END AS sale\_amount,
  CASE WHEN oi.is\_return = 1 THEN ABS(oi.unit\_price \* oi.qty) ELSE 0 END AS refund\_amount,
  CASE WHEN oi.is\_return = 1 THEN -ABS(oi.qty) ELSE oi.qty END AS signed\_qty
  FROM eligible\_orders AS eo
  JOIN order\_items AS oi ON oi.order\_id = eo.order\_id
),
item\_totals AS (
  SELECT branch\_id, sales\_day,
  SUM(sale\_amount) AS gross\_sales,
  SUM(refund\_amount) AS refunds
  FROM item\_lines
  GROUP BY branch\_id, sales\_day
),
item\_quantities AS (
  SELECT branch\_id, sales\_day, item\_name, SUM(signed\_qty) AS net\_qty
  FROM item\_lines
  GROUP BY branch\_id, sales\_day, item\_name
),
ranked\_items AS (
  SELECT branch\_id, sales\_day, item\_name, net\_qty,
  ROW\_NUMBER() OVER (
  PARTITION BY branch\_id, sales\_day
  ORDER BY net\_qty DESC, item\_name COLLATE BINARY ASC
  ) AS item\_rank
  FROM item\_quantities
),
daily\_totals AS (
  SELECT it.branch\_id,
  it.sales\_day,
  ROUND(it.gross\_sales, 2) AS gross\_sales,
  ROUND(COALESCE(od.discounts, 0), 2) AS discounts,
  ROUND(it.refunds, 2) AS refunds,
  ROUND(it.gross\_sales - COALESCE(od.discounts, 0) - it.refunds, 2) AS net\_sales
  FROM item\_totals AS it
  LEFT JOIN order\_discounts AS od
  ON od.branch\_id = it.branch\_id AND od.sales\_day = it.sales\_day
  WHERE it.gross\_sales > 0 -- أيام فيها مبيعات فعليّة فقط
)
SELECT b.name AS branch,
  dt.sales\_day AS day,
  dt.gross\_sales,
  dt.discounts,
  dt.refunds,
  dt.net\_sales,
  ROUND(dt.net\_sales - dt.net\_sales / p.vat\_inclusive\_divisor, 2) AS vat\_15,
  ri.item\_name AS top\_item,
  ri.net\_qty AS top\_item\_qty
FROM daily\_totals AS dt
JOIN branches AS b ON b.id = dt.branch\_id
JOIN ranked\_items AS ri ON ri.branch\_id = dt.branch\_id
  AND ri.sales\_day = dt.sales\_day
  AND ri.item\_rank = 1
CROSS JOIN params AS p
ORDER BY b.id, dt.sales\_day;
الشرح
الاستعلام كلّه مبنيٌّ على أنّ الخصم يعيش في رأس الطلب والمبيعات تعيش في البنود، فلا يصحّ جمعهما من ربطٍ واحد. لو ربطتَ orders بـ order\_items ثمّ جمعتَ discount\_amount لتكرّر خصم الطلب بعدد بنوده: الطلب 1 في العليا عليه خصم 5 وفيه بندان، فيظهر الخصم 10 ويهبط الصافي دون سبب. لهذا يُجمع الخصم في order\_discounts على مستوى الطلب، وتُجمع البنود في item\_totals مستقلّة، ثمّ يلتقيان على الفرع واليوم. هذا أشيع عيبٍ في تقارير من هذا النوع، والبيانات مصمَّمةٌ لتكشفه.
قاعدة المرتجعات تحتمل قراءتين، والفارق بينهما في سؤالٍ واحد: هل تدخل بنود المرتجع في gross\_sales أصلاً؟ إن دخلت ثمّ طُرحت في net عادت القيمة كأنّها بيعٌ أُلغي فقط، أو طُرحت مرّتين إن خُزّنت سالبة. القراءة التي تُرضي نصّك وصيغتك معاً هي أن يخرج المرتجع من gross ويظهر في refunds بقيمته المطلقة، ثمّ يُطرح مرّةً واحدة في net\_sales؛ وهي المعتمدة. واستعمال ABS يجعل النتيجة واحدةً سواء أرسل الفرع كمّيّة المرتجع موجبةً أو سالبة.
top\_item يُحسب من الكمّيّة الصافية: بنود المرتجع تدخل بإشارة سالبة. في العليا يوم 2026-10-01 كان الكلاسيك سيتصدّر بـ 4 لو أُهمل المرتجع، لكنّ صافيه 2 فيتقدّم برجر دجاج بـ 3. واخترتُ ROW\_NUMBER بدل RANK لأنّ الثانية تُعيد صفّين عند التعادل فتتضاعف صفوف التقرير. في النرجس يتعادل الكلاسيك والبطاطس عند 2، فيُحسم أبجديّاً لصالح «برجر كلاسيك» لأنّ الراء تسبق الطاء. المقارنة BINARY تتبع ترتيب Unicode، وهو يطابق الترتيب الهجائيّ للحروف الأساسيّة لكنّه لا يُراعي صور الهمزة والتاء المربوطة والتشكيل.
المعامل محفوظٌ في جدولٍ مؤقّتٍ بصفٍّ واحد، وعليه CHECK يقارن القيمة بـ date() لها. لذلك يُرفض تاريخٌ مثل 2026-02-30 بخطأٍ يوقف الملف عبر .bail بدل أن يُخرج تقريراً فارغاً يُقرأ خطأً على أنّه يومٌ بلا مبيعات. وكلمة «حتى» قُرئت شاملةً لليوم نفسه. أمّا الفلتر ordered\_at < date(day, '+1 day') فهو مقارنة نصّيّة تستفيد من الفهرس وتلتقط اليوم كاملاً حتى 23:59، والتجميع اليوميّ يتمّ عبر date(ordered\_at) الذي يستبعد أيّ طابعٍ زمنيٍّ مشوّه.
«أيام لها مبيعات فعليّة» نُفّذت بشرط gross\_sales > 0 دون توليد تقويم. فاليوم الذي ليس فيه إلّا طلبٌ ملغى أو مرتجعٌ وحده لا يظهر، والروضة لا صفّ لها في 2026-10-02.
المبالغ تُقرَّب إلى رقمين للعرض، وvat\_15 تُحسب من net\_sales المقرَّب بالصيغة التي حدّدتها حرفيّاً. أبقيتُ REAL كما سمحتَ، والأسعار في البيانات قيمٌ تُمثَّل في الثنائيّ بدقّة.
لم أشغّل الملف. ما يلي تتبّعٌ يدويٌّ للبيانات أعلاه عبر الاستعلام:
branch day gross\_sales discounts refunds net\_sales vat\_15 top\_item top\_item\_qty
العليا 2026-10-01 159 5 25 129 16.83 برجر دجاج 3
العليا 2026-10-02 25 0 0 25 3.26 برجر كلاسيك 1
الروضة 2026-10-01 37 0 0 37 4.83 مشروب غازي 2
النرجس 2026-10-01 68 0 0 68 8.87 برجر كلاسيك 2
لو تسرّب الطلب الملغى لصار gross الروضة 125 ولتصدّرها برجر دجاج بـ 4، ولو تجاوز الفلترُ يومَ التقرير لظهر للنرجس صفٌّ في 2026-10-03. هذان هما الصفّان اللذان يفضحان أيّ خللٍ في الاستبعاد.
طريقة الاستخدام
الأمر الذي ذكرتَه لن يشغّل الملف كما تتوقّع.
مهمّ
الأمر sqlite3 report.sql يفتح report.sql بوصفه ملفّ قاعدة بيانات لا سكربتاً، فيفشل برسالة "file is not a database". الأمر الصحيح: sqlite3 :memory: < report.sql
التشغيل على :memory: مقصود. لو وُجّه الملف إلى قاعدتكم المركزيّة لفشل عند أوّل CREATE TABLE وتوقّف بفضل .bail، وهذا أسلم من DROP يمسح بياناتٍ حقيقيّة. لتقرير يومٍ آخر غيّر '2026-10-02' في سطر INSERT INTO report\_params وحده.
حين تنقل الاستعلام إلى التطبيق خذ ما بعد -- REPORT QUERY فقط، واحذف أوامر النقطة لأنّها خاصّةٌ بواجهة sqlite3. ثمّ استبدل جسم params بـ SELECT :report\_end\_day AS report\_end\_day, date(:report\_end\_day, '+1 day') AS exclusive\_upper\_bound, 'cancelled' AS cancelled\_status, 1.15 AS vat\_inclusive\_divisor مع ربط التاريخ معاملاً، لا بلصقه في النصّ.
جClaude Sonnet
سأكتب الملف وأشغّله فعلياً للتأكد من النتائج.
الملف report.sql جاهز: يبني الجداول والبيانات ثم ينفّذ الاستعلام المعلَّم بـ -- REPORT QUERY، وقيمة اليوم المعيّن مثبّتة داخله في params (حالياً 2026-10-02).
طريقة التشغيل: الأمر sqlite3 report.sql سيفتح الملف كقاعدة بيانات وليس كسكربت. استخدم بدلاً منه:
sqlite3 :memory: < report.sql
لم يكن sqlite3 CLI متاحاً عندي، فاختبرت الملف عبر مكتبة sqlite3 في بايثون (الإصدار 3.45.1) بعد تجاهل سطرَي .headers و.mode. النتيجة:
branch day gross\_sales discounts refunds net\_sales vat\_15 top\_item top\_item\_qty
الروضة 2026-10-01 65.0 0.0 0.0 65.0 8.48 دجاج مقرمش 2
الروضة 2026-10-02 97.0 2.5 25.0 69.5 9.07 كولا 3
العليا 2026-10-01 136.0 5.0 23.0 108.0 14.09 برجر كلاسيك 4
العليا 2026-10-02 41.0 0.0 0.0 41.0 5.35 برجر دبل 1
النرجس 2026-10-01 67.0 0.0 0.0 67.0 8.74 برجر كلاسيك 2
ما تُبرزه البيانات:
الطلب الملغى (الروضة 10-01، خصمه 10 وبنوده 5 برجر دبل) لا يظهر في أي رقم.
مرتجع العليا: طُرحت قيمته (23) من المبيعات الإجمالية وظهرت في refunds، وطُرحت كميته من برجر كلاسيك (2+3−1=4).
التعادل: في النرجس تعادل كولا وبرجر كلاسيك بكمية 2، فاختير «برجر كلاسيك» أبجدياً مع أن «كولا» أُدخل أولاً.
الخصم لا يتكرر: الروضة 10-02 فيها طلبان، والخصم 2.5 يُحسب مرة واحدة لأنه يُجمع من orders مباشرة وليس عبر الربط مع البنود.
الأيام المستبعدة: النرجس 10-02 فيه طلب ملغى فقط فلا صف له، وطلب 10-03 يتجاوز اليوم المعيّن.
افتراضات اتخذتها، وأرجو مراجعتها مع المالية:
gross\_sales يحسب البنود غير المرتجعة فقط. نصّ القاعدة يقول «لكل البنود»، لكن إدخال بنود المرتجع فيه ثم طرح refunds منه يُلغي أثر المرتجع بدل أن يخصمه. إن أرادوا الجمع على كل البنود حرفياً، فاحذف شرط is\_return = 0 من amounts.
ظهور اليوم في التقرير: يظهر اليوم إذا كان فيه طلب غير ملغى له بنود، حتى لو كانت كلها مرتجعات، كي لا يضيع مرتجع يوم بلا مبيعات.
التقريب: يُقرَّب كل عمود إلى رقمين، وتُحسب vat\_15 من net\_sales المقرّب.
الترتيب الأبجدي: يعتمد على المقارنة الثنائية الافتراضية في SQLite، وهي كافية للأسماء العربية البسيطة.
صافي كمية سالب: إذا كان صافي كمية الصنف الأعلى سالباً أو صفراً فسيظهر كما هو في top\_item\_qty.
لتمرير اليوم كمعامل بدل القيمة الثابتة، استبدل '2026-10-02' في params بـ :as\_of واربطه من التطبيق.
Report
Code·SQL
-- ============================================================
\-- تقرير المبيعات اليومي لكل فرع (SQLite)
\-- التشغيل: sqlite3 :memory: < report.sql
\-- ============================================================
PRAGMA foreign\_keys = ON;
DROP TABLE IF EXISTS order\_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  branch\_id INTEGER NOT NULL REFERENCES branches(id),
  ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM'
  status TEXT NOT NULL, -- 'completed' | 'cancelled'
  discount\_amount REAL NOT NULL DEFAULT 0
);
CREATE TABLE order\_items (
  id INTEGER PRIMARY KEY,
  order\_id INTEGER NOT NULL REFERENCES orders(id),
  item\_name TEXT NOT NULL,
  unit\_price REAL NOT NULL, -- شامل الضريبة
  qty INTEGER NOT NULL,
  is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع
);
CREATE INDEX idx\_orders\_branch\_day ON orders(branch\_id, ordered\_at);
CREATE INDEX idx\_items\_order ON order\_items(order\_id);
INSERT INTO branches (id, name) VALUES (1,'العليا'), (2,'الروضة'), (3,'النرجس');
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
 (1, 1, '2026-10-01 12:10', 'completed', 5), -- العليا: خصم 5
 (2, 1, '2026-10-01 19:30', 'completed', 0), -- العليا: فيه مرتجع
 (3, 2, '2026-10-01 13:00', 'completed', 0),
 (4, 2, '2026-10-01 14:00', 'cancelled', 10), -- ملغى: يجب ألا يظهر (بنوده وخصمه)
 (5, 3, '2026-10-01 20:00', 'completed', 0), -- النرجس: تعادل في الكمية
 (6, 1, '2026-10-02 13:00', 'completed', 0),
 (7, 2, '2026-10-02 21:45', 'completed', 2.5), -- الروضة: خصم + مرتجع
 (8, 2, '2026-10-02 23:30', 'completed', 0), -- طلب ثانٍ لنفس اليوم (فحص عدم تكرار الخصم)
 (9, 3, '2026-10-03 11:00', 'completed', 0), -- بعد اليوم المعيّن: يُستبعد
 (10,3, '2026-10-02 18:00', 'cancelled', 0); -- النرجس 10-02 ملغى فقط: لا صف لهذا اليوم
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
 -- طلب 1 (العليا)
 (1, 1, 'برجر كلاسيك', 23, 2, 0),
 (2, 1, 'بطاطس', 9, 1, 0),
 -- طلب 2 (العليا) مع مرتجع 1 برجر كلاسيك
 (3, 2, 'برجر كلاسيك', 23, 3, 0),
 (4, 2, 'كولا', 6, 2, 0),
 (5, 2, 'برجر كلاسيك', 23, 1, 1),
 -- طلب 3 (الروضة)
 (6, 3, 'دجاج مقرمش', 25, 2, 0),
 (7, 3, 'بطاطس', 9, 1, 0),
 (8, 3, 'كولا', 6, 1, 0),
 -- طلب 4 (ملغى)
 (9, 4, 'برجر دبل', 35, 5, 0),
 -- طلب 5 (النرجس): تعادل كولا = برجر كلاسيك = 2 (كولا أولاً في الإدخال)
 (10, 5, 'كولا', 6, 2, 0),
 (11, 5, 'برجر كلاسيك', 23, 2, 0),
 (12, 5, 'بطاطس', 9, 1, 0),
 -- طلب 6 (العليا 10-02)
 (13, 6, 'برجر دبل', 35, 1, 0),
 (14, 6, 'كولا', 6, 1, 0),
 -- طلب 7 (الروضة 10-02) مع مرتجع دجاج
 (15, 7, 'برجر دبل', 35, 2, 0),
 (16, 7, 'بطاطس', 9, 1, 0),
 (17, 7, 'دجاج مقرمش', 25, 1, 1),
 -- طلب 8 (الروضة 10-02)
 (18, 8, 'كولا', 6, 3, 0),
 -- طلب 9 (بعد اليوم المعيّن)
 (19, 9, 'برجر دبل', 35, 1, 0),
 -- طلب 10 (ملغى)
 (20,10, 'كولا', 6, 4, 0);
.headers on
.mode column
\-- REPORT QUERY
WITH params AS (
  SELECT '2026-10-02' AS as\_of -- اليوم المعيّن (شاملاً)
),
valid\_orders AS ( -- استبعاد الملغى كلياً
  SELECT o.id, o.branch\_id, substr(o.ordered\_at, 1, 10) AS day, o.discount\_amount
  FROM orders o, params p
  WHERE o.status <> 'cancelled'
  AND substr(o.ordered\_at, 1, 10) <= p.as\_of
),
\-- الخصومات من رأس الطلب: تُجمَّع من orders مباشرة (بدون JOIN على البنود) لتجنّب تكرارها
disc AS (
  SELECT branch\_id, day, SUM(discount\_amount) AS discounts
  FROM valid\_orders
  GROUP BY branch\_id, day
),
\-- المبيعات والمرتجعات من البنود
amounts AS (
  SELECT vo.branch\_id, vo.day,
  SUM(CASE WHEN oi.is\_return = 0 THEN oi.unit\_price \* oi.qty ELSE 0 END) AS gross\_sales,
  SUM(CASE WHEN oi.is\_return = 1 THEN ABS(oi.unit\_price \* oi.qty) ELSE 0 END) AS refunds
  FROM valid\_orders vo
  JOIN order\_items oi ON oi.order\_id = vo.id
  GROUP BY vo.branch\_id, vo.day
),
\-- صافي كمية كل صنف (المرتجع يُطرح)
item\_qty AS (
  SELECT vo.branch\_id, vo.day, oi.item\_name,
  SUM(CASE WHEN oi.is\_return = 1 THEN -oi.qty ELSE oi.qty END) AS net\_qty
  FROM valid\_orders vo
  JOIN order\_items oi ON oi.order\_id = vo.id
  GROUP BY vo.branch\_id, vo.day, oi.item\_name
),
ranked AS (
  SELECT branch\_id, day, item\_name, net\_qty,
  ROW\_NUMBER() OVER (PARTITION BY branch\_id, day
  ORDER BY net\_qty DESC, item\_name ASC) AS rn
  FROM item\_qty
),
calc AS (
  SELECT a.branch\_id, a.day,
  ROUND(a.gross\_sales, 2) AS gross\_sales,
  ROUND(d.discounts, 2) AS discounts,
  ROUND(a.refunds, 2) AS refunds,
  ROUND(a.gross\_sales - d.discounts - a.refunds, 2) AS net\_sales
  FROM amounts a
  JOIN disc d ON d.branch\_id = a.branch\_id AND d.day = a.day
)
SELECT b.name AS branch,
  c.day AS day,
  c.gross\_sales AS gross\_sales,
  c.discounts AS discounts,
  c.refunds AS refunds,
  c.net\_sales AS net\_sales,
  ROUND(c.net\_sales - c.net\_sales / 1.15, 2) AS vat\_15,
  r.item\_name AS top\_item,
  r.net\_qty AS top\_item\_qty
FROM calc c
JOIN branches b ON b.id = c.branch\_id
JOIN ranked r ON r.branch\_id = c.branch\_id AND r.day = c.day AND r.rn = 1
ORDER BY b.name, c.day;
أChatGPT
سأبني التقرير باستخدام CTEs وROW\_NUMBER() في SQLite، مع تجميع المبيعات والمرتجعات والخصومات كلٌّ على حدة قبل ربطها؛ لتجنّب مضاعفة الخصومات عند وجود عدة بنود في الطلب.
هناك نقطة مهمة في الحساب: بما أن unit\_price شامل ضريبة القيمة المضافة، فسنستخدمه كما هو في المبيعات، ونطرح قيمة المرتجعات مرة واحدة، ثم نحسب صافي المبيعات والضريبة وفق الصيغة المطلوبة. وسأستبعد الطلبات الملغاة من جميع الحسابات، بما فيها الخصومات وترتيب الأصناف.
هذا محتوى ملف report.sql كاملاً، جاهز للنسخ والتشغيل.
كتابة
\-- =========================================================
\-- report.sql
\-- Daily branch sales report (SQLite)
\-- Fixed report cutoff date: 2026-10-01
\-- =========================================================
PRAGMA foreign\_keys = ON;
DROP TABLE IF EXISTS order\_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL
);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  branch\_id INTEGER NOT NULL REFERENCES branches(id),
  ordered\_at TEXT NOT NULL,
  status TEXT NOT NULL,
  discount\_amount REAL NOT NULL DEFAULT 0
);
CREATE TABLE order\_items (
  id INTEGER PRIMARY KEY,
  order\_id INTEGER NOT NULL REFERENCES orders(id),
  item\_name TEXT NOT NULL,
  unit\_price REAL NOT NULL,
  qty INTEGER NOT NULL,
  is\_return INTEGER NOT NULL DEFAULT 0
);
\-- ---------------------------------------------------------
\-- Sample data
\-- ---------------------------------------------------------
INSERT INTO branches (id, name) VALUES
  (1, 'العليا'),
  (2, 'الروضة'),
  (3, 'النرجس');
INSERT INTO orders
  (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES
  -- العليا: two completed orders, total discount = 5
  (101, 1, '2026-10-01 12:00', 'completed', 5),
  (102, 1, '2026-10-01 18:30', 'completed', 0),
  -- الروضة: completed order and cancelled order
  (201, 2, '2026-10-01 13:15', 'completed', 2),
  (202, 2, '2026-10-01 19:00', 'cancelled', 100),
  -- النرجس: two items tied in net quantity
  (301, 3, '2026-10-01 14:00', 'completed', 0),
  -- A later order, excluded by the fixed cutoff date
  (401, 1, '2026-10-02 12:00', 'completed', 0);
INSERT INTO order\_items
  (id, order\_id, item\_name, unit\_price, qty, is\_return)
VALUES
  -- العليا: order 101
  (1, 101, 'Classic Burger', 30, 2, 0),
  (2, 101, 'French Fries', 10, 1, 0),
  (3, 101, 'Classic Burger', 30, 1, 1),
  -- العليا: order 102
  (4, 102, 'Classic Burger', 30, 1, 0),
  (5, 102, 'Cola', 5, 2, 0),
  -- الروضة: completed order
  (6, 201, 'Classic Burger', 25, 2, 0),
  (7, 201, 'Cola', 5, 1, 0),
  -- الروضة: cancelled order; all its data must be ignored
  (8, 202, 'Classic Burger', 25, 10, 0),
  -- النرجس: tie in quantity; alphabetically first wins
  (9, 301, 'Chicken Burger', 20, 2, 0),
  (10, 301, 'Classic Burger', 25, 2, 0),
  -- Later sale: excluded
  (11, 401, 'Classic Burger', 30, 3, 0);
\-- =========================================================
\-- REPORT QUERY
\-- =========================================================
WITH
params AS (
  SELECT '2026-10-01' AS cutoff\_day
),
\-- Include completed orders only, up to the cutoff date.
valid\_orders AS (
  SELECT
  o.id,
  o.branch\_id,
  date(o.ordered\_at) AS sale\_day,
  o.discount\_amount
  FROM orders AS o
  CROSS JOIN params AS p
  WHERE o.status <> 'cancelled'
  AND date(o.ordered\_at) <= p.cutoff\_day
),
\-- Aggregate item amounts and quantities by branch and day.
daily\_items AS (
  SELECT
  vo.branch\_id,
  vo.sale\_day,
  -- Gross sales include sold items, excluding returns.
  SUM(
  CASE
  WHEN oi.is\_return = 0
  THEN oi.unit\_price \* oi.qty
  ELSE 0
  END
  ) AS gross\_sales,
  -- Refunds are reported as positive amounts.
  SUM(
  CASE
  WHEN oi.is\_return = 1
  THEN ABS(oi.unit\_price \* oi.qty)
  ELSE 0
  END
  ) AS refunds
  FROM valid\_orders AS vo
  JOIN order\_items AS oi
  ON oi.order\_id = vo.id
  GROUP BY
  vo.branch\_id,
  vo.sale\_day
),
\-- Discounts are aggregated at order level, not item level.
daily\_discounts AS (
  SELECT
  branch\_id,
  sale\_day,
  SUM(discount\_amount) AS discounts
  FROM valid\_orders
  GROUP BY
  branch\_id,
  sale\_day
),
\-- Net quantity per item; returned quantities are subtracted.
item\_quantities AS (
  SELECT
  vo.branch\_id,
  vo.sale\_day,
  oi.item\_name,
  SUM(
  CASE
  WHEN oi.is\_return = 1 THEN -oi.qty
  ELSE oi.qty
  END
  ) AS net\_qty
  FROM valid\_orders AS vo
  JOIN order\_items AS oi
  ON oi.order\_id = vo.id
  GROUP BY
  vo.branch\_id,
  vo.sale\_day,
  oi.item\_name
),
\-- Highest net quantity first; alphabetical name breaks ties.
ranked\_items AS (
  SELECT
  branch\_id,
  sale\_day,
  item\_name,
  net\_qty,
  ROW\_NUMBER() OVER (
  PARTITION BY branch\_id, sale\_day
  ORDER BY net\_qty DESC, item\_name COLLATE BINARY ASC
  ) AS rn
  FROM item\_quantities
  WHERE net\_qty > 0
),
\-- Calculate daily totals and the requested VAT amount.
daily\_totals AS (
  SELECT
  di.branch\_id,
  di.sale\_day,
  di.gross\_sales,
  dd.discounts,
  di.refunds,
  di.gross\_sales - dd.discounts - di.refunds AS net\_sales
  FROM daily\_items AS di
  JOIN daily\_discounts AS dd
  ON dd.branch\_id = di.branch\_id
  AND dd.sale\_day = di.sale\_day
  -- Only days with actual positive sales are reported.
  WHERE di.gross\_sales > 0
)
SELECT
  b.name AS branch,
  dt.sale\_day AS day,
  ROUND(dt.gross\_sales, 2) AS gross\_sales,
  ROUND(dt.discounts, 2) AS discounts,
  ROUND(dt.refunds, 2) AS refunds,
  ROUND(dt.net\_sales, 2) AS net\_sales,
  ROUND(dt.net\_sales - dt.net\_sales / 1.15, 2) AS vat\_15,
  ri.item\_name AS top\_item,
  ri.net\_qty AS top\_item\_qty
FROM daily\_totals AS dt
JOIN branches AS b
  ON b.id = dt.branch\_id
LEFT JOIN ranked\_items AS ri
  ON ri.branch\_id = dt.branch\_id
 AND ri.sale\_day = dt.sale\_day
 AND ri.rn = 1
ORDER BY
  dt.sale\_day,
  b.id;
التشغيل
احفظ المحتوى باسم report.sql، ثم نفّذه باستخدام:
sqlite3 < report.sql
أو إذا أردت حفظ النتائج في قاعدة بيانات مستقلة:
sqlite3 restaurant.db < report.sql
ملاحظة: الأمر sqlite3 report.sql وحده يفتح ملفاً باسم report.sql باعتباره قاعدة بيانات، ولا ينفّذ محتواه كسكربت SQL. لذلك استخدم إعادة توجيه الإدخال كما في الأمرين أعلاه.
النتيجة المتوقعة
branch
	
day
	
gross\_sales
	
discounts
	
refunds
	
net\_sales
	
vat\_15
	
top\_item
	
top\_item\_qty
العليا
	
2026-10-01
	
105.00
	
5.00
	
30.00
	
70.00
	
9.13
	
Classic Burger
	
2
الروضة
	
2026-10-01
	
55.00
	
2.00
	
0.00
	
53.00
	
6.91
	
Classic Burger
	
2
النرجس
	
2026-10-01
	
90.00
	
0.00
	
0.00
	
90.00
	
11.74
	
Chicken Burger
	
2
نقاط التحقق
المرتجعات: تظهر بقيمة موجبة في refunds، وتُطرح مرة واحدة من المبيعات في حساب net\_sales.
الخصومات: تُجمع من رأس الطلب مرة واحدة لكل طلب، ولا تتضاعف بعدد البنود.
الطلبات الملغاة: لا تؤثر في أي عمود أو في اختيار الصنف الأعلى.
الصنف الأعلى: تُطرح الكميات المرتجعة، ثم يُحسم التعادل بترتيب أسماء الأصناف.
تاريخ التقرير: القيمة المثبتة هي 2026-10-01، وتُستبعد المبيعات اللاحقة.
الأيام دون مبيعات: لا تظهر إذا لم توجد مبيعات فعلية موجبة.
تنبيه بشأن تعريف gross\_sales: استبعدت بنود المرتجعات من gross\_sales حتى لا تُحتسب قيمتها مرتين عند طرح refunds. وهذا يجعل الأعمدة متوافقة مع معادلة صافي المبيعات المطلوبة.
دDeepSeek
سأبني لك ملف report.sql كاملاً وقابلاً للتشغيل. الفكرة الأساسية:
نستبعد الطلبات الملغاة.
نحسب gross\_sales من البنود غير المرتجعة.
نحسب refunds من البنود المرتجعة (قيمة مطلقة).
نطرح المرتجعات من الكميات عند حساب top\_item.
نستخدم CTEs وwindow functions فقط.
sql
\-- report.sql
PRAGMA foreign\_keys = ON;
DROP TABLE IF EXISTS order\_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  branch\_id INTEGER NOT NULL REFERENCES branches(id),
  ordered\_at TEXT NOT NULL,
  status TEXT NOT NULL,
  discount\_amount REAL NOT NULL DEFAULT 0
);
CREATE TABLE order\_items (
  id INTEGER PRIMARY KEY,
  order\_id INTEGER NOT NULL REFERENCES orders(id),
  item\_name TEXT NOT NULL,
  unit\_price REAL NOT NULL,
  qty INTEGER NOT NULL,
  is\_return INTEGER NOT NULL DEFAULT 0
);
INSERT INTO branches (id, name) VALUES
  (1,'العليا'), (2,'الروضة'), (3,'النرجس');
\-- ===== العليا 2026-10-01 =====
\-- طلب 1 مكتمل: برجر كلاسيك x2، بطاطس x1
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
  (1, 1, '2026-10-01 12:00', 'completed', 5);
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (1, 1, 'برجر كلاسيك', 20.0, 2, 0),
  (2, 1, 'بطاطس', 10.0, 1, 0);
\-- طلب 2 مكتمل: برجر كلاسيك x1، مرتجع برجر كلاسيك x1
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
  (2, 1, '2026-10-01 14:00', 'completed', 0);
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (3, 2, 'برجر كلاسيك', 20.0, 1, 0),
  (4, 2, 'برجر كلاسيك', 20.0, 1, 1); -- مرتجع
\-- ===== الروضة 2026-10-01 =====
\-- طلب 3 مكتمل: برجر دبل x1، مشروب x2
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
  (3, 2, '2026-10-01 11:00', 'completed', 3);
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (5, 3, 'برجر دبل', 25.0, 1, 0),
  (6, 3, 'مشروب', 5.0, 2, 0);
\-- طلب 4 ملغى (يجب استبعاده كلياً)
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
  (4, 2, '2026-10-01 13:00', 'cancelled', 10);
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (7, 4, 'برجر دبل', 25.0, 5, 0);
\-- ===== النرجس 2026-10-01 =====
\-- طلب 5 مكتمل: تعادل بالكمية بين 'برجر كلاسيك' و 'برجر دبل' (كلاهما x2)
\-- الأبجدي أولاً: 'برجر دبل' < 'برجر كلاسيك' حسب ترتيب النص
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES
  (5, 3, '2026-10-01 10:00', 'completed', 2);
INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (8, 5, 'برجر كلاسيك', 20.0, 2, 0),
  (9, 5, 'برجر دبل', 25.0, 2, 0),
  (10,5, 'بطاطس', 10.0, 1, 0);
\-- ===== REPORT QUERY =====
\-- يوم التقرير (غيّره كما تريد)
WITH params AS (
  SELECT '2026-10-01' AS day
),
\-- الطلبات الصالحة (غير ملغاة) حتى اليوم المطلوب
valid\_orders AS (
  SELECT o.id, o.branch\_id, o.ordered\_at, o.discount\_amount
  FROM orders o, params p
  WHERE o.status <> 'cancelled'
  AND date(o.ordered\_at) <= date(p.day)
),
\-- المبيعات الإجمالية (بنود غير مرتجعة)
gross AS (
  SELECT vo.branch\_id, date(vo.ordered\_at) AS day,
  SUM(oi.unit\_price \* oi.qty) AS gross\_sales
  FROM valid\_orders vo
  JOIN order\_items oi ON oi.order\_id = vo.id AND oi.is\_return = 0
  GROUP BY vo.branch\_id, date(vo.ordered\_at)
),
\-- المرتجعات (قيمة مطلقة)
refunds AS (
  SELECT vo.branch\_id, date(vo.ordered\_at) AS day,
  SUM(oi.unit\_price \* oi.qty) AS refunds
  FROM valid\_orders vo
  JOIN order\_items oi ON oi.order\_id = vo.id AND oi.is\_return = 1
  GROUP BY vo.branch\_id, date(vo.ordered\_at)
),
\-- الخصومات على مستوى اليوم/الفرع
discounts AS (
  SELECT vo.branch\_id, date(vo.ordered\_at) AS day,
  SUM(vo.discount\_amount) AS discounts
  FROM valid\_orders vo
  GROUP BY vo.branch\_id, date(vo.ordered\_at)
),
\-- صافي كميات كل صنف (مع طرح المرتجعات) لحساب top\_item
item\_qty AS (
  SELECT vo.branch\_id, date(vo.ordered\_at) AS day,
  oi.item\_name,
  SUM(CASE WHEN oi.is\_return = 0 THEN oi.qty ELSE -oi.qty END) AS net\_qty
  FROM valid\_orders vo
  JOIN order\_items oi ON oi.order\_id = vo.id
  GROUP BY vo.branch\_id, date(vo.ordered\_at), oi.item\_name
),
\-- ترتيب الأصناف: الأعلى كمية ثم الأبجدي
ranked\_items AS (
  SELECT iq.\*,
  ROW\_NUMBER() OVER (
  PARTITION BY branch\_id, day
  ORDER BY net\_qty DESC, item\_name ASC
  ) AS rn
  FROM item\_qty iq
  WHERE net\_qty > 0
),
top\_items AS (
  SELECT branch\_id, day, item\_name AS top\_item, net\_qty AS top\_item\_qty
  FROM ranked\_items
  WHERE rn = 1
),
\-- تجميع كل القيم
combined AS (
  SELECT
  b.name AS branch,
  g.day AS day,
  g.gross\_sales,
  COALESCE(d.discounts, 0) AS discounts,
  COALESCE(r.refunds, 0) AS refunds,
  g.gross\_sales - COALESCE(d.discounts, 0) - COALESCE(r.refunds, 0) AS net\_sales
  FROM gross g
  JOIN branches b ON b.id = g.branch\_id
  LEFT JOIN discounts d ON d.branch\_id = g.branch\_id AND d.day = g.day
  LEFT JOIN refunds r ON r.branch\_id = g.branch\_id AND r.day = g.day
)
SELECT
  c.branch,
  c.day,
  ROUND(c.gross\_sales, 2) AS gross\_sales,
  ROUND(c.discounts, 2) AS discounts,
  ROUND(c.refunds, 2) AS refunds,
  ROUND(c.net\_sales, 2) AS net\_sales,
  ROUND(c.net\_sales - c.net\_sales / 1.15, 2) AS vat\_15,
  t.top\_item,
  t.top\_item\_qty
FROM combined c
LEFT JOIN top\_items t ON t.branch\_id = (
  SELECT id FROM branches WHERE name = c.branch
) AND t.day = c.day
ORDER BY c.branch, c.day;
ملاحظات مهمة على التصميم:
gross\_sales يُحسب فقط من البنود غير المرتجعة (is\_return = 0)، والمرتجع لا يُخصم من gross\_sales بل يظهر منفصلاً في refunds كما طلبت القواعد.
refunds هي القيمة المطلقة لبنود المرتجع (مجموع unit\_price \* qty للبنود ذات is\_return = 1).
net\_sales = gross\_sales - discounts - refunds تماماً كما في القواعد.
vat\_15 = net\_sales - net\_sales/1.15 مقرّباً لرقمين عشريين (لأن الأسعار شاملة الضريبة).
top\_item: نحسب صافي الكمية لكل صنف (مبيعات − مرتجعات)، نستبعد الأصناف التي صافيها ≤ 0، ثم نرتب بـ ROW\_NUMBER() حسب net\_qty DESC, item\_name ASC لضمان اختيار الاسم الأبجدي عند التعادل.
استبعاد الملغاة يتم في valid\_orders قبل كل التجميعات.
حتى يوم معيّن: نستخدم date(ordered\_at) <= date(params.day)، والدالة date() على النص 'YYYY-MM-DD HH:MM' تُرجع التاريخ فقط.
كل فرع فقط أيام له فيها مبيعات فعلية: لأن gross مبنية على JOIN مع البنود غير المرتجعة، فالفرع/اليوم بلا مبيعات فعلية لن يظهر. لاحظ أن يوماً فيه مرتجعات فقط (بلا مبيعات) لن يظهر أيضاً — وهذا متسق مع "أيام له فيها مبيعات فعلية".
التعادل في النرجس: 'برجر دبل' و 'برجر كلاسيك' كلاهما بكمية 2؛ الترتيب الأبجدي حسب item\_name ASC سيختار 'برجر دبل' (لأن مقارنة النصوص تتم حسب ترميز Unicode، و'د' < 'ك' في هذه الحالة). إن أردت ترتيباً عربياً مخصصاً يمكن تعديله، لكن القاعدة المطلوبة هي "الاسم الأبجدي أولاً" وهذا ما يطبقه ORDER BY item\_name ASC.
للتشغيل:
bash
sqlite3 :memory: < report.sql
\# أو
sqlite3 test.db < report.sql
الناتج المتوقع (مبسّط):
branch day gross\_sales discounts refunds net\_sales vat\_15 top\_item top\_item\_qty
الروضة 2026-10-01 35 3 0 32 4.17 مشروب 2
العليا 2026-10-01 50 5 20 25 3.26 برجر كلاسيك 2
النرجس 2026-10-01 100 2 0 98 12.78 برجر دبل 2
ملاحظة: ترتيب الصفوف أبجدي حسب اسم الفرع بسبب ORDER BY c.branch. لو أردت ترتيباً حسب branch\_id يمكن تعديله بسهولة.
بGemini
إليك ملف report.sql كاملاً يحتوي على إنشاء الجداول، بيانات الاختبار الشاملة للحالات، والاستعلام النهائي باستخدام Common Table Expressions (CTEs) و Window Functions الخاصة بـ SQLite:SQL-- ==========================================
\-- 1. إنشاء الجداول
\-- ==========================================
DROP TABLE IF EXISTS order\_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL
);
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  branch\_id INTEGER NOT NULL REFERENCES branches(id),
  ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM'
  status TEXT NOT NULL, -- 'completed' | 'cancelled'
  discount\_amount REAL NOT NULL DEFAULT 0
);
CREATE TABLE order\_items (
  id INTEGER PRIMARY KEY,
  order\_id INTEGER NOT NULL REFERENCES orders(id),
  item\_name TEXT NOT NULL,
  unit\_price REAL NOT NULL, -- شامل الضريبة
  qty INTEGER NOT NULL,
  is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع
);
\-- ==========================================
\-- 2. إدراج البيانات التوضيحية
\-- ==========================================
INSERT INTO branches (id, name) VALUES
  (1, 'العليا'),
  (2, 'الروضة'),
  (3, 'النرجس');
\-- ----- فرع العليا (2026-10-01) -----
\-- طلب 1: مكتمل، خصم 5 ريال، فيه مرتجع برجر كلاسيك (1)
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES (1, 1, '2026-10-01 12:30', 'completed', 5.00);
INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (1, 'برجر كلاسيك', 30.00, 3, 0), -- مباع: 3
  (1, 'برجر كلاسيك', 30.00, 1, 1), -- مرتجع: 1 (الصافي = 2)
  (1, 'بطاطس', 10.00, 2, 0); -- مباع: 2
\-- طلب 2: مكتمل بدون خصم
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES (2, 1, '2026-10-01 14:15', 'completed', 0.00);
INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (2, 'برجر دبل', 40.00, 1, 0);
\-- ----- فرع الروضة (2026-10-01) -----
\-- طلب 3: مكتمل
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES (3, 2, '2026-10-01 13:00', 'completed', 0.00);
INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (3, 'برجر كلاسيك', 30.00, 2, 0);
\-- طلب 4: ملغى (يجب استبعاده كلياً وعدم احتساب مبيعاته)
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES (4, 2, '2026-10-01 15:00', 'cancelled', 10.00);
INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (4, 'برجر دبل', 40.00, 5, 0);
\-- ----- فرع النرجس (2026-10-01) -----
\-- طلب 5: مكتمل، تعادل في صافي الكمية (2 لكل من 'برجر دبل' و 'بطاطس')
\-- يجب أن يختار الاستعلام 'بطاطس' أبجدياً
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES (5, 3, '2026-10-01 18:00', 'completed', 0.00);
INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (5, 'برجر دبل', 40.00, 2, 0),
  (5, 'بطاطس', 10.00, 2, 0);
\-- ----- يوم لاحق خارج تاريخ المعامل (2026-10-02) -----
INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount)
VALUES (6, 1, '2026-10-02 20:00', 'completed', 0.00);
INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES
  (6, 'برجر كلاسيك', 30.00, 1, 0);
\-- ==========================================
\-- REPORT QUERY
\-- ==========================================
WITH target\_date AS (
  -- تغيير تاريخ المعامل المراد تصفية التقرير عنده
  SELECT '2026-10-01' AS cutoff\_day
),
valid\_orders AS (
  SELECT
  o.id AS order\_id,
  o.branch\_id,
  DATE(o.ordered\_at) AS day,
  o.discount\_amount
  FROM orders o
  JOIN target\_date td ON DATE(o.ordered\_at) <= td.cutoff\_day
  WHERE o.status != 'cancelled'
),
daily\_order\_totals AS (
  SELECT
  vo.branch\_id,
  vo.day,
  SUM(vo.discount\_amount) AS total\_discounts
  FROM (
  SELECT DISTINCT order\_id, branch\_id, day, discount\_amount
  FROM valid\_orders
  ) vo
  GROUP BY vo.branch\_id, vo.day
),
daily\_item\_sales AS (
  SELECT
  vo.branch\_id,
  vo.day,
  oi.item\_name,
  SUM(CASE WHEN oi.is\_return = 0 THEN oi.unit\_price \* oi.qty ELSE 0 END) AS gross\_sales,
  SUM(CASE WHEN oi.is\_return = 1 THEN oi.unit\_price \* oi.qty ELSE 0 END) AS refunds,
  SUM(CASE WHEN oi.is\_return = 0 THEN oi.qty ELSE -oi.qty END) AS net\_qty
  FROM valid\_orders vo
  JOIN order\_items oi ON vo.order\_id = oi.order\_id
  GROUP BY vo.branch\_id, vo.day, oi.item\_name
),
ranked\_items AS (
  SELECT
  dis.branch\_id,
  dis.day,
  dis.item\_name,
  dis.net\_qty,
  ROW\_NUMBER() OVER (
  PARTITION BY dis.branch\_id, dis.day
  ORDER BY dis.net\_qty DESC, dis.item\_name ASC
  ) AS rn
  FROM daily\_item\_sales dis
),
branch\_daily\_summary AS (
  SELECT
  b.name AS branch,
  dis.day,
  SUM(dis.gross\_sales) AS gross\_sales,
  dot.total\_discounts AS discounts,
  SUM(dis.refunds) AS refunds
  FROM daily\_item\_sales dis
  JOIN branches b ON dis.branch\_id = b.id
  JOIN daily\_order\_totals dot ON dis.branch\_id = dot.branch\_id AND dis.day = dot.day
  GROUP BY b.name, dis.day, dot.total\_discounts
)
SELECT
  s.branch,
  s.day,
  ROUND(s.gross\_sales, 2) AS gross\_sales,
  ROUND(s.discounts, 2) AS discounts,
  ROUND(s.refunds, 2) AS refunds,
  ROUND(s.gross\_sales - s.discounts - s.refunds, 2) AS net\_sales,
  ROUND(
  (s.gross\_sales - s.discounts - s.refunds) -
  ((s.gross\_sales - s.discounts - s.refunds) / 1.15),
  2
  ) AS vat\_15,
  r.item\_name AS top\_item,
  r.net\_qty AS top\_item\_qty
FROM branch\_daily\_summary s
JOIN ranked\_items r ON s.branch = (SELECT name FROM branches WHERE id = r.branch\_id)
  AND s.day = r.day
  AND r.rn = 1
ORDER BY s.day ASC, s.branch ASC;
طريقة التشغيل عبر السطر البرمجي:Bashsqlite3 report.sql
نتيجة الاستعلام بالتمرير على البيانات المرفقة:branchdaygross\_salesdiscountsrefundsnet\_salesvat\_15top\_itemtop\_item\_qtyالعليا2026-10-01160.05.030.0125.016.3 AS (125 - 125/1.15 = 16.304...)برجر كلاسيك2النرجس2026-10-01100.00.00.0100.013.04بطاطس2الروضة2026-10-0160.00.00.060.07.83برجر كلاسيك2