דלגו לתוכן

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

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

סקילים — נתונים ואינטגרציות

עודכן 30.08.2026

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

הסקיל הכיוון
myb-p-data-import קובץ ← CRM
myb-p-web2lead-web2table אתר ← CRM
myb-p-create-update-reports CRM ← שאלה

טעינה המונית מאקסל/CSV, וייצור נתוני דמה סינתטיים. הסקיל מטפל בזיהוי לקוחות קיימים (לפי מייל, טלפון, שם או objectId), במיפוי כותרות בעברית לשדות, בפתרון ערכי lookup, בהסרת כפילויות וביצירה מרוכזת.

1. קריאת הקובץ → גיליונות, כותרות, מספר שורות
2. סיווג העמודות → מזהה / שדה CRM / מטא-דאטה
3. פתרון lookups → objectIds למשתמשים, סטטוסים, מקורות
4. התאמת רשומות → מציאת או יצירת Accounts
5. הסרת כפילויות → מהמקור, לפני הכתיבה
6. יצירה → Create-Many
7. דיווח → נוצרו / דולגו / נכשלו

הטבלה הבאה היא דוגמת מיפוי מהסקיל, מלקוח ספציפי — שמות השדות אינם שמות מערכת. במערכת חדשה (vanilla) קיימים Accounts.LeadSourceId (← LeadSource), Accounts.LeadOwnerId, Sales.Source (String) ו‑Sales.SaleStatusId; SaleSource/SaleSourceList, C_LeadSource/C_LeadSourceList ו‑NewCoordinator אינם קיימים בה (כפי שרואים ב‑Get-Schema). המסקנה: מיפוי נבנה מול הסכימה החיה, לא מהזיכרון.

הכותרת בקובץ הטבלה השדה התפקיד
מייל / אימייל Accounts Email מזהה (חיפוש)
טלפון / מספר טלפון Accounts PhoneNumber מזהה (חיפוש)
AccountId.objectId Accounts objectId מזהה ישיר
שם Accounts Name, F_name, L_name שדה ישיר
כותרת Sales Name שדה ישיר
סטטוס / סטטוס מכירה Sales SaleStatusId lookup ← SaleStatuses
מקור המכירה Sales SaleSource lookup ← SaleSourceList
מקור הליד Accounts C_LeadSource lookup ← C_LeadSourceList
אחראי מכירה / יועץ Sales OwnerId lookup ← _User
מתאם Accounts LeadOwnerId, NewCoordinator lookup ← _User
תאריך התקשרות הבא Sales NextStepDate המרת תאריך
המזהה בקובץ האסטרטגיה האמינות
AccountId / objectId חיפוש ישיר עם $in מושלמת
מייל חיפוש ב‑Accounts.Email גבוהה
טלפון נרמול ← חיפוש regex בינונית-גבוהה
שם מוצא אחרון — חיפוש לפי L_name נמוכה — הרבה התאמות שווא
אין יצירת Accounts חדש

ליד חדש נוצר כרשומת Accounts עם IsAccount: false ושדות ליד (LeadStatusId, C_LeadSource, LeadOwnerId, NewCoordinator).

יצירה — הפורמטים שחייבים להיות מדויקים

Section titled “יצירה — הפורמטים שחייבים להיות מדויקים”
JSON
// Pointer
{ "__type": "Pointer", "className": "SaleStatuses", "objectId": "abc1234567" }
// Date
{ "__type": "Date", "iso": "2026-01-01T00:00:00.000Z" }

גודל אצווה: עד ~50 רשומות בקריאה נוחה אחת. לסטים גדולים — פיצול ל‑46–50 בכל פעם.

יצירה דו‑שלבית (לידים + מכירות):

  1. יצירת כל רשומות ה‑Accounts ב‑Create-Many ← מקבלים objectIds
  2. מיפוי כל objectId לנתוני המכירה המתאימה ← Create-Many על Sales

