דלגו לתוכן

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

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

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

עודכן 31.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)
emailVerified ברירת מחדל true
objectId קיים ⇒ עדכון של השדות שהעברתם בלבד; נעדר ⇒ יצירה

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

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 הרישום אינו חוסם התחברות.

העברה בין חבילות: קודם 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 מפנה אליהם.

שש פעולות, וכל אחת ממפה מי ← 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 נדחה בוולידציה (expected true). {} על פעולה = אף אחד דרך ה‑API הרגיל (רק Master Key / MCP).

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-Packages מציג relatedPackages חדש; CLP השתנה ⇒ Get-Table-Permissions מציג את הצורה החדשה.


מתאים את שפת המוצר לשפת הלקוח (“מכירה” ← “פרויקט”, “לקוח” ← “משתתף”, “פנייה” ← “קריאה”) בכל הממשק, בכלים ילידיים שמשנים 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-Menus ← Get-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. מונח שמופיע גם בשם מחלקת CSS או בתכונת data-* יהרוס את הדף. הסכימה מזהירה מזה במפורש.
  1. Set-Terminology-Dictionary — המונחים המקוריים
  2. Replace-Terms על כל הדפים עם המיפוי ההפוך ({"פרויקט": "מכירה"})
  3. Set-Menu-Items — הכותרות
  4. Edit-Page — תוויות השדות

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

מה הסטטוס
קריאות 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> השני.

קובצי PNG של לוגואים מגיעים לרוב עם שטח שקוף עצום מסביב (קובץ 3000×3000 שהלוגו תופס בו 2100×500 במרכז), ולכן max-width: 300px נותן לוגו זעיר. הפתרון: חיתוך לפי ערוץ 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, או שהלקוח מעלה דרך ממשק המדיה ומוסר את ה‑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.