דלגו לתוכן

או התחילו מנקודה בטוחה: אינדקס הכלים המלא · מפת הטבלאות המרכזיות

מערכת חדשה — 14 יום חינם

סקילים — ניהול, מינוח ומכירות

עודכן 30.08.2026

ארבעה סקילים שנוגעים בצד התפעולי — מי נכנס, איך המערכת מדברת, ואיך היא מוציאה מסמכים. שניים מהם נוגעים בדברים שקשה או בלתי אפשרי להחזיר לאחור: הרשאות, ומספרי מסמכים.

הסקיל מה הוא משנה במערכת חיה מה מחייב אישור לפני ביצוע
myb-p-users-roles-permissions גישה של אנשים לנתונים כל Set-Table-Permissions — הוא דורס
myb-p-rename-terms טקסט ב‑HTML של עשרות דפים הרשימה המלאה של הדפים והמונחים
myb-p-mybooks-setup מסמכים חשבונאיים ורצפי מספור כל דבר — כולל כל הפקת מסמך
myb-p-price-quote-template מסמך שהלקוח של הלקוח רואה תוכן ועיצוב, לפני שליחה ראשונה

שלושה עמודי תווך של בקרת גישה, ושלוש החלטות עצמאיות לכל עובד:

┌──────────────┐ שייך ל ┌─────────────┐
│ משתמש │ ─────────────► │ חבילה │ מושב / רישיון
│ (_User) │ │ Business │
│ │ חבר ב │ Enterprise │
│ │ ─────────────► └─────────────┘
│ │ ┌─────────────┐
│ │ │ תפקיד │ קבוצת הרשאות
│ │ │ Admin │
└──────────────┘ │ Sales … │
└──────┬──────┘
│ מוזכר ב
┌─────────────┐
│ הרשאות │ find / get / create
│ טבלה (CLP) │ update / delete / addField
└─────────────┘

מי הוא? ← איזה מושב הוא תופס? ← מה מותר לו לעשות? שלוש שאלות נפרדות. למשתמש אין תפקיד אוטומטי, ולתפקיד אין הרשאה אוטומטית.

קליטת עובד — שלושה צעדים, אף אחד לא אופציונלי

Section titled “קליטת עובד — שלושה צעדים, אף אחד לא אופציונלי”
JSON
// 1. יצירה
Create-or-Update-User({
"name": "דוד כהן",
"username": "david.cohen@acme.co.il",
"password": "<Passw0rd-לדוגמה>",
"job": "מנהל מכירות",
"phone": "0501111001",
"active": true
})
// → { objectId: "6uWdJUouOg", ... }
// 2. מושב
Get-Packages() // מאתרים resellerPackageId
Set-Package-for-User({ "userId": "6uWdJUouOg",
"resellerPackageId": "<businessPackageId>", "action": "add" })
// 3. תפקיד
Get-Roles() // מאתרים roleId
Add-Users-to-Role({ "roleId": "slOOdR6Dyh", "userIds": ["6uWdJUouOg"] })
השדה הערה
name שם תצוגה (עברית בסדר)
username מזהה ההתחברות — חייב להיות כתובת מייל (הכלי דוחה ערך אחר: Email address format is invalid.)
password לפחות 8 תווים, עם אות קטנה + אות גדולה + ספרה (נאכף: Password must be at least 8 characters long / must contain at least one uppercase letter, one lowercase letter, and one number)
job תפקיד (שדה מותאם)
phone / extension טלפון ושלוחה
active true = יכול להתחבר · false = נעול (לא נמחק)
isPortalUser true למשתמשי פורטל חיצוניים — הנחיית יצירה, לא שדה שנשמר: קובע אם ליצור את המשתמש גם במערכת הראשית. משתמש פורטל אינו נוצר שם. הוא אינו נספר לחיוב ואינו מתחבר במסלול הראשי (by-design, mcp-a-03)
emailVerified ברירת מחדל true
objectId קיים ⇒ עדכון של השדות שהעברתם בלבד; נעדר ⇒ יצירה

