دورة المشتريات والمخزون — نحو فلو مرن تحكمه الحسابات

🔀 الفلو المرن للمشتريات والمخزون: الموجود · الناقص · الباتشات والسيريالز

وثيقة للاعتماد قبل التنفيذ. بتوصّف الدورة زي ما الكود شغّال دلوقتي، والفلو المرن اللي إنت عايزه (فوترة من أمر الشراء أو من إذن الاستلام، استلام على دفعات، فاتورة شراء مباشرة بدون أمر، والحسابات — 3-way match + GR/IR — هي اللي تقرر الصح مش الأقفال الصلبة)، ثم الفجوات بينهم، وقرار أفضل نقطة لالتقاط الباتش/السيريال (استلام؟ جودة؟ إذن إضافة؟)، ومراجعة شاشة أرصدة المخزون وهل بتدعم الباتشات/الصلاحيات، ووحدة توريد ذكي جديدة (RFQ): بوابة تسعير للمورد بلينك يونيك + مصفوفة مقارنة ملوّنة تولّد أوامر الشراء + طلب شراء واحد يطلّع كذا أمر. كل قسم بيقفل بتوصية محتاجة موافقتك.

📅 9 يوليو 2026 🧠 تحليل بقراءة الكود مباشرة · تصميم بمراجعة Fable 5 🔗 يكمّل: إعادة تصميم الفلو المُحكَم · التحليل الفني · دليل المستخدم

١ الملخص التنفيذي والتحوّل في الفلسفة

من «منع صارم» إلى «مرن + الحسابات تقرر».

الفلو المُحكَم اتبنى كـحوكمة بالأقفال الصلبة: كل مسار غير المسار الرسمي بيتمنع بـ 422/403. أثناء تجربتك اتضح إن سير عملك الفعلي أوسع — عايز مرونة: تستلم على دفعات، تفوتر من أمر الشراء أو من إذن الاستلام، وتعمل فاتورة شراء مباشرة من غير أمر شراء — والحسابات هي اللي تحرس الصح (3-way match يمنع الفوترة الزيادة، وحساب GR/IR يوازن ما بين الاستلام والفوترة أيًّا كان ترتيبهم).

المبدأ الحاكم (بعد مراجعة Fable): نسيب الوضع المُحكَم صارم في جوهره، ونضيف الاستثناءات المحوكَمة بس — وكل منع سياسة يبقى «لافتة إرشاد» بتقولك المسار المعتمد، مش «إيرور أحمر». أي استثناء مقبول لو كان ظاهر + بصلاحية + بعلامة مميزة + في تقرير؛ أي حاجة صامتة أو مفعّلة افتراضيًا = تغيير وضع، وتترفض.

اتظبط واتنشر بالفعل (خارج نطاق الاعتماد ده): زرار «فاتورة شراء» على أمر الشراء (كان مختفي بسبب قيمة حالة قديمة)، وإيرور إذن الإضافة المباشر الوهمي (كان بيعمل حركة فعلًا). دول إصلاحات مؤكدة اتعملها push و deploy.

٢ الفلو الحالي (الموجود في الكود دلوقتي)

دورة المستندات + الحراس الـ9 اللي الوضع المُحكَم بيفرضها + المحاسبة.

سلسلة المستندات

1
المشتريات
طلب شراء → أمر شراء طلب داخلي يتحوّل لأمر شراء لمورد (حاليًا: طلب واحد → أمر واحد).
2
المشتريات
أمر الشراء → إذن استلام (GRN) في الوضع المُحكَم: grn_quality — GRN مبدئي بيتعمل، والاستلام لا يدخل مخزون فورًا.
3
الجودة
فحص الجودة تحديد المقبول/المرفوض + الباتش والصلاحية على بنود الـGRN، ثم اعتماد الجودة.
4
أمين المخازن
اعتماد إذن الإضافة (Inventory Receipt) اعتماد GRN المُحكَم بيعمل إذن إضافة مسودة؛ أمين المخازن يعتمده → المخزون يدخل + إثبات GR/IR.
5
الحسابات
الفاتورة حاليًا في المُحكَم: تتعمل من إذن الاستلام فقط (فاتورة واحدة لكل GRN)، وترحيلها يقفل GR/IR + فروق السعر (PPV).

