דוגמה — הרשמה לקורס עם web2table
דף נחיתה של קורס. הגולש בוחר קורס מרשימה, מסמן נושאי עניין, ממלא פרטים ולוחץ “הרשמה”. מה שצריך לקרות ב‑CRM: שורה בטבלת ההרשמות, מקושרת לאיש קשר — קיים אם הוא כבר במערכת, חדש אם לא.
זו בדיוק העבודה של web2table, והעמוד הזה הוא המימוש המלא שלה — כולל שני הטיפוסים שהכי קל לטעות בהם (Pointer ובחירה מרובה) ותאריך בפורמט שהשכבה הזו מצפה לו.
הדוגמה הורצה בפועל (23.08.2026) מול טננט בדיקות שבו נבנו הטבלאות Courses, CourseTopics ו‑CourseRegistrations בדיוק כמו בסכימת הדוגמה: הטאבים של Node.js ו‑Python רצו כמו שהם, יצרו איש קשר חדש ושורת הרשמה עם Pointer, תאריך, בוליאני ומערך — וכל הערכים נבדקו בשליפה חוזרת. מה שנמצא שונה מהמקור תוקן בעמוד.
מודל הנתונים בדוגמה
Section titled “מודל הנתונים בדוגמה”| הטבלה | התפקיד | השדות שהדוגמה נוגעת בהם |
|---|---|---|
Accounts |
איש הקשר (ליד או לקוח) | F_name, L_name, PhoneNumber, Email, City, CompanyId |
Courses |
טבלת lookup של הקורסים | objectId, Name |
CourseTopics |
טבלת lookup לנושאי עניין | objectId, Name |
CourseRegistrations |
טבלת היעד — שורה לכל הרשמה | CourseId (Pointer→Courses), StartDate (Date), Comment (String), IsPaid (Boolean), array_topics_Pointer_CourseTopics (Array) |
שלוש הקבוצות — הנקודה שבה רוב האינטגרציות נשברות
Section titled “שלוש הקבוצות — הנקודה שבה רוב האינטגרציות נשברות”לכל קבוצת שדות קידומת משלה, ולא מערבבים ביניהן. זו הטבלה של הדוגמה הזו, שדה־שדה:
| השדה בבקשה | הקבוצה | לאן הוא הולך |
|---|---|---|
table |
מפתח איתור — בלי קידומת | שם המחלקה המדויק: "CourseRegistrations" |
phone |
מפתח איתור — בלי קידומת | מזין את זיהוי הכפילויות · באיש קשר חדש → Accounts.PhoneNumber |
email |
מפתח איתור — בלי קידומת | מזין את הזיהוי · באיש קשר חדש → Accounts.Email |
idnum |
מפתח איתור — בלי קידומת | ת.ז. / ח.פ. · באיש קשר חדש → Accounts.CompanyId |
account_F_name · account_L_name · account_City |
שדות Accounts |
מוחלים רק כשנוצר איש קשר חדש |
table_CourseId |
שדה טבלת היעד | Pointer — מחרוזת objectId חשופה בת 10 תווים. אורך אחר או אובייקט Pointer עטוף → השדה נזרק בשקט (200) |
table_StartDate |
שדה טבלת היעד | Date — מחרוזת ISO שטוחה ("2026-09-01" או ISO מלא; גם אובייקט Date עטוף מתקבל) |
table_Comment |
שדה טבלת היעד | String |
table_IsPaid |
שדה טבלת היעד | Boolean — מחרוזת "true" / "false" או בוליאני אמיתי; שניהם נשמרים כ‑Boolean |
table_array_topics_Pointer_CourseTopics |
שדה טבלת היעד | מערך — נשמר בדיוק כפי שנשלח (מזהים חשופים או אובייקטי Pointer), בלי המרה ובלי בדיקת אורך |
PageName · isExistsAccountCheck |
תוספות ברמה העליונה | ראו מה השרת ממלא לבד |
שני המיפויים שנבנים פעם אחת
Section titled “שני המיפויים שנבנים פעם אחת”הטופס שולח טקסט (“קורס יסודות CRM”); ה‑API רוצה objectId. לכן שולפים את טבלאות ה‑lookup פעם אחת ומטמיעים מפה סטטית.
# שליפת רשימת הקורסים — פעם אחת, בזמן בניית הדףcurl -sS -G "https://api.mbapps.co.il/parse/classes/Courses" \ -H "X-Parse-Application-Id: $MB_APP_ID" \ -H "X-Parse-API-Key: $MB_API_KEY" \ --data-urlencode 'keys=objectId,Name' \ --data-urlencode 'order=Name' \ --data-urlencode 'limit=200'// שליפת רשימת הקורסים — פעם אחת, בזמן בניית הדףconst params = new URLSearchParams({ keys: 'objectId,Name', order: 'Name', limit: '200',});
const res = await fetch(`https://api.mbapps.co.il/parse/classes/Courses?${params}`, { headers: { 'X-Parse-Application-Id': process.env.MB_APP_ID, 'X-Parse-API-Key': process.env.MB_API_KEY, },});const { results } = await res.json();# שליפת רשימת הקורסים — פעם אחת, בזמן בניית הדףimport os
import requests
res = requests.get( "https://api.mbapps.co.il/parse/classes/Courses", headers={ "X-Parse-Application-Id": os.environ["MB_APP_ID"], "X-Parse-API-Key": os.environ["MB_API_KEY"], }, params={ "keys": "objectId,Name", "order": "Name", "limit": 200, },)results = res.json()["results"]<?php// שליפת רשימת הקורסים — פעם אחת, בזמן בניית הדף$query = http_build_query([ 'keys' => 'objectId,Name', 'order' => 'Name', 'limit' => 200,]);
$ch = curl_init("https://api.mbapps.co.il/parse/classes/Courses?$query");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-Parse-Application-Id: ' . getenv('MB_APP_ID'), 'X-Parse-API-Key: ' . getenv('MB_API_KEY'), ],]);$response = curl_exec($ch);curl_close($ch);$results = json_decode($response, true)['results'] ?? [];{ "results": [ { "objectId": "aB3dEf9HiJ", "Name": "קורס יסודות CRM" }, { "objectId": "kLm4NoP7Qr", "Name": "סדנת אוטומציות" }] }ומכאן המפה שנכנסת לקוד:
// נשלף פעם אחת מטבלת Courses — עדכנו כשמוסיפים קורסconst COURSE_MAP = { 'קורס יסודות CRM': 'aB3dEf9HiJ', 'סדנת אוטומציות': 'kLm4NoP7Qr',};
// נשלף פעם אחת מטבלת CourseTopicsconst TOPIC_MAP = { 'אוטומציות': 'uNbbHuMGlK', 'דוחות': 'j17RjXmHdN', 'אינטגרציות': 'GGZUYqBjqT',};<form id="enroll-form" novalidate> <input name="firstName" placeholder="שם פרטי" /> <input name="lastName" placeholder="שם משפחה" /> <input name="phone" type="tel" placeholder="טלפון" required /> <input name="email" type="email" placeholder="אימייל" /> <input name="idNumber" placeholder="ת.ז." /> <input name="city" placeholder="עיר" />
<select name="course" required> <option value="">בחרו קורס</option> <option>קורס יסודות CRM</option> <option>סדנת אוטומציות</option> </select>
<input name="startDate" type="date" />
<fieldset> <legend>נושאים שמעניינים אתכם</legend> <label><input type="checkbox" name="topics" value="אוטומציות" /> אוטומציות</label> <label><input type="checkbox" name="topics" value="דוחות" /> דוחות</label> <label><input type="checkbox" name="topics" value="אינטגרציות" /> אינטגרציות</label> </fieldset>
<textarea name="notes" placeholder="הערות"></textarea>
<!-- מלכודת דבש: בוט ימלא, אדם לא יראה --> <input name="company_website" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px" aria-hidden="true" />
<button type="submit">הרשמה</button> <p class="form-msg" role="status" aria-live="polite"></p></form><input type="date"> מחזיר "2026-09-01" — בדיוק הפורמט ששכבת הטפסים רוצה: ISO שטוח, 10 תווים ומעלה (אומת: נשמר כ‑2026-09-01T00:00:00.000Z). אין צורך להמיר לאובייקט Parse — אם כבר המרתם, גם {"__type":"Date","iso":…} מתקבל.
השליחה — בחמש שפות
Section titled “השליחה — בחמש שפות”<?phpdeclare(strict_types=1);
const APP_ID = 'הדביקו-כאן';const ENDPOINT = 'https://api.mbapps.co.il/functions/' . APP_ID . '/web2table';
const COURSE_MAP = [ 'קורס יסודות CRM' => 'aB3dEf9HiJ', 'סדנת אוטומציות' => 'kLm4NoP7Qr',];const TOPIC_MAP = [ 'אוטומציות' => 'uNbbHuMGlK', 'דוחות' => 'j17RjXmHdN', 'אינטגרציות' => 'GGZUYqBjqT',];
/** * @param array<string,mixed> $form * @return array{ok:bool, accountId?:string, rowId?:string, error?:string} */function submitEnrollment(array $form): array{ // בחירה מרובה → מערך של objectId חשופים. ערך שאינו במפה — מושמט. $topicIds = array_values(array_filter( array_map(static fn ($t) => TOPIC_MAP[$t] ?? null, $form['topics'] ?? []) ));
$data = array_filter([ // מפתחות איתור — בלי קידומת 'table' => 'CourseRegistrations', 'phone' => $form['phone'] ?? null, // חובה 'email' => $form['email'] ?? null, 'idnum' => $form['idNumber'] ?? null, // → Accounts.CompanyId על איש קשר חדש
// שדות איש הקשר — רק אם הוא נוצר עכשיו 'account_F_name' => $form['firstName'] ?? null, 'account_L_name' => $form['lastName'] ?? null, 'account_City' => $form['city'] ?? null,
// שדות טבלת היעד 'table_CourseId' => COURSE_MAP[$form['course'] ?? ''] ?? null, // objectId חשוף 'table_StartDate' => $form['startDate'] ?? null, // ISO שטוח 'table_Comment' => $form['notes'] ?? null, 'table_IsPaid' => 'false', // בוליאני כמחרוזת
// תוספות ברמה העליונה 'PageName' => 'course-landing-2026', 'isExistsAccountCheck' => 'true', ], static fn ($v) => $v !== null && $v !== '');
if (!empty($topicIds)) { $data['table_array_topics_Pointer_CourseTopics'] = $topicIds; }
if (empty($data['phone'])) { return ['ok' => false, 'error' => 'נא למלא טלפון']; }
$ch = curl_init(ENDPOINT); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-Parse-Application-Id: ' . APP_ID], CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE), ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); curl_close($ch);
if ($body === false) return ['ok' => false, 'error' => 'שגיאת רשת']; $json = json_decode($body, true) ?: []; if ($status === 400) { // כל שגיאה עסקית חוזרת כ-400 — מסתעפים על הגוף $err = $json['error'] ?? $json['message'] ?? ''; if (str_contains($err, 'Disabled')) return ['ok' => false, 'error' => 'AcceptWebToTable כבוי במערכת']; if (str_contains($err, 'missing')) return ['ok' => false, 'error' => 'חסר טלפון או שם טבלה']; return ['ok' => false, 'error' => 'נתונים לא תקינים', 'raw' => $json]; // למשל code 111 — טיפוס שגוי } if ($status !== 200) return ['ok' => false, 'error' => "שגיאת שרת ($status)"];
return empty($json['success']) ? ['ok' => false, 'error' => 'תשובה לא צפויה מהשרת'] : ['ok' => true, 'accountId' => $json['accountId'], 'rowId' => $json['Id']];}import requests
APP_ID = "הדביקו-כאן"ENDPOINT = f"https://api.mbapps.co.il/functions/{APP_ID}/web2table"HEADERS = {"Content-Type": "application/json", "X-Parse-Application-Id": APP_ID}
COURSE_MAP = { "קורס יסודות CRM": "aB3dEf9HiJ", "סדנת אוטומציות": "kLm4NoP7Qr",}TOPIC_MAP = { "אוטומציות": "uNbbHuMGlK", "דוחות": "j17RjXmHdN", "אינטגרציות": "GGZUYqBjqT",}
def submit_enrollment(form: dict) -> dict: # בחירה מרובה → מערך של objectId חשופים. ערך שאינו במפה — מושמט. topic_ids = [TOPIC_MAP[t] for t in form.get("topics", []) if t in TOPIC_MAP]
data = { # מפתחות איתור — בלי קידומת "table": "CourseRegistrations", "phone": form.get("phone"), # חובה "email": form.get("email"), "idnum": form.get("id_number"), # → Accounts.CompanyId על איש קשר חדש
# שדות איש הקשר — רק אם הוא נוצר עכשיו "account_F_name": form.get("first_name"), "account_L_name": form.get("last_name"), "account_City": form.get("city"),
# שדות טבלת היעד "table_CourseId": COURSE_MAP.get(form.get("course")), # objectId חשוף "table_StartDate": form.get("start_date"), # ISO שטוח "table_Comment": form.get("notes"), "table_IsPaid": "false", # בוליאני כמחרוזת
# תוספות ברמה העליונה "PageName": "course-landing-2026", "isExistsAccountCheck": "true", } data = {k: v for k, v in data.items() if v not in (None, "")}
if topic_ids: data["table_array_topics_Pointer_CourseTopics"] = topic_ids
if not data.get("phone"): return {"ok": False, "error": "נא למלא טלפון"}
try: r = requests.post(ENDPOINT, json=data, headers=HEADERS, timeout=10) except requests.RequestException as exc: return {"ok": False, "error": f"שגיאת רשת: {exc}"}
body = r.json() if r.headers.get("content-type", "").startswith("application/json") else {}
if r.status_code == 200 and body.get("success"): return {"ok": True, "accountId": body["accountId"], "rowId": body["Id"]} if r.status_code == 400: # כל שגיאה עסקית חוזרת כ-400 — מסתעפים על הגוף err = body.get("error") or body.get("message") or "" if "Disabled" in err: return {"ok": False, "error": "AcceptWebToTable כבוי במערכת"} if "missing" in err: return {"ok": False, "error": "חסר טלפון או שם טבלה", "raw": err} return {"ok": False, "error": "נתונים לא תקינים", "raw": body or r.text} # למשל code 111 return {"ok": False, "error": "שגיאת שרת", "status": r.status_code, "raw": body or r.text}const APP_ID = process.env.MB_APP_ID;const ENDPOINT = `https://api.mbapps.co.il/functions/${APP_ID}/web2table`;
const COURSE_MAP = { 'קורס יסודות CRM': 'aB3dEf9HiJ', 'סדנת אוטומציות': 'kLm4NoP7Qr',};const TOPIC_MAP = { 'אוטומציות': 'uNbbHuMGlK', 'דוחות': 'j17RjXmHdN', 'אינטגרציות': 'GGZUYqBjqT',};
export async function submitEnrollment(form) { // בחירה מרובה → מערך של objectId חשופים. ערך שאינו במפה — מושמט. const topicIds = (form.topics ?? []).map((t) => TOPIC_MAP[t]).filter(Boolean);
const data = { // מפתחות איתור — בלי קידומת table: 'CourseRegistrations', phone: form.phone, // חובה email: form.email, idnum: form.idNumber, // → Accounts.CompanyId על איש קשר חדש
// שדות איש הקשר — רק אם הוא נוצר עכשיו account_F_name: form.firstName, account_L_name: form.lastName, account_City: form.city,
// שדות טבלת היעד table_CourseId: COURSE_MAP[form.course], // objectId חשוף table_StartDate: form.startDate, // ISO שטוח: "2026-09-01" table_Comment: form.notes, table_IsPaid: 'false', // בוליאני כמחרוזת
// תוספות ברמה העליונה PageName: 'course-landing-2026', isExistsAccountCheck: 'true', }; Object.keys(data).forEach((k) => { if (data[k] == null || data[k] === '') delete data[k]; });
if (topicIds.length) data.table_array_topics_Pointer_CourseTopics = topicIds; if (!data.phone) return { ok: false, error: 'נא למלא טלפון' };
let res; try { res = await fetch(ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APP_ID }, body: JSON.stringify(data), signal: AbortSignal.timeout(10_000), }); } catch { return { ok: false, error: 'שגיאת רשת או timeout' }; }
const body = await res.json().catch(() => ({})); // { success, accountId, Id } · או { error } / { code, message } if (res.status === 400) { // כל שגיאה עסקית חוזרת כ-400 — מסתעפים על הגוף const err = body.error || body.message || ''; if (err.includes('Disabled')) return { ok: false, error: 'AcceptWebToTable כבוי במערכת' }; if (err.includes('missing')) return { ok: false, error: 'חסר טלפון או שם טבלה', raw: err }; return { ok: false, error: 'נתונים לא תקינים', raw: body }; // למשל code 111 — טיפוס שגוי } if (!res.ok) return { ok: false, error: `שגיאת שרת (${res.status})` };
return body.success ? { ok: true, accountId: body.accountId, rowId: body.Id } : { ok: false, error: 'תשובה לא צפויה מהשרת' };}using System.Net.Http.Json;using System.Text.Json;
public sealed class EnrollmentClient(HttpClient http, string appId){ private readonly string _endpoint = $"https://api.mbapps.co.il/functions/{appId}/web2table";
private static readonly Dictionary<string, string> CourseMap = new() { ["קורס יסודות CRM"] = "aB3dEf9HiJ", ["סדנת אוטומציות"] = "kLm4NoP7Qr", };
private static readonly Dictionary<string, string> TopicMap = new() { ["אוטומציות"] = "uNbbHuMGlK", ["דוחות"] = "j17RjXmHdN", ["אינטגרציות"] = "GGZUYqBjqT", };
public async Task<(bool Ok, string? AccountId, string? RowId, string? Error)> SubmitAsync( EnrollmentForm form, CancellationToken ct = default) { // בחירה מרובה → מערך של objectId חשופים. ערך שאינו במפה — מושמט. var topicIds = (form.Topics ?? []) .Where(TopicMap.ContainsKey) .Select(t => TopicMap[t]) .ToArray();
var data = new Dictionary<string, object?> { // מפתחות איתור — בלי קידומת ["table"] = "CourseRegistrations", ["phone"] = form.Phone, // חובה ["email"] = form.Email, ["idnum"] = form.IdNumber, // → Accounts.CompanyId על איש קשר חדש
// שדות איש הקשר — רק אם הוא נוצר עכשיו ["account_F_name"] = form.FirstName, ["account_L_name"] = form.LastName, ["account_City"] = form.City,
// שדות טבלת היעד ["table_CourseId"] = form.Course is not null && CourseMap.TryGetValue(form.Course, out var c) ? c : null, // objectId חשוף ["table_StartDate"] = form.StartDate, // ISO שטוח: "2026-09-01" ["table_Comment"] = form.Notes, ["table_IsPaid"] = "false", // בוליאני כמחרוזת
// תוספות ברמה העליונה ["PageName"] = "course-landing-2026", ["isExistsAccountCheck"] = "true", }; foreach (var k in data.Where(kv => kv.Value is null or "").Select(kv => kv.Key).ToList()) data.Remove(k);
if (topicIds.Length > 0) data["table_array_topics_Pointer_CourseTopics"] = topicIds;
if (!data.ContainsKey("phone")) return (false, null, null, "נא למלא טלפון");
using var req = new HttpRequestMessage(HttpMethod.Post, _endpoint) { Content = JsonContent.Create(data), }; req.Headers.Add("X-Parse-Application-Id", appId);
HttpResponseMessage res; try { res = await http.SendAsync(req, ct); } catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException) { return (false, null, null, "שגיאת רשת או timeout"); }
var body = await res.Content.ReadFromJsonAsync<JsonElement>(cancellationToken: ct); if ((int)res.StatusCode == 400) // כל שגיאה עסקית חוזרת כ-400 — מסתעפים על הגוף { var err = body.TryGetProperty("error", out var e) ? e.GetString() ?? "" : body.TryGetProperty("message", out var m) ? m.GetString() ?? "" : ""; if (err.Contains("Disabled")) return (false, null, null, "AcceptWebToTable כבוי במערכת"); if (err.Contains("missing")) return (false, null, null, "חסר טלפון או שם טבלה"); return (false, null, null, "נתונים לא תקינים"); // למשל code 111 — טיפוס שגוי } if (!res.IsSuccessStatusCode) return (false, null, null, $"שגיאת שרת ({(int)res.StatusCode})");
if (!body.TryGetProperty("success", out var ok) || !ok.GetBoolean()) return (false, null, null, "תשובה לא צפויה מהשרת");
return (true, body.GetProperty("accountId").GetString(), body.GetProperty("Id").GetString(), null); }}
public record EnrollmentForm( string? FirstName, string? LastName, string? Phone, string? Email, string? IdNumber, string? City, string? Course, string? StartDate, string? Notes, string[]? Topics);// רץ בדף הנחיתה — בלי סודות. ה-Application Id הוא מזהה ציבורי.const APP_ID = 'הדביקו-כאן';
const COURSE_MAP = { 'קורס יסודות CRM': 'aB3dEf9HiJ', 'סדנת אוטומציות': 'kLm4NoP7Qr',};const TOPIC_MAP = { 'אוטומציות': 'uNbbHuMGlK', 'דוחות': 'j17RjXmHdN', 'אינטגרציות': 'GGZUYqBjqT',};
const form = document.getElementById('enroll-form');const msg = form.querySelector('.form-msg');const btn = form.querySelector('button');
form.addEventListener('submit', async (e) => { e.preventDefault(); const fd = new FormData(form); const f = Object.fromEntries(fd);
// מלכודת דבש מלאה → בוט. "הצלחה" בלי לשלוח כלום. if (f.company_website) { window.location.href = '/thank-you/'; return; }
// בחירה מרובה: כל תיבות הסימון בשם topics const topicIds = fd.getAll('topics').map((t) => TOPIC_MAP[t]).filter(Boolean);
const data = { table: 'CourseRegistrations', phone: f.phone, // חובה, בלי קידומת email: f.email, idnum: f.idNumber,
account_F_name: f.firstName, account_L_name: f.lastName, account_City: f.city,
table_CourseId: COURSE_MAP[f.course], // objectId חשוף table_StartDate: f.startDate, // ISO שטוח מ-<input type="date"> table_Comment: f.notes, table_IsPaid: 'false',
PageName: 'course-landing-2026', isExistsAccountCheck: 'true', }; Object.keys(data).forEach((k) => { if (!data[k]) delete data[k]; }); if (topicIds.length) data.table_array_topics_Pointer_CourseTopics = topicIds;
if (!data.phone) { msg.textContent = 'נא למלא טלפון'; return; }
btn.disabled = true; // לחיצה כפולה = שתי הרשמות try { const res = await fetch(`https://api.mbapps.co.il/functions/${APP_ID}/web2table`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APP_ID }, body: JSON.stringify(data), });
if (res.ok) { const body = await res.json(); // { success, accountId, Id } window.dataLayer?.push({ event: 'form_submit', registration_id: body.Id }); window.location.href = '/thank-you/'; return; } const err = String((await res.json().catch(() => ({}))).error || ''); // 400 לכל שגיאה עסקית if (res.status === 400 && err.includes('missing')) msg.textContent = 'נא למלא טלפון תקין'; else if (res.status === 400 && err.includes('Disabled')) msg.textContent = 'ההרשמה אינה פעילה כרגע'; else msg.textContent = 'אירעה שגיאה, נסו שוב'; } catch { msg.textContent = 'אין חיבור לרשת, נסו שוב'; } finally { btn.disabled = false; }});התשובה
Section titled “התשובה”{ "success": true, "accountId": "xK9mP2qRsT", "Id": "Cpl9lrueY5" }| השדה | מה הוא |
|---|---|
accountId |
ה‑objectId של איש הקשר — שנמצא או שנוצר |
Id |
ה‑objectId של שורת ההרשמה החדשה ב‑CourseRegistrations |
שמרו את שניהם: Id הוא מספר האסמכתא שאפשר להציג בעמוד התודה, ו‑accountId הוא מה שיאפשר לכם למצוא את הפנייה אצל הלקוח בלי לחפש לפי טלפון.
| סטטוס | מה זה אומר |
|---|---|
400 |
{"error":"missing \"phone\""} · {"error":"missing \"table\" in request body"} · {"error":"AcceptWebToTable is Disabled"} — דגל נפרד מזה של getlead, ראו דגלי Config · {"code":111,"message":"schema mismatch …"} (טיפוס שגוי בשדה account_*) · {"code":100,…"unescaped characters"} (שם טבלה בעברית/עם רווח — table נכנס לנתיב) · {"code":103,"message":"Class X does not exist."} |
500 |
תקלת שרת |
כל השגיאות העסקיות הן 400 — אין 422 ואין 403 (אומת). הסתעפו על גוף התשובה.
מה השרת ממלא לבד
Section titled “מה השרת ממלא לבד”אל תשלחו את השדות האלה — הם נקבעים בצד השרת:
| השדה בשורת ההרשמה | הערך |
|---|---|
AccountId |
Pointer לאיש הקשר שנמצא או נוצר |
IsFirst |
true לאיש קשר חדש; false רק כשאיש הקשר היה קיים וגם נשלח isExistsAccountCheck: true |
Name |
ה‑Name מהבקשה; אחרת "פניה ראשונה" / "פניה חוזרת" |
CampaignName / PageName |
מהבקשה; אחרת "MainLandingPage" |
PhoneNumber, Email |
מועתקים מאיש הקשר |
AccountNotExist |
true תמיד — גם לנרשם חוזר שאותר (מגבלה ידועה, באג web-forms-05). אין לו ערך אבחוני; ההבחנה חדש/חוזר היא IsFirst בלבד. ראו web2table |
מקרי קצה שיקרו לכם
Section titled “מקרי קצה שיקרו לכם”| מה קורה | למה | מה עושים |
|---|---|---|
| נרשם חוזר — הפרטים שלו ב‑CRM לא התעדכנו | שדות account_* מוחלים רק על איש קשר חדש |
זו התנהגות מכוונת. עדכון של איש קשר קיים דורש REST או פונקציית שרת |
שדה Pointer (table_*) נשאר ריק, והתשובה 200 |
הערך לא היה objectId בן בדיוק 10 תווים — או שנשלח כאובייקט Parse עטוף (שניהם אומתו). מזהה בן 10 תווים שאינו קיים כן נשמר, כמצביע תלוי באוויר | הדפיסו את הגוף לפני השליחה ובדקו אורך. ראו אבחון |
| שדה שנשלח פשוט לא נשמר | מפתח שאינו קיים בסכימה נזרק בשקט (אומת: table_NoSuchField → 200, לא נוצר שדה) |
שלפו סכימה ואמתו כל שדה |
| שתי הרשמות זהות | כל קריאה מוסיפה שורה — אין סמנטיקת עדכון | נעילת כפתור + הפניה אחרי הצלחה. אל תנסו שוב אחרי timeout |
table_IsPaid לא נקלט |
מספר (1/0) בשדה בוליאני נדחה ב‑400 code 111. true ו‑"true" שניהם נשמרים (אומת) |
בוליאני או מחרוזת "true" / "false" |
| נושאי העניין נשמרו בצורה לא צפויה | המערך נשמר בדיוק כפי שנשלח — מזהים חשופים נשארים מחרוזות, אובייקטי Pointer נשארים אובייקטים, ומזהה באורך שגוי נשמר כמו שהוא (אומת). הממשק של המוצר שומר בשדות כאלה אובייקטי Pointer מלאים | בחרו צורה אחת ועמדו בה; קוד שקורא את השדה צריך לטפל בשתי הצורות |
| הרשמה עם קובץ מצורף | לא נתמך — ושדה File בגוף אינו נזרק בשקט אלא מפיל את הבקשה ב‑400, ובצד השרת עלולות להיווצר עד 5 שורות כפולות (באג web-forms-10) |
העלאה נפרדת — קבצים |
הכול עבד, ואז 400 AcceptWebToTable is Disabled |
AcceptWebToTable כבוי, או שתי שורות Config לאותו דגל |
דגלי Config |
אחרי הפריסה — מה לבדוק במערכת
Section titled “אחרי הפריסה — מה לבדוק במערכת”- נוצרה שורה ב‑
CourseRegistrations, וה‑AccountIdשלה מצביע לאיש הקשר הנכון -
CourseIdאינו ריק — זו הבדיקה שתופסת 90% מטעויות ה‑Pointer -
StartDateנראה כתאריך במערכת ולא כטקסט - שדה הנושאים מכיל בשליפה את כל המזהים שנשלחו, בצורה שבחרתם (מחרוזות או אובייקטי Pointer)
- הרשמה של מספר טלפון שכבר קיים מקשרת לאיש הקשר הקיים ולא יוצרת כפילות (אומת בחמישה פורמטי קלט:
05XXXXXXXX,9725XXXXXXX,+9725XXXXXXX,05X-XXXXXXX,+972-5X-XXXXXXX. הפורמט05X-XXX-XXXX— למשל051-070-5161— אינו מאותר, לא כקלט ולא כפורמט שבו המספר כבר שמור אצל הלקוח, ומספר כזה ייצור כפילות שקטה. באג forms-b-03) -
IsFirstנכון בשני המקרים (חדש / חוזר) — כלומרisExistsAccountCheckנשלח -
tableהוא שם המחלקה המדויק, לא התווית בעברית - ה‑CLP של טבלת ההרשמות נשאר סגור
- web2table — התיעוד המלא: שלוש הקבוצות, שדות אוטומטיים, מודל האבטחה
- מיפוי שדות מטופס לסכימה — התהליך בחמישה צעדים, מסכימה עד קוד
- זיהוי כפילויות — מה בדיוק מחפש השרת, בכל פורמטי הטלפון
- דגלי Config —
AcceptWebToTable, וכלל השורה היחידה - אבחון תקלות בטפסים · סוגי נתונים
- שרת ביניים לטופס — כשצריך CAPTCHA או שליפת lookup בזמן אמת
- דוגמאות קוד — הקטלוג המלא