אין כלי למחיקת משתמש. הניב הוא active: false — הוא שומר את ההיסטוריה (createdBy, בעלות על רשומות, צירי זמן) וחוסם התחברות. ה‑CRM מלא בהפניות Pointer למשתמשים שיישברו במחיקה קשה.

חבילות — ההבחנה שבולעת זמן

Section titled “חבילות — ההבחנה שבולעת זמן”

Get-Packages מחזיר שני דברים:

JSON
{
"packages": [
{ "resellerPackageId": "5ae59690…", "numberOfUsers": 7,
"resellerPackageIdData": { "name": "Business" } }
],
"users": [
{ "parseId": "2b0QVKoigE", "email": "…", "relatedPackages": ["5ae5986c…"] }
]
}

numberOfUsers הוא תקרת המושבים. לפני קליטה מרובה, ספרו: users.filter(u => u.relatedPackages.includes(pkgId)).length מול numberOfUsers. (האכיפה לא נבדקה בסבב האימות — סביבת הבדיקות היא ללא חבילה, packages: []; ב‑API הרישום אינו חוסם התחברות — core-api-13.)

העברת משתמש בין חבילות: קודם remove מהישנה, אחר כך add לחדשה — אחרת הוא תופס שני מושבים לרגע.

התפקיד הצבע לרוב
Admin red מנהל בסיס נתונים מלא
CRM purple משתמש CRM כללי — קריאה/כתיבה ברוב הטבלאות
Sales azure צוות מכירות
Support green צוות תמיכה
Lead Admin red ניהול לידים
Report Admin orange דוחות ואנליטיקה
MyBooks Admin orange הנהלת חשבונות
Campaign Manager green קמפיינים
MyChatAdmin / MyChatUser red / azure מודול הצ’אט

Create-Role מקבל name (חובה), description וצבע מתוך green · red · yellow · orange · azure · purple.

תפקיד חדש נולד ריק — בלי משתמשים ובלי הרשאות טבלה. חברות בתפקיד לבדה לא עושה כלום עד שמוסיפים אותו ל‑CLP של טבלה כלשהי.

Add-Users-to-Role ו‑Remove-Users-from-Role מקבלים מערכים — אוגדו. הרשאות אפקטיביות של משתמש הן האיחוד של כל התפקידים שלו; אם למישהו יש גישה רחבה באופן מפתיע, בדקו את כל התפקידים שלו, לא רק את הברור.

תפקידי מערכת לא ניתנים לשינוי שם דרך MCP — השמות (Admin, Sales…) הם המפתחות הקנוניים שכל CLP מפנה אליהם. שינוי שם היה שובר כל טבלה שכתוב בה role:Admin.

שש פעולות, וכל אחת ממפה מי ← true:

JSON
{
"find": { "role:Admin": true, "role:CRM": true, "role:Sales": true },
"get": { "role:Admin": true, "role:CRM": true, "role:Sales": true },
"create": { "role:Admin": true, "role:CRM": true, "role:Sales": true },
"update": { "role:Admin": true, "role:CRM": true, "role:Sales": true, "EkeJaH4t4W": true },
"delete": { "role:Admin": true },
"addField": {}
}
תבנית המפתח מעניקה ל
role:<RoleName> כל חברי התפקיד — שם מדויק, תלוי רישיות (רווחים מותרים: role:Report Admin)
<userObjectId> משתמש בודד — מנגנון חריגים, לשמור נדיר
requiresAuthentication כל משתמש מחובר
* ציבורי/אנונימי — רק לטבלאות שנחשפות לאינטרנט במכוון

הערכים תמיד true — אין false, והיעדר = אין הענקה. {} על פעולה = אף אחד דרך ה‑API הרגיל (רק Master Key / MCP). ערך false נדחה בוולידציה (expected true).

