أربع نقاط طلبتها: عمود اسم العميل صاحب المنتج في الأرصدة (مع بحث وفلتر) · طباعة حركات كارت الصنف ·
عمود يوصّل من الحركة لأمر الإنتاج أو تاج التشغيل مباشرة بدل إذن الصرف · وإصلاح فلاتر شاشة المنتجات.
التلاتة الأولانية كلها ممكنة والبيانات موجودة فعلًا —
والرابعة عيب سببه سطر ناقص واحد، لقيته ومؤكَّد.
فيه اشتباك تسمية واحد لازم تحسمه قبل ما نبدأ.
/core/stock-card/{id}) — الحركات مش ممكن تتطبع.قرأت moonui2_dev_be مباشرة. الصنف اللي إنت ضربته مثال (17139 — خام بودرة ميلامين):
| نوع المصدر على الحركة | العدد | وبعدين؟ |
|---|---|---|
| inventory_issue | 7 | السبعة كلهم على أوامر إنتاج (GDN-000014 → أمر 16، GDN-000016 → أمر 28 … إلخ) |
| sales_invoice | 8 | فاتورة بيع |
| inventory_receipt | 2 | إذن استلام |
| opening | 1 | رصيد افتتاحي |
يعني الشاشة النهاردة بتوريك ٧ أذون صرف، والحقيقة إنهم ٧ أوامر إنتاج. المعلومة مخزّنة — الشاشة بس مش بتقطع الخطوة التانية.
وعلى مستوى الشركة كلها، inventory_issues.reference_type بياخد ٣ قيم فعليًا:
جدول inventory_issue_tags. عندك دلوقتي تاجين: «امر انتاح111» و«اورد 1».
وتعليق الكود صريح جدًا في وصفه: لافتة تشغيلية بحتة — مش أمر إنتاج ومش مركز تكلفة؛ مالوش حساب ولا قيمة ولا أي ربط بالقيود.
والدليل السلوكي: إذن صرف على تاج ما بيعملش MfgMaterialIssue، وما بيرحّلش على الإنتاج تحت التشغيل، وما بيفكّش أي حجز.
يعني الاتنين لازم يتعاملوا مختلف: أمر الإنتاج ليه رقم (order_number) وشاشة يروح لها؛ التاج ليه اسم بس وشاشة قايمة.
ولاحظ: ProductionOrder مالوش عمود اسم — المعرّف الوحيد هو order_number (زائد اسم المنتج المُنتَج اختياريًا). فـ«الاسم» اللي هيظهر لأمر الإنتاج = رقمه.
ومفيش كيان «أمر شغل» تصنيعي أصلًا — الـWorkOrder الوحيد في النظام بتاع الصيانة (CMMS) وما بيعملش أي حركة مخزون.
الشاشة فيها عمود «المصدر» فعلًا، بيترجم reference_type عبر خريطة ثابتة
(stock-card.component.ts:56-66) ويعمل router.navigate([route], {queryParams:{viewId}}).
فالحركة اللي مصدرها inventory_issue بتوديك على شاشة أذون الصرف — وده بالظبط اللي إنت مش عايزه.
وكمان اللينك مبني كـ<a (click)=…> مش routerLink — يعني مش ممكن تفتحه في تبويب جديد ولا تنسخ رابطه.
inventory_movements فيه reference_type (نص عادي) وreference_id (رقم) —
من غير مفتاح أجنبي، ومن غير أي علاقة معرّفة على الموديل، ومن غير enum ولا خريطة تسميات (بعكس أنواع الإذن والاستلام اللي ليهم enum مترجَم).
وأخطر تفصيلة: مجموعة الأنواع ~٢٨ قيمة، ومعنى الرقم مش موحّد:
| النوع | الرقم بيشاور على |
|---|---|
| inventory_issue / receipt / adjustment / transfer … | معرّف المستند (الأغلبية) |
| production_order · production_staging | معرّف أمر الإنتاج مباشرة — خطوة واحدة بس! |
| consignment_receipt · consignment_return | معرّف العميل (شريك تجاري) — مش مستند |
| *_cancel | معرّف المستند الأصلي، مش مستند إلغاء |
النتيجة: أي حلّ عام لازم يتعامل مع كل نوع على حدة. اللي يفترض «الرقم ده مستند» هيوصّل المستخدم لصفحة غلط في حالة الأمانات.
مش كل استهلاك إنتاج بياخد خطوتين: الـbackflush بيكتب الحركة مباشرة بـreference_type='production_order'
وreference_id = معرّف الأمر — خطوة واحدة. وكذلك production_staging.
فالسلسلة الطويلة (خطوتين) هي حالة الصرف اليدوي بس.
| المفهوم | مصدره | معناه |
|---|---|---|
| عدسة المالك (الموجودة) | inventory_lot_balances.owner_partner_id | مالك الكمية الفعلية — بضاعة أمانة محفوظة لعميل، خارج دفاتر الشركة |
| عميل التصنيع (المطلوب) | products.toll_customer_id | العميل صاحب تعريف الصنف في الكتالوج |
الاتنين ممكن يختلفوا في نفس الصف، وقايمة العملاء في الفلتر الموجود بتيجي من الأمانات الحيّة بس — فعميل تصنيع كل بضاعته مملوكة للشركة مش هيظهر فيها أصلًا.
يعني لو حطّينا عمود اسمه «العميل» جنب عدسة «المالك»، هيبقى في الشاشة مفهومين مختلفين بنفس الكلمة. ده قرار رقم ١ في §٩.
وباقي الوضع في شاشة الأرصدة: الاستعلام مش بيمرّ على ProductService::search() إطلاقًا — الكنترولر بيبني استعلامه بنفسه. والبحث بيغطي ٤ أعمدة بس (name, name_ar, code, sku). والـResource مافيهوش toll_customer_id ولا اسم العميل. والشاشة مالهاش تحكم في الأعمدة (لا سحب ولا تحجيم).
| النمط | مستخدم في | مناسب لكارت الصنف؟ |
|---|---|---|
| PrintService + قوالب | ~٣٠ شاشة مستندات (فواتير، أذون، سندات) | لأ — ده لطباعة مستند بقالب وشعار، والكارت تقرير |
window.print() + @media print | كشف حساب عميل · كشف مورّد · حركة النقدية · BMR · إشعار إرجاع الأمانات | أيوه — ده النمط المطابق، ومستخدم في كل شاشات الكشوف |
| ExportService (Excel/PDF) | تقارير المخزون · جداول app-data-table | إضافة كويسة، بس مش «طباعة» |
مفيش أي مدخل /inventory/*/print على السيرفر — الطباعة من جهة السيرفر موجودة لسندات ومستندات مبيعات بس.
// products.component.ts:2573 buildFilters() if (this.filterTollCustomer) f.toll_customer_id = this.filterTollCustomer; // ✅ بيتحطّ // product.service.ts:77 list() — بتقرا ٦ مفاتيح بس: search · type · status · product_category_id · is_active · track_inventory // ❌ toll_customer_id مش واحد منهم — القيمة بتتبخّر
السيرفر مش هو المشكلة: ProductService::search() بيدعم toll_customer_id=<id>|own تمامًا،
وtollCustomer محمّل مسبقًا فمفيش N+1، والـResource بيطلّع toll_customer_name خلاص.
يعني الفلتر بيبعت طلب مطابق حرفيًا لحالة «بدون فلتر» — عشان كده شكله بيشتغل والنتيجة ما بتتغيّرش، والعدّاد بيفضل بيقول العدد الكلي.
ومن كوميت الميزة نفسها: الديف ضاف الحقل للـinterface بس — جسم list() ما اتلمسش.
| # | المشكلة | الأثر |
|---|---|---|
| 1 | toll_customer_id ما بيتبعتش | الفلتر ميّت تمامًا — ده اللي بلّغت عنه |
| 2 | البحث بيطلب مع كل حرف — بلا تأخير وبلا switchMap | طلب لكل حرف، وردّ قديم ممكن يوصل متأخر ويرسم نتيجة بحث سابق. شاشة الأرصدة بتأخّر ٤٠٠ms — دي الشاذّة |
| 3 | تحميل مزدوج عند الفتح | طلبين بدل واحد؛ شاشة الأرصدة عندها حارس لده والمنتجات لأ |
| 4 | عرض الشجرة بيتجاهل كل الفلاتر | تحوّل للشجرة فتلاقي كل حاجة ظهرت من غير أي إشارة |
| 5 | الطباعة/التصدير على الصفحة الظاهرة بس | «اطبع كل أصناف العميل ده» = بيطبع ٢٥ صف عشوائي |
| 6 | البحث جوّه القوائم بيدوّر في غير المعروض | قايمة العميل بتعرض display_label وبتدوّر في name,name_ar,prefix — تكتب اللي شايفه فما يطلعش. ونفس الحاجة في قايمة التصنيفات |
| 7 | مفيش حفظ للفلاتر | تحديث الصفحة = كل الفلاتر اتصفّرت. ولا لينك بيتنسخ |
| 8 | آلية بحث موازية ميتة لسه في الكود | لو حد ربطها بأي مربع بحث هتتجاهل ٣ فلاتر في صمت وتستبدل الجدول كله. لغم نايم |
| 9 | حراسة القيم بالـ«صدق» مش بالوجود | أي قيمة تساوي صفر بتتشال. مش قابلة للحصول دلوقتي، بس مختلفة عن شاشة الأرصدة اللي بتستخدم != null عمدًا |
| 10 | brand_id مدعوم على السيرفر وغير مستخدَم | فلتر ماركة مجاني لو حبيت |
| الطلب | الموجود | الناقص | الحكم |
|---|---|---|---|
| عمود العميل في الأرصدة | العلاقة موجودة على المنتج · الـResource بتاع المنتجات بيطلّع الاسم خلاص | تحميل مسبق + حقلين في StockBalanceResource + فلتر + إضافة العميل لجملة البحث |
سهل — مع حسم اسم العمود (قرار ١) |
| طباعة الحركات | مفيش أي طباعة على الكارت · نمط الكشوف جاهز ومستخدم في ٥ شاشات | زرار + كتلة @media print + قرار «نطبع إيه بالظبط» |
سهل — نتبع نمط كشف الحساب |
| لينك المصدر المباشر | البيانات كاملة · عمود المصدر موجود · نمط الحلّ المجمّع بلا N+1 موجود على نفس المدخل (كتلة التشغيلات) | مُحلِّل مصدر على السيرفر يقطع الخطوة التانية ويرجّع (نوع · اسم · رقم · وجهة) + تعديل العمود في الواجهة | متوسط — التعقيد في تعدّد الأنواع مش في الإنتاج |
| فلاتر المنتجات | السيرفر بيدعم كل حاجة ومحمّل مسبقًا | سطر واحد في list() — زائد ٩ مشاكل تانية مكتشفة |
الإصلاح الأساسي سطر واحد |
اعتماديات لازم تتحسب:
join خام منفصلة — أي فلتر جديد لازم يتضاف ليها كمان وإلا الإجمالي هيخالف القايمة المفلترة.join يدوي مع إعادة select — لازم تفضل موجودة وإلا التحميل المسبق بيقع.per_page (٢٥ و٥٠ ثابتين) — والواجهة بتبعته وبتعوّض بحلقة صفحات.| الحالة | المعالجة |
|---|---|
| صنف بلا عميل تصنيع (الأغلبية الساحقة) | الخلية شرطة «—». على moonui2 فيه منتجين بس من ١٧١٦٦ عندهم عميل — والصنف اللي ضربته مثال مش منهم. العمود هيبان فاضي هنا؛ البيانات المليانة على prod. |
حركة أمانات (consignment_receipt/return) | رقم المرجع = عميل مش مستند. المُحلِّل لازم يعرض اسم العميل ولا يحاول يفتح مستند. |
حركة إلغاء (*_cancel) | الرقم بيشاور على المستند الأصلي — يتعرض بعلامة «ملغي» ويوصّل للأصل. |
| backflush / staging | أمر الإنتاج مباشرة بخطوة واحدة — المُحلِّل لازم يمسك الحالتين (خطوة وخطوتين). |
| إذن صرف على تاج تشغيل | يعرض اسم التاج، ويوصّل لشاشة التاجات. مايتعرضش كأمر إنتاج — مالوش قيود ولا إنتاج تحت التشغيل. |
| الجرد | ما بيعملش حركة بنفسه — بيظهر كتسوية. أمر الجرد على بُعد ٣ خطوات. أنصح نقف عند التسوية في المرحلة دي. |
| مستند اتمسح (soft delete) | مفيش مفتاح أجنبي على المرجع — المُحلِّل لازم يتحمّل مرجع ميّت ويرجّع الرقم الخام بدل ما يقع. |
| مرجع من شركة تانية | المرجع بالاتفاق مش بيعبّر عن نطاق الشركة — كل حلّ لازم يفلتر بالشركة صراحةً (السابقة الموجودة بتعمل كده في الذاكرة). |
| الطباعة والفترة | الكارت بيفتح على الأسبوع الحالي افتراضيًا والجدول مقسّم ٢٥ صف. الطباعة لازم تطبع كل الحركات المحمّلة والفترة تتكتب في الترويسة — وإلا هتطبع أسبوع وإنت فاكرها كل التاريخ. |
| الاتجاه والطباعة | الصفحة RTL — قوالب الطباعة الموجودة بتتعامل مع ده؛ نتبعها ولا نخترع. |
| N+1 | التحميل المسبق للعميل = استعلام واحد إضافي للصفحة. ومُحلِّل المصدر يتعمل مجمّع للصفحة كلها على نمط كتلة التشغيلات الموجودة على نفس المدخل. |
| التاريخ | الحركة | المخزن | وارد | منصرف | الرصيد | المصدر |
|---|---|---|---|---|---|---|
| 2026-07-28 | صرف | الخام | — | 120 | 1,880 | GDN-000014 |
| 2026-07-29 | صرف | الخام | — | 80 | 1,800 | GDN-000016 |
| 2026-07-30 | وارد | الخام | 500 | — | 2,300 | GRN-000009 |
| التاريخ | الحركة | المخزن | وارد | منصرف | الرصيد | المصدر المباشر ★ | المستند |
|---|---|---|---|---|---|---|---|
| 2026-07-28 | صرف | الخام | — | 120 | 1,880 | أمر إنتاج MO-000016 | GDN-000014 |
| 2026-07-29 | صرف | الخام | — | 80 | 1,800 | أمر إنتاج MO-000028 | GDN-000016 |
| 2026-07-31 | صرف | الخام | — | 40 | 1,760 | تاج تشغيل امر انتاح111 | GDN-000046 |
| 2026-07-30 | وارد | الخام | 500 | — | 2,300 | — | GRN-000009 |
routerLink — تنفع تتفتح في تبويب جديد وتتنسخ
| الكود | الصنف | المخزن | الوحدة | الرصيد | متوسط التكلفة | القيمة |
|---|---|---|---|---|---|---|
| RXP-00015 | Rxobronex Label | الخام | قطعة | 4,000 | 0.850 | 3,400 |
| PRD-17137 | خام بودرة ميلامين | الخام | كجم | 1,760 | 12.400 | 21,824 |
| الكود | الصنف | عميل التصنيع ★ | المخزن | الوحدة | الرصيد | متوسط التكلفة | القيمة |
|---|---|---|---|---|---|---|---|
| RXP-00015 | Rxobronex Label | ميد وان فارما | الخام | قطعة | 4,000 | 0.850 | 3,400 |
| PRD-17137 | خام بودرة ميلامين | — | الخام | كجم | 1,760 | 12.400 | 21,824 |
| التاريخ | الحركة | المصدر المباشر | وارد | منصرف | الرصيد |
|---|---|---|---|---|---|
| 2026-07-28 | صرف | أمر إنتاج MO-000016 | — | 120 | 1,880 |
| 2026-07-31 | صرف | تاج تشغيل — امر انتاح111 | — | 40 | 1,760 |
| WP | النطاق | المستودع | الإثبات |
|---|---|---|---|
| WP1 أولًا |
إصلاح فلتر العميل في المنتجات — تمرير toll_customer_id في list(). أرخص وأوضح مكسب في الخطة. |
FE | أحمر قبل أخضر: إثبات إن الطلب النهاردة بيخرج بلا الوسيط، وبيخرج بيه بعد الإصلاح. |
| WP2 | باقي فلاتر المنتجات — تأخير + switchMap للبحث · منع التحميل المزدوج · filterBy يطابق المعروض · تمرير الفلاتر لعرض الشجرة أو تعطيلها بوضوح · حذف آلية البحث الميتة (اللغم). |
FE | لكل بند: قبل/بعد بعدد الطلبات أو النتيجة. |
| WP3 | مُحلِّل مصدر الحركة (سيرفر) — يرجّع لكل حركة {type, label, name, number, target}، مجمّع للصفحة على نمط كتلة التشغيلات الموجودة، بترجمة عربي/إنجليزي، ويغطي كل الأنواع الـ٢٨ مش الإنتاج بس. لازم يفلتر بالشركة صراحةً ويتحمّل مرجع ميّت. |
BE | اختبارات لكل نوع: أمر إنتاج (خطوتين) · backflush (خطوة) · تاج · أمانة (عميل مش مستند) · إلغاء · مرجع ميّت. |
| WP4 | عمود المصدر المباشر (واجهة) — يعرض النوع والاسم/الرقم ويوصّل بـrouterLink حقيقي (يفتح في تبويب). العمود القديم يفضل للمستند. |
FE | يعتمد على WP3. |
| WP5 | عمود عميل التصنيع في الأرصدة — تحميل مسبق + حقلين في الـResource + فلتر + إضافة العميل لجملة البحث + نفس الفلتر على بطاقة الإجماليات. | BE+FE | اختبار: البحث باسم العميل بيرجّع أصنافه · الإجمالي بيساوي مجموع القايمة المفلترة. |
| WP6 | طباعة كارت الصنف — نمط كشف الحساب: زرار + @media print. الترويسة تكتب الصنف والفترة والمخزن، والطباعة تشمل كل الحركات المحمّلة مش الصفحة الظاهرة، وتحت إجماليات وارد/منصرف/ختامي. |
FE | معاينة طباعة على فترة فيها أكتر من ٢٥ حركة. |
الترتيب مقصود: WP1 لوحده بيحلّ الشكوى الرابعة في جلسة قصيرة. وWP3 هو أثقل حزمة وكل قيمة الطلب التالت متوقفة عليها، فالأحسن تيجي بعد المكاسب السريعة.
أرصدة المخزون فيها عدسة «المالك» = مالك الكمية (بضاعة أمانة). والمطلوب مالك تعريف الصنف (عميل التصنيع). مفهومين مختلفين وممكن يختلفوا في نفس الصف.
توصيتي: عمودين وفلترين منفصلين بأسماء صريحة — «المالك (أمانة)» للموجود، و«عميل التصنيع» للجديد. دمجهم في مفهوم واحد هيخلّي أمين المخزن يقرا رقم على إنه حاجة وهو حاجة تانية، وده خطأ أغلى من عمود زيادة.
توصيتي: عمود جديد «المصدر المباشر» والقديم يفضل «المستند». إنت قلت «مش على إذن الصرف» — بس إذن الصرف نفسه معلومة مطلوبة أحيانًا (رقمه هو اللي على الورق). عمود زيادة أرخص من إنك تفقد وصولك لرقم الإذن. ولو تفضّل استبدال العمود خالص، ده قرارك وأنفّذه.
توصيتي: نبدأ بالطباعة (نمط كشف الحساب) في WP6. هي اللي طلبتها بالظبط، ونمطها موجود في ٥ شاشات. وتصدير Excel له آلية جاهزة في النظام لو حبيت نضيفه بعدين — بس ما نعملش الاتنين في نفس الحزمة.
توصيتي: لأ في المرحلة دي. الجرد ما بيعملش حركة بنفسه، بيظهر كتسوية. نوصّل للتسوية ونقف. ملاحقة الخطوة التالتة تكلفة زيادة على حالة نادرة — لو طلعت مهمة نعملها لوحدها.
شاشة الأرصدة مش من الـ١٤ شاشة اللي عندها سحب/تحجيم الأعمدة اللي خلّصناها الأسبوع ده.
توصيتي: مش في الطلب ده. إنت طلبت عمود وبحث وفلتر — والتحكم في الأعمدة شغل مستقل بحجمه. لو عايزه، يتحط في الباك-لوج جنب باقي البنود.
التلات طلبات الأولانية كلهم البيانات بتاعتهم موجودة فعلًا — العلاقة على المنتج، وسلسلة المصدر في الجداول، ونمط الطباعة في ٥ شاشات. مفيش حاجة محتاجة جدول جديد ولا عمود جديد في قاعدة البيانات.
وأثبتنا الطلب التالت بالبيانات نفسها: الصنف اللي ضربته مثال عنده ٧ حركات صرف — السبعة كلهم على أوامر إنتاج. الشاشة بتوريك ٧ أذون صرف بدل كده.
والرابع عيب سببه سطر واحد: الشاشة بتجهّز toll_customer_id والخدمة اللي بتبني الطلب ما بتقراهوش — فالفلتر بيبعت طلب مطابق لحالة «بدون فلتر». ولمّا فتحنا الشاشة لقينا ٩ مشاكل تانية في نفس الفلاتر، أخطرهم بحث بيطلب مع كل حرف من غير ترتيب للردود، وآلية بحث ميتة لو اترجعت هتلغي ٣ فلاتر في صمت.
وقرار واحد لازم تحسمه قبل ما نبدأ: شاشة الأرصدة فيها كلمة «مالك» مستخدمة خلاص لمعنى تاني (مالك بضاعة الأمانة). لو حطّينا «العميل» جنبها من غير تسمية صريحة، هنخلق التباس في شاشة أمين المخزن.
تحليل المرحلة ١ — مفيش كود ولا migration اتكتب · مسارَي تتبّع متوازيين + قراءة مباشرة لقاعدة البيانات · ٦ أغسطس ٢٠٢٦ · moonui2 · hazemdev2
مستنّي موافقتك على النطاق والترتيب والقرار الأول قبل أي تنفيذ.