SBO_SP_TransactionNotification: أمثلة لـ SQL Server و HANA

بقلم زين عباس, كودينوفا · آخر تحديث

الإجابة المختصرةSBO_SP_TransactionNotification إجراء مخزن يستدعيه SAP Business One داخل حركة قاعدة البيانات نفسها في كل مرة يُضاف فيها كائن أو يُحدَّث أو يُحذف أو يُلغى أو يُغلق. إذا ضبط كودك قيمة error على أي رقم غير 0، يتراجع SAP B1 عن الحركة ويعرض رسالة error_message للمستخدم. إنها أبسط طريقة لفرض قاعدة عمل على كل القنوات دفعة واحدة: عميل B1، وإضافات DI API، و Service Layer، وعمليات استيراد البيانات.

فيما يلي طريقة ربط الإجراء، والقواعد نفسها مكتوبة لـ SQL Server ولـ HANA، ورموز أنواع الكائنات التي تحتاجها فعلًا، والأخطاء التي تظهر أكثر من غيرها في الإجراءات القائمة.

كيف يعمل SBO_SP_TransactionNotification؟

تأتي كل قاعدة بيانات شركة والإجراء منشأ فيها مسبقًا مع كتلة فارغة بعنوان “ADD YOUR CODE HERE”. يمرر SAP B1 خمسة مدخلات:

المعاملالنوعما الذي يحمله
object_typenvarchar(30)رمز الكائن كنص: '17' لأمر البيع، و'2' لشريك الأعمال، أو رمز كائن UDO
transaction_typenchar(1)A إضافة، U تحديث، D حذف، C إلغاء، L إغلاق
num_of_cols_in_keyintعدد أعمدة المفتاح في الكائن
list_of_key_cols_tab_delnvarchar(255)أسماء أعمدة المفتاح مفصولة بعلامة Tab (عادةً DocEntry أو CardCode أو ItemCode)
list_of_cols_val_tab_delnvarchar(255)قيم المفتاح مفصولة بعلامة Tab، كنص

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

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

ماذا تعني رموز أنواع الكائنات؟

object_type هي القيمة نفسها التي تراها في عمود ObjType في كل جدول رأس. هذه الرموز تغطي معظم القواعد:

الرمزالكائنجدول الرأس
2شريك أعمالOCRD
4صنفOITM
13فاتورة مبيعاتOINV
14إشعار دائن للمبيعاتORIN
15مذكرة تسليمODLN
17أمر بيعORDR
18فاتورة مشترياتOPCH
20إيصال استلام بضاعة لأمر الشراءOPDN
22أمر شراءOPOR
23عرض أسعار مبيعاتOQUT
24دفعة واردةORCT
30قيد يوميةOJDT
46دفعة صادرةOVPM
59 / 60استلام بضاعة / صرف بضاعةOIGN / OIGE
67تحويل مخزونOWTR
112مسودة (أي نوع مستند)ODRF
202أمر إنتاجOWOR

بالنسبة لكائن UDO، تكون object_type هي رمز UDO الذي سجّلته، لا رقمًا. وتفاصيل الحقول للجداول الرئيسية موجودة في مرجع جداول SAP B1 لدينا.

كيف تمنع أمر بيع بلا رقم أمر شراء العميل؟

قاعدة أولى نموذجية: يجب أن يحمل أمر البيع مرجع العميل (NumAtCard، ويظهر في العميل باسم “Customer Ref. No.”).

SQL Server ‏(T-SQL):

-- inside the "ADD YOUR CODE HERE" block
IF @object_type = '17' AND @transaction_type IN ('A', 'U')
BEGIN
    IF EXISTS (SELECT 1
               FROM ORDR
               WHERE DocEntry = CAST(@list_of_cols_val_tab_del AS INT)
                 AND ISNULL(NumAtCard, '') = '')
    BEGIN
        SET @error = 17001
        SET @error_message = N'Sales order: enter the Customer Ref. No.'
    END
END

SAP HANA ‏(SQLScript):

-- declare at the top of the procedure, next to error and error_message:
-- cnt INT;

IF :object_type = '17' AND (:transaction_type = 'A' OR :transaction_type = 'U') THEN
    SELECT COUNT(*) INTO cnt
    FROM "ORDR"
    WHERE "DocEntry" = CAST(:list_of_cols_val_tab_del AS INT)
      AND IFNULL("NumAtCard", '') = '';

    IF :cnt > 0 THEN
        error := 17001;
        error_message := N'Sales order: enter the Customer Ref. No.';
    END IF;
END IF;

نسخة HANA ليست بحثًا واستبدالًا لنسخة T-SQL. أربعة فروق مهمة:

  1. التعريفات في الأعلى. يريد SQLScript السطر cnt INT; في قسم التعريفات قبل أول جملة، حيث error و error_message معرّفان أصلًا. تعريفه في وسط الجسم يفشل عند الترجمة. وإن شئت، ضع القاعدة في كتلة BEGIN ... END خاصة بها مع DECLARE خاص بها.
  2. لا يوجد IF EXISTS (SELECT ...). احسب العدد في متغير ثم اختبر المتغير.
  3. اقرأ المعاملات بنقطتين (:object_type)، وأسند القيم بـ :=.
  4. اقتبس الأسماء ذات الأحرف المختلطة. يجب اقتباس "DocEntry" و "NumAtCard" لأن HANA يعامل الأسماء غير المقتبسة كأحرف كبيرة.

استبدالات الصياغة الأخرى (ISNULL إلى IFNULL، و GETDATE إلى CURRENT_DATE، و + إلى ||) هي نفسها التي تجريها في الاستعلامات المحفوظة. يتعامل محوّل SQL إلى HANA لدينا معها في مرور أول سريع، ويغطي دليل الترحيل إلى HANA إعادة كتابة الإجراء كاملة.

كيف تتحقق من أسطر المستند؟

فحوص الرأس سهلة لأن المفتاح هو الرأس. أما للأسطر فاربط جدول الأسطر عبر DocEntry. مثال: امنع فاتورة مبيعات فيها أي سطر بسعر صفر لصنف يُباع وليس مجانيًا.

SQL Server:

IF @object_type = '13' AND @transaction_type = 'A'
BEGIN
    IF EXISTS (SELECT 1
               FROM INV1 T1
               INNER JOIN OITM T2 ON T2.ItemCode = T1.ItemCode
               WHERE T1.DocEntry = CAST(@list_of_cols_val_tab_del AS INT)
                 AND T1.Price = 0
                 AND T2.SellItem = 'Y')
    BEGIN
        SET @error = 13001
        SET @error_message = N'A/R invoice: a line has price 0. Check the price list.'
    END
END

SAP HANA:

IF :object_type = '13' AND :transaction_type = 'A' THEN
    SELECT COUNT(*) INTO cnt
    FROM "INV1" T1
    INNER JOIN "OITM" T2 ON T2."ItemCode" = T1."ItemCode"
    WHERE T1."DocEntry" = CAST(:list_of_cols_val_tab_del AS INT)
      AND T1."Price" = 0
      AND T2."SellItem" = 'Y';

    IF :cnt > 0 THEN
        error := 13001;
        error_message := N'A/R invoice: a line has price 0. Check the price list.';
    END IF;
END IF;

ضع رقم السطر في الرسالة متى استطعت ('Line ' + CAST(T1.LineNum + 1 AS NVARCHAR) في SQL Server، و'Line ' || TO_NVARCHAR(T1."LineNum" + 1) في HANA). يبدأ LineNum من 0 بينما يعرض العميل الصفوف بدءًا من 1، وإلا سيبحث المستخدمون في فاتورة من 60 سطرًا.

كيف تجعل الرقم الضريبي إلزاميًا للفوترة الإلكترونية؟

في مشاريع ZATCA و SRI و FEL و DTE، يُعد غياب الرقم الضريبي للمشتري أو خطأ صيغته سببًا شائعًا لرفض الفاتورة بعد أن تكون قد رُحّلت بالفعل في SAP B1. إيقاف ذلك عند شريك الأعمال أرخص من إصلاح فاتورة مرفوضة لاحقًا.

-- SQL Server: customers must have a tax ID (LicTradNum)
IF @object_type = '2' AND @transaction_type IN ('A', 'U')
BEGIN
    IF EXISTS (SELECT 1 FROM OCRD
               WHERE CardCode = @list_of_cols_val_tab_del
                 AND CardType = 'C'
                 AND ISNULL(LicTradNum, '') = '')
    BEGIN
        SET @error = 2001
        SET @error_message = N'Customer: Federal Tax ID is required for e-invoicing.'
    END
END

لاحظ أن مفتاح شريك الأعمال هو CardCode، وهو نص، فلا يوجد تحويل نوع (CAST) هنا. وفي HANA تستخدم الكتلة نفسها "CardCode" = :list_of_cols_val_tab_del مع نمط العدّ السابق.

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

ما الأخطاء الشائعة؟

قواعد تتجاهل المسودات. إذا حوّل إجراء موافقة أمر بيع إلى مسودة، يحفظه SAP B1 ككائن من النوع 112 في ODRF/DRF1، لا كـ 17. قاعدتك للنوع 17 لا تعمل إلا عند تحويل المسودة إلى مستند حقيقي. وهذا مقبول غالبًا، لكن إذا كان الهدف من القاعدة إيقاف البيانات الخاطئة قبل أن يراها المعتمِد، فأضف فرعًا للنوع 112 مصفّى بالشرط ODRF.ObjType = '17'.

إرجاع مجموعات نتائج إضافية. في SQL Server، قد تنتج جملة SELECT أو PRINT شاردة مستخدمة لتصحيح الأخطاء مجموعات نتائج قبل جملة SELECT @error, @error_message النهائية، وقد يقرأ SAP B1 المجموعة الخاطئة. أبقِ مخرجات التصحيح خارج الإجراء؛ وسجّلها في جدول إن اضطررت.

الفروع البطيئة. كل عملية حفظ في الشركة تنتظر الإجراء. صفِّ حسب object_type و transaction_type في جملة IF الخارجية، واستخدم المفتاح في جملة WHERE، وتجنب المؤشرات. القاعدة التي تمسح OINV دون شرط DocEntry ستبدو جيدة في شركة اختبار ومؤلمة في قاعدة بيانات فيها فواتير سنوات.

رموز أخطاء لا يمكن تتبعها. استخدم نظام ترقيم، مثل نوع الكائن مضروبًا في 1000 مع عدّاد (17001 و13001 و2001). عندما يرسل مستخدم لقطة شاشة، تعرف أي فرع عمل دون قراءة الإجراء كله.

الإلغاء والإغلاق حدثان منفصلان. يصل C و L كنوعي حركة مستقلين. القاعدة المكتوبة لـ A و U لن تمنع أحدًا من إغلاق أمر بيع. إذا كنت تحتاج منع الإغلاق (مثلًا عندما تكون هناك مذكرات تسليم مفتوحة مرتبطة)، فتعامل مع L صراحة.

منع أشياء يحدّثها SAP B1 في الخلفية. تُحدَّث بعض الكائنات كأثر جانبي لحركة أخرى (حقول المخزون في الصنف عند ترحيل مستند، ورصيد شريك الأعمال). قاعدة صارمة على U للكائن 4 أو 2 قد تمنع مستند مبيعات بسبب تحديث جانبي. اجعل قواعد التحديث ضيقة، واختبرها بترحيل مستندات، لا بتعديل البيانات الرئيسية فقط.

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

وماذا عن SBO_SP_PostTransactionNotice؟

له المعاملات نفسها، لكن SAP B1 يستدعيه بعد تثبيت الحركة. ولا يستطيع منع أي شيء. استخدمه للمهام الجانبية: الكتابة في جدول سجل، أو وضع DocEntry في طابور لتكامل ما (مُرسِل الفوترة الإلكترونية، أو إشعار واتساب، أو مزامنة مع نظام آخر). اجعله خفيفًا أيضًا، ولا تستدعِ خدمات خارجية من داخله أبدًا. جدول طابور مع خدمة منفصلة تقرؤه هو التصميم الأكثر أمانًا.

كيف ترى الإضافات و Service Layer الخطأ؟

تعمل القاعدة على مستوى قاعدة البيانات، فتنطبق أيًّا كان مصدر المستند. تعيد الدالة Add() في DI API نتيجة غير صفرية، وتعود رسالتك عبر GetLastError. ويفشل طلب POST في Service Layer بخطأ يحمل نص الرسالة (راجع دليل Service Layer لمعرفة كيفية قراءته). وتفشل عمليات الاستيراد في Data Transfer Workbench صفًا بصف. لهذا يُعد الإجراء مكانًا جيدًا للقواعد التي يجب أن تسري في كل مكان، ولهذا يجب أن تكون رسائلك مفهومة لمن يقرأ سجل تكامل، لا لمستخدم العميل فقط. ولجانب DI API من معالجة الأخطاء، راجع دليل رموز أخطاء DI API لدينا.


تحتاج قواعد كهذه مكتوبة ومختبرة على SQL Server و HANA معًا؟ هذا جزء من عملنا في تطوير SAP Business One.

المصادر

خدمات ذات صلة

الأسئلة الشائعة

أسئلة شائعة

أين أجد SBO_SP_TransactionNotification؟

في SQL Server يوجد تحت قاعدة بيانات الشركة في Programmability > Stored Procedures. وفي HANA يوجد في مخطط الشركة تحت Procedures. عدّله باستخدام SSMS أو SAP HANA Studio / Database Explorer، واختبره أولًا على نسخة من الشركة.

هل يمكن أن يكون لدي أكثر من قاعدة للكائن نفسه؟

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

هل TransactionNotification هو نفسه إجراء الموافقة؟

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

جاهز للبدء؟

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