find מול get: find הוא שאילתת רשימה (הרשת), get הוא שליפת רשומה בודדת לפי objectId (פתיחת כרטיס). שמרו אותם זהים כמעט תמיד — תפקיד שיכול לפתוח כרטיסים אבל לא לראות רשימה יוצר חוויה שבורה.

# התבנית הצורה
1 טבלת CRM דיפולטיבית CRM+Admin+Sales+Support על כל 5 הפעולות, addField: {}ברירת המחדל לטבלה מותאמת חדשה
2 טבלת מכירות בלבד להוריד role:Support מכל מקום
3 טבלת תמיכה בלבד להוריד role:Sales
4 צופה לקריאה בלבד התפקיד ב‑find+get בלבד
5 שותף בלי מחיקה find/get/create/update, לא delete — מכריח “בוטל” במקום מחיקה קשה
6 חריג למשתמש בודד "update": { "role:Support": true, "EkeJaH4t4W": true }
7 נעילה למנהלים רק role:Admin על הכול (טבלאות קונפיגורציה/ביקורת)
8 טבלה נעולה (מונעת‑טריגר) קריאות לתפקידים; create/update/delete כולם {} — רק master key כותב
9 כל מי שמחובר find/get: requiresAuthentication; כתיבה למנהלים
10 קריאה ציבורית "*" ב‑find/get — רק לקטלוגים של דפי נחיתה, באישור הלקוח
עזיבה:
1. Create-or-Update-User({ objectId, active: false }) ← חסימת התחברות
2. Remove-Users-from-Role לכל תפקיד שהיה בו ← מסודר
3. Set-Package-for-User(action: "remove") ← שחרור המושב
קידום:
Remove-Users-from-Role(oldRoleId) → Add-Users-to-Role(newRoleId)
אימות ב-Get-Role-Users על שני התפקידים

אחרי כל שינוי — הוכיחו שהוא עבד. משתמש נוצר? Get-all-Users כולל אותו. חבילה הוצבה? Get-Packagesusers מציג relatedPackages חדש. CLP השתנה? Get-Table-Permissions מציג את הצורה החדשה. אל תסמכו על “OK” של קריאת הכתיבה כשההרשאות רגישות.


מתאים את שפת המוצר לשפת הלקוח — “מכירה” ← “פרויקט”, “לקוח” ← “משתתף”, “פנייה” ← “קריאה” — בכל הממשק, בעזרת כלים ילידיים שמשנים את ה‑HTML ואת מילוני המערכת ישירות.

הכלי מה הוא משנה
Set-Terminology-Dictionary מיפוי מונחים כלל‑מערכתי שקובצי ה‑JS של המוצר קוראים
Replace-Terms החלפת טקסט ישירות ב‑HTML של דף — קבוע
Edit-Page + change-existing-field-label תוויות שדה בטופס
Set-Menu-Items כותרות פריטי תפריט
Add-Edit-Text-Element אלמנט טקסט/כותרת ספציפי לפי elemId
הישות המחלקה יחיד רבים
Sale Sales מכירה מכירות
Account Accounts לקוח לקוחות
Case Cases פנייה פניות
Task Tasks משימה משימות
Activity Activities פעילות פעילויות
Contact Contacts איש קשר אנשי קשר

1. גילוי כל הדפים המושפעים — בשתי אסטרטגיות משלימות:

לפי שם: Get-Site-Pages ואז סינון לפי שם המחלקה. עבור Sale: Sale, Sales, SaleRows, Pipeline, DashboardSales, DashboardSalesManager, DashboardSalesRep, System-Tables-Sale-Statuses, Mobile-Sale, Mobile-Sales.

לפי סכימה: Get-Schema ← טבלאות עם שדות Pointer שמצביעים למחלקה. אם יש להן דפים, הן עלולות להציג את המונח בתוויות או בכותרות סקשן.

