Skip to content
English

دليل Jasper Reports الشامل لنظام نما ​

كل تقرير وكل نموذج طباعة يخرج من نظام نما هو تصميم JasperReports: ملف تصميم خلفه استعلام SQL وأمامه مجموعة من المدخلات يملؤها المستخدم. وهذه الصفحة مكتوبة لمن يبني هذه التصاميم ويصونها — للمنفّذ الذي عليه أن يجعل الفاتورة المطبوعة مطابقة لترويسة العميل، ولموظف الدعم الذي عليه أن يعرف لماذا لم تطابقها.

وهناك طريقان للوصول إلى تقرير. أداة إنشاء تقرير (Report Wizard) تبني لك التصميم من جدول رئيسي وقائمة حقول وبضعة جداول إعداد، ولا تفتح أنت ملف التصميم أصلاً. أو ترسم التصميم بنفسك في Jaspersoft Studio وترفعه، فتملك تحكماً كاملاً في كل حزمة وكل تعبير وكل صفحة. الأداة تغطي معظم احتياجات التقارير اليومية؛ أما التصميم المرسوم يدوياً فهو ما تلجأ إليه حين يجب أن يخرج المستند بشكل بعينه لا غير. وكلا الطريقين ينتهي إلى تعريف تقرير من النوع نفسه، فمعظم ما يلي ينطبق أياً كان الطريق الذي سلكته.

وحيثما رأيت تعبير Groovy في هذه الصفحة فهو استدعاء لمساعد التقارير الذي يتيحه نما داخل كل تقرير. أما الفهرس الكامل لتلك الاستدعاءات — الأسماء، والتواريخ، والأسعار، والروابط، والأمان، ورموز QR، ومدخلات $P{} الجاهزة التي يتلقاها كل تقرير — ففي مرجع تعبيرات NamaRep. هذه الصفحة تشرح المهام، وتلك الصفحة تسرد الاستدعاءات.

أين تسكن التقارير ​

كل ما يتعلق بتعريفات التقارير تجده تحت إدارة النظام ← التقارير:

الشاشةما تحتويه
مجموعة تقاريرالتجميعات التي تحدد تحت أي قائمة يظهر التقرير، ومن ثَمّ من الذي سيعثر عليه.
تعريف تقريرالتقرير نفسه — ملف التصميم المرفوع، وكوده، ومجموعته، وتقاريره الفرعية وموارده. هنا يُسجَّل التصميم المرسوم يدوياً حتى يستطيع المستخدمون تشغيله.
أداة إنشاء تقريرطريق «ابنِ لي إياه»: اختر جدولاً، واختر حقولاً، واحفظ، فيُولَّد لك تعريف تقرير.
أداة إنشاء نموذج طباعةالفكرة نفسها، لكن لنماذج الطباعة بدل تقارير القوائم.
مصدر بياناتكتلة استعلام قابلة لإعادة الاستخدام يسحب منها تقرير الأداة أعمدة إضافية — انظر مصادر البيانات.
كيان افتراضيجملة SQL محفوظة تتصرف كأنها جدول — انظر الكيانات الافتراضية.
Report Styleتنسيقات مسمّاة تتشاركها التقارير، بدل أن يحمل كل تصميم خطوطه وحدوده الخاصة.
قائمة تقارير مخصصةقائمة تقارير تبنيها بيدك، حين لا يكون الترتيب المبني على المجموعات هو ما تريد.

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

البناء بالأداة بدلاً من ذلك

إن لم تكن ستَرسم التصميم بيدك فابدأ من دليل أداة إنشاء التقارير — فهو يمشي بك خطوة خطوة في بناء تقرير من الشاشة، حقلاً حقلاً. ثم عُد إلى هنا لما لا تغطيه الأداة: المدخلات المكتوبة يدوياً، والتقارير الفرعية، وأحجام الصفحات، والخطوط، وقيود الأمان.

أين يظهر التقرير في القائمة ​

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

من يحق له تشغيل التقرير ​

«لماذا لا يرى هذا التقرير إلا شخص واحد؟» و«اقصر هذا التقرير على فريق المالية» سؤال واحد يُطرح من طرفين. وتعريف التقرير يجيب عنه بأربعة فحوص، تُطبَّق بهذا الترتيب كلما شغّل أحد التقرير:

  1. جدول صلاحيات التقرير نفسه. في تبويب الصلاحيات جدول سطوره من عمودين: مطبق على (مستخدم، أو ملف صلاحيات، أو مجموعة مستخدمين) وسماح - منع. والسطر الذي يسمّي المستخدم نفسه يتقدم على السطر الذي يسمّي ملف صلاحياته أو مجموعته. فإن طابقه سطر سماح شغّل التقرير فوراً وتُخطّيت بقية الفحوص. وإن طابقه سطر منع رُفض. وإن كانت سطور الجدول كلها سماح صار الجدول قائمة مدعوين: كل من لا يسمّيه الجدول يُرفض. ولهذا قد يغلق السماح لمستخدم واحد التقريرَ على الجميع — فأضف الباقين، أو أضف سطر منع لأحدهم حتى لا يبقى الجدول سماحاً خالصاً. والجدول نفسه يخفي التقرير من القائمة ومن قائمة التقارير عمّن يستبعدهم. أما من كان ملف صلاحياته بصلاحيات كاملة فلا يوقفه هذا الجدول أبداً.
  2. المحددات. التقرير المصنف تحت شركة أو فرع أو قطاع أو إدارة أو مجموعة تحليلية لا يعمل إلا لمن يمنحه دخولُه ذلك المحدد.
  3. صلاحية المطالعة. إن حمل التقرير صلاحية مطالعة فلا بد أن يمنحها ملفُ صلاحيات المستخدم. والتقارير النظامية تحمل واحدة تلقائياً — انظر قسم «التقارير النظامية» أدناه.
  4. نوع الصلاحيات والنوع المطبق عليها الصلاحية. هذان يربطان التقرير بصلاحيات شاشة عادية. فمع قياسي يحتاج المستخدم إلى الصلاحية القياسية (العرض بقائمة إن تُركت فارغة) على النوع المطبق عليها الصلاحية: فتقرير أرصدة العملاء الذي نوعه المطبق عليه هو العميل يعمل لكل من يحق له فتح قائمة العملاء. ومع مخصص يحتاج بدلاً من ذلك إلى الصلاحية المخصصة على ذلك النوع. أما بدون فيتخطى هذا الفحص.

والرفض الآتي من الجدول نصه: «لا تملك الصلاحيه لتشغيل هذ التقرير {0}» وخيار الإعدادات العامة عرض التقرير اذا كان المستخدم/المجموعة/ملف الصلاحية مسموح له من خلال جدول الصلاحيات بغض النظر عن الصلاحيات في تبويب التقارير والطباعة يجعل سطر سماح كافياً لعرض التقرير في القائمة حتى حيث تخفيه الفلاتر الأخرى.

وحقلان يبدوان من الصلاحيات وليسا منها. للمستخدم لا يُستعمل إلا عند اختيار النموذج المطبوع الذي يُطبع. ولا يقرر من يحق له تشغيل تقرير من القائمة. والسماح بتشغيل التقرير بدون تسجيل دخول يقرر هل يجوز فتح التقرير عبر رابط بلا تسجيل دخول (انظر قسم «مشاركة تقرير مع شخص بلا حساب»). ومثل هذا الرابط يتخطى كل الفحوص السابقة، وبدون هذه العلامة يُرفض برسالة إنجليزية لا ترجمة عربية لها: You can not run this report without authentication

وللتحكم فيمن يرى أي الصفوف داخل التقرير انظر قسم «تحديد ما يراه كل مستخدم».

جدول صلاحيات تعريف تقرير: سطر سماح لملف صلاحيات وسطر منع لمستخدم

حقلا مكافئ الصلاحية ونوع الصلاحية في تعريف التقرير

التقارير النظامية ​

التقرير النظامي واحد من التقارير ونماذج الطباعة التي تشحنها «نما» جاهزة. وتكون خانة نظامي في تبويب متقدم معلَّمة فيه. وهذه التقارير مصنفة تحت مجموعات مثل System Reports - Accounting. ويأخذ كل منها صلاحية مجموعته صلاحيةَ مطالعة، فلا يشغّله المستخدم إلا إذا حمل ملف صلاحياته تلك الصلاحية، أو كانت صلاحياته كاملة. أما علامة مطالعة التقارير النظامية في ملف الصلاحيات فلا تمنح إلا صلاحية التقارير النظامية العامة، وهي لا تغطي إلا التقارير النظامية المصنفة بلا مجموعة. وهذه العلامة مخفية عن شاشة ملف الصلاحيات عن قصد — وتجد في حقول مخفية عن قصد كيف تُظهرها بـ«تعديل شاشة».

من أين تأتي. يُنزِّل الإجراء Update System Reports الفهرس الحالي من خادم «نما»، فلا بد أن يكون للخادم اتصال بالإنترنت. وهذا الإجراء يظهر باسمه الإنجليزي حتى في الواجهة العربية. تجده في صفحة الأدوات المساعدة تحت System Update، أو في قائمة المزيد في شاشة تعريف التقرير بعد ضغط Ctrl + Alt + X (انظر اختصارات لوحة المفاتيح). ويطلب منك التأكيد قبل أن يعمل. ولكل موديول مثبَّت عندك ينشئ التقارير الناقصة. ويطمس أيضاً التقارير الموجودة: تصميمها، ونوعها، ونوع صلاحياتها، وأسماءها، ومجموعتها. أما سطور الصلاحيات الخاصة بك، وللمستخدم، ومجموعات تقارير فتبقى كما هي. وبعد ذلك يحذف التقارير النظامية التي لم يعد موديولها مثبتاً أو التي سحبتها «نما» من الفهرس، ويزيل المجموعات النظامية التي بقيت فارغة. والتشغيل نفسه يحدّث كذلك التنبيهات واللوحات والتلميحات والمهام المجدولة الجاهزة.

