مرحبًا بكم في 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 والقالب غير صالح / فشل العرض → إرجاع خطأ بدل الرجوع للمسار القديم |
السلوك:
pdf_template_idفارغ → القالب الافتراضي النشط (إن وُجد)- لا يوجد افتراضي → مسار PDF القديم / الخاص
pdf_template_idصالح → استخدام القالب- معرّف غير صالح +
pdf_template_strict = 1→ خطأ - معرّف غير صالح + 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؛ رموز الصفحة داخلها فقط |
| العبرية ليست RTL | document_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
الإعداد والتدفق
- تضمين
https://{domain}/app/sdk/biz1-sdk.js - إنشاء
Biz1SDK.Biz1Clientواحد عند التشغيل معdomainوكائن storage - استدعاء
client.login()— يُحفظ الرمز فيbiz1_sdk_bearer_token - بعد الدخول:
client.account.basic()وحفظ المستخدم والمنظمة والمجلدات والحالات والفريق وإعدادات الحقول - استدعاء المسارات عبر helpers أو
client.routes.*أوclient.request() - حجم صفحة القوائم: 25 أو أقل
- التواريخ: كائنات
Dateأو نص محلي — يرسل SDK بصيغة UTCY-m-d H:i:s - حفظ المعرّفات من list/add لإعادة استخدامها
- عند
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 المحلية تلقائيًا).