المرحلة ١ — تحليل بلا كود · implement-research

إعدادات نقاط البيع الكاملة + تبويب الدواء في المنتجات

الطلب: «اعمل ليستة بالإعدادات بشرح صغير تحت كل واحد، وبعدين ننفّذ إعداد إعداد» + «نضيف للمنتجات نوع اسمه دواء، ولو اخترناه يفتح تبويب فيه المواد الفعّالة والشكل الدوائي والمكوّنات». التدقيق طلّع اكتشاف بيقلب ترتيب الشغل: آلة الإعدادات العامّة موجودة وممتازة وشغّالة على ٧ تبويبات — وتبويب نقاط البيع هو الوحيد المكتوب بالإيد. وكل إعدادات الماكينة السبعة الحالية مالهاش أي تنفيذ على السيرفر.

إعدادات في الليستة: ٤٦ موجودة ومعطّلة: ٩ مالي: [FIN] ١ أغسطس ٢٠٢٦ · moonui2 · hazemdev2 الحالة: مستنّي موافقتك

٠ تصحيحات على تقرير أمس

التدقيق الجديد صحّح تلات حاجات في pos-pharmacy-fitness-analysis.html. واحدة لصالحنا واتنين ضدنا:

اللي قلته أمسالصحالأثر
nphies:poll مالوش مدخل في الجدولة مجدول فعلًا كل ٥ دقايق — NPHIESServiceProvider.php:55-61 لصالحنا. متابعة المطالبات مش يدوية.
tracking_type/variant_type حيّة في القاعدة ومالهاش migration — أصلها مجهول ليهم migrations، بس في database/migrations/ الجذر مش تحت Modules/* لصالحنا — وده كمان بيدّينا النمط المعتمد لإضافة عمود على جدول تملكه وحدة تانية.
«الـQR بتاع زاتكا متولّد ومتخزّن على السيرفر خلاص» مشروط ومضلّل. الفوترة الإلكترونية مقفولة افتراضيًا ومفيش شركة مفعّلاها هنا ⇒ ما بيتعملش مستند ⇒ مفيش QR. وكمان التوقيع نفسه stub (بيتحقن كتعليق XML)، وأعمدة العنوان الوطني السعودي (رقم المبنى، الحي، القطعة، المنطقة) مش موجودة في جدول الشركات. ضدنا. الإيصال السعودي المطابق متوقّف على الـschema مش على القالب — أوسع مما قدّرت.

وكمان اتأكد من القاعدة: نفيس ما اتصلش ولا مرة — ١٢ معاملة كلهم timeout، التفعيل is_active=0، والشهادات مش مرفوعة. الموديول شكله مكتمل واتصاله غير مُثبَت.

١ الاكتشاف الحاكم — الآلة موجودة، وتبويب البيع بس برّاها

آلة الإعدادات العامّة أحسن مما توقّعت

فيه جدولين: setting_definitions (التعريف) وsettings (قيمة الشركة). والتعريف نفسه بيحمل:

والشاشة العامّة بتقرا التعريف وترسم نفسها: boolean ⇒ مفتاح، enum ⇒ قائمة، integer ⇒ رقم. وشغّالة كده على ٧ تبويبات: المحاسبة · المخزون · المنتجات · الموارد البشرية · المبيعات · المشتريات · المعمل.

وتبويب نقاط البيع هو الوحيد المكتوب بالإيد

تبويب الـPOS في شاشة الإعدادات مش بيقرا التعريفات إطلاقًا — هو تلات قوائم منسدلة مكتوبة حرفيًا في الـHTML وزرار حفظ. مفيش حلقة، مفيش تصنيف حسب النوع، ومفيش مكان أصلًا للشرح.

يعني: لو بدأنا نضيف إعدادات دلوقتي، كل إعداد هيتكتب بالإيد في ٤ أماكن ومن غير شرح. ولو حوّلنا التبويب للنمط العام الأول — كل إعداد بعدها بيبقى سطر واحد في seeder + قارئ واحد، وبيرسم نفسه بلغتين مع شرحه في مجموعته الصح.

وده سبب إن أول حزمة في الخطة مش إعداد — هي تحويل التبويب. بتتعمل مرة واحدة، وبعدها الـ٤٦ إعداد بيبقوا رخاص.

تكلفة إعداد شركة بعد التحويل
2 ملف
تكلفة إعداد ماكينة (دلوقتي وبعدين)
9 ملفات
إعدادات ماكينة بتنفيذ سيرفر
0 من ٧
تبويبات على النمط العام
7

٢ الآليتين — والقاعدة اللي بتحدد بيت كل إعداد

إعداد على مستوى الشركةإعداد على مستوى الماكينة
التخزينصف في settings + تعريف في setting_definitionsمفتاح جوّه عمود JSON واحد على pos_terminals.settings
يحمل شرحًا؟أيوه — عمودين عربي/إنجليزيلأ — مفيش خانة شرح إطلاقًا
بلغتين؟أيوه، من قاعدة البياناتلأ — محتاج مفتاح ترجمة يدوي
الرسم في الشاشةتلقائي من النوع (بعد التحويل)يدوي في ٤ مواضع
الصلاحيةمجانية — pos.settings.manage متعرّفة ومزروعة خلاصصلاحية تعديل الماكينة
الوصول للعملاء الحاليينتلقائي — سيدرات الإعدادات مسجّلة في قائمة التحديث، وفيه migration جسر بيشغّلهمتلقائي (JSON بيندمج مع الافتراضي)
مين يقدر يقراه؟أي كود معاه company_id — يعني كل الموديولاتكود نقاط البيع بس

القاعدة الحاكمة — مستخرجة من شجرة الاستدعاء الفعلية

بيعة الـPOS بتمشي كده:

POSSaleController::store() → createSale() → PostSalesInvoice  (Modules/Sales)
                                          → StockService::decreaseStock  (Modules/Inventory)
                                          → LotAllocationService::allocate  (Modules/Inventory)

الكونترولر بتاع الـPOS معاه الجلسة ⇒ معاه الماكينة والفرع ⇒ يقدر يقرا إعداد ماكينة.
لكن PostSalesInvoice وStockService وLotAllocationService وValidatesMinimumSalePrice بياخدوا company_id بسومستحيل يقروا إعداد ماكينة، إلا لو غيّرنا توقيع دوال مشتركة بيستخدمها المخزون والمشتريات والإنتاج والمبيعات والمتجر.

فالقاعدة: أي سلوك بيتقرّر جوّه المبيعات أو المخزون أو الكورإعداد شركة إجباريًا. وبس اللي بيتقرّر جوّه كونترولرات نقاط البيع نفسها ينفع يبقى إعداد ماكينة.

ومثال عملي على الخطأ ده: السعر الجبري. لو حطّيناه على الماكينة، مسار /sales/invoices مش هيشوفه ⇒ الماكينة والشاشة المكتبية هيدّوا إجابتين مختلفتين لنفس المنتج. وده حرفيًا سبب إن خصم السطر اتساب مفتوح في المراجعة الأخيرة.

٣ أربع فخاخ لازم الخطة تتفاداهم

الفخ ١ — كل إعدادات الماكينة السبعة مالهاش تنفيذ على السيرفر أمني

مش allow_price_override بس زي ما قلت أمس — السبعة كلهم:

الإعدادالواقع على السيرفر
allow_negative_stockميت + تضارب أسماء. الحارس الوحيد بيقرا عمود allow_negative_stock على المخزن، مش على الماكينة. المخزن بيكسب دايمًا.
require_customerالسيرفر أشد: customer_id مطلوب دايمًا مهما كان المفتاح. وفيه تلات قيم افتراضية متضاربة للمفتاح ده في تلات ملفات.
default_tax_rateصفر قراءة — الضريبة بتيجي على السطر.
receipt_header / footerصفر قراءة — الإيصال بيترسم في المتصفح.
allow_discountصفر قراءة — الخصم بيتطبّق بلا أي فحص. مذكور في التعليقات بس.
allow_price_overrideصفر قراءة.
show_unitمفتاح تامن مش معرّف أصلًا — موجود في الواجهة بس، مش في الافتراضيات ولا في التحقق. بيعيش بالصدفة.

الخلاصة: شاشة إعدادات الماكينة الحالية ديكور بالكامل من ناحية الحماية. أي عميل بيبعت طلب مباشر للـAPI بيتخطّاها كلها. لازم تتصلّح مع أول حزمة — مش بعدين.

الفخ ٢ — الكاشير ما يقدرش يقرا إعدادات الشركة أخطر بند

شاشة الـPOS بتقرا إعدادات الشركة مفتاح مفتاح من GET /core/settings/{key}. والمدخل ده بيطلب صلاحية core.settings.view، إلا للمفاتيح المدرَجة في قائمة قراءة عامّة — ومفيش ولا مفتاح pos.* في القائمة دي.

يعني: الكاشير اللي معاه صلاحيات نقاط البيع بس بياخد ٤٠٣، والواجهة بتبلع الخطأ في فرع مكتوب أصلًا لحالة الأوفلاين، وبترجع للقيمة الافتراضية في صمت.

النتيجة العملية اللي لازم نتجنّبها: المدير يفتح الشاشة ويفعّل «امنع بيع المنتهي»، يجرّبه بنفسه فيشتغل — وعند كل كاشير يبقى مقفول. ودي أسوأ أنواع الأعطال: بتبان مظبوطة لمين بيختبرها.

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

الفخ ٣ — إعداد بيشحن قبل قارئه = إعداد ميت

عندنا مثال حيّ: clinic.pharmacy_source (منتجات مقابل فورميلاري مستقل) — معرّف بالكامل بلغتين، وظاهر في الشاشة، وليه صفر قراءة في الريبو كله. والوصف بتاعه نفسه بيقول «الآثار هتيجي في م٧». والشاشة بتكتب جنبه «قريبًا — غير مفعّل».

القاعدة اللي هلتزم بيها: ما نعرّفش إعداد إلا في نفس الحزمة اللي بتكتب أول قارئ ليه. الليستة تحت بتقول «إيه اللي هيتعمل»، مش «إيه اللي هيتزرع».

الفخ ٤ — مفتاح واحد لكل مفهوم

حاجتين موجودتين خلاص وما ينفعش نعمل نسخة تانية منهم:

٤ ليستة الإعدادات — ٤٦ إعدادًا بشرح كل واحد

الحالة: موجود وشغّال · موجود ومعطّل (متخزّن ومحدش بيقراه) · جديد · البيت: شركة ماكينة · التنفيذ: سيرفر واجهة

مجموعة ١ — الصلاحية والتشغيلات [FIN]

الإعدادالنوعالافتراضيالبيتالحالةالشرح
تفعيل نظام التشغيلات في نقاط البيعpos.enforce_batch_selectionمفتاحمقفولشركةجديد لما يكون مفعّلًا، أي دواء متتبَّع بتشغيلة لازم يتحدد لوطه عند البيع (تلقائيًا بالأقرب انتهاءً أو يدويًا). صيدليات كتير مش بتشتغل بتشغيلات — والمفتاح ده بيخلّي الشاشة تفضل بسيطة عندهم.
منع بيع الدواء المنتهيpos.block_expired_saleقائمة: امنع/حذّر/اسمححذّرشركةجديد يرفض إتمام البيعة لو الكمية مش مغطّاة بلوطات سارية. ⚠ ده مش نفس inventory.expired_issue_policy الموجود — ده حارس رفض على حدود نقاط البيع، والموجود مجرد مُصفٍّ في الاختيار التلقائي.
سياسة الصرف المنتهي (مخزن)inventory.expired_issue_policyقائمةامنعشركةموجود سيرفر بيحكم استبعاد اللوطات المنتهية من التخصيص التلقائي في كل الحركات. موجود وبيشتغل — بس بيستبعد مش بيرفض.
ترتيب الصرفinventory.issuing_orderقائمة: الأقرب انتهاءً/الأقدمالأقرب انتهاءًشركةموجود سيرفر يحدد اللوط اللي يتصرف أولًا. للصيدلية الأقرب انتهاءً هو الصح دايمًا.
مدة تحذير قرب الانتهاءinventory.near_expiry_daysرقم (أيام)٣٠شركةموجود الدواء اللي بينتهي خلال المدة دي يتعلّم بشارة صفراء. الشاشة تعرضه بالشهور وتحوّل لأيام — مفتاح واحد بوحدة واحدة.
إلزام التشغيلة والصلاحية عند الاستلامinventory.require_batch_on_receiptمفتاحمفتوحشركةجديد يمنع اعتماد استلام لصنف متتبَّع بتشغيلة من غير تشغيلة وتاريخ صلاحية. دي نقطة الدخول — لو الصلاحية ما اتسجّلتش هنا، مفيش أي شاشة تانية تقدر تعرفها بعدين.
عرض التشغيلة والصلاحية على سطر الفاتورةpos.show_batch_on_lineمفتاحمفتوحماكينةجديد إظهار رقم التشغيلة وتاريخ الانتهاء تحت اسم الصنف. عرض بحت — عشان كده ماكينة.
السماح بتجاوز المنتهي بصلاحيةinventory.allow_expired_overrideمفتاحمقفولشركةموجود ومعطّل يسمح لمستخدم معاه صلاحية خاصة بصرف لوط منتهي مع تسجيل التجاوز. ⚠ الصلاحية inventory.issues.expired-override مزروعة، وتنفيذها في الواجهة بس — السيرفر ما بيفحصهاش.

مجموعة ٢ — وحدة الصرف والتسعير [FIN]

الإعدادالنوعالافتراضيالبيتالحالةالشرح
السماح بتغيير وحدة البيعpos.allow_unit_switchمفتاحمفتوحماكينةجديد يظهر قائمة الوحدة على السطر (علبة/شريط/قرص) والسعر يتبع تلقائيًا. الحساب موجود وشغّال بالكامل في السيرفر — ده مفتاح إظهار بس.
إظهار الوحدةpos.show_unitمفتاحمفتوحماكينةغير معرّف إظهار اختصار الوحدة على السطر. ⚠ شغّال حاليًا بالصدفة — مش في الافتراضيات ولا في التحقق. لازم يتقنّن.
فرض السعر الجبريsales.enforce_fixed_priceمفتاحمقفولشركةجديد الأصناف المعلَّمة «سعر مقفول» تتباع بسعرها المطبوع بالظبط — لا زيادة ولا نقصان، ولا خصم سطر ولا خصم فاتورة. ⚠ لازم شركة: الفحص في trait مشترك بتستخدمه فواتير المبيعات كمان.
السماح بالبيع تحت الحد الأدنىsales.allow_below_min_priceمفتاحمقفولشركةموجود سيرفر يعطّل أرضية السعر بالكامل. للصيدلية لازم يفضل مقفول.
السماح بتعديل السعر على الماكينةallow_price_overrideمفتاحمقفولماكينة ← شركةمعطّل واجهة يسمح للكاشير بتعديل سعر السطر. صفر تنفيذ على السيرفر. لازم يتنفّذ سيرفر-سايد، والأنضف نقله لمستوى الشركة عشان مسار المبيعات المكتبي يشوفه.
أقصى خصم على السطرpos.max_line_discount_percentرقم عشري١٠٠شركةجديد سقف نسبة الخصم على سطر واحد. حاليًا مفيش سقف غير ١٠٠٪ — ومتروك معروف إن خصم السطر لسه يقدر ينزل تحت أرضية السعر.
أقصى خصم على الفاتورةpos.max_header_discount_percentرقم عشري١٠٠شركةجديد سقف خصم إجمالي الفاتورة. حاليًا بلا أي سقف — أي رقم موجب مقبول.
السماح بالخصم أصلًاallow_discountمفتاحمفتوحماكينةمعطّل واجهة يخفي أدوات الخصم. ⚠ صفر تنفيذ سيرفر — مذكور في التعليقات بس.

مجموعة ٣ — الروشتة والصرف

الإعدادالنوعالافتراضيالبيتالحالةالشرح
تفعيل نظام صرف الروشتةpharmacy.dispensing_enabledمفتاحمقفولشركةجديد يفعّل ربط البيعة بروشتة: بحث بالروشتة، صرف كامل أو جزئي، وتحديث حالتها. ده مش مفتاح على سلوك موجود — ده مفتاح على ميزة لسه هتتبني. ما يتعرّفش غير مع أول قارئ.
مصدر الأدويةclinic.pharmacy_sourceقائمةمنتجاتشركةمعطّل الأدوية من سجل الأصناف ولا من فورميلاري مستقل. ⚠ معرّف بلغتين وليه صفر قراءة في الريبو. والشاشة نفسها بتكتب «قريبًا».
السماح بالصرف الجزئيpharmacy.allow_partial_dispenseمفتاحمفتوحشركةجديد المريض ياخد جزء من الروشتة والباقي يفضل محجوز عليها. الأوردر المعلَّق الموجود هو الآلية الطبيعية لده.
صلاحية الروشتة (أيام)pharmacy.prescription_validity_daysرقم٣٠شركةجديد المدة اللي بعدها الروشتة ما تتصرفش. مفيش نافذة صلاحية على الروشتة النهارده إطلاقًا.
إلزام روشتة للأدوية الموصوفةpharmacy.require_prescription_rxمفتاحمقفولشركةجديد الأصناف المعلَّمة «تصرف بروشتة» ما تتباعش من غير روشتة. متوقّف على عمود Rx/OTC في المنتج (قسم ٦).
إلزام روشتة للمقيَّدةpharmacy.require_prescription_controlledمفتاحمفتوحشركةجديد الأدوية المخدِّرة/المقيَّدة تتطلّب روشتة وبيانات طبيب إجباريًا. متوقّف على علم «مقيَّد» في المنتج.
منع صرف المقيَّدة أوفلاينpharmacy.controlled_offline_blockedمفتاحمفتوحشركةجديد يمنع بيع الأدوية المقيَّدة والماكينة مقطوعة عن السيرفر. سجل مخدِّرات بيتزامن بعدين مش سجل. توصيتي ما يبقاش قابل للإطفاء أصلًا.

مجموعة ٤ — المريض والتأمين [FIN]

الإعدادالنوعالافتراضيالبيتالحالةالشرح
تفعيل وضع المريضpos.enable_patient_modeمفتاحمقفولشركةجديد منتقي العميل يبحث في المرضى كمان ويعرض كود المريض والعمر. الجسر بين المريض والعميل موجود في القاعدة خلاص — ٤١ مريض بس من ١١٬٧٩٧ معبّى، فمحتاج تعبئة رجعية.
تفعيل تقسيم التأمينpos.enable_insurance_splitمفتاحمقفولشركةجديد الفاتورة تتقسم: تحمّل الجهة الدافعة مقابل تحمّل المريض. ⚠ إضافة وسيلة دفع «تأمين» تعديل كود في enum مش إعداد — الإعداد بيفعّل التقسيم بس.
حساب تحصيل الجهة الدافعةpos.payer_receiving_account_idرقم (حساب)شركةجديد الحساب اللي يترحّل عليه نصيب التأمين كمدينية. على نمط حسابات التحصيل الموجودة للكاش والشبكة.
تفعيل نفيسnphies.enabledمفتاحمقفولشركةموجود سيرفر يحوّل مسار المطالبات من يدوي لنفيس. ده أنضف نموذج ربط عندنا — بيبدّل المحوّل في مزوّد الخدمة حسب الإعداد. هنقلّده. ⚠ بس نفيس ما اتصلش ولا مرة (١٢/١٢ timeout).
فحص الأهلية قبل الصرفpos.require_eligibility_checkمفتاحمقفولشركةجديد يمنع إتمام بيعة تأمينية قبل التحقق من أهلية المريض. مدخل الأهلية موجود وشغّال — محتاج معرّف المريض بس.

مجموعة ٥ — المرتجع الدوائي [FIN]

الإعدادالنوعالافتراضيالبيتالحالةالشرح
سياسة إرجاع الدواء للمخزونpos.refund_stock_policyقائمة: يرجع/يتحجر/اسأليتحجرشركةجديد الدوا الراجع يدخل المخزون المتاح تاني، ولا يروح مخزن حجر. ⚠ النهارده بيرجع دايمًا وبلا شرط وبلا تشغيلة — وده أكبر مصدر انحراف قِسناه. لازم شركة عشان مسار المرتجعات المكتبي بياخد نفس القرار.
مخزن الحجرpos.quarantine_warehouse_idرقم (مخزن)شركةجديد وجهة الدوا المرتجع لما تكون السياسة «يتحجر». مخزن منفصل بيخلّي المتاح للبيع صح تلقائيًا.
مدة قبول المرتجع (أيام)pos.refund_window_daysرقم٠ (بلا حد)شركةجديد أقصى مدة بعد البيع يتقبل فيها المرتجع. النهارده مفيش أي نافذة زمنية — فاتورة من سنة تترجع عادي.
المرتجع يتطلّب مشرفًاpos.refund_requires_supervisorمفتاحمقفولشركةجديد موافقة إضافية قبل صرف الفلوس من الدرج. النهارده المرتجع بيعتمد نفسه بنفسه.
منع مرتجع المقيَّدةpharmacy.block_refund_controlledمفتاحمفتوحشركةجديد الأدوية المخدِّرة ما تترجعش إطلاقًا. قاعدة تنظيمية في أغلب الأسواق.
المرتجع بنفس وسيلة الدفعpos.refund_to_original_tenderمفتاحمفتوحشركةجديد يرجع بنفس وسيلة الدفع الأصلية بدل الكاش دايمًا. النهارده الافتراضي كاش مهما كان الأصل.

مجموعة ٦ — هوية الصيدلي والصلاحيات

الإعدادالنوعالافتراضيالبيتالحالةالشرح
إلزام هوية الصيدلي على البيعةpharmacy.require_pharmacistمفتاحمقفولشركةجديد تسجيل «صُرف بمعرفة» على كل بيعة دواء وطباعته على الإيصال. مفيش عمود ترخيص على المستخدمين — أرخص نسخة: صلاحية صرف + مستخدم الجلسة.
فصل صلاحية البيع على الماكينة (صلاحية مش إعداد)صلاحيةشركةجديد أمني مدخل بيعة نقاط البيع محروس النهارده بصلاحية إنشاء فاتورة مبيعات. يعني أي حد يقدر يعمل فاتورة مكتبية يقدر يرحّل بيعة على الماكينة. لازم صلاحية مستقلة — ودي مش خاصة بالصيدلية، مفتوحة دلوقتي.

مجموعة ٧ — الإيصال

الإعدادالنوعالافتراضيالبيتالحالةالشرح
عرض التشغيلة والصلاحية على الإيصالpos.receipt_show_batchمفتاحمفتوحشركةجديد طباعة رقم التشغيلة وتاريخ الانتهاء تحت كل صنف. مطلب تتبّع تنظيمي.
عرض الوحدة المصروفةpos.receipt_show_unitمفتاحمفتوحشركةجديد طباعة «٢ علبة» بدل «٢». ⚠ الكود الحالي بيرمي الوحدة تمامًا وهو بيجهّز أسطر الإيصال.
طباعة رمز QR الضريبيpos.receipt_show_qrمفتاحمقفولشركةجديد طباعة رمز الاستجابة السريعة والرقم الضريبي. ⚠ متوقّف على تفعيل الفوترة الإلكترونية و على إصلاح التوقيع وأعمدة العنوان الوطني.
عرض بيانات الصيدليpos.receipt_show_pharmacistمفتاحمقفولشركةجديد طباعة اسم ورقم ترخيص الصيدلي الصارف.
عرض بيانات الروشتة والطبيبpos.receipt_show_prescriptionمفتاحمقفولشركةجديد طباعة رقم الروشتة واسم الطبيب.
عرض تقسيم التأمينpos.receipt_show_insuranceمفتاحمفتوحشركةجديد طباعة نصيب الجهة ونصيب المريض.
عرض الاسم العلميpos.receipt_show_genericمفتاحمقفولشركةجديد طباعة المادة الفعّالة جنب الاسم التجاري.
عرض الجرعة والتعليماتpos.receipt_show_dosageمفتاحمقفولشركةجديد طباعة «قرص كل ٨ ساعات» من الروشتة.
عرض الطباعة التلقائيةpos.auto_print_receiptقائمة: دايمًا/اسأل/أبدًادايمًاماكينةجديد هل يطبع الإيصال تلقائيًا بعد كل بيعة. النهارده بيطبع دايمًا بلا اختيار.
عدد النسخpos.receipt_copiesرقم١ماكينةجديد نسخة للعميل ونسخة للصيدلية.
عرض الورقpos.receipt_paper_widthقائمة: ٥٨/٨٠ مم٨٠ ممماكينةجديد عرض الطابعة الحرارية. النهارده ٨٠ مم مثبَّتة في الكود، و٥٨ مم شائعة.

مجموعة ٨ — التشغيل والدرج والأوفلاين [FIN]

الإعدادالنوعالافتراضيالبيتالحالةالشرح
فئات النقودpos.cash_denominationsقائمة أرقام٥٠/١٠٠/٢٠٠/٥٠٠شركةجديد أزرار الفئات السريعة في نافذة الدفع. ⚠ النهارده مثبّتة في ملفين مختلفين ومربوطة بالجنيه — تكسر مع أي عملة تانية.
سماحية تقريب الدفعpos.payment_rounding_toleranceرقم عشري٠٫٠١شركةجديد الفرق المسموح بين المدفوع والمستحق. مثبّت لعملة بخانتين — الدينار الكويتي والبحريني بتلاتة.
إقفال الوردية مع وجود مبيعات غير مزامَنةpos.allow_close_with_pending_syncمفتاحمقفولشركةجديد هل يُسمح بإقفال الوردية والطابور فيه بيعات. المنع هو الصح — إقفال فوقها بيحبس الفلوس للأبد.
حد محاولات المزامنةpos.offline_max_retriesرقم٥شركةجديد عدد المحاولات قبل ما البيعة تتحجز للمراجعة.
مدة الأوردر المعلَّق (ساعات)pos.held_order_ttl_hoursرقم٠ (بلا حد)شركةجديد الأوردرات المعلَّقة تنتهي تلقائيًا بعد المدة دي. النهارده بتفضل للأبد.
السماح بالبيع الآجلpos.allow_on_account_salesمفتاحمفتوحشركةجديد البيع على الحساب من الماكينة. صيدليات كتير ما بتديش آجل من العدّاد.
الزبون النقدي يفي بشرط العميلpos.walkin_counts_as_customerمفتاحمقفولشركةجديد هل «زبون نقدي» يعتبر عميلًا لما يكون تحديد العميل مطلوبًا.
إلزام تحديد العميلrequire_customerمفتاحمقفولماكينةمعطّل واجهة يمنع البيعة من غير عميل محدد. ⚠ السيرفر أشد من الإعداد — العميل مطلوب دايمًا. وفيه تلات قيم افتراضية متضاربة في تلات ملفات.
السماح بالرصيد السالبallow_negative_stockمفتاحمقفولماكينةمعطّل البيع بأكتر من الرصيد. ⚠ الحارس الحقيقي بيقرا العمود اللي على المخزنالمخزن بيكسب دايمًا وإعداد الماكينة ديكور.
تاريخ البيعة الأوفلاينpos.offline_sale_dates_at_captureمفتاحمقفولشركةجديد البيعة المزامَنة تتسجّل بتاريخ وقت البيع مش وقت المزامنة. بيعة اتعملت ١١:٥٠ مساءً وزامنت ١٢:٠٥ بتقع النهارده في اليوم الغلط.

مجموعة ٩ — الربط الحكومي (السعودية) محتاج توثيق مورّد

الإعدادالنوعالافتراضيالبيتالحالةالشرح
مزوّد الروشتة الإلكترونيةpharmacy.eprescription_providerقائمة: بدون/…بدونشركةجديد يحدد منصة الروشتة الإلكترونية المتصلين بيها. صفر كود عندنا لوصفتي — لا مفتاح ولا stub ولا TODO. والمفتاح ده ما يتعرّفش قبل ما يبقى فيه محوّل حقيقي وراه.
التحقق من الهويةpharmacy.identity_verificationقائمة: بدون/…بدونشركةجديد التحقق من هوية المريض عند العدّاد. ⚠ صفر كود لياقين. بس الأصل موجود: رقم الهوية/الإقامة مُصنَّف ومعبّى لـ٩٩٫٩٪ من المرضى، وفيه enum بيعرف الخمس أنواع السعودية.
تفعيل الفوترة الإلكترونيةeinvoicing.enabledمفتاحمقفولشركةموجود سيرفر يفعّل توليد وإرسال الفاتورة الإلكترونية. التوقيع stub (تعليق XML مش توقيع)، وأعمدة العنوان الوطني السعودي مش موجودة في جدول الشركات. مفعّلش ده على عميل سعودي قبل ما يتصلّح.
إجمالي الإعدادات
46
موجود وشغّال
6
موجود ومعطّل
9
جديد
31
على مستوى الماكينة
9

٥ معاينة شاشة الإعدادات — قبل / بعد

قبل — تبويب نقاط البيع الحالي: تلات قوائم مكتوبة بالإيد، بلا شرح
محاسبةمخزوننقاط البيع
زبون نقدي ▾
المخزن الرئيسي ▾
الصندوق ▾
حفظ
بعد — النمط العام: مجموعات + شرح تحت كل إعداد، بيترسم لوحده
محاسبةمخزوننقاط البيع
الصلاحية والتشغيلات
تفعيل نظام التشغيلات أي دواء متتبَّع بتشغيلة لازم يتحدد لوطه عند البيع. صيدليات كتير مش بتشتغل بتشغيلات.
منع بيع الدواء المنتهي يرفض إتمام البيعة لو الكمية مش مغطّاة بلوطات سارية.
حذّر ▾
مدة تحذير قرب الانتهاء الدواء اللي بينتهي خلال المدة دي يتعلّم بشارة صفراء.
٣ شهور
المرتجع الدوائي
سياسة إرجاع الدواء للمخزون الدوا الراجع يدخل المتاح تاني، ولا يروح مخزن حجر.
يتحجر ▾
منع مرتجع الأدوية المقيَّدة الأدوية المخدِّرة ما تترجعش إطلاقًا.

الشرح اللي تحت كل إعداد مش نص في الشاشة — هو عمود في قاعدة البيانات بيتحمّل بلغة المستخدم. يعني إعداد جديد بيجيب معاه اسمه وشرحه بالعربي والإنجليزي من غير أي شغل واجهة.

٦ تبويب الدواء في المنتجات

«نوع = دواء» هيكسر تلات شاشات يوم ما ينزل

عمود النوع في المنتجات فيه قيمتين بس: منتج وخدمة. وفيه تلات استعلامات حيّة بتفلتر «منتج» حرفيًا:

المكانالأثر لو بقى «دواء» نوعًا تالتًا
فهرس منتجات نقاط البيعكل الأدوية تختفي من شاشة البيع
بحث نقاط البيع + الباركودالأدوية ما تتلاقاش بالبحث ولا بالمسح
جرد المخزونالأدوية تتستثنى من الجرد

والسبب: النوع عندنا مش تصنيف — هو ملموس مقابل غير ملموس. التصنيف الحقيقي في شجرة التصنيفات.

البديل: علم «دواء» + جدول تفاصيل ١:١ — وفيه سابقة حرفية

وحدة الإنتاج عملت بالظبط كده: بدل ما تحط ١١ عمود بتاعها على جدول المنتجات، عملت جدول إعدادات جانبي مربوط ١:١ بالمنتج. نقلّده حرفيًا.

products.is_drug  (علم واحد — هو اللي بيفتح التبويب)
   │
   ├── product_drug_details  (١:١ ، فريد على الشركة+المنتج)
   │      ├── الشكل الدوائي     → FK لجدول أشكال دوائية
   │      ├── التركيز + وحدته
   │      ├── يُصرف بروشتة؟ (Rx/OTC)
   │      ├── مقيَّد/مخدِّر؟
   │      ├── حرارة التخزين
   │      └── التصنيف العلاجي
   │
   └── product_active_ingredient  (جدول ربط — دواء ↔ عدة مواد فعّالة)
          └── التركيز لكل مادة (لو الدوا مركّب)
الخيارخطر الانحدارالحكم
نوع «دواء» جديدعالي — ٣ شاشات تكسر فورًامرفوض
علم + جدول ١:١صفر — الدواء يفضل منتجًا عاديًا في كل الشاشاتالتوصية
الأعمدة على جدول المنتجاتمتوسطمرفوض — الجدول ٣٩ عمودًا خلاص، وكل الموديولات هتحمل التكلفة

سابقتان تحذيريتان — مش نظرية

فيه في المشروع جدولين lookup اتبنوا بالكامل في الباك-إند وماحدش عمللهم شاشة: العلامات التجارية، والشركات المصنِّعة. وكمان manufacturer_id وshelf_life_days موجودين على المنتج وغايبين عن الـResource وعن نموذجَي الإضافة والتعديل في الكور — يعني موجودين ومش قابلين للتحرير.

الدرس المطبَّق في الخطة: المادة الفعّالة والشكل الدوائي يتحطّوا في الكور، ويتسجّلوا في مسارات الكور، ويتضافوا لـResource المنتج ولنموذجَي الإضافة والتعديل في نفس الحزمة. وإلا نبقى عملنا اليتيم التالت.

٧ موديول المادة الفعّالة

طلبك: «المادة الفعّالة عايزين نعمل ليها موديول كامل — إضافة وتعديل وحذف، وكل دوا يختار مادة أو أكتر». النمط موجود بالكامل، وهنسخه من أنضف مثالين.

الطبقةهننسخ منليه
الباك-إند (الجدول والموديل والكونترولر)العلامات التجاريةأعمدته مطابقة ١:١ للمطلوب (كود، اسم عربي/إنجليزي، وصف، مفعّل)، وكونترولره فيه بحث وفلترة وعدّ مرتبط واختبار جاهز.
الفرونت-إند (الشاشة والخدمة والحالة)مجموعات الوحداتأبسط شاشة CRUD كاملة عندنا: قائمة + نافذة + خدمة + إدارة حالة.
الربط متعدد لمتعدد + الاختيار المتعددالحساب البنكي ↔ الفروعاختيار متعدد مربوط بنموذج تفاعلي — نفس حالتنا بالظبط. والتحقق بيتحقق من ملكية الشركة.
تركيز لكل مادة (دوا مركّب)عروض المنتجسابقة حفظ بيانات إضافية على جدول الربط نفسه.

الحجم الحقيقي

ملفات جديدة
16
ملفات تتعدّل
7
سطور تقريبًا
~950
صلاحيات جديدة محتاجة
0 (بالنمط)

ونفس الـ١٦ ملف بالظبط بتتكرر لـالشكل الدوائي بتغيير النصوص بس — عشان كده الاتنين في حزمة واحدة.

الشكل الدوائي: enum في الكود ولا جدول؟

توصيتي: جدول — قايمة الأشكال مش مقفولة بين الأسواق، والـenum معناه إصدار برنامج جديد كل ما عميل يحتاج «فوّار» أو «لبوس». وبما إن هيكل المادة الفعّالة هيتبني على أي حال، التاني بيبقى شبه مجاني. وكمان الجدول بيدّي اسمًا عربيًا لكل شركة — وده الـenum ما يقدرش يعمله.

٨ المكوّنات والوحدات — الفخ الصامت

الخبر الحلو: «كل وحدة ليها سعر» مبني بالكامل — والناقص الشاشة بس

جدول وحدات المنتج فيه خلاص: معامل تحويل · باركود لكل وحدة · سعر بيع · سعر شراء · تُباع؟ · تُشترى؟ والـAPI بيقبلهم كلهم.

والشاشة بتعرض السعرين كنص عادي مش كحقول إدخال، والباركود وخانات «تُباع/تُشترى» مش ظاهرين إطلاقًا — بحث في كل ملفات الواجهة رجّع صفر.

وأطرف تفصيلة: دالة تعديل الوحدة مكتوبة في خدمة الواجهة ومحدش بيناديها. الكود مستنّي شاشة اتكتبتش.

ودي إصلاح في الطبقة المشتركة — بيفيد كل المنتجات مش الأدوية بس.

الخبر الوحش: التداخل مش موجود، والغلط بيعدّي في صمت خطر

معامل التحويل دايمًا منسوب للوحدة الأساسية، مش للوحدة الأب. مفيش عمود «الوحدة الأب» ومفيش تتبّع سلسلة.

الإدخال الصح

الوحدةالمعاملالمعنى
قرص (أساسية)١
شريط١٠١٠ أقراص
علبة١٠٠١٠ شرايط × ١٠

علبتين ⇒ ٢٠٠ قرص. صح.

الإدخال الطبيعي (والغلط)

الوحدةالمعاملالمعنى المقصود
قرص (أساسية)١
شريط١٠١٠ أقراص
علبة١٠«١٠ شرايط»

علبتين ⇒ ٢٠ قرص بدل ٢٠٠. غلط ١٠ أضعاف — ومفيش أي رسالة خطأ.

والأسوأ: خدمة التحويل مصمَّمة إنها ما ترميش استثناء — أي إعداد غلط بيرجع ١ عشان «ما تكسرش حركة مخزون». والتحقق عند الإدخال كان مؤجَّلًا لحزمة ما اتكتبتش، ونموذجا إضافة وتعديل الوحدة ما بيفحصوش المعامل إطلاقًا.

الحل مش تغيير المحرك — المحرك ده بيمشي عليه كل حركة مخزون وبيع وشراء وتقييم في النظام، ولمسه بتغيّر معنى كل صف موجود. الحل حقل في الشاشة: «العلبة تحتوي على ⟨١٠⟩ ⟨شريط⟩» والشاشة تضرب لوحدها وتخزّن ١٠٠، وتعرض الناتج قدام المستخدم + قاعدة تحقق ترفض المعامل ≤ صفر.

٩ معاينة شاشات المنتج

٩-١ تبويب الدواء (يظهر بس لو العلم مفعّل)

نافذة المنتج — التبويبات الأربعة الحالية + تبويب خامس شرطي
تعديل منتج — أوجمنتين ١جم
بيانات أساسيةتفاصيلمتغيّرات وأكثرمرفقات 💊 دواء جديد
التبويب ده بيظهر بس لما «هذا الصنف دواء» يبقى مفعّلًا في تبويب البيانات الأساسية.
أموكسيسيلين ٨٧٥مج ✕كلافولانيك أسيد ١٢٥مج ✕ + إضافة…
قرص مغلّف ▾
١٠٠٠
مج ▾
مضاد حيوي — بنسلين ▾
جلاكسو ▾
أقل من ٢٥° ▾
يُصرف بروشتةما يتباعش من غير روشتة لو الإعداد مفعّل.
دواء مقيَّد/مخدِّرسجل خاص · ممنوع المرتجع · ممنوع أوفلاين.

٩-٢ المكوّنات — جدول الوحدات بعد ما يبقى قابلًا للتحرير

نفس الجدول الموجود — الأخضر = حقول جديدة قابلة للإدخال
💡 مساعد التركيب: ١ علبة تحتوي على ١٠ شريط ▾ ⇒ يتخزّن ١٠٠ قرص (الوحدة الأساسية). الشاشة بتضرب لوحدها — ما تكتبش الناتج بإيدك.
الوحدةالمعامل للأساسيةالباركودسعر الشراءسعر البيعتُباعتُشترى
قرص أساسية١ ٠٫٤٨٠٠٫٩٦٠
شريط١٠ 62210…٤٫٥٠٠٨٫٩٠٠🗑
علبة١٠٠ (محسوبة) 62211…٢٤٫٠٠٠٤٨٫٠٠٠🗑

النهارده: عمودا السعر نص للقراءة فقط، والباركود و«تُباع/تُشترى» مش موجودين في الشاشة أصلًا — رغم إن الـAPI بيقبلهم كلهم.

٩-٣ شاشة المواد الفعّالة (موديول مستقل)

نسخة من أبسط شاشة CRUD موجودة
المواد الفعّالة+ مادة جديدة
🔍 بحث…
الحالة: الكل ▾
الكودالاسم بالعربيالاسم بالإنجليزيعدد الأدويةمفعّلة
AI-001باراسيتامولParacetamol١٤✎ 🗑
AI-002أموكسيسيلينAmoxicillin٦✎ 🗑
AI-003كلافولانيك أسيدClavulanic acid٣✎ 🗑

«عدد الأدوية» بيجي مجانًا من نمط الكونترولر اللي بننسخ منه — وهو كمان أساس منتقي البديل الجنيسي في نقاط البيع بعدين.

١٠ خطة التنفيذ

الترتيب مقصود: حزمة ١ بتخلي الـ٤٦ إعداد رخاص، وحزمة ٢ بتقفل ثغرة أمنية مفتوحة دلوقتي. الاتنين قبل أي إعداد صيدلية.

المرحلة أ — البنية التحتية للإعدادات (قبل أي إعداد)

WPالنطاقالمستودعلماذا أولًا
WP1تحويل تبويب نقاط البيع للنمط العام — كونترولر إعدادات POS على نمط المبيعات + كتلة الرسم التلقائي في الشاشةBE+FEبعدها كل إعداد = سطر seeder + قارئ. من غيرها كل إعداد = ٤ مواضع بالإيد وبلا شرح.
WP2تنفيذ إعدادات الماكينة السبعة على السيرفر + تقنين المفتاح التامن + توحيد القيم الافتراضية المتضاربة أمنيBEدلوقتي كلها ديكور — أي طلب مباشر بيتخطّاها.
WP3تسليم الإعدادات مع فتح الوردية بدل قراءة مفتاح مفتاحBE+FEمن غيرها كل إعداد بيشتغل عند المدير ويفشل صامتًا عند الكاشير.
WP4صلاحية بيع مستقلة لنقاط البيع بدل صلاحية فاتورة المبيعات أمنيBEثغرة مفتوحة على نقاط البيع العادية، مش خاصة بالصيدلية.

المرحلة ب — المنتج والدواء

WPالنطاقالمستودعملاحظات
WP5موديول المادة الفعّالة (CRUD كامل) + موديول الشكل الدوائي — الاتنين في الكور ومسجَّلين في مسارات الكورBE+FE١٦ ملفًا لكل واحد بالنمط · ~٩٥٠ سطر
WP6علم «دواء» + جدول التفاصيل ١:١ + جدول الربط بالمواد الفعّالة (مع التركيز لكل مادة)BEmigration بنمط الجذر بالحراسات · + إضافتها لـResource المنتج ولنموذجَي الإضافة والتعديل في نفس الحزمة
WP7تبويب الدواء الشرطي في نافذة المنتج + معالجة إعادة ضبط التبويب النشط لما العلم يتقفلFE~٩٠ سطرًا · مفيش تبويب شرطي في المشروع قبل كده
WP8جدول الوحدات قابل للتحرير (سعر بيع/شراء/باركود/تُباع/تُشترى) + توصيل دالة التعديل الميتة + مساعد التركيب + تحقق المعاملBE+FEإصلاح في الطبقة المشتركة — بيفيد كل المنتجات

المرحلة ج — الإعدادات، إعدادًا إعدادًا (بعد موافقتك على الليستة)

كل إعداد = حزمة صغيرة فيها: تعريفه بلغتين + شرحه + أول قارئ ليه + اختبار بيثبت إن قفله بيغيّر السلوك فعلًا. الترتيب المقترح:

الترتيبالإعدادليه في المكان ده
ج-١تفعيل نظام التشغيلات + منع بيع المنتهي + إلزام التشغيلة عند الاستلامأهم مطلب صيدلية، ومتوقّف على إصلاح دفتر اللوطات في التقرير السابق
ج-٢وحدة الصرف (تغيير الوحدة + إظهارها)أرخص مكسب تشغيلي — الحساب مبني بالكامل
ج-٣السعر الجبري + سقوف الخصم + تنفيذ تعديل السعر[FIN] — يتعملوا مع بعض عشان يغطّوا التلات طلبات معًا
ج-٤سياسة المرتجع الدوائي + مخزن الحجر + نافذة المرتجع[FIN] — بيقفل أكبر مصدر انحراف مقيس
ج-٥وضع المريض + تقسيم التأمين + الأهليةمحتاج تعبئة رجعية للمرضى
ج-٦الروشتة والصرفأكبر بند — ميزة مش مفتاح
ج-٧الإيصال (تشغيلة/وحدة/صيدلي/تأمين)بيستنى الحقول اللي فوق
ج-٨التشغيل والدرج (فئات النقود، التقريب، الأوفلاين)مستقلة — تتعمل أي وقت
ج-٩الربط الحكوميموقوفة على توثيق المورّد — ما تتعرّفش قبله

١١ قرارات محتاجة قرارك

١. أوافق على تحويل تبويب الإعدادات الأول (WP1)؟

توصيتي: أيوه، وهي أول حاجة. من غيرها الـ٤٦ إعداد بيتكتبوا بالإيد وبلا شرح — والشرح ده كان نص طلبك. التحويل مرة واحدة، وبعدها الإعداد بيرسم نفسه بلغتين مع شرحه.

٢. أقفل الثغرتين الأمنيتين (WP2 + WP4) قبل شغل الصيدلية؟

توصيتي: أيوه. الاتنين مفتوحين دلوقتي على نقاط البيع العادية عند كل عميل: إعدادات الماكينة كلها بلا تنفيذ سيرفر، وأي حد يعمل فاتورة مكتبية يقدر يرحّل بيعة على الماكينة. مالهمش علاقة بالصيدلية — بس اكتشفناهم وإحنا بندوّر.

٣. الدواء يبقى علمًا وجدولًا جانبيًا، ولا نوعًا تالتًا؟

توصيتي: علم + جدول ١:١. النوع التالت بيخفي الأدوية من شاشة البيع ومن الجرد يوم ما ينزل. وسؤال واحد بيحسمها نهائيًا: هل ممكن دواء يتباع كـخدمة (حقنة بتتعطى وتتحاسب كإجراء)؟ لو أيوه، فالنوع لازم يفضل مستقل عن كونه دواء.

٤. الشكل الدوائي: جدول قابل للإضافة، ولا قايمة ثابتة؟

توصيتي: جدول. السؤال الحقيقي: عملاؤك محتاجين يضيفوا أشكالًا بنفسهم، ولا قايمة ثابتة (~١٥ شكلًا) تكفي للأبد؟ لو ثابتة فعلًا، الـenum بيوفّر ~٤٠٠ سطر — وبرضه هفضّل الجدول عشان الاسم العربي لكل شركة.

٥. مساعد التركيب: نعرض المعامل المحسوب ولا اللي كتبته؟

توصيتي: نخزّن المحسوب ونعرض الاتنين. إضافة «الوحدة الأب» للمحرك تغيير في أخطر primitive عندنا — كل حركة مخزون وبيع وشراء وتقييم ماشية عليه، وهيغيّر معنى كل صف موجود. المساعد بيدّي نفس الراحة بلا الخطر.

٦. تبويب الدواء يتضاف لشاشة منتجات المتجر كمان؟

مسار المتجر منفصل تمامًا عن مسار الكور وبينهم انحراف قايم خلاص. أي حقل دوائي نضيفه في الكور هيبقى غير مرئي في شاشة المتجر إلا لو اتكرر. توصيتي: لأ في المرحلة الأولى — إلا لو الصيدلية هتبيع أونلاين من اليوم الأول.

٧. البيانات القديمة: فيه مواد فعّالة متكتوبة كنص حر؟

جدول أدوية العيادة فيه عمود «مادة فعّالة» نص حر. في بيئة التطوير الجدول فاضي — بس عند عميل حقيقي ممكن يكون معبّى. توصيتي: WP5 يطلع تقرير بالقيم الفريدة الموجودة الأول، وبعدين نقرّر الترحيل. ترحيل أعمى لنص حر بيولّد مواد مكرّرة باختلاف مسافة.

الخلاصة

منهج «الإعدادات الأول» صح — والآلة اللي محتاجينها موجودة وأحسن مما توقّعت: التعريف بيحمل الاسم والشرح بلغتين، والشاشة بترسم نفسها من النوع، والتوصيل للعملاء الحاليين تلقائي. بس تبويب نقاط البيع هو الوحيد برّه الآلة دي — فالحزمة الأولى مش إعداد، هي تحويل التبويب. وبعدها الـ٤٦ إعداد بيبقوا رخاص فعلًا.

وفي الطريق اكتشفنا حاجتين مفتوحتين دلوقتي عند كل عميل ومالهمش علاقة بالصيدلية: إعدادات الماكينة السبعة كلها بلا تنفيذ على السيرفر (شاشة حماية ديكور)، ومدخل البيع محروس بصلاحية الفواتير المكتبية.

وفي المنتج: الدواء علم وجدول جانبي مش نوع تالت، والمادة الفعّالة موديول كامل بنمط منسوخ من أنضف مثالين عندنا، و«كل وحدة ليها سعر» مبني بالكامل ومستنّي شاشة — وفيه فخ تركيب صامت لازم نقفله بحقل مساعد مش بتغيير المحرك.

تحليل المرحلة ١ — مفيش كود ولا migration اتكتب · تلات مسارات تدقيق متوازية · ١ أغسطس ٢٠٢٦ · moonui2 · hazemdev2
مستنّي موافقتك على الليستة والترتيب قبل أي تنفيذ.