الحراس الـ9 اللي الوضع المُحكَم بيفرضها (ProcurementPolicy)

الوضع المُحكَم بيتجاهل الإعدادات الفردية ويفرض هذا الطُقم الثابت — عشان ما يتضعّفش بإعداد شارد:

🔒 grn_quality (استلام بجودة) 🔒 إثبات المخزون بالاستلام 🔒 حد الاستلام مفعّل 🔒 3-way match مفعّل 🔒 يتطلب أمر شراء للفاتورة 🔒 دورة GRN واحدة نشطة 🔒 فاتورة واحدة لكل GRN 🔒 تقييد الإذون المستقلة 🔒 اعتماد إذن الاستلام يدوي

المحاسبة (GR/IR — الاستلام أولًا)

الحدثالقيدالغرض
اعتماد إذن الاستلامDR Inventory / CR GR-IRإثبات دخول المخزون بتكلفة الاستلام (استحقاق «بضاعة مستلمة لم تُفوتَر»).
ترحيل الفاتورة (من GRN)DR GR-IR (+PPV) / CR APيقفل الاستحقاق ويحوّل فرق السعر لحساب انحراف أسعار الشراء.
النتيجة: المخزون يتحمّل مرة واحدة (عند الاستلام)، والموردون يتزوّدوا (عند الفاتورة)، وGR/IR يتصفّى صفر. الفلو المرن لازم يحافظ على نفس التوازن أيًّا كان ترتيب الاستلام/الفوترة.

٣ الفلو المطلوب (المرن — زي ما وصفته)

مسارات متعددة مفتوحة، والحسابات تقرر الصح.
طلب شراء ──┬──► أمر شراء            (لاحقًا: طلب واحد → كذا أمر)
          └──► أمر شراء
                 │
  أمر الشراء ────┼──► إذن استلام ١ (دفعة) ─► جودة ─► إذن إضافة ─► أمين المخازن يعتمد ─► مخزون
                 ├──► إذن استلام ٢ (دفعة) ─► …            (كذا GRN بحسب الكميات)
                 │
                 └──► فاتورة:  من أمر الشراء (تجميعي لكل المستلَم)
                              أو من كل إذن استلام (بالدفعة)
                              → الاتنين متاحين، والحسابات تقرر

فاتورة شراء مباشرة (بدون أمر شراء) ─► إذن إضافة مسودة ─► أمين المخازن يعتمد ─► مخزون
  
قواعد المرونة: (١) استلام على دفعات = كذا GRN لأمر واحد. (٢) الفوترة من أمر الشراء (تجميعي) أو من الـGRN (بالدفعة) — الاتنين متاحين. (٣) فاتورة مباشرة بدون أمر شراء مسموحة بمسار محوكَم. (٤) لا فوترة زيادة: الـ3-way match يسقّف الفاتورة عند المستلَم تراكميًا.

٣ب التوريد الذكي — بوابة تسعير المورد ومصفوفة المقارنة (RFQ)

وحدة جديدة (طلب عروض أسعار) قبل أمر الشراء: تبعت الطلب لموردين، تجمّع أسعارهم، وتولّد أوامر الشراء من مصفوفة مقارنة. لا تُلغي المسار اليدوي.

الفلو المطلوب

1
المشتريات
طلب شراء → RFQ تعمل طلب عروض أسعار من طلب الشراء (بياخد نسخة snapshot من البنود).
2
المشتريات
دعوة الموردين تختار مجموعة موردين (أو واحد) وتبعتلهم لينك يونيك لكل مورد على الإيميل / واتساب، بموعد نهائي.
3
المورد
بوابة التسعير (بدون تسجيل دخول) المورد يفتح اللينك، يسعّر البنود (سعر الوحدة + مهلة توريد + ملاحظة)، ويقدّم — يقدر يعدّل لحد الموعد.
4
المشتريات
مصفوفة المقارنة صفوف = المنتجات، أعمدة = الموردون وأسعارهم. أخضر = أرخص، أحمر = أغلى لكل صنف. + إدخال يدوي للعروض اللي جت بالتليفون.
5
المشتريات
توليد أوامر الشراء تختار الأفضل لكل صنف (ممكن موردين مختلفين) → أمر شراء واحد لكل مورد فايز (Draft يدخل الموافقة)، مربوط بالـRFQ والطلب.

