דוגמה — הרשמה לקורס עם web2table
דף נחיתה של קורס. הגולש בוחר קורס מרשימה, מסמן נושאי עניין, ממלא פרטים ולוחץ “הרשמה”. מה שצריך לקרות ב‑CRM: שורה בטבלת ההרשמות, מקושרת לאיש קשר — קיים אם הוא כבר במערכת, חדש אם לא.
זו בדיוק העבודה של web2table, והעמוד הזה הוא המימוש המלא שלה — כולל שני הטיפוסים שהכי קל לטעות בהם (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 תמיד — גם לנרשם חוזר שאותר (מגבלה ידועה). אין לו ערך אבחוני; ההבחנה חדש/חוזר היא 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 שורות כפולות |
העלאה נפרדת — קבצים |
הכול עבד, ואז 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— אינו מאותר, לא כקלט ולא כפורמט שבו המספר כבר שמור אצל הלקוח, ומספר כזה ייצור כפילות שקטה) -
IsFirstנכון בשני המקרים (חדש / חוזר) — כלומרisExistsAccountCheckנשלח -
tableהוא שם המחלקה המדויק, לא התווית בעברית - ה‑CLP של טבלת ההרשמות נשאר סגור
- web2table — התיעוד המלא: שלוש הקבוצות, שדות אוטומטיים, מודל האבטחה
- מיפוי שדות מטופס לסכימה — התהליך בחמישה צעדים, מסכימה עד קוד
- זיהוי כפילויות — מה בדיוק מחפש השרת, בכל פורמטי הטלפון
- דגלי Config —
AcceptWebToTable, וכלל השורה היחידה - אבחון תקלות בטפסים · סוגי נתונים
- שרת ביניים לטופס — כשצריך CAPTCHA או שליפת lookup בזמן אמת
- דוגמאות קוד — הקטלוג המלא