CRM School

مرحبًا بكم في CRM School: دروس منظّمة لـ Biz1 CRM والمستندات وApp SDK.

1. قوالب PDF للمستندات

أنشئوا تصميم HTML/CSS خاصًا لملفات PDF. عند عدم اختيار قالب مخصّص (أو عدم تعيينه كافتراضي)، يستمر Biz1 في استخدام مسار PDF الحالي.

أين تُدار القوالب

افتحوا: الإعدادات → قوالب PDF للمستندات (/setting/document_pdf_templates).

من الصفحة يمكن:

  • إضافة قالب
  • تعديل قالب
  • الاختبار ببيانات وهمية
  • عرض قائمة القوالب
  • تعيين قالب افتراضي
  • حذف / أرشفة قالب
  • التصفية حسب نوع المستند

أنواع المستندات المدعومة

order_proposals
purchase_orders
invoice
receipt
receipt_tax_invoice
credit_invoice
delivery_invoice
proforma_invoice
detail_orders
gi_ir
organization_receipt

كيف يُختار القالب عند إنشاء مستند

حقول اختيارية في الطلب:

الحقلالمعنى
pdf_template_idاستخدام هذا القالب (يجب أن يملكه المستخدم ويطابق نوع المستند)
pdf_template_strictإذا كان 1 والقالب غير صالح / فشل العرض → إرجاع خطأ بدل الرجوع للمسار القديم

السلوك:

  1. pdf_template_id فارغ → القالب الافتراضي النشط (إن وُجد)
  2. لا يوجد افتراضي → مسار PDF القديم / الخاص
  3. pdf_template_id صالح → استخدام القالب
  4. معرّف غير صالح + pdf_template_strict = 1 → خطأ
  5. معرّف غير صالح + strict ليس 1 → الرجوع لـ PDF القديم

يُحفظ على كل مستند pdf_template_id وpdf_template_version_id لنسخة القالب المستخدمة وقت الإنشاء.

الأجزاء المطلوبة قبل الحفظ / الاختبار

الجزء المطلوبplaceholders المقبولة
اسم الشركة{{company.company_name}}, {{company.name}}, {{owner.company_name}}
معرّف الشركة{{company.company_tax_id}}, {{company.company_id}}, {{owner.company_tax_number}}
عنوان الشركة{{company.company_address}}, {{company.address}}, {{owner.address}}
هاتف الشركة{{company.company_phone}}, {{company.phone}}, {{owner.phone}}, {{owner.mobile}}
اسم العميل{{customer.name}}
تاريخ المستند{{document.date_created}}, {{document.date}}
معرّف المستند{{document.last_documents_id}}, {{document.id}}
شعار Biz1{{crm_logo}}
نص ائتمان Biz1{{crm_credit}}

كتلة الائتمان الموصى بها:

<a href="https://biz1.co.il">
  <img src="{{crm_logo}}" width="48" />
  <span>{{crm_credit}}</span>
</a>

يُحظر HTML/CSS غير الآمن. الأجزاء الناقصة تُرجع خطأ تحقق مفصّلًا.

أهم الـ placeholders

المستند

{{document.id}}
{{document.last_documents_id}}
{{document.date_created}}
{{document.date}}
{{document.due_date}}
{{document.type}}
{{document.lang}}
{{document.direction}}
{{document.coin}}
{{document.total}}
{{document.tax}}
{{document.discount}}
{{document.final_amount}}
{{document.note}}
{{document.note_html}}
{{document.payment_link}}
{{document.invoice_israel_code}}

العميل

{{customer.name}}
{{customer.email}}
{{customer.mobile}}
{{customer.phone}}
{{customer.address}}
{{customer.company}}
{{customer.corporation}}
{{customer.cf_name}}

الشركة / إعدادات الفاتورة

{{company.company_name}}
{{company.company_tax_id}}
{{company.company_address}}
{{company.company_phone}}
{{company.email}}
{{company.logo}}
{{company.company_letter}}
{{company.order_letter}}
{{company.receipt_letter}}
{{company.invoice_receipt_letter}}
{{company.purchase_order_letter}}
{{company.details_order_letter}}

المالك / المستخدم

{{owner.name}}
{{owner.email}}
{{owner.phone}}
{{owner.mobile}}
{{owner.website}}
{{owner.logo}}
{{owner.invoice_logo}}
{{owner.address}}
{{owner.company_name}}
{{owner.company_tax_number}}