התשובה של Create-Many מחזירה objectIds באותו סדר של מערך הקלט — מיפוי לפי מיקום.

  1. $in נקטע בשקט. בחיפוש עם $in, ערך אחד שמחזיר הרבה תוצאות “בולע” את המכסה ומפיל בשקט ערכים אחרים. תמיד לוודא שמספר הרשומות שנמצאו תואם לציפייה.
  2. נרמול טלפונים. מספרים ישראליים מגיעים בעשרות פורמטים. תמיד לרדת לגרעין של 9 ספרות.
  3. חיפוש לפי שם משפחה — “כהן” יתאים להמון לקוחות לא קשורים.
  4. מגבלה ידועה — Get-all-Users מחזיר רק 100 משתמשים ראשונים. משתמש שלא שם — לא לעקוף את שכבת ההרשאות; לסמן את השורות כחסומות ולהסלים.
  5. Get-Data מחזיר 5 שורות כברירת מחדל. תשובה קטומה נראית כמו טבלה קטנה. תמיד limit מפורש.

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

חמישה חלקים: טבלת ספירות (שורות במקור ← יובאו ← דולגו, עם סיווג סיבה ← אימות חי ב‑Count-Data) · סיכום מיפוי בשפה עסקית · רשימת חריגים עם הכרעה לכל קבוצה · פרוטוקול דגימה שבו הלקוח בוחר 5–10 רשומות שהוא מכיר ומשווים אותן יחד מול המקור · בלוק חתימה עם “הנתונים נבדקו ואושרו להפעלה”.

נכשלה דגימה? מתקנים ← מאמתים מחדש ← מריצים את הדגימה שוב על הפריטים שנכשלו. לעולם לא אוספים חתימה על נתונים שידוע שהם שגויים “כדי לעמוד בלוח הזמנים” — לוח הזמנים שנחסך עולה באימוץ.

נתוני דמה — שיחות MyChat / WhatsApp

Section titled “נתוני דמה — שיחות MyChat / WhatsApp”

לייצור שיחות WhatsApp ריאליסטיות, הסקיל נושא מדריך עצמאי (references/mychat-conversations.md). העיקר:

שלוש שכבות, מלמטה למעלה: Channels (מספר עסקי) ← Conversations (שרשור לכל טלפון לקוח) ← ConversationMessages (Create-Many). שדה סוג הערוץ הוא TypeId, לא ChannelTypeId.

כללי הודעה: Direction הוא מילולית "Incomeing"/"Outgoing"שומרים על שגיאת הכתיב. Identity הוא טלפון הלקוח בפורמט בינלאומי בלי +. Message (עם escaping ל‑HTML) חייב להתאים ל‑Data.text.body. כל Data.id (wamid) ייחודי. חלון 24 השעות נאכף: טקסט חופשי יוצא יותר מ‑24 שעות אחרי ההודעה הנכנסת האחרונה חייב להיות template, אחרת failed עם קוד 131047.

מגבלות תצוגה נוספות: תיבת MyChat מציגה רק שיחות על ערוץ מחובר אמיתי — ערוץ סינתטי לא יופיע שם; במקום זה חושפים את ההיסטוריה על כרטיס הלקוח דרך ווידג’ט Conversations מסונן לפי AccountId. ו‑createdAt לא ניתן לתיארוך לאחור — הכרונולוגיה נבנית מ‑Data.timestamp/sentAt/LastMessageAt.


מייצר קוד production-ready ב‑JavaScript/jQuery, PHP, Python, C# או Node.js שמגיש טופס HTML חיצוני לאחת משתי פונקציות הענן.

נקודת הקצה מה נוצר מתי
getlead שורה ב‑Accounts בלבד, עם IsAccount: false (התשובה: {result: {success, leadId}}) הטופס לוכד איש קשר בלבד — ראו הערת השם למטה
web2table שורה בכל טבלת יעד וגם Accounts אם איש הקשר חדש (התשובה: {success, accountId, Id}) הטופס יוצר רשומה מקושרת (מכירה, פנייה, הרשמה)
web2case פנייה + Accounts (דגל AcceptWebToCaseLeads) קיים כנקודת קצה נפרדת

web2sale ו‑web2contact אינם נקודות קצה (400 User function not found) — משתמשים ב‑web2table עם table: "Sales". web2case כן קיים.

זה הכלל היחיד והחשוב ביותר של הסקיל: אי אפשר לייצר קוד נכון מהזיכרון. סכימת Accounts וסכימת טבלת היעד שונות בין לקוח ללקוח — שדות מותאמים, תוויות ששונו, שדות שהוסרו — ושדות Pointer דורשים objectIds חיים. המחיר של ניחוש הוא כישלון שקט: ה‑API מחזיר 200, והרשומה נוחתת בצורה שגויה.