الوضع الحالي (اللي موجود دلوقتي)

البندالحالة
وحدة RFQ / تسعير موردين / مقارنةمفيش خالص (greenfield) — الأقرب: SalesQuotation (نمط دورة حياة للاستعارة) وSupplierPriceList (نموذج بيانات).
مقارنة أسعار الموردينفيه endpoint compare() بيرتّب منتج واحد ويعلّم الأرخص — بس الواجهة مبتستهلكوش، ومفيش مصفوفة منتج×مورد. لازم تتبني.
طلب شراء → كذا أمرمقفول 1→1: canConvert() = الحالة «معتمد» فقط، وبعد التحويل الحالة تبقى «محوَّل» + converted_to_order_id عمود مفرد → التحويل التاني يترفض 422.
بوابة خارجية بلينك بدون دخولنمط جاهز للاستعارة: بوابة المريض (portal_link_token 64 حرف → linkAccess → توكن جلسة مُجزّأ → middleware بيقصر الرؤية على سجل واحد + تسجيل محاولات IDOR). (مش بوابة المعمل الخارجي — دي username/password.)
إرسال إيميلضعيف: الـmailer الافتراضي log، صفر Mailable، والإشعارات كلها database بس → محتاج Mailable جديد + SMTP حقيقي.
واتسابجاهز للاستعارة: روابط wa.me من الواجهة + قالب رسالة مخزّن (نفس نمط LIS). مفيش API سيرفر (الإرسال التلقائي هيحتاج WhatsApp Business API — مش موجود).

التصميم المقترح (Fable)

نموذج البيانات — 4 جداول بس، الـRFQ مستند مستقل بيشير للطلب (مش الطلب نفسه):

الجدولالوظيفة
purchase_rfqالمناقصة: مرجع الطلب، رقم، عملة، أساس الضريبة، موعد نهائي، حالة (مسودة → مُرسل → مفتوح → مقفول → مُرسى/ملغي).
purchase_rfq_itemsنسخة (snapshot) من بنود الطلب (منتج، كمية، وحدة، ملاحظة) — منسوخة مش مربوطة حيّة، فتعديل الطلب بعدين ما يغيّرش مناقصة شغّالة.
purchase_rfq_suppliersالدعوة + رأس العرض في صف واحد (عرض واحد لكل مورد لكل RFQ): المورد، الـtoken، القناة، توقيتات الإرسال/الفتح/التقديم، الحالة، ملاحظات المورد.
purchase_rfq_quote_itemsالأسعار: دعوة × بند → سعر الوحدة، مهلة التوريد، حد أدنى للكمية، علامة «مسعّر».

الترسية بأعمدة مش جدول خامس: awarded + purchase_order_id على سطور العرض الفايزة، وrfq_id على أمر الشراء → أثر كامل: طلب → RFQ → عرض → أمر.

لازم من أول يوم — إدخال المشتري اليدوي: في السوق ده موردين كتير بيردّوا بالتليفون مش باللينك. لازم المشتري يقدر يكتب السعر بالنيابة عن المورد في المصفوفة (مميّز «أُدخل يدويًا»). من غير ده المصفوفة هتفضل فيها فراغات والميزة تموت عمليًا.

أمان البوابة (نسخ نمط بوابة المريض — عطاء مغلق):

المصفوفة — السعر هو اللون، وكل حاجة تانية badge:

«طلب → كذا أمر» = متطلب سابق (بيتحقق مع المصفوفة مجانًا): نشيل عمود converted_to_order_id المفرد، ونعتمد على purchase_orders.purchase_request_id الموجود (hasMany) + تتبّع «الكمية المطلوبة مقابل المأمورة» على مستوى بند الطلب + حالة partially_converted. كده الطلب الواحد يطلّع كذا أمر — من المصفوفة ومن المسار اليدوي. (بونس رخيص: عند الترسية نحدّث سعر المورد في SupplierPriceList تلقائيًا.)

مراحل التوريد الذكي

مرحلة أ (أساس صغير)صغيرة

طلب → كذا أمر (تتبّع كمية بالسطر + حالة تحويل جزئي). المسار اليدوي والمصفوفة الاتنين بيحتاجوها؛ تتشحن وتتجرّب لوحدها.