2. עדכון מילון המונחיםGet-Terminology-Dictionary תחילה, ואז Set-Terminology-Dictionary שמעדכן את המונחים שמשתנים ומשמר את כל השאר:

JSON
Set-Terminology-Dictionary({ "terminology": {
"מכירה": "פרויקט", "מכירות": "פרויקטים",
"פנייה": "פנייה", "פניות": "פניות",
"משימה": "משימה", "משימות": "משימות",
"לקוח": "לקוח", "לקוחות": "לקוחות"
}})

3. Replace-Terms על CRMmaster — הצעד החשוב ביותר. שם יושב המילון הראשי שמשמש את כל הדפים.

4. Replace-Terms על Timeline — בנפרד. הוא מכיל שמות ישויות קשיחים בתבניות האירועים.

5. Replace-Terms על כל הדפים שהתגלו — אפשר במקביל. זה מטפל בכותרות, טקסט כפתורים (“מכירה חדשה” ← “פרויקט חדש”), כותרות גרפים ומונים, כותרות מודאל, תוויות צ’קבוקס, כותרות עמודות, ותיאורי כרטיסי הגדרות. דף שלא הכיל את המונח מחזיר Error: No changes found (לא changes: []) — זו לא תקלה; דף שהכיל מחזיר changes: [{term, count}].

6. תוויות שדה — לדפים שבהם הישות מופיעה כתווית של שדה Pointer (“משויך למכירה”):

JSON
Edit-Page({ "pageId": "<PriceQuote>", "actions": [
{ "actionType": "change-existing-field-label",
"fieldName": "SaleId", "label": "משויך לפרויקט" }
]})

7. פריטי תפריטGet-MenusGet-Menu-Items לכל תפריט רלוונטי ← Set-Menu-Items. כל פריט חייב לכלול את כל השדות (_id, title, type, page, order, icon) — מעתיקים מהתשובה של Get-Menu-Items.

8. אימות — המשתמש מרענן (Ctrl+F5) ובודק: תפריט הצד · דף הרשימה (כותרת, כפתור, עמודות) · דף הטופס · Pipeline · דף הלקוח (לשוניות, סקשנים) · ציר הזמן · לשוניות “הוספה חדשה” · דשבורדים · דף ההגדרות ותת‑הדפים.

כשהמגדר משתנה (מכירה = נקבה ← פרויקט = זכר), פעלי ציר הזמן צריכים התאמה:

JSON
Replace-Terms({ "pageId": "<Timeline>", "terms": {
"מכירה": "פרויקט", "מכירות": "פרויקטים",
"עודכנה": "עודכן", "נוצרה": "נוצר"
}})

רק על Timeline. בדפים אחרים המילים האלה עשויות להופיע בהקשרים שונים לגמרי.

  1. אין דריסות JS בלי אישור מפורש.
  2. CRMmaster + Timeline הם חובה.
  3. לעולם לא לגעת בסכימה — שמות טבלאות, שמות שדות ומחלקות CSS הם מזהים פנימיים.
  4. קריאה לפני כתיבהGet-Page-Content לקבלת מזהי הדפים.
  5. אידמפוטנטי — הרצה שנייה עם אותם מונחים מחזירה No changes found; המונח הישן כבר איננו.
  6. להיזהר ממונחים ששוברים HTML — הסכימה מזהירה מזה במפורש. מונח שמופיע גם בשם מחלקת CSS או בתכונת data-* יהרוס את הדף.
  1. Set-Terminology-Dictionary — שחזור המונחים המקוריים
  2. Replace-Terms על כל הדפים עם המיפוי ההפוך ({"פרויקט": "מכירה"})
  3. Set-Menu-Items — שחזור הכותרות
  4. Edit-Page — שחזור תוויות השדות