Biz1

{{crm_logo}}
{{crm_credit}}

{{crm_credit}} يعتمد على اللغة.

رابط الدفع / الموافقة

{{payment_link}}
{{document.payment_link}}
{{payment_title}}
{{payment_text}}
{{payment_link_label}}

متى يتوفر:

  • order_proposals — طالما العرض غير موافق عليه / غير موقّع
  • invoice, detail_orders, purchase_orders — عند عدم الدفع وتفعيل زر الدفع
  • غير ذلك — سلسلة فارغة

مثال:

{{#if payment_link}}
<section class="payment-cta">
  <strong>{{payment_title}}</strong>
  <span>{{payment_text}}</span>
  <a href="{{payment_link}}">{{payment_link_label}}</a>
</section>
{{/if}}

ترويسة وتذييل متكرران وأرقام الصفحات

افتراضي:

{{header}}
{{footer}}

مخصّص:

{{#header}}
<div style="font-family:Arial;font-size:9px;width:100%">
  {{company.company_name}} - {{document.last_documents_id}}
  <span style="float:right">{{page}} / {{pages}}</span>
</div>
{{/header}}

{{#footer}}
<div style="font-family:Arial;font-size:8px;width:100%">
  <a href="https://biz1.co.il">{{crm_credit}}</a>
  <span style="float:right">{{page}} / {{pages}}</span>
</div>
{{/footer}}

رموز الصفحة (فقط داخل header/footer):

{{page}}
{{pages}}
{{total_pages}}

RTL واللغة

العبرية وRTL تحصل على dir="rtl". الإنجليزية والتايلاندية — LTR.

.document-pdf-template-root[dir=rtl] {
  direction: rtl;
  text-align: right;
}

الملاحظات والنص الغني

ملاحظة عادية (مُهرَّبة):

{{document.note}}

HTML آمن (ثلاثة أقواس):

{{{document.note_html}}}
{{{company.order_letter_html}}}

تتوفر أيضًا ملاحظات الترويسة / الرسائل / multi-footer حسب نوع المستند من إعدادات الفاتورة.

البنود والمنتجات والفئات والخيارات

{{#items}}
<tr>
  <td>{{item.name}}</td>
  <td>{{item.qty}}</td>
  <td>{{format_money(item.price, document.coin)}}</td>
  <td>{{format_money(item.qty * item.price, document.coin)}}</td>
</tr>
{{/items}}

حقول البند: name, qty, price, discount, description, notes, sku, discount_type, vat_include, line_total, options_text.

تصفية حسب الفئة:

{{#items_by_category 123}}
...
{{/items_by_category}}

تصفية عامة:

{{#items where category.id == 456}}
...
{{/items}}

المعاملات: == != > >= < <=

الشروط والحساب والمال

{{#if item.discount > 0}}
  <td>{{item.discount}}</td>
{{else}}
  <td>-</td>
{{/if}}

القواعد:

  • مستوى واحد فقط لـ {{#if}} (بدون تداخل)
  • الحساب داخل تعبير واحد: {{item.qty * item.price}}
  • المال: {{format_money(item.line_total, document.coin)}}
  • لا تكتبوا {{item.qty}}*{{item.price}}

الحقول المخصّصة

{{customer.cf_name}}
{{product.cf_color}}
{{customer_cf("cf-name")}}
{{product_cf("color")}}
{{category_cf("floor-type")}}

الصور وCSS

الصور فقط من مضيفات Biz1 الموثوقة. Playwright يدعم CSS حديثًا. للطباعة يُفضّل break-inside: avoid على الصفوف.

محظور: <script>, <iframe>, <form>, أحداث JS، javascript:، وسوم PHP، صور خارجية غير معروفة.

فاتورة إسرائيل والتوقيعات

حقول Invoice Israel متاحة. حاليًا لا يوجد placeholder للتوقيع الرقمي / صورة الموافقة في قوالب HTML.

استكشاف أخطاء سريع

المشكلةتحققوا من
القالب غير مستخدمالملكية، document_type، الحالة active، الافتراضي أو pdf_template_id
خطأ حقول ناقصةكل الأجزاء المطلوبة بما فيها {{crm_logo}} و{{crm_credit}}
رابط الدفع فارغالعرض غير موافق عليه؛ الفاتورة غير مدفوعة؛ زر الدفع مفعّل
الترويسة لا تتكررصيغة header/footer؛ رموز الصفحة داخلها فقط
العبرية ليست RTLdocument_lang=he + CSS لـ RTL

المرجع الكامل: document-pdf-template-module-help.md


2. App SDK وخيارات Realtime

ابنوا شاشات web / React / Vue / Flutter WebView / Android WebView باستخدام JavaScript SDK لـ Biz1. عرّفوا النطاق مرة واحدة، وسجّلوا الدخول مرة واحدة، ثم أعيدوا استخدام نفس العميل لـ REST وrealtime.

مساعدة API المباشرة (كل المسارات): https://bull36.com/app/help

الإعداد والتدفق

  1. تضمين https://{domain}/app/sdk/biz1-sdk.js
  2. إنشاء Biz1SDK.Biz1Client واحد عند التشغيل مع domain وكائن storage
  3. استدعاء client.login() — يُحفظ الرمز في biz1_sdk_bearer_token
  4. بعد الدخول: client.account.basic() وحفظ المستخدم والمنظمة والمجلدات والحالات والفريق وإعدادات الحقول
  5. استدعاء المسارات عبر helpers أو client.routes.* أو client.request()
  6. حجم صفحة القوائم: 25 أو أقل
  7. التواريخ: كائنات Date أو نص محلي — يرسل SDK بصيغة UTC Y-m-d H:i:s
  8. حفظ المعرّفات من list/add لإعادة استخدامها
  9. عند 401 يمسح SDK الرمز — أعيدوا شاشة الدخول

مثال أساسي

const client = new Biz1SDK.Biz1Client({
  domain: 'https://{user}.bull36.com',
  storage: localStorage
});

await client.login({
  username: 'USER EMAIL',
  password: 'USER PASSWORD'
});

const user = await client.account.basic();
const customers = await client.customers.list({ folder_id: 1, length: 25 });
const total = await client.customers.count({ folder_id: 1 });

أي مسار

await client.request('Customer.List', { folder_id: 1, length: 25 });
await client.routes.Customer.List({ folder_id: 1, length: 25 });

التاريخ والوقت

await client.customers.add({
  name: 'John Demo',
  phone: '0500000000',
  followup: new Date(2026, 6, 20, 10, 0, 0)
});

الاستدعاء بدون helpers

POST /app/{Route.Name}
Authorization: Bearer YOUR TOKEN

تسجيل الدخول عبر /app/Login (مع OTP إن لزم)، حفظ Bearer، إرسال المعاملات في جسم POST، حد أقصى 25 صفًا للقوائم، وعند 401 العودة لتسجيل الدخول.

Realtime (socket)

بعد الدخول وصّلوا الـ socket بنفس الـ bearer لتحديث الشاشات دون polling.

const socket = client.realtime.connect({
  platform: 'web',
  path: '/realtime/socket.io'
});

client.realtime.on('biz1:ready', (payload) => {
  console.log(payload.userId, payload.events);
});

client.realtime.on('crm.lead.created', (event) => {
  refreshCustomerList();
});

client.realtime.on('mission.created', (event) => {
  refreshMissionList({ customer_id: event.payload.customer_id });
});

client.realtime.on('message.created', (event) => {
  refreshCustomerMessages(event.payload.customer_id);
});

مجموعات الأحداث الرئيسية

  • العملاء / المتابعة: crm.lead.created, customer.updated, customer.followup, customer.restored
  • الرسائل / البريد: message.created, chat.message.received, email.created, …
  • المهام / التذكيرات: mission.created, mission.updated, mission.done, mission.reminder, …
  • الاجتماعات: meeting.*, appointment.*
  • المكالمات: call.incoming, call.status.popup
  • التبويبات / الحالات: entries.*, statuses.*

إذا تضمنت استجابة المسار socket_event، استخدموا نفس المفتاح في client.realtime.on(...).

بناء الواجهة بمساعدة AI

وجّهوا البنّائين إلى https://{domain}/app/help وذكّروهم أن كل حقل تاريخ/وقت يُرسل كـ UTC Y-m-d H:i:s (يحّول SDK قيم Date المحلية تلقائيًا).

→ العودة إلى قاعدة المعرفة

واتساب