مرحلة ب — الـMLPكبيرة

مستند RFQ + الدعوات + البوابة (إرسال إيميل + wa.me/نسخ لينك) + المصفوفة بالألوان + توليد أوامر Draft + إدخال المشتري اليدوي. ما ننزلش البوابة من غير المصفوفة — من غير المقارنة والترسية تبقى مجرد فورم بيعمل شغل إدخال زيادة.

مرحلة ج (تحسينات)متوسطة

تذكيرات الموعد، رفض بسبب، إيميلات ترسية/اعتذار (بلا كشف الفايز أو السعر)، مرفقات العرض، طباعة/تصدير المصفوفة (PDF عربي)، تحديث SupplierPriceList لو ما اتعملش في ب، WhatsApp API لو الحجم برّره.

مطبّات (من Fable)

٤ الفجوات (اللي ناقص للوصول للمرن)

قارنّا كل بند: الحالي مقابل المطلوب مقابل التغيير المقترح.
البندالحاليالمطلوبالتغيير المقترح
فوترة من أمر الشراء ممنوعة (oneBillPerGrn) — وحاليًا بعد إصلاح سريع بتُوجَّه لـGRN واحد فقط فاتورة تجميعية للمستلَم عبر كل إذون الاستلام (فاتورة واحدة في الآخر) شيل قفل oneBillPerGrn على createFromOrder + حارس الترحيل؛ الفوترة بالمستلَم-غير-المفوتَر؛ الـ3-way match يسقّف
فوترة من إذن الاستلام (بالدفعة) شغّالة (createFromGrn) تفضل متاحة بالتوازي مع فوترة الأمر بدون تغيير — بس تتعايش مع مسار الأمر
فاتورة شراء مباشرة (بدون أمر) ممنوعة (require_po_for_bill) مسموحة → إذن إضافة مسودة → اعتماد → مخزون مسار «فاتورة شراء مباشر» مميّز بعلامة؛ إثبات GR/IR معكوس؛ إرخاء require_po للفواتير بدون أمر
كذا إذن استلام لأمر واحد تسلسلي (single_active_grn_cycle يمنع فتح دورتين معًا) استلام على دفعات حسب الكميات تسلسلي يكفي غالبًا؛ قرار: نرخّي للسماح بدورتين نشطتين معًا؟
طلب شراء → كذا أمر مقفول 1→1canConvert()=«معتمد»، وبعد التحويل الحالة «محوَّل» + converted_to_order_id مفرد → التاني 422 أي عدد من الأوامر من طلب واحد (يدوي + من مصفوفة RFQ) شيل العمود المفرد → hasMany عبر purchase_request_id + تتبّع كمية بالسطر + حالة partially_converted (القسم ٣ب)
توريد ذكي (RFQ + مصفوفة مقارنة) مفيش خالص — بس compare() لمنتج واحد (غير مستهلَك) + نمط بوابة المريض للاستعارة بوابة تسعير للمورد بلينك يونيك + مصفوفة ملوّنة تولّد أوامر شراء وحدة جديدة (4 جداول) — التفاصيل والمراحل في القسم ٣ب
التقاط الباتش/السيريال + الصلاحية — القسم ٥ (تحت التحليل) —
عرض الأرصدة بالباتش/الصلاحية — القسم ٦ (تحت التحليل) —
ملاحظة أمان مالي: إرخاء oneBillPerGrn وrequire_po_for_bill لا يفتح باب الفوترة الزيادة — حارس الـ3-way match عند الترحيل بيسقّف الفاتورة عند الكمية المستلَمة تراكميًا (اتأكدنا: alreadyBilled + billQty ≤ received × tolerance). فالأقفال اللي هنشيلها كانت «تقييد مسار»، مش «حماية محاسبية».

٥ الباتشات والسيريالز — الموجود وأفضل نقطة التقاط

نوع التتبّع للمنتج عمود واحد: tracking_type = none / batch / serial (يتحدد من فورم المنتج).

نقاط الالتقاط الثلاثة — مين بيلتقط إيه فعلًا

