الطلب: «اعمل ليستة بالإعدادات بشرح صغير تحت كل واحد، وبعدين ننفّذ إعداد إعداد» + «نضيف للمنتجات نوع اسمه دواء، ولو اخترناه يفتح تبويب فيه المواد الفعّالة والشكل الدوائي والمكوّنات». التدقيق طلّع اكتشاف بيقلب ترتيب الشغل: آلة الإعدادات العامّة موجودة وممتازة وشغّالة على ٧ تبويبات — وتبويب نقاط البيع هو الوحيد المكتوب بالإيد. وكل إعدادات الماكينة السبعة الحالية مالهاش أي تنفيذ على السيرفر.
التدقيق الجديد صحّح تلات حاجات في 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 (قيمة الشركة). والتعريف نفسه بيحمل:
label_ar/label_en)description_ar/description_en) — ده بالظبط «الشرح الصغير تحت كل إعداد» اللي طلبته، وموجود كعمود في قاعدة البياناتboolean|integer|decimal|enum|json|string) وقائمة القيم المسموحة والمجموعة والترتيبوالشاشة العامّة بتقرا التعريف وترسم نفسها: boolean ⇒ مفتاح، enum ⇒ قائمة، integer ⇒ رقم. وشغّالة كده على ٧ تبويبات: المحاسبة · المخزون · المنتجات · الموارد البشرية · المبيعات · المشتريات · المعمل.
تبويب الـPOS في شاشة الإعدادات مش بيقرا التعريفات إطلاقًا — هو تلات قوائم منسدلة مكتوبة حرفيًا في الـHTML وزرار حفظ. مفيش حلقة، مفيش تصنيف حسب النوع، ومفيش مكان أصلًا للشرح.
يعني: لو بدأنا نضيف إعدادات دلوقتي، كل إعداد هيتكتب بالإيد في ٤ أماكن ومن غير شرح. ولو حوّلنا التبويب للنمط العام الأول — كل إعداد بعدها بيبقى سطر واحد في seeder + قارئ واحد، وبيرسم نفسه بلغتين مع شرحه في مجموعته الصح.
وده سبب إن أول حزمة في الخطة مش إعداد — هي تحويل التبويب. بتتعمل مرة واحدة، وبعدها الـ٤٦ إعداد بيبقوا رخاص.
| إعداد على مستوى الشركة | إعداد على مستوى الماكينة | |
|---|---|---|
| التخزين | صف في 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 (منتجات مقابل فورميلاري مستقل) — معرّف بالكامل بلغتين، وظاهر في الشاشة، وليه صفر قراءة في الريبو كله. والوصف بتاعه نفسه بيقول «الآثار هتيجي في م٧». والشاشة بتكتب جنبه «قريبًا — غير مفعّل».
القاعدة اللي هلتزم بيها: ما نعرّفش إعداد إلا في نفس الحزمة اللي بتكتب أول قارئ ليه. الليستة تحت بتقول «إيه اللي هيتعمل»، مش «إيه اللي هيتزرع».
حاجتين موجودتين خلاص وما ينفعش نعمل نسخة تانية منهم:
inventory.expired_issue_policy — block/warn/allow، افتراضي block. ⚠ بس «block» عندنا بيستبعد اللوط المنتهي من الاختيار التلقائي — ما بيرفضش البيعة. فالمنع الحقيقي محتاج حارس جديد على حدود الـPOS، مش مفتاح جديد.inventory.near_expiry_days — بالأيام. إنت قلت «شهور» — التحويل يتعمل في الشاشة، وما نعملش مفتاح تاني بوحدة مختلفة، وإلا بعد سنة يبقى عندنا رقمين بيتناقضوا.الحالة: موجود وشغّال · موجود ومعطّل (متخزّن ومحدش بيقراه) · جديد · البيت: شركة ماكينة · التنفيذ: سيرفر واجهة
| الإعداد | النوع | الافتراضي | البيت | الحالة | الشرح |
|---|---|---|---|---|---|
تفعيل نظام التشغيلات في نقاط البيع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 مزروعة، وتنفيذها في الواجهة بس — السيرفر ما بيفحصهاش. |
| الإعداد | النوع | الافتراضي | البيت | الحالة | الشرح |
|---|---|---|---|---|---|
السماح بتغيير وحدة البيع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 | مفتاح | مفتوح | شركة | جديد | يمنع بيع الأدوية المقيَّدة والماكينة مقطوعة عن السيرفر. سجل مخدِّرات بيتزامن بعدين مش سجل. توصيتي ما يبقاش قابل للإطفاء أصلًا. |
| الإعداد | النوع | الافتراضي | البيت | الحالة | الشرح |
|---|---|---|---|---|---|
تفعيل وضع المريضpos.enable_patient_mode | مفتاح | مقفول | شركة | جديد | منتقي العميل يبحث في المرضى كمان ويعرض كود المريض والعمر. الجسر بين المريض والعميل موجود في القاعدة خلاص — ٤١ مريض بس من ١١٬٧٩٧ معبّى، فمحتاج تعبئة رجعية. |
تفعيل تقسيم التأمينpos.enable_insurance_split | مفتاح | مقفول | شركة | جديد | الفاتورة تتقسم: تحمّل الجهة الدافعة مقابل تحمّل المريض. ⚠ إضافة وسيلة دفع «تأمين» تعديل كود في enum مش إعداد — الإعداد بيفعّل التقسيم بس. |
حساب تحصيل الجهة الدافعةpos.payer_receiving_account_id | رقم (حساب) | — | شركة | جديد | الحساب اللي يترحّل عليه نصيب التأمين كمدينية. على نمط حسابات التحصيل الموجودة للكاش والشبكة. |
تفعيل نفيسnphies.enabled | مفتاح | مقفول | شركة | موجود سيرفر | يحوّل مسار المطالبات من يدوي لنفيس. ✅ ده أنضف نموذج ربط عندنا — بيبدّل المحوّل في مزوّد الخدمة حسب الإعداد. هنقلّده. ⚠ بس نفيس ما اتصلش ولا مرة (١٢/١٢ timeout). |
فحص الأهلية قبل الصرفpos.require_eligibility_check | مفتاح | مقفول | شركة | جديد | يمنع إتمام بيعة تأمينية قبل التحقق من أهلية المريض. مدخل الأهلية موجود وشغّال — محتاج معرّف المريض بس. |
| الإعداد | النوع | الافتراضي | البيت | الحالة | الشرح |
|---|---|---|---|---|---|
سياسة إرجاع الدواء للمخزون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 | قائمة: ٥٨/٨٠ مم | ٨٠ مم | ماكينة | جديد | عرض الطابعة الحرارية. النهارده ٨٠ مم مثبَّتة في الكود، و٥٨ مم شائعة. |
| الإعداد | النوع | الافتراضي | البيت | الحالة | الشرح |
|---|---|---|---|---|---|
فئات النقود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 مش توقيع)، وأعمدة العنوان الوطني السعودي مش موجودة في جدول الشركات. مفعّلش ده على عميل سعودي قبل ما يتصلّح. |
الشرح اللي تحت كل إعداد مش نص في الشاشة — هو عمود في قاعدة البيانات بيتحمّل بلغة المستخدم. يعني إعداد جديد بيجيب معاه اسمه وشرحه بالعربي والإنجليزي من غير أي شغل واجهة.
عمود النوع في المنتجات فيه قيمتين بس: منتج وخدمة. وفيه تلات استعلامات حيّة بتفلتر «منتج» حرفيًا:
| المكان | الأثر لو بقى «دواء» نوعًا تالتًا |
|---|---|
| فهرس منتجات نقاط البيع | كل الأدوية تختفي من شاشة البيع |
| بحث نقاط البيع + الباركود | الأدوية ما تتلاقاش بالبحث ولا بالمسح |
| جرد المخزون | الأدوية تتستثنى من الجرد |
والسبب: النوع عندنا مش تصنيف — هو ملموس مقابل غير ملموس. التصنيف الحقيقي في شجرة التصنيفات.
وحدة الإنتاج عملت بالظبط كده: بدل ما تحط ١١ عمود بتاعها على جدول المنتجات، عملت جدول إعدادات جانبي مربوط ١:١ بالمنتج. نقلّده حرفيًا.
products.is_drug (علم واحد — هو اللي بيفتح التبويب)
│
├── product_drug_details (١:١ ، فريد على الشركة+المنتج)
│ ├── الشكل الدوائي → FK لجدول أشكال دوائية
│ ├── التركيز + وحدته
│ ├── يُصرف بروشتة؟ (Rx/OTC)
│ ├── مقيَّد/مخدِّر؟
│ ├── حرارة التخزين
│ └── التصنيف العلاجي
│
└── product_active_ingredient (جدول ربط — دواء ↔ عدة مواد فعّالة)
└── التركيز لكل مادة (لو الدوا مركّب)
| الخيار | خطر الانحدار | الحكم |
|---|---|---|
| نوع «دواء» جديد | عالي — ٣ شاشات تكسر فورًا | مرفوض |
| علم + جدول ١:١ | صفر — الدواء يفضل منتجًا عاديًا في كل الشاشات | التوصية |
| الأعمدة على جدول المنتجات | متوسط | مرفوض — الجدول ٣٩ عمودًا خلاص، وكل الموديولات هتحمل التكلفة |
فيه في المشروع جدولين lookup اتبنوا بالكامل في الباك-إند وماحدش عمللهم شاشة: العلامات التجارية، والشركات المصنِّعة. وكمان manufacturer_id وshelf_life_days موجودين على المنتج وغايبين عن الـResource وعن نموذجَي الإضافة والتعديل في الكور — يعني موجودين ومش قابلين للتحرير.
الدرس المطبَّق في الخطة: المادة الفعّالة والشكل الدوائي يتحطّوا في الكور، ويتسجّلوا في مسارات الكور، ويتضافوا لـResource المنتج ولنموذجَي الإضافة والتعديل في نفس الحزمة. وإلا نبقى عملنا اليتيم التالت.
طلبك: «المادة الفعّالة عايزين نعمل ليها موديول كامل — إضافة وتعديل وحذف، وكل دوا يختار مادة أو أكتر». النمط موجود بالكامل، وهنسخه من أنضف مثالين.
| الطبقة | هننسخ من | ليه |
|---|---|---|
| الباك-إند (الجدول والموديل والكونترولر) | العلامات التجارية | أعمدته مطابقة ١:١ للمطلوب (كود، اسم عربي/إنجليزي، وصف، مفعّل)، وكونترولره فيه بحث وفلترة وعدّ مرتبط واختبار جاهز. |
| الفرونت-إند (الشاشة والخدمة والحالة) | مجموعات الوحدات | أبسط شاشة CRUD كاملة عندنا: قائمة + نافذة + خدمة + إدارة حالة. |
| الربط متعدد لمتعدد + الاختيار المتعدد | الحساب البنكي ↔ الفروع | اختيار متعدد مربوط بنموذج تفاعلي — نفس حالتنا بالظبط. والتحقق بيتحقق من ملكية الشركة. |
| تركيز لكل مادة (دوا مركّب) | عروض المنتج | سابقة حفظ بيانات إضافية على جدول الربط نفسه. |
ونفس الـ١٦ ملف بالظبط بتتكرر لـالشكل الدوائي بتغيير النصوص بس — عشان كده الاتنين في حزمة واحدة.
توصيتي: جدول — قايمة الأشكال مش مقفولة بين الأسواق، والـenum معناه إصدار برنامج جديد كل ما عميل يحتاج «فوّار» أو «لبوس». وبما إن هيكل المادة الفعّالة هيتبني على أي حال، التاني بيبقى شبه مجاني. وكمان الجدول بيدّي اسمًا عربيًا لكل شركة — وده الـenum ما يقدرش يعمله.
جدول وحدات المنتج فيه خلاص: معامل تحويل · باركود لكل وحدة · سعر بيع · سعر شراء · تُباع؟ · تُشترى؟ والـAPI بيقبلهم كلهم.
والشاشة بتعرض السعرين كنص عادي مش كحقول إدخال، والباركود وخانات «تُباع/تُشترى» مش ظاهرين إطلاقًا — بحث في كل ملفات الواجهة رجّع صفر.
وأطرف تفصيلة: دالة تعديل الوحدة مكتوبة في خدمة الواجهة ومحدش بيناديها. الكود مستنّي شاشة اتكتبتش.
ودي إصلاح في الطبقة المشتركة — بيفيد كل المنتجات مش الأدوية بس.
معامل التحويل دايمًا منسوب للوحدة الأساسية، مش للوحدة الأب. مفيش عمود «الوحدة الأب» ومفيش تتبّع سلسلة.
| الوحدة | المعامل | المعنى |
|---|---|---|
| قرص (أساسية) | ١ | — |
| شريط | ١٠ | ١٠ أقراص |
| علبة | ١٠٠ | ١٠ شرايط × ١٠ |
علبتين ⇒ ٢٠٠ قرص. صح.
| الوحدة | المعامل | المعنى المقصود |
|---|---|---|
| قرص (أساسية) | ١ | — |
| شريط | ١٠ | ١٠ أقراص |
| علبة | ١٠ | «١٠ شرايط» |
علبتين ⇒ ٢٠ قرص بدل ٢٠٠. غلط ١٠ أضعاف — ومفيش أي رسالة خطأ.
والأسوأ: خدمة التحويل مصمَّمة إنها ما ترميش استثناء — أي إعداد غلط بيرجع ١ عشان «ما تكسرش حركة مخزون». والتحقق عند الإدخال كان مؤجَّلًا لحزمة ما اتكتبتش، ونموذجا إضافة وتعديل الوحدة ما بيفحصوش المعامل إطلاقًا.
الحل مش تغيير المحرك — المحرك ده بيمشي عليه كل حركة مخزون وبيع وشراء وتقييم في النظام، ولمسه بتغيّر معنى كل صف موجود. الحل حقل في الشاشة: «العلبة تحتوي على ⟨١٠⟩ ⟨شريط⟩» والشاشة تضرب لوحدها وتخزّن ١٠٠، وتعرض الناتج قدام المستخدم + قاعدة تحقق ترفض المعامل ≤ صفر.
| الوحدة | المعامل للأساسية | الباركود | سعر الشراء | سعر البيع | تُباع | تُشترى | |
|---|---|---|---|---|---|---|---|
| قرص أساسية | ١ | — | ٠٫٤٨٠ | ٠٫٩٦٠ | ✔ | — | — |
| شريط | ١٠ | 62210… | ٤٫٥٠٠ | ٨٫٩٠٠ | ✔ | — | 🗑 |
| علبة | ١٠٠ (محسوبة) | 62211… | ٢٤٫٠٠٠ | ٤٨٫٠٠٠ | ✔ | ✔ | 🗑 |
النهارده: عمودا السعر نص للقراءة فقط، والباركود و«تُباع/تُشترى» مش موجودين في الشاشة أصلًا — رغم إن الـAPI بيقبلهم كلهم.
| الكود | الاسم بالعربي | الاسم بالإنجليزي | عدد الأدوية | مفعّلة | |
|---|---|---|---|---|---|
| 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 | علم «دواء» + جدول التفاصيل ١:١ + جدول الربط بالمواد الفعّالة (مع التركيز لكل مادة) | BE | migration بنمط الجذر بالحراسات · + إضافتها لـResource المنتج ولنموذجَي الإضافة والتعديل في نفس الحزمة |
| WP7 | تبويب الدواء الشرطي في نافذة المنتج + معالجة إعادة ضبط التبويب النشط لما العلم يتقفل | FE | ~٩٠ سطرًا · مفيش تبويب شرطي في المشروع قبل كده |
| WP8 | جدول الوحدات قابل للتحرير (سعر بيع/شراء/باركود/تُباع/تُشترى) + توصيل دالة التعديل الميتة + مساعد التركيب + تحقق المعامل | BE+FE | إصلاح في الطبقة المشتركة — بيفيد كل المنتجات |
كل إعداد = حزمة صغيرة فيها: تعريفه بلغتين + شرحه + أول قارئ ليه + اختبار بيثبت إن قفله بيغيّر السلوك فعلًا. الترتيب المقترح:
| الترتيب | الإعداد | ليه في المكان ده |
|---|---|---|
| ج-١ | تفعيل نظام التشغيلات + منع بيع المنتهي + إلزام التشغيلة عند الاستلام | أهم مطلب صيدلية، ومتوقّف على إصلاح دفتر اللوطات في التقرير السابق |
| ج-٢ | وحدة الصرف (تغيير الوحدة + إظهارها) | أرخص مكسب تشغيلي — الحساب مبني بالكامل |
| ج-٣ | السعر الجبري + سقوف الخصم + تنفيذ تعديل السعر | [FIN] — يتعملوا مع بعض عشان يغطّوا التلات طلبات معًا |
| ج-٤ | سياسة المرتجع الدوائي + مخزن الحجر + نافذة المرتجع | [FIN] — بيقفل أكبر مصدر انحراف مقيس |
| ج-٥ | وضع المريض + تقسيم التأمين + الأهلية | محتاج تعبئة رجعية للمرضى |
| ج-٦ | الروشتة والصرف | أكبر بند — ميزة مش مفتاح |
| ج-٧ | الإيصال (تشغيلة/وحدة/صيدلي/تأمين) | بيستنى الحقول اللي فوق |
| ج-٨ | التشغيل والدرج (فئات النقود، التقريب، الأوفلاين) | مستقلة — تتعمل أي وقت |
| ج-٩ | الربط الحكومي | موقوفة على توثيق المورّد — ما تتعرّفش قبله |
توصيتي: أيوه، وهي أول حاجة. من غيرها الـ٤٦ إعداد بيتكتبوا بالإيد وبلا شرح — والشرح ده كان نص طلبك. التحويل مرة واحدة، وبعدها الإعداد بيرسم نفسه بلغتين مع شرحه.
توصيتي: أيوه. الاتنين مفتوحين دلوقتي على نقاط البيع العادية عند كل عميل: إعدادات الماكينة كلها بلا تنفيذ سيرفر، وأي حد يعمل فاتورة مكتبية يقدر يرحّل بيعة على الماكينة. مالهمش علاقة بالصيدلية — بس اكتشفناهم وإحنا بندوّر.
توصيتي: علم + جدول ١:١. النوع التالت بيخفي الأدوية من شاشة البيع ومن الجرد يوم ما ينزل. وسؤال واحد بيحسمها نهائيًا: هل ممكن دواء يتباع كـخدمة (حقنة بتتعطى وتتحاسب كإجراء)؟ لو أيوه، فالنوع لازم يفضل مستقل عن كونه دواء.
توصيتي: جدول. السؤال الحقيقي: عملاؤك محتاجين يضيفوا أشكالًا بنفسهم، ولا قايمة ثابتة (~١٥ شكلًا) تكفي للأبد؟ لو ثابتة فعلًا، الـenum بيوفّر ~٤٠٠ سطر — وبرضه هفضّل الجدول عشان الاسم العربي لكل شركة.
توصيتي: نخزّن المحسوب ونعرض الاتنين. إضافة «الوحدة الأب» للمحرك تغيير في أخطر primitive عندنا — كل حركة مخزون وبيع وشراء وتقييم ماشية عليه، وهيغيّر معنى كل صف موجود. المساعد بيدّي نفس الراحة بلا الخطر.
مسار المتجر منفصل تمامًا عن مسار الكور وبينهم انحراف قايم خلاص. أي حقل دوائي نضيفه في الكور هيبقى غير مرئي في شاشة المتجر إلا لو اتكرر. توصيتي: لأ في المرحلة الأولى — إلا لو الصيدلية هتبيع أونلاين من اليوم الأول.
جدول أدوية العيادة فيه عمود «مادة فعّالة» نص حر. في بيئة التطوير الجدول فاضي — بس عند عميل حقيقي ممكن يكون معبّى. توصيتي: WP5 يطلع تقرير بالقيم الفريدة الموجودة الأول، وبعدين نقرّر الترحيل. ترحيل أعمى لنص حر بيولّد مواد مكرّرة باختلاف مسافة.
منهج «الإعدادات الأول» صح — والآلة اللي محتاجينها موجودة وأحسن مما توقّعت: التعريف بيحمل الاسم والشرح بلغتين، والشاشة بترسم نفسها من النوع، والتوصيل للعملاء الحاليين تلقائي. بس تبويب نقاط البيع هو الوحيد برّه الآلة دي — فالحزمة الأولى مش إعداد، هي تحويل التبويب. وبعدها الـ٤٦ إعداد بيبقوا رخاص فعلًا.
وفي الطريق اكتشفنا حاجتين مفتوحتين دلوقتي عند كل عميل ومالهمش علاقة بالصيدلية: إعدادات الماكينة السبعة كلها بلا تنفيذ على السيرفر (شاشة حماية ديكور)، ومدخل البيع محروس بصلاحية الفواتير المكتبية.
وفي المنتج: الدواء علم وجدول جانبي مش نوع تالت، والمادة الفعّالة موديول كامل بنمط منسوخ من أنضف مثالين عندنا، و«كل وحدة ليها سعر» مبني بالكامل ومستنّي شاشة — وفيه فخ تركيب صامت لازم نقفله بحقل مساعد مش بتغيير المحرك.
تحليل المرحلة ١ — مفيش كود ولا migration اتكتب · تلات مسارات تدقيق متوازية · ١ أغسطس ٢٠٢٦ · moonui2 · hazemdev2
مستنّي موافقتك على الليستة والترتيب قبل أي تنفيذ.