אתגרים טכניים בהזמנת משאבים
תארו לעצמכם שלקוח מנסה להזמין חדר ישיבות לשעה 15:00, אבל המנהל כבר אישר הזמנה אחרת לאותה שעה. המערכת שותקת — כפל הזמנות, שערורייה. נתקלנו בזה עשרות פעמים. לכן במהלך הפיתוח אנו מיישמים מנגנונים שמונעים התנגשויות ברמת מסד הנתונים. שום תוסף מוכן אינו מציע את הגמישות של פתרון מותאם אישית. לפי הסטטיסטיקות שלנו, מספר ההתנגשויות יורד ב-90%. חשוב גם לשלוט לא רק בחפיפות זמן אלא גם בכמות היחידות המשמשות בו-זמנית — במיוחד עבור ציוד. חבילות משאבים (חדר + מקרן) דורשות לוגיקה נפרדת כאשר נוצרת הזמנה למספר משאבים עם מטרה משותפת.
ההבדל המרכזי מהזמנה לאדם: משאב אחד יכול להיות מוזמן על ידי מספר אנשים בו-זמנית. לדוגמה, שלושה מקרנים — שלוש הזמנות נפרדות. וחלק מהמשאבים זמינים רק כחבילה: חדר + ציוד.
סוגי משאבים ומאפייניהם
| סוג | מאפיינים |
|---|---|
| אולם / חדר | לקוח אחד בכל פעם, משך מינימלי, גרנולריות של משבצות |
| ציוד | מספר יחידות לפריט (3 מקרנים) |
| מקום חניה | משבצת קבועה, ללא אפשרויות |
| חדר ישיבות | קיבולת מוגבלת, לא ניתן להזמין לשעתיים באמצע היום אם הזמן הנותר לפני/אחרי הוא פחות מ-30 דקות |
כיצד להימנע מהתנגשויות בהזמנות?
הכלי המרכזי הוא שאילתה מצרפית המתחשבת ב-capacity. עבור כל משאב אנו שומרים את מספר היחידות. בעת ניסיון הזמנה, אנו בודקים כמה כבר תפוסות:
CREATE TABLE bookable_resources ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, resource_type VARCHAR(50), capacity INTEGER DEFAULT 1, -- количество единиц (3 проектора → 3) min_duration INTERVAL DEFAULT '1 hour', max_duration INTERVAL, slot_step INTERVAL DEFAULT '30 minutes', advance_booking INTERVAL DEFAULT '1 day', max_lookahead INTERVAL DEFAULT '90 days', location VARCHAR(255), amenities TEXT[], images JSONB DEFAULT '[]', is_active BOOLEAN DEFAULT TRUE ); CREATE TABLE bookings ( id BIGSERIAL PRIMARY KEY, resource_id INTEGER REFERENCES bookable_resources(id), quantity SMALLINT DEFAULT 1, starts_at TIMESTAMP NOT NULL, ends_at TIMESTAMP NOT NULL, status VARCHAR(20) DEFAULT 'pending', booker_name VARCHAR(255), booker_email VARCHAR(255), purpose TEXT, attendees_count INTEGER, metadata JSONB ); -- Сколько единиц ресурса занято в запрошенный интервал SELECT COALESCE(SUM(quantity), 0) AS booked_qty FROM bookings WHERE resource_id = $1 AND status NOT IN ('cancelled') AND tsrange(starts_at, ends_at, '[)') && tsrange($2::timestamp, $3::timestamp, '[)'); אם CREATE TABLE bookable_resources ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, resource_type VARCHAR(50), capacity INTEGER DEFAULT 1, -- количество единиц (3 проектора → 3) min_duration INTERVAL DEFAULT '1 hour', max_duration INTERVAL, slot_step INTERVAL DEFAULT '30 minutes', advance_booking INTERVAL DEFAULT '1 day', max_lookahead INTERVAL DEFAULT '90 days', location VARCHAR(255), amenities TEXT[], images JSONB DEFAULT '[]', is_active BOOLEAN DEFAULT TRUE ); CREATE TABLE bookings ( id BIGSERIAL PRIMARY KEY, resource_id INTEGER REFERENCES bookable_resources(id), quantity SMALLINT DEFAULT 1, starts_at TIMESTAMP NOT NULL, ends_at TIMESTAMP NOT NULL, status VARCHAR(20) DEFAULT 'pending', booker_name VARCHAR(255), booker_email VARCHAR(255), purpose TEXT, attendees_count INTEGER, metadata JSONB ); — המשבצת פנויה. שימוש בסוגי טווחים של PostgreSQL מבטיח ללא חפיפות גם תחת בקשות במקביל. בפרויקט אחד צמצמנו את זמן הבדיקה מ-2 שניות ל-50 אלפיות השנייה בזכות אינדקסים על סוג הטווח.
למה צריך חיץ בין הזמנות?
חדרים דורשים לעיתים קרובות זמן לניקוי או התארגנות. אנו מיישמים זאת כהגדרת משאב:
CLEANUP_BUFFER = timedelta(minutes=30) def get_effective_booked_intervals(resource_id: int, date: date) -> list[Interval]: raw = get_bookings(resource_id, date, status_not_in=['cancelled']) return [ Interval( start=b.starts_at - CLEANUP_BUFFER, end=b.ends_at + CLEANUP_BUFFER, ) for b in raw ] עבור צד הלקוח, עבור חדרים התצוגה הנוחה ביותר היא תצוגה שבועית עם עמודות לכל משאב:
| שעה | חדר A | חדר B | חדר ישיבות |
|---|---|---|---|
| 9:00 | פנוי | מוזמן | פנוי |
| 9:30 | מוזמן | מוזמן | פנוי |
| 10:00 | מוזמן | פנוי | מוזמן |
ליישום אנו משתמשים בתצוגת resourceTimeGrid של FullCalendar עם צד שרת מותאם אישית:
calendar = new FullCalendar.Calendar(el, { plugins: ['resourceTimeGrid'], initialView: 'resourceTimeGridDay', resources: '/api/rooms', events: '/api/bookings', selectable: true, select: (info) => openBookingModal(info), }); השוואה: תוסף מוכן מול פיתוח מותאם אישית
| קריטריון | תוסף מוכן | פתרון מותאם אישית |
|---|---|---|
| ניהול התנגשויות | בסיסי, לעיתים קרובות למשאב אחד בלבד | מתקדם: קיבולת, חיץ, חבילות |
| ביצועים | מפגר עם 1000+ הזמנות ביום | פי 3 מהיר יותר באותו נפח בזכות אינדקסים וטווחים |
| גמישות תצורה | תלוי בספק | כל חוק: תאריכים חסומים, תקופה מינימלית/מקסימלית, אישור אוטומטי |
| אינטגרציה עם CMS | רק CMS פופולריים | כל מערכת, כולל פאנל ניהול מותאם אישית |
תצורת CMS
המנהל מגדיר דרך הממשק:
- שעות עבודה לפי יום בשבוע
- חגים וימי אי-עבודה (תאריכים חסומים)
- תקופת הזמנה מינימלית ומקסימלית
- האם נדרש אישור או שזה אוטומטי
- חוקי ביטול (כמה שעות לפני ביטול חינם)
טעויות אופייניות בתכנון מערכת הזמנות
- התעלמות מאזורי זמן. אם השרת והלקוח נמצאים באזורי זמן שונים, ההזמנות עלולות לזוז. שמרו זמן ב-UTC והמירו בצד הלקוח.
- חוסר נעילה לבקשות במקביל. גם עם בדיקות קיבולת, יכולות להתרחש תנאי מרוץ. השתמשו ב-
-- Сколько единиц ресурса занято в запрошенный интервал SELECT COALESCE(SUM(quantity), 0) AS booked_qty FROM bookings WHERE resource_id = $1 AND status NOT IN ('cancelled') AND tsrange(starts_at, ends_at, '[)') && tsrange($2::timestamp, $3::timestamp, '[)');או בנעילה אופטימית. - אי-התחשבות בחיץ בין הזמנות. אם לא מוסיפים זמן לניקוי, הלקוח הבא יתמודד עם חדר מלוכלך. הגדירו חיץ כפרמטר משאב.
- לוח שנה מורכב מדי. המשתמש חייב למצוא במהירות משבצת פנויה. אל תעמיסו על הממשק — תצוגה שבועית לחדרים ותצוגה יומית לציוד מספיקות.
מה כלול
- אנליטיקה: ניתוח סוגי משאבים, תרחישי שימוש, אינטגרציות
- עיצוב סכמת מסד נתונים ו-API
- יישום מודול ההזמנות עם בדיקות זמינות וחיץ
- פיתוח ממשק לוח שנה (תצוגה שבועית / חודשית)
- אינטגרציה עם CMS (WordPress, Drupal, Laravel, Strapi, או כל מערכת אחרת)
- בדיקות להתנגשויות ועומס (אנו מבטיחים ללא כפל הזמנות)
- תיעוד API והוראות למנהל
- תמיכה לאחר השקה (שבועיים בחינם)
תהליך
- אנליטיקה — זיהוי כל סוגי המשאבים, חוקי ההזמנות, מקרי קצה
- עיצוב — סכמת נתונים, נקודות קצה של API (REST/GraphQL), ארכיטקטורת צד לקוח
- יישום — צד שרת (Laravel, Node.js, או Django), צד לקוח (React/Vue עם FullCalendar)
- בדיקות — בדיקות יחידה ללוגיקת התנגשויות, בדיקות עומס (סימולציה של 1000+ הזמנות בשעה)
- פריסה — לשרת שלכם או לענן (AWS, Vercel, Selectel)
לוח זמנים ליישום
הזמנה לסוג משאב אחד עם ניהול בסיסי — 5–7 ימי עסקים. מספר סוגים, בדיקות קיבולת, חיץ, ממשק לוח שנה, ניהול חריגים ב-CMS — 8–12 ימי עסקים.
עם ניסיון של למעלה מ-10 שנים ו-40+ פרויקטים מוצלחים, אנו מספקים פתרונות חזקים. צרו קשר לפיתוח מותאם אישית — נתחשב בכל הניואנסים של הפרויקט שלכם. פנו אלינו לדיון בפרטים.