تعديل تقرير نظامي. تصميم التقرير النظامي مجمَّد، وحفظ أي تغيير فيه يُرفض — انظر قسم «رسائل قد تظهر لك». فخذ منه نسخة مماثلة بدلاً من ذلك: النسخة تقرير من عندك، وخانة نظامي فيها غير معلَّمة. وفي نماذج الطباعة يتقدم النموذج غير النظامي دائماً على النظامي (انظر أي نموذج طباعة سيخرج). وأي تقرير من عندك يحمل كود تقرير نظامي يطمسه التحديث التالي ويعيده تقريراً نظامياً. ويحدث ذلك مثلاً لتقرير حُوِّل بالخيار السماح بتغيير التقرير النظامي الي غير نظامي. ولا يُستثنى من ذلك إلا حين يكون خيار عدم تحديث تقرير غير نظامي ولكن له كود تقرير نظامي مفعّلاً في الإعدادات العامة.

حذف تقرير نظامي. الحذف مسموح، لكنه لا يدوم: فالتحديث التالي ينشئ التقرير من جديد. فإن أردت إبعاد تقرير جاهز عن بعض المستخدمين فأضف سطور منع إلى جدول صلاحياته بدلاً من حذفه.

كيف يعرف النموذج أي سجل يطبع ​

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

نموذج يُطبع من سجل واحد ​

عرّف مدخلاً اسمه id، ثم رشّح الجدول الرئيسي به:

xml
<parameter name="id" class="java.lang.Object">
    <property name="entityType" value="SalesInvoice"/>
</parameter>
sql
select inv.code, inv.valueDate, cust.name1
from SalesInvoice inv
    left join Customer cust on cust.id = inv.customer_id
where inv.id = $P{id}

هذا هو العقد كله، وهو ما تفعله كل النماذج التي تأتي مع المنتج. أما الخاصية entityType فتسمّي نوع السجل الذي تشير إليه القيمة، تماماً كما تفعل في حقل مرجع يُسأل عنه المستخدم.

النموذج يتسلّم قيمة واحدة لا غير، والربط فيها بالموضع

نموذج الطباعة لا يعرض على المستخدم أي سؤال — فقد ضغط طباعة على سجل مفتوح أمامه وانتهى الأمر. والقيمة الوحيدة التي يتسلّمها هي معرّف السجل، ويكتبها نما في أول مدخل يطرحه التصميم بوصفه سؤالاً. والاسم ليس هو الحَكَم: فبعض النماذج التي تأتي مع المنتج تسمّي هذا المدخل Id أو ID لا id، وتطبع على أكمل وجه.

ولذلك ينبغي أن يكون مدخل المعرّف هو السؤال المفتوح الوحيد في التصميم. وأي مدخل آخر يحتاجه التصميم — قيمة يحسبها، أو راية يشغّل بها شيئاً — يجب إغلاقه، وإلا التقط هو معرّف السجل فرسم النموذج سجلاً خاطئاً دون أن تظهر رسالة تدل على السبب. وثلاثة أمور يُغلق بها المدخل، ويكفي واحد منها: isForPrompting="false" على المدخل، أو <property name="ignore" value="true"/> بداخله، أو خاصية src تسمّي المدخل الذي يأخذ عنه قيمته. أما مدخلات النظام الجاهزة — loginUserName1 و loginLegalEntityLogo وسائرها — فيتعرّف عليها النظام بأسمائها ويتخطاها كذلك، فلها أن تقع في أي موضع من التصميم.

نموذج يُطبع من شاشة قائمة ​

اجعل نوع التقرير في التعريف نموذج قائمة (List) فيصير النموذج معروضاً من شاشة القائمة بدلاً من ذلك، حيث يؤشّر المستخدم عدة صفوف ثم يضغط طباعة — انظر طباعة قائمة بدل سجل.

النموذج هنا يتسلّم كل السجلات المختارة لا سجلاً واحداً، فيصل المدخل نفسه بوصفه قائمة معرّفات. ولذلك يتغير تعريفه ويتغير الاستعلام الذي يقرؤه معاً:

xml
<parameter name="id" class="java.util.List">
    <property name="entityType" value="SalesInvoice"/>
</parameter>
sql
where $X{IN, inv.id, id}

و $X{IN, column, parameter} هي دالة الشرط التي يقدّمها Jasper لهذه الحالة بعينها: تكتب لك in (?, ?, ?) بعدد ما أشّر عليه المستخدم من صفوف. ولا سبيل إلى كتابة تلك القائمة بيدك، لأن عدد الصفوف التي سيختارها المستخدم غير معلوم وقت رسم التصميم.

والطريقان يختلفان كذلك في طريقة الربط، وهو ما يحسن معرفته قبل أن تنسخ نموذجاً يعمل: الطباعة من القائمة تربط بـالاسم أياً كان موضع المدخل في التصميم، ويجب أن يكون الاسم id بحروفه هذه. أما الصيغتان Id و ID اللتان يتسامح معهما نموذج السجل الواحد فلا تتسلّمان هنا شيئاً.

= $P{id} مع قائمة يفشل

هذا أشيع سبب لنموذج يُطبع من السجل على أكمل وجه ثم يفشل من شاشة القائمة. فـ $P{} يربط قيمة واحدة، وقائمة من خمسة معرّفات ليس لها قيمة واحدة تُربط، فيسقط الاستعلام عند قاعدة البيانات بخطأ في النوع لا يذكر شيئاً عن القوائم. غيّر النوع إلى java.util.List و غيّر المقارنة إلى $X{IN, …} معاً — فأي تغيير منهما وحده يبقي العطل قائماً.

الأداة تقلب الاثنين نيابةً عنك

خيار طباعة كقائمة في أداة إنشاء نموذج طباعة هو هذا المفتاح نفسه. إن تركته مطفأً رشّح النموذج المولَّد معرّفاً واحداً، وإن أشّرته ولّد مدخل قائمة وشرط IN. والذي تكتبه الأداة في حالة السجل الواحد هو $X{EQUAL, column, parameter} — وهي صياغة column = $P{parameter} نفسها بدالة شرط. وكلتا الصياغتين مقبولة في تصميم ترسمه بنفسك.

وضع شعار الشركة على التقرير ​

الشعار أول ما يطلبه الجميع، ولا يحتاج استعلاماً ولا إعداداً — فالنظام يسلّمه لكل تقرير يطلبه.

  1. عرّف مدخلاً باسم loginLegalEntityLogo من النوع java.lang.Object أو java.io.InputStream.
  2. أضف عنصر صورة إلى التصميم.
  3. اجعل تعبير الصورة هو $P{loginLegalEntityLogo}.

هذا كل شيء. فعند تشغيل التقرير يصل إلى ذلك المدخل شعارُ الشركة التي سجّل المستخدم دخوله بها. وهناك أربعة شعارات إضافية بالطريقة نفسها — من loginLegalEntityLogo2 إلى loginLegalEntityLogo5 — وبها تضع المنشآت التي تحتاج علامة ثانية، كشهادة جودة أو شعار امتياز، تلك العلامة على الصفحة.

أما أيّ شركة يؤخذ شعارها حين يكون المستند تابعاً لشركة غير شركة المستخدم فيُحدَّد في الإعداد العام، في تبويب التقارير والطباعة.

أي صورة أو مرفق آخر ​

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

groovy
NamaRep.getFile($F{attachmentId})
// أو
NamaRep.getAttachment($F{attachmentId})

التقارير الفرعية والموارد الإضافية ​

يستطيع التقرير أن يضمّن تقريراً آخر داخله. وبهذا يطبع إذنُ التسليم أسطرَه من تصميم وشروطَه وأحكامَه من تصميم آخر، ويطبع كشفُ الحساب كتلة ملخّص مختلفة لكل فرع.

سجّل التقرير الفرعي على تعريف التقرير، ثم اربطه بالتصميم:

  1. أعطِ التقرير الفرعي معرّف تقرير فرعي على تعريف التقرير. وهو نص حر — أنت من يختاره.
  2. في التصميم، عرّف مدخلاً بهذا الاسم بالضبط.
  3. اجعل فئة ذلك المدخل java.lang.Object.
  4. استخدم المدخل تعبيراً للتقرير الفرعي.

عرّفه java.lang.Object لا java.io.InputStream

ما يضعه النظام في ذلك المدخل تقريرٌ مُصرَّف جاهز، لا الملف الخام. والمدخل المعرَّف java.io.InputStream يفشل عند التعبئة برسالة خطأ في الأنواع لا تدل على السبب بشيء. وكل تقرير يأتي مع المنتج يعرّف مدخلات تقاريره الفرعية java.lang.Object.

والموارد الإضافية — صورة يستخدمها التصميم، أو ملف يحتاجه — تعمل بالطريقة نفسها: سجّل المورد على تعريف التقرير، وعرّف مدخلاً بالاسم نفسه وبالفئة java.lang.Object، وأشر إليه حيث يحتاجه التصميم.

تعديل التصميم في مكانه ​

معظم التعديلات على تصميم مرسوم يدوياً تصل بالطريقة التي بُني بها التصميم: تصلح التصميم في Jaspersoft Studio، وتصدّره، وترفع الملف الجديد في ملف التقرير. لكن التعريف يحتفظ أيضاً بنسخة نصية من ذلك الملف، وفي التصحيح الصغير — اسم عمود مكتوب خطأً، أو عنوان، أو عرض — يكون تعديل النص أسرع من رحلة ذهاب وعودة إلى الاستوديو.

