Salla Products Template Sheet
1–10: المعلومات الأساسية للمنتج
| # | اسم العمود | الحقل | نوع البيانات | الاستخدام |
|---|---|---|---|---|
| 1 | النوع | type | نص | يحدد هل الصف منتج أم خيار |
| 2 | أسم المنتج | name | نص | اسم المنتج |
| 3 | تصنيف المنتج | category | نص | التصنيف الهرمي للمنتج |
| 4 | صورة المنتج | images | روابط | صور المنتج، ويمكن أن تكون عدة روابط مفصولة بفواصل |
| 5 | وصف صورة المنتج | image_description | نص | وصف الصورة |
| 6 | نوع المنتج | product_type | نص | نوع المنتج، مثل منتج جاهز |
| 7 | سعر المنتج | price | رقم | السعر الأساسي للبيع |
| 8 | الوصف | description | نص طويل | الوصف التفصيلي للمنتج |
| 9 | هل يتطلب شحن؟ | requires_shipping | نعم/لا | تحديد ما إذا كان المنتج يحتاج إلى شحن |
| 10 | رمز المنتج SKU | sku | نص | المعرف الداخلي للمنتج/الخيار |
مثال من الملف
المنتج الرئيسي:
النوع: منتج
أسم المنتج: ملابس سباحة
تصنيف المنتج: ملابس > رجالي
نوع المنتج: منتج جاهز
سعر المنتج: 45
هل يتطلب شحن؟ نعم
SKU: sku ABC
💰 11–20: التسعير والمخزون والشحن والعلامة التجارية
| # | اسم العمود | الحقل | نوع البيانات | الاستخدام |
|---|---|---|---|---|
| 11 | سعر التكلفة | cost_price | رقم | تكلفة شراء/إنتاج المنتج |
| 12 | السعر المخفض | sale_price | رقم | السعر بعد الخصم |
| 13 | تاريخ بداية التخفيض | sale_start | تاريخ/وقت | بداية فترة السعر المخفض |
| 14 | تاريخ نهاية التخفيض | sale_end | تاريخ/وقت | نهاية فترة السعر المخفض |
| 15 | اقصي كمية لكل عميل | max_quantity_per_customer | رقم | الحد الأقصى للكمية التي يستطيع العميل شراءها |
| 16 | إخفاء خيار تحديد الكمية | hide_quantity | نعم/لا | إخفاء أداة اختيار الكمية |
| 17 | اضافة صورة عند الطلب | add_image_on_request | نعم/لا | السماح بإضافة صورة مرتبطة بالطلب |
| 18 | الوزن | weight | رقم | وزن المنتج |
| 19 | وحدة الوزن | weight_unit | نص | وحدة الوزن، مثل kg |
| 20 | الماركة | brand | نص | العلامة التجارية |
القيم الموجودة في المنتج الرئيسي
سعر التكلفة: 40.9
السعر المخفض: 45
بداية التخفيض: 2026-09-13
نهاية التخفيض: 2026-09-13
أقصى كمية لكل عميل: 0
إخفاء خيار تحديد الكمية: نعم
إضافة صورة عند الطلب: نعم
الوزن: 10
وحدة الوزن: kg
الماركة: زارا
ملاحظة مهمة: حقل
السعر المخفضيظهر في Excel كقيمة تاريخية داخلية مثل1900-02-04، وهذا يحتاج إلى تنظيف/تصحيح قبل تحويل الملف إلى نظام إنتاجي.
📣 21–28: التسويق والبيانات التعريفية والضريبة
| # | اسم العمود | الحقل | نوع البيانات | الاستخدام |
|---|---|---|---|---|
| 21 | العنوان الترويجي | promotion_title | نص | عنوان أو عبارة ترويجية للمنتج |
| 22 | تثبيت المنتج | pin_product | نعم/لا | إبقاء المنتج مثبتًا/بارزًا |
| 23 | الباركود | barcode | نص | Barcode الخاص بالمنتج |
| 24 | السعرات الحرارية | calories | رقم | السعرات، خصوصًا للمنتجات الغذائية |
| 25 | MPN | mpn | نص | Manufacturer Part Number |
| 26 | GTIN | gtin | نص | Global Trade Item Number |
| 27 | خاضع للضريبة؟ | taxable | نعم/لا | تحديد خضوع المنتج للضريبة |
| 28 | سبب عدم الخضوع للضريبة | tax_exemption_reason | نص | السبب في حالة عدم خضوع المنتج للضريبة |
البيانات الموجودة
العنوان الترويجي: فارغ
تثبيت المنتج: نعم
الباركود:
نص توضيحي فقط : المنتج ليس له قيمة باركود
السعرات الحرارية:
نص توضيحي فقط : يتم وضع السعرات الحرارية للمنتج إذا كان من نوع طعام
MPN: MPN
GTIN: GTIN
خاضع للضريبة؟ نعم
سبب عدم الخضوع للضريبة:
نص توضيحي فقط : يتم وضع سبب عدم الخضوع للضريبة في حال المنتج غير خاضع
وهنا واضح أن بعض القيم عبارة عن تعليمات داخل القالب وليست بيانات حقيقية.
🎨 29–32: الخيار الأول [1]
هذه المجموعة تمثل الخيار الأول للمنتج.
| # | اسم العمود | الحقل | المعنى |
|---|---|---|---|
| 29 | [1] الاسم | option_1_name | اسم الخيار |
| 30 | [1] النوع | option_1_type | نوع الخيار |
| 31 | [1] القيمة | option_1_value | قيمة الخيار |
| 32 | [1] الصورة / اللون | option_1_image_color | صورة أو لون مرتبط بالخيار |
في المنتج:
[1] الاسم: اللون الأساسي
[1] النوع: لون
[1] القيمة: ممنوع الكتابة في هذا الحقل في صف المنتج
[1] الصورة / اللون: ممنوع الكتابة في هذا الحقل في صف المنتج
أما في صفوف الخيارات الفعلية:
[1] الاسم: فارغ
[1] النوع: فارغ
[1] القيمة: أسود
[1] الصورة / اللون: #000000
أو:
[1] القيمة: أحمر
[1] الصورة / اللون: #FF0000
أو:
[1] القيمة: أزرق
[1] الصورة / اللون: #0000FF
إذًا الخيار الأول هنا هو:
اللون الأساسي → لون → أسود/أحمر/أزرق
📏 33–36: الخيار الثاني [2]
| # | اسم العمود | الحقل | المعنى |
|---|---|---|---|
| 33 | [2] الاسم | option_2_name | اسم الخيار الثاني |
| 34 | [2] النوع | option_2_type | نوع الخيار الثاني |
| 35 | [2] القيمة | option_2_value | قيمة الخيار |
| 36 | [2] الصورة / اللون | option_2_image_color | صورة أو لون الخيار |
في المنتج الرئيسي:
[2] الاسم: المقاس
[2] النوع: نص
[2] القيمة: ممنوع الكتابة في هذا الحقل في صف المنتج
[2] الصورة / اللون: ممنوع الكتابة في هذا الحقل في صف المنتج
وفي المتغيرات:
[2] القيمة: XL
أو:
[2] القيمة: L
إذن الخيار الثاني هو:
المقاس → نص → XL / L
🖼️ 37–40: الخيار الثالث [3]
| # | اسم العمود | الحقل | المعنى |
|---|---|---|---|
| 37 | [3] الاسم | option_3_name | اسم الخيار الثالث |
| 38 | [3] النوع | option_3_type | نوع الخيار الثالث |
| 39 | [3] القيمة | option_3_value | قيمة الخيار |
| 40 | [3] الصورة / اللون | option_3_image_color | صورة مرتبطة بالخيار |
في المنتج الرئيسي:
[3] الاسم: صورة الشعار
[3] النوع: صورة
[3] القيمة: ممنوع الكتابة في هذا الحقل في صف المنتج
[3] الصورة / اللون: ممنوع الكتابة في هذا الحقل في صف المنتج
وفي المتغيرات:
[3] القيمة: الصورة الأولى
[3] الصورة / اللون: رابط الصورة
أو:
[3] القيمة: الصورة الثانية
[3] الصورة / اللون: رابط الصورة
إذن الخيار الثالث هو:
صورة الشعار → صورة → الصورة الأولى/الثانية
🧩 البنية الحقيقية للمنتج
بعد تحليل الأعمدة مع الصفوف، يمكن تمثيل المنتج الأول هكذا:
ملابس سباحة
│
├── اللون الأساسي
│ ├── أسود #000000
│ ├── أحمر #FF0000
│ └── أزرق #0000FF
│
├── المقاس
│ ├── XL
│ └── L
│
└── صورة الشعار
├── الصورة الأولى
└── الصورة الثانية
وهذا ينتج:
3 ألوان × 2 مقاسات × 2 صور = 12 تركيبة/Variant
وهي الموجودة في الملف:
sku-test-1
sku-test-2
sku-test-3
sku-test-4
sku-test-5
sku-test-6
sku-test-7
sku-test-8
sku-test-9
sku-test-10
sku-test-11
sku-test-12
📑 ورقة Categories, Types & Brands
الورقة الثانية تحتوي 3 أعمدة مرجعية:
| # | اسم العمود | وظيفته |
|---|---|---|
| 1 | ما يمتلكه متجرك الحالى من تصنيفات | قائمة تصنيفات المتجر |
| 2 | أنواع المنتجات المتاحة حالياَ | أنواع المنتجات التي يمكن استخدامها |
| 3 | ما يمتلكه متجرك الحالى من ماركات تجارية | قائمة العلامات التجارية |
والأنواع الموجودة في الملف تشمل:
تبرع
مجموعة منتجات
بطاقة رقمية
منتج رقمي
أكل
خدمة حسب الطلب
منتج جاهز
منتج حجز
كفالة
🧠 الخلاصة الهيكلية
الـ 40 عمودًا ليست 40 معلومة منفصلة فقط؛ بل هي مقسمة منطقيًا إلى 8 طبقات:
01. Product Identity
├── النوع
├── اسم المنتج
├── التصنيف
└── نوع المنتج
02. Media
├── صور المنتج
└── وصف الصور
03. Pricing
├── السعر
├── سعر التكلفة
├── السعر المخفض
└── فترة التخفيض
04. Shipping & Quantity
├── الشحن
├── أقصى كمية
├── إخفاء الكمية
├── صورة عند الطلب
└── الوزن
05. Brand & Marketing
├── الماركة
├── العنوان الترويجي
└── تثبيت المنتج
06. Identifiers
├── SKU
├── Barcode
├── MPN
└── GTIN
07. Tax & Food
├── الضريبة
├── سبب الإعفاء
└── السعرات الحرارية
08. Product Options
├── Option 1: اللون
├── Option 2: المقاس
└── Option 3: الصورة
والأهم: الملف ليس مجرد جدول منتجات؛ إنه في الحقيقة Schema لاستيراد Product + Variants + Options + Reference Data.
في الخطوة التالية أستطيع إعادة كتابة هذه الـ40 خانة في جدول احترافي كامل جدًا يحتوي لكل حقل: الاسم العربي + الاسم التقني المقترح + نوع البيانات SQL + Required/Optional + مثال + قواعد التحقق Validation + هل يخص Product أم Variant + علاقته بقاعدة البيانات. وهذا سيكون أساسًا ممتازًا لتحويل القالب إلى نظام تجارة إلكترونية مثل TwistTaste Mart.
استخراج الأعمدة الأربعين الفعلية من ورقة Salla Products Template Sheet، وسأميز بين اسم الحقل الأصلي وبين الاسم التقني المقترح حتى لا نخلط بين قالب Salla وتصميم قاعدة بيانات جديدة.
📦 قاموس الحقول الكامل — 40 حقلًا
| # | اسم العمود الأصلي | الاسم التقني المقترح | نوع البيانات | المستوى | مطلوب؟ | مثال من الملف | الوصف / قاعدة الاستخدام |
|---|---|---|---|---|---|---|---|
| 1 | النوع | type | ENUM / VARCHAR | Product / Variant | نعم | منتج / خيار | يحدد هل الصف منتج رئيسي أم Variant/خيار |
| 2 | أسم المنتج | product_name | VARCHAR(255) | Product | نعم | ملابس سباحة | الاسم الظاهر للمنتج |
| 3 | تصنيف المنتج | category | VARCHAR / Path | Product | نعم | ملابس > رجالي | مسار تصنيف المنتج |
| 4 | صورة المنتج | product_images | TEXT / URLs | Product | اختياري | 3 روابط | روابط صور المنتج، ويمكن أن يحتوي الحقل على عدة صور |
| 5 | وصف صورة المنتج | image_description | TEXT | Product | اختياري | وصف صورة المنتج | وصف/نص بديل للصورة |
| 6 | نوع المنتج | product_type | ENUM / VARCHAR | Product | نعم | منتج جاهز | نوع المنتج وفق أنواع المنصة |
| 7 | سعر المنتج | price | DECIMAL(12,2) | Product / Variant | نعم | 45 | سعر البيع الأساسي |
| 8 | الوصف | description | LONGTEXT | Product | اختياري | فارغ | الوصف التفصيلي للمنتج |
| 9 | هل يتطلب شحن؟ | requires_shipping | BOOLEAN | Product | نعم | نعم | يحدد هل المنتج مادي ويحتاج إلى شحن |
| 10 | رمز المنتج sku | sku | VARCHAR(100) | Product / Variant | نعم | sku ABC | معرف SKU الفريد |
| 11 | سعر التكلفة | cost_price | DECIMAL(12,2) | Product / Variant | اختياري | 40.9 | تكلفة المنتج لحساب الهامش والربحية |
| 12 | السعر المخفض | sale_price | DECIMAL(12,2) | Product / Variant | اختياري | 45* | سعر البيع أثناء التخفيض |
| 13 | تاريخ بداية التخفيض | sale_start_date | DATETIME | Product / Variant | اختياري | 2026-09-13 | بداية صلاحية السعر المخفض |
| 14 | تاريخ نهاية التخفيض | sale_end_date | DATETIME | Product / Variant | اختياري | 2026-09-13 | نهاية صلاحية السعر المخفض |
| 15 | اقصي كمية لكل عميل | max_quantity_per_customer | INT | Product / Variant | اختياري | 0 | الحد الأعلى لكمية الشراء للعميل |
| 16 | إخفاء خيار تحديد الكمية | hide_quantity_selector | BOOLEAN | Product | اختياري | نعم | إخفاء واجهة اختيار الكمية |
| 17 | اضافة صورة عند الطلب | allow_order_image | BOOLEAN | Product | اختياري | نعم | السماح للعميل بإرفاق صورة مع الطلب |
| 18 | الوزن | weight | DECIMAL(10,3) | Product / Variant | اختياري | 10 | وزن المنتج |
| 19 | وحدة الوزن | weight_unit | ENUM / VARCHAR | Product / Variant | اختياري | kg | وحدة الوزن مثل kg / g |
| 20 | الماركة | brand | VARCHAR(255) | Product | اختياري | زارا | العلامة التجارية |
| 21 | العنوان الترويجي | promotion_title | VARCHAR(255) | Product | اختياري | فارغ | عنوان تسويقي قصير |
| 22 | تثبيت المنتج | is_pinned | BOOLEAN | Product | اختياري | نعم | تثبيت المنتج وإبرازه |
| 23 | الباركود | barcode | VARCHAR(100) | Product / Variant | اختياري | test barcode optional | رمز Barcode للمنتج |
| 24 | السعرات الحرارية | calories | DECIMAL(10,2) | Product / Variant | اختياري | نص توضيحي | السعرات، خصوصًا للمنتجات الغذائية |
| 25 | MPN | mpn | VARCHAR(100) | Product / Variant | اختياري | MPN | Manufacturer Part Number |
| 26 | GTIN | gtin | VARCHAR(100) | Product / Variant | اختياري | GTIN | Global Trade Item Number |
| 27 | خاضع للضريبة ؟ | is_taxable | BOOLEAN | Product | نعم | نعم | تحديد خضوع المنتج للضريبة |
| 28 | سبب عدم الخضوع للضريبة | tax_exemption_reason | TEXT | Product | مشروط | نص توضيحي | يستخدم عندما يكون المنتج غير خاضع للضريبة |
| 29 | [1] الاسم | option_1_name | VARCHAR(255) | Option | مشروط | اللون الأساسي | اسم الخيار الأول |
| 30 | [1] النوع | option_1_type | ENUM / VARCHAR | Option | مشروط | لون | نوع الخيار الأول |
| 31 | [1] القيمة | option_1_value | VARCHAR(255) | Variant | مشروط | أسود | قيمة الخيار الأول |
| 32 | [1] الصورة / اللون | option_1_visual | VARCHAR / URL / HEX | Variant | مشروط | #000000 | لون HEX أو رابط صورة حسب نوع الخيار |
| 33 | [2] الاسم | option_2_name | VARCHAR(255) | Option | مشروط | المقاس | اسم الخيار الثاني |
| 34 | [2] النوع | option_2_type | ENUM / VARCHAR | Option | مشروط | نص | نوع الخيار الثاني |
| 35 | [2] القيمة | option_2_value | VARCHAR(255) | Variant | مشروط | XL | قيمة الخيار الثاني |
| 36 | [2] الصورة / اللون | option_2_visual | VARCHAR / URL / HEX | Variant | مشروط | فارغ | قيمة مرئية إضافية إذا كان نوع الخيار يسمح بذلك |
| 37 | [3] الاسم | option_3_name | VARCHAR(255) | Option | مشروط | صورة الشعار | اسم الخيار الثالث |
| 38 | [3] النوع | option_3_type | ENUM / VARCHAR | Option | مشروط | صورة | نوع الخيار الثالث |
| 39 | [3] القيمة | option_3_value | VARCHAR(255) | Variant | مشروط | الصورة الأولى | قيمة الخيار الثالث |
| 40 | [3] الصورة / اللون | option_3_visual | VARCHAR / URL / HEX | Variant | مشروط | رابط صورة | الصورة المرتبطة بقيمة الخيار |
ملاحظة مهمة حول
*: Excel يخزن بعض القيم الرقمية في الملف بتنسيق تاريخ. مثلًا القيمة الموجودة في حقل السعر المخفض تظهر برمجيًا كتاريخ Excel مثل1900-02-04، لذلك عند بناء Importer يجب قراءة القيمة الرقمية الأصلية والتنسيق وليس اعتبارها تاريخًا حقيقيًا تلقائيًا.
🧬 تقسيم الـ40 حقلًا معماريًا
بدل التعامل معها كـ40 عمودًا مسطحًا، الأفضل فهمها كـ 8 Domains:
PRODUCT
│
├── 01. Identity
│ ├── type
│ ├── product_name
│ ├── category
│ └── product_type
│
├── 02. Media
│ ├── product_images
│ └── image_description
│
├── 03. Content
│ └── description
│
├── 04. Pricing
│ ├── price
│ ├── cost_price
│ ├── sale_price
│ ├── sale_start_date
│ └── sale_end_date
│
├── 05. Inventory / Shipping
│ ├── requires_shipping
│ ├── sku
│ ├── max_quantity_per_customer
│ ├── hide_quantity_selector
│ ├── weight
│ └── weight_unit
│
├── 06. Brand / Marketing
│ ├── brand
│ ├── promotion_title
│ └── is_pinned
│
├── 07. Identification / Tax
│ ├── barcode
│ ├── calories
│ ├── mpn
│ ├── gtin
│ ├── is_taxable
│ └── tax_exemption_reason
│
└── 08. Options / Variants
├── Option 1
│ ├── name
│ ├── type
│ ├── value
│ └── visual
│
├── Option 2
│ ├── name
│ ├── type
│ ├── value
│ └── visual
│
└── Option 3
├── name
├── type
├── value
└── visual
🔗 العلاقة بين Product و Variant
وهذه أهم نقطة في الملف.
الصف:
منتج | ملابس سباحة
هو Parent Product.
أما:
خيار | ملابس سباحة | sku-test-1
فهو Variant تابع للمنتج.
مثال:
Product
└── ملابس سباحة
│
├── Option: اللون الأساسي
│ ├── أسود
│ ├── أحمر
│ └── أزرق
│
├── Option: المقاس
│ ├── XL
│ └── L
│
└── Option: صورة الشعار
├── الصورة الأولى
└── الصورة الثانية
وبالتالي لدينا:
3 × 2 × 2 = 12 Variants
وكل Variant يمكن أن يمتلك بياناته الخاصة:
Variant
├── SKU
├── Price
├── Cost Price
├── Sale Price
├── Barcode
├── MPN
├── GTIN
├── Option Values
└── Image
وهذا واضح جدًا في الملف؛ فمثلًا sku-test-1 يمثل تركيبة:
اللون = أسود
المقاس = XL
الصورة = الصورة الأولى
السعر = 55
التكلفة = 22.94
بينما sku-test-12 يمثل:
اللون = أزرق
المقاس = L
الصورة = الصورة الثانية
السعر = 100
التكلفة = 90.94
⚠️ حقول تحتاج معالجة قبل تحويلها إلى نظام Production
هناك 5 نقاط أعتبرها مهمة جدًا:
1. type
القيم الحالية:
منتج
خيار
الأفضل داخليًا:
product
variant
مع الاحتفاظ بالترجمة في طبقة العرض.
2. category
القيمة:
ملابس > رجالي
ليست أفضل صيغة لقاعدة البيانات.
الأفضل:
categories
├── ملابس
└── رجالي
ثم علاقة:
product_categories
3. الصور
الحقل الحالي يجمع عدة URLs في خلية واحدة:
URL1,URL2,URL3
الأفضل في النظام:
product_images
----------------
id
product_id
url
sort_order
alt_text
is_primary
4. Options
وجود:
[1]
[2]
[3]
مناسب لـ Excel Import Template، لكنه ليس التصميم المثالي لقاعدة البيانات.
الأفضل:
product_options
----------------
id
product_id
name
type
sort_order
product_option_values
----------------
id
option_id
value
visual
5. حقول التعليمات
مثل:
نص توضيحي فقط: ممنوع الكتابة...
هذه ليست بيانات منتج.
يجب أن تكون:
Template Instructions
خارج البيانات الفعلية، أو يتم تجاهلها أثناء الاستيراد.
🏗️ الشكل الاحترافي المقترح للـSchema
لو كان الهدف تحويل هذا القالب إلى منصة تجارة إلكترونية حديثة، فأنا لا أنصح بإنشاء جدول واحد فيه 40 عمودًا.
الأفضل:
products
│
├── product_images
│
├── product_categories
│
├── product_options
│ └── product_option_values
│
├── product_variants
│ └── variant_option_values
│
├── product_prices
│
├── product_identifiers
│
├── product_shipping
│
├── product_tax
│
└── brands
أي أن ملف Excel الحالي هو Import/Export Schema، وليس بالضرورة أن يكون هو Database Schema.
وهذا التفريق مهم جدًا إذا كنا سنبني فوقه لاحقًا TwistTaste Mart / Multivendor Commerce / Salla-compatible Importer.
إذا أردت الاستمرار بنفس المستوى، فالخطوة المنطقية التالية هي أن أحول هذه الـ40 خانة إلى Database Schema احترافي كامل: MySQL tables + primary/foreign keys + indexes + ENUMs + validation rules + Product/Variant/Option relationships + JSON API schema، بحيث يصبح جاهزًا للبناء الفعلي.
هل-يمكنك-اعادة-كتابتها-في-جداول-منظمة-ومفرغة-للادخال-كاصيغة-Production واعادة-كتابة-الجداول-كاملة
تم التحليل
import openpyxl, os, json, pandas as pd
from openpyxl import Workbook
from openpyxl.worksheet.table import Table, TableStyleInfo
src="/mnt/data/files/product-sample (1).xlsx"
wb_src=openpyxl.load_workbook(src, data_only=False)
ws=wb_src["Salla Products Template Sheet"]
headers=[c.value for c in ws[1]]
headers
النتائج
['بيانات المنتج', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]
تم التحليل
import openpyxl, os, re, json
from openpyxl import Workbook
from openpyxl.worksheet.table import Table, TableStyleInfo
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
out="/mnt/data/production_product_template.xlsx"
# Production-oriented normalized workbook derived from the uploaded Salla product template.
wb=Workbook()
default=wb.active
wb.remove(default)
# ---------- Styles ----------
header_fill = PatternFill("solid", fgColor="1F6D66")
sub_fill = PatternFill("solid", fgColor="DCEFEA")
header_font = Font(color="FFFFFF", bold=True)
bold = Font(bold=True)
thin = Side(style="thin", color="D9E2E1")
border = Border(left=thin,right=thin,top=thin,bottom=thin)
def make_sheet(name, headers, widths=None, freeze="A2", table_name=None):
ws=wb.create_sheet(name)
ws.append(headers)
for cell in ws[1]:
cell.fill=header_fill
cell.font=header_font
cell.alignment=Alignment(horizontal="center", vertical="center", wrap_text=True)
cell.border=border
ws.freeze_panes=freeze
ws.auto_filter.ref=f"A1:{get_column_letter(len(headers))}1"
ws.row_dimensions[1].height=34
if widths:
for i,w in enumerate(widths,1):
ws.column_dimensions[get_column_letter(i)].width=w
if table_name:
ref=f"A1:{get_column_letter(len(headers))}2"
tab=Table(displayName=table_name, ref=ref)
tab.tableStyleInfo=TableStyleInfo(name="TableStyleMedium2", showFirstColumn=False,
showLastColumn=False, showRowStripes=True, showColumnStripes=False)
ws.add_table(tab)
return ws
# 1 Products
products_headers=[
"product_id","product_code","name","slug","category_id","product_type_id","brand_id",
"description","promotion_title","status","is_pinned","requires_shipping","hide_quantity_selector",
"allow_order_image","max_quantity_per_customer","weight","weight_unit","is_taxable",
"tax_exemption_reason","calories","primary_image_id","created_at","updated_at"
]
ws=make_sheet("Products",products_headers,[18,18,30,28,16,18,16,45,30,14,12,18,22,18,22,12,12,14,35,14,18,20,20],"A2","ProductsTable")
# add blank production rows
for _ in range(20): ws.append([""]*len(products_headers))
# 2 Variants
variants_headers=[
"variant_id","product_id","sku","barcode","mpn","gtin","price","cost_price","sale_price",
"sale_start_date","sale_end_date","weight","weight_unit","status","stock_quantity","image_id","notes"
]
ws=make_sheet("Variants",variants_headers,[18,18,20,20,18,20,14,14,14,20,20,12,12,14,16,18,35],"A2","VariantsTable")
for _ in range(30): ws.append([""]*len(variants_headers))
# 3 Options
options_headers=["option_id","product_id","name","type","sort_order","is_required"]
ws=make_sheet("Options",options_headers,[18,18,28,18,12,14],"A2","OptionsTable")
for _ in range(15): ws.append([""]*len(options_headers))
# 4 Option Values
values_headers=["option_value_id","option_id","value","visual_type","visual_value","sort_order","is_active"]
ws=make_sheet("OptionValues",values_headers,[20,18,28,16,45,12,14],"A2","OptionValuesTable")
for _ in range(30): ws.append([""]*len(values_headers))
# 5 Variant Option Values
vov_headers=["variant_id","option_value_id"]
ws=make_sheet("VariantOptionValues",vov_headers,[18,22],"A2","VariantOptionValuesTable")
for _ in range(40): ws.append([""]*len(vov_headers))
# 6 Images
images_headers=["image_id","product_id","variant_id","url","alt_text","sort_order","is_primary"]
ws=make_sheet("Images",images_headers,[18,18,18,70,40,12,14],"A2","ImagesTable")
for _ in range(30): ws.append([""]*len(images_headers))
# 7 Categories
cat_headers=["category_id","parent_category_id","name","slug","status","sort_order"]
ws=make_sheet("Categories",cat_headers,[20,24,35,30,14,12],"A2","CategoriesTable")
for _ in range(30): ws.append([""]*len(cat_headers))
# 8 Brands
brand_headers=["brand_id","name","slug","status"]
ws=make_sheet("Brands",brand_headers,[18,35,35,14],"A2","BrandsTable")
for _ in range(20): ws.append([""]*len(brand_headers))
# 9 Product Types
ptype_headers=["product_type_id","name","code","status"]
ws=make_sheet("ProductTypes",ptype_headers,[22,35,30,14],"A2","ProductTypesTable")
for _ in range(15): ws.append([""]*len(ptype_headers))
# 10 Import Map
map_headers=["original_column","production_field","entity","data_type","required","validation_rule","notes"]
ws=make_sheet("ImportMap",map_headers,[34,34,20,20,12,55,55],"A2","ImportMapTable")
mapping=[
("النوع","Products / Variants","Product/Variant","ENUM", "YES","product|variant","يفصل المنتج الرئيسي عن المتغير"),
("أسم المنتج","Products.name","Product","VARCHAR(255)","YES","1-255 chars","اسم المنتج"),
("تصنيف المنتج","Products.category_id","Product","FK","YES","existing category_id","العلاقة مع Categories"),
("صورة المنتج","Images.url","Image","URL","NO","valid URL","لا تجمع عدة روابط في خلية واحدة"),
("وصف صورة المنتج","Images.alt_text","Image","TEXT","NO","max 500 chars","وصف بديل للصورة"),
("نوع المنتج","Products.product_type_id","Product","FK","YES","existing product_type_id","العلاقة مع ProductTypes"),
("سعر المنتج","Variants.price","Variant","DECIMAL(12,2)","YES",">=0","السعر الفعلي للمتغير"),
("الوصف","Products.description","Product","LONGTEXT","NO","UTF-8 text","الوصف"),
("هل يتطلب شحن؟","Products.requires_shipping","Product","BOOLEAN","YES","0|1",""),
("رمز المنتج SKU","Variants.sku","Variant","VARCHAR(100)","YES","unique","SKU يجب أن يكون فريدًا"),
("سعر التكلفة","Variants.cost_price","Variant","DECIMAL(12,2)","NO",">=0",""),
("السعر المخفض","Variants.sale_price","Variant","DECIMAL(12,2)","NO",">=0 and <= price",""),
("تاريخ بداية التخفيض","Variants.sale_start_date","Variant","DATETIME","NO","valid datetime",""),
("تاريخ نهاية التخفيض","Variants.sale_end_date","Variant","DATETIME","NO",">= start date",""),
("اقصي كمية لكل عميل","Products.max_quantity_per_customer","Product","INT","NO",">=0","0 يمكن اعتباره بلا حد حسب سياسة النظام"),
("إخفاء خيار تحديد الكمية","Products.hide_quantity_selector","Product","BOOLEAN","NO","0|1",""),
("اضافة صورة عند الطلب","Products.allow_order_image","Product","BOOLEAN","NO","0|1",""),
("الوزن","Products.weight / Variants.weight","Product/Variant","DECIMAL(10,3)","NO",">=0",""),
("وحدة الوزن","Products.weight_unit / Variants.weight_unit","Product/Variant","ENUM","NO","g|kg|lb|oz",""),
("الماركة","Products.brand_id","Product","FK","NO","existing brand_id","العلاقة مع Brands"),
("العنوان الترويجي","Products.promotion_title","Product","VARCHAR(255)","NO","max 255 chars",""),
("تثبيت المنتج","Products.is_pinned","Product","BOOLEAN","NO","0|1",""),
("الباركود","Variants.barcode","Variant","VARCHAR(100)","NO","unique when present",""),
("السعرات الحرارية","Products.calories","Product","DECIMAL(10,2)","NO",">=0",""),
("MPN","Variants.mpn","Variant","VARCHAR(100)","NO","max 100 chars",""),
("GTIN","Variants.gtin","Variant","VARCHAR(100)","NO","valid GTIN when supplied",""),
("خاضع للضريبة؟","Products.is_taxable","Product","BOOLEAN","YES","0|1",""),
("سبب عدم الخضوع للضريبة","Products.tax_exemption_reason","Product","TEXT","CONDITIONAL","required when is_taxable=0",""),
("[1] الاسم","Options.name","Option","VARCHAR(255)","CONDITIONAL","option exists","الخيار الأول"),
("[1] النوع","Options.type","Option","ENUM","CONDITIONAL","text|color|image|...",""),
("[1] القيمة","OptionValues.value","OptionValue","VARCHAR(255)","CONDITIONAL","non-empty",""),
("[1] الصورة / اللون","OptionValues.visual_value","OptionValue","VARCHAR/TEXT","NO","HEX or URL depending on visual_type",""),
("[2] الاسم","Options.name","Option","VARCHAR(255)","CONDITIONAL","option exists","الخيار الثاني"),
("[2] النوع","Options.type","Option","ENUM","CONDITIONAL","text|color|image|...",""),
("[2] القيمة","OptionValues.value","OptionValue","VARCHAR(255)","CONDITIONAL","non-empty",""),
("[2] الصورة / اللون","OptionValues.visual_value","OptionValue","VARCHAR/TEXT","NO","HEX or URL depending on visual_type",""),
("[3] الاسم","Options.name","Option","VARCHAR(255)","CONDITIONAL","option exists","الخيار الثالث"),
("[3] النوع","Options.type","Option","ENUM","CONDITIONAL","text|color|image|...",""),
("[3] القيمة","OptionValues.value","OptionValue","VARCHAR(255)","CONDITIONAL","non-empty",""),
("[3] الصورة / اللون","OptionValues.visual_value","OptionValue","VARCHAR/TEXT","NO","HEX or URL depending on visual_type",""),
]
for row in mapping: ws.append(row)
# 11 Instructions
ws=make_sheet("README",["section","item","details"],[25,35,90],"A2","ReadmeTable")
readme=[
("Purpose","Production template","قالب فارغ ومنظم للإدخال قبل الاستيراد إلى نظام تجارة إلكترونية."),
("Architecture","Products","البيانات العامة للمنتج."),
("Architecture","Variants","كل تركيبة قابلة للبيع لها SKU وسعر ومخزون مستقل."),
("Architecture","Options","تعريف خيارات المنتج مثل اللون والمقاس."),
("Architecture","OptionValues","القيم الفعلية لكل خيار."),
("Architecture","VariantOptionValues","يربط كل Variant بقيم خياراته."),
("Architecture","Images","صور المنتج والمتغيرات، رابط واحد لكل صف."),
("Reference","Categories","التصنيفات الهرمية."),
("Reference","Brands","العلامات التجارية."),
("Reference","ProductTypes","أنواع المنتجات."),
("Validation","Money","استخدم أرقامًا عشرية، وليس تواريخ Excel."),
("Validation","Dates","صيغة مقترحة: YYYY-MM-DD HH:MM:SS."),
("Validation","IDs","استخدم معرفات فريدة وثابتة."),
("Validation","SKU","يجب أن يكون فريدًا على مستوى المتغيرات."),
("Validation","Tax","إذا is_taxable=0 يجب إدخال tax_exemption_reason."),
("Validation","Images","لا تضع عدة URLs في خلية واحدة؛ أنشئ صفًا لكل صورة."),
("Validation","Options","لا تستخدم أعمدة [1][2][3] داخل قاعدة البيانات؛ استخدم Options/OptionValues."),
]
for r in readme: ws.append(r)
# Add light formatting and data validation
from openpyxl.worksheet.datavalidation import DataValidation
for ws in wb.worksheets:
for row in ws.iter_rows(min_row=2):
for cell in row:
cell.border=border
cell.alignment=Alignment(vertical="top", wrap_text=True)
# ensure blank production rows are visually ready
if ws.title not in ("ImportMap","README"):
for row in ws.iter_rows(min_row=2):
for cell in row:
if cell.value=="":
cell.fill=PatternFill("solid", fgColor="F8FBFA")
# Basic validations
ws=wb["Products"]
dv_bool=DataValidation(type="list", formula1='"0,1"', allow_blank=True)
ws.add_data_validation(dv_bool); dv_bool.add(f"K2:N21"); dv_bool.add(f"R2:R21")
ws=wb["Variants"]
dv_bool2=DataValidation(type="list", formula1='"active,inactive"', allow_blank=True)
ws.add_data_validation(dv_bool2); dv_bool2.add("N2:N31")
# Remove table second blank-row issue by keeping exactly header + blank rows is valid.
# Set workbook properties
wb.properties.title="Production Product Data Template"
wb.properties.subject="Normalized production product/variant/options import template"
wb.properties.creator="Zezo Technology AI"
wb.save(out)
print(f"تم إنشاء قالب Production كامل وفارغ: {out}")
print("الأوراق:", ", ".join(wb.sheetnames))
print("الهيكل: Products + Variants + Options + OptionValues + VariantOptionValues + Images + Categories + Brands + ProductTypes + ImportMap + README")
STDOUT/STDERR
تم إنشاء قالب Production كامل وفارغ: /mnt/data/production_product_template.xlsx الأوراق: Products, Variants, Options, OptionValues, VariantOptionValues, Images, Categories, Brands, ProductTypes, ImportMap, README الهيكل: Products + Variants + Options + OptionValues + VariantOptionValues + Images + Categories + Brands + ProductTypes + ImportMap + README
نعم. جهزت لك نسخة Production فارغة ومنظمة بالكامل بدل الجدول المسطح ذي الـ40 عمودًا.
📦 ملف Production Template
تحميل قالب Production — Excel
يحتوي الملف على الجداول الكاملة:
| الجدول | الغرض |
|---|---|
Products | البيانات الأساسية للمنتجات |
Variants | المتغيرات والتركيبات القابلة للبيع |
Options | تعريف خيارات المنتج |
OptionValues | قيم الخيارات مثل أسود، XL |
VariantOptionValues | ربط المتغيرات بخياراتها |
Images | صور المنتجات والمتغيرات |
Categories | التصنيفات الهرمية |
Brands | العلامات التجارية |
ProductTypes | أنواع المنتجات |
ImportMap | تحويل حقول Salla إلى Production Schema |
README | قواعد الإدخال والتحقق |
مثال الهيكل
Products
│
├── Categories
├── Brands
├── ProductTypes
├── Images
│
├── Options
│ └── OptionValues
│
└── Variants
│
└── VariantOptionValues
وهذا أفضل بكثير للإنتاج من وضع كل شيء في جدول واحد:
Product + 40 Columns
لأنه يسمح لاحقًا بتحويل البيانات مباشرة إلى:
MySQL → Laravel → REST API → Vue/React → WooCommerce/Salla-compatible importer → Multivendor Marketplace.
كما أضفت داخل الملف ImportMap بحيث نستطيع معرفة أين ينتقل كل حقل من قالب Salla إلى البنية الجديدة، وقواعد التحقق مثل SKU unique، وGTIN، والتواريخ، والضريبة، والصور.
الملف الحالي فارغ للإدخال Production؛ أي لم أخلط بيانات العينة داخل جداول الإنتاج. production_product_template.xlsxجداول البياناتفتح الملف