النقطةالباتشالصلاحيةالسيريال (لكل وحدة)الواقع
أ) إنشاء إذن الاستلام (GRN) الباك إند فيه الأعمدة، لكن الواجهة مفيهاش فورم بنود (بنود GRN بتيجي من أمر الشراء) — مفيش إدخال هنا خالص.
ب) فحص الجودة ✓ (للسطر) بيلتقط باتش + صلاحية للسطر ويحفظهم على بنود الـGRN. مفيش سيريالات.
ج) إذن الإضافة (stock-receipts) ✓ + صلاحية/سيريال الشاشة الوحيدة فيها UI لإدخال السيريالات (ديالوج بيجمّع صلاحية لكل سيريال). بس مقفولة عن مسار الشراء في المُحكَم، والالتقاط الفعلي (persist) للسيريالات بيحصل هنا عند اعتماد أمين المخازن.

فجوتان حقيقيتان (بلوكرز) في تدفّق البيانات

بلوكر أمنتج بسيريال متبضّع بالمُحكَم = مستحيل يتعمَد
السبب
مفيش شاشة في مسار الشراء (GRN/جودة) بتلتقط سيريالات → بند الاستلام serial_numbers = null. للمنتج المتتبَّع بسيريال، اعتماد أمين المخازن بيرمي serial_count_mismatch (0 ≠ الكمية).
القفل المزدوج
إذون الاستلام المرتبطة بـGRN مقفولة للتعديل (receipt_locked_by_grn) → أمين المخازن مش قادر يدخل السيريالات كمان. طريق مسدود.
الأثر
أي منتج tracking=serial لو اشتريته عبر أمر شراء → GRN، مش هتقدر تعتمد استلامه أبدًا.
بلوكر بالصلاحية لكل سيريال بتتجمّع في الواجهة بس بتتفقد
السبب
ديالوج السيريالات في شاشة إذن الإضافة بيجمّع صلاحية لكل سيريال (rowSerialsExpiryMap)، لكن الـpayload بيبعت serial_numbers[] + صلاحية سطر واحدة بس. الباك إند بيحط صلاحية السطر على كل السيريالات.
الأثر
لو دخّلت صلاحيات مختلفة لكل وحدة → بتضيع، وكلهم بياخدوا صلاحية واحدة.

مين بيُدخِل مقابل مين بيحفظ (شراء مُحكَم اليوم)

النقطةالإدخال البشري اليومالحفظ الفعلي (product_serials / حركة)
إنشاء GRNلا شيء (بنود من الأمر)لا
فحص الجودةباتش + صلاحية (بيتحفظ على بند الـGRN)لا (مرحلي على الـGRN بس)
اعتماد أمين المخازنلا شيء جديد — الإذن مقفول للتعديلنعم — نقطة الحفظ: باتش/صلاحية → حركة؛ سيريال → product_serials. (بس السيريال null فبيتبلوك)
إذن إضافة مستقلباتش + سيريال + صلاحية/سيريال (الشاشة الوحيدة)نعم عند الاعتماد — بس مقيّدة في المُحكَم + بتفقد صلاحية/سيريال
المفارقة اللي بتحدد القرار: الشاشة الوحيدة اللي تقدر تلتقط باتش+سيريال+صلاحية-لكل-وحدة (إذن الإضافة) مفصولة عن مسار الشراء؛ ومسار الشراء بيلتقط باتش+صلاحية بس (في الجودة)؛ والحفظ الفعلي بيحصل عند اعتماد أمين المخازن اللي مالوش UI إدخال للإذون المرتبطة بـGRN (مقفولة). التوصية بأفضل نقطة تحت 👇

التوصية — نقطة التقاط واحدة: (ج) اعتماد أمين المخازن

القرار (بعد مراجعة Fable)
وحّد الالتقاط عند اعتماد أمين المخازن (إذن الإضافة) — مع بقاء فحص الجودة مصدر تعبئة مسبقة. لا نبني شاشة (أ) نهائيًا.

٦ مراجعة شاشة أرصدة المخزون (stock-balances)

الشاشة: /core/stock-balances — راجعناها بقراءة الكود (واجهة + باك إند + الجدول).

اللي بتعرضه دلوقتي

الأعمدة: كود المنتج · الاسم · المتغيّر (variant) · المخزن · الكمية · المتاح · متوسط التكلفة · القيمة الإجمالية + زرار كارت الصنف. وكروت إحصائية: عدد المنتجات، القيمة الإجمالية، تحت الحد، رصيد صفر.

