Pricing & Promotionsالتسعير والعروض.
إزاي Magento بيوصل للسعر النهائي. أنواع الأسعار وترتيب أولويتها، الفرق بين عروض الكتالوج وعروض السلة، الـ price indexer، والضرايب.
التسعير في Magento 2 بيبدأ من الـ price، والـ final_price هو الأقل بينه وبين الـ special_price والـ tier_price وسعر الـ Catalog Price Rule، وده اللي بيظهر في الكتالوج والفلاتر ومحتاج فهرسة. الـ Cart Price Rules والكوبونات بتخصم من الإجمالي في السلة كسطر منفصل من غير ما تغيّر سعر المنتج. في الكود استخدم حقول base_ للمنطق.
أنواع الأسعار
فيه أكتر من "سعر" على المنتج الواحد.
سلّم الأولوية
أهم قسم — إزاي Magento بيقرّر السعر النهائي.
Magento بيبدأ بالسعر الأساسي وبيمرّ على الطبقات. في كل طبقة، بياخد الأقل عادة — بس الترتيب نفسه مهم لأن كل طبقة بتشتغل على ناتج اللي قبلها.
الطبقات 1–3 بتحدد final_price — السعر اللي بيظهر في صفحة المنتج وفي القوائم. الطبقة 4 مابتغيّرش سعر المنتج، بتضيف خصم على مستوى السلة.
price = 1000 special_price = 800 # الأقل، فياخد الأولوية # → 800 catalog rule: خصم 10% # بتشتغل على الـ 800 # → 720 = final_price # العميل بيشوف 720 في صفحة المنتج cart rule: خصم 50 ج # على مستوى السلة # → الإجمالي 670 # بس سعر المنتج في السلة لسه بيتعرض 720 # والخصم بيظهر كسطر منفصل في الإجماليات
الـ Scope والعملات
فين السعر بيختلف، وليه الـ base مهمة.
بيتحدد من Stores → Configuration → Catalog → Price → Catalog Price Scope، وله قيمتين.
Global
سعر واحد لكل المتاجر. أبسط في الإدارة.
Website
سعر مختلف لكل website. للأسواق المختلفة.
if ($quote->getGrandTotal() > 500) { // 500 بأي عملة؟ النتيجة بتختلف // حسب العملة اللي العميل مختارها }
if ($quote->getBaseGrandTotal() > 500) { // 500 بالعملة الأساسية — ثابت دايماً }
Catalog Price Rules
خصومات بتغيّر سعر المنتج نفسه.
قاعدة بتطبّق خصم على منتجات بتنطبق عليها شروط، والخصم بيظهر في سعر المنتج نفسه — في صفحة المنتج وفي القوائم والفلاتر.
- الشروط — على خصائص المنتج (القسم، الماركة، السعر).
- الإجراء — نسبة مئوية، مبلغ ثابت، أو سعر ثابت.
- النطاق — websites ومجموعات عملاء محددة.
- التواريخ — بداية ونهاية.
- الأولوية — لما أكتر من قاعدة تنطبق.
لأن الخصم بيدخل في الـ final_price، فهو بيظهر في الفلاتر والترتيب بالسعر. لو فلترت "أقل من 500"، المنتج اللي بقى 450 بعد الخصم هيظهر.
# إعادة فهرسة أسعار الكتالوج bin/magento indexer:reindex catalog_rule # وفهرس الأسعار العام bin/magento indexer:reindex catalog_product_price bin/magento cache:clean
- تخفيض على فئة كاملة — "خصم 20% على كل الشتوي".
- تسعير خاص لمجموعة عملاء — أسعار الجملة.
- لما تحتاج الخصم يظهر في الفلاتر.
Cart Price Rules
خصومات على مستوى السلة.
قاعدة بتطبّق خصم على السلة لما شروط تتحقق — إجمالي معيّن، عدد أصناف، منتج محدد موجود. الخصم بيظهر كسطر منفصل في الإجماليات.
أقوى من الـ catalog rules — تقدر تشترط على الإجمالي، عدد الأصناف، الوزن، بلد الشحن، مجموعة العميل، وجود منتج معيّن.
الفرق والتفاعل بينهم
أهم مقارنة في الموضوع كله.
الـ cart rule بتشتغل على الـ final_price اللي طلع من الـ catalog rule. يعني بيتراكموا.
السعر 1000 catalog rule -20% → 800 (final_price) cart rule -10% → 720 (الإجمالي) # الخصم الفعلي 28% مش 30% # لأن التانية اشتغلت على 800 مش 1000
Catalog Rule
لما عايز الخصم يبان في الكتالوج ويجذب العميل من الأول.
Cart Rule
لما الخصم مشروط بالسلة — إجمالي معيّن، كوبون، شحن مجاني.
الكوبونات
نوع خاص من الـ cart rules.
الـ Price Indexer
ليه الأسعار محتاجة فهرسة أصلاً.
حساب الـ final_price لمنتج واحد بيحتاج: السعر، الـ special، الـ tier، مجموعة العميل، الـ website، والقواعد المنطبقة. عمل ده لكل منتج في صفحة قسم مكلّف جداً.
Magento بيحسب الأسعار مقدماً ويحطها في جدول مسطّح. صفحة القسم بتقرا من الجدول ده مباشرة.
# حالة الفهارس bin/magento indexer:status # إعادة فهرسة الأسعار bin/magento indexer:reindex catalog_product_price bin/magento indexer:reindex catalog_rule_product # شوف السعر المفهرس فعلاً # SELECT * FROM catalog_product_index_price # WHERE entity_id = 42;
التسعير في الكود
قراءة الأسعار والتدخّل فيها.
use Magento\Catalog\Pricing\Price\FinalPrice; use Magento\Catalog\Pricing\Price\RegularPrice; // السعر النهائي بعد كل القواعد $final = $product ->getPriceInfo() ->getPrice(FinalPrice::PRICE_CODE) ->getValue(); // السعر الأساسي قبل الخصومات $regular = $product ->getPriceInfo() ->getPrice(RegularPrice::PRICE_CODE) ->getValue(); // فيه خصم؟ $hasDiscount = $final < $regular;
$product->getPrice(); // الأساسي $product->getFinalPrice(); // بعد الخصومات $product->getSpecialPrice(); // ملاحظة: getFinalPrice مابتاخدش في الاعتبار // الـ cart rules — دي على مستوى السلة
$item = $quote->getItemById($itemId); // الاتنين مع بعض — مهم $item->setCustomPrice($newPrice); $item->setOriginalCustomPrice($newPrice); // عشان السعر ما يترجعش $item->getProduct()->setIsSuperMode(true); $quote->collectTotals(); $this->cartRepository->save($quote);
الضرايب
الجزء اللي بيلخبط في الحسبة.
- Tax Class — تصنيف للمنتج وللعميل.
- Tax Rate — النسبة لمنطقة جغرافية.
- Tax Rule — بتربط التلاتة: منتج + عميل + منطقة = نسبة.
Excluding Tax
السعر المدخل بدون ضريبة، وبتتضاف عند الحساب. شائع في أمريكا.
Including Tax
السعر المدخل شامل الضريبة. شائع في أوروبا والخليج.
الضريبة بتتحسب على المبلغ بعد الخصم. ده الترتيب الصح محاسبياً — العميل مايدفعش ضريبة على فلوس ماخدهاش.
السعر 1000
خصم 10% -100
────
الصافي 900
ضريبة 15% +135 # على 900 مش 1000
────
الإجمالي 1035
الفخاخ الشائعة
اقرا دي حتى لو مش هتقرا حاجة تانية.
- نسيان إعادة الفهرسة — السبب الأول لـ "غيّرت السعر ومش ظاهر".
- خصومات متراكمة بدون Discard subsequent rules — إجمالي أقل من المتوقع أو صفر.
- استخدام getGrandTotal بدل getBaseGrandTotal في المنطق — بيكسر المتاجر متعددة العملات.
- setCustomPrice من غير setOriginalCustomPrice — السعر بيترجع، والعميل يدفع غير اللي شافه.
- قراءة السعر من collection بدون addPriceData — بترجّع السعر الأساسي بدون خصومات، بصمت.
- كوبون بدون حد استخدام أو تاريخ انتهاء — خسارة كبيرة لو اتسرّب.
- الاعتماد على Uses per Customer مع الضيوف — الحد مابيتطبّقش بفعالية.
- plugin على حساب السعر — بيتنده لكل منتج في كل صفحة. جرّب catalog rule الأول.
- تغيير نطاق السعر أو إعداد الضريبة بعد التشغيل — بيأثر على كل الأسعار الموجودة.
- كشف cost في الـ API — تسريب بيانات تجارية حسّاسة.
أسئلة الإنترفيو
الأسئلة المتكررة.
Pricing & Promotions ✓
دلوقتي فاهم سلّم الأسعار وترتيب أولويته، الفرق بين عروض الكتالوج والسلة وإزاي بيتراكموا، الـ price indexer، والتسعير في الكود والضرايب.
الخلاصة: الخصومات بتتراكم على ناتج بعض، والـ catalog rule بتغيّر السعر نفسه والـ cart rule بتخصم من الإجمالي، والفهرسة هي أول مكان تشوفه لما سعر مايظهرش.
خلّصت الدرس؟علّمه عشان تتابع تقدّمك في الكورس.
أسئلة شائعة
إيه الفرق بين Catalog Price Rule و Cart Price Rule في Magento 2؟
الـ Catalog Rule بتغيّر سعر المنتج نفسه، فالخصم بيظهر في صفحة المنتج والقوائم والفلاتر والترتيب بالسعر، وشروطها على خصائص المنتج ومحتاجة فهرسة. الـ Cart Rule بتضيف خصم على مستوى السلة كسطر منفصل في الإجماليات، شروطها على السلة كلها زي الإجمالي وبلد الشحن، وهي الوحيدة اللي بتدعم الكوبونات وبتشتغل لحظيًا.
إزاي Magento 2 بيحسب الـ final_price؟
بياخد الأقل بين السعر الأساسي والـ special_price لو في فترته والـ tier_price المناسب لمجموعة العميل والكمية، وسعر الـ Catalog Rule المحسوب من السعر الأساسي. يعني منتج بـ 1000 عليه special 800 وقاعدة 10% بيبقى سعره 800 مش 720، لأن القاعدة والسعر الخاص بيتنافسوا مش بيتراكموا. اللي بيتراكم فعلًا هو الـ Cart Rule لأنها بتخصم من الـ final_price في السلة.
غيّرت السعر في Magento والعميل لسه شايف القديم، ليه؟
غالبًا الفهرس. الأسعار بتتحسب مقدمًا في جدول catalog_product_index_price، فلو الفهرس invalid أو على Update on Schedule والـ cron واقف، السعر القديم بيفضل. شغّل indexer:status وبعدين indexer:reindex catalog_product_price، ولو الخصم من Catalog Rule أعد فهرسة catalogrule_rule كمان. لو الفهرس صح والعميل شايف غيره، المشكلة في الـ Full Page Cache.
ليه أستخدم getBaseGrandTotal بدل getGrandTotal في الكود؟
لأن أي حقل ليه نسخة base_ متخزّن بالعملة الأساسية الثابتة، والنسخة العادية بعملة العرض اللي بتتحوّل بسعر الصرف حسب اختيار العميل. مقارنة زي الإجمالي أكبر من 500 بعملة العرض بتدي نتيجة مختلفة لكل عملة. استخدم نسخة الـ base في المنطق والمقارنات والعادية في العرض بس، وإلا الكود هيحسب غلط بصمت أول ما تضيف عملة تانية.
إزاي أحط سعر مخصّص لصنف في السلة برمجيًا؟
على الـ quote item نادي setCustomPrice($price) و setOriginalCustomPrice($price) مع بعض، و$item->getProduct()->setIsSuperMode(true)، وبعدين $quote->collectTotals() واحفظ الـ quote. لو حطّيت setCustomPrice لوحدها السعر بيترجع لسعر الكتالوج مع أول collectTotals، وده بيحصل كتير في الـ checkout فالعميل يشوف سعر ويدفع غيره.
ليه خصومات الـ Cart Rules بتتجمّع على بعض في Magento؟
لأن كل قاعدة شروطها متحقّقة بتتطبّق بالترتيب حسب Priority، والرقم الأقل الأول. عشان تمنع التراكم فعّل Discard subsequent rules في القاعدة اللي المفروض تكسب، فالقواعد اللي بعدها مابتشتغلش. من غيره ممكن تلات قواعد يتجمّعوا والإجمالي يوصل صفر. راجع القواعد النشطة مع بعض بسلة حقيقية قبل أي حملة.
إزاي أحمي الكوبونات من الاستغلال في Magento 2؟
حدّد Uses per Coupon و Uses per Customer وتاريخ انتهاء دايمًا، لأن كوبون من غير حدود بيفضل شغّال للأبد لو اتسرّب. خلّي بالك إن Uses per Customer بيشتغل على العملاء المسجّلين بس، فالضيف يقدر يستخدمه أكتر من مرة بإيميلات مختلفة. لو قيمة الكوبون عالية اشترط تسجيل الدخول، وللحملات الشخصية استخدم أكواد مولّدة فريدة.