1. Get-Schema על Accounts → שמות וטיפוסים זמינים
2. Get-Schema על טבלת היעד (web2table) → אותו דבר
3. Get-Data על כל targetClass של Pointer מבוקש → מפת ערך→objectId
4. Get-Data על LeadStatuses (סטטוס מותאם) → אימות ה-objectId
5. אימות כל שדה שהמשתמש ביקש מול הסכימה → שדה שלא קיים — מסמנים, לא משמיטים בשקט
6. רק עכשיו — ייצור קוד
  1. איזו שפה / framework?
  2. איפה הקוד רץ — דף נחיתה ב‑WordPress/Wix/Elementor (דפדפן), או נקודת קצה בשרת (Express, Flask, Laravel, ASP.NET) שהטופס מגיש אליה קודם?
  3. מה קורה בהצלחה? — הפניה לדף תודה? אישור בתוך הדף? אירוע GTM? איפוס הטופס?
  4. יש captcha או שדות אנטי‑בוט? — reCAPTCHA, Turnstile, honeypot. הטוקנים שלהם מאומתים לפני ההעברה הלאה.
  5. הטופס בדומיין אחר מ‑api.mbapps.co.il? — כן, כמעט תמיד. Parse Cloud Functions מאפשרות CORS לנקודות הקצה האלה כברירת מחדל.

טיפוסים — שכבת הטפסים אינה Parse REST

Section titled “טיפוסים — שכבת הטפסים אינה Parse REST”
טיפוס בסכימה מה שולחים הערה
String מחרוזת כמו שהוא
Number מספר או מספר כמחרוזת ה‑API ממיר
Boolean בוליאני אמיתי true/false גם "true"/"false" עובדות ברוב השדות — אבל account_IsAccount מקבל רק בוליאני אמיתי (מחרוזת → 400). המספר 1 → 400
Date מחרוזת ISO מרופדת (2026-06-01) או ISO מלא גם {__type:"Date"} נקלט; מה שנדחה הוא תאריך לא מרופד (2026-6-1 → 400)
Pointer מחרוזת ה‑objectId בת 10 התווים בשדות table_* אובייקט {__type:"Pointer"} או מזהה באורך שגוי נזרקים בשקט (200, שדה ריק); בשדות account_* האובייקט נקלט ואורך שגוי → 400
Array מערך JSON למשל מערך objectIds לשדה array_*_Pointer_*
File / PrivateFile לא נתמך בזרימה הזו הבקשה נכשלת ב‑400; להתריע למשתמש

דגל ה‑Config — ולמה MCP לא יכול לגעת בו

Section titled “דגל ה‑Config — ולמה MCP לא יכול לגעת בו”

לכל נקודת קצה יש שורה משלה בטבלת Config: AcceptWebLeads ל‑getlead, AcceptWebToTable ל‑web2table, AcceptWebToCaseLeads ל‑web2case. שורה חסרה או false400 עם "AcceptWebToTable is Disabled" (לא 403), לפני שהבקשה בכלל מגיעה לזיהוי כפילויות.

צורת השורה, שקל לטעות בה:

השדה הערך
Name "AcceptWebToTable" (String)
Value { "AcceptWebToTable": true } (Object — כן, המפתח חוזר בתוך האובייקט)

הפונקציה קוראת את המפתח הפנימי; ה‑Name החיצוני הוא רק מפתח החיפוש. בוליאני חשוף true לא יעבוד.

שורות כפולות לאותו דגל גורמות ל‑400 בלתי צפוי: הקוד לוקח query.first() — ובפועל השורה שנוצרה ראשונה מנצחת תמיד, לא “האחרונה”. שומרים בדיוק שורה אחת לכל דגל. פירוט מלא: דגלי Config.

לכל שדה Pointer שהטופס אמור למלא:

  1. מהסכימה — ה‑targetClass של ה‑Pointer (למשל LeadSource עבור LeadSourceId).
  2. Get-Data(table: "<targetClass>", keys: ["Name"], limit: 200) — שליפת האפשרויות.
  3. ייצור מפה סטטית name → objectId בקוד, עם הערה איך להרחיב.
  4. ה‑<select> מגיש את ה‑Name, והקוד מתרגם לפני השליחה.

טבלת lookup גדולה או משתנה תדיר? מייצרים דפוס של שליפה בזמן ההגשה (קריאת REST נוספת אחת). ברירת המחדל — מפה סטטית.