مفيش أي عمود للباتش أو الصلاحية أو السيريال — ولا أي مؤشّر قرب انتهاء صلاحية. ده مش بق في الشاشة: هو انعكاس لنموذج البيانات نفسه.

ليه الصلاحية مش باينة — السبب الجذري في النموذج

جدول الأرصدة inventory_stock_balances إجمالي بحت: صف واحد لكل (منتج + متغيّر + مخزن)، بعمود كمية واحد. مفيهوش أعمدة باتش/صلاحية/سيريال أصلًا (مؤكَّد من الميجريشن والموديل والـResource). فالصلاحية مش موجودة على مستوى الرصيد — هي متسجّلة في أماكن تانية موازية:

البيانالرصيد (stock_balances)الحركات (movements)السيريالات (product_serials)بنود الإذون
رقم الباتش✓ نَسَب
تاريخ الصلاحية (نهاية)✓ نَسَب
السيريال (لكل وحدة)✓ صف/وحدة✓ JSON
تاريخ الإنتاج/البداية
رصيد لكل دفعة (كمية)✗ إجماليعبر حالة السيريال فقط
الخلاصة: المخزن مش بيدعم «رصيد بالدفعة» ولا صرف بالأقدم-صلاحية (FEFO) — الرصيد رقم إجمالي واحد، والصرف بالتكلفة (FIFO layers أو متوسط مرجّح) مش بالصلاحية. اللي موجود: (أ) تتبّع بالسيريال لكل وحدة مع باتش وصلاحية، (ب) نَسَب باتش/صلاحية على الحركات + تقرير قرب انتهاء الصلاحية جاهز من السيريالات. تاريخ الإنتاج (البداية) مش متخزّن في أي مكان — الصلاحية-النهاية بس.

الفجوة + التوصية

٧ التوصيات وخطة التنفيذ المرحلية

تسلسل يبني على بعضه — الجودة قبل الرؤية، والرؤية قبل الإنفاذ.

توصية تاريخ الإنتاج (البداية)

أضِفه — عمود معلوماتي فقط. production_date جنب expiry_date في product_serials (+ يتنسخ للحركات زي الصلاحية). حدّان: (١) مفيش أي منطق يعتمد عليه — معلوماتي بحت؛ (٢) تحقّق بسيط: الإنتاج < الانتهاء. مش هنبني «جدول دفعات رئيسي» دلوقتي (جزء من P4 المؤجّل).

توصية رؤية الصلاحية في الأرصدة

عمود مشتق + تنقيب — بدون نموذج أرصدة جديد. ٣ لمسات قراءة-فقط على شاشة الأرصدة: (١) عمود «أقرب صلاحية» — دقيق من product_serials للأصناف بسيريال، تقريبي من حركات الوارد للأصناف بتشغيلة (ويتوسم «تقريبي»). (٢) شارات لونية: أحمر=منتهي، برتقالي=قريب (عتبة أيام قابلة للضبط). (٣) تنقيب: نقرة الصف تفتح تقرير الصلاحية الموجود أصلًا مفلتَر. تنويه: دي رؤية مش إنفاذ — الصرف مش هيختار الأقرب-انتهاءً تلقائيًا (FEFO محتاج أرصدة بالدفعة = مؤجّل).

خطة المراحل

المرحلة ١ — الآنمتوسطة

الفوترة المرنة (من الأمر تجميعي + من الـGRN بالدفعة) + الفاتورة المباشرة بدون أمر (بإثبات GR/IR معكوس) + إصلاح بلوكر (ب) فقدان صلاحية السيريالات.

ملاحظة: بلوكر (ب) مش إصلاح واجهة بس — شكل الـpayload ومسار الحفظ بيقبلوا صلاحية واحدة؛ صغير بس FE+BE. لو الفقد مش مؤلم دلوقتي، ممكن تبتلعه المرحلة ٢.

المرحلة ٢ — توحيد الالتقاطكبيرة

الالتقاط الموحّد عند إذن الإضافة: الفتح الجزئي (بيصلح بلوكر (أ) الطريق المسدود جذريًا) + ديالوج الدفعات-أولًا + تعبئة مسبقة من الجودة + إضافة production_date.

لو أصناف السيريال محجوبة فعليًا دلوقتي → نقدّم الفتح الجزئي وحده كإصلاح عاجل قبل باقي المرحلة.