افتح صفحة متقدم. في أسفلها حقلان:

الحقلما يحمله
محتوي ملف التقرير (Report File Content)نص ملف التصميم المرفوع. يُعاد ملؤه من الملف مع كل حفظ للتعريف، فما تقرؤه هنا هو دائماً ما يعمل منه التقرير.
نسخ المحتوي النصي الي الملف (Copy Content To Report)تعليمة للحفظ التالي: اكتب النص من جديد في الملف.

عدّل النص واحفظ، ويتولى النظام الباقي. يُعاد كتابة الملف من النص، ويُعاد تصريف التقرير من الملف، ثم يُقرأ النص من الملف مرة أخرى فيعودان متطابقين. وحقل نسخ المحتوي النصي الي الملف يضع العلامة بنفسه حين يجد الحفظ أن النص يختلف عما كان محفوظاً، فلا تحتاج عادةً إلى لمسه، وستجده قد أُزيلت علامته بعد الحفظ: فهو يصف إجراءً يُنفَّذ مرة واحدة لا إعداداً دائماً.

وينطبق الأمر نفسه حين يصل التعديل عبر استيراد أو تكامل لا من الشاشة. فالتعريف المحفوظ الذي يختلف محتواه عن النسخة المخزنة يُعامَل تماماً كما لو كنت كتبت التعديل بيدك. والتعريف الجديد كلياً الذي أُنشئ من نص ملصوق دون رفع أي ملف يُعامَل بالطريقة نفسها: فأول حفظ ينشئ الملف من النص ويسمّيه باسم كود التقرير (أو باسمه إن لم يكن في الكود حروف صالحة) بامتداد .jrxml، فيمكن إنشاء تعريف كامل من النص وحده.

وتحمل أسطر التقارير الفرعية والموارد الزوج نفسه من الحقول، وإن كانت الجداول على الشاشة لا تعرض إلا المعرّف والملف. فبالنسبة لأداة استيراد أو لأدوات MCP أو لواجهة API، يتيح كل سطر في التقارير الفرعية وكل سطر في الموارد الحقلين reportContent وreportFileName إلى جانب ملفه: سطر التقرير الفرعي يحمل نص تصميمه كما هو، وسطر المورد يحمل بايتات الملف مرمَّزة Base64. وحين توضع علامة نسخ المحتوي النصي الي الملف يكتب حفظ واحد محتوى الرأس ومحتوى كل سطر في ملفاتها معاً. والعلامة الآلية تراقب محتوى الرأس وأي سطر له محتوى ولا ملف له بعد، فالسطر المنشأ من نص يحصل على ملفه مع أول حفظ. أما الاستيراد الذي يغيّر نص سطر تقرير فرعي أو مورد موجود من قبل فيجب أن يضبط copyContentToReport صراحةً وإلا بقي ملف ذلك السطر كما كان.

ما الذي يعمل فعلاً

لا يعمل التقرير أبداً من الحقل النصي. فكل حفظ يعيد بناء نسخة مصرَّفة من ملف التقرير، وتلك النسخة المصرَّفة هي ما يشغّله المستخدمون. الحقل النصي طريق إلى الملف لا مصدر ثانٍ، ولهذا يتطابقان دائماً بعد الحفظ.

سؤال المستخدم: مدخلات التقرير ​

المدخلات هي ما يملؤه المستخدم قبل تشغيل التقرير، وهي نفسها القيم التي يقرؤها استعلام SQL. يُعرَّف المدخل مرة واحدة في التصميم ويؤدي المهمتين معاً.

وبعد ظهور التقرير على الشاشة، يعيد زر إعادة تشغيل التقرير بنفس المدخلات (أيقونة التشغيل) في شريط أدوات العارض — أو مفتاح F5 — تشغيلَه بالقيم نفسها، فتظهر الأرقام التي تغيرت في الأثناء دون إعادة ملء المدخلات.

مدخلات التحديد المتعدد (القوائم) ​

أحياناً لا تكفي قيمة واحدة — فالمستخدم يريد خمسة موظفين، أو كل الفروع إلا اثنين. ويستطيع المدخل أن يقبل قائمة:

  1. اضبط الخاصية list = true.
  2. ولكل ما ليس مرجعاً إلى سجل، اضبط كذلك listType (مثلاً java.util.Date).
  3. ولطباعة ما اختاره المستخدم، عرّف مدخلات نصية مرافقة يملؤها النظام تلقائياً:
    • <parameterName>_csv — القيم المترجمة مفصولة بفواصل
    • <parameterName>_codecsv — الأكواد
    • <parameterName>_name1csv — الأسماء العربية
    • <parameterName>_name2csv — الأسماء الإنجليزية
  4. وdoNotAutoShowList = true يمنع سرد القيم المختارة تلقائياً على التقرير.
  5. وlistDisplayType يحدد الأداة التي يراها المستخدم:
    • Default — إدخال التحديد المتعدد القياسي، وهو المستخدَم عند حذف الخاصية
    • Dropdown — تظهر القيم المختارة شرائحَ قابلة للإزالة داخل الإدخال، وتُفتح قائمة الخيارات الكاملة في قائمة منسدلة قابلة للبحث. وهو الخيار الصحيح حين تكون مجموعة القيم كبيرة
    • Chips — تظهر كل القيم المسموحة شرائحَ قابلة للنقر، ويُبدَّل التحديد بالنقر. وهو الخيار الصحيح لعدد قليل من الخيارات تريدها ظاهرة دون فتح شيء
xml
<parameter name="MultiEmployee" class="java.util.List">
    <property name="entityType" value="Employee"/>
    <property name="list" value="true"/>
    <property name="listDisplayType" value="Chips"/>
</parameter>
مثال أوسع — مدخلا قائمة
xml
<!-- قائمة سجلات -->
<parameter name="MultiEmployee" class="java.util.List">
    <property name="entityType" value="Employee"/>
    <property name="arabic" value="الموظفين"/>
    <property name="english" value="Employees"/>
    <property name="property" value="code"/>
    <property name="list" value="true"/>
    <property name="doNotAutoShowList" value="false"/>
</parameter>
<parameter name="MultiEmployee_csv" class="java.lang.String" isForPrompting="false"/>

<!-- قائمة قيم عادية -->
<parameter name="MultiDate" class="java.util.Date">
    <property name="english" value="Dates"/>
    <property name="arabic" value="التواريخ"/>
    <property name="defaultValue" value="$monthStart()"/>
    <property name="list" value="true"/>
    <property name="listType" value="java.util.Date"/>
</parameter>
<parameter name="MultiDate_csv" class="java.lang.String" isForPrompting="false"/>

المهمة المجدولة ليس فيها مستخدم يختار القيم، فتمرّر لمدخل القائمة سجل قيم متعددة لمدخل تقرير محفوظًا بدلًا من ذلك.

مدخلات نطاق التاريخ ​

مدخلا «من تاريخ» و«إلى تاريخ» منفصلين يعملان بلا مشكلة، لكنهما يشغلان صفَّين ولا شيء على الشاشة يخبر المستخدم أنهما مرتبطان. وخاصية showAsDateRange تعطي المستخدم منتقيَ نطاق واحداً بينما يحتفظ الاستعلام بمدخلَي التاريخ الحقيقيين اللذين يحتاجهما.

تتعاون ثلاثة مدخلات:

  1. مدخل تحكّم — نصي، بخاصية showAsDateRange = true. هذا ما يراه المستخدم، ولا يحمل قيمة خاصة به.
  2. مدخل «من تاريخ» بخاصية isForPrompting="false"، يسمّيه مدخل التحكّم في fromDateId.
  3. مدخل «إلى تاريخ» بخاصية isForPrompting="false"، يسمّيه مدخل التحكّم في toDateId.

وعندما يختار المستخدم نطاقاً تُكتب التواريخ المختارة في المدخلين الأساسيين. ومن وجهة نظر الاستعلام لم يتغير شيء — فأنت تشير إلى $P{FromValueDate} و$P{ToValueDate} تماماً كأي مدخل تاريخ آخر.

xml
<parameter name="FromValueDate" class="java.util.Date" isForPrompting="false">
    <property name="arabic" value="من التاريخ الفعلي"/>
    <property name="english" value="From Value Date"/>
</parameter>

<parameter name="ToValueDate" class="java.util.Date" isForPrompting="false">
    <property name="arabic" value="إلى التاريخ الفعلي"/>
    <property name="english" value="To Value Date"/>
</parameter>

<parameter name="ValueDate" class="java.lang.String">
    <property name="showAsDateRange" value="true"/>
    <property name="fromDateId" value="FromValueDate"/>
    <property name="toDateId" value="ToValueDate"/>
    <property name="arabic" value="التاريخ الفعلي"/>
    <property name="english" value="Value Date"/>
</parameter>

ثم في الاستعلام:

sql
WHERE valueDate >= $P{FromValueDate}
  AND valueDate <= $P{ToValueDate}

أو بصيغة between:

sql
where $X{[BETWEEN],valueDate,FromValueDate,ToValueDate}

ولا يظهر المنتقي ما لم تتحقق ثلاثة أمور: أن يكون مدخل التحكّم من النوع java.lang.String، وأن يضبط مدخلا التاريخ isForPrompting="false" حتى لا يظهرا مطالبتين منفصلتين بجانب المنتقي، وأن تطابق قيمتا fromDateId وtoDateId اسمَي المدخلين بالضبط.

تقسيم المدخلات إلى مجموعات ​

تقرير فيه خمسة عشر مدخلاً يصير جداراً من الخانات، وعلى المستخدم أن يقرأها كلها ليجد الاثنين اللذين يهمّانه. والمجموعات تتيح لك أن توزّع المدخلات على أقسام لها عناوين — تواريخ، العميل، الأصناف — كما توزّع شاشة التعديل حقولها، ويمكن طيّ كل قسم حتى يحتاجه المستخدم.