MyBooks הוא אפליקציית החיוב והמסמכים (apps/mybooks/) — היא חולקת את הטבלאות Accounts ו‑Products עם ליבת ה‑CRM, ומפיקה מסמכים ממוספרים, בעלי משמעות משפטית, ובלתי ניתנים לשינוי.

MCP מול ממשק — למה זה סקיל של הדרכה

Section titled “MCP מול ממשק — למה זה סקיל של הדרכה”
מה הסטטוס
קריאות MCP (Get-Data, Count-Data, Get-Schema, Get-Site-Pages) משטח האימות המתועד. אחרי כל צעד בממשק
כתיבות MCP לטבלאות הקונפיגורציה (AccountingSettings, AccountingDocsType, Retainers, PaymentBtns) אינן מנגנון נתמך — אל תכתבו. חיווט השרת (מספור, טוקנים, מנוע החיוב) עלול להיעקף בכתיבת שורה גולמית. לא נבדק בסבב האימות בכוונה (אין דרך בטוחה לבדוק זאת בלי לסכן רצפי מספור). ברירת המחדל: הדרכה בממשק + קריאה חוזרת
Products חריג — טבלת נתונים משותפת רגילה. Create-Data בסדר, ואז אימות שהמוצר מופיע בדף ההגדרות

“הדרכה בממשק” פירושה: לומר למשתמש בדיוק איפה (נתיב הדף) ומה להזין — ואז הסוכן מאמת בקריאה חוזרת. שום צעד לא מסתיים ב“תעשה את זה בממשק” בלי אימות.

שלב 0 — ביקורת בסיס (קריאה בלבד, תמיד ראשונה)

