تسجيل خطوة إضافة على الخادم
معرّف المخطط: plugin-step@1
المخطط القابل للقراءة آليًا: plugin-step.schema.json
الغلاف المرجعي لتسجيل خطوة إضافة على الخادم (القسم 6.6) — وهي منطق يعمل على الخادم عند إنشاء سجل على جدول أو تحديثه أو حذفه. ويحدد الغلاف متى يعمل المنطق وكيف؛ أما التنفيذ المكتوب بلغة BS Lang فيحمل السكربت مضمَّنًا أيضًا، ويجري التحقق منه بقواعد لغة BS Lang إضافةً إلى هذا المخطط. ويجري التحقق من التسجيل بالطريقة نفسها سواء جاء من مصمّم، أو من استدعاء API، أو من مساعد ذكاء اصطناعي، أو من استيراد حل.
الشكل
| الحقل | النوع | مطلوب | ملاحظات |
|---|---|---|---|
logicalName | string | نعم | مسبوق ببادئة الناشر، بصيغة snake_case صغيرة. |
displayName | string | نعم | اسم الخطوة المقروء. |
publisherPrefix | string | نعم | بادئة الناشر المالك التي يجب أن يُنسَب إليها logicalName — لا بادئة الجدول الذي تراقبه الخطوة. فالجدول القياسي مثل account بلا بادئة عن قصد، والتحقق مقابله كان يجعل تسمية خطوة عليه مستحيلة. |
entity | string | نعم | الجدول الذي تُطلق عمليات الكتابة عليه الخطوة. |
message | string | نعم | Create أو Update أو Delete. |
stage | string | نعم | PreValidation أو PreOperation أو PostOperation. |
executionMode | string | نعم | Synchronous (متزامن، داخل عملية الكتابة) أو Asynchronous (يُصفّ عبر صندوق الصادر ويُنفَّذ بعد اعتماد الكتابة). والخطوة غير المتزامنة يجب أن تكون PostOperation: فهي تعمل بعد حفظ السجل فعلاً، فلم يبقَ لها ما ترفضه. |
rank | integer | نعم | ترتيب التنفيذ بين الخطوات المشتركة في الجدول وال رسالة والمرحلة نفسها؛ والأقل يعمل أولًا. |
isEnabled | boolean | نعم | الخطوة المعطّلة تُخزَّن لكنها لا تُنفَّذ أبدًا. |
implementation | object | نعم | تنفيذ واحد بالضبط (انظر أدناه). |
التنفيذ
| الحقل | النوع | مطلوب | ملاحظات |
|---|---|---|---|
type | string | نعم | BsLangScript أو DotNetAssembly. |
script | string | عند BsLangScript | شيفرة BS Lang المصدرية. |
assemblyReference | string | عند DotNetAssembly | مفتاح التخزين للتجميعة المرفوعة. |
typeName | string | عند DotNetAssembly | نوع الإضافة المراد إنشاء مثيل منه. |
المراحل
- PreValidation — تعمل قبل العملية؛ لفحوص الشكل والأمان العامة.
- PreOperation — تعمل قبيل الكتابة مباشرةً؛ ويجوز لها تغيير قيم حقول على السج ل أو رفض العملية بأكملها.
- PostOperation — تعمل بُعيد الكتابة مباشرةً؛ للتغييرات المتتالية أو التابعة.
ترفض الخطوة عمليةً باستدعاء throwValidationError("reason")، وهو ما يوقف الكتابة ويعيد
السبب إلى المستدعي.
مثال
{
"logicalName": "par_invoice_reject_negative",
"displayName": "Reject negative invoice total",
"entity": "par_invoice",
"message": "Create",
"stage": "PreOperation",
"executionMode": "Synchronous",
"rank": 1,
"isEnabled": true,
"implementation": {
"type": "BsLangScript",
"script": "if (getValue(\"par_total\") < 0) { throwValidationError(\"Total cannot be negative.\"); }"
}
}
دوال المضيف لسكربتات BS Lang
تقرأ خطوة من نوع BsLangScript السجلَ وتغيّره عبر دوال المضيف هذه:
| الدالة | الغرض |
|---|---|
getValue("field") | القيمة الحالية لحقل على السجل الجاري كتابته. |
setValue("field", value) | تغيير قيمة حقل (يُعتدّ بها في PreOperation فقط). |
getPreValue("field") | القيمة السابقة للحقل (في التحديثات والحذف). |
throwValidationError("message") | رفض العملية والتراجع عنها. |
message() / stage() / entityName() | العملية والمرحلة والجدول الحالي. |
userId() / depth() | المستخدم الذي أطلق العملية وعمق التكرار. |
الوصول إلى جداول أخرى
الدوال أعلاه لا ترى إلا السجل قيد الكتابة. وهذه تصل إلى بقية البيئة، وكل واحدة منها تُنفَّذ بهوية المستخدم الذي أطلق الخطوة — فالسكربت ليس طريقاً للالتفاف على صلاحية، والخطوة التي لا يستطيع مستدعيها قراءة جدول لا تحصل منه على شيء.
| الدالة | الغرض |
|---|---|
getRecord("table", id) | سجل واحد ككائن، أو فارغ إن لم يوجد. و.id عليه. |
queryRecords("table", "field", value) | الصفوف التي يساوي فيها حقلٌ قيمةً، كقائمة كائنات. بحد أقصى 200. |
runQuery({ … }) | ينفّذ مستند parsware-query@1 كاملاً — مرشّحات and/or متداخلة بعشرة معاملات، وارتباطات، وترتيب، وتصفّح، أو aggregate يجيب بمجموعات. وهي لغة الاستعلام نفسها التي يُنتجها «البحث المتقدم» ويخزّنها الاستعلام المحفوظ. الصفوف محدودة بـ200؛ والتجميع غير محدود. والعمود المرتبط يُقرأ تحت اسمه المستعار (row.acct.par_industry)، و'from' في الارتباط يجب أن يكون بين اقتباسين لأن from كلمة مفتاحية في BS Lang. |
runSavedQuery("name") | ينفّذ استعلاماً محفوظا ً باسمه المنطقي. فالتعقيد يبقى في مصمّم ويسافر مع الحل، ولا يسمّي السكربت إلا السؤال. |
countRecords("table", "field", value) | كم عددها، دون جلبها — عدٌّ حقيقي، فيصحّ حتى بعد حد الـ200 صف الذي يحدّ queryRecords. |
createRecord("table", { field: value, … }) | يُنشئ سجلاً ويعيد معرّفه. |
updateRecord("table", id, { field: value, … }) | يغيّر الحقول المذكورة وحدها. |
deleteRecord("table", id) | يحذف سجلاً. |
الكتابة بهذه الطريقة تُشغّل القواعد المعتادة، فالخطوة المسجّلة على الجدول المكتوب تعمل بدورها.
وهذا التداخل محدود بعمق ثمانية، وdepth() تخبر السكربت أين هو — لكن الحد شبكة أمان لا تصميم.
فالخطوة التي تكتب الجدول الذي تراقبه ستُطلق نفسها، وعلاجها شرطٌ أو جدولٌ آخر.
استيراد منطق مشترك
يمكن لسكربت الخطوة أن يستورد سكربت حلّ بالاسم بـ import، تماماً كقاعدة عمل على جهة العميل،
فتُكتب القاعدة التي تفرضها عدة خطوات مرة واحدة:
import 'par_approval_rules';
if (getValue('par_total') > par_approval_rules.limit()) { … }
كلا نوعَي علامات الاقتباس يعمل. ويجب أن تكون الوحدة سكربت حلّ في البيئة نفسها؛ وأي شيء آخر يفشل عند حفظ الخطوة لا عند تنفيذها.