שני סוגי תצוגות נתונים, שניהם ב‑_DynamicQueries ושניהם ב‑Create-or-Update-Report:

דוחות שאילתות
איפה apps/mybusiness/reports דף ישות (apps/mybusiness/sales)
מה זה תצוגה אנליטית עצמאית פילטר שמור בתפריט הנפתח של אותו דף
מזוהה לפי PageName/PageId + FormName בפורמט dynamic-table-formP<number>
הסוג המאפיין מתי
רגיל IsAggr: false רשימת רשומות עם פילטרים, ייצוא
מצטבר IsAggr: true “כמה” ו“סך הכל” — לפי חודש, לפי אחראי
פיבוט IsAggr: true + PivotInfo פילוח דו‑ממדי (אחראי × חודש)
עם שדות מחושבים עמודות נגזרות מרווח רווח, אחוזים
דוח מורכב רק SubqueriesInfo כמה תת‑דוחות בתצוגה אחת

שלישיית השדות — קריטית לדוחות מצטברים

Section titled “שלישיית השדות — קריטית לדוחות מצטברים”

לכל עמודה בדוח מצטבר, שלושה ערכים חייבים להתיישר. אחד שגוי — והדוח נכשל בשקט או זורק Group object must contain values:

התכונה הפורמט דוגמה ל‑AccountId.Name
ShowFields[i] נקודות, בלי שם הטבלה "AccountId.Name"
OptionalFields[i].field זהה ל‑ShowFields[i] "AccountId.Name"
OptionalFields[i].aggrField מסלול מלא עם שם הטבלה "AccountId.Accounts.Name"

הטעות החוזרת: field: "AccountId" (אובייקט ה‑Pointer) במקום field: "AccountId.Name" (המחרוזת המוצגת). לשדות _User שימו לב ל‑name באות קטנה: ShowFields: "OwnerId.name" · aggrField: "OwnerId._User.name".

תמיד Get-Optional-Fields(className) קודם, ומעתיקים משם את הערכים המדויקים.

פריסת הפילטרים — כלל עיצוב, לא טכני

Section titled “פריסת הפילטרים — כלל עיצוב, לא טכני”

GridDivider תמיד 3 (=4 בשורה) או 4 (=3 בשורה). לעולם לא 6 או 12 — אלה יוצרים פילטרים מתוחים על כל רוחב העמוד. גם בדוח עם שני פילטרים בלבד, תאים ריקים בסדר; פילטרים מתוחים לא.

סדר הפילטרים (מימין לשמאל, מלמעלה למטה): חיפוש טקסט ← בחירה מרובה של Pointer (סטטוס, סוג, אחראי) ← זוגות טווח תאריכים ← צ’קבוקסים בוליאניים תמיד אחרונים (הם נרנדרים בפינה השמאלית‑תחתונה).

Group object must contain values — הדוח מנסה לקבץ לפי שדה שהוא NULL בחלק מהרשומות; צינור האגרגציה לא יכול לייצר מפתח קיבוץ מערך חסר. התיקון: פילטר exists על שדה הקיבוץ, כברירת מחדל:

JSON
{ "QueryElems": [
{ "F": "Industry", "C": "exists", "T": "String", "FText": "תעשייה קיימת" }
]}

לשדה Pointer: {"F": "OwnerId", "C": "exists", "T": "Pointer", "P": {"targetClass": "_User"}}. המשתמש יכול לבטל את הפילטר מהממשק כדי לראות גם רשומות בלי הערך.

תוצאות מצטברות ריקות — לפי הסדר: (1) field תואם ל‑ShowFields? (2) aggrField כולל את שם המחלקה? (3) יש בכלל רשומות שתואמות לערכי ברירת המחדל של ה‑QueryElems?

  • הרשאות נדרשות ביצירהrole:<RoleName> לתפקידים, objectId למשתמש ספציפי.
  • editPermissions נדרש כשעובדים עם master key — objectId של משתמש תקף.
  • FormName נדרש לשאילתה על דף ישות — לוקחים אותו משאילתה קיימת באותו דף.
  • מיון יורד עם מינוס: -createdAt.
  • QueryElems מסוג Pointer דורשים אובייקט P עם targetClass (ו‑multiple: true ל‑containedIn).
  • טווחי תאריכים מגיעים בזוגות: greaterThanOrEqualTo + lessThanOrEqualTo.