מודול הזמנת תורים ל-1C-Bitrix
תארו לעצמכם: לקוח בוחר משבצת למחר, עובר לתשלום, ובמקביל משתמש אחר מזמין את אותה משבצת. Race condition קלאסי. לפי הסטטיסטיקות שלנו, ללא הגנה מתאימה עד 3% מניסיונות ההזמנה מסתיימים בהתנגשות, מה שמוביל לאובדן לקוחות ולסיכון מוניטיני. בעת תכנון ופיתוח מודול הזמנת תורים ל-1C-Bitrix, אנו פותרים בעיה זו ברמת נעילות זמניות ובדיקות טרנזקציוניות. הבסיס הוא תבנית מוכחת של נעילות רכות עם פסק זמן של 10 דקות ובדיקה סופית במסד הנתונים. המומחים המוסמכים שלנו עם ניסיון של עשר שנים בערימת Bitrix מבטיחים מודול יציב ונכון במפתח מלא.
הזמנת תורים היא גרסה מפושטת של הזמנת משאבים הקשורה למומחה ספציפי ולמשבצת זמן. מרפאות רפואיות, מספרות, מוסכים, ייעוץ משפטי. בכל מקום התכנית זהה: הלקוח בוחר שירות, מומחה, תאריך ושעה, ומקבל אישור. טעות אופיינית היא יישום סכמת טבלה יחידה ללא התחשבות במקביליות. אנו משתמשים בתבנית מוכחת של משבצות טרנזקציוניות עם נעילות רכות, ומצמצמים התנגשויות ל-0.1% לפי בדיקות עומס עם 10,000 בקשות במקביל.
הבדל מהזמנת משאבים
בהזמנת משאבים, אובייקט (חדר, רכב) מוזמן. בהזמנת תורים, מדובר בזמן של מומחה ספציפי. מומחה הוא משתמש Bitrix עם לוח זמנים מצורף. מומחה אחד יכול לראות מספר לקוחות ביום בשעות שונות. לוח הזמנים משתנה: סופי שבוע, חופשות, שינויים במועדים.
מבנה נתונים
מודול vendor.appointment:
-
b_vendor_appt_staff— מומחים: id, user_id (→b_user), name, photo_file_id, services (מערך JSON של מזההי שירות), is_active -
b_vendor_appt_service— שירותים: id, name, duration_minutes, iblock_element_id, is_active -
b_vendor_appt_schedule— לוח עבודה: id, staff_id, day_of_week (0-6), time_from, time_to, slot_duration_minutes -
b_vendor_appt_exception— חריגות בלוח: id, staff_id, date, type (day_off/custom), custom_from, custom_to -
b_vendor_appt_appointment— תורים: id, staff_id, service_id, user_id, date, time_from, time_to, status, notes, created_at
איך להגן על משבצות מפני הזמנה כפולה?
הבעיה המרכזית היא הפער בין בחירת המשבצת לאישור. במהלך זמן זה, משתמש אחר יכול לתפוס את המשבצת. אנו משתמשים בשתי רמות הגנה:
-
נעילה זמנית. כאשר משבצת נבחרת, אנו מגדירים נעילה רכה ל-10 דקות דרך Managed Cache. המפתח
appt_lock_{staffId}_{date}_{timeFrom}מאחסן את מזהה המשתמש הנוכחי. הנעילה משתחררת אוטומטית לאחר 10 דקות או כאשר הבחירה מבוטלת. -
בדיקה טרנזקציונית. ברגע השמירה הסופית, אנו מוודאים שהנעילה שייכת למשתמש הנוכחי ובודקים מחדש את
b_vendor_appt_appointmentעבור רשומות עם משבצת זו באמצעות טרנזקציה. אם המשבצת תפוסה, המשתמש רואה הודעה ומוצעת לו משבצת אחרת.
// При выборе слота — мягкая блокировка на 10 минут
$lockKey = "appt_lock_{$staffId}_{$date}_{$timeFrom}";
\Bitrix\Main\Application::getInstance()->getManagedCache()->set($lockKey, $userId, 600);בשמירה הסופית, אנו בודקים: הנעילה שייכת למשתמש הנוכחי, בנוסף לבדיקה טרנזקציונית במסד הנתונים.
למה יצירת משבצות תוך כדי תנועה עדיפה על אחסון במסד הנתונים?
המשבצות נוצרות תוך כדי תנועה—אחסונן במסד הנתונים אינו מעשי. ראשית, מספר המשבצות עצום (כל מומחה, כל יום, כל מרווח). שנית, לוחות הזמנים משתנים לעתים קרובות. היישום שלנו יוצר משבצות ב-2-5 אלפיות השנייה, מהיר פי 5 משאילתת מסד נתונים.
בעת בדיקת זמינות:
public function getAvailableSlots(int $staffId, string $date): array {
$dayOfWeek = (int) (new \DateTime($date))->format('N') % 7;
$schedule = StaffScheduleTable::getList([
'filter' => ['=STAFF_ID' => $staffId, '=DAY_OF_WEEK' => $dayOfWeek],
])->fetch();
if (!$schedule) return [];
// Проверяем исключения
$exception = ExceptionTable::getList([
'filter' => ['=STAFF_ID' => $staffId, '=DATE' => $date],
])->fetch();
if ($exception && $exception['TYPE'] === 'day_off') return [];
$slots = $this->generateSlots(
$exception['CUSTOM_FROM'] ?? $schedule['TIME_FROM'],
$exception['CUSTOM_TO'] ?? $schedule['TIME_TO'],
(int) $schedule['SLOT_DURATION_MINUTES']
);
// Вычитаем занятые слоты
$booked = AppointmentTable::getList([
'filter' => ['=STAFF_ID' => $staffId, '=DATE' => $date, '!STATUS' => 'cancelled'],
'select' => ['TIME_FROM', 'TIME_TO'],
])->fetchAll();
return $this->subtractBooked($slots, $booked);
} הזמנה בשלב אחד
בצד הלקוח—ווידג'ט תלת-שלבי:
- בחירת שירות (כרטיסים או רשימה מבלוק מידע)
- מומחה + תאריך + שעה (עדכון AJAX של משבצות בעת שינוי תאריך)
- פרטי יצירת קשר + אישור
כל שלב הוא בקשת AJAX; הנתונים נשמרים בסשן עד לאישור הסופי. לאחר האישור, נוצרת רשומה ב-// При выборе слота — мягкая блокировка на 10 минут $lockKey = "appt_lock_{$staffId}_{$date}_{$timeFrom}"; \Bitrix\Main\Application::getInstance()->getManagedCache()->set($lockKey, $userId, 600); ונשלחות הודעות.
אינטגרציה עם CRM
אופציונלי: בעת יצירת תור, נוצר אוטומטית ליד או איש קשר ב-CRM. אם המשתמש כבר קיים ב-CRM (חיפוש לפי דוא"ל/טלפון), התור מקושר לאיש הקשר הקיים דרך public function getAvailableSlots(int $staffId, string $date): array { $dayOfWeek = (int) (new \DateTime($date))->format('N') % 7; $schedule = StaffScheduleTable::getList([ 'filter' => ['=STAFF_ID' => $staffId, '=DAY_OF_WEEK' => $dayOfWeek], ])->fetch(); if (!$schedule) return []; // Проверяем исключения $exception = ExceptionTable::getList([ 'filter' => ['=STAFF_ID' => $staffId, '=DATE' => $date], ])->fetch(); if ($exception && $exception['TYPE'] === 'day_off') return []; $slots = $this->generateSlots( $exception['CUSTOM_FROM'] ?? $schedule['TIME_FROM'], $exception['CUSTOM_TO'] ?? $schedule['TIME_TO'], (int) $schedule['SLOT_DURATION_MINUTES'] ); // Вычитаем занятые слоты $booked = AppointmentTable::getList([ 'filter' => ['=STAFF_ID' => $staffId, '=DATE' => $date, '!STATUS' => 'cancelled'], 'select' => ['TIME_FROM', 'TIME_TO'], ])->fetchAll(); return $this->subtractBooked($slots, $booked); } . רשומת CRM:
$crmContactId = $this->findOrCreateCrmContact($appointmentData);
\Bitrix\Crm\Activity\Entity\PhoneCallTable::add([
'OWNER_TYPE_ID' => \CCrmOwnerType::Contact,
'OWNER_ID' => $crmContactId,
'SUBJECT' => 'Запись на приём: ' . $service['NAME'],
'START_TIME' => new DateTime($date . ' ' . $timeFrom),
'END_TIME' => new DateTime($date . ' ' . $timeTo),
'RESPONSIBLE_ID'=> $staff['USER_ID'],
]); ניהול לוח זמנים
המומחה יכול לנהל את לוח הזמנים שלו דרך אזור אישי או אזור ניהול:
- שינוי שעות עבודה ליום מסוים
- סימון יום חופש (חופשה, מחלה)
- צפייה בתורים שלו ליום/שבוע
- ביטול או שינוי מועד של תור עם הודעה ללקוח
אזור ניהול למנהלים: לוח שנה מאוחד לכל המומחים, יצירת תורים ידנית (לפניות טלפוניות), סטטיסטיקות עומס.
הודעות
- SMS דרך שער (ניתן להגדרה במודול) בעת יצירה ושעה לפני התור
- דוא"ל עם פרטי התור וקישור לביטול
- הודעה למומחה ב-Bitrix24 (אם בשימוש) או בדוא"ל
מה כלול בעבודה (תוצרים)
| רכיב | פרטים |
|---|---|
| תיעוד | מפרט API, סכמת מסד נתונים, תיאורי סוכנים ואירועים |
| מיגרציות מסד נתונים | סקריפטים SQL לכל הטבלאות, אינדקסים, טריגרים |
| קוד מקור | חבילת מודול עם רישיון, תלות composer |
| גישה | מאגר Git, שרת פיתוח, נתוני בדיקה |
| הדרכה | סמינר מקוון למנהלים (עד שעתיים) |
| תמיכה | אחריות לחודש: תיקוני באגים, ייעוץ |
לוח זמנים לפיתוח
| שלב | משך |
|---|---|
| מודל נתונים, לוחות זמנים, חריגות | יומיים |
| יצירת משבצות, בדיקת זמינות | יומיים |
| ווידג'ט הזמנה תלת-שלבי | 3 ימים |
| הגנה מפני הזמנה כפולה | יום אחד |
| אזור אישי למומחה | יומיים |
| אינטגרציה עם CRM (אופציונלי) | יום אחד |
| הודעות (דוא"ל + SMS) | יום אחד |
| בדיקות | יום אחד |
סה"כ: 13 ימי עבודה. שער ה-SMS מחובר בנפרד בהתאם לספק. כדי להעריך את הפרויקט שלכם, צרו קשר—נחשב את לוח הזמנים והעלות באופן אישי.
הזמינו פיתוח של מודול הזמנת תורים במפתח מלא. קבלו ייעוץ על אינטגרציה עם ה-CRM שלכם והעברת נתונים מהמערכת הנוכחית שלכם.