المرحلة ٣ — رؤية الصلاحيةصغيرة-متوسطة

عمود «أقرب صلاحية» + الشارات + التنقيب على شاشة الأرصدة. بعد المرحلة ٢ حتمًا — رؤية بلا التقاط موثوق = بيانات مضلِّلة.

المرحلة ٤ — مؤجّلةكبيرة

أرصدة لكل دفعة (per-lot balances) + صرف FEFO. مذكورة في الكود كـ«عمل مستقبلي». مبتبدأش إلا بعد إثبات جودة البيانات من المرحلتين ٢-٣.

مطبّات UX لازم ننتبه لها (من Fable)

٨ القرارات المطلوبة موافقتك

وافق على دول (أو عدّلهم) وأبدأ المرحلة ١ فورًا.
  1. الفوترة المرنة: إرخاء oneBillPerGrn → فوترة من أمر الشراء (تجميعي لكل المستلَم) و من الـGRN (بالدفعة) — الاتنين متاحين، والـ3-way match يمنع الزيادة. موصى به.
  2. الفاتورة المباشرة (بدون أمر شراء): مسار «فاتورة شراء مباشر» بعلامة → إذن إضافة مسودة → اعتماد أمين المخازن → مخزون، بإثبات GR/IR معكوس. موصى به (النسخة العملية اللي اخترتها).
  3. كذا إذن استلام لأمر واحد: التسلسلي (واحد بعد التاني) يكفي — ولا ترخّي single_active_grn_cycle علشان تفتح كذا دورة استلام معًا؟ (توصيتي: تسلسلي دلوقتي، نرخّي بس لو احتجت فعلًا)
  4. نقطة التقاط الباتش/السيريال: التوحيد عند اعتماد أمين المخازن (فتح جزئي للكمية مقفولة/الدفعات مفتوحة + ديالوج دفعات-أولًا + تعبئة مسبقة من الجودة). موصى به.
  5. تاريخ الإنتاج (البداية): نضيف production_date كعمود معلوماتي (إنتاج < انتهاء)؟ موصى به.
  6. رؤية الصلاحية في الأرصدة: عمود «أقرب صلاحية» + شارات + تنقيب لتقرير الصلاحية — رؤية مش FEFO. موصى به.
  7. الترتيب: المرحلة ١ (فوترة مرنة + فاتورة مباشرة + إصلاح فقدان صلاحية السيريال) دلوقتي → ٢ (توحيد الالتقاط + إصلاح الطريق المسدود) → ٣ (رؤية الصلاحية) → ٤ (أرصدة بالدفعة/FEFO) مؤجّل. موصى به.
  8. طلب شراء → كذا أمر (بقى مهم عندك): نعمله كـمرحلة أساس صغيرة (تتبّع كمية بالسطر + تحويل جزئي) — بيخدم المسار اليدوي والـRFQ. موصى به كأول خطوة في مسار التوريد.
  9. وحدة التوريد الذكي (RFQ): نبني البوابة + المصفوفة الملوّنة + توليد الأوامر (4 جداول، نمط بوابة المريض للينك)؟ ده مسار منفصل عن الفلو المرن — MLP = بوابة + مصفوفة + أوامر Draft + إدخال يدوي. موصى به بعد مرحلة الأساس.
  10. الرسائل: الإيميل محتاج ضبط SMTP + عمل Mailable (مش موجود دلوقتي)؛ الواتساب عبر wa.me + قالب (جاهز). نمشي بـwa.me أولًا والإيميل يتفعّل لما نضبط SMTP؟ (توصيتي: أيوة)
بلوكر عاجل ممكن نقدّمه: لو عندك دلوقتي منتج متتبَّع بسيريال محتاج تستلمه بالمُحكَم — هو محجوب تمامًا (بلوكر أ). أقدر أقدّم الفتح الجزئي وحده كإصلاح عاجل قبل باقي المرحلة ٢. قوللي لو ده حالتك.
الخطوة الجاية
وافق على القائمة (كلها أو المعدّلة) → أبدأ المرحلة ١ حالًا: الفوترة المرنة + الفاتورة المباشرة + إصلاح فقدان صلاحية السيريالات، مع اختبارات عميقة ومراجعة قبل الـpush.