Section titled “שלב 0 — ביקורת בסיס (קריאה בלבד, תמיד ראשונה)”
Get-Site-Pages ← יש apps/mybooks/*? אם לא — המודול לא מותקן
Get-Data("AccountingSettings", keys: [...], limit: 1)
Get-Data("AccountingDocsType", limit: 10) ← סוגי המסמכים + NumLast לכל אחד
Get-Data("CreditClearingTerminal", limit: 10)
Count-Data("AccountingHeaders") ← כבר הופקו מסמכים? זה משנה את חשבון ההסבה
Count-Data("AccountingHeadersDraft")
Count-Data("Retainers")
Get-Data("RetainerPlans", limit: 20)
Get-Data("PaymentBtns", limit: 20)

אם Count-Data("AccountingHeaders") > 0 — כבר הופקו מסמכים, והמספור לא ניתן להורדה מתחת למספרים האלה. אומרים את זה לפני הריאיון, לא אחריו.

  1. סוגי מסמכים — מה העסק מפיק בפועל? (חשבונית מס · קבלה · חשבונית מס קבלה · חשבונית מס זיכוי · חשבונית עסקה · הזמנת עבודה · תעודת משלוח)
  2. המשך רצף ממערכת קודמת — מה המספר האחרון שהופק בכל סוג? החלטה חשבונאית‑משפטית: המספור ניתן להעלאה בלבד וטעות אינה הפיכה. בקשו אישור בכתב למספרים, ואשרור מול רואה החשבון.
  3. פרטי עסק — שם משפטי (עברית + אנגלית אם מפיקים באנגלית), ח.פ/עוסק, כתובת, לוגו, אחוז מע“מ, מטבע.
  4. סליקה — כן/לא, איזה ספק (Upay / Pelecard), ומי מחזיק בפרטי ההתחברות (הלקוח, תמיד).
  5. ריטיינרים / הוראות קבע — תדירות, תאריך חיוב ראשון, סכומים או תוכניות, סוג המסמך בכל חיוב, כמה לקוחות.
  6. דפי/כפתורי תשלום — אילו מוצרים במחיר קבוע, טווח כמויות, איפה הכפתור יוטמע.
  7. שליחת מסמכים במייל — מכתובת המערכת או מכתובת ממותגת (SMTP)?
  8. חשבונית ישראל — צפויות חשבוניות מעל סף מספרי ההקצאה? (הסף משתנה לפי שנה — לוודא מול רואה החשבון.)
  9. מלאי — האם מסמכים צריכים להזיז מלאי? מחוץ לליבת הסקיל; להוסיף להיקף במפורש.
# השלב הדף בממשק הקריאה החוזרת
3 זהות עסקית apps/mybooks/SettingCompany Get-Data("AccountingSettings", keys: [...])
4 סוגי מסמכים ומספור הגדרות מסמכים Get-Data("AccountingDocsType", limit: 20)NumLast מול האישור בכתב
5 תבניות ומוצרים SettingsTemplates · SettingProducts Get-Data("Products") + בדיקה חזותית
6 סליקה SettingPelecard Get-Data("CreditClearingTerminal") — שורה עם Default: true
7 ריטיינרים RetainerPlans · Retainers Get-Data("Retainers", keys: [...]) + RetainerRows
8 דפי תשלום PaymentsBtns Get-Data("PaymentBtns") + פתיחת ה‑Link הציבורי
9 שליחה במייל בלוק SMTP ב‑AccountingSettings Get-Data(keys: ["SmtpServer","SmptPort",…]) + שליחה חוזרת של מסמך קיים
10 תאימות ישראלית הגדרות ← חשבונית ישראל · ExportUniformFiles אימות טבעי בחשבונית הראשונה מעל הסף

מעבר האימות — טיוטה, ורק אז ייצור

Section titled “מעבר האימות — טיוטה, ורק אז ייצור”

זרימת טיוטה מקצה לקצה, חובה: הלקוח יוצר מסמך חדש מהסוג העיקרי ב‑apps/mybooks/Documents ← בוחר לקוח ← מוסיף שורת מוצר ← שמור טיוטא (לא הפק מסמך). הסוכן מאמת Get-Data("AccountingHeadersDraft") ואת שורות ה‑AccountingInvoiceLinesDraft/AccountingReceiptLinesDraft, ואז הטיוטה נמחקת מלשונית הטיוטות — והמחיקה מאומתת בקריאה חוזרת.

ההפקה האמיתית הראשונה — רק באישור מפורש, ורצוי על עסקה אמיתית ראשונה: מאשרים עם המשתמש את ה‑DocumentNumber הצפוי (= NumLast + 1) לפני הלחיצה, מזהירים שה‑PDF נפתח בחלון קופץ, וקוראים חזרה Get-Data("AccountingHeaders") למספר ולקובץ, ו‑AccountingInvoiceBalance לסוגי חשבונית.

הבקשה לאן
תבניות הצעת מחיר / עיצוב PDF ממותג myb-p-price-quote-template
אוטומציות חיוב (מיילים על אירועי מסמך, תזכורות) myb-p-trigger-setup
דוחות מעבר לדוחות המובנים myb-p-create-update-reports
ייבוא חשבוניות ישנות כמסמכים שהופקו לא יכולת מתועדת. מנגנון ההסבה המתועד הוא המשך רצף, עם הארכיון הישן נשמר במערכת הקודמת. כל בקשה כזו מסווגת כ‑Gap
ייעוץ חשבונאי או מיסויי רואה החשבון של הלקוח

תבניות הצעת מחיר הן HTML מלא עם placeholders דינמיים שמוחלפים בנתוני המכירה בזמן יצירת ה‑PDF. התבנית רצה בתוך Webview בתוך דף ה‑CRM — ומכאן המגבלות.

אחסון: טבלת PDFTemplate, נגישה דרך Get-Data / Update-Data / Create-Update-Price-Quote-Template.

HTML
<div id="windowDiv" class="rtl"
style="height: auto; padding: 20px 50px; -webkit-print-color-adjust: exact; direction: rtl;">
<!-- כל התוכן -->
<div id="sigDiv">
<div id="Signature">חתימה</div>
<div id="SignatureName">שם חתום</div>
<div id="SignatureDate">תאריך חתימה</div>
</div>
</div>
{{number}} מספר הצעת המחיר
{{date}} תאריך היצירה
{{totalIncludingVat}} סה"כ כולל מע"מ
{{Vat}} אחוז המע"מ
{{Sales.AccountId.Name}} שם הלקוח
{{Sales.AccountId.PhoneNumber}} טלפון
{{Sales.AccountId.Email}} מייל
{{Sales.AccountId.Address}} כתובת
{{Sales.TotalBeforeDiscount}} לפני הנחה
{{Sales.DiscountValue}} סכום ההנחה
{{Sales.Total}} אחרי הנחה, לפני מע"מ

שורות מוצריםdata-repeat יושב על ה‑<tr>, לא על ה‑<table> ולא על ה‑<td>:

HTML
<tr data-repeat="SaleRows">
<td>{{SaleRows.ProductId.Name}}</td>
<td>{{SaleRows.Description}}</td>
<td>{{SaleRows.Quantity}}</td>
<td>{{SaleRows.PricePerUnit}} &#8362;</td>
<td>{{SaleRows.Total}} &#8362;</td>
</tr>

מה אסור להכניס להצעת מחיר

Section titled “מה אסור להכניס להצעת מחיר”

הצעת מחיר היא מסמך חיצוני שהלקוח רואה. שדות ניהול פנימיים לא נכנסים:

שדה אסור למה
{{Sales.Probability}} הסתברות סגירה — למנהל המכירות
{{Sales.ReasonForLost}} סיבת כישלון
{{Sales.NextStepDate}} תאריך התקשרות הבא
{{Sales.IsWon}} / {{Sales.IsLost}} סטטוס פנימי

בכיוון RTL, ה‑<td> הראשון בשורה מוצג בצד ימין. רוצים לוגו בימין — הוא ה‑<td> הראשון; פרטי חברה בשמאל — ה‑<td> השני.

הלוגו — הבעיה שנראית כמו בעיית עיצוב

Section titled “הלוגו — הבעיה שנראית כמו בעיית עיצוב”

קובצי PNG של לוגואים מגיעים לרוב עם שטח שקוף עצום מסביב: קובץ 3000×3000 שהלוגו בו תופס 2100×500 במרכז. הצגה עם max-width: 300px תיתן לוגו זעיר — כי הוא רק 70% מגובה התמונה.

הפתרון הוא חיתוך לפי ערוץ Alpha לפני ההעלאה. אחרי חיתוך נכון, width קבוע עם height: auto מספיק:

HTML
<img src="URL" style="width: 240px; height: auto; display: block;
margin-right: 0; margin-left: auto;" />

אל תשתמשו ב‑max-height — זה מה שגורם ללוגו להיראות קטן.

העלאה: Upload-Public-File עם base64, או שהלקוח מעלה דרך ממשק המדיה של ה‑CRM ומוסר את ה‑URL.

JSON
// יצירה — מחזיר objectId, לשמור לעדכונים
Create-Update-Price-Quote-Template({
"templateName": "הצעת מחיר — מותג א'",
"templateContent": "<div id=\"windowDiv\" class=\"rtl\" …>…</div>"
})
// עדכון — עם objectId
Create-Update-Price-Quote-Template({
"templateName": "הצעת מחיר — מותג א'",
"objectId": "xxxxxxxxxx",
"templateContent": "<div id=\"windowDiv\" …>…</div>"
})
// רשימת התבניות הקיימות
Get-Price-Quote-Templates()
// קריאת תבנית — מחזיר את שדה ה-HTML
Get-Data({ "table": "PDFTemplate", "objectId": "xxxxxxxxxx" })

שכפול תבנית למותג אחר: קוראים את ה‑HTML של המקורית עם Get-Data, מזינים אותה ל‑Create-Update-Price-Quote-Template עם שם חדש ובלי objectId.