مرجع تعبيرات NamaRep
داخل التقرير، وفي كل موضع يقبل فيه JasperReports تعبيراً — حقل نصي، أو متغير، أو قيمة افتراضية لمدخل، أو شرط طباعة — تستطيع استدعاء NamaRep. فهو الجسر بين التصميم وبقية نظام نما: يترجم، وينسّق التواريخ والأرقام، ويستخرج الأسعار، ويبني الروابط الراجعة إلى النظام، ويطبّق قواعد الأمان التي يتجاهلها استعلام SQL الخام.
وهذه الصفحة فهرس، مرتّب لتجد الاستدعاء وتنسخه. أما كيفية بناء التقارير ونشرها وتعريف مدخلاتها ففي دليل Jasper Reports الشامل.
وثمة عرفان يسريان على كل ما يلي. الوسائط التي تسمّي سجلاً تقبل المعرّف أو الكود ما لم يُذكر خلاف ذلك. والاستدعاءات ذات البنّاء — priceCalculator() وlink() وlistView() وnewWithFields() — سلاسل تنتهي باستدعاء ختامي مثل .toString() أو .price()، ولا يحدث شيء قبل تنفيذ الاستدعاء الختامي.
الأسماء والترجمة
يحمل كل سجل اسماً عربياً واسماً إنجليزياً. وname() يختار المناسب للغة التي يشغّل بها القارئ التقرير، فيخدم حقل نصي واحد الجمهورَين معاً.
// يختار الاسم العربي أو الإنجليزي بحسب اللغة الحالية
NamaRep.name(name1, name2)
// الشيء نفسه، مع الرجوع إلى الكود إذا كان الاسمان فارغين
NamaRep.nameOrCode(code, name1, name2)// ترجمة قيمة — نص، أو نعم/لا، أو قيمة اختيار
NamaRep.translate(value)
NamaRep.translate(true) // يعطي «نعم» أو Yes بحسب اللغة
// ترجمة تسمية حقل في سياق نوع سجل
NamaRep.title(entityType, fieldId)
NamaRep.translate(entityType, fieldId)
// ترجمة "prefix.value"
NamaRep.translate("prefix", "value")
// تقسيم ترجمة تحمل عنواناً وعنواناً فرعياً يفصلهما |
NamaRep.head("header|subtitle") // "header"
NamaRep.sub("header|subtitle") // "subtitle"التواريخ والأوقات
أسماء الأيام
NamaRep.dayName($F{dateField}) // باللغة الحالية
NamaRep.arDayName($F{dateField}) // بالعربية
NamaRep.enDayName($F{dateField}) // بالإنجليزية
NamaRep.dayName(dayNumber) // ١ = الأحد، ٢ = الاثنين، ...التواريخ الهجرية
NamaRep.toHijri($F{date}) // التاريخ الهجري الكامل نصاً
NamaRep.toHijriDate($F{date}) // كائن تاريخ هجري
NamaRep.hijriDay($F{date}) // اليوم، بحشو أصفار
NamaRep.hijriMonth($F{date}) // الشهر، بحشو أصفار
NamaRep.hijriYear($F{date}) // السنة
NamaRep.hijri_yyyyMMdd($F{date}) // yyyyMMdd
// ركّب تنسيقك الخاص
NamaRep.hijriDay($F{date})+"/"+NamaRep.hijriMonth($F{date})+"/"+NamaRep.hijriYear($F{date})الأوقات
// من ساعات عشرية إلى وقت
NamaRep.decimalToTime(9.5) // "09:30"
NamaRep.decimalToTimeWithSeconds(9.5) // "09:30:00"
NamaRep.decimalToTimeNullable(0) // لا شيء، بدل "00:00"
NamaRep.decimalToTimeWithSecondsNullable(0) // لا شيء، بدل "00:00:00"
// من مللي ثانية إلى وقت
NamaRep.timeToString(9120000) // "02:32"
NamaRep.timeToStringNullable(0) // لا شيء، بدل "00:00"ووجدت صيغ Nullable لتطبع المدة الفارغة خانةً فارغة بدل 00:00 المضلِّل.
الفرق بين تاريخين
NamaRep.dateDiffInMonth(date1, date2)وللتفصيل بالسنوات والأشهر والأيام، احسب الفرق في متغير نوعُ إعادة تعيينه ونوعُ زيادته كلاهما None:
java.time.Period.between(
new java.util.Date($F{FromDate}.getTime()).toInstant()
.atZone(java.time.ZoneId.systemDefault()).toLocalDate(),
java.time.LocalDate.now()
)ثم اطبعه:
$V{period}.getYears()+" سنة "+$V{period}.getMonths()+" شهر "+$V{period}.getDays()+" يوم"الأرقام والعملات والمبالغ كتابةً
الأرقام العربية-الهندية
NamaRep.arNumbers("123") // ١٢٣
NamaRep.arNumbers(value)مساعدات آمنة من القيم الفارغة
الخانة الفارغة في التقرير ليست صفراً، والحساب عليها يفشل. وهذه تحوّلها قبل أن تصل إلى تعبيرك:
NamaRep.zeroIfNull(fieldOrVariable) // الفارغ يصير 0
NamaRep.oneIfZero(fieldOrVariable) // الصفر يصير 1 — قسمة آمنة
NamaRep.nullIfZero(fieldOrVariable) // الصفر يصير فارغاً — خانات فارغة بدل 0.00
NamaRep.objectToDecimal(value) // تحويل آمن إلى عدد عشريرمز الريال السعودي
NamaRep.sar()يُرجع الرمز صورةً، فيوضع في عنصر صورة لا في حقل نصي.
تفقيط المبالغ
NamaRep.tafqeet(currencyCode, amount) // اللغة الحالية
NamaRep.tafqeetArabic(currencyCode, amount)
NamaRep.tafqeetEnglish(currencyCode, amount)
NamaRep.tafqeetFrench(currencyCode, amount)وطريقة كتابة كل عملة — اسم الوحدة واسم الكسر — تأتي من الإعداد العام تحت value.info.tafqeetInfo.currencyCode.
الأسعار
priceCalculator() يجيب عن سؤال «كم يساوي هذا الصنف، لهذا العميل، في هذا التاريخ؟» بتشغيل استخراج السعر نفسه الذي تستعمله شاشات المبيعات. وهو الطريق لعرض سعر حالي على التقرير دون أن تكتب استعلاماً على قوائم الأسعار بنفسك.
NamaRep.priceCalculator()
.item($F{item})
.uom($F{UOM})
.qty($F{Quantity})
.unitPriceOnly()
.price()خزّن الناتج في متغير
.price() يُرجع كائن سعر كاملاً، لا رقماً. عرّف متغيراً يحمله — فئته java.lang.Object، والحساب No Calculation Function، ونوع الزيادة None، ونوع إعادة التعيين None — ثم اقرأ المكوّنات التي تحتاجها من $V{price}.
وإن كنت تريد الرقم وحده فأنهِ السلسلة بـ .unitPrice() بدل .price()، فيُرجع سعر الوحدة مجرداً.
اختيار السعر الساري في تاريخ معيّن
كثيراً ما تغطي عدة قوائم أسعار الصنفَ نفسه في فترات متداخلة. و.date(...) هو ما يحسم أيّها يفوز — مرّر مدخل التاريخ في التقرير لتسعير السطر بتاريخ السؤال لا بتاريخ اليوم:
// السعر الساري اليوم
NamaRep.priceCalculator().item(@{details.item.item.id}@).unitPriceOnly().unitPrice()
// السعر الساري في التاريخ الذي اختاره المستخدم
NamaRep.priceCalculator().item(@{details.item.item.id}@).date($P{FromDate}).unitPriceOnly().unitPrice()مرّر الشركة، ولا تختبر القيمة الفارغة
أمران يوقعان الناس هنا.
إن لم تحدد الشركة استُعملت الشركة الافتراضية للمستخدم الذي يشغّل التقرير — وليست بالضرورة الشركة التي يتكلم عنها التقرير. فقل أي شركة تقصد: .legalEntity($P{loginLegalEntityId}). وloginLegalEntityId هو المدخل الجاهز الذي يحمل الشركة التي سجّل المستخدم دخوله بها؛ ولا وجود لمدخل اسمه CompanyId.
والحساب لا يعود فارغاً بمعنى null أبداً. فالصنف الذي يتعذّر تحديده يعطي كائن سعر فارغاً؛ والصنف الذي لا تطابقه قائمة أسعار يعطي كائن سعر حقيقياً سعرُه صفر. لذلك فتعبير يفحص null لا يتحقق أبداً — افحص الصفر أو الفراغ بدلاً منه.
البنّاء كاملاً
NamaRep.priceCalculator()
.item($F{itemIdOrCode})
.customer($F{customerIdOrCode})
.supplier($F{supplierIdOrCode})
.uom($F{uomIdOrCode})
.invoiceClassification($F{classificationIdOrCode})
.ic($F{classificationIdOrCode}) // اختصار invoiceClassification
.legalEntity($F{legalEntityIdOrCode})
.le($F{legalEntityIdOrCode}) // اختصار legalEntity
.sector($F{sectorIdOrCode})
.sc($F{sectorIdOrCode}) // اختصار sector
.branch($F{branchIdOrCode})
.br($F{branchIdOrCode}) // اختصار branch
.department($F{departmentIdOrCode})
.dep($F{departmentIdOrCode}) // اختصار department
.analysisSet($F{analysisSetIdOrCode})
.anset($F{analysisSetIdOrCode}) // اختصار analysisSet
.priceClassifier1($F{priceClassifier1IdOrCode})
.pc1($F{priceClassifier1IdOrCode}) // اختصار priceClassifier1
.priceClassifier2($F{priceClassifier2IdOrCode})
.pc2($F{priceClassifier2IdOrCode})
.priceClassifier3($F{priceClassifier3IdOrCode})
.pc3($F{priceClassifier3IdOrCode})
.priceClassifier4($F{priceClassifier4IdOrCode})
.pc4($F{priceClassifier4IdOrCode})
.priceClassifier5($F{priceClassifier5IdOrCode})
.pc5($F{priceClassifier5IdOrCode})
.revision($F{revision})
.color($F{colorCode})
.size($F{size})
.qty($F{qty})
.date($F{date})
.unitPriceOnly()
.price() // أو .unitPrice() — الاستدعاء الختامي، ويكون آخراً دائماًقراءة كائن السعر
// القيم الرئيسية
$V{price}.unitPrice.primitiveValue
$V{price}.netValue.primitiveValue
$V{price}.custom.primitiveValue
$V{price}.totalCashShare.primitiveValue
$V{price}.totalPaymentMethodShare.primitiveValue
// الخصومات — من 1 إلى 8
$V{price}.discount1.percentage.primitiveValue
$V{price}.discount1.value.primitiveValue
$V{price}.discount1.afterValue.primitiveValue
$V{price}.discount1.maxNormalPercent.primitiveValue
// خصم الرأسية
$V{price}.headerDicount.percentage.primitiveValue
$V{price}.headerDicount.value.primitiveValue
$V{price}.headerDicount.afterValue.primitiveValue
// الضرائب — من 1 إلى 4
$V{price}.tax1.percentage.primitiveValue
$V{price}.tax1.value.primitiveValue
$V{price}.tax1.afterValue.primitiveValue
$V{price}.tax1.maxNormalPercent.primitiveValueالروابط الراجعة إلى النظام
التقرير المطبوع طريق مسدود، أما التقرير على الشاشة فلا يلزم أن يكون كذلك. وهذه البنّاءات تحوّل الصف إلى رابط يفتح السجل، أو تقريراً آخر، أو قائمة مفلترة.
سجل
NamaRep.link(entityType, id)
NamaRep.link(serverUrl, entityType, id)
// صيغة البنّاء، حين تحتاج شاشة أو قائمة بعينها
NamaRep.link()
.entityType($F{entityType})
.id($F{id})
.viewName("theViewName")
.menuCode("abcMenu")
.url(serverUrl)
.toString()مرفق
NamaRep.attachmentLink(id)
NamaRep.attachmentLink(serverUrl, attachmentId)تقرير آخر
NamaRep.repLinkByCode($P{REPORT_PARAMETERS_MAP}, "ReportCode")
.p("p1 id").v(value expression)
.p("p2 id").v(value expression)
.copyParams() // مرّر مدخلات التقرير الحالي المشتركة
.toString()و.p(...) يسمّي المدخل الهدف، وما بعده يزوّده بقيمته:
.p("param").v($F{id}, $F{entity}, $F{code}, $F{name1}, $F{name2})
.p("param").v($F{id}, $F{entity}, $F{code})
.p("param").ref($F{entityType}, $F{id})
.p("param").refCode($F{entityType}, $F{code})وأضف .directLink() لحذف عنوان الخادم، فيصير الرابط #rpt:xxx بدل عنوان كامل — وهو مفيد حين لا يُتبع الرابط إلا من داخل النظام.
و.toNoAuthResultLink() ينتج رابطاً يعمل بلا تسجيل دخول أصلاً، لإرساله إلى العملاء. انظر «مشاركة تقرير مع شخص بلا حساب» في الدليل لما يجب إعداده أولاً.
ثلاثة روابط تقارير جاهزة
// كشف حساب
NamaRep.repLinkByCode($P{REPORT_PARAMETERS_MAP}, "Statement")
.copyParams()
.p("fromAccount").v($F{accountId}, $F{accountEntityType}, $F{accountCode})
.p("toAccount").v($F{accountId}, "Account", $F{accountCode})
.toString()
// ملخص أرباح المبيعات
NamaRep.repLinkByCode($P{REPORT_PARAMETERS_MAP}, "SalesProfitSummary")
.copyParams($P{REPORT_PARAMETERS_MAP})
.p("SalesInvoice").ref("SalesInvoice", $F{SSIid})
.p("cust").refCode("Customer", "Customer501")
.p("fromDate").v("23-04-2014")
.p("showDetails").v("true")
.toString()
// كشف حساب ذمة
NamaRep.repLinkByCode($P{REPORT_PARAMETERS_MAP}, "SubsidiaryAccountStatement")
.p("subsidiaryType").v($F{CustomerEntityType})
.p("fromSubsidiary").v($F{customerId}, $F{CustomerEntityType}, $F{customerCode})
.p("toSubsidiary").v($F{customerId}, $F{CustomerEntityType}, $F{customerCode})
.p("accuontType").v("mainAccount")
.toString()اختصار رابط طويل
NamaRep.shortenURL(serverurl, signature, url)وقسم {shortenurl()} في وثائق Tempo يشرح التوقيع.
قائمة مفلترة
listView() يفتح شاشة قائمة مفلترة سلفاً — وهو رابط «أرني الفواتير التي وراء هذا الإجمالي» الطبيعي.
NamaRep.listView()
.entityType("SalesInvoice")
.criteria($P{REPORT_SCRIPTLET}.tempo("""
customer.code,Equal,{customerCode},AND;
valueDate,GreaterThanOrEqual,{fromDate},AND;
"""))
.toString()وأضف .directLink() لرابط بلا عنوان خادم.
| الاستدعاء | ما يضبطه |
|---|---|
.entityType(String) | أي سجلات تُعرض، مثلاً "SalesInvoice" أو "Customer" |
.criteria(String) | الفلتر، بتنسيق المعايير النصي |
.listViewName(String) | عرض قائمة بعينه يُفتح |
.menuCode(String) | القائمة التي تُفتح تحتها |
.orderBy(String) | الحقل الذي يُرتَّب به |
.ascending(Boolean) | اتجاه الترتيب — true تصاعدي |
.currentPage(Integer) | الصفحة التي يبدأ عندها |
.pageSize(Integer) | عدد الصفوف في الصفحة؛ و-1 تعني كلها |
.showTree(Boolean) | عرض القائمة شجرةً |
.extraCriteriaId(String) | معايير محفوظة إضافية تُطبَّق كذلك |
حقن قيم الصف في الفلتر
نص المعايير ثابت، ولفّه بـ tempo(...) يجعله ديناميكياً. فداخل الأقواس المعقوفة تسمّي حقلاً أو مدخلاً أو متغيراً فتحل قيمته الحالية محله، بلا حاجة إلى $F{} أو $P{} أو $V{}:
$P{REPORT_SCRIPTLET}.tempo("""
field,Operator,{value},AND;
""")فيستطيع تقرير العملاء أن يحمل رابطاً إلى فواتير العميل عن الفترة التي شُغّل عنها التقرير:
NamaRep.listView()
.entityType("SalesInvoice")
.criteria($P{REPORT_SCRIPTLET}.tempo("""
customer.code,Equal,{customerCode},AND;
valueDate,GreaterThanOrEqual,{fromDate},AND;
valueDate,LessThanOrEqual,{toDate},AND;
"""))
.listViewName("SalesInvoicesForCustomer")
.orderBy("valueDate")
.ascending(false)
.toString()تنسيق المعايير
يتبع الفلتر تنسيق معايير النص:
fieldID,operator,value,logic;العوامل: Equal، NotEqual، GreaterThan، GreaterThanOrEqual، LessThan، LessThanOrEqual، StartsWith، NotStartsWith، EndsWith، NotEndWith، Contains، NotContain، In، NotIn
روابط المنطق: AND، OR — التواريخ: dd-MM-yyyy — المراجع: id:entityType:code والكود اختياري
دع النظام يكتب المعايير لك
ابنِ الشروط بصرياً في شاشة Criteria Definition، ثم استخدم Convert to Text. والناتج قالب يعمل تلصقه ثم تجعله ديناميكياً بـ tempo(...).
إنشاء السجلات من التقرير
يستطيع التقرير أن يحمل زراً يفتح سجلاً جديداً مملوءاً سلفاً — سند قبض مقابل الفاتورة التي على السطر، أو أمر شراء مبنياً على قائمة النواقص التي تُطبع. والرابط لا يحفظ شيئاً؛ إنما يفتح شاشة السجل الجديد وحقولها ممتلئة، ويعتمده المستخدم بنفسه.
NamaRep.newWithFields("ReceiptVoucher")
.f("term").value("POTermCode")
.f("book").value("POBook1")
.f("remarks").v("Auto Created")
.f("fromDoc#type").v("SalesInvoice")
.f("fromDoc#code").v($F{code})
.menuCode("NormalReceiptMenu")
.viewName("NormalReceipts")
.toString()
// الشيء نفسه، مكتوباً بالكامل
NamaRep.creator("ReceiptVoucher")
.field("supplier").value(supplierId)
.toString()
حقول المرجع العام تحتاج جزأين
الحقول مثل ref1 إلى ref5، وكل حقل يمكن أن يشير إلى أكثر من نوع سجل، تحتاج أن تخبرها بالنوع كما تخبرها بالقيمة. فخاطب الجزأين على حدة بلاحقة على معرّف الحقل:
.f("ref2#type").v("Warehouse")
.f("ref2#code").v($F{warehouseCode})و#entitytype مقبولة مرادفاً لـ #type، و#value مرادفاً لـ #code.
ويجوز حذف #type حين يسمح الحقل بنوع سجل واحد لا غير — فعندها تختار الشاشة النوع الوحيد المسموح سلفاً وتخفي منتقي النوع، فلا يبقى ما تضبطه. أما الحقل الذي يسمح بنوعين فأكثر فيتجاهل بصمت قيمةً بلا نوع بجانبها، ويصل الحقل فارغاً.
بناء سجل بأسطر
للتقرير الذي يجب أن تسهم حزمة تفاصيله بسطر عن كل صف:
- عرّف متغيراً — سمّه
creatorLink— تعبيرُ قيمته الأولية يبدأ السجل ويقف عند.root():
NamaRep.newWithFields("PurchaseOrder")
.field("term").value("P.Order.Term")
.root()- وفي حزمة التفاصيل، أضف سطراً لكل صف:
$V{creatorLink}
.field("details.item.itemCode").value($F{code})
.field("details.quantity").value($F{qty})
.row($V{REPORT_COUNT})- وحيث ينبغي أن يظهر الرابط — في الملخّص عادةً — حوّل البنّاء المتراكم إلى رابط:
$V{creatorLink}.toString()الموافقات
التقارير هي وسيلة إنجاز كثير من الموافقات فعلياً: يستقبل المعتمِد المستند بالبريد وينقر رابطاً فيه.
// التصرف في المستند كله
NamaRep.approveAllLink($P{REPORT_PARAMETERS_MAP})
NamaRep.rejectAllLink($P{REPORT_PARAMETERS_MAP})
NamaRep.returnAllLink($P{REPORT_PARAMETERS_MAP})
NamaRep.returnAllToPreviousStepLink($P{REPORT_PARAMETERS_MAP})
// أو مرّر القرار: "Approve" أو "Reject" أو "Return"
NamaRep.approveAllLink($P{REPORT_PARAMETERS_MAP}, decision)وللمستندات التي تُعتمد سطراً سطراً:
NamaRep.approveLink($P{REPORT_PARAMETERS_MAP}, $F{lineNumber})
NamaRep.rejectLink($P{REPORT_PARAMETERS_MAP}, $F{lineNumber})
NamaRep.returnLink($P{REPORT_PARAMETERS_MAP}, $F{lineNumber})
NamaRep.returnToPreviousStepLink($P{REPORT_PARAMETERS_MAP}, $F{lineNumber})
// مع سبب
NamaRep.approveLink($P{REPORT_PARAMETERS_MAP}, $F{lineNumber}, reasonCodeOrId)
NamaRep.rejectLink($P{REPORT_PARAMETERS_MAP}, $F{lineNumber}, reasonCodeOrId)
// هل هذا السطر مما يُسأل عنه هذا المعتمِد؟
NamaRep.isConcernedLine($P{REPORT_PARAMETERS_MAP}, $F{lineNumber})استخدم isConcernedLine في شرط طباعة حتى لا تظهر الأزرار إلا بجانب الأسطر التي يستطيع القارئ التصرف فيها.
// افتح حوار الموافقة في المتصفح بدل التصرف مباشرةً
NamaRep.approveFromJS(entityType, entityId, nextStepName,
concernedLines, nextStepSeq, summary)أرصدة إجازات الموظفين
أرصدة الإجازات محسوبة لا مخزّنة، ولذلك لا يستطيع التقرير أن يختارها ببساطة من عمود.
// أنواع الإجازات الثلاثة القياسية
NamaRep.getVacation1RemainderBalance(empIdOrCode)
NamaRep.getVacation2RemainderBalance(empIdOrCode)
NamaRep.getVacation3RemainderBalance(empIdOrCode)
// أي نوع إجازة، وبتاريخ محدد اختيارياً
NamaRep.getVacationRemainderBalance(empCodeOrId, vacationTypeIdOrCode)
NamaRep.getVacationRemainderBalance(empCodeOrId, vacationTypeIdOrCode, atDate)
// موزعاً على السنوات
NamaRep.getRemainderBalancePerYears(employeeId, atDate, yearsCount)المستحق والمستهلك والمتبقي معاً
تُرجع getVacationBalance كائناً واحداً يحمل كل أرقام حساب الرصيد، كل رقم باسمه الخاص. تُقرأ الأرقام هكذا: $V{balance}.assigned و$V{balance}.consumed وهكذا.
// رصيد موظف واحد ونوع إجازة واحد
NamaRep.getVacationBalance(employeeId, vacationType)
NamaRep.getVacationBalance(employeeId, vacationType, atDate)
// بتمرير مدخلات التقرير كأول وسيط تُحفظ النتيجة لبقية التشغيل،
// فيكلف طلب ثلاثة أرقام عملية حساب واحدة
NamaRep.getVacationBalance($P{REPORT_PARAMETERS_MAP}, employeeId, vacationType, atDate)| الحقل | ما الذي يحمله |
|---|---|
assigned | الأيام المستحقة للموظف عن السنة الحالية |
consumed | الأيام المستهلكة فعلاً |
remainder | الأيام المتبقية في التاريخ المطلوب |
yearRemainder | الأيام المتبقية بعد احتساب استحقاق السنة كاملةً، لا الجزء المستحق حتى التاريخ المطلوب فقط. وهو الرقم نفسه الموجود في remainder عند تفعيل خيار «احتساب رصيد الاجازات بناء على تاريخ مباشرة العمل» في إعدادات الموارد البشرية |
balance | الرصيد المستحق الذي بدأ منه الحساب، قبل تطبيق الأرصدة المضافة والأيام المستهلكة |
assignedTillEndOfYear | إجمالي الأيام التي ستكون مستحقة للموظف بنهاية السنة — رصيد نهاية السنة مضافاً إليه الأيام المستهلكة |
remainderTillEndOfYear | الأيام التي ستظل متبقية في نهاية السنة |
والترتيب المعتاد متغيّر واحد للرصيد وحقل نصي لكل رقم:
// متغيّر باسم balance من النوع java.lang.Object
NamaRep.getVacationBalance($P{REPORT_PARAMETERS_MAP}, $F{EmployeeCode}, $F{VacationTypeCode}, $P{atDate})
// ثم في ثلاثة حقول نصية
$V{balance}.assigned
$V{balance}.consumed
$V{balance}.remainderوتعتمد هذه الصيغة المختصرة على أن تكون لغة التقرير Groovy، وهو الإعداد المعتاد. أما في تقرير مضبوط على Java فاكتب $V{balance}.getAssigned() بدلاً منها.
TIP
getVacationAssignedConsumedRemainder هي الصورة الأقدم من الاستدعاء نفسه. تُرجع الأرقام ذاتها مضغوطة في أزواج متداخلة — المستحق، ثم زوج من المستهلك والمتبقي، ثم متبقي السنة — ولا بد من فكها بـ .x و.y.x و.y.y و.z. وهي ما زالت تعمل فتستمر التقارير المكتوبة بها، لكن استخدم getVacationBalance في كل ما هو جديد.
نقاط المكافأة
// ما هو متاح لعميل أو مورد
NamaRep.availableRewardPoints("Customer", $F{customerId})
NamaRep.availableRewardAmount("Customer", $F{customerId})
// ما كسبه مستند بعينه
NamaRep.earnedPoints("SalesInvoice", $F{invoiceId})TIP
availableRewardPoints وavailableRewardAmount تعملان مع أي ملف رئيسي. أما earnedPoints فتعمل مع المستندات فقط — ومع الملف الرئيسي تعود فارغة. وثلاثتها تقبل معرّفاً أو كوداً وسيطاً ثانياً.
الاستعلام من قاعدة البيانات مباشرةً
أحياناً تريد قيمة واحدة لا يصل إليها استعلام التقرير نفسه.
NamaRep.runSQLQuery(sql, paramName, paramValue, paramName, paramValue)
NamaRep.executeQuery(
"SELECT cast(w.name1 collate Arabic_CI_AI_KS_WS as varchar(250))
FROM warehouse w WHERE w.id = :wid",
"wid", $F{wid}
)
// تسطيح عدة صفوف وأعمدة في نص واحد
NamaRep.formatQueryResult(results, "\n", ",") // فاصل الصفوف، فاصل الأعمدةاستعلام لكل صف
تعبير كهذا يُنفَّذ مرة عن كل صف يطبعه التقرير. وفي تقرير من مئة صف يعني ذلك مئة رحلة إلى قاعدة البيانات. فحيث تستطيع أن تأتي بالقيمة من ربط داخل استعلام التقرير نفسه، ضعها هناك.
قراءة إعداد وحدة
NamaRep.getValueFromModuleConfig(moduleId, fieldId)
NamaRep.getValueFromModuleConfig("basic", "value.info.useCurrentUserAsSalesMan")ومعرّفات الوحدات: accounting، basic، supplychain، fixedassets، humanresource، dms، project، ecpa، manufacturing، srvcenter، crm، contracting، travel، realestate، housing، auditing، education، namapos، mc.
قيود الأمان
NamaRep.security()
.fieldEntityType("Account")
.tableAlias("acc")
.capabilities("firstAuthor", "viewCapability", "usageCapability",
"updateCapability", "legalEntity", "branch",
"sector", "department", "analysisSet")يُرجع جزءاً منطقياً من SQL خاصاً بالمستخدم الحالي، يُدرَج في جملة WHERE بـ $P!{}. أما الوصفة الكاملة، بما فيها المدخل المخفي الذي يسكنه وكيفية تغطية أكثر من جدول، ففي قسم «تحديد ما يراه كل مستخدم» من الدليل.
// هل يُسمح لهذا المستخدم برؤية موضوع هذا المدخل؟
NamaRep.canDisplay($P{param})استخدمه في printWhenExpression لإخفاء العمود بدل عرضه فارغاً.
النصوص والأرقام التسلسلية ورموز QR
من HTML إلى نص عادي
NamaRep.htmlToText(htmlContent)حقول النص المنسّق تُخزَّن بصيغة HTML. وطباعتها كما هي تُظهر الوسوم، وهذه الدالة تنزعها.
الأرقام التسلسلية
تُخزَّن الأرقام التسلسلية مضغوطةً في نطاقات. وسّعها لتطبع واحداً في كل سطر، أو اضغط قائمة إلى نطاقات لتبقى الخانة قصيرة:
NamaRep.expandSerials(serials) // واحد في كل سطر
NamaRep.expandSerials(serials, separator)
NamaRep.unzipSerials(serials) // كقائمة
NamaRep.unzipSerialsWithNewLines(serials)
NamaRep.unzipSerialsWithComma(serials)
NamaRep.unzipSerialsWithSeparator(serials, ";")
NamaRep.zipSerialsRange(serials) // إعادة الضغط إلى نطاقاترموز QR لزاتكا
للفواتير التي يجب أن تحمل رمز QR الخاص بهيئة الزكاة والضريبة والجمارك:
NamaRep.genZATCAQR(sellerName, vatNumber, timestamp,
invoiceAmount, vatAmount)
// حين يختلف تاريخ القيمة عن تاريخ الإنشاء
NamaRep.genZATCAQRWithCreationDate(sellerName, vatNumber,
valueDate, creationDate,
invoiceAmount, vatAmount)
// من المستند مباشرةً، فيملأ الحقول نيابةً عنك
NamaRep.genZatcaQrCodeFromEntity(entityType, idOrCode)
NamaRep.zatcaHashedInvoice(entityType, id)رموز QR للجوال
رمز QR يمسحه تطبيق نما للجوال لإنشاء سجل أو تحديثه — تسليم يُعتمد على عتبة الباب، أو مسح حضور في مقر عميل.
NamaRep.mobileQr()
.code("IntegratorCode")
.toString()
// حاملاً قيماً إلى السجل الذي ينشئه
NamaRep.mobileQr()
.code("CustomerAttendance")
.addParam("customer", $F{customerCode})
.addParam("date", $F{valueDate})
.addParam("amount", $F{totalAmount})
.toString()
// مشفّراً، لكل ما هو سرّي ويُطبع على ورق
NamaRep.mobileQr()
.code("SecureIntegrator")
.addParam("sensitive", $F{confidentialData})
.encrypted()
.toString()ويحمل الرمز كودَ الـ integrator ومدخلاته بصيغة JSON، ويفك التطبيق تشفير المشفّر منها تلقائياً. وتصل المدخلات إلى مسار الكيان بصيغة $map.paramName، ولا بد أن يكون الـ integrator نفسه موجوداً سجلاً في Mobile QR Integrator قبل أن يفعل شيء من هذا شيئاً.
مسار المراجعة
يمكن طباعة سجل الإصدارات داخل التقرير، وهو ما تحتاجه مراجعات الامتثال وملفات اعتماد التغيير.
NamaRep.audit(entityType, id, versionNumber, actionType, language, outputFormat)| الوسيط | ما تمرّره |
|---|---|
entityType | نوع السجل، مثلاً "SalesInvoice" أو "Customer" |
id | معرّف السجل |
versionNumber | الإصدار الذي تجري المقارنة معه — الحالي عادةً |
actionType | "Update" في العادة |
language | "arabic" أو "english" |
outputFormat | "html" أو "text" |
NamaRep.audit($F{entityType}, $F{id}, $F{versionNumber}, "Update", "arabic", "html")
NamaRep.audit("Customer", $F{customerId}, $F{currentVersion}, "Update", "english", "text")وما يعود يغطي تغييرات حقول الرأسية بقيمها القديمة والجديدة، وتغييرات الأسطر — مضافة ومحذوفة ومعدَّلة — ومن غيّر ماذا ومتى. و"html" ينتج جدولاً منسّقاً يناسب التقرير أو البريد؛ و"text" ينتج نصاً عادياً بمسافات بادئة يناسب رسالة قصيرة أو رسالة نصية بسيطة.
تجميع عدة حقول في مفتاح واحد
NamaRep.groupExpression(field1, field2, field3)يدمج عدة حقول في قيمة واحدة، لمجموعة تقرير يجب أن تنكسر على تركيبة لا على عمود واحد.
مدخلات النظام الجاهزة
يتلقى كل تقرير المدخلات التالية دون أن يعرّفها. أشر إلى الواحد منها بـ $P{name} — مثلاً $P{loginLegalEntityName1} لاسم الشركة في رأس الصفحة، أو $P{loginLegalEntityId} لتسعير سطر بحسب الشركة التي سجّل المستخدم دخوله بها.
القائمة الكاملة لمدخلات النظام
المستخدم وتسجيل الدخول
loginLanguage— اللغة التي يُشغَّل بها التقريرoriginalLoginLanguage— اللغة التي سجّل بها المستخدم دخولهloginUserId،loginUserCode،loginUserName1،loginUserName2loginUserTreatAsAuthorIds— المستخدمون الذين ينوب عنهم هذا المستخدمloginEmployeeId
الهيكل التنظيمي
loginLegalEntityId،loginLegalEntityCode،loginLegalEntityName1،loginLegalEntityName2loginSectorId،loginSectorCode،loginSectorName1،loginSectorName2loginBranchId،loginBranchCode،loginBranchName1،loginBranchName2loginDepartmentId،loginDepartmentCode،loginDepartmentName1،loginDepartmentName2loginAnalysisSetId،loginAnalysisSetCode،loginAnalysisSetName1،loginAnalysisSetName2
معرّفات المنظمة العامة
publicLegalEntityId،publicSectorId،publicBranchIdpublicDepartmentId،publicAnalysisSetId
الشعارات والعلامة التجارية
loginLegalEntityLogo— الشعار الرئيسي، صورةًloginLegalEntityLogo2إلىloginLegalEntityLogo5— شعارات إضافيةreportsFooterNote1،reportsFooterNote2— سطرا التذييل من الإعداد العام
سياق التقرير
formEntityType— نوع السجل الذي يخصه النموذج، ويفيد في ترجمة التسمياتreportCode،reportId،reportName1،reportName2namaReportInstance— التقرير الجاري نفسهrunId— معرّف فريد لهذا التشغيل
العناوين
guiServerURL،externalServerURL،currentGUIURL
الموافقات
concernedLines— الأسطر التي يُسأل عنها هذا المعتمِدcandidateEmployeeId،candidateEmployeeCode،candidateEmployeeName1،candidateEmployeeName2approvedRecordId،approvedRecordType،approvedRecordCodeapprovalSecret،approvalStepSeq
الصلاحيات
allowedCapabilities،allowedEntities،allowedDocuments،allowedFilesnotAllowedEntities،notAllowedDocuments،notAllowedFilesaccessibleLegalEntityIds،accessibleSectorIds،accessibleBranchIdsaccessibleDepartmentIds،accessibleAnalysisSetIdslegalEntityNotUsedInSecurity،sectorNotUsedInSecurity،branchNotUsedInSecurity،departmentNotUsedInSecurity،analysisSetNotUsedInSecurity
ما بقي
posShiftCode— وردية نقطة البيع التي يخصها الإيصالcurrentReplicationSiteId،currentReplicationSiteCode،currentReplicationSiteName1،currentReplicationSiteName2
لا وجود لمدخل اسمه currentUser
$currentUser() دالة قيمة افتراضية لمدخل، لا مدخلَ نظام — فـ $P{currentUser} غير موجود، والتقرير الذي يشير إليه يفشل في التصريف. وللمستخدم الذي يشغّل التقرير استخدم $P{loginUserId} أو $P{loginUserCode} أو $P{loginUserName1} / $P{loginUserName2}. ودوال القيم الافتراضية مسرودة في الدليل.