المجموعة مدخل قائم بذاته لا يسأل عن شيء. يحمل paramGroup = true وعنواناً في arabic وenglish، ويجب أن يضبط isForPrompting="false". والمدخلات التي تنتمي إليه تذكر اسمه في خاصيتها paramGroupId:

xml
<parameter name="grpDates" class="java.lang.String" isForPrompting="false">
    <property name="paramGroup" value="true"/>
    <property name="arabic" value="تواريخ"/>
    <property name="english" value="Dates"/>
</parameter>

<parameter name="ValueDate" class="java.lang.String">
    <property name="paramGroupId" value="grpDates"/>
    <property name="showAsDateRange" value="true"/>
    <property name="fromDateId" value="FromValueDate"/>
    <property name="toDateId" value="ToValueDate"/>
    <property name="arabic" value="التاريخ الفعلي"/>
    <property name="english" value="Value Date"/>
</parameter>

<parameter name="grpCustomer" class="java.lang.String" isForPrompting="false">
    <property name="paramGroup" value="true"/>
    <property name="arabic" value="العميل"/>
    <property name="english" value="Customer"/>
    <property name="collapsed" value="true"/>
</parameter>

<parameter name="Customer" class="java.lang.String">
    <property name="paramGroupId" value="grpCustomer"/>
    <property name="entityType" value="Customer"/>
    <property name="arabic" value="العميل"/>
    <property name="english" value="Customer"/>
</parameter>

وهكذا ترتّبها شاشة التشغيل:

  • الأقسام تتبع ترتيب مدخلات المجموعات في التصميم، وداخل القسم تحتفظ المدخلات بترتيبها.
  • الانتماء صريح. لا ينضم المدخل إلى مجموعة إلا عبر paramGroupId، ولا يهمّ موضعه في الملف. والمدخلات التي لا تذكر مجموعة تُعرض بعد كل الأقسام كما كانت.
  • collapsed = true يفتح القسم مطوياً، فلا يظهر منه إلا عنوانه حتى يضغط عليه المستخدم. وإن تركتها فُتح القسم مفروداً.
  • القسم الذي كل مدخلاته مخفية لا يُعرض أصلاً، فلا يظهر عنوان فارغ.

ولا ترسم المجموعات إلا الواجهة الجديدة. أما الواجهة القديمة فتتجاهلها وتسرد المدخلات كما كانت دائماً، فإضافة مجموعات إلى تقرير لا تفسده هناك.

ويرفض حفظ تعريف التقرير تصميماً تُرك فيه مدخل مجموعة كمطالبة، أو تذكر فيه خاصية paramGroupId لمدخل شيئاً ليس مدخل مجموعة — انظر الرسائل في آخر هذه الصفحة.

تذكّر آخر إجابة للمستخدم ​

بعض التقارير تُشغَّل بالطريقة نفسها كل صباح: الفرع نفسه، والمخزن نفسه، ومجموعة العملاء نفسها. وخاصية rememberLastValue = true على مدخل تجعل شاشة التشغيل تفتح على ما اختاره المستخدم في المرة السابقة بدل القيمة الافتراضية في التصميم.

xml
<parameter name="Warehouse" class="java.lang.String">
    <property name="entityType" value="Warehouse"/>
    <property name="rememberLastValue" value="true"/>
    <property name="arabic" value="المخزن"/>
    <property name="english" value="Warehouse"/>
</parameter>

تُسجَّل الإجابة حين يشغّل المستخدم التقرير، ومرة أخرى حين يعدّل المدخلات من نتيجة التقرير ويطبّقها. وتُحفظ لكل مستخدم ولكل تقرير ولكل مدخل على حدة. وهذه، كالمجموعات، ميزة في الواجهة الجديدة؛ والواجهة القديمة تتجاهل الخاصية.

أي قيمة تغلب حين تُفتح شاشة التشغيل:

  1. القيمة الممرَّرة في رابط التقرير تغلب دائماً.
  2. وإلا فالإجابة المتذكَّرة.
  3. وإلا فقيمة defaultValue في التصميم.

ويترتب على ذلك بضعة أمور:

  • التاريخ المتذكَّر تاريخ ثابت. إن كانت القيمة الافتراضية للمدخل $monthStart() واختار المستخدم 1 مارس، فستفتح الشاشة على 1 مارس في الشهر التالي أيضاً — فالقيمة المتحرّكة لا تعمل ما دامت هناك إجابة متذكَّرة. أما الاختيار الجاهز في منتقي نطاق التاريخ، مثل هذا الشهر، فيُتذكَّر بوصفه الاختيار نفسه، فيظل يتحرّك.
  • إفراغ المدخل وتشغيل التقرير ينسيه. لا يُتذكَّر إلا ما مُلئ، فالمدخل الذي أُفرغ يعود إلى قيمته الافتراضية في المرة التالية.
  • الإجابة تعيش في المتصفح. تُحفظ على جهاز المستخدم نفسه، في المتصفح الذي شغّل منه التقرير. فمتصفح آخر أو جهاز آخر يبدأ من القيم الافتراضية، ومسح بيانات الموقع من المتصفح ينسي كل الإجابات المتذكَّرة.

مرجع خصائص المدخلات ​

الخصائص الأساسية ​

  • list — true/false، يفعّل التحديد المتعدد
  • listType — مطلوب لكل ما ليس مرجعاً (مثلاً java.util.Date)
  • listDisplayType — الأداة التي يُعرَض بها مدخل القائمة: Default أو Dropdown أو Chips
  • showAsDateRange — true/false، يعرض مدخلاً نصياً كمنتقي نطاق موحّد؛ يُستخدم مع fromDateId وtoDateId، وتفصيله في قسم «مدخلات نطاق التاريخ» أعلاه
  • fromDateId / toDateId — اسما مدخلَي التاريخ الأساسيين عند تفعيل showAsDateRange
  • layout — كيف تُرتَّب المطالبة: alone أو spanned أو normal أو spanned2
  • required — true/false، يجعل المطالبة إلزامية
  • requiredGroup — يجمع مدخلات يجب ملء واحد منها على الأقل
  • hijri — true/false، يطالب بتاريخ هجري
  • paramGroup — true يجعل هذا المدخل عنوان قسم بدل أن يكون مطالبة، ويجب أن يضبط معه isForPrompting="false". وتفصيله في قسم «تقسيم المدخلات إلى مجموعات» أعلاه
  • collapsed — على مدخل المجموعة، true يفتح القسم مطوياً
  • paramGroupId — على المطالبة، اسم مدخل المجموعة الذي تنتمي إليه
  • rememberLastValue — true/false، يفتح المطالبة على آخر إجابة للمستخدم. وتفصيله في قسم «تذكّر آخر إجابة للمستخدم» أعلاه
  • nama-id — معرّف داخلي تستخدمه أداة إنشاء التقارير، ولا تضبطه أنت بيدك

الاقتراحات في الحقول النصية ​

  • suggestionquery — استعلام SQL يغذّي الإكمال التلقائي. عمودان يعنيان الكود مع عرض عربي؛ وثلاثة أعمدة تعني الكود والعربي والإنجليزي.
sql
SELECT DISTINCT TOP 25 revisionId, revisionName
FROM ItemRevision
WHERE invItem_id = {fItem}
  AND (revisionId LIKE '%' + {revision} + '%'
       OR revisionName LIKE '%' + {revision} + '%')

اختيار سجل ​

  • entityType — ما الذي يختار المستخدم منه
  • property — أي حقل من السجل المختار يصل إلى الاستعلام: code (وهو المعتاد) أو id أو name1 أو name2، أو — للفترة المالية — startDate وendDate

في الحالة المعتادة يسمي entityType نوعاً واحداً ثابتاً — Employee أو Customer — فتكون المطالبة اختياراً من ذلك النوع. لكن كثيراً من الأعمدة تحمل مرجعاً عاماً (generic reference): ذمة قد تكون عميلاً أو مورداً أو موظفاً، أو مالكاً قد يكون فرعاً أو إدارة. والتقرير الذي يفلتر على عمود كهذا لا بد أن يسأل المستخدم سؤالين، وإجابة الأول هي التي تحدد ما يجوز للثاني أن يعرضه.

وجّه entityType إلى مدخل آخر مسبوقاً بعلامة $، فيأخذ المُنتقي نوعه مما يحمله ذلك المدخل في تلك اللحظة:

xml
<parameter name="subsidiaryType" class="java.lang.String">
    <property name="enumType" value="EntityTypeDF"/>
    <property name="allowedValues" value="Customer,Supplier,Employee"/>
    <property name="arabic" value="نوع الذمة"/>
    <property name="english" value="Subsidiary Type"/>
</parameter>

<parameter name="subsidiary" class="java.lang.Object">
    <property name="entityType" value="$subsidiaryType"/>
    <property name="property" value="id"/>
    <property name="arabic" value="الذمة"/>
    <property name="english" value="Subsidiary"/>
</parameter>

يختار المستخدم مورد في المطالبة الأولى فتصير الثانية منتقي موردين؛ ويبدّلها إلى عميل فتبدأ المطالبة نفسها تعرض العملاء. وما دامت مطالبة النوع بلا إجابة فليس أمام المنتقي شيء يبحث فيه، لذلك عرّف مدخل النوع قبل المدخل الذي يعتمد عليه — فالمطالبات تظهر بترتيب التعريف — وفكّر في جعله required.

ومعالج التقارير (Report Wizard) يكتب هذا الزوج نيابةً عنك كلما فلترت على حقل مرجع عام: يضيف مطالبة نوع تحمل اسم الحقل ولاحقة EntityTypeSelector — أي subsidiaryEntityTypeSelector لحقل اسمه subsidiary — ويحصر قائمتها المنسدلة في الأنواع التي يقبلها ذلك العمود فعلاً. وحين لا يقبل العمود إلا نوعاً واحداً فلا سؤال أصلاً، فيتخطى المعالج المطالبة الإضافية ويكتب ذلك النوع مباشرةً في entityType.

القوائم المنسدلة ​

  • enumType — مجموعة الخيارات المعروضة
  • allowedValues — القيم المسموحة مفصولة بفواصل، وتُعرض قائمة منسدلة
  • allowedValuesAr / allowedValuesEn — التسميات العربية والإنجليزية لتلك القيم، مفصولة بفواصل وبالترتيب نفسه
xml
<parameter name="entityType" class="java.lang.String">
    <property name="enumType" value="EntityTypeDF"/>
    <property name="allowedValues" value="Employee,Supplier"/>
    <property name="allowedValuesAr" value="موظف,مورد"/>
    <property name="allowedValuesEn" value="Employee,Supplier"/>
</parameter>

التسميات التي تكتبها هنا لا تمر على الترجمة أبدًا

خاصيتا التسمية ليستا مفتاح ترجمة، بل هما النص النهائي. اكتبهما فتظهر المطالبة بهذه الكلمات بعينها إلى الأبد، مهما قالت شاشة تعديل الترجمات (Translation OverRider) عن القيمة نفسها في باقي النظام. وبذلك تحتفظ القيمة التي أُعيدت تسميتها على مستوى النظام بتسميتها القديمة في هذا التقرير وحده، دون أي تنبيه على الفرق؛ وعلى كاتب التقرير أن يعود ويعدّل ملف .jrxml.

أما إن تركتهما فتُقرأ التسميات حيّة: enumType وحدها، أو allowedValues وحدها، تعرض كل قيمة بالاسم الذي يعطيه النظام لها حاليًا، فتتبع أي إعادة تسمية لاحقة من تلقاء نفسها. فاجعل ذلك هو الأصل، واحتفظ بـ allowedValuesAr/allowedValuesEn للحالات التي لا يفي بها — قيمة ليس لها اسم في النظام أصلًا، أو صياغة يجب أن تختلف في هذا التقرير عنها في كل مكان آخر.

تضييق ما يستطيع المستخدم اختياره ​

منتقي السجلات يعرض كل سجلات نوعه، وهذا قلّ أن يكون ما يريده التقرير: مطالبة المخزن في تقرير تحويل ينبغي أن تعرض مخازن الفرع المختار سلفاً، ومطالبة الحساب ينبغي أن تعرض الحسابات التفصيلية لا العناوين. وخصيصة filter هي التي تضيّق المنتقي.

  • filter — تعبير واحد أو أكثر بالصيغة field,operator,value[,relation].

فـ field حقلٌ في السجل الذي يُنتقى — لا مدخل من مدخلات التقرير ولا عمود من أعمدته. وoperator أحد الأسماء المذكورة أدناه بحرفه. وvalue القيمة التي تُقارن بها. وrelation تربط التعبير بما قبله وهي AND إلا أن تكتب OR، وإن حذفتها فُهمت AND. وتُفصل التعبيرات المتعددة بفواصل منقوطة أو بأسطر جديدة، والفاصلة المنقوطة الزائدة في آخر السطر لا تضر. ولا تضع مسافات حول الفواصل — فالمسافة تصبح جزءاً من اسم العامل أو من القيمة.

forType,Equal,Department,AND;isLeaf,Equal,true
documentType,Equal,SalesInvoice,OR;documentType,Equal,SalesReturn
type,Equal,Detail

العوامل:

Equal, NotEqual,
GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual,
StartsWith, NotStartsWith, EndsWith, NotEndWith,
Contains, NotContain, ContainsWithAnyOrder,
In, NotIn, WithinPeriod, OutsidePeriod,
OpenBracket, CloseBracket

ويجوز أن يُكتب أي منها ولاحقته OrEmpty — EqualOrEmpty وContainsOrEmpty وInOrEmpty — وهذا يصبح مهماً في اللحظة التي تأتي فيها القيمة من مدخل آخر بدلاً من أن تكون مكتوبة في التقرير.

الفلترة بما أجاب به المستخدم في مدخل آخر ​

اكتب ${parameterId} في أي موضع من القيمة فيُستبدل بالجواب الذي أدخله المستخدم في ذلك المدخل، فيضيّق مدخلٌ نطاقَ مدخل آخر:

xml
<parameter name="FromWarehouse" class="java.lang.Object">
    <property name="entityType" value="Warehouse"/>
    <property name="arabic" value="من مخزن"/>
    <property name="english" value="From Warehouse"/>
</parameter>

<parameter name="OverdraftPolicy" class="java.lang.Object">
    <property name="entityType" value="OverDraftPolicy"/>
    <property name="filter" value="warehouse,Equal,${FromWarehouse}"/>
    <property name="arabic" value="سياسة السالب"/>
    <property name="english" value="Overdraft Policy"/>
</parameter>

فيختار المستخدم مخزناً في المدخل الأول، فلا يعرض المدخل الثاني إلا سياسات السالب التابعة له. ولا يحتاج الربط إلى إعلان شيء آخر غير ${…} نفسها، وللمدخل الواحد أن تقرأه فلاتر كثيرة.

وهذا هو النظير القيمي لثنائية الحقل المرجع العام الموصوفة أعلاه، حيث entityType = $subsidiaryType تأخذ نوع المنتقي من مدخل آخر. فتنبّه للصيغتين فإنهما لا تتبادلان: النوع يُقرأ بـ $name بغير أقواس معقوفة، والقيمة بـ ${name} بها.

وثمة أمور يحسن معرفتها قبل الاتكال على هذا:

  • ما بين المعقوفتين هو اسم المدخل كما أُعلن في التقرير، يُطابَق بحرفه وبحالة أحرفه — فـ ${FromWarehouse} و${fromwarehouse} شيئان مختلفان، وواحد منهما فقط موجود. ولا يُستبدل إلا ${…}؛ أما $(FromWarehouse) بالأقواس المستديرة فيبقى نصاً كما هو، فيقارن الفلتر بذلك النص فلا يجد شيئاً.
  • والاسم الذي لا يطابق أي مدخل يُستبدل بلا شيء، وكذلك المدخل الذي لم يُجب المستخدم عنه بعد. لا تظهر رسالة خطأ ولا يُكتب شيء في السجل، وإنما يتصرف المنتقي كأن الفلتر يقول warehouse,Equal, — أي يطلب السجلات التي مخزنها فارغ، وهذه في العادة لا وجود لها. والمنتقي الذي يرجع فارغاً بإصرار سببه هذا في الأغلب.
  • وهذا بالضبط ما وُضعت له عوامل OrEmpty. فـ warehouse,EqualOrEmpty,${FromWarehouse} يُسقط ذلك التعبير بتمامه ما دامت مطالبة المخزن فارغة، ويطبّقه بمجرد الإجابة عنها — وهو الفرق بين تضييق اختياري ومطالبة لا يستطيع أحد استخدامها. وبقية الفلتر تعمل في أثناء ذلك؛ ومن التقارير الجاهزة ما يقرن ارتباطاً اختيارياً بشرط ثابت في سطر واحد: subsidiaryType,EqualOrEmpty,${SubsidiaryType},AND;trackDebtAges,Equal,true.
  • والأجوبة تُستبدل نصاً على الهيئة التي تحملها داخلياً: التاريخ بصيغة dd-MM-yyyy، والتاريخ والوقت بصيغة yyyy-MM-ddTHH:mm:ss.SSS، ونعم/لا بـ true أو false، وقيمة القائمة المنسدلة أو نوع الكيان بالقيمة نفسها، والسجل المختار بصيغة id:entityType:code:actualCode:name1:name2 مع إسقاط الأجزاء الفارغة.
  • وهذه الصيغة المركّبة للسجل تُفهم حين تُقارن بحقل يحمل هو نفسه سجلاً — وهي الحالة المعتادة، warehouse,Equal,${FromWarehouse}. أما مقارنتها بحقل نصي أو حقل كود فتفشل، لأن النص كله يُؤخذ حينئذٍ حرفياً. وخصيصة property لا تنقذ الموقف: فهي تحدد ما يستقبله استعلام التقرير، لا ما تستبدله ${…}.
  • والمدخل متعدد القيم (list = true) يستبدل كل قيمه مرة واحدة مفصولةً بـ @A=@X، وهذا بعينه ما يقرأه العامل In: فـ warehouse,In,${Warehouses} يضيّق بكل مخزن أشّر عليه المستخدم.
  • وأعلن المدخل المقروء قبل المدخل الذي يقرؤه. فالمطالبات تظهر بترتيب إعلانها، والمستخدم الذي يلقى المنتقي التابع أولاً يلقاه مفلتراً بقيمة فارغة. وتأشير المدخل القائد بـ required يجعل الترتيب ملزماً لا متعارَفاً عليه فحسب.

اجعل الارتباط الحيّ على مدخل مفرد القيمة

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

فلتر مختلف لكل نوع ​

المدخل الذي تشير entityType فيه إلى مدخل آخر يعرض نوعاً مختلفاً من السجلات بحسب جواب ذلك المدخل، والحقل الجدير بالفلترة قلّ أن يحمل الاسم نفسه في جميعها. فصدّر كل تعبير بالنوع الذي يخصه بالصيغة #Type{expression}:

#Customer{allowCredit,Equal,true}#Employee{salesMan,Equal,true}

فيُستخدم الفرع المطابق للنوع القائم حينئذٍ ويُهمل ما سواه. والفلتر المكتوب على الوجه المعتاد بغير # يُطبَّق على كل نوع — فاستخدم الصيغة البسيطة لشرط يصحّ في كل حال، وصيغة # حيث تختلف الأنواع اختلافاً حقيقياً. والصيغتان لا تُخلطان: فمتى بدأت القيمة بـ # لم تُقرأ إلا فروع #، والنوع الذي لا فرع له يبقى بلا فلتر أصلاً.

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

وهناك خمس خصائص ترخي هذا القيد، واحدة لكل محدد:

  • ignoreLoginLegalEntity
  • ignoreLoginBranch
  • ignoreLoginSector
  • ignoreLoginDepartment
  • ignoreLoginAnalysisSet

اضبط إحداها على true فتتوقف المطالبة عن الفلترة بقيمة المستخدم نفسه لذلك المحدد وتستعمل القيمة العامة بدلاً منها، فتصبح السجلات التابعة لأي قيمة منه قابلةً للإيجاد.

xml
<parameter name="branch" class="java.lang.Object">
    <property name="entityType" value="Branch"/>
    <property name="ignoreLoginBranch" value="true"/>
    <property name="arabic" value="الفرع"/>
    <property name="english" value="Branch"/>
</parameter>

هذا يوسّع المطالبة لا التقرير

هذه الخصائص لا تغيّر إلا ما تسمح المطالبة للمستخدم بإيجاده. فهي لا تمنح صلاحية ولا تمس استعلاماً. والمستخدم الذي صار قادراً على اختيار فرع لا يحق له قراءته سيسترجع مع ذلك ما تسمح به جملة WHERE في التقرير وقيود الأمان فيه — وقد لا يكون ذلك شيئاً على الإطلاق، فيخرج له تقرير فارغ بلا تفسير. فإن كان المقصود أن يصل المنتقي إلى كل شيء، فالاستعلام خلفه يحتاج مراجعة في الوقت نفسه.

القيم الافتراضية ​

  • defaultValue — نص يُقرأ بحسب نوع المدخل: التاريخ بصيغة dd-MM-yyyy، والوقت بصيغة yyyy-MM-dd'T'HH:mm:ss.SSS، والمرجع بصيغة id:entityType:code.

تُحسب الرموز المتحرّكة أدناه لحظة فتح شاشة التشغيل لا لحظة كتابة التقرير، فيظلّ تقرير كُتب في يناير يفتح على الفترة الصحيحة في ديسمبر. ويقبل عمود القيمة الافتراضية في معالج التقارير الرموز نفسها.

الرمزما يتحوّل إليه
$today() و $now()تاريخ اليوم / اللحظة الحالية
$monthStart() و $monthEnd()أول وآخر يوم في الشهر الحالي
$previousMonthStart() و $previousMonthEnd()الشهر السابق
$nextMonthStart() و $nextMonthEnd()الشهر التالي
$yearStart() و $yearEnd()أول وآخر يوم في السنة الحالية
$previousYearStart() و $previousYearEnd() و $nextYearStart() و $nextYearEnd()السنة السابقة / التالية
$quarterStart() و $quarterEnd()الربع الحالي
$halveStart() و $halveEnd() و $thirdStart() و $thirdEnd()نصف السنة الحالي / ثلث السنة الحالي
$todayPlusDays(7) و $todayMinusDays(7)عدد من الأيام بعد اليوم أو قبله
$todayPlusWeeks(2) و $todayMinusWeeks(2)المثل بالأسابيع
$todayPlusMonths(3) و $todayMinusMonths(3)المثل بالشهور
$todayPlusYears(1) و $todayMinusYears(1)المثل بالسنوات
$currentUser() و$currentUserEmployee() و$currentUserRelatedSubsidiary() و$currentFiscalPeriod()مدخلات المراجع: المستخدم الذي يشغّل التقرير، وملفه كموظف، وذمته، والفترة المالية الحالية

وهذه الدوال تخص defaultValue وحدها. و$currentUser() على وجه الخصوص يملأ قيمة افتراضية — وليست قيمة تستطيع قراءتها بـ $P{} من داخل التقرير.

وللقيمة الافتراضية متعددة القيم، افصل بينها بـ @A=@X:

id:entityType:code@A=@Xid:entityType:code@A=@X...

ما الذي تتحول إليه الإجابة الفارغة ​

المطالبة التي يتركها المستخدم فارغة تصل إلى الاستعلام بقيمة null، وnull في أي مقارنة SQL عادية لا يطابق شيئاً — فالتقرير الذي ظن المستخدم أنه سيغطي كل الفروع يخرج فارغاً تماماً. وخاصية type تعالج ذلك من منبعه، إذ تستبدل بالفراغ قيمةً طرفيةً قبل أن يراه الاستعلام أصلاً:

  • type = > — الإجابة الفارغة تصير أصغر قيمة في نوعها: النص الفارغ، أو أصغر رقم، أو أول يناير 1900.
  • type = < — الإجابة الفارغة تصير أكبر قيمة: أكبر رقم، أو أول يناير 2999، وفي النصوص سلسلة من حرف الياء تأتي في الترتيب بعد أي نص عادي.

اقرأ الخاصية على أنها المقارنة التي يقع عليها هذا المدخل. فمدخل «من تاريخ» المستعمل في sales.date >= $P{fromDate} يأخذ >، فتصير قيمته الفارغة سنة 1900 ويكف الشرط عن استبعاد أي شيء. ومدخل «إلى تاريخ» المقابل له يأخذ < للسبب نفسه عند الطرف الآخر. وفي XML لا بد أن تُكتب علامة < هكذا &lt;:

xml
<parameter name="fromDate" class="java.util.Date">
    <property name="type" value="&gt;"/>
    <property name="arabic" value="من تاريخ"/>
    <property name="english" value="From Date"/>
</parameter>

<parameter name="toDate" class="java.util.Date">
    <property name="type" value="&lt;"/>
    <property name="arabic" value="إلى تاريخ"/>
    <property name="english" value="To Date"/>
</parameter>

واحذف type كلياً فتبقى الإجابة الفارغة null، وهو الصواب كلما كان الاستعلام نفسه يتعامل مع null — كما تفعل دوال $X{}، وكما تفعل الصيغة ($P{p} is null or …) التي يولّدها معالج التقارير.

وأمران في هذا الباب يوقعان الناس. المدخل الرقمي الذي يُجاب بـ صفر يُعامل معاملة الفارغ ويُستبدل مثله تماماً. والاستبدال يقع سواء كان له معنى في سياقه أو لم يكن: فالفلتر النصي من نوع يحتوي على الذي يبنيه المعالج يحمل type = >، فتصير الإجابة الفارغة نصاً فارغاً ويطابق like '%%' كل الصفوف بهدوء — وهو المقصود هناك، لكنه يفسر كثيراً من أسئلة «لماذا لم يفعل هذا الفلتر شيئاً؟».

الإظهار والإخفاء والتحقق ​

  • NamaRep.canDisplay($P{param}) — استخدمه في printWhenExpression ليختفي العنصر عن المستخدمين غير المسموح لهم برؤية موضوع ذلك المدخل
  • no-mirror = true — يمنع انعكاس العنصر في التصاميم من اليمين إلى اليسار
  • fromParam — يربط مدخل التاريخ «إلى» بمدخل التاريخ «من» المقابل له، ليتمكن التقرير من تقييد المدى بينهما
  • fromParamMaxGapInDays — أوسع مدى مسموح للمستخدم بين التاريخين، بالأيام
xml
<parameter name="toDate" class="java.util.Date">
    <property name="arabic" value="إلى تاريخ"/>
    <property name="english" value="To Date"/>
    <property name="fromParam" value="fromDate"/>
    <property name="fromParamMaxGapInDays" value="30"/>
</parameter>

الخاصيتان تُستخدمان معاً. فـ fromParam قاعدة وليست مجرد ربط: متى وُضعت فحص النظام التاريخين في كل مرة يعمل فيها التقرير، وفعل ثلاثة أشياء.

  1. يصبح التاريخان إلزاميين. إن تُرك أحدهما فارغاً توقف التقرير بالرسالة المذكورة أدناه، ولذلك لا يمكن لتاريخين مربوطين بهذه الطريقة أن يستعملا type لترك أحد طرفي المدى مفتوحاً.
  2. يُقيَّد المدى. لا يجوز أن يزيد عدد الأيام بين التاريخين على fromParamMaxGapInDays. ولا يهم الترتيب: التاريخان المدخلان بالعكس يُقاسان بالطريقة نفسها.
  3. غياب الحد يعني صفراً. إن وضعت fromParam دون fromParamMaxGapInDays صار المدى المسموح صفر أيام، فلا يمر إلا يوم واحد. واختيار الفترة من 1 سبتمبر إلى 19 سبتمبر يُرفض بالرسالة:
The gap 18 between the dates 2026-09-01 and 2026-09-19 is greater than maximum allowed gap 0 for parameters fromDate and toDate

فإن أردت المطالبتين دون حد فاحذف الخاصيتين كلتيهما، ولا تضع fromParam وحدها.

لا توجد ترجمة عربية لأي من الرسالتين، فتظهران بالإنجليزية على الشاشات العربية أيضاً:

The parameter {0} can not be empty
The gap {0} between the dates {1} and {2} is greater than maximum allowed gap {3} for parameters {4} and {5}

التسميات وما بقي ​

  • arabic / english — تسميتا المطالبة
  • resource — مفتاح ترجمة، حين يجب أن تأتي التسمية من ملفات الترجمة
  • src — إعادة استخدام خاصية معرَّفة على مدخل آخر
  • ignore — إبقاء المدخل خارج المطالبة كلياً
  • type — ما الذي تتحول إليه الإجابة الفارغة؛ راجع قسم ما الذي تتحول إليه الإجابة الفارغة أعلاه
  • ignoreMinDates — يستثني مدخل تاريخ من رفع أقل تاريخ الموصوف في قسم حين يغيّر النظام إجابة المستخدم أدناه

حين يغيّر النظام إجابة المستخدم ​

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

وهناك ثلاث عمليات إعادة كتابة، وكلها جديرة بأن تُحفظ.

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

ولا بد أن تُترك مطالبات «إلى تاريخ» وشأنها وإلا انهارت الفترة إلى لا شيء. والنظام يستنتج أي المطالبات هي تلك من أسمائها لا من معناها: تُعد المطالبة «إلى تاريخ» حين يحتوي اسم المدخل على to ولا يحتوي على from (بغض النظر عن حالة الأحرف)، أو حين يكون الاسم tdate بالضبط. فـ toDate وdateTo وToDocDate في مأمن، وfromDate يُرفع كما ينبغي. أما مدخل «إلى تاريخ» المسمى endDate أو until أو dueDate فلا ينطبق عليه شيء من ذلك، فيُرفع كأنه «من تاريخ»، فتنقلب فترة المستخدم رأساً على عقب ويخرج التقرير فارغاً. وحين لا يسترجع مستخدم مقيَّد شيئاً من تقرير يعمل عند غيره بلا مشكلة، فأسماء مدخلات التاريخ فيه هي أول ما يُنظر فيه.

وأمران يفلتان من إعادة الكتابة. مطالبة التاريخ المعرَّفة كقائمة (list) لا تُمس أبداً. وignoreMinDates = true تستثني مطالبةً واحدة بعينها — وهي المخرج للتاريخ الذي ليس حدّ فترة أصلاً، أو الذي تقرأ القاعدة اسمه بالمقلوب.

xml
<parameter name="dueDate" class="java.util.Date">
    <property name="ignoreMinDates" value="true"/>
    <property name="arabic" value="تاريخ الاستحقاق"/>
    <property name="english" value="Due Date"/>
</parameter>

السنوات والفترات المالية تُقدَّم للأمام. المطالبة التي تختار سنة مالية أو فترة محاسبية تُقارن بالسنوات والفترات المسموحة لذلك المستخدم. فإن كانت المختارة تبدأ قبل أقدم ما يُسمح له به، استُبدلت بتلك السنة أو الفترة الأقدم المسموحة. وقاعدة الأسماء to/from نفسها تستثني مطالبات «إلى» هنا أيضاً.

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

  • أبداً — اترك إجابة المستخدم كما هي.
  • دائماً — استبدلها بمحدد الدخول في كل مرة، فتصير المطالبة زينةً لا أكثر.
  • حين لا يكون عاماً — استبدلها إلا إذا كان المستخدم داخلاً على محدد مركّب (composite) وأجاب بذلك المحدد نفسه أو بأحد المحددات تحته. والمستخدم الداخل على المحدد العام لا يُستبدل له شيء أبداً. وهذا هو الضبط الذي يتيح لمدير فرع مركّب أن يستعرض الفروع التي تحته، ولا يتجاوزها.

نشر عدة صور من تقرير واحد ​

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

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

وترث الصورة كل ما يصنع التقرير: التصميم وتقاريره الفرعية وموارده ومجموعة مطالباته كاملةً. وما تأتي به من عندها هو كودها واسمها ومجموعة تقاريرها وأمانها — فتظهر في القوائم تقريراً مستقلاً ويمكن أن تُعطى لمجموعة مستخدمين أخرى.

حسم إجابة سلفاً ​

جدول تعديل المدخلات في صفحة «متقدم» أيضاً هو موضع إعطاء المطالبات الموروثة إجاباتها الثابتة — سطر لكل مدخل:

العمودما يفعله
المعرف (Parameter Id)اسم المدخل كما عرّفه التصميم بالحرف
القيمة (Param Value)القيمة المستعملة، تُكتب كما تُكتب defaultValue — بما في ذلك الدوال الديناميكية مثل $yearStart() و$currentUserEmployee()
طريقة الاستعمال (Usage)Default Value أو Replace Value — انظر أدناه
القيم المسموحة (Allowed Values)قائمة مفصولة بفواصل تحصر منسدلة هذه المطالبة في جزء مما يعرضه التصميم
نوع الكيان (Entity Type)لمطالبة السجل، النوع الذي يختار المستخدم منه — وهي وسيلة لإعادة توجيه منتقٍ دون لمس التصميم
النوع (Param Type)لا يُحتاج إلا حين لا يكون المدخل من مطالبات التصميم؛ انظر أدناه

وطريقة الاستعمال هي العمود الذي يقرر صرامة الصورة:

  • Default Value — تُملأ المطالبة للمستخدم وتبقى ظاهرة، وله أن يغيّرها. وهو الخيار الصحيح لـ«هذا التقرير يُشغَّل غالباً على السنة الحالية».
  • Replace Value — تُملأ المطالبة وتُخفى. فلا يراها المستخدم ولا يستطيع تغييرها. وهكذا يُثبَّت التقرير على فرع واحد تثبيتاً حقيقياً.

Replace Value تيسير لا صلاحية

المطالبة المخفية مخفية في هذه الصورة وحدها. وهي لا تمنع المستخدم نفسه من فتح التقرير الأساس أو صورة أخرى منه والإجابة عن السؤال بنفسه. فإن كان الواجب ألا يرى مستخدم أرقام فرع آخر أبداً، فمجموعة التقارير وسطور أمان التقرير وقيود الأمان فيه هي التي تفرض ذلك. وتعديل المدخلات يوفر على الجميع الكتابة، لكنه لا يبني جداراً.

ويجوز أن يسمي سطر التعديل مدخلاً ليس من مطالبات التصميم — مدخلاً عرّفه التصميم بـ isForPrompting="false" أو ignore فلا يُسأل عنه المستخدم أصلاً. املأ النوع ليعرف النظام أي قيمة يحمل، فتغذيه الصورة بالقيمة في صمت.

اختيار الأساس وقت التشغيل ​

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

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

استبدال التصميم لاحقاً

حفظ التقرير الأساس يعيد حفظ كل تقرير مبني عليه، فتُعاد التعديلات على التصميم الجديد من تلقاء نفسها. فليست هناك قائمة صور تُطاف عليها وتُحدَّث يدوياً — ورفع ملف jrxml مصحح على التقرير الأساس هو العمل كله.

واحضار الملفات الإضافية من هو ابن العم الأصغر للفكرة نفسها: يترك تصميم التقرير كما هو ولا يستعير من تقرير آخر إلا صوره وخطوطه وتقاريره الفرعية، فيسكن الطابع العام في مكان واحد بدل رفعه على كل تقرير يستعمله.

تحديد ما يراه كل مستخدم ​

التقرير الذي يستعلم من قاعدة البيانات مباشرةً يتجاوز كل صلاحيات المستخدم، ما لم تُعِد أنت تلك الصلاحيات إلى الاستعلام بنفسك. وهذا ما وُجدت له قيود الأمان: مدخل مخفي قيمته جزء من SQL يصف ما يُسمح لهذا المستخدم بالذات برؤيته، يُدرَج في جملة WHERE عندك.

لنفترض أنك تريد تقرير حسابات مصفّى بحسب منشئ كل سجل، وبحسب صلاحيات العرض والتعديل، وكذلك بحسب الشركة أو القطاع أو الفرع أو أي مُحدد آخر على الحساب.

١. عرّف مدخلاً مخفياً باسم SECURITY_CONSTRAINTS ​

أنشئ مدخل String معلَّماً بـ Not For Prompting، وتعبير قيمته الافتراضية:

groovy
NamaRep.security()
  .fieldEntityType("Account")
  .tableAlias("acc")
  .capabilities("firstAuthor", "viewCapability", "usageCapability",
                "updateCapability", "legalEntity", "branch",
                "sector", "department", "analysisSet")
  • .fieldEntityType("Account") — أي نوع من السجلات يجري تصفيته
  • .tableAlias("acc") — الاسم المستعار الذي يحمله جدول ذلك السجل في استعلامك
  • .capabilities(...) — ما الذي يُصفّى به. فـ firstAuthor يقصر النتيجة على السجلات التي أنشأها المستخدم؛ وviewCapability وupdateCapability وusageCapability تطبّق الصلاحيات المقابلة؛ وlegalEntity وbranch وsector وdepartment وanalysisSet تطبّق المحددات التنظيمية

٢. أدرجه في الاستعلام ​

يحمل المدخل نص SQL خاماً، فيُدرَج بـ $P!{} — وعلامة التعجب هي التي تخبر Jasper بلصق النص بدل ربطه كقيمة:

sql
SELECT a, b, c
FROM Account acc
LEFT JOIN Table2 t2 ON t2.id = acc.someId
WHERE acc.code <> 'abc'
  AND $P!{SECURITY_CONSTRAINTS}

٣. أكثر من جدول ​

ابنِ جزءاً لكل جدول واربطها بـ AND:

groovy
NamaRep.security()
  .fieldEntityType("Account")
  .tableAlias("Account")
  .capabilities("firstAuthor", "viewCapability")
+ " AND " +
NamaRep.security()
  .fieldEntityType("FiscalYear")
  .tableAlias("FiscalYear")
  .capabilities("legalEntity", "branch", "sector")

أنت من يعرّفه — وليس تلقائياً

لا شيء يحقن مدخل الأمان في تقرير مرسوم يدوياً نيابةً عنك. والتقرير الخالي منه يعرض كل صف يُرجعه الاستعلام، لكل من يستطيع تشغيل التقرير. أما التقارير المبنية بأداة إنشاء التقارير فتُطبَّق عليها التصفية نفسها خلف الكواليس.

مشاركة تقرير مع شخص بلا حساب ​

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

groovy
NamaRep.repLinkByCode($P{REPORT_PARAMETERS_MAP}, "ARG000046-report")
  .p("Code_Equals").ref($F{entityType}, $F{id})
  .toNoAuthResultLink()

أما البنّاء الذي خلف ذلك — تمرير المدخلات والمراجع، ونسخ مدخلات التقرير الحالي — فموثّق في مرجع تعبيرات NamaRep.

ولا يعمل الرابط إلا لتقرير علامة السماح بتشغيل التقرير بدون تسجيل دخول فيه معلَّمة. وحينها يتخطى كل الفحوص الواردة في قسم «من يحق له تشغيل التقرير». وإجابات مدخلات التقرير تسافر داخل الرابط نفسه، فلا تعلّم هذه الخانة إلا على تقرير يصح أن يعيد تشغيله بإجابات مختلفة كلُّ من يحمل الرابط.

نشر نموذج مطبوع ليجلبه العميل بنفسه

الرابط العام لا ينفع ما لم يكن لديك ما ترسله إليه. فحدِّد النموذج المطبوع الذي يعرضه الرابط العام في أعدادات الحقول و الشاشات — فهناك تقول أي تصميم يفتحه رابط الفاتورة العام، فينزّل العميلُ التابعُ لرابط في بريد أو لرمز QR النموذجَ الذي قصدته بالضبط دون حساب.

خلط أحجام الصفحات في مستند واحد ​

لتصميم التقرير الواحد حجم صفحة واحد. وحين يحتاج مستند واحد حجمين فعلاً — صفحة غلاف A4 يتبعها جدول عريض A3 — فالحل هو تقرير Book: هيكل محتواه سلسلة أجزاء، كل جزء تقرير قائم بذاته بحجم صفحته الخاص.

  1. ابدأ بتقرير رئيسي من النوع Book Report.
  2. أعطه استعلام SQL بسيطاً يُرجع الحقول التي تحتاجها الأجزاء للتمييز بينها.
  3. استخدم Add Part to Content لإضافة كل تقرير فرعي جزءاً.
  4. أعطِ كل جزء Print When Expression حتى لا تُطبع إلا الأجزاء التي تنطبق على هذا المستند.
  5. عرّف المدخلات التي تحتاجها الأجزاء ومرّرها إلى كل جزء.

والنتيجة المعتادة قالب من جزأين — جزء A4 وجزء A3 — يُختار بشرط على حقل ملاحظات المستند، ولكل جزء أن يحتوي تقاريره الفرعية.

الخطوط وإخراج PDF العربي ​

يأتي نظام نما وفيه Times New Roman مسجَّلاً للنص العربي، وهو ما يحصل عليه التقرير ما لم تقل غير ذلك. ولاستخدام أي خط آخر — Cairo أو Amiri أو Droid Arabic Naskh — يجب تحزيم الخط وتثبيته على الخادم، لأن ملف PDF لا بد أن يحمل الخط معه.

وهناك أيضاً فيديو يمشي بك في هذه الخطوات.

١. أضف الخط في Jaspersoft Studio ​

افتح Jaspersoft Studio، واذهب إلى Window > Preferences، ثم Jaspersoft Studio > Fonts، وانقر Add.

٢. اضبط خصائص الخط ​

في مربع حوار Font Family:

  • اختر ملف الخط .ttf أو .otf، أو الملفات
  • ضع علامة على Embed this font in PDF documents
  • اضبط PDF Encoding على Identity-H

مربع حوار عائلة الخطوط في Jasper Reports للخطوط العربية

ثم انقر Finish.

٣. صدّر الخط ملفَ JAR ​

بعد إضافة الخط، انقر Export واحفظ ملف .jar المولَّد.

٤. انشر ملف JAR ​

انسخ ملف .jar المصدَّر إلى مجلد tomcat/lib وأعد تشغيل خدمة Tomcat. وإلى أن تُعيد التشغيل، ترجع التقارير التي تطلب الخط الجديد إلى ما هو مثبَّت أصلاً.

الخطوط على أجهزة نقاط البيع

أجهزة نقاط البيع تعرض إيصالاتها بنفسها، فلا بد أن يصلها الخط هي أيضاً. ارفع ملف JAR نفسه إلى حقل Jasper Fonts («خطوط خاصة ب Jasper») في شاشة إعدادات واجهة نقاط البيع الجديده (Pos UI Settings).

والرفع لا يدفع الخط إلى أي جهاز بذاته: فكل جهاز نقطة بيع يلتقطه في مزامنة البيانات الرئيسية التالية، ويكتبه في مجلد jasper-fonts-extension عنده، ويبدأ استخدامه فوراً — أو عند إعادة تشغيل المشغّل التالية إن كان الملف الحالي قيد الاستعمال. لذلك بعد تحديث أجهزة نقاط البيع إلى إصدار يدعم امتداد الخطوط، أعد حفظ سجل إعدادات واجهة نقاط البيع ليكون لديها ما تلتقطه، ثم امهلها دورة مزامنة قبل أن تفحص جهازاً.

٥. استخدم الخط ​

عيّن عائلة الخط الجديدة لعناصر النص في التصميم. فيُضمَّن الخط في ملف PDF، ويظهر العربي صحيحاً أينما فُتح الملف.

كم يُسمح للتقرير أن يعمل ​

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

وهذا الحد يُضبط عادةً أكرم من بقية حدود الاستعلامات، لأن تقرير نهاية الشهر يستغرق دقائق بحق. فإن بدأت التقارير تتوقف بعد تغيير ما فهذا أول ما تفحصه؛ وإن كان الخادم يئنّ تحت حِمل التقارير فتضييقه هو الرافعة الآمنة.

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

كتابة التعبيرات ​

كل ما تستطيع استدعاءه من داخل تعبير في التقرير — الأسماء والترجمة، والتواريخ الهجرية، وتفقيط الأرقام، واستخراج الأسعار، والروابط الراجعة إلى النظام، ومنشئات السجلات، وروابط الموافقة، ورموز QR، والقائمة الكاملة لمدخلات $P{} الجاهزة — مفهرس في مرجع تعبيرات NamaRep.

رسائل قد تظهر لك ​

رسائل ترفض حفظ تعريف التقرير أو تشغيله.

الرسالةلماذاما تفعله
«تقارير اللوحة لا تحتاج إلى ملف تعريف تقرير. يرجى مسح حقل التعريف أو تغيير نوع التقرير.»نوع التقرير هو لوحة بينما التعريف ما زال يحمل ملف تصميم. واللوحة ترسم نفسها ولا تصميم Jasper لها.أفرغ التعريف، أو أعد نوع التقرير إلى النوع الذي يستعمل ملف تصميم.
«مجموعة التقارير {0} لا يمكن تكرارها»مجموعة التقارير نفسها تظهر مرتين في جدول مجموعات التقرير.احذف السطر المكرر — لكل مجموعة سطر واحد.
«لا يمكن تعديل التقرير {0} لأنه تقرير نظامي.»تغيّر جزء مجمَّد من تقرير جاهز مع النظام: نوعه أو دفتره أو توجيهه أو نوع التقرير أو الصفحة أو الترتيب أو اسم العرض أو مكافئ الأمان أو التقرير المبني عليه أو السكربت، أو محتوى التصميم أو تقاريره الفرعية أو مواردها. أما الكود والأسماء والمجموعات وسطور الأمان الخاصة بالتقرير فليست مجمَّدة.انسخ التقرير إلى تقرير من عندك وعدّل النسخة. وإن كان لا بد من تعديل الأصل فبإمكان مدير النظام تفعيل السماح بتغيير التقرير النظامي الي غير نظامي في الإعدادات العامة — وبعدها لا تحدّثه الترقيات.
«لا يمكن تعديل أو إنشاء التقارير النظامية»حُفظ تقرير جديد وخانة نظامي معلَّمة فيه أصلاً. ولا تكون التقارير النظامية إلا التقارير الجاهزة مع النظام.أزل علامة نظامي واحفظه تقريراً من عندك.
«لا تملك الصلاحيه لتشغيل هذ التقرير {0}»جدول صلاحيات التقرير يستبعد هذا المستخدم: سطر منع يسمّيه أو يسمّي ملف صلاحياته أو مجموعته، أو أن الجدول لا يحوي إلا سطور سماح ولا يطابقه أي منها.أضف سطر سماح للمستخدم أو لملف صلاحياته أو لمجموعته — انظر قسم «من يحق له تشغيل التقرير».
«يجب أن تكون قيمة isForPrompting في مدخل المجموعة {0} هي false»مدخل معلَّم بـ paramGroup = true ما زال مطالبة، والمجموعة لا تفعل إلا أن تعنون قسماً ولا إجابة لها.أضف isForPrompting="false" إلى مدخل المجموعة ذاك.
«المدخل {0} يشير إلى المجموعة {1} وهو ليس مدخل مجموعة»خاصية paramGroupId لمطالبة تذكر مدخلاً غير موجود، أو مدخلاً غير معلَّم بـ paramGroup = true — وغالباً ما يكون خطأً في كتابة الاسم.صحّح paramGroupId إلى اسم مدخل المجموعة بالضبط، أو أضف paramGroup = true إلى المدخل الذي تذكره.
You can not run this report without authenticationفُتح رابط بلا تسجيل دخول لتقرير ليست فيه علامة السماح بتشغيل التقرير بدون تسجيل دخول. ولا ترجمة عربية لهذه الرسالة، فتظهر بالإنجليزية في الواجهة العربية.علِّم الخانة في التقرير، إن كان التقرير معدّاً فعلاً ليكون عاماً.