تكامل Microsoft Dynamics — التدفّق من التحضير إلى الـcallback
Dynamics لا يطبع: يحضّر الشيك بنداء API ويستلم رابطًا يفتحه موظف مسجَّل الدخول فيطبع من شاشة الطباعة المعتادة. الصف يُكتب قبل الطباعة، وعند الإنهاء يُؤكَّد وتُرسل النتيجة والصورة إلى Dynamics. ثماني خطوات وأربع نقاط نهاية وثلاث طبقات ضد التكرار.
آخر تحديث: 2026-10-24
في الصفحة دي
الفكرة
البرنامج لا يطبع بالنيابة عن أحد — الطباعة قرار موظف من متصفحه على الطابعة الموصولة به. لذلك التكامل قسمان: نداء API يحضّر الشيك، وجلسة موظف تطبعه.
[Dynamics] ──(1) POST /api/cheque-log/dynamics بمفتاح API──▶ [الشيكات] يحضّر ويخزّن مسودة (ساعة)
[Dynamics] ◀──(2) launch_url + launch_token ─────────────────┘
│ (3) يعرض الرابط للموظف أو يحوّله إليه
▼
[متصفح الموظف — مسجَّل الدخول]
(4) يفتح /bank/{id}/template/{id}?dynamics_launch=TOKEN
(5) GET …/launch/{token} تحمّل المسودة في شاشة الطباعة
(6) POST …/launch/{token}/register ⟹ يُكتب الصف غير مؤكَّد — ثم يطبع من الشاشة المعتادة
(7) POST …/launch/{token}/complete بعد إغلاق نافذة الطباعة ⟹ يُؤكَّد الصف
[Dynamics] ◀──(8) callback HTTP POST «cheque.printed» ─── [الشيكات]
الخطوة 1 لا تُنشئ شيكًا — لا صف في القاعدة حتى الخطوة 6 (التسجيل قبل الطباعة). والخطوات 5 و6 و7 جلسة متصفح لا تُنادى من سيرفر.
الخطوة 1 — التحضير: POST /api/cheque-log/dynamics
(واسمه البديل POST /api/cheque-logs/dynamics.) الحقول وخريطتها في الحقول والنموذج.
curl -s -X POST "APP_URL/api/cheque-log/dynamics" \
-H "X-API-KEY: KEY" -H "Content-Type: application/json" -H "Accept: application/json" \
-d '{
"external_id": "DYN-1001",
"bank_id": 2,
"payee_name": "شركة النيل للتوريدات",
"net_amount": "1250.50",
"currency_code": "EGP",
"issue_date": "2026-10-15",
"document_no": "PV-1001",
"memo": "دفعة مورّد"
}'
200 (أو 302 في وضع التحويل redirect: true):
{
"success": true,
"message": "تم تجهيز الشيك للمعاينة والطباعة",
"action": "prepared",
"launch_token": "<48 محرفًا>",
"save_mode": "after_print",
"data": { "bank_name": "بنك مصر", "template_name": "بنك مصر - نموذج 1", "recipient_name": "…",
"amount": "1250.50", "external_source": "microsoft_dynamics", "external_id": "DYN-1001",
"request_hash": "…", "template_selection_required": false, "available_templates": [] },
"preview_url": "APP_URL/bank/2/template/5?dynamics_launch=…",
"launch_url": "…", "redirect_url": "…", "print_url": "…"
}
الروابط الأربعة قيمة واحدة بأربعة أسماء. احفظ launch_token وسلّم launch_url للموظف. المسودة تعيش ساعة (DYNAMICS_LAUNCH_TTL_SECONDS).
template_selection_required: true = حُلّ البنك ولم يُحسم النموذج (عدة نماذج نشطة بلا مُعلَّم وحيد): الرابط يصير /bank/{id}?dynamics_launch=… ويختار الموظف النموذج على الشاشة — وavailable_templates تحمل الخيارات.
الخطوات 5 و6 و7 — جلسة الموظف
| النقطة | الحارس | الردّ |
|---|---|---|
GET /api/cheque-log/dynamics/launch/{token} |
جلسة + شركة نشطة | 200 بالمسودة (data + resolved_bank_id + resolved_template_id) · 404 {"message":"Dynamics launch draft not found or expired."} |
POST /api/cheque-log/dynamics/launch/{token}/register |
جلسة + صلاحية إدارة دفعات الطباعة | 201 action: "registered" · 200 reused · 422 «الصفحة قديمة — حدّثها ثم أعد الطباعة» بلا لقطة الرسم، ورفض التحقّق (ومنه تكرار رقم الشيك وحارس الورقة، و«تاريخ الشيك يجب أن يقع بين سنة 2000 وسنة 2100.» على cheque_date بعد دمج جسم التسجيل مع المسودة) — كلها قبل الورق |
POST /api/cheque-log/dynamics/launch/{token}/complete |
جلسة + صلاحية إدارة دفعات الطباعة | 200 action: "confirmed" · 200 reused عند التكرار · 422 «الصفحة قديمة…» بلا صف مسجَّل · «سجل هذا الشيك حُذف بعد تسجيله قبل الطباعة — لا يمكن إكماله.» |
الشاشة تنادي الثلاثة بنفسها؛ ما يهمّ المكامِل: الصف يُكتب في التسجيل قبل الورق غير مؤكَّد، والإنهاء بعد الورق يؤكّده ويرسل الـcallback ولا يكتب صفًّا — إنهاء بلا تسجيل سابق يُرفض «الصفحة قديمة». جسم التسجيل يعلو المسودة في كل حقل عدا الشركة، وجسم الإنهاء صورته وحدها. ومع cheque_leaf_id (في المسودة أو في جسم التسجيل): التحضير يفحص الورقة ولا يحجزها، والتسجيل يقفلها ويفحصها ويحجزها للصف (رقم الشيك والدفتر والحساب منها)، والإنهاء يجعلها مستخدمة — والرفض برسائل حارس الورقة. ورقة المسودة تعبر بلا قيد «التالية»، وورقة يختارها الموظف في الشاشة أثناء الإطلاق تحتاج صلاحية «اختيار ورقة من الدفتر» إن لم تكن التالية. والتسجيل يفحص المراجع (الحساب · الدفتر · الورقة · الشريك) ضد شركة المسودة.
صورة الشيك في الإنهاء
شاشة الطباعة تلتقط صورة الشيك بعد الطباعة، من نفس البيانات التي رُسم منها الورق. الطباعة لم تعد تنتظر الصورة.
image_base64اختياري: صورة JPEG (جودة 0.72) كـdata URI (data:image/jpeg;base64,...).- لو فشل الالتقاط بعد محاولة إضافية، يُؤكَّد الشيك ويُرسل الـcallback بلا صورة (
image_base64: null) — الورق خرج بالفعل، والسجل لا يعتمد على الصورة. save_cheque_imageيُهمَل على هذه النقطة.- التخزين: الصورة تُمرَّر إلى الـcallback كما هي ولا تُخزَّن نسخة على الخادم (لا ملف ولا علامة مائية). ردّ الإكمال يرجّع
image_url: nullوimage_saved: false.
مثال الطلب:
{
"image_base64": "data:image/jpeg;base64,/9j/4AAQ..."
}
تنبيه
liner_enabled وtwo_lines تدوسان المسودة: غيابهما في جسم التسجيل = false — من فعّل التسطير في التحضير ولم يُعِده في التسجيل يُحفظ شيكه بلا تسطير.
والصف يُكتب عند التسجيل بـprinted_at (لحظة التسجيل) · printed_by = الموظف · printed_status = false حتى الإنهاء · الحالة الابتدائية من الاتجاه · external_source · external_id · request_hash من المسودة لا من الطلب. والإنهاء يقلب printed_status إلى true ولا يغيّر حقلًا آخر. إلا ورقة الدفتر: تصير مستخدمة في ترانزاكشن الإنهاء نفسها.
الخطوة 8 — الـcallback
بعد التأكيد، نداء HTTP POST واحد إلى DYNAMICS_PRINT_CALLBACK_URL بحدث cheque.printed وبيانات الشيك وصورته (إن التُقطت). محاولة واحدة متزامنة بلا إعادة (تُعاد فقط إن نودي الإنهاء ثانية ولم تكن السابقة sent). نتيجته في ردّ الإنهاء تحت dynamics_callback: sent · failed · skipped (لا عنوان مضبوط).
حقول الصورة في الحمولة:
| الحقل | القيمة |
|---|---|
image_url |
مهمل ودائمًا null — يبقى في الحمولة حتى لا تنكسر المحلِّلات القائمة |
image_base64 |
صورة JPEG المُلتقطة بعد الطباعة بلا علامة مائية، أو null عند فشل الالتقاط |
image_mime_type |
image/jpeg، أو null بلا صورة |
image_file_name |
cheque-<id>.png — الامتداد .png للتوافق، والمحتوى JPEG |
ما يراه الموظف: «تم حفظ الشيك وإرسال حالة الطباعة إلى Dynamics بنجاح» · «… لأن إعدادات الربط غير مكتملة» · «… تعذر إرسال صورة الشيك وحالة الطباعة».
الشيك الذي خرج ورقه ولم يُكمَل — «إعادة إرسال الإكمال»
إعادة تحميل الصفحة أو إغلاقها بين الطباعة والإنهاء لا تُضيّع الشيك: صفه مكتوب قبل الورق، فيظهر كـ«خرج ورقه ولم يُبلَّغ Dynamics» في ثلاثة مواضع — العرض عند فتح شاشة الطباعة التالي · كارت على صفحة الشيك · بند «يحتاج تدخّلًا» في لوحة التحكم. كلها زرّ واحد «إعادة إرسال الإكمال»: يؤكّد الطباعة ويُبلغ Dynamics بـcheque.printed بلا صورة الشيك وlaunch_token: null.


- النقطة:
POST /api/cheque-log/{id}/dynamics-resend— جلسة + صلاحية إدارة دفعات الطباعة، ولمن طبع الشيك وحده («غير مصرّح بإكمال هذا السجل.»). شيك غير معلَّق ⟹ok: falseوcode: "not_pending". - الرسالة تقول النتيجة: «تم تأكيد الطباعة وإبلاغ Dynamics — بلا صورة الشيك.» · «… ولم يُبلَّغ Dynamics لأن إعدادات الربط غير مكتملة.» · «… لكن تعذّر إبلاغ Dynamics. بلّغ مسؤول الربط.»
- «تأكيد» العادي مرفوض لشيك Dynamics — لا تأكيد بلا callback، فالمخرج الوحيد هو هذا الزرّ.
ضد التكرار — ثلاث طبقات
- نفس الرمز مرتين: المسودة تحمل الصف المسجَّل ⟹ تسجيل ثانٍ 200
reused(«الشيك مسجَّل بالفعل لهذا الإطلاق — جارٍ الطباعة») وإنهاء ثانٍ 200reused— والصف كما هو (التحقق وحلّ النموذج يُتخطّيان). - المطابقة بالمفتاح الخارجي:
external_source+external_id(وإن غاب:request_hash) بقفل صف — الصف المطبوع يُعاد بلا لمس والحمولة الجديدة تُهمل. - قيد القاعدة:
UNIQUE(external_source, external_id).
request_hash يُحسب من 17 حقلًا (البنك والنموذج والاتجاه والتاريخ والمستفيد والمبلغ والعملة والرقم والتسطير…) ويستثني حقول القسيمة ومعرّفات الحساب/الدفتر/الشريك. أرسل external_id دائمًا.
الشركة
تُحسم عند التحضير من شركة المفتاح وتُحفظ في المسودة؛ التسجيل يقرأها منها ولا يقبل تغييرها من جسم الطلب. وإن عُطِّلت شركة المفتاح أو حُذفت بين التحضير والطباعة، يُرفض التسجيل بـ422 «شركة هذا الشيك معطَّلة أو محذوفة — لا يمكن إكماله الآن…» قبل الورق ولا تُمَسّ المسودة — تعود بإعادة تفعيل الشركة أو استرجاعها. حمولة التحضير التي تخالفها (company_id أو مرجع من شركة أخرى) ⟹ 422. وعند تحديث صف قائم بشركة مختلفة: 422 السجل ده مسجّل لشركة تانية. والصف النهائي (تالف · ملغى · مُعاد إصداره) يرفض التحديث — والإنهاء — برسالته.
الإعداد
في البرنامج — تبويب Dynamics في شاشة التكامل ومتغيّرات البيئة على الخادم:

| المتغيّر | الافتراضي | الأثر |
|---|---|---|
DYNAMICS_PRINT_CALLBACK_URL |
فارغ | فارغ ⟹ لا نداء، الحالة skipped |
DYNAMICS_PRINT_CALLBACK_AUTH_HEADER |
Authorization |
تُرسل فقط مع قيمة |
DYNAMICS_PRINT_CALLBACK_AUTH_VALUE |
— | الاسم والقيمة معًا شرط الإرسال |
DYNAMICS_PRINT_CALLBACK_TIMEOUT_SECONDS |
15 | مهلة النداء |
DYNAMICS_PRINT_CALLBACK_VERIFY_SSL |
true |
false يعطّل تحقق الشهادة — لا تفعلها في الإنتاج |
DYNAMICS_SOURCE |
microsoft_dynamics |
قيمة external_source المختومة |
DYNAMICS_LAUNCH_TTL_SECONDS |
3600 | عمر رمز التشغيل |
في Dynamics: استقبال launch_url وعرضه لموظف مسجَّل دخوله في الشيكات، ومنفذ يستقبل الـcallback ويقرأ event: "cheque.printed" — على HTTPS لأن الحمولة تحمل صورة الشيك.
صفحات ذات صلة
هل أجابت هذه الصفحة عن سؤالك؟
شكرًا — رأيك يصلنا ويساعدنا نحسّن الدليل.
تعذّر الإرسال — حاول مرة أخرى بعد قليل.