מה חדש ב-Shipnest
כל שינוי במערכת מתועד כאן — מתיקוני באג קטנים ועד פיצ'רים חדשים. עודכן אוטומטית מ-changelog.json.
01.125.001•317 רשומות סה"כיוני 2026
v01.125.001תיקוןפורטל לקוחות / הרשאותתיקון: עריכת הרשאות של משתמש פורטל חזרה לאחור בכל רענון
כשמנהל ערך משתמש פורטל בעמוד הלקוח והוריד לו הרשאות (למשל כרטסת או API), השינוי נשמר אך 'חזר' בטעינה הבאה — וכל המשתמשים נראו כבעלים עם גישה מלאה.
פרטים נוספים ↓הסתר ↑
כשמנהל ערך משתמש פורטל בעמוד הלקוח והוריד לו הרשאות (למשל כרטסת או API), השינוי נשמר אך 'חזר' בטעינה הבאה — וכל המשתמשים נראו כבעלים עם גישה מלאה. הסיבה: הפונקציה שמזריעה את משתמש הפורטל הראשון מהאימייל של הלקוח רצה בכל טעינה של עמוד הלקוח, ובתוכה 'השלמת הרשאות בעלים' שהוסיפה מחדש את כל הרשאות הבעלים לכל משתמש שהיו לו יותר מהרשאת הצפייה הבסיסית בלבד. כך כל הורדת הרשאה (שלא הורידה עד לצפייה-בלבד) שוחזרה אוטומטית. ההשלמה האוטומטית הזו הוסרה — מעתה כל צירוף הרשאות שמגדירים נשמר ונשאר. הערה: משתמשים שכבר 'הועלו' לבעלים צריך לערוך מחדש פעם אחת אחרי העדכון.
v01.125.000חדשפורטל לקוחות / הרשאותminorדרגת 'מנהל משתמשים' ללקוח — האצלת ניהול ההרשאות ללקוח עצמו
נוספה הרשאת לקוח חדשה — 'ניהול משתמשי פורטל' — הדרגה הגבוהה ביותר, שאותה רק הטננט יכול להעניק (מתוך עמוד הלקוח, ככל שאר ההרשאות).
פרטים נוספים ↓הסתר ↑
נוספה הרשאת לקוח חדשה — 'ניהול משתמשי פורטל' — הדרגה הגבוהה ביותר, שאותה רק הטננט יכול להעניק (מתוך עמוד הלקוח, ככל שאר ההרשאות). משתמש פורטל שקיבל אותה (וכן בעל החשבון) יכול כעת לנהל בעצמו, מתוך עמוד 'צוות וגישה' בפורטל, את כל משתמשי הפורטל של הלקוח (וחשבונות מקושרים) — לקבוע לכל אחד את ההרשאות שלו (צפייה/יצירת משלוחים, כרטסת, מפתחות API) ואת החשבונות שהוא רואה. גבולות הבטיחות: מנהל משתמשים בפורטל אינו יכול להעניק או לשלול את הרשאת 'ניהול משתמשים' עצמה (נשארת בלעדית לטננט), לא יכול לערוך בעלים ולא את עצמו — כך שהטננט שומר על השליטה בדרגה העליונה ואין הסלמת הרשאות. האכיפה בשרת (ה-API מגודר), לא רק ב-UI. הרחבה של עמוד 'צוות וגישה' שנוסף קודם.
- הרשאת 'ניהול משתמשי פורטל' — הדרגה העליונה, ניתנת על ידי הטננט בלבד (מופיעה אוטומטית ברשימת ההרשאות בעמוד הלקוח)
- מנהל משתמשים (או בעלים) קובע מתוך הפורטל את ההרשאות ואת גישת החשבונות של כל חבר צוות
- מנהל בפורטל יכול להעניק כל הרשאה חוץ מ'ניהול משתמשים' עצמה — נשארת בידי הטננט
- מניעת הסלמת הרשאות: אי אפשר לערוך בעלים, אי אפשר לערוך את עצמך, וה-MANAGE_USERS של היעד נשמר תמיד
- אכיפה בשרת (ה-API מגודר ב-canManagePortalUsers), לא רק הסתרת UI
- ללא מיגרציה — בעלים קיימים ממשיכים לנהל את הצוות כרגיל
v01.124.000חדשמשלוחים / שיבוץ איסופיםminorטבלת שיבוץ איסופים — מסך תפעולי לשיבוץ שליחים, תאריכים וסטטוסים למשימות איסוף פתוחות
נוסף דף חדש 'שיבוץ איסופים' (/pickups) כפריט ניווט עליון.
פרטים נוספים ↓הסתר ↑
נוסף דף חדש 'שיבוץ איסופים' (/pickups) כפריט ניווט עליון. הדף מרכז את כל המשלוחים שיש בהם משימת איסוף שטרם בוצעה ומאפשר לנציגות לשבץ שליח, לקבוע תאריך איסוף ולעדכן סטטוס — אינליין בכל שורה, או בבחירה מרובה בפעולה אחת. הדף בנוי במתכונת דף המשלוחים: בורר טווח תאריכים (היום/אתמול/השבוע/שבוע שעבר/החודש/חודש קודם/טווח מותאם — מסונן לפי תאריך האיסוף), פילטרים מבוססי-ספירה לפי לקוח, עיר איסוף, אזור איסוף, שליח איסוף וסטטוס, חיפוש חופשי, שליטה בעמודות (הצגה/הסתרה/הצמדה/מיון/סינון בתוך העמודה), ייצוא לאקסל, סקלטון, רספונסיביות, מצב כהה/בהיר ופאגינציית שרת. כל שורה כוללת ברקוד, סטטוס, שם לקוח, כתובת האיסוף, עיר ואזור האיסוף, שליח האיסוף ותאריך האיסוף. בנוסף נוספה תצוגת מפה לצד הטבלה (split-view) עם סימון אזור (lasso) לשיבוץ שליח איסוף לקבוצת משלוחים. כדי לתמוך בעמודה ובפילטר 'אזור איסוף' נוסף שדה pickupZoneCode למשלוח, הנגזר מעיר האיסוף, מתעדכן גם בסנכרון, ועבר backfill לכל הקיים. במסגרת זו תוקנה גם מכונת-המצבים של האיסוף בכל המערכת: שיבוץ שליח לאיסוף מעדכן כעת ל'שובץ לשליח איסוף' ולא ל'נאסף', ומעבר לסטטוס שאחרי האיסוף (נאסף/נקלט במחסן/יצא להפצה/בהעברה/הושלם/נמסר) מסמן אוטומטית את משימת האיסוף כבוצעה.
- דף חדש /pickups עם פריט ניווט עליון 'שיבוץ איסופים' — כל המשלוחים עם משימת איסוף פתוחה
- עריכה אינליין של שלושה שדות בכל שורה: שליח איסוף (אותה קומפוננטת שיבוץ עם השלמה אוטומטית ו-X להסרה כמו בדף המשלוח), תאריך איסוף, וסטטוס המשלוח
- בחירה מרובה + טולבר לשיבוץ שליח / קביעת תאריך / שינוי סטטוס לכל הנבחרים בפעולה אחת
- פילטרים מבוססי-ספירה (לקוח, עיר איסוף, אזור איסוף, שליח איסוף, סטטוס), בורר טווח תאריכים לפי תאריך האיסוף, חיפוש, שליטה בעמודות, ייצוא לאקסל ותצוגת מפה עם lasso
- עמודת 'אזור איסוף' חדשה (pickupZoneCode) שנגזרת מעיר האיסוף, מתעדכנת בסנכרון ועברה backfill
- תיקון גלובלי: שיבוץ שליח איסוף → 'שובץ לשליח איסוף' (לא 'נאסף'); מעבר לסטטוס שאחרי האיסוף מסמן אוטומטית את משימת האיסוף כבוצעה
- פיצול ה'מזהה היוצא' לשתי רגליים: 'מזהה יוצא (איסוף)' (שדה חדש pickupOutboundTaskId) ו'מזהה יוצא (מסירה)' — כדי לתמוך באיסוף ומסירה אצל קבלנים שונים. שיבוץ שליח קבלן לאיסוף שומר כעת את מזהה הקבלן בעמודת האיסוף, ותוקן באג ששידר שיבוץ של ביקור בודד תמיד כמסירה
v01.123.000חדשפורטל לקוחות / הרשאותminorהגבלת משתמשי פורטל לחשבונות מקושרים נבחרים
בעל חשבון בפורטל יכול כעת להגביל כל חבר צוות לתת-קבוצה של החשבונות המקושרים — למשל עובד שאחראי רק על 'פרשה' יראה אך ורק את המשלוחים של 'פרשה', ולא של שאר החשבונות בקבוצה.
פרטים נוספים ↓הסתר ↑
בעל חשבון בפורטל יכול כעת להגביל כל חבר צוות לתת-קבוצה של החשבונות המקושרים — למשל עובד שאחראי רק על 'פרשה' יראה אך ורק את המשלוחים של 'פרשה', ולא של שאר החשבונות בקבוצה. עד כה כל משתמש פורטל קיבל אוטומטית גישה לכל הקבוצה המקושרת (הורה + כל הילדים). נוסף עמוד 'צוות וגישה' בפורטל (גלוי לבעלים בלבד) שבו מגדירים פר-חבר-צוות לאילו חשבונות יש לו גישה (ברירת מחדל: כל החשבונות; בעלים תמיד עם גישה מלאה ואי אפשר להגביל אותם). ההגבלה נאכפת בשרת בכל נקודות הסינון של נתוני משלוחים, ייבוא, תוויות וכרטסת — מזהה ה-scope הוא פרמטר חובה בפונקציית ההרשאה, כך שהקומפיילר מוודא שאף מסלול לא נשכח (אין דליפת מידע משקט). בורר החשבונות בעמוד המשלוחים מציג למשתמש מוגבל רק את החשבונות שלו.
- עמוד 'צוות וגישה' בפורטל (לבעלים בלבד) — הגדרת גישה פר-חבר-צוות לחשבונות מקושרים נבחרים
- scope נשמר על משתמש הפורטל (scoped_customer_ids); ריק = גישה מלאה (תאימות לאחור — אף משתמש קיים לא מושפע)
- אכיפה בשרת בכל ~20 נקודות הסינון: scope הוא פרמטר חובה, ה-TypeScript מכריח לעדכן כל מסלול
- החיתוך תמיד מול הקבוצה המקושרת — מזהה זר/ישן לעולם לא מרחיב גישה (fail-closed)
- בורר 'חשבונות מקושרים' ועמודי הפורטל מציגים למשתמש מוגבל רק את החשבונות המורשים לו
- בעלים אינם ניתנים להגבלה — תמיד גישה מלאה (מניעת נעילה-עצמית)
v01.122.000חדשהודעות / WhatsApp / Green APIminorניטור ניתוק WhatsApp (Green API) — באנר גלובלי וחיווי 'נותק/חזר' בתוך כל צ'אט
עד כה, כשמופע ה-WhatsApp (Green API) התנתק, איש לא ידע — הודעות נעלמו בלי עקבות ונציגי השירות לא הבינו לאן.
פרטים נוספים ↓הסתר ↑
עד כה, כשמופע ה-WhatsApp (Green API) התנתק, איש לא ידע — הודעות נעלמו בלי עקבות ונציגי השירות לא הבינו לאן. נוסף ניטור מצב מלא: המערכת מתעדת כעת כל מעבר חיבור/ניתוק של המופע (notAuthorized / sleepMode / blocked / yellowCard ↔ authorized) משני מקורות — webhook מיידי (stateInstanceChanged) ו-cron גיבוי כל 5 דקות שמתשאל את getStateInstance (תופס גם ניתוקים שה-webhook לא נמסר עליהם). נשמרים רק מעברים (טבלת provider_state_events), כך שאין הצפת רשומות. שתי נקודות תצוגה: (1) באנר ענבר גלובלי בראש האפליקציה כל עוד המופע מנותק, עם שעת הניתוק והסיבה; (2) קו-מפריד מערכתי בתוך כל צ'אט ב-business-inbox שמראה בדיוק מתי החיבור נותק ומתי חזר — ממוקם כרונולוגית בין ההודעות, כך שכל נציג שנכנס לכל צ'אט מבין את הפער.
- באנר גלובלי באפליקציה כל עוד ה-WhatsApp מנותק — עם שעת הניתוק והסיבה
- חיווי 'נותק ב-HH:MM · חזר ב-HH:MM' בתוך כל צ'אט ב-business-inbox, ממוקם כרונולוגית בין ההודעות
- זיהוי כפול: webhook מיידי (stateInstanceChanged) + cron גיבוי כל 5 דק' (getStateInstance) שתופס גם ניתוקים שלא נמסר עליהם webhook
- תיעוד מעברים בלבד (provider_state_events) — בלי הצפת רשומות מה-poll
- סקריפט חד-פעמי להפעלת stateWebhook על המופע (הזיהוי המיידי); ה-cron עובד גם בלעדיו
- מדריך הגדרה מובנה בדיאלוג אינטגרציית Green API — כתובת ה-webhook להעתקה + צ'קליסט מלא של ה-toggles וההגדרות שצריך בקונסולה כדי שכל היכולות יעבדו
v01.121.001תיקוןלקוחות / אינטגרציית סאמיטחיבור לקוח לסאמיט שנתקע על טעינה אינסופית — קריאת ה-config עברה לפר-טננט
דיאלוג 'חיבור לקוח לסאמיט' נתקע על ספינר בלי להציג התאמות או הצעות.
פרטים נוספים ↓הסתר ↑
דיאלוג 'חיבור לקוח לסאמיט' נתקע על ספינר בלי להציג התאמות או הצעות. הסיבה: לאחר המעבר של אינטגרציית סאמיט להגדרה פר-טננט (TenantProviderConfig), חמש הפונקציות ב-actions/customers.ts (שליפת לקוחות סאמיט, פרטי לקוח, חוב, קישור לכרטסת, וחיבור לקוח) נשארו על ה-config הגלובלי הישן מה-env — שלא מוגדר בפרודקשן כי כל טננט מגדיר את סאמיט שלו בעצמו. כתוצאה getSumitCredentials החזיר null, ה-API החזיר רשימה ריקה (עם שגיאה 'Summit API not configured' בלוג), והדיאלוג נשאר במצב טעינה לנצח. תוקן: כל חמש הפונקציות עברו ל-getTenantSummitConfig(tenantId) — אותו דפוס פר-טננט שכבר בשימוש ב-summit-accounting.ts. בנוסף, הדיאלוג כבר לא נתקע על ספינר כשהרשימה ריקה (תקלה או טננט שלא הגדיר סאמיט) אלא עובר למצב חיפוש ידני.
v01.121.000חדשפורטל לקוחות / מדבקותminorלוגו הלקוח על מדבקות המשלוח
לקוחות הפורטל יכולים כעת להעלות את הלוגו שלהם, והוא מודפס על כל מדבקת משלוח.
פרטים נוספים ↓הסתר ↑
לקוחות הפורטל יכולים כעת להעלות את הלוגו שלהם, והוא מודפס על כל מדבקת משלוח. ההעלאה נעשית מתוך הגדרות → מדבקה בפורטל (כרטיס 'לוגו על המדבקה', PNG/JPG עד 2MB); הלוגו נשמר ברמת הלקוח/חברה ומשותף לכל משתמשי הפורטל של אותו חשבון. במדבקה הלוגו מצויר בתחתית במקום סמל ברירת המחדל, מגודר בהגדרת 'הצג לוגו'. הלוגו נפתר פר-משלוח לפי החברה שלו — כך שבהדפסה קבוצתית של מספר חשבונות מקושרים כל מדבקה מקבלת את הלוגו הנכון; הוא מופיע גם כשמדפיסים מצד הצוות או דרך קישור שיתוף. מימוש נשען על תשתית הלוגו והאחסון הקיימת (Supabase) ועל אותו דפוס שמסמכי ה-CRM כבר משתמשים בו לציור לוגו ב-PDF (הטמעת data-URI, רק PNG/JPG שנתמכים ב-react-pdf).
- העלאת לוגו פר-לקוח מתוך הגדרות → מדבקה בפורטל (כרטיס חדש), עם תצוגה מקדימה והסרה
- הלוגו נשמר על הלקוח (logo_url) ומופיע על מדבקות שמדפיס כל משתמש של אותו חשבון
- פתרון פר-משלוח: בהדפסה קבוצתית של חשבונות מקושרים כל מדבקה מקבלת את הלוגו של החברה שלה
- מופיע בכל מסלולי ההדפסה — פורטל בודד/קבוצתי, צד צוות, וקישור שיתוף
- גודל הלוגו ב-footer מתואם לגובה הסמל הקיים כדי לא לדחוף את שאר פרטי המדבקה; העלאה מוגבלת ל-PNG/JPG (נתמך ב-react-pdf)
- הרשאה: רק חבר עם 'יצירת משלוחים' יכול לשנות את הלוגו; צפייה פתוחה לכל משתמש
v01.120.000תיקוןטריגרים / הודעותminorטריגרי סטטוס משלוח שלא נורו אף פעם — "יצא להפצה", "נקלט במחסן", "נאסף" — פעילים סוף-סוף
שלושה סוגי טריגרים שניתן היה להגדיר בטופס הטריגרים ("יצא להפצה"/out_inventory, "נקלט במחסן"/in_inventory, "נאסף"/pickup_completed) מעולם לא נשלחו בפועל — מנוע הטריגרים החזיק רשימה מקוצרת משלו שלא כללה אותם, כך שהטריגר הי…
פרטים נוספים ↓הסתר ↑
שלושה סוגי טריגרים שניתן היה להגדיר בטופס הטריגרים ("יצא להפצה"/out_inventory, "נקלט במחסן"/in_inventory, "נאסף"/pickup_completed) מעולם לא נשלחו בפועל — מנוע הטריגרים החזיק רשימה מקוצרת משלו שלא כללה אותם, כך שהטריגר היה מוגדר ופעיל בממשק אך יצא מהפונקציה עוד לפני שליפת ההגדרה מה-DB. נוסף לכך, גם מעבר ל"יצא להפצה" שמקורו פנימי (שיבוץ שליח מסירה בסריקה/בלאסו, או שינוי סטטוס ידני בממשק) כלל לא הגיע לצינור הטריגרים — רק webhook מליונוויל הפעיל אותו. תוקנו שני הפערים: (1) מנוע הטריגרים גוזר כעת את סוגי הטריגרים ואת טבלת מיפוי-הסטטוס ישירות מ-status-mappings.ts (מקור אמת יחיד, בלי דריפט עתידי); (2) שינויי סטטוס פנימיים (applyAssignmentStatus המשמש סריקה+שיבוץ-מרובה, וה-PATCH הידני של סטטוס) מפעילים כעת את אותו צינור טריגרים שה-webhook מפעיל. שליחה כפולה נמנעת על ידי שמירת המשמרת הקיימת (statusChanged): ה-echo של ליונוויל אחרי הדחיפה רואה שהסטטוס המקומי כבר עודכן ואינו שולח שוב.
- out_inventory / in_inventory / pickup_completed — טריגרים שהיו מוגדרים בממשק אך מתים — נשלחים סוף-סוף
- מקור אמת יחיד: המנוע נגזר מ-status-mappings.ts במקום רשימה כפולה שנטתה לדריפט
- שינוי סטטוס פנימי (שיבוץ שליח / שינוי ידני) מפעיל טריגר בדיוק כמו webhook מליונוויל
- הרחבה: שינוי סטטוס ידני בממשק מפעיל כעת גם טריגרי מחזור-חיים (למשל סימון 'נמסר' ידנית שולח את הודעת delivery_completed)
- מניעת כפילות מובנית — ה-echo של ליונוויל לא שולח שוב (statusChanged=false)
v01.119.000שיפוריומן פעולות / משלוחיםminorיומן הפעולות של המשלוח — כיסוי מלא: מי שינה, מה שינה ומתי, על כל פעולת ליקוט / תפעול / סטטוס
עד כה חלק גדול מהפעולות שמשנות משלוח שמרו חותמת זמן בלבד (למשל 'נלקט') בלי לתעד מי ביצע אותן, ולכן הן לא הופיעו ביומן הפעולות של המשלוח.
פרטים נוספים ↓הסתר ↑
עד כה חלק גדול מהפעולות שמשנות משלוח שמרו חותמת זמן בלבד (למשל 'נלקט') בלי לתעד מי ביצע אותן, ולכן הן לא הופיעו ביומן הפעולות של המשלוח. הסיבה: היומן מוזן מטבלת ה-AuditLog, ונקודות כתיבה רבות פשוט לא קראו ל-logAudit. הושלם הפער: כל פעולה משמעותית שנוגעת במשלוח רושמת כעת רשומת audit פר-משלוח עם ייחוס מלא (משתמש / API / מערכת), כולל דיף 'לפני ← אחרי' לכל שדה שהשתנה. כוסו: כל מסלולי הליקוט (סימון נלקט / ביטול ליקוט / הוספה / הסרה / שינוי קטגוריה / הערת ליקוט), פעולות תפעול (תוספות תשלום, הערת מעקב פער, ביטול פעולת מאסה), פעולות שליח מהאפליקציה (סימון הושלם / נכשל — משויך לשם השליח), שינויים מ-API ציבורי, מפורטל הלקוח ומעמוד המעקב הציבורי (מתויגים כמקור חיצוני), וגם מעברים אוטומטיים — הוספה אוטומטית לליקוט, סימון-נלקט אוטומטי לפי סטטוס, ומעברי סטטוס שמגיעים מהספק ב-webhook (מתויגים 'סנכרון ספק' / 'אוטומטי', ונרשמים רק על מעבר אמיתי). במקביל הורחבה טבלת תוויות השדות של היומן כך ששבת יעד, תאריך אקספרס, הערת ליקוט, הערת מעקב פער וסך תוספות התשלום מוצגים בעברית עם פורמט ₪ / תאריך תקין.
- סימון 'נלקט' / 'בוטל ליקוט' מציג סוף-סוף מי לקט את המשלוח — התלונה שהובילה לשינוי
- כל מסלולי הליקוט נרשמים פר-משלוח עם דיף לפני/אחרי (סטטוס, קטגוריה, סוג, הערה, שבת יעד)
- פעולות שליח מהאפליקציה (הושלם / נכשל) משויכות לשם השליח שביצע אותן
- שינויים מ-API ציבורי, פורטל לקוח ועמוד מעקב — נרשמים ומתויגים לפי מקור (שם מפתח ה-API / פורטל / עמוד מעקב)
- מעברי סטטוס אוטומטיים מהספק (webhook) וסימון-נלקט אוטומטי — נרשמים ומתויגים 'סנכרון ספק' / 'אוטומטי', רק על מעבר אמיתי
- תוספות תשלום, אקספרס, הערות ושבת יעד — קיבלו תוויות עבריות ופורמט ₪ / תאריך ביומן
- הודעות WhatsApp לא מופיעות ביומן הפעולות — הן נשארות באקורדיון 'מסרונים' הייעודי בלבד (סינון פעולות send_whatsapp ושדות הודעה)
v01.118.004שיפוראנליטיקה / עיצובריענון כרטיסי ה-KPI בסקירת האנליטיקה — שפה שקטה, עומק פיזי וגרף מגמה אמיתי
כרטיסי המדדים בטאב 'סקירה כללית' (אנליטיקה) עוצבו מחדש בשפה מרוסנת: הוסרו ארבעת צבעי הגרדיאנט השונים, אפקטי ה-glow, ה-blob המטושטש, ה-sparkle וה-shine sweep — והוחלפו במשטח ניטרלי אחיד שבו הצבע נושא משמעות בלבד (ירוק/אדו…
פרטים נוספים ↓הסתר ↑
כרטיסי המדדים בטאב 'סקירה כללית' (אנליטיקה) עוצבו מחדש בשפה מרוסנת: הוסרו ארבעת צבעי הגרדיאנט השונים, אפקטי ה-glow, ה-blob המטושטש, ה-sparkle וה-shine sweep — והוחלפו במשטח ניטרלי אחיד שבו הצבע נושא משמעות בלבד (ירוק/אדום לכיוון השינוי). המספרים הוגדלו עם tabular-nums והיררכיה טיפוגרפית חדה, נוסף sparkline אמיתי מסדרת המשלוחים היומית לכרטיס 'סה"כ משלוחים', ופס התקדמות אמיתי לכרטיס 'אחוז הצלחה'. אפקט ה-hover עבר מהרמה + זוהר ניאון לעומק צל פיזי בלבד. במקביל נוסף סולם elevation (טוקני --elevation-1/2/3 + utility classes) ל-globals.css כבסיס לשפת העיצוב החדשה.
v01.118.003תיקוןפורטל לקוחות / מדבקותהדפסת מדבקות בפורטל — עד 1000 בבת אחת, מדבקה לכל חבילה, ותיקון בורר החשבונות
לקוחות בפורטל קיבלו שגיאה כשניסו להדפיס מדבקות לבחירה גדולה (מעל 250 משלוחים) — הבקשה נדחתה והם נאלצו לפצל ידנית.
פרטים נוספים ↓הסתר ↑
לקוחות בפורטל קיבלו שגיאה כשניסו להדפיס מדבקות לבחירה גדולה (מעל 250 משלוחים) — הבקשה נדחתה והם נאלצו לפצל ידנית. הוסר חסם ה-250: מחולל ה-PDF מרנדר כעת אצוות גדולות במקטעים מוגבלים (כדי לא להעמיס את הזיכרון של הפונקציה) וממזג אותם לקובץ PDF אחד עם pdf-lib, כך שניתן להדפיס עד 1000 מדבקות בקובץ אחד. במקביל שופר ה-UI בפורטל: חיווי טעינה בזמן ההכנה ופתיחת הטאב מראש כדי לא להיחסם ע"י חוסם החלונות הקופצים בהדפסות ארוכות. בנוסף תוקנו שני באגים: (1) בהדפסה מרובה משלוח עם כמה חבילות הדפיס מדבקה אחת בלבד במקום מדבקה לכל חבילה — כעת ההדפסה הקבוצתית מרחיבה משלוח רב-חבילתי ל-N מדבקות בדיוק כמו הדפסה מדף המשלוח (תוקן גם במסלול הפורטל וגם במסלול השיתוף לצוות). (2) בבורר 'חשבונות מקושרים' בעמוד המשלוחים בפורטל לא ניתן היה לבטל את הבחירה 'הכל' — צ'קבוקס 'הכל' היה תקוע מסומן; כעת לחיצה עליו מבטלת ומתמקדת בחשבון של המשתמש עצמו, וניתן להוסיף משם חשבונות נוספים. בנוסף שופרה תבנית המדבקה עצמה: (א) מספר החבילה (חבילה 1/2, 2/2 וכו') מוצג כעת בבירור על מדבקת משלוח רב-חבילתי — קודם הוא חושב פנימית אך מעולם לא צויר ב-PDF; (ב) הפונט של פרטי המשלוח (עיר, כתובת, שם מקבל, טלפון, גוביינא, הערות) הוגדל לקריאוּת טובה יותר, עם ריווחים שהודקו כדי שהכול ימשיך להיכנס במדבקת 100×100; (ג) המדבקה נחתכת כעת בגבול הפיזי (overflow hidden) במקום לגלוש לעמוד שני מיותר בהדפסה קבוצתית. הערות המשלוח והערת היעד/מיקום ממשיכות להופיע (כברירת מחדל) וכעת בפונט גדול יותר.
v01.118.002תיקוןבוט קבוצות / תרחישיםתיקון בוט הקבוצות — סוף לזליגת הודעות לא-קשורות וכפילויות ב-relay
תוקנה תקלה שבה בוט הקבוצות (מנוע התרחישים) העביר הודעות זבל לא-קשורות בחזרה ללקוח/שליח.
פרטים נוספים ↓הסתר ↑
תוקנה תקלה שבה בוט הקבוצות (מנוע התרחישים) העביר הודעות זבל לא-קשורות בחזרה ללקוח/שליח. כששאלת צפי (eta_relay) של לקוח נשלחה לקבוצת שליח עמוסה, כל פטפוט שהגיע שם ('בוקר טוב', צילום מדבקה של משלוח אחר, רשימת כתובות) הוחזר ללקוח עם הקידומת 'לגבי משלוח X:'. שני שורשים: (1) ה-leg של תשובת ה-relay קיבל כל טקסט כ'תשובה' (heuristic טריוויאלי בלבד), בעוד שצד הטריגר מוגן במסווג LLM זהיר — נוסף כעת שער רלוונטיות LLM זול שמעביר רק הודעה שבאמת עונה על מה שנשאל, ומדלג על הודעות שכתב נציג שלנו בצד היעד; בכל ספק — שתיקה וה-relay נשאר פתוח לתשובה אמיתית. (2) ה-burst בקבוצת הלקוח לא התאפס, כך שכל הודעה חדשה הפעילה מחדש את אותו תרחיש ויצרה relay כפול ופנייה חוזרת לשליח — נוסף guard שמונע פתיחת relay כפול כשכבר קיים אחד פתוח לאותו משלוח. בנוסף בוטל relay תקוע שנותר פתוח, כמיטיגציה מיידית.
v01.118.001שיפורשיחות עסקיות / UIכפתור ניקוי (X) בתיבת החיפוש בשיחות עסקיות
נוסף כפתור X בתיבת החיפוש בעמוד השיחות העסקיות (business-inbox) לניקוי מהיר של השדה, בדיוק כפי שקיים כבר בתיבת החיפוש של תיבת הדואר הרגילה.
פרטים נוספים ↓הסתר ↑
נוסף כפתור X בתיבת החיפוש בעמוד השיחות העסקיות (business-inbox) לניקוי מהיר של השדה, בדיוק כפי שקיים כבר בתיבת החיפוש של תיבת הדואר הרגילה. הכפתור מופיע רק כשיש טקסט בשדה.
v01.118.000חדשAI / לולאות למידהminorסגירת שתי לולאות המשוב של ה-AI — תשובות לשאלות ממתינות הופכות לתובנות, ולמידת תרחישים לבוט הקבוצות
הושלמו שתי לולאות משוב שהיו פתוחות.
פרטים נוספים ↓הסתר ↑
הושלמו שתי לולאות משוב שהיו פתוחות. (1) 'שאלות ממתינות' → תובנה: עד כה כשמשתמש ענה כן/לא על שאלה שעוזר ה-AI שאל (פאנל 'שאלות ממתינות'), התשובה רק נשמרה ב-DB ונעלמה — ה-AI לא קיבל אותה בחזרה. כעת תשובת כן/לא הופכת אוטומטית לתובנה מאושרת על הישות הקשורה (שליח/משלוח/חברה), כך שהידע נכנס לזיכרון ה-AI ומשפיע על תשובות עתידיות; קריאת Haiku קצרה מנסחת את התשובה כעובדה הצהרתית, והמשתמש מקבל הודעת 'נשמר כתובנה'. דחייה אינה מלמדת דבר. (2) למידת תרחישים לבוט הקבוצות (Phase 2): נוסף עמוד 'החלטות הבוט' (הגדרות → AI → החלטות הבוט) שמציג את ההחלטות האחרונות של הבוט בקבוצות — ובמיוחד היכן שתק. נציג יכול ללמד 'זה היה אמור להיות תרחיש X', והתיקון מוזרק כדוגמה למסווג של אותו טננט ומשטח (קבוצת שליח/לקוח) — כך הדיוק עולה עם הזמן בלי שינוי קוד. ניתן להסיר דוגמה שגויה (ואז ההחלטה חוזרת לרשימת הטיפול). תרחיש מושבת לעולם אינו מסווג גם אם נלמד.
- תשובת כן/לא בפאנל השאלות הממתינות → תובנה מאושרת שנכנסת לזיכרון ה-AI (כולל קידום תובנה שכבר הומתנה)
- עמוד 'החלטות הבוט' — סקירת החלטות הבוט בקבוצות ולימוד 'זה היה תרחיש X' לשיפור המסווג
- דוגמאות שנלמדו מוזרקות למסווג לפי טננט ומשטח; הסרת דוגמה שגויה מחזירה את ההחלטה לטיפול
- תיקון: סוגי אירועי התרחישים מבודדים מ-digest הלמידה (לא יוצרים תובנות שווא); אינדקס + ניקוי טלמטריה לבונד גודל הטבלה
- תיוג מקור-מפתח (tenant/system) מדויק בחיוב, ומדידת עלות לכל קריאות החילוץ
v01.117.000שיפורמשלוחים / היסטוריית פעולותminorהיסטוריית פעולות: מעקב מלא אחר פעולות ה-AI, זהות לסטטוס תפעול/החזרה/גוביינא, וקישור לשיחה
שדרוג מקיף להיסטוריית הפעולות של המשלוח.
פרטים נוספים ↓הסתר ↑
שדרוג מקיף להיסטוריית הפעולות של המשלוח. (1) פעולות שמבצע עוזר ה-AI שעד כה לא תועדו כלל — שינוי כתובת/טלפון יעד (updateShipmentAddress) ושינוי סטטוס תפעולי (updateShipmentOperationalStatus) — נרשמות כעת ב-audit log, מיוחסות למשתמש שביקש מה-AI (קישור FK רק כשהוא משתמש אמיתי) ועם diff מלא של השדות שהשתנו. (2) כל פעולת AI (סטטוס, כתובת, תפעול) מסומנת כעת בתג 'AI' ענברי + נקודת ניצוץ, וכוללת קישור 'צפה בשיחה' שפותח את ה-widget של העוזר ישירות על הצ'אט שגרם לפעולה — סוף-סוף ניתן לעקוב מההיסטוריה איך ולמה ה-AI פעל. הקישור ממומש דרך עמודת session_id חדשה ב-audit_logs שנלכדת בכל פעולת AI. (3) סוגי פעולה שהוצגו עד כה כ'פעולה' גנרית עם שעון אפור — סטטוס תפעול וסטטוס החזרה — קיבלו אייקון, תווית עברית וצ'יפ-סינון ייעודיים. (4) שינויי גוביינא (COD) שהופיעו בהיסטוריה מחופשים לשינוי/עדכון/מחיקה ברמת המשלוח (כולל פח אדום מטעה על מחיקת גוביינא) קיבלו זהות 'גוביינא' נפרדת עם אייקון מטבעות. הכול תוך שמירה על תאימות: הנתונים נשמרים, ותג ה-AI/כפתור הביטול הקיימים ממשיכים לעבוד. (5) בקרת גישה לקישור 'צפה בשיחה': צפייה בשיחה של משתמש אחר מותרת רק לבעלי הרשאת AUDIT_LOGS_VIEW (בעלים/אדמין) — נציג רגיל רואה ופותח רק את השיחות שהוא עצמו יצר. נאכף בשרת ב-/api/ai/chat (לא רק הסתרת הקישור ב-UI): טעינת שיחה בודדת בודקת בעלות, והקישור עצמו מוצג רק כשהצופה רשאי לפתוח. סוגר פער קודם שבו טעינת שיחה לפי id הייתה מסוננת לפי tenant בלבד.
- שינויי כתובת/טלפון וסטטוס תפעולי של ה-AI נרשמים כעת בהיסטוריה (קודם נעלמו לגמרי), מיוחסים למשתמש שביקש
- כל פעולת AI מסומנת בתג 'AI' + קישור 'צפה בשיחה' שפותח את הצ'אט שגרם לה
- סטטוס תפעול וסטטוס החזרה קיבלו אייקון/תווית/צ'יפ-סינון ייעודיים במקום 'פעולה' גנרית
- שינויי גוביינא קיבלו זהות נפרדת — כבר לא מתחזים לשינוי/מחיקה ברמת המשלוח (פח אדום מטעה הוסר)
- עמודת session_id חדשה ב-audit_logs מקשרת כל פעולת AI לשיחה שיצרה אותה
- בקרת גישה: צפייה בשיחה של משתמש אחר רק לבעלים/אדמין (AUDIT_LOGS_VIEW), נאכף בשרת — נציג רואה רק את שלו
v01.116.000חדשבוט קבוצות / תרחישיםminorתרחיש 'אימות קבלה מול הנמען' + שדרוגי אמינות לבדיקת המסירה
נוסף תרחיש בוט שביעי: 'אימות קבלה מול הנמען'.
פרטים נוספים ↓הסתר ↑
נוסף תרחיש בוט שביעי: 'אימות קבלה מול הנמען'. כשקבלן/שליח טוען בקבוצתו שהנמען כבר קיבל את המשלוח ('נמסר', 'הלקוח קיבל', 'כבר אצלו') אך אצלנו הוא לא מסומן כנמסר, הבוט פונה אוטומטית לנמען בצ'אט לקוחות הקצה (תבנית ה-Meta המסומנת 'בדיקת מסירה') ושואל אם קיבל; אם מאשר — המשלוח מסומן DELIVERED אוטומטית, אם מכחיש — מסומן לטיפול. הבוט מאשר לקבלן שהבדיקה יצאה. התרחיש משתמש מחדש בתשתית בדיקת המסירה הקיימת (sendDeliveryConfirmationViaTemplate + ה-webhook של Meta) ומוסיף שני שומרים: דילוג אם המשלוח כבר מסומן כנמסר, ודילוג אם כבר יש בקשת אישור פתוחה למשלוח (לא להציק לנמען פעמיים). כבוי כברירת מחדל, רמת סיכון 'פעולה'. בנוסף — שני שדרוגי אמינות ל-webhook של Meta: (1) תקתוק על כפתור תשובה מהירה ('כן'/'לא') ממופה ישירות ל-CONFIRMED/DENIED בלי קריאת LLM (חד-משמעי וזול יותר; טקסט חופשי עדיין עובר לסיווג); (2) תפוגת זמן — בקשת אישור ממתינה תיחשב רק אם נוצרה ב-14 הימים האחרונים, כדי שתקתוק מאוחר על כפתור לא ישייך תשובה לבקשה ישנה/לא קשורה. בנוסף — תיעוד מלא בהיסטוריית הפעולות של המשלוח: באישור הנמען נכתבת רשומת 'שינוי סטטוס' עם הנימוק שבוצעה בדיקת מסירה חיובית (כולל אם זה היה בלחיצת כפתור או בהודעה), ובהכחשה נכתבת רשומת 'בדיקת מסירה' שהנמען הכחיש קבלה. תיקון חשוב שהתגלה תוך כדי: בדיקת המסירה הקודמת עדכנה רק את sendStatus (שמתאר אם הודעת ההתראה נשלחה — לא אם המשלוח נמסר), כך שלמעשה היא לא סגרה את המשלוח כנמסר בפועל; כעת היא מעדכנת את הסטטוס האמיתי (additionalData.status → COMPLETED), כך שהמשלוח באמת מסומן כהושלם. גם השומר 'כבר נמסר' בתרחיש תוקן לבדוק את שדה הסטטוס הנכון.
- תרחיש 'אימות קבלה מול הנמען' — קבלן טוען שנמסר → בדיקה אוטומטית מול הנמען → סימון נמסר באישור
- שומרים: דילוג אם כבר נמסר, ודילוג אם כבר יש בקשת אישור פתוחה
- מיפוי דטרמיניסטי של תקתוק כפתור (כן/לא) בלי LLM
- TTL של 14 יום על בקשות אישור ממתינות
- תיעוד בהיסטוריית המשלוח: אישור → 'שינוי סטטוס' עם נימוק בדיקת מסירה חיובית; הכחשה → רשומת 'בדיקת מסירה'
- תיקון: בדיקת מסירה חיובית סוגרת עכשיו את הסטטוס האמיתי (additionalData.status=COMPLETED), לא רק sendStatus
v01.115.000חדשAI / תובנות ולמידהminorמערכת למידה לעוזר ה-AI — תובנות לפי רמה, חילוץ בזמן אמת, וחידוד בצ'אט
הרחבה מקיפה של מנגנון התובנות כך שהעוזר הופך ל'מכונה לומדת' עם פיקוח אנושי.
פרטים נוספים ↓הסתר ↑
הרחבה מקיפה של מנגנון התובנות כך שהעוזר הופך ל'מכונה לומדת' עם פיקוח אנושי. תובנות נשמרות ברמה המדויקת שלהן ונשלפות רק כשהן רלוונטיות, במקום להיכנס עיוורת לכל פרומפט: משלוח, לקוח, שליח, חברה (קיימים) + שתי רמות חדשות — 'משתמש' (תובנות על אופן העבודה המועדף של משתמש ספציפי, מוזרקות לפרומפט רק כשאותו משתמש מדבר) ו'מערכת' (משוב על העוזר עצמו: פעולות שלא הצליח לבצע, טעויות חוזרות, שאלות שלא ידע לענות — נשמר למפתחים בלבד ולעולם לא מוזרק לשום פרומפט). כל תובנה נשמרת כעת עם 'הקשר מקור' (sourceContext) שנלכד ברגע היצירה — באיזו קבוצה/שיחה נוצרה, מי אמר (צוות/שליח/לקוח), מתי, והטקסט המקורי המדויק. נוסף חילוץ תובנות בזמן אמת מקבוצות WhatsApp: כשמנוע התרחישים מזהה אירוע תפעולי ודאי (חריג), קריאת Haiku זולה וfire-and-forget מחלצת תובנה ארוכת-טווח אם יש כזו, עם dedup ושמירה כ'ממתינה'. בדף 'תובנות AI' נוסף כפתור 'חדד' שפותח drawer עם מיני-צ'אט: המשתמש יכול לחקור את העוזר כיצד הגיע לתובנה, לראות את הטקסט המקורי, ולדון בנכונותה — ובסוף לאשר, לדחות, או לשמור ניסוח מחודד שהעוזר מציע. תובנות מערכת מוצגות בלוח הבקרה (Control Plane → תובנות מערכת) בלבד, חוצות-טננט, מאחורי הרשאת פלטפורמה, כדי שהמפתחים ידעו מה לשפר. בנוסף, פריטים 'ממתינים' שלא טופלו 60 יום נמחקים בשקט כדי לשמור על מיקוד דף הסקירה.
- שתי רמות תובנה חדשות: 'משתמש' (מוזרקת רק לאותו משתמש) ו'מערכת' (משוב למפתחים, לעולם לא בפרומפט)
- הקשר מקור מלא לכל תובנה — קבוצה/שיחה, דובר, תאריך וטקסט מקורי
- חילוץ תובנות בזמן אמת מקבוצות WhatsApp בעת אירוע תפעולי ודאי (fire-and-forget, dedup)
- כפתור 'חדד' — מיני-צ'אט לבחינת התובנה וקבלת ניסוח מחודד, לצד אישור/דחייה
- לוח בקרה לתובנות מערכת חוצה-טננט (הרשאת פלטפורמה) + ניקוי שקט של ממתינות אחרי 60 יום
- בידוד תובנות מערכת נאכף בשרת (לא רק ב-UI) — אינן דולפות או ניתנות לעריכה דרך נתיבי הטננט
v01.113.001תיקוןתיבת הודעותכפתור 'אחורה' במובייל ב-business-inbox חוזר לרשימת הצ'אטים במקום לצאת מהדף
בצ'אט הלקוחות העסקיים (/messages/business-inbox), לחיצה על כפתור/מחוות 'אחורה' של הטלפון בתוך שיחה פתוחה הוציאה את המשתמש לגמרי מהדף, בניגוד לצ'אט לקוחות הקצה (/messages/inbox) ששם 'אחורה' חוזר לרשימת הצ'אטים.
פרטים נוספים ↓הסתר ↑
בצ'אט הלקוחות העסקיים (/messages/business-inbox), לחיצה על כפתור/מחוות 'אחורה' של הטלפון בתוך שיחה פתוחה הוציאה את המשתמש לגמרי מהדף, בניגוד לצ'אט לקוחות הקצה (/messages/inbox) ששם 'אחורה' חוזר לרשימת הצ'אטים. נוסף ל-business-inbox אותו יירוט מבוסס-היסטוריה (pushState בפתיחת שיחה + מאזין popstate): כעת 'אחורה' סוגר את השיחה הפתוחה וחוזר לרשימה, וזהה בשני הצ'אטים.
v01.113.000תיקוןליונוויל / הוכחת מסירהminorתמונות הוכחת מסירה מליונוויל — תיקון פער שהשפיע על רוב המשלוחים שהושלמו
התגלה שתמונות הוכחת המסירה (POD) שצולמו בליונוויל כמעט ולא הופיעו אצלנו — רק חלק זעיר מהמשלוחים שהושלמו הציגו תמונה.
פרטים נוספים ↓הסתר ↑
התגלה שתמונות הוכחת המסירה (POD) שצולמו בליונוויל כמעט ולא הופיעו אצלנו — רק חלק זעיר מהמשלוחים שהושלמו הציגו תמונה. שורש הבעיה: ליונוויל מצרף את התמונה ברגע המסירה או מיד אחריה, כשהמשלוח כבר במצב 'הושלם' (טרמינלי), וה-payload של ה-webhook הרגיל לרוב לא כולל את מערך התמונות — התמונה זמינה באופן אמין רק דרך /tasks/show. בנוסף, ה-cron שמסנכרן משלוחים פתוחים מדלג על משלוחים טרמינליים, כך שמשלוח שהושלם לא נמשך שוב ולכן התמונה שלו לא נשלפה. כמו כן הסנכרון הקודם היה הרסני — מחק תמונות קיימות בכל עדכון וייצר אותן מחדש רק אם ה-payload הנוכחי כלל תמונות, כך שכל webhook מאוחר בלי תמונות מחק תמונה שכבר נשמרה. התיקון: (1) הסנכרון הפך ללא-הרסני — לא מוחק תמונות קיימות כשאין תמונות ב-payload; (2) טיפול ייעודי ב-webhook של 'צירוף תמונה' (is_photo_attached) — כשמתקבל סיגנל שצורפה תמונה אך ה-payload לא נשא אותה, התמונה נמשכת מ-/tasks/show; (3) cron התאמה חדש (sync-completed-pod-images) שרץ כל 30 דקות, מאתר משלוחים שהושלמו לאחרונה וחסרי תמונה, ומושך את ה-POD מליונוויל — עם חלון זמן מוגבל וניסיונות חוזרים תחומים כדי לכבד את ה-rate limit. התיקון פועל קדימה (משלוחים חדשים); לא בוצע backfill היסטורי המוני.
- סנכרון תמונות POD לא-הרסני — webhook בלי תמונות לא מוחק יותר תמונה שכבר נשמרה
- משיכת התמונה מ-/tasks/show כשליונוויל מסמן is_photo_attached אך ה-webhook לא נשא את ה-URL
- cron התאמה חדש שתופס תמונות שמצורפות אחרי השלמת המשלוח (כל 30 דק', חלון מוגבל)
v01.112.001שיפורתיבת הודעות / עוזר AIרמז 'Shift+Enter לשורה חדשה' בפלייסהולדר תיבת ההודעה בשני הצ'אטים
הפלייסהולדר של תיבת כתיבת ההודעה בשני הצ'אטים (/messages/inbox ו-/messages/business-inbox) מציין כעת גם איך יורדים שורה: 'כתוב הודעה...
פרטים נוספים ↓הסתר ↑
הפלייסהולדר של תיבת כתיבת ההודעה בשני הצ'אטים (/messages/inbox ו-/messages/business-inbox) מציין כעת גם איך יורדים שורה: 'כתוב הודעה... (Enter לשליחה, Shift+Enter לשורה חדשה)'. במובייל (ב-/messages/inbox) נשאר 'כתוב הודעה...' בלבד כי שם Enter ממילא יורד שורה. בנוסף — לארבעת כפתורי ה-header של חלון עוזר ה-AI (שאלות ממתינות, היסטוריית שיחות, שיחה חדשה, סגור) נוספו Tooltip תקניים בהתאם לקונבנציית shadcn/ui. כמו כן תוקנה אי-עקביות במקלדת: ב-business-inbox במובייל Enter שלח את ההודעה (במקום לרדת שורה) בניגוד ל-/messages/inbox — נוסף זיהוי מכשיר-מגע גם ל-business-inbox, כך שכעת בשני הצ'אטים זהה: במובייל Enter יורד שורה (השליחה בכפתור) ובדסקטופ Enter שולח ו-Shift+Enter יורד שורה.
v01.112.000חדשAI / עלויותminorלשונית 'עלויות' חדשה תחת AI — מעקב עלויות AI פר-לקוח (מדידה עצמית)
נוספה תחת הלשונית הראשית AI (לצד 'ספקים', 'תרחישים', 'תובנות') לשונית 'עלויות' המציגה לכל לקוח כמה עלה לו השימוש ב-AI — בעיצוב זהה לדף עלויות המסרונים.
פרטים נוספים ↓הסתר ↑
נוספה תחת הלשונית הראשית AI (לצד 'ספקים', 'תרחישים', 'תובנות') לשונית 'עלויות' המציגה לכל לקוח כמה עלה לו השימוש ב-AI — בעיצוב זהה לדף עלויות המסרונים. מכיוון שכל לקוח מחבר ספק AI משלו (ואנחנו לא מספקים שירות AI), העלות נמדדת בשיטת מדידה-עצמית: בכל קריאת AI נרשם אירוע שימוש פר-לקוח (טבלת ai_usage_event) עם ספירת הטוקנים בפועל, והעלות מחושבת ונשמרת באותו רגע מול מחירון. כך השיטה אחידה לכל הספקים (Anthropic/Google/OpenAI), והכי חשוב — היא מייחסת ללקוח גם שימוש שרץ על מפתח-הגיבוי של המערכת (key_source = system), כך שאפשר להציג ולחייב עליו בנפרד (הדף מפצל 'מפתח שלך' מול 'מפתח מערכת'). המספר הוא הערכה: ספירת הטוקנים מדויקת, התמחור לפי מחירון ציבורי. הדף מציג כרטיס עלות כוללת (₪/$ עם שער חי), מדדי טוקנים, וטבלאות פירוט לפי מודל ולפי יום, וכן אזהרה על מודלים ללא מחיר מוגדר. אינסטרומנטציה: מנוע התרחישים (classify + סיכומי דיווח שליח) וסוכני הצ'אט (עובדים + פורטל לקוחות) — דרך wrapper מדידה guarded שלעולם לא שובר קריאת AI. בנוסף, נוסף דף ניהול 'מחירי מודלים' (למנהלי פלטפורמה בלבד) לתחזוקת המחירון לאורך זמן: ברירות מחדל בקוד + override ב-DB הניתן לעריכה ידנית, וכפתור 'בדוק מחירים' שמריץ חיפוש אינטרנט (Claude + web_search) מול דפי התמחור הרשמיים ומציג diff לאישור — מסמן שינויים חריגים, ולעולם לא מחיל אוטומטית בלי אישור אנושי. עלויות היסטוריות אינן משתנות בעדכון מחיר (snapshot בזמן הרישום).
- לשונית 'עלויות' פר-לקוח תחת AI — הערכת עלות לפי ספירת טוקנים ומחירון, אחידה לכל הספקים
- מדידה עצמית מייחסת גם שימוש על מפתח-הגיבוי של המערכת ללקוח (פיצול 'מפתח שלך' מול 'מפתח מערכת') — בסיס לחיוב עתידי
- דף 'מחירי מודלים' למנהלי פלטפורמה: מחירון בקוד + override, וכפתור 'בדוק מחירים' (web search → diff לאישור, ללא החלה אוטומטית)
- עלויות היסטוריות יציבות — המחיר מצולם בזמן הרישום ואינו משתנה בעדכון מחירון
v01.111.002שיפורבוט קבוצות / תרחישיםעדכון השולח במלוא פרטי הדיווח של השליח (אין מענה / כשל מסירה) — עם סינון מידע פנימי
עד כה תרחיש 'אין מענה' שלח ללקוח העסקי תבנית קבועה שדיברה רק על אי-מענה, וסיננה פרטים נוספים שהשליח ציין באותה הודעה.
פרטים נוספים ↓הסתר ↑
עד כה תרחיש 'אין מענה' שלח ללקוח העסקי תבנית קבועה שדיברה רק על אי-מענה, וסיננה פרטים נוספים שהשליח ציין באותה הודעה. כעת הבוט מחלץ מהדיווח של השליח תקציר נאמן של כל הפרטים התפעוליים שעשויים לעזור במסירה — למשל 'אין מענה מהנמען, והשליח גם אינו מצליח לאתר את הכתובת' — ומשבץ אותו בהודעה ללקוח דרך placeholder חדש {driverReport}. כך הלקוח מקבל תמונה מלאה (אם השליח לא מצא את הכתובת הוא לא יכול היה אפילו לדפוק בדלת או להניח ליד הדלת אם זה מאושר) ויכול לתת הנחיה מועילה. זה אינו תרחיש 'כתובת שגויה' — שם הכתובת עצמה שגויה וצריך לתקנה; כאן הכתובת תקינה והשליח פשוט אינו מאתר אותה, והפרט מועבר ללקוח במסגרת האין-מענה. בנוסף — אותו חילוץ הוחל גם על תרחיש 'דיווח כשל מסירה', שעד כה העביר ללקוח את הטקסט הגולמי של השליח (סיכון לחשיפת מידע פנימי). חשוב מכך, מנגנון החילוץ מסנן כעת מידע פנימי/תפעולי שהלקוח לא אמור להיחשף אליו ללא בקרה: עיכובים אצלנו/במחסן, בעיות שידור/שיבוץ, החבילה הגיעה לשליח באיחור מצדנו, פספוס יום איסוף, נזק/אשמה, מחירים, שמות עובדים, מספרי משלוח ותיוגי צוות. הכלל: כל פרט שעוזר למסירה (מצד הנמען/הכתובת) מועבר לשולח; כל פרט פנימי — מסונן, ובספק מושמט; אם לא נשאר פרט בטוח לחשיפה — נשלח משפט גנרי. החילוץ נעשה בקריאת Haiku קצרה שמפענחת גם קיצורים (אמ=אין מענה). בנוסף, גם תשובת השליח בתרחיש 'בקשת תיאום/שינוי מועד' עוברת ניקוי לפני שהיא מועברת ללקוח — שומר על התשובה המהותית (מתי/האם/אילוצי מסירה) ומסיר מידע פנימי; אם לא נשאר תוכן בטוח, התשובה כלל לא מועברת.
v01.111.001תיקוןניהול / ניטור AIחיתוך כותרות ארוכות ברשימת שיחות ה-AI עם Tooltip
כותרות ארוכות ברשימת השיחות בדף /admin/ai-sessions גלשו ודחקו את חותמת הזמן מהשורה.
פרטים נוספים ↓הסתר ↑
כותרות ארוכות ברשימת השיחות בדף /admin/ai-sessions גלשו ודחקו את חותמת הזמן מהשורה. התיקון בשני שלבים: (1) נוסף min-w-0 לשדה הכותרת כך ש-truncate ייכנס לפועל ב-flex child; (2) הרשימה הוחזרה משימוש ב-Radix ScrollArea ל-div עם overflow-y-auto רגיל (כמו ב-/messages/inbox) — ה-Viewport של Radix עוטף את התוכן ב-wrapper עם display:table שמתרחב לרוחב הכותרת הארוכה ביותר ולכן ביטל את ה-truncate לחלוטין. הכותרת נעטפה גם ברכיב ה-Tooltip המשותף (בהתאם לקונבנציה שאוסרת title נייטיבי) שמציג את הכותרת המלאה במעבר עכבר. בנוסף — פעולות שינוי-סטטוס שמבצע עוזר ה-AI (כולל ביטול משלוח) מתעדות כעת מי ביקש אותן: ה-audit log מקשר את המשתמש המבקש (כשהוא משתמש אמיתי) ומוסיף '(לבקשת <שם>)' לתיאור, כך שבהיסטוריית הפעולות של המשלוח רואים את שם המבקש והאווטאר שלו עם חותמת הזמן — במקום 'מערכת' בלבד. תג ה-AI וכפתור הביטול נשמרים. עד כה לא היה ניתן לדעת מההיסטוריה מי הורה ל-AI לבצע את הפעולה. בנוסף — ניקיון רעש בהיסטוריית הפעולות של המשלוח: רשומות עדכון-ביקור הציגו 'עודכנו השדות: שליח (משלוח 12345)' — גם מספר המשלוח (מיותר בדף המשלוח עצמו) וגם שם השדה (שכבר מוצג בטבלת השינויים מתחת) — וכעת שורת התיאור מנוקה ונשען על תג 'עדכון ביקור' + טבלת השינויים בלבד; ושליחות WhatsApp/תבנית Meta (send_whatsapp) הוסרו מהטיימליין כיוון שכבר מוצגות בפירוט באקורדיון 'מסרונים' הייעודי — שניהם שינויי תצוגה בלבד, הנתונים נשמרים ב-DB.
v01.111.000תיקוןAI / WhatsApp inboxminorבוט הקבוצות מזהה היררכיות — תת-לקוחות וסאב-שליחים מנותבים לקבוצת האב/המנהל
מנוע התרחישים בקבוצות שייך עד כה משלוחים לפי רשומת הלקוח/השליח המדויקת בלבד, ולכן פספס שתי היררכיות נפוצות שכבר קיימות במערכת: (1) לקוח-אב עם תת-לקוחות (parentCustomerId) — למשל 'חבד ישראל' שיש לו קבוצה ו-8 תת-לקוחות (חי…
פרטים נוספים ↓הסתר ↑
מנוע התרחישים בקבוצות שייך עד כה משלוחים לפי רשומת הלקוח/השליח המדויקת בלבד, ולכן פספס שתי היררכיות נפוצות שכבר קיימות במערכת: (1) לקוח-אב עם תת-לקוחות (parentCustomerId) — למשל 'חבד ישראל' שיש לו קבוצה ו-8 תת-לקוחות (חיינו, פרוייקטים, עלון פרשה...) עם אלפי משלוחים, אך ללא קבוצה משלהם; (2) שליח-מנהל עם סאב-שליחים (DriverManagerLink) — למשל 'מישל ראשון לציון' שיש לו קבוצה ו-12 סאב-שליחים ללא קבוצה. כתוצאה מכך משלוחים של תת-הלקוחות/הסאב-שליחים לא טופלו ע"י הבוט. כעת השיוך מודע-היררכיה: בכל תרחישי הגישור (אין-מענה, צפי מסירה, כתובת שגויה, טלפון שגוי, כשל מסירה) — איתור המשלוח מקבוצת שליח-מנהל מכסה את כל הסאב-שליחים (visit.driverId בין המנהל לסאבים), שליחת הודעה ללקוח נופלת חזרה לקבוצת לקוח-האב כשלתת-לקוח אין קבוצה, איתור משלוח מקבוצת לקוח-אב מכסה את כל תת-הלקוחות, ושאלה לשליח נופלת חזרה לקבוצת המנהל כשלסאב-שליח אין קבוצה. מומש מרכזית ב-lib/ai/scenarios/shared.ts (getDriverAndSubIds, getCustomerAndChildrenIds, customerChatTarget מודע-אב, resolveDriverGroupChat מודע-מנהל) כך שכל ה-handlers נהנים מהתיקון.
- קבוצת לקוח-אב (חבד ישראל) מכסה כעת את משלוחי כל תת-הלקוחות שלה
- קבוצת שליח-מנהל (מישל) מכסה כעת את משלוחי כל הסאב-שליחים שלו
- fallback אוטומטי לקבוצת האב/המנהל כשלישות-הבן אין קבוצה — בכל 5 תרחישי הגישור
v01.110.000שיפורAI / תרחישי הבוטminorעיצוב מחדש לדף תרחישי הבוט — כרטיסי יכולת + מגירת עריכה
דף תרחישי הבוט (AI → תרחישים) עוצב מחדש מרשימת סקשנים ארוכה וטקסטואלית לגריד כרטיסי יכולת קומפקטי.
פרטים נוספים ↓הסתר ↑
דף תרחישי הבוט (AI → תרחישים) עוצב מחדש מרשימת סקשנים ארוכה וטקסטואלית לגריד כרטיסי יכולת קומפקטי. כל תרחיש מוצג ככרטיס עם אייקון בוט, כותרת, תגיות סיכון (קריאה/גישור/פעולה) וסוג קבוצה, צ'יפ זרימה ויזואלי שממחיש את כיוון הגישור (שליח ← בוט ← לקוח), ומתג הפעלה/כיבוי כפעולה הראשית. ההפעלה/כיבוי נשמרים מיידית (אופטימי, עם חזרה לאחור בכשל) במקום כפתור שמירה גלובלי. עריכת נוסח התגובות עברה למגירה צדדית ממוקדת לפי קונבנציית ה-Drawer של המערכת (כותרת קבועה, אזור גלילה, וכפתורי שמירה/ביטול קבועים). כרטיס מופעל מקבל הדגשה סגולה עדינה, וכרטיס עם נוסח מותאם מסומן 'מותאם'. שמירת נוסח שומרת רק overrides שנבדלים מברירת המחדל, כך ששינויי נוסח עתידיים בברירת המחדל עדיין מגיעים לטננטים שלא התאימו.
- גריד כרטיסי יכולת במקום רשימת סקשנים ארוכה
- צ'יפ זרימה ויזואלי לכיוון הגישור (שליח ← בוט ← לקוח)
- מתג הפעלה/כיבוי עם שמירה מיידית (אופטימי)
- עריכת נוסח התגובות במגירה צדדית ממוקדת
- שמירת overrides בלבד — שינויי ברירת מחדל עתידיים עדיין מתפשטים לטננטים
v01.109.001שיפורתמחור / משלוחי איסוףזיהוי משלוחי איסוף/החזרה — מילוי הפער מליונוויל
משלוחי 'איסוף' (סחורה שנאספת מלקוח קצה וחוזרת לבית העסק) זוהו עד היום רק כשליונוויל תייג task_type='pickup', אבל פריסת השדה אצל ליונוויל אינה עקבית — חלק מהאיסופים הגיעו ללא תיוג ולכן נחשבו (וגם תומחרו) כמסירה רגילה.
פרטים נוספים ↓הסתר ↑
משלוחי 'איסוף' (סחורה שנאספת מלקוח קצה וחוזרת לבית העסק) זוהו עד היום רק כשליונוויל תייג task_type='pickup', אבל פריסת השדה אצל ליונוויל אינה עקבית — חלק מהאיסופים הגיעו ללא תיוג ולכן נחשבו (וגם תומחרו) כמסירה רגילה. חוזק מנגנון הזיהוי האוטומטי שגוזר 'pickup' כשהנמען הוא העסק עצמו: עיגון לפי טלפון או כתובת החנות (מרשומת הלקוח), עם תנאי-בטיחות ששם הנמען תואם לשם העסק — כך שמסירה רגילה לעולם לא תסומן בטעות (חשוב כי task_type מזין תמחור ועמלות סוכן). הכלל מחליף את ה-fallback הישן שהתאים רק כתובת מדויקת (פספס סניף שני, כתיב רחוב שונה, או לקוח ללא כתובת ברשומה). בנוסף בוצע backfill חד-פעמי שתייג 298 משלוחי איסוף שהיו ללא תיוג. הזיהוי המחוזק רץ מעתה אוטומטית בכל סנכרון. בנוסף, משלוחי האיסוף הוצאו מטאבי פערי המסירה (מסירה / קריטיים / יום אחרון) כדי שלא יתערבבו עם מסירות רגילות, וטאב 'כפולות' הורחב ל'החזרות וכפולות' — מציג כעת גם את כל האיסופים הפתוחים (סחורה שנאספה מלקוח וטרם חזרה לעסק) לצד הכפולות. נוספה עמודת 'סוג' (כפולה / החזרה), מניין הימים לאיסוף נספר מרגע האיסוף בפועל מהלקוח, ועמודת השליח מציגה את מי שמביא חזרה לעסק (שליח ההחזרה לכפולה, שליח המסירה-חזרה-לעסק לאיסוף).
v01.109.000חדשבוט קבוצות / תרחישיםminorחמישה תרחישי בוט חדשים — כתובת/טלפון שגויים, תיאום מועד, כשל מסירה ובקשת שידור
מנוע התרחישים של בוט הקבוצות (Green API) הורחב בחמישה תרחישים חדשים שניתן להפעיל ולערוך פר-טננט בהגדרות → תרחישי הבוט.
פרטים נוספים ↓הסתר ↑
מנוע התרחישים של בוט הקבוצות (Green API) הורחב בחמישה תרחישים חדשים שניתן להפעיל ולערוך פר-טננט בהגדרות → תרחישי הבוט. (1) 'כתובת שגויה/חסרה' — שליח מדווח בקבוצתו שהכתובת שגויה או שאינו מוצא אותה, הבוט שואל את הלקוח העסקי לכתובת הנכונה, מחלץ את השדות (עיר/רחוב/מספר/קומה/דירה) בקריאת Haiku קצרה, מעדכן את המשלוח ומסנכרן לליונוויל, ומעביר את הכתובת המעודכנת לשליח. (2) 'טלפון שגוי' — שליח מדווח שמספר הנמען לא תקין, הבוט מבקש מהלקוח מספר נכון, מעדכן את המשלוח ומעביר לשליח. (3) 'בקשת תיאום/שינוי מועד' — לקוח מבקש בקבוצתו לתאם או לשנות מועד מסירה, הבוט מעביר את הבקשה לשליח המשובץ ושואל אם אפשרי, ומעביר את התשובה חזרה ללקוח (אם המשלוח לא משובץ — שותק). (4) 'דיווח כשל מסירה' — שליח מדווח על כשל סופי, הבוט מיידע את הלקוח העסקי על הכשל והסיבה (יידוע חד-כיווני ללא גישור). (5) 'בקשת שידור' — קבלן/שליח מדווח בקבוצתו שמשלוח לא משודר אליו ומבקש לשדר (מילים כמו 'לשדר', 'לא משודר', 'לא נסרק', 'שימו עלי', 'לא מופיע לי באפליקציה'), או שולח צילום של מדבקת המשלוח. הבוט מזהה את המשלוח לפי המספר — מטקסט או מתוך התמונה ב-OCR (Claude vision) — משבץ את הקבלן ומשדר אליו בצורה גנרית (ליונוויל / SNL / שותף Shipnest), ממתין מספר שניות וקורא את מזהה השידור החיצוני (target_partner_task_id המאוחד), ומחזיר את מספר השידור לקבוצה. אם הקבלן טועה והמשלוח כבר משודר — הבוט מחזיר את מספר השידור הקיים במקום לשדר שוב. אם לא צוין מספר — הבוט מבקש אותו (ופותח relay לתשובה). כל חמשת התרחישים כבויים כברירת מחדל — הם אינם משנים את התנהגות הבוט של אף טננט עד שהוא בוחר להפעיל אותם — בהתאם לעקרון 'בספק → שתיקה'. התרחישים שמעדכנים נתונים או משבצים/משדרים (כתובת/טלפון/שידור) מסומנים ברמת סיכון 'פעולה'. ה-OCR לתמונות מוגבל לקבוצות שליח מקושרות שבהן תרחיש השידור מופעל בלבד (בקרת עלות). הקלסיפייר מקבל רמזי טריגר עם הבחנות מפורשות בין התרחישים (למשל 'אין מענה' מול 'טלפון שגוי' מול 'כשל מסירה' מול 'בקשת שידור') כדי למנוע בלבול.
- תרחיש כתובת שגויה — חילוץ כתובת ב-Haiku, עדכון משלוח וסנכרון ליונוויל
- תרחיש טלפון שגוי — בקשת מספר נכון מהלקוח, עדכון והעברה לשליח
- תרחיש בקשת תיאום/שינוי מועד — גישור לקוח↔שליח
- תרחיש דיווח כשל מסירה — יידוע חד-כיווני ללקוח העסקי
- תרחיש בקשת שידור — שיבוץ ושידור אוטומטי לקבלן (ליונוויל/SNL/שותף), כולל OCR לזיהוי מספר המשלוח מצילום מדבקה והחזרת מספר השידור
- זיהוי קיצור השליחים 'אמ'/'א.מ' (=אין מענה) בתרחיש 'אין מענה', והעברת הודעות קצרות עם מספר משלוח או קיצור מוכר את מסנן ה-substantive (כדי שדיווחים תמציתיים כמו '25510238 אמ' ייתפסו)
- כל התרחישים החדשים כבויים כברירת מחדל (opt-in פר-טננט)
v01.108.000חדשניהול / ניטור AIminorשדרוג דף ניטור שיחות ה-AI — חיפוש עומק, פילטר תאריכים, מיון, רינדור עשיר ועיצוב מחדש
דף /admin/ai-sessions לניטור שיחות ה-AI בכל הטננטים קיבל ארבע יכולות חדשות: (1) חיפוש העומק עכשיו סורק גם את תוכן ההודעות עצמן (מעבר לכותרת ושם הטננט) — מנוצל אינדקס ה-pg_trgm שכבר קיים על עמודת ה-content; (2) פילטר טווח…
פרטים נוספים ↓הסתר ↑
דף /admin/ai-sessions לניטור שיחות ה-AI בכל הטננטים קיבל ארבע יכולות חדשות: (1) חיפוש העומק עכשיו סורק גם את תוכן ההודעות עצמן (מעבר לכותרת ושם הטננט) — מנוצל אינדקס ה-pg_trgm שכבר קיים על עמודת ה-content; (2) פילטר טווח תאריכים דרך רכיב DateRangeFilter המשותף, מסנן לפי פעילות אחרונה (updatedAt); (3) בורר מיון — פעילות אחרונה / פעילות ישנה / הכי הרבה הודעות / תאריך יצירה; (4) רינדור הודעות עשיר בצד הפירוט: תצוגת מדיה inline (תמונה/סטיקר/אודיו/וידאו/קובץ עם fallback כש-URL פג), סטטוס מסירה (נשלח/נמסר/נקרא/נכשל), ייחוס שולח בהודעות קבוצה (senderName/senderPhone עם צבע דטרמיניסטי), אבחנה ויזואלית בין assistant לבין staff, ועיצוב נקי של קריאות לכלים (tool calls) במקום JSON גולמי. בנוסף, הדף עוצב מחדש בשפת העיצוב של צ'אט לקוחות הקצה (/messages/inbox): רשימת שיחות עם שורות rounded-xl ואווטרים עגולים ממופי-צבע (ראשי תיבות הטננט) עם טבעת active, חיפוש בשדה מעוגל (rounded-full) עם כפתור ניקוי, פילטר ההקשר הומר לשורת צ'יפים בסגנון ה-inbox, וכותרת הפירוט ובועות ההודעות (בועות שקופות מעוגלות עם פינה אסימטרית, border ו-shadow) תואמות עכשיו את מראה הצ'אט.
- חיפוש עומק בתוך תוכן ההודעות (pg_trgm)
- פילטר טווח תאריכים + ארבע אפשרויות מיון
- תצוגת מדיה inline, סטטוס מסירה וייחוס שולח בהודעות קבוצה
- רינדור מסודר של קריאות לכלים במקום JSON גולמי
- עיצוב מחדש בשפת ה-inbox — אווטרים עגולים, צ'יפים, בועות שקופות ושורות rounded-xl
v01.107.010שיפורתיבת הודעותאפקט בלור עדין מתחת לתיבת הכתיבה הצפה גם ב-business-inbox
אפקט ה-frosted blur שמתחת לתיבת הכתיבה הצפה (שכבר היה ב-/messages/inbox) הוחל גם על צ'אט הלקוחות העסקיים (business-inbox) — רצועת טשטוש עדין (backdrop-blur) בתחתית אזור ההודעות עם מסכת גרדיאנט שמתחזקת ליד האינפוט ודועכת …
פרטים נוספים ↓הסתר ↑
אפקט ה-frosted blur שמתחת לתיבת הכתיבה הצפה (שכבר היה ב-/messages/inbox) הוחל גם על צ'אט הלקוחות העסקיים (business-inbox) — רצועת טשטוש עדין (backdrop-blur) בתחתית אזור ההודעות עם מסכת גרדיאנט שמתחזקת ליד האינפוט ודועכת כלפי מעלה, כך שההודעות שנגללות מתחת לתיבה הצפה נראות 'מעושנות' במקום חתוכות. כעת שני הצ'אטים זהים באפקט.
v01.107.009שיפורממשק / Tooltipהחלפת tooltips נייטיביים (title) ב-Tooltip מעוצב בכל המערכת
כל השימושים שנותרו ב-title נייטיבי של הדפדפן בתור tooltip הומרו לרכיב ה-Tooltip המעוצב של המערכת — בהתאם לקונבנציה שאוסרת title כ-tooltip.
פרטים נוספים ↓הסתר ↑
כל השימושים שנותרו ב-title נייטיבי של הדפדפן בתור tooltip הומרו לרכיב ה-Tooltip המעוצב של המערכת — בהתאם לקונבנציה שאוסרת title כ-tooltip. הומרו: כפתורי הסרה ממועדפים בסיידבר, צ'יפ העדיפות בעמוד הפידבק, נקודת אירוע ביומן הנוכחות, פעולות בצ'אט לקוחות הקצה (סמן כנקרא/לא-נקרא, שלח קובץ, בחר תמונה אחרת, תבניות Meta), כפתור רענון באנליטיקס ההודעות, עריכה/מחיקה בתבניות תעריפי המשלוח, ארבע הפעולות במודאל סנכרון משלוחים, תצוגה מקדימה בכרטסת ובחשבונאות הלקוח, וכפתור 'שלח לאישור Meta' בטריגרים. ה-iframes (title כדרישת נגישות) ומרקר ה-Leaflet (title בתוך מחרוזת HTML) נותרו ללא שינוי. במקומות שבהם ה-title שכפל aria-label על אלמנט שלא ניתן לעטוף בבטחה (מתג הטננט בראש הסיידבר, פס הגרירה של הסיידבר) הוסר ה-title המיותר.
v01.107.007תיקוןתשתית / Next.js 16ניקוי אזהרות Next.js 16 בטרמינל — Edge Runtime, proxy ו-metadataBase
טופלו שלוש אזהרות שהוצפו בטרמינל הפיתוח: (1) המודול lib/encryption.ts (שמייבא את crypto של Node) נמשך אל ה-Edge Runtime דרך שרשרת instrumentation → logger → whatsapp → provider-factory, ויצר אזהרת קומפילציה חוזרת בכל בקש…
פרטים נוספים ↓הסתר ↑
טופלו שלוש אזהרות שהוצפו בטרמינל הפיתוח: (1) המודול lib/encryption.ts (שמייבא את crypto של Node) נמשך אל ה-Edge Runtime דרך שרשרת instrumentation → logger → whatsapp → provider-factory, ויצר אזהרת קומפילציה חוזרת בכל בקשה. נוסף guard לפי NEXT_RUNTIME ב-onRequestError כך שה-bundler משמיט את ייבוא ה-logger ואת התלויות הצד-שרתיות שלו מ-bundle ה-Edge לחלוטין. (2) הקובץ middleware.ts שונה ל-proxy.ts בהתאם לקונבנציה החדשה של Next.js 16 (middleware הוצא משימוש). (3) הוגדר metadataBase ב-root layout כך שכתובות תמונות ה-OG/Twitter נפתרות מול הדומיין הקנוני במקום http://localhost.
v01.107.006שיפורצ'אט / Inboxריענון ראש שני הצ'אטים — תפריט 3-נקודות, אייקון אחיד, והסרת בורדר מיותר
בתיבת צ'אט לקוחות הקצה (/messages/inbox) הוסר הקו (border-b) שהפריד בין הכותרת הראשית לבין שדה החיפוש — הוא היה מיותר ויצר חלוקה עודפת.
פרטים נוספים ↓הסתר ↑
בתיבת צ'אט לקוחות הקצה (/messages/inbox) הוסר הקו (border-b) שהפריד בין הכותרת הראשית לבין שדה החיפוש — הוא היה מיותר ויצר חלוקה עודפת. בנוסף, בשתי תיבות הצ'אט הפעולות שהיו מוצגות משמאל לכותרת קופלו אל תוך כפתור 3-נקודות אנכי (kebab): בתיבה העסקית — 'סנכרן קבוצות' ומעבר ל'ארכיון'; בתיבת לקוחות הקצה — 'שיחה חדשה' ו'אפשר התראות דסקטופ'. גם האייקון שליד כותרת התיבה העסקית הוחלף מאייקון בניין (כחול) לאייקון הצ'אט הקלאסי (MessageCircle אפור), זהה לתיבת לקוחות הקצה.
v01.107.005שיפורצ'אט / Inboxעיצוב אחיד לאינפוט החיפוש בשתי תיבות הצ'אט
שדה החיפוש בתיבת הצ'אט העסקי ובתיבת לקוחות הקצה אוחד, תוך לקיחת היתרונות מכל אחד: שוליים צרים של 8px משני צידי השדה (מ-business-inbox); רקע פנימי שמתמזג עם רקע הדף וצל עדין מסביב (מ-inbox); אייקון החיפוש בצד ימין עם רווח…
פרטים נוספים ↓הסתר ↑
שדה החיפוש בתיבת הצ'אט העסקי ובתיבת לקוחות הקצה אוחד, תוך לקיחת היתרונות מכל אחד: שוליים צרים של 8px משני צידי השדה (מ-business-inbox); רקע פנימי שמתמזג עם רקע הדף וצל עדין מסביב (מ-inbox); אייקון החיפוש בצד ימין עם רווח של 8px בדיוק בינו לבין הפלייסהולדר (תוקן הרווח שהיה גדול מדי); ופינות מעוגלות לגמרי (rounded-full) בסגנון וואטסאפ — שהיה חסר בשניהם. רצועת הצ'יפים ב-inbox יושרה ל-8px בהתאם, כדי שתישאר צמודה לשדה החיפוש.
v01.107.004שיפורתיבת הודעותתיבת כתיבת ההודעה צפה מעל הצ'אט בשני דפי השיחות
בשני הצ'אטים (/messages/inbox ו-/messages/business-inbox) אזור כתיבת ההודעה בתחתית עוצב מחדש כך ש'יצוף' מעל השיחה במקום לשבת על פס נפרד.
פרטים נוספים ↓הסתר ↑
בשני הצ'אטים (/messages/inbox ו-/messages/business-inbox) אזור כתיבת ההודעה בתחתית עוצב מחדש כך ש'יצוף' מעל השיחה במקום לשבת על פס נפרד. הוסר רקע הבר הנפרד (היה bg-muted) והבורדר העליון — הסביבה סביב האינפוט שקופה לחלוטין, כך שההודעות נגללות ונראות מאחוריה (מלמעלה ומלמטה). רק תיבת הקלט עצמה אטומה (רקע לבן/כהה + בורדר משלה + צל עדין) ונראית צפה. הסביבה השקופה היא click-through (pointer-events) כך שניתן לגלול את ההודעות גם דרך השוליים. אזור ההודעות מקבל padding תחתון כדי שההודעה האחרונה נחה מעל התיבה הצפה, וכפתור 'גלול למטה' הוזז להופיע מעליה.
- תיבת קלט צפה עם סביבה שקופה — ההודעות נראות נגללות מאחוריה
- ללא רקע נפרד וללא בורדר עליון; רק הקלט אטום עם צל עדין
- חל על שני דפי הצ'אט (לקוחות קצה + לקוחות עסקיים)
v01.107.003שיפורצ'אט / Inboxגלילת צ'יפי הסינון בשתי תיבות הצ'אט — גרירה בסגנון וואטסאפ במקום סקרולבר
צ'יפי הסינון בתיבת הצ'אט העסקי ובתיבת לקוחות הקצה כבר לא מציגים סרגל גלילה (שנראה לא נקי).
פרטים נוספים ↓הסתר ↑
צ'יפי הסינון בתיבת הצ'אט העסקי ובתיבת לקוחות הקצה כבר לא מציגים סרגל גלילה (שנראה לא נקי). במקום זאת ניתן לגרור את הצ'יפים הצידה עם העכבר — בדיוק כמו ברשימות הצ'יפים של וואטסאפ — וגם גלגלת העכבר וגלילת מגע/טראקפד פועלות. גרירה מעבר לסף קטן 'בולעת' את הקליק שאחריה כך שגרירה לעולם לא תחליף בטעות פילטר, והמנגנון מופעל רק כשהרצועה באמת גולשת. המימוש רוכז ב-hook משותף (useDragScroll) המשמש את שני הדפים, ומודע לכיוון RTL. בנוסף, הצ'יפ הפעיל קיבל מראה 'מתואר' (outlined) — בורדר דק בצבע המותג, רקע שקוף קלות, וטקסט באותו צבע המותג — במקום רקע מלא וטקסט לבן.
v01.107.002שיפורצ'אט לקוחות קצה / Inboxסינון תיבת לקוחות הקצה (/messages/inbox) עבר לצ'יפים בעיצוב אחיד
טאבי הסינון בתיבת צ'אט לקוחות הקצה (הכל / הסלמות / טופל) הוחלפו בצ'יפים באותו עיצוב בדיוק כמו בתיבת הצ'אט העסקי: אייקון ייעודי לכל צ'יפ (תיבה / משולש אזהרה / וי), בורדר עדין, ריווח קומפקטי, ושורה אחת שנגללת אופקית (כולל …
פרטים נוספים ↓הסתר ↑
טאבי הסינון בתיבת צ'אט לקוחות הקצה (הכל / הסלמות / טופל) הוחלפו בצ'יפים באותו עיצוב בדיוק כמו בתיבת הצ'אט העסקי: אייקון ייעודי לכל צ'יפ (תיבה / משולש אזהרה / וי), בורדר עדין, ריווח קומפקטי, ושורה אחת שנגללת אופקית (כולל גלגלת עכבר, מודע ל-RTL) אם תצר מדי. ספירת ההסלמות החיה (שיחות הדורשות טיפול אנושי) נשמרה — מוצגת כ-badge אדום בתוך צ'יפ 'הסלמות'. ההתנהגות הפונקציונלית של הסינון לא השתנתה.
v01.107.001תיקוןמשלוחים / טופס יצירהתיקון: לא ניתן היה לגלול ברשימות הנפתחות (מועדפות / עיר / לקוח) בטופס יצירת משלוח
ברשימות הנפתחות בטופס יצירת המשלוח — כתובות מועדפות (מוצא / יעד), בחירת עיר ובחירת לקוח — לא ניתן היה לגלול כשהרשימה ארוכה מ-300px.
פרטים נוספים ↓הסתר ↑
ברשימות הנפתחות בטופס יצירת המשלוח — כתובות מועדפות (מוצא / יעד), בחירת עיר ובחירת לקוח — לא ניתן היה לגלול כשהרשימה ארוכה מ-300px. הסיבה: הטופס נפתח בתוך Dialog שנועל גלילה (react-remove-scroll), ותוכן הבורר מרונדר ב-portal מחוץ ל-Dialog, כך שאירועי הגלילה שלו נחסמו. הבעיה צפה כעת כשהמועדפות יכולות להגיע ל-100 כתובות. הפתרון: הבוררים הללו הוגדרו כ-modal, כך שהם פותחים נעילת גלילה משלהם שמתירה גלילה בתוך הרשימה — בלי לפגוע במיקום. בורר הרחוב (typeahead) הושאר ללא שינוי בכוונה כדי לא לשבור את ההקלדה בשדה.
v01.107.000חדשטריגרים / שיווק / CRMminorטריגרים: טאב 'שיווק' חדש — אוטומציות לליד חדש (התראה לקבוצה + חימום ליד)
בדף הטריגרים (/messages/triggers) נוסף טאב שלישי — 'שיווק' — לצד 'הפצה' ו'תפעול'.
פרטים נוספים ↓הסתר ↑
בדף הטריגרים (/messages/triggers) נוסף טאב שלישי — 'שיווק' — לצד 'הפצה' ו'תפעול'. הטאב מאפשר להגדיר שני סוגי אוטומציות שמופעלות אוטומטית בכל פעם שנכנס ליד חדש (מטופס באתר, מהוובהוק של מקורות חיצוניים, או מיצירה ידנית): (1) 'התראה לקבוצה' — שולח הודעה לקבוצת WhatsApp של הצוות (כמו טריגר טעויות המיון) ו/או לרשימת מספרי טלפון, עם פרטי הליד ולינק לכרטיס; (2) 'חימום ליד' — שולח הודעת פתיחה/חימום ישירות לטלפון של הליד החדש, כשהנמען נקבע אוטומטית מהליד. שניהם בנויים על תשתית ה-SystemTriggerSetting הקיימת עם תבניות הודעה הניתנות לעריכה ומשתנים ייעודיים. שליחה היא fire-and-forget — כשל בשליחה לעולם לא חוסם את קליטת הליד. ייבוא לידים מרובה (CSV) אינו מפעיל את הטריגרים כדי למנוע הצפת הודעות.
- טאב 'שיווק' חדש בדף הטריגרים עם סינון וחיפוש משלו, נפרד מטריגרי התפעול
- טריגר 'ליד חדש - התראה לקבוצה' (NEW_LEAD_GROUP): שליחה לקבוצת WhatsApp ו/או רשימת טלפונים בכל ליד חדש
- טריגר 'ליד חדש - חימום ליד' (NEW_LEAD_WELCOME): הודעת חימום אוטומטית לטלפון של הליד
- משתני תבנית ייעודיים ללידים: שם, עסק, טלפון, עיר, מקור, סוכן, לינק לכרטיס ועוד
- מופעל מכל מסלולי קליטת הליד (טופס ציבורי, webhook, יצירה ידנית); ייבוא CSV מוחרג למניעת הצפה
v01.106.000חדשAI / WhatsApp inboxminorאווטר פר-הודעה בצ'אט הקבוצתי + זיהוי צוות מול קבלן לפי טלפון
בצ'אטים קבוצתיים ב-/messages/business-inbox כל הודעה נכנסת מציגה כעת את האווטר של המשתתף שכתב אותה, ובריחוף עליו tooltip עם השם המלא.
פרטים נוספים ↓הסתר ↑
בצ'אטים קבוצתיים ב-/messages/business-inbox כל הודעה נכנסת מציגה כעת את האווטר של המשתתף שכתב אותה, ובריחוף עליו tooltip עם השם המלא. מתחת למכסה נוסף יסוד משותף: כל הודעה נכנסת בקבוצה שומרת מעתה את שולח-המשנה (טלפון + שם ה-WhatsApp), וטלפון השולח מוצלב מול משתמשי הטננט — כך שהמערכת מבחינה בין נציג/ת צוות שלנו (שם אמיתי + אווטר + טבעת ירוקה + תווית 'צוות') לבין משתתף חיצוני (הקבלן — ראשי תיבות בצבע). היסוד הזה גם מאפשר שיוך מדויק לבוט: כל שורה במטח שמגיע למנוע התרחישים מתויגת כעת לפי מחברה — שורות שכתב נציג שלנו מקבלות תיוג [צוות: שם], בעוד הצד השני (הקבלן/הלקוח) נשאר ללא תיוג. ה-classifier הונחה ששורות [צוות] אינן טריגר (הטריגר חייב להגיע מהצד השני), כך שתשובה של נציגת השירות בקבוצת השליח כבר לא נחשבת בטעות כדיווח של הקבלן. בנוסף, הזנת תור הלמידה (כשל/הצלחת מסירה) רצה רק על הודעות הקבלן עצמן, לא על תשובות צוות. התיוג משפיע על הסיווג בלבד ואינו דולף להודעות יוצאות (הן מבוססות תבניות). טכנית: נוספו עמודות sender_phone/sender_name ל-ai_chat_messages, פונקציית resolveGroupSender עם cache פר-טננט, לכידת senderData.sender ב-Green API webhook, וזיהוי השולח ב-API טעינת השיחה ובמנוע התרחישים. כמו כן נוסף, בסגנון WhatsApp, שם השולח המלא מעל ההודעה הראשונה בכל רצף הודעות רצופות מאותו משתתף (בנוסף לאווטר) — בצבע ייחודי פר-משתתף התואם לצבע האווטר, ואנשי צוות מקבלים צבע ירוק ותווית 'צוות'.
- אווטר של שולח ההודעה ליד כל הודעה נכנסת בקבוצה, עם tooltip של השם המלא
- זיהוי צוות מול קבלן לפי הצלבת טלפון השולח מול משתמשי המערכת (טבעת ירוקה + 'צוות' לצוות)
- הבוט מבחין כעת בין תשובת נציג שלנו (תיוג [צוות]) לדיווח של הקבלן/לקוח — תיקון שיוך שגוי בקבוצות
v01.105.001שיפורסריקת מחסן / מוביילסריקת מצלמה: מנוע מהיר יותר (BarcodeDetector) במקום ZXing
סורק המצלמה בדף /shipments/scan עבר מ-@zxing/browser למנוע BarcodeDetector המובנה של הדפדפן — מהיר משמעותית (פי 2–5 ב-Android/Chrome, מואץ-חומרה), עם נפילה אוטומטית ל-zxing-wasm (ponyfill) ב-iOS Safari ו-Firefox, שגם הוא…
פרטים נוספים ↓הסתר ↑
סורק המצלמה בדף /shipments/scan עבר מ-@zxing/browser למנוע BarcodeDetector המובנה של הדפדפן — מהיר משמעותית (פי 2–5 ב-Android/Chrome, מואץ-חומרה), עם נפילה אוטומטית ל-zxing-wasm (ponyfill) ב-iOS Safari ו-Firefox, שגם הוא מהיר ומדויק יותר מהמנוע הישן. המצלמה נפתחת כעת ב-Full-HD לשיפור קריאה ממרחק. הוסרו התלויות @zxing/browser ו-@zxing/library. כל ה-UI (overlay, מסך שגיאות הרשאה, reticle, פנס) נשאר ללא שינוי. נוסף מסמך STRICH-UPGRADE.md עם מסלול שדרוג מוכן-להפעלה למנוע פרימיום (STRICH) מאחורי NEXT_PUBLIC_STRICH_LICENSE_KEY.
v01.105.000חדשוואטסאפ / בוט לקוחות קצהminorבוט וואטסאפ ללקוחות קצה: מענה אמפתי לפניות "המשלוח מתעכב" עם הסבר התחייבות ההפצה
בוט הוואטסאפ ללקוחות הקצה יודע כעת להתמודד עם פניות מסוג "כבר עברו כמה ימים והמשלוח לא הגיע" / "אמרתם X ימים".
פרטים נוספים ↓הסתר ↑
בוט הוואטסאפ ללקוחות הקצה יודע כעת להתמודד עם פניות מסוג "כבר עברו כמה ימים והמשלוח לא הגיע" / "אמרתם X ימים". הבוט עונה בטון מנומס, מתנצל ומכיל — מכיר בתסכול ובאי-הנוחות — אך מסביר בעדינות שלא חרגנו מההתחייבות שלנו (כשזה המצב): ימי ההפצה נספרים מהרגע שהמשלוח נקלט אצלנו ונאסף מבית העסק, ולא ממועד ההזמנה; לפני כן המשלוח היה ברשות בית העסק השולח. הבוט מסביר שיום האיסוף עצמו לא נספר, שישי/שבת וחגים לא נספרים, ושיעד רגיל = עד 3 ימי הפצה ויעד חריג = עד 7. החישוב משתמש בדיוק באותו מנגנון של דף המעקב הציבורי (delayClockStartedAt + countBusinessDays + סיווג יעד פר-tenant), כך שהמספרים זהים בכל המערכת. אם המשלוח כן חרג מההתחייבות — הבוט מתנצל בכנות ומסלים לנציג אנושי במקום לטעון שהכול תקין.
- מודול חדש computeShipmentSlaContext שמחשב את הקשר ההתחייבות (ימי הפצה שחלפו, האם בתוך החלון, תאריך הגעה צפוי) — אותו חישוב כמו דף המעקב הציבורי
- ה-classifier מקבל את הקשר ה-SLA וסעיף ייעודי בפרומפט: טון מתנצל ומכיל, אך הסבר ברור שספירת ימי ההפצה מתחילה מקליטת המשלוח אצלנו ולא ממועד ההזמנה
- תוך-התחייבות → תשובה מרגיעה (קטגוריה 2) עם תאריך הגעה צפוי כגבול עליון; חריגה מההתחייבות → הסלמה לנציג (קטגוריה 4) עם הערה פנימית
v01.104.001שיפורסריקת מחסן / מוביילסריקה: כפתור 'פתח סורק' מוסתר במחשב
בדף סריקת הברקודים (/shipments/scan) כפתור 'פתח סורק' (מצלמה) מוצג כעת רק במכשירי מגע (טלפון/טאבלט), שם המצלמה היא אמצעי הסריקה הרלוונטי.
פרטים נוספים ↓הסתר ↑
בדף סריקת הברקודים (/shipments/scan) כפתור 'פתח סורק' (מצלמה) מוצג כעת רק במכשירי מגע (טלפון/טאבלט), שם המצלמה היא אמצעי הסריקה הרלוונטי. במחשב — שבו סורקים עם קורא USB דרך שדה הקלט הנסתר — הכפתור מוסתר לחלוטין. הזיהוי מבוסס על יכולת מגע אמיתית (pointer: coarse) ולא על רוחב מסך, כך שטאבלט מציג את הכפתור ואילו חלון דפדפן צר במחשב לא. קלט ה-USB וההקלדה הידנית נותרו ללא שינוי. נוסף hook משותף useHasTouch.
v01.104.000שיפורתשתית / כלל-מערכתminorסבב שדרוגים בטוחים — ביצועים, אבטחה, אמינות, בדיקות ו-DevX
סבב רחב של שיפורים שעברו ביקורת רב-שלבית ואומתו מול הקוד, כולם תוכננו כאדיטיביים / משמרי-התנהגות כדי לא לשבור דבר קיים.
פרטים נוספים ↓הסתר ↑
סבב רחב של שיפורים שעברו ביקורת רב-שלבית ואומתו מול הקוד, כולם תוכננו כאדיטיביים / משמרי-התנהגות כדי לא לשבור דבר קיים. ביצועים ומסד נתונים: אינדקסים על shipment.meta_message_id ו-cod_tracking.settlement_id לנתיבים חמים (webhook של WhatsApp ותצוגת הזדכות מובייל), caching (withCache, TTL 5 דק') לאנליטיקות נהגים ולקוחות, וטעינה עצלה (next/dynamic) של דיאלוגי ה-PDF הכבדים (react-pdf/pdfjs) כך שלא נטענים ב-bundle הראשוני. אבטחה: דגלי opt-in ל-fail-closed ב-webhooks של ש.נ.ל ו-Green API (ברירת מחדל = התנהגות נוכחית, כי קיים tenant פעיל ללא secret) + אזהרה ברורה כשרצים ללא אימות; הגבלת קצב לפי IP לנתיבים ציבוריים (תוויות שיתוף, מעקב משלוח); ואימות קיום tenant ב-webhook של לידים. אמינות וניטור: timeout (AbortSignal) לכל לקוחות האינטגרציה (konimbo/shopify/woocommerce/cashcow/nopcommerce) כדי שספק תקוע לא יתקע קרון; instrumentation.ts שמנתב שגיאות שרת לא-תפוסות ללוגר; endpoint בריאות GET /api/health; ו-removeConsole בפרודקשן שומר כעת על console.error/warn (היו נמחקים — שחזור נראות בלוגי Vercel). בדיקות: 47 בדיקות חדשות שנועלות הצפנה (round-trip), חילוץ סכום COD (מגן מול באג ה-×100), אימות webhook fail-closed, ונרמול טלפון. נגישות: המרת tooltips של title= ל-Tooltip משותף + aria-label. איכות קוד: איחוד נרמול הטלפון הישראלי למודול קנוני אחד (lib/phone) — 5 מימושים כפולים אוחדו ובאג פיצול-שיחות בדף השליח תוקן. API: חילוץ helpers משותפים (parseJsonBody/parsePagination) ל-v1, זהים-בהתנהגות. DevX: תיקון באג ב-hook של ה-changelog, כלל lint שאוכף את קונבנציית ה-DatePicker, ו-typecheck ל-packages/shared ב-CI. ניקוי: הסרת תלויות מתות (react-datepicker, jsbarcode, zod ב-shared) וקבצי artifact מיותרים מ-git.
- DB: אינדקסים על shipment.meta_message_id ו-cod_tracking.settlement_id (מסירים full table scan מנתיבי webhook/מובייל) + caching לאנליטיקות
- ביצועים: lazy-load של דיאלוגי ה-PDF הכבדים מחוץ ל-bundle הראשוני
- לוגים: removeConsole שומר console.error/warn בפרודקשן — שחזור נראות בלוגי Vercel; + instrumentation.ts ו-/api/health
- אבטחה: opt-in fail-closed ל-webhooks (SNL/Green API), rate-limit לפי IP לנתיבים ציבוריים, אימות tenant ב-webhook לידים
- אמינות: timeout לכל לקוחות האינטגרציה החיצוניים
- בדיקות: 47 חדשות (הצפנה, סכום COD נגד ×100, webhook fail-closed, נרמול טלפון); DevX: תיקון hook, כלל lint ל-DatePicker, typecheck ל-shared ב-CI
- נגישות: המרת 8 tooltips של title= ל-Tooltip משותף + aria-label (כפתורים disabled נעטפים ב-span כדי שה-tooltip ימשיך לעבוד)
- איכות קוד: איחוד נרמול הטלפון הישראלי למודול קנוני אחד (lib/phone) — 5 מימושים כפולים אוחדו, ובאג פיצול-שיחות תוקן
- API: חילוץ helpers משותפים parseJsonBody + parsePagination ל-v1 (זהים-בהתנהגות, ללא שינוי חוזה)
v01.103.000חדשפורטל לקוחות / כתובות מועדפותminorכתובות מועדפות בפורטל — הפרדה בין כתובות מוצא לכתובות יעד
פנקס הכתובות המועדפות של הלקוח בפורטל פוצל לשתי רשימות: כתובות יעד (נמען) וכתובות מוצא (שולח).
פרטים נוספים ↓הסתר ↑
פנקס הכתובות המועדפות של הלקוח בפורטל פוצל לשתי רשימות: כתובות יעד (נמען) וכתובות מוצא (שולח). עמוד ההגדרות 'כתובות מועדפות' מציג כעת שתי לשוניות (יעד / מוצא) עם מונה לכל אחת, וכל כתובת ניתנת לסימון ככתובת יעד, כתובת מוצא, או שתיהן — כתובת המסומנת כשתיהן מופיעה בשתי הרשימות עם תווית 'מוצא + יעד'. בטופס יצירת המשלוח, בורר המועדפות של כתובת המוצא מציג רק כתובות מוצא, ובורר כתובת היעד מציג רק כתובות יעד. גם בייבוא משלוחים, בורר כתובת האיסוף (מוצא) מציג רק כתובות מוצא. כפתור 'שמור במועדפות' על כתובת נמען בפרטי משלוח שומר כברירת מחדל ככתובת יעד. כל הכתובות הקיימות סומנו אוטומטית ככתובות יעד (כפי שנשמרו עד היום), כך שאין רגרסיה. מגבלת הכתובות הוגדלה ל-100 (מאגר משותף לשתי הרשימות).
- שתי רשימות נפרדות — כתובות יעד וכתובות מוצא — בלשוניות בעמוד ההגדרות
- כל כתובת ניתנת לסימון כמוצא / יעד / שתיהן (תווית 'מוצא + יעד')
- בוררי המועדפות בטופס המשלוח ובייבוא מסננים לפי הצד הרלוונטי
- כל הכתובות הקיימות סומנו ככתובות יעד — ללא רגרסיה; המגבלה הוגדלה ל-100
v01.102.000חדשצ'אט לקוחות עסקיים / Inboxminorצ'יפים לסינון תיבת הצ'אט העסקי לפי שיוך — הכל / לקוחות / שליחים / קבלנים / לא משוייכים
מעל רשימת השיחות בצ'אט הלקוחות העסקיים נוספה שורת צ'יפים לסינון מהיר לפי בעל השיחה: הכל, לקוחות, שליחים, קבלנים, ולא משוייכים.
פרטים נוספים ↓הסתר ↑
מעל רשימת השיחות בצ'אט הלקוחות העסקיים נוספה שורת צ'יפים לסינון מהיר לפי בעל השיחה: הכל, לקוחות, שליחים, קבלנים, ולא משוייכים. הסינון מתבצע בצד השרת כך שהוא משתלב נכון עם הדפדוף (keyset pagination) והחיפוש — בחירת קטגוריה טוענת מחדש את העמוד הראשון מסונן. הסיווג נגזר מהשיוך הנוכחי: שיחת לקוח מזוהה (customerId) או קבוצת לקוח → 'לקוחות'; קבוצה המקושרת לשליח שכיר או קבלן פנימי (EMPLOYEE/CONTRACTOR) → 'שליחים'; קבוצה המקושרת לקבלן חיצוני (EXTERNAL) → 'קבלנים'; שיחה ללא לקוח וללא קישור לנהג/לקוח → 'לא משוייכים'. הקישור לקבוצה נקרא מהרשומה העדכנית של הנהג/לקוח (whatsappGroupChatId), כך שגם קבוצות שקושרו אחרי יצירת השיחה מסווגות נכון.
- 5 צ'יפים: הכל / לקוחות / שליחים / קבלנים / לא משוייכים, מעל רשימת השיחות
- סינון server-side שמשתלב עם דפדוף וחיפוש — ללא שבירת ה-pagination
- 'שליחים' = שכיר + קבלן פנימי; 'קבלנים' = קבלן חיצוני בלבד (לפי driverType)
- לכל צ'יפ אייקון ייעודי (לקוח / משאית / לחיצת יד / עיגול מקווקו), בורדר עדין ועיצוב קומפקטי
- שורה אחת שנגללת אופקית; גלגלת העכבר מניעה אותה הצידה (תיקון ל-Windows + עכבר, מודע ל-RTL)
v01.101.001שיפורפורטל לקוחות / משלוחים / SNLאפשרויות עמוד גדולות יותר ברשימת המשלוחים בפורטל הלקוח — עד 1000 בעמוד
לבקשת לקוחות פורטל, אפשרויות מספר המשלוחים לעמוד ברשימת 'המשלוחים שלי' עודכנו מ-25/50/100/250 ל-50/200/500/1000.
פרטים נוספים ↓הסתר ↑
לבקשת לקוחות פורטל, אפשרויות מספר המשלוחים לעמוד ברשימת 'המשלוחים שלי' עודכנו מ-25/50/100/250 ל-50/200/500/1000. במקביל הוּרם תקרת ה-limit ב-API של פורטל המשלוחים מ-500 ל-1000 כדי שהאפשרות החדשה תחזיר באמת 1000 שורות ולא תיחתך בשקט. תוקנו שתי בעיות ב-webhook SNL: (1) webhook ישן/רטרואקטיבי יכול היה להוריד סטטוס COMPLETED חזרה ל-IN_INVENTORY — נוספה הגנת regression שמונעת כל שינוי סטטוס לאחור ממצב סופי; (2) Lionwheel המשיך לדרוס את targetPartnerTaskId של SNL — תוקן באג ב-data-service שבו delete extraFields.targetPartnerTaskId לא השפיע כי shipmentData כבר הועתק, הgarden עבר ל-safeShipmentData.
v01.101.000חדשAPI ציבורי / זמני הפצה / פורטל לקוחותminorנקודת קצה ציבורית להטמעה באתר הלקוח — בדיקת ישוב: יעד רגיל (עד 3 ימים) או חריג (עד 7)
נוספה נקודת קצה ציבורית שלקוחות עסקיים יכולים להטמיע ישירות באתר שלהם: כשהקונה בוחר ישוב, האתר מקבל בזמן אמת האם זה יעד רגיל (עד 3 ימי הפצה) או יעד חריג (עד 7 ימי הפצה).
פרטים נוספים ↓הסתר ↑
נוספה נקודת קצה ציבורית שלקוחות עסקיים יכולים להטמיע ישירות באתר שלהם: כשהקונה בוחר ישוב, האתר מקבל בזמן אמת האם זה יעד רגיל (עד 3 ימי הפצה) או יעד חריג (עד 7 ימי הפצה). GET /api/public/delivery-type/{slug}?city=... — הטננט מזוהה דרך ה-slug הציבורי שלו ב-URL (ללא מפתח סודי, בטוח לקריאה מהדפדפן), עם CORS פתוח ו-rate-limit לפי IP. הבדיקה משתמשת באותו מנגנון פתרון שמות שמשמש את כל המערכת: התאמה מדויקת → נרמול (ניקוד/גרשיים/מקפים) → שמות חלופיים (aliases) → הסרת קידומת/סיומת של 'מושב'/'קיבוץ'/'כפר'/'ישוב'/'יישוב' — כך שגם שם שנכתב בשונה מהרישום הקנוני נתפס נכון. הסיווג רגיל/חריג מחושב לפי ההגדרה הפר-טננט (override של הטננט, אחרת ברירת המחדל הגלובלית), כך שכל חברה רואה את הסיווג שלה. הבקשה מוגבלת בכוונה לישוב בודד בלבד: פרמטר city יחיד, ותווי הפרדה (פסיק/נקודה-פסיק/קו-אנכי/שורה חדשה) נדחים — אין אפשרות לבדיקה מרובת ישובים בבקשה אחת. תשובה כוללת את השם הקנוני שזוהה, deliveryType, isExceptional, maxDeliveryDays ותווית מוכנה לתצוגה. בנוסף, נוסף בפורטל הלקוחות (עמוד 'זמני הפצה') כפתור 'הטמעה באתר שלכם' שפותח מודל עם מדריך קצר ושלוש דרכי הטמעה — פרומט ל-AI (מומלץ), HTML מוכן, ו-cURL — בכל אחת מהן כתובת ה-API כבר כוללת את ה-slug של הלקוח, כך שמה שמעתיקים עובד מיד. ה-slug נשלף בצד שרת מהטננט המשויך ללקוח המחובר.
- GET /api/public/delivery-type/{slug}?city=... — ללא מפתח, CORS פתוח, מזוהה לפי slug של הטננט
- שימוש באותו פותר שמות (aliases + נרמול + הסרת מושב/קיבוץ/כפר/ישוב) כמו כל המערכת
- ישוב בודד בלבד — פרמטר city יחיד, תווי הפרדה נדחים; סיווג רגיל/חריג פר-טננט
- כפתור 'הטמעה באתר שלכם' בפורטל (עמוד 'זמני הפצה') — מודל עם מדריך, פרומט ל-AI, HTML ו-cURL; ה-slug של הלקוח כבר מוטמע בכתובת
v01.100.001תיקוןפורטל לקוחות / משלוחיםביטול משלוח בפורטל הלקוח מסמן 'בוטל' במקום למחוק — המשלוח נשאר גלוי למעקב
תוקן באג שבו לקוח שביטל משלוח בפורטל ('ביטול משלוח') איבד אותו לחלוטין: הפעולה ביצעה מחיקה רכה (deletedAt), וה-soft-delete extension הסתיר את השורה מכל הקריאות — כולל מרשימת המשלוחים של הלקוח עצמו, כך שלא ניתן היה לעקוב …
פרטים נוספים ↓הסתר ↑
תוקן באג שבו לקוח שביטל משלוח בפורטל ('ביטול משלוח') איבד אותו לחלוטין: הפעולה ביצעה מחיקה רכה (deletedAt), וה-soft-delete extension הסתיר את השורה מכל הקריאות — כולל מרשימת המשלוחים של הלקוח עצמו, כך שלא ניתן היה לעקוב אחר משלוחים מבוטלים. כעת ביטול משלוח משנה את הסטטוס ל'בוטל' (CANCELED) ב-additionalData במקום למחוק, כך שהמשלוח נשאר גלוי בפורטל עם תווית 'בוטל' וניתן לסינון. בנוסף, הביטול נדחף גם ל-Lionwheel (status=4) באמצעות הקרדנציאלים הפר-tenant, כדי שנהג לא יאסוף אותו בטעות ושסנכרון עתידי לא יחזיר את הסטטוס. כשל בדחיפה ל-Lionwheel אינו חוסם את הביטול המקומי (נרשם lionwheel_sync=false).
v01.100.000חדשAI / WhatsApp inboxminorמנוע תרחישים לבוט הקבוצות — opt-in פר-חברה, גישור בין שליח ללקוח, ושתיקה כברירת מחדל
הבוט שמגיב בקבוצות WhatsApp (דרך Green API) הוחלף ממענה חופשי לכל הודעה למנוע תרחישים מסודר.
פרטים נוספים ↓הסתר ↑
הבוט שמגיב בקבוצות WhatsApp (דרך Green API) הוחלף ממענה חופשי לכל הודעה למנוע תרחישים מסודר. כל חברה בוחרת אילו תרחישים להפעיל ועורכת את נוסח התגובות בעמוד הגדרות חדש ('תרחישי הבוט'). העיקרון: בכל מקרה שאינו תואם בוודאות תרחיש מופעל — הבוט שותק (precision over recall). חל רק על צ'אטים משויכים (קבוצת לקוח עסקי או קבוצת שליח/קבלן); קבוצה לא-משויכת או הודעה מעורפלת → שתיקה. שני תרחישים בגרסה זו, שניהם גישור בין שתי קבוצות: (1) 'אין מענה' — שליח/קבלן מדווח בקבוצתו שאין מענה מהנמען → הבוט מודיע ללקוח העסקי ושואל על טלפון/הנחיה חלופית → אם הלקוח עונה, הבוט מעביר את העדכון לשליח (ומעדכן טלפון במשלוח אם נמסר מספר). (2) 'צפי מסירה' — לקוח שואל מתי משלוח יימסר → אם משובץ לשליח, הבוט שואל את השליח ומעביר את התשובה (אם תקינה) חזרה ללקוח. מתחת למכסה: registry מרכזי של תרחישים, מסווג Haiku זול שמכיר רק תרחישים מופעלים (ברירת מחדל 'none'→שתיקה), טבלת ai_scenario_relay שזוכרת לאן להעביר חזרה כל תשובה, debounce של 8 שניות שמאחד 'מטח' הודעות לתגובה אחת, ורישום החלטות ל-ai_learning_queue כבסיס ללולאת למידה עתידית. תגובות קצרות בלבד וניתנות לעריכה פר-חברה. צ'אט 1:1 פרטי עם לקוח לא הושפע. הערה: בקבוצות שליח מקושרות, הסוכן ההיברידי החופשי הקודם (הצעות פעולה/תובנות אוטומטיות) הוחלף במנוע התרחישים — יכולות אלו יחזרו בהמשך כתרחישים נוספים. בנוסף שופר פאנל 'פעולות שהבוט מציע' בראש שיחת הקבוצה (משמש להצעות מהמערכת ההיברידית הקודמת): כל הצעה מציגה כעת את מועד יצירתה (שעון + זמן יחסי) ואת הודעת המקור מהקבוצה שהובילה אליה ('מההודעה בקבוצה'), כדי שיהיה ברור מי אמר מה; תוכן ההודעה היוצאת תויג במפורש 'ההודעה שתישלח' ומופיע רק כשהפעולה אכן שולחת הודעה (notifyBusinessCustomer/sendWhatsappToDriver) — עדכון כתובת/סטטוס אינו שולח דבר לאיש אלא מעדכן במערכת; ונמנעות כפילויות: הצעת PENDING קיימת לאותה פעולה+משלוח באותה שיחה מתעדכנת בנימוק/בנתונים האחרונים במקום ליצור כרטיס שני. בנוסף, בדף המשלוח (כרטיסיית 'זיהוי וסטטוס') הערות ה-AI כבר לא משתלטות על הכרטיס ומעוותות את הפריסה: מוצגות 3 ההערות האחרונות בלבד, והשאר מאחורי כפתור 'הצג עוד N הערות' שפותח מודל עם כל ההערות (תוכן מלא + מועד כל אחת). זה ממתן את ההצפה שנוצרה כשהסוכן הקודם הוסיף הערה כמעט-זהה על כל הודעה בקבוצה.
- עמוד הגדרות 'תרחישי הבוט' — הפעלה/כיבוי פר-תרחיש + עריכת נוסח התגובות
- שני תרחישי גישור: 'אין מענה' (שליח↔לקוח) ו'צפי מסירה' (לקוח↔שליח)
- בכל ספק הבוט שותק; חל רק על צ'אטים משויכים; debounce שמאחד מטח הודעות לתגובה אחת
v01.99.000חדשפורטל לקוחותminorסימן 'הודפסה מדבקה' על משלוחים בפורטל הלקוחות
לקוחות הפורטל מקבלים כעת סימן ויזואלי על כל משלוח שעבורו כבר הופקה מדבקה — אייקון מדפסת ירוק לצד הברקוד בטבלת המשלוחים (עם tooltip של מועד ההדפסה), ובדף המשלוח המלא + בדראוור התצוגה המהירה מוצג תג 'מדבקה הודפסה' לצד שורת…
פרטים נוספים ↓הסתר ↑
לקוחות הפורטל מקבלים כעת סימן ויזואלי על כל משלוח שעבורו כבר הופקה מדבקה — אייקון מדפסת ירוק לצד הברקוד בטבלת המשלוחים (עם tooltip של מועד ההדפסה), ובדף המשלוח המלא + בדראוור התצוגה המהירה מוצג תג 'מדבקה הודפסה' לצד שורת מועד ההדפסה. נוספה עמודת מעקב label_printed_at למשלוח שמתעדכנת בפעם הראשונה שמופק PDF מדבקה (sticky — לא מתאפסת), מכל מסלולי ההדפסה: הדפסת לקוח מהפורטל (בודד ובאצווה), הדפסת צוות מממשק הניהול, וקישורי שיתוף מדבקה (בודד/מרובה) — כך שהסימן משקף שמדבקה הודפסה ללא תלות במי שהדפיס. העדכון מתבצע כ-fire-and-forget שלא חוסם או מכשיל את הפקת המדבקה.
- אייקון מדפסת לצד הברקוד בטבלת המשלוחים בפורטל, עם מועד ההדפסה ב-tooltip
- תג 'מדבקה הודפסה' + מועד בדף המשלוח ובדראוור התצוגה המהירה
- מעקב הדפסה נרשם מכל מסלולי המדבקה — פורטל (בודד/אצווה), ניהול, וקישורי שיתוף
v01.98.001שיפורתיבת הודעותעיצוב בועות הצ'אט ב-/messages/inbox בסגנון צ'אט הלקוחות העסקיים
בועות הצ'אט ב-/messages/inbox עוצבו מחדש כך שיתאימו לעיצוב הנעים יותר של צ'אט הלקוחות העסקיים (business-inbox) — רק הצ'אט עצמו (בועות, טקסט, מדיה), בלי שינוי בסיידבר/כותרת.
פרטים נוספים ↓הסתר ↑
בועות הצ'אט ב-/messages/inbox עוצבו מחדש כך שיתאימו לעיצוב הנעים יותר של צ'אט הלקוחות העסקיים (business-inbox) — רק הצ'אט עצמו (בועות, טקסט, מדיה), בלי שינוי בסיידבר/כותרת. בועה יוצאת עברה מרקע כהה-מלא (zinc-900/לבן עם טקסט הפוך) ל-tint רך בצבע המותג (bg-primary/10 עם border עדין) וטקסט בצבע foreground רגיל; הבועה הנכנסת רוככה ל-bg-muted/50; ריווח פנימי נדיב יותר (px-3.5 py-2.5) עם leading-relaxed; ושעת/סטטוס ההודעה (✓/✓✓, אדום לכשל, כחול לנקרא) הותאמו לבועה הבהירה. גם המדיה שודרגה בהתאם ל-business-inbox: תמונות וסרטונים גדולים יותר בדסקטופ (md:max-h-96), מדבקות קומפקטיות (max-h-32), ומסמכים מוצגים כקופסה ממוסגרת ('קובץ מצורף') במקום קישור טקסט. אטריביוציית מקור ההודעה (אייקון נציג/AI/מערכת/טריגר) ושאר הפיצ'רים נשמרו במלואם. בנוסף תוקן באג קאש ב-/messages/inbox: בטאבים 'הסלמות' ו'טופל' לא הוצגו צ'אטים במובייל בעוד שבדסקטופ עבדו — iOS Safari קישש תגובת API ריקה לכל URL של ?filter= (מהזמן שבו הטבלה המנורמלת whatsapp_conversations הוזרמה והעמודות status/last_message_dir היו עדיין ריקות) והגיש אותה גם אחרי רענון. נוסף Cache-Control: no-store לתגובות (conversations + counts) ו-cache:"no-store" לבקשות הלקוח, כך שרשימת השיחות והספירות נטענות תמיד טריות בכל מכשיר. המשך כוונון לזהות מלאה: כיוון הצד והצבעים זהים כעת ל-business-inbox — הודעה יוצאת ממוקמת באותו צד (flex-row-reverse) כמו ב-business-inbox, וצבע הבועה היוצאת נקבע לפי מחבר ההודעה: נציג אנושי = ירוק (emerald), AI/אוטומטי/מערכת = סגול המותג, נכנסת = muted — בדיוק כמו ההבחנה assistant/staff שם. גם פינת ה'זנב' של הבועה הותאמה (rounded-tr/tl). בנוסף תוקן סקלטון טעינה ייעודי לצ׳אט בניווט: בניווט client-side בין /messages/inbox ל-/messages/business-inbox הוצג סקלטון של טבלה (יורש מ-messages/loading.tsx האב) במקום סקלטון צ׳אט. נוסף loading.tsx ייעודי לכל אחד משני דפי הצ׳אט שמרנדר שלד בצורת צ׳אט (סיידבר רשימה + אזור שיחה ריק), כך שגם בניווט וגם בריענון מופיע אותו סקלטון. לבסוף אוחד צבע הבועה היוצאת: בועה יוצאת ירוקה אחידה בשני הצ׳אטים (emerald) ללא קשר למחבר — ב-/messages/inbox היה סגול (לפי מקור: טריגר/AI) וב-business-inbox assistant היה סגול; כעת כל הודעה יוצאת ירוקה בשני הדפים, נכנסת נשארת אפורה. ההבחנה בין מחברים נשמרת דרך אייקון המקור/תפקיד ולא דרך צבע הבועה. כמו כן סיידבר רשימת הצ׳אטים הורחב בדסקטופ ל-380px (md:w-95) בשני הצ׳אטים — היה צר (320px) ביחס לכמות הנתונים בכל כרטיס, על חשבון פאנל הצ׳אט שהיה רחב מדי (הוא flex-1 ומתכווץ אוטומטית). רלוונטי לדסקטופ בלבד; במובייל הרשימה נשארת ברוחב מלא. השלד בטעינה עודכן בהתאם. כמו כן אוחד שלד הטעינה הפנימי של business-inbox לסקלטון היפה: בריענון הדף הופיע שלד פנימי מכוער (בלוקים h-16 ריקים) בעוד בניווט הופיע השלד היפה של loading.tsx (אווטאר + שתי שורות טקסט). חולץ רכיב ConversationCardsSkeleton משותף שמשמש גם את loading.tsx וגם את מצב הטעינה הפנימי, כך שהסקלטון זהה ויפה בשני המצבים. בנוסף תוקנו שלושה דברים בצ׳אט לקוחות הקצה (/messages/inbox): (1) באג תאריך — ה-classifier לא קיבל את התאריך הנוכחי ולכן ניחש שגוי איזה יום זה 'יום ראשון' (חשב 23.6 במקום 21.6); כעת מוזרק להקשר התאריך הנוכחי באזור-זמן ישראל + טבלת הימים הקרובים עם שמות הימים בעברית, וה-AI ממפה מתוכה ולא מנחש. (2) הסלמה — בקשת הגבלת מועד מסירה (אל תספקו לפני/רק אחרי/החזיקו עד תאריך) מסווגת כעת כקטגוריה 4 (הסלמה לאדם) במקום שה-AI יאשר ויבטיח תיאום מול השליח שאיננו יכולים לאכוף; הובהר ההבדל בין 'אהיה בבית ביום X' (זמינות, cat 2) להגבלה קשיחה (cat 4). (3) עיצוב — הרווח האנכי בין בועות ההודעה הוגדל מ-2px ל-8px (mb-0.5→mb-2). כמו כן תוקן ששיתוף מיקום מלקוח הקצה מוצג כקישור למפה (/messages/inbox): הודעת location של WhatsApp הוצגה כ'קובץ מצורף' ריק ולא ניתן לפתיחה, כי ה-webhook שמר רק type=location בלי הקואורדינטות. כעת ה-webhook לוכד את ה-lat/long ושומר קישור Google Maps (וגם שם/כתובת אם סופקו), וה-UI מציג את ה-location כקישור-מפה לחיץ עם אייקון נעץ (גם בתצוגה המקדימה ברשימה — 'מיקום'). חל על הודעות מיקום חדשות; הודעה ישנה שכבר נשמרה בלי קואורדינטות תוצג כ'מיקום שהתקבל' ללא קישור. לבסוף הוקשח ה-Service Worker כדי שעדכוני קוד יגיעו אוטומטית: התברר שמשתמש במובייל המשיך לראות רשימת 'הסלמות'/'טופל' ריקה גם אחרי מחיקת קאש (בעוד שבלשונית פרטית עבד) — כי ה-SW המשיך להגיש את ה-build הישן. ה-SW כבר לא מקאש את ה-HTML של מסלולי האפליקציה (shell מאומת שמצביע על chunks של ה-build הנוכחי) — ניווטים תמיד נטענים מהרשת, ורק אם אין רשת מוצג דף offline; וגרסת הקאש הועלתה (v1→v2) כך שה-SW החדש מוחק את הקאש הישן בהפעלה. כך deploy חדש מגיע למכשירים בלי צורך בניקוי ידני. בנוסף תוקנו הסלמות-שווא של שיחות שכבר טופלו (/messages/inbox): שיחה שטופלה הופיעה שוב תחת 'הסלמות' רק כי הלקוח שלח 'תודה' סוגרת — כל הודעה נכנסת איפסה את הסטטוס ל-pending. שני תיקונים: (1) תשובת נציג אנושי מהתיבה מסמנת את השיחה כ-handled (קודם תשובה ידנית לא שינתה סטטוס); (2) אישור-סיום קצר בלבד ('תודה'/'תודה רבה'/👍/אוקיי וכו', allowlist מחמיר) שמגיע לשיחה שכבר handled — משאיר אותה handled במקום להחזיר ל-pending. כך 'תודה' סוגרת לא פותחת מחדש שיחה פתורה. תוקן באג שורש משמעותי: הודעות המשך מלקוח הקצה לא עובדו ע״י ה-AI (ולכן עדכוני כתובת/קומה/טלפון שהלקוח כתב לא הוחלו, ושיחות נתקעו ב'הסלמות'). ב-scheduleDeliverySession ה-fallback לאיתור המשלוח לפי טלפון השתמש בהתאמה מדויקת (phone = '972…'/'0…'), אבל טלפוני משלוחים מאוחסנים בפורמטים שונים (למשל '+972…') — כך שאחרי שתשובה (נציג/AI) שינתה את ה-wamid האחרון, הודעת המשך של הלקוח לא מצאה משלוח ולא נוצר סשן AI כלל. ה-fallback עבר להתאמה מנורמלת (regexp_replace ספרות-בלבד, עם MATERIALIZED CTE שמכריח את אינדקס הטלפון) — כך כל הודעת המשך מאתרת את המשלוח ומקבלת סשן AI. כמו כן (בדומה ל-location) שיתוף איש קשר (contacts) מוצג כשם+טלפון: כשלקוח משתף כרטיס איש-קשר ב-WhatsApp (type=contacts), קודם הוצג 'קובץ מצורף' ריק כי ה-webhook שמר רק את הסוג; כעת ה-webhook לוכד את השם והטלפון(ים) של אנשי הקשר כתוכן ההודעה, וה-UI מציג אותם בתיבת איש-קשר עם אייקון (ובתצוגה המקדימה — 'איש קשר'). שימושי במיוחד כשהלקוח משתף את המספר הנכון של הנמען. כמו כן תוקנה הגלילה בצ׳אט הלקוחות העסקיים (business-inbox): גלילה אחורה לקריאת הודעות קודמות 'נחטפה' חזרה לתחתית כל כמה שניות, כי ה-polling (כל 8 שניות) החליף את מערך ההודעות והפעיל מחדש את הגלילה-לתחתית בכל מחזור — גם כשלא נכנסה הודעה חדשה. כעת הגלילה האוטומטית קורית רק בפתיחת שיחה (קפיצה מיידית לתחתית) או כשבאמת נכנסה הודעה חדשה ורק אם המשתמש כבר קרוב לתחתית; אם גלל למעלה לקרוא היסטוריה — לא נוגעים במיקום הגלילה שלו. בנוסף רוסן הבוט שמגיב בקבוצות לקוח ב-business-inbox: הוא נהג להגיב לכל הודעה בודדת (גם תמונות, 'וזה הבית', 'אפשר להשאיר בתיבה?'), לערום כמה תגובות ארוכות באותה שיחה (race condition כשכמה הודעות הגיעו באותה שנייה — כל webhook הריץ סוכן בנפרד), לכתוב 'מאמרים' ארוכים, ואפילו לפרש מספר טלפון/@אזכור כברקוד. כעת: (1) debounce של 8 שניות לקבוצות שמאחד 'מטח' הודעות לתגובה אחת במקום אחת-לכל-הודעה; (2) prompt ייעודי לקבוצות עם כללי התערבות מפורשים — הבוט עונה אך ורק על שאלת סטטוס עם ברקוד ברור, בקשה מפורשת לפתוח בירור עם ברקוד, או שאלת כמות/סיכום מהלקוח; (3) בכל מקרה אחר או בכל ספק — הבוט פולט סנטינל [NO_REPLY] ופשוט שותק (precision over recall); (4) תגובות קצרות בלבד (שורה-שתיים, סטייל וואטסאפ, ללא כותרות/רשימות, maxTokens מוקטן); (5) pre-filter שמדלג על מטח לא-מהותי (תמונה בלבד / אישור קצר). צ'אט 1:1 פרטי עם לקוח לא הושפע — ההתנהגות הקיימת נשמרה. נוסף טאב 'בעיבוד' חדש בתיבת ההודעות (/messages/inbox): עד כה כל הודעה נכנסת נכנסה ל'הסלמות' מיד — גם בזמן שהבוט עומד לטפל בה (debounce 60ש + cron), מה שגרם לנציגים לקפוץ ולענות בזמן שהבוט גם עונה. כעת כשנוצר סשן AI להודעה, השיחה מסומנת 'processing' ומופיעה תחת טאב 'בעיבוד' (אייקון שעון-חול + מונה כחול) ולא תחת 'הסלמות'; כשהבוט מסיים — היא עוברת אוטומטית ל'טופל' או ל'הסלמות' (אם הוסלמה). הודעה שאין לה סשן (אין משלוח/סוג לא נתמך) ממשיכה ישר ל'הסלמות'. נוסף גם חיווי 'בעיבוד' על כרטיס השיחה, וה-counts/סינון עודכנו כך ש-processing לא נספר כהסלמה. נוסף טשטוש עדין מתחת לאינפוט בצ׳אט (/messages/inbox): שכבת backdrop-blur עדינה (2px) ברצועה התחתונה של אזור ההודעות, עם מסכת gradient שחזקה ביותר מתחת לתיבת הקלט ומתעמעמת כלפי מעלה — כך שההודעות מקבלות מראה 'זכוכית מט' רק כשהן חולפות מתחת לאינפוט, בלי לטשטש את שאר השיחה. תוקן באג: תמלול הודעה קולית נשמר ומוצג — ה-webhook תמלל אודיו אך שמר את ההודעה לפני התמלול (body=null), כך שהתמלול אבד: הנציג לא ראה טקסט וה-classifier לא עיבד את תוכן ההקלטה. כעת התמלול נשמר על שורת ההודעה אחרי שהוא מוכן, מוצג בבועה מתחת לנגן עם תווית 'תמלול אוטומטי', והבוט מעבד את מה שהלקוח אמר בפועל (במקום שההקלטה תיתקע ללא מענה). שופרו מהירות ויעילות העדכונים (polling מהיר ויעיל ב-/messages/inbox): במקום לשלוף את רשימת השיחות + counts + הודעות הצ׳אט הכבדות כל 15ש׳, הלקוח בודק כעת כל 5ש׳ דגל-שינוי זעיר (endpoint חדש /version שמחזיר רק MAX(updated_at) של השיחות + MAX(occurred_at) של הצ׳אט הפתוח — שאילתה אחת מאונדקסת; נוסף אינדקס (tenant_id, updated_at desc)), ושולף את הנתונים הכבדים רק כשהדגל זז. בנוסף ה-polling נעצר לחלוטין כשהטאב מוסתר ומתרענן מיידית כשחוזרים אליו, עם רשת-ביטחון של רענון מלא כל 60ש׳. התוצאה: latency ~5ש׳ במקום 15ש׳, רענון מיידי בפוקוס, וכמעט אפס עומס DB כשאין שינוי/כשהטאב ברקע.
v01.98.000חדשAI / WhatsApp inboxminorהבוט הופך לאיש צוות תפעולי בקבוצות WhatsApp
הבוט שצורף לקבוצות שליחים ולקוחות מתקדם מ"קורא" ל"איש צוות תפעולי".
פרטים נוספים ↓הסתר ↑
הבוט שצורף לקבוצות שליחים ולקוחות מתקדם מ"קורא" ל"איש צוות תפעולי". שלב 1 (קישור קבוצות): ניתן לקשר כל שיחת קבוצה ב-business-inbox לשליח או ללקוח בלחיצה (כפתור 'קשר קבוצה' בכותרת השיחה, עם בורר חיפוש) — כך הבוט יודע לשייך את הקבוצה ולפעול עליה (קובע whatsappGroupChatId). קבוצה לא-מקושרת מסומנת, ומקושרת מציגה צ'יפ עם השם. שלב 2 (הבנת מדיה): הקלטות קוליות נכנסות מתומללות אוטומטית (Whisper) כך שהבוט מבין ומסווג אותן, ותמונה/אודיו עם טקסט (כיתוב או תמלול) מפעילים את הסוכן במקום להידלג. (דורש מפתח תמלול OpenAI מוגדר לחברה.) שלב 3 (סוכן תפעול היברידי — backend): בקבוצת שליח מקושרת, הודעה תפעולית מפעילה כעת סוכן AI אוטונומי שמבצע לבד פעולות בטוחות/הפיכות (הוספת הערת AI, תזמון MARK_FAILED מותנה, תובנות) — ומציע פעולות רגישות (שינוי סטטוס, עדכון כתובת, שליחה ללקוח/שליח) דרך proposeAction לאישור צוות, במקום לבצען לבד. ההצעות נשמרות בטבלת ai_action_proposals. קבוצה לא-מקושרת ממשיכה במסלול ה-Haiku הפשוט. (פאנל אישור ההצעות בשלב הבא.) שלב 3b (אישור הצוות): נוסף פאנל 'פעולות שהבוט מציע' בראש שיחת הקבוצה ב-business-inbox — כל הצעה מוצגת עם הפעולה, הנימוק, ותוכן ההודעה (אם רלוונטי) + כפתורי 'אשר ובצע' / 'דחה'. אישור מבצע את הכלי בפועל (מיוחס למאשר ב-audit) ומסמן את ההצעה כבוצעה; דחייה סוגרת אותה. שלב 4 (תיקון אישורי-מסירה): אותר שורש 69 אישורים שפגו ו-0 שאושרו — ה-Meta webhook התאים תשובות אישור-מסירה רק להודעת טקסט, אבל לקוחות עונים לתבנית גם דרך כפתור Quick Reply / interactive / ריאקציית 👍. כעת כל סוגי התגובות נתפסים (חולץ הטקסט מ-button/interactive ונוסף לטיפוס ולגוף ההודעה), כך שאישור מסירה אמיתי מסמן את המשלוח כנמסר. (תגובות שליח קוליות כבר מטופלות דרך התמלול בשלב 2.) שלב 5 (לולאת למידה לקבוצות): סוכן התפעול של הקבוצות משתמש כעת בתובנות המאושרות (הזרקה להנחיות) וכותב תובנות חדשות בעצמו (addEntityInsight) על דפוסי שליחים/לקוחות. בנוסף, תוצאות מסירה משליחים בקבוצות (כשל/הצלחה) מוזנות לתור הלמידה (ai_learning_queue) כך ש-ai-learning-digest כורה דפוסים פר-שליח אוטומטית — משלים את לולאת הלמידה (שתשתיתה נבנתה במקביל) גם לצד השליחים. כמו כן הוסרה תווית הטריאז' 'ממתין' מכרטיסי השיחות ב-business-inbox (יותרה כמיותרת); אייקון ה-✨ למענה-AI נשאר. בנוסף נוסף כפתור 'סנכרן קבוצות' בכותרת ה-business-inbox: שיחת קבוצה הופיעה ברשימה רק אחרי שעברה בה הודעה, אז קבוצות שקטות מאז שהעוזר צורף לא הופיעו. הכפתור מושך מ-Green API (getContacts) את כל הקבוצות שהעוזר חבר בהן ויוצר רשומת שיחה לכל קבוצה שעדיין לא ברשימה — כך ניתן לראות ולקשר את כולן מיד.
- קישור קבוצה → שליח/לקוח מתוך כותרת השיחה ב-business-inbox
- בורר חיפוש לשליחים/לקוחות + סימון קבוצות שכבר מקושרות
- שלבים הבאים: הבנת אודיו/תמונה, סוכן תפעול היברידי, ולולאת למידה
v01.97.001תיקוןמשלוחים / שיבוץ שליחיםשיבוץ שליח מסירה מעדכן אוטומטית את סטטוס המשלוח לפי סוג השליח
תוקן באג שבו סטטוס המשלוח לא השתנה בשיבוץ קבלן הפצה חיצוני שאינו ש.נ.ל (למשל 'זיפ') — המשלוח נשאר תקוע על 'נקלט במחסן'.
פרטים נוספים ↓הסתר ↑
תוקן באג שבו סטטוס המשלוח לא השתנה בשיבוץ קבלן הפצה חיצוני שאינו ש.נ.ל (למשל 'זיפ') — המשלוח נשאר תקוע על 'נקלט במחסן'. עד כה שינוי הסטטוס ל'בהעברה' התרחש רק כתופעת לוואי של שידור ל-SNL (לפי providerCode) ולא לפי סוג השליח, כך ש-ש.נ.ל עבד במקרה בלבד. כעת, בשיבוץ שליח לביקור פתוח — בין אם דרך סריקת הוצאת מחסן, שיבוץ ידני מדף המשלוח, או שיבוץ מרובה מהמפה — הסטטוס מתעדכן לפי הכלל: ביקור מסירה לקבלן חיצוני → 'בהעברה'; ביקור מסירה לקבלן פנימי/שכיר → 'יצא להפצה'; ביקור איסוף → 'נאסף'. ספקי שידור (SNL / שותף פנימי) ממשיכים לקבל את הסטטוס דרך מסלול השידור שלהם. השינוי כולל דחיפת סטטוס ל-Lionwheel, חותמות sticky (inTransferAt/outInventoryAt), זיכוי עמלה, אירוע realtime ויומן ביקורת — בהתאם לדפוס עדכון הסטטוס הקיים.
v01.97.000חדשAI / תובנותminorתובנות ה-AI נסגרות למעגל למידה — העוזר קורא ומשתמש בהן בפועל
עד היום העוזר הדיגיטלי רק כתב תובנות (על שליח/לקוח/משלוח/חברה) — אך מעולם לא קרא אותן חזרה, כך שהידע הצטבר בעמוד 'תובנות AI' בלי להשפיע על התשובות.
פרטים נוספים ↓הסתר ↑
עד היום העוזר הדיגיטלי רק כתב תובנות (על שליח/לקוח/משלוח/חברה) — אך מעולם לא קרא אותן חזרה, כך שהידע הצטבר בעמוד 'תובנות AI' בלי להשפיע על התשובות. כעת המעגל נסגר: כשהעוזר שולף פרטי משלוח או לקוח, הוא מקבל גם את התובנות המאושרות הרלוונטיות לאותו משלוח, ללקוח שלו ולשליח המשויך (שליפה ממוקדת — רק מה שנוגע לשאלה, לא כל בסיס הידע), וידע כללי מאושר ברמת החברה מוזרק לראש ההנחיות. רק תובנות בסטטוס 'אושר' משפיעות — ניחושים שטרם אושרו לעולם לא. במקביל הועלה רף האיכות של הלמידה האוטומטית (דורשת דפוס חוזר וניתן להכללה, לא הערה נקודתית), נוסף מנגנון מניעת כפילויות בכל מסלולי הכתיבה, וניהול התובנות בעמוד ההגדרות שודרג: אישור/דחייה זמינים תמיד (לא רק על 'ממתין'), עריכת תוכן תובנה, סימון תובנות ישנות (120+ יום) עם אפשרות 'אמת מחדש', אינדיקציית מקור (נוצר תוך כדי שיחה), ותג מונה של תובנות הממתינות לאישור בתפריט הצד.
- העוזר קורא תובנות מאושרות רלוונטיות בעת שליפת משלוח/לקוח — שליפה ממוקדת לפי ההקשר
- ידע מאושר ברמת החברה מוזרק להנחיות; רק סטטוס 'אושר' משפיע על תשובות
- רף איכות גבוה יותר ללמידה האוטומטית + מניעת כפילויות בכל מסלולי הכתיבה
- ניהול משופר: אישור/דחייה תמידיים, עריכת תובנה, סימון 'ישנה' + 'אמת מחדש', אינדיקציית מקור
- תג מונה לתובנות ממתינות לאישור בתפריט הצד
- כלי getDriverDetails חדש לעוזר — פרטי שליח מלאים (סוג, סטטוס, עומס משימות, מסירות/כשלים ב-30 יום) + תובנות מאושרות עליו
v01.96.001שיפוראינטגרציות / e-shopתמיכה ב-Static Egress IP לאינטגרציות הדורשות IP allow-list (אישופ/e-shop)
נוספה שכבת proxy יוצא בעל כתובת IP קבועה: כשמוגדר משתנה הסביבה EGRESS_PROXY_URL, קריאות ה-API היוצאות לאישופ (e-shop) מנותבות דרך proxy בעל IP יציב — כדי לעמוד בדרישת אישופ ל-allow-list של הכתובת שממנה אנו פונים ל-API של…
פרטים נוספים ↓הסתר ↑
נוספה שכבת proxy יוצא בעל כתובת IP קבועה: כשמוגדר משתנה הסביבה EGRESS_PROXY_URL, קריאות ה-API היוצאות לאישופ (e-shop) מנותבות דרך proxy בעל IP יציב — כדי לעמוד בדרישת אישופ ל-allow-list של הכתובת שממנה אנו פונים ל-API שלהם. נדרש מכיוון ש-Shipnest רץ על Vercel serverless, שיוצא מ-pool של כתובות IP מתחלפות וללא IP יציב יחיד. ה-helper החדש (lib/egress-proxy.ts, fetchViaEgress) טוען את undici ProxyAgent רק כשה-proxy מוגדר בפועל, כך שברירת המחדל (ללא proxy) זהה ל-fetch רגיל — no-op מוחלט. השינוי מבודד לאדפטר של e-shop בלבד ואינו נוגע בשאר המערכת, וניתן לשימוש חוזר לכל שותף עתידי שדורש IP allow-listing.
v01.96.000חדשמשלוחים / תמחורminorעלות איסוף ועלות מסירה לכל משלוח — לצד המחיר ללקוח והרווח
לכל משלוח, בנוסף למחיר שהלקוח משלם, נשמרות כעת שתי עלויות ביצוע: עלות האיסוף ועלות המסירה — כל אחת מתומחרת לפי מחירון השליח שביצע בפועל את המשימה (תעריף בסיס, אזורי תעריף, חבילות נוספות וכללי תמחור — בדיוק כמו בתחשיב התש…
פרטים נוספים ↓הסתר ↑
לכל משלוח, בנוסף למחיר שהלקוח משלם, נשמרות כעת שתי עלויות ביצוע: עלות האיסוף ועלות המסירה — כל אחת מתומחרת לפי מחירון השליח שביצע בפועל את המשימה (תעריף בסיס, אזורי תעריף, חבילות נוספות וכללי תמחור — בדיוק כמו בתחשיב התשלום החודשי לשליח). התמחור נשמר ברגע השלמת המשימה: עלות האיסוף מופיעה כשהאיסוף הושלם, ועלות המסירה כשהמסירה הושלמה. כששליח אוסף 2+ משלוחים מאותו לקוח באותו יום מוחל תעריף האיסוף העסקי המקובץ (זהה לתשלום החודשי). העלות מחושבת מחדש אוטומטית אם השליח של המשימה משתנה או אם עורכים את מחירון השליח בדיעבד. בכרטיסיית 'תמחור' של המשלוח נוסף בלוק 'עלויות ורווח' (עלות איסוף, עלות מסירה, סה"כ עלות ורווח = מחיר − עלויות), ובטבלת המשלוחים נוספו עמודות אופציונליות 'עלות איסוף', 'עלות מסירה' ו'רווח'. הנתונים הפיננסיים האלה גלויים רק למשתמשים עם הרשאת 'צפייה בפיננסים' (finance:view).
- עלות איסוף ועלות מסירה פר-משלוח, לפי מחירון השליח שביצע
- תמחור נשמר ברגע השלמת המשימה, ומחושב מחדש אוטומטית בשינוי שליח/מחירון
- תעריף איסוף עסקי מקובץ — זהה לתחשיב התשלום החודשי
- בלוק 'עלויות ורווח' בכרטיסיית התמחור + עמודות אופציונליות בטבלה
- מוגבל להרשאת finance:view
v01.95.004שיפורפורטל לקוחות / תוויותבורר התוויות בפורטל הלקוחות מציג צ'יפים במקום רשימת שורות
בעקבות בקשת לקוחות — רשימת התוויות שנפתחת בעת שיוך תווית למשלוח בפורטל הוסבה מרשימת שורות עם תיבות-סימון לתצוגת צ'יפים צבעוניים בפריסת flex-wrap.
פרטים נוספים ↓הסתר ↑
בעקבות בקשת לקוחות — רשימת התוויות שנפתחת בעת שיוך תווית למשלוח בפורטל הוסבה מרשימת שורות עם תיבות-סימון לתצוגת צ'יפים צבעוניים בפריסת flex-wrap. שני בוררי התוויות הומרו: (1) TagAssignPopover — המשמש בדף המשלוח, בעמודת הטבלה, בבחירה מרובה ובסינון; (2) TagMultiSelect — בורר התוויות בטופס הקמת משלוח חדש (וגם באשף הייבוא). תווית משויכת מוצגת כצ'יפ מלא בצבע התווית עם וי, תווית לא-משויכת כצ'יפ עם מתאר ונקודת צבע, ובבחירה מרובה מצב חלקי מוצג כצ'יפ דהוי עם מינוס. גם פעולת 'צור תווית' הפכה לצ'יפ מקווקו בתוך אותה פריסה. עקבי עם הצ'יפים הקיימים (TagBadge) המציגים תוויות נבחרות.
v01.95.003תיקוןטריגרים / הודעותהודעת 'נוצר משלוח' תישלח ללקוח פעם אחת בלבד — לתמיד
חיזוק לתיקון הקודם של הטריגרים, בעקבות לקוחה שקיבלה 7 הודעות על אותו משלוח (רובן הודעת 'נוצר משלוח').
פרטים נוספים ↓הסתר ↑
חיזוק לתיקון הקודם של הטריגרים, בעקבות לקוחה שקיבלה 7 הודעות על אותו משלוח (רובן הודעת 'נוצר משלוח'). התיקון הקודם סגר את הנתיב שבו סנכרון ידני (manual-sync) שידר מחדש את task_created דרך הברירת-מחדל מהסטטוס. אבל נשארו שתי פרצות: (1) הענף המפורש — webhook מליונוויל שה-trigger_field שלו עצמו הוא 'task_created' — שנשלח ללא תנאי, כך ש-re-delivery של אותו webhook יכול לברך את הלקוח מחדש; (2) backlog של הודעות מתוזמנות שכבר נוצרו לפני התיקון (כשזמן השליחה של הטריגר אינו 'מיידי'), שממשיכות להישלח ע"י ה-cron. התיקון הופך את task_created ל-idempotent — נשלח פעם אחת בלבד לכל משלוח: לפני השליחה נבדק ב-auditLog אם כבר נרשמה שליחת task_created מוצלחת למשלוח הזה, ואם כן — מדלגים (כולל מניעת יצירת הודעה מתוזמנת חדשה). בנוסף נוסף שומר ב-cron של ההודעות המתוזמנות: הודעת task_created שכבר נשלחה למשלוח נזרקת מהתור במקום להישלח, כך שגם backlog היסטורי לא יבריך מחדש לקוח. הבהרה חשובה: ההודעות שהלקוחה כבר קיבלה נשלחו לפני שהתיקון עלה לפרודקשן; מרגע ה-deploy כל משלוח יקבל הודעת יצירה אחת בדיוק. בנוסף חוזק תיעוד הודעות הטריגר: ה-catch על recordOutboundMessage במסלול הטריגר הפך מבליעה שקטה ללוג שגיאה (logError), כך שגם כשל-תיעוד נדיר של הודעה שכבר שודרה משאיר עקבה בלוגים ולא נעלם בשקט — אבטחה לכך שהודעות שטריגרים שולחים ששודרו לא ייעלמו מהתיבה.
v01.95.002שיפורנהגים / לקוחותבורר קבוצות WhatsApp בדף הנהג ובדף הלקוח
בכרטיסיית 'פרטים אישיים' בדף הנהג, עריכת קבוצת ה-WhatsApp (Green API) הומרה מ-popover עם הקלדה ידנית בלבד לדיאלוג קטן שכולל את בורר הקבוצות המשותף (כפתור 'בחר קבוצה').
פרטים נוספים ↓הסתר ↑
בכרטיסיית 'פרטים אישיים' בדף הנהג, עריכת קבוצת ה-WhatsApp (Green API) הומרה מ-popover עם הקלדה ידנית בלבד לדיאלוג קטן שכולל את בורר הקבוצות המשותף (כפתור 'בחר קבוצה'). כך אפשר לבחור קבוצה מתוך רשימת הקבוצות של ה-instance במקום להדביק מזהה @g.us ידנית — הבחירה ממלאת אוטומטית גם את מזהה הקבוצה וגם את שמה, וההקלדה הידנית נשארה כגיבוי. עד כה הבורר היה זמין רק בטופס עריכת הנהג; כעת הוא נגיש גם ישירות מדף הנהג. (ההמרה מ-popover לדיאלוג גם נמנעת מ-popover מקונן בתוך popover, שהיה סוגר את החלונית בעת בחירה.) כעת נוסף אותו עורך מהיר (דיאלוג עם בורר הקבוצות) גם לדף הלקוח העסקי — בכרטיס 'קבוצת וואטסאפ' חדש בעמודה הימנית, שמציג את הקבוצה המשויכת ומאפשר בחירה/עדכון ישירות מדף הלקוח (PATCH ל-API של הלקוח, עם הוספת השדות whatsappGroupChatId/whatsappGroupName ל-handler). כך השיוך לקבוצה זמין מדף הישות עצמה — גם לנהגים וגם ללקוחות — ולא רק מטופסי העריכה.
v01.95.001תיקוןטעויות מיוןטעות מיון נשלחה שוב (התראה + וואטסאפ) בכל סנכרון אחרי שטופלה
המשך לתיקון הקודם של הודעות כפולות בסנכרון — הפעם בנתיב טעויות המיון, שהוא נפרד לחלוטין מטריגרי המשלוח ולא הושפע מהתיקון הקודם.
פרטים נוספים ↓הסתר ↑
המשך לתיקון הקודם של הודעות כפולות בסנכרון — הפעם בנתיב טעויות המיון, שהוא נפרד לחלוטין מטריגרי המשלוח ולא הושפע מהתיקון הקודם. כשמשלוח נמצא בטעות מיון (נהג שובץ לאזור שאינו מורשה לו), הזיהוי (detectSortingErrors) רץ על *כל* סנכרון של המשלוח — סנכרון ידני מרענון/ליקוט/פערי-חלוקה/יעד-שבת וכו', וגם כל webhook — בלי בדיקה אם משהו השתנה. ההגנה היחידה מפני כפילות הייתה שאילתת dedup שחיפשה טעות קיימת בסטטוס PENDING בלבד. לכן ברגע שמישהו סימן את הטעות כ'טופלה'/'התעלם' — או שהיא נסגרה אוטומטית ע"י אישור 👍 בקבוצת הוואטסאפ (תרחיש נפוץ במיוחד) — בלי להוסיף בפועל את האזור לנהג, הסנכרון הבא של אותו משלוח (שעדיין עם אותו נהג לא-מורשה ועדיין בסטטוס איסוף/בדרך/החזרה) יצר רשומת טעות חדשה, שידר שוב את ההתראה בזמן-אמת (מודאל אדום + סירנה לכל הדשבורדים הפתוחים של הטננט) ושלח שוב הודעת וואטסאפ לכל הנמענים המוגדרים בטריגר (כל הטלפונים + הקבוצה) — וחוזר חלילה בכל סנכרון. התיקון: שאילתת ה-dedup ב-checkDriverZoneAllowance כבר לא מסננת לפי PENDING — היא מתאימה לטעות קיימת לאותו (ברקוד, נהג, סוג) בכל סטטוס. כך טעות מיון נרשמת ומתריעה פעם אחת בלבד, ולא משוחזרת אחרי שטופלה. שיבוץ נהג *אחר* לאותו משלוח עדיין נחשב טעות חדשה ומתריע כרגיל, כך שלא מפספסים טעויות אמיתיות. בונוס: גם נמנעת הצטברות בלתי-מוגבלת של רשומות טעות מאותו סוג. הובהר אגב שהמילה 'נכשל' שתיארה את התקלה אינה מדויקת — סטטוס FAILED כלל לא נכנס לזיהוי טעויות מיון; המשלוחים שמייצרים את ההתראות הם אלה שבסטטוס בדרך/איסוף/החזרה עם נהג לא-מורשה.
v01.95.000חדשControl Plane / AIminorמעקב שיחות AI בלוח הבקרה
נוסף עמוד ניטור מלא בלוח הבקרה של הפלטפורמה (Control Plane → מעקב AI) המציג את כל שיחות ה-AI בכל הטננטים.
פרטים נוספים ↓הסתר ↑
נוסף עמוד ניטור מלא בלוח הבקרה של הפלטפורמה (Control Plane → מעקב AI) המציג את כל שיחות ה-AI בכל הטננטים. ממשק split-pane: רשימת שיחות עם חיפוש, סינון לפי טננט והקשר, ו-pagination — ולחיצה על שיחה פותחת את כל ההודעות עם עיבוד Markdown, הצגת קריאות לכלים, ומספר טוקנים. מטרה: לאפשר זיהוי כשלים, שיפור prompt וניטור שוטף של התנהגות הדגם. בנוסף תוקן רינדור של הודעות מערכת (system) של WhatsApp ב-/messages/inbox: אירוע מערכת (למשל שינוי מספר טלפון של לקוח) הוצג כקישור-מסמך עם הטקסט הגולמי system שנפתח לטאב ריק (href=#). כעת ה-webhook לוכד את טקסט ההודעה (system.body) ושומר אותו כתוכן ללא mediaType, וה-UI מציג אירועי מערכת כהערה עדינה במקום קישור שבור. כמו כן תווית קובץ מצורף שונתה מהצגת ה-mediaType הגולמי לטקסט קבוע, וקישור נפתח רק כשקיים URL אמיתי. (תוקנה שגיאת type-check שהפילה את ה-build: ב-page של מעקב ה-AI, הביטוי שקבע אם להציג קריאות לכלים פתח ב-msg.toolCalls שטיפוסו unknown, כך ש-hasTools קיבל טיפוס unknown ולא boolean — מה שגרם ל-child מסוג unknown שאינו ReactNode חוקי. הוחלף ל-Array.isArray(...) מוביל שמצמצם ל-boolean.)
- רשימת שיחות עם חיפוש, סינון טננט/הקשר ו-pagination (50 שיחות לעמוד)
- צפייה בשיחה מלאה — בועות הודעה, Markdown, timestamps ומספר טוקנים
- תגיות הקשר צבעוניות: עוזר פנימי (סגול), פורטל לקוחות (כחול), לקוח קצה (ירוק)
- API endpoints מאובטחים עם requirePlatformRole
v01.94.003תיקוןפערי הפצהפערי הפצה — משלוחים הוצגו כ'ללא אזור' למרות שיש להם אזור
בדף 'פערי הפצה', פילטר 'אזור הפצה' הציג משלוחים תחת 'ללא אזור' למרות שבדף המשלוח עצמו מופיע אזור הפצה תקין.
פרטים נוספים ↓הסתר ↑
בדף 'פערי הפצה', פילטר 'אזור הפצה' הציג משלוחים תחת 'ללא אזור' למרות שבדף המשלוח עצמו מופיע אזור הפצה תקין. הסיבה: הרשימה גזרה את האזור מ-regionStr של ביקור ה-DELIVERY (שלעיתים ריק), בעוד דף המשלוח מציג את shipment.regionCode (האזור שנשמר מליונוויל בעת יצירת המשלוח). כעת הרשימה נופלת ל-shipment.region_code כש-regionStr של הביקור ריק (COALESCE), כך שהאזור עקבי בין הרשימה לדף המשלוח. התיקון חל על כל הטאבים ועל פילטר האזור. בנוסף, בעקבות בקשת לקוחות, נוספה עמודה 'ימים אצל השליח' לטבלת פערי ההפצה (וגם בייצוא ל-Excel) — מציגה כמה ימי עסקים המשלוח כבר משובץ לשליח הנוכחי, עם תאריך השיבוץ ב-tooltip. לשם כך נוסף שדה driver_assigned_at על ביקור (Visit) שנקבע בעת שיבוץ שליח (סנכרון מליונוויל, שיבוץ אצווה, או עריכת ביקור) ומתאפס בהחלפת שליח או בביטול שיבוץ — כך הספירה מתחילה מחדש בכל החלפה. הספירה לכל טאב מתבססת על הביקור הרלוונטי (מסירה / איסוף / החזרה). אין חותמת שיבוץ היסטורית מליונוויל, ולכן למשלוחים שכבר היו משובצים בעת ההטמעה הוצב תאריך השיבוץ לרגע ההטמעה (כולם מתחילים מ-0 היום) — הספירה מדויקת לחלוטין מכאן והלאה.
v01.94.002תיקוןטריגרים / הודעותטריגר 'נוצר משלוח' נשלח שוב ושוב למשלוחים ישנים
לקוחות קיבלו את הודעת הוואטסאפ של טריגר 'נוצר משלוח' (task_created) פעמים נוספות, ימים אחרי שהמשלוח באמת נוצר.
פרטים נוספים ↓הסתר ↑
לקוחות קיבלו את הודעת הוואטסאפ של טריגר 'נוצר משלוח' (task_created) פעמים נוספות, ימים אחרי שהמשלוח באמת נוצר. הסיבה: מנגנון בחירת סוג הטריגר היה נופל לברירת מחדל שגוזרת את הטריגר מ*הסטטוס הנוכחי* של המשלוח כש-trigger_field אינו אחד מ-5 הסוגים המוכרים — וסטטוס UNASSIGNED ממופה ל-task_created. כל מסלולי הסנכרון (sync-shipment-status שמופעל מרענון דף המשלוחים, ליקוט, פערי חלוקה, יעד שבת/אקספרס, התראות אזור ושגיאות מיון; וכן manual-sync) שולחים trigger_field='manual-sync'/'manual_sync' שאינו מוכר — כך שכל סנכרון של משלוח שעדיין UNASSIGNED (טרם שובץ לשליח) גזר מחדש task_created ושלח שוב את הודעת ה'נוצר משלוח'. אותה תקלה חלה גם בוובהוק על עדכון לא-קשור (עריכת כתובת, הגדרת גוביינא) למשלוח UNASSIGNED. התיקון: הברירת-מחדל מהסטטוס שוב לא יכולה לסנתז task_created — הודעת 'נוצר משלוח' נשלחת רק כשהשורה באמת נוצרת באירוע הזה (shipmentAction='created') או דרך אות ה-task_created המפורש של ליונוויל; שאר טריגרי מחזור-החיים (שובץ/נמסר/בוטל/נכשל) נשלחים מהברירת-מחדל רק כששינוי הסטטוס באמת התרחש באירוע (previousStatus≠statusMapped), כך שסנכרון חוזר של משלוח ללא שינוי לא שולח כלום. הודעת ה-welcome הלגיטימית שמגיעה מהוובהוק המפורש של ליונוויל לא הושפעה.
v01.94.001תיקוןCRM / הצעות מחירשיפורי הצגה בהצעת מחיר — יישור הנוסח המשפטי ותעריפי 0 ₪
בקובץ ה-PDF של הצעת מחיר שנשלח ללקוח, הערת הנוסח המשפטי שבתחתית ('הצעה זו אינה מהווה התחייבות חוזית מצד הצדדים.
פרטים נוספים ↓הסתר ↑
בקובץ ה-PDF של הצעת מחיר שנשלח ללקוח, הערת הנוסח המשפטי שבתחתית ('הצעה זו אינה מהווה התחייבות חוזית מצד הצדדים. הסכם מחייב טעון חתימה נפרדת.') נדחקה לקצה הימני של העמוד ואף חרגה מעט מעבר לשוליים, במקום לקבל הזחה מימין כמו שאר התוכן בהצעה. הסיבה: react-pdf ממקם את שורת ה-RTL הזו בהיסט קבוע (~13pt) ימינה מקצה הריפוד של הקופסה — תקלת מיקום שורה ב-RTL שגדלה עם אורך השורה ומופיעה רק במסמך המלא. התיקון מוסיף ריפוד-ימני נוסף לכרטיס ההערה (כך שהריפוד השמאלי תואם את שאר הסקציות וההיסט נבלע בריפוד הימני), והטקסט יושב כעת מוזח מהקצה הימני באותו מרווח כמו 'פרטי הלקוח', 'תעריפי בסיס' ו'הערות'. אומת אמפירית ע״י רינדור ה-PDF ומדידת מיקום הגליפים — קצה השורה הימני עבר מ-3pt מעבר לשוליים ל-16pt בתוך השוליים, בהתאמה לשאר הבלוקים, גם בהצעה מינימלית וגם בהצעה מקסימלית בת שני עמודים. בנוסף: תעריפי בסיס שהוגדרו על 0 ₪ ('שירות ללא תשלום') מוצגים כעת ברשימת התעריפים עם '0.00 ₪' במקום להיעלם — גם ב-PDF, גם בתצוגה הציבורית (/q/[token]) וגם בתצוגה הפנימית. שיווקית זה מבליט שירותים ללא עלות. הסינון שונה מ'הסתר אם 0 או ריק' ל'הסתר רק אם השדה חסר ב-JSON' — כך תעריף שהוגדר במפורש ל-0 מוצג, בעוד שדות שכלל לא היו חלק מהצעות ישנות (שנוספו אחרי יצירתן) עדיין מוסתרים.
v01.94.000שיפורזמני הפצה / מחירוןminorסיווג יעד חריג/רגיל — פר-לקוח (במקום גלובלי)
עד היום הסיווג של ישוב כ'יעד חריג' או 'רגיל' היה גלובלי ומשותף לכל הלקוחות, והוא זה שמפעיל גם את תוספת החיוב 'יעד חריג' וגם את זמן האספקה המוצג (3 מול 7 ימים).
פרטים נוספים ↓הסתר ↑
עד היום הסיווג של ישוב כ'יעד חריג' או 'רגיל' היה גלובלי ומשותף לכל הלקוחות, והוא זה שמפעיל גם את תוספת החיוב 'יעד חריג' וגם את זמן האספקה המוצג (3 מול 7 ימים). מאחר שרוב הישובים סווגו כחריגים בברירת מחדל, הפעלת תוספת יעד חריג במחירונים הייתה גורמת לגביית-יתר על כמעט כל משלוח. כעת רשימת הישובים נשארת גלובלית, אבל הסיווג חריג/רגיל הוא פר-לקוח: כל לקוח מתחיל מהמצב הגלובלי הקיים (כברירת מחדל, ללא שינוי) ויכול לסווג מחדש ישובים לעצמו דרך מסך 'זמני הפצה → ישובים'. ה-override חל גם על תוספת החיוב וגם על זמן האספקה בהקשרים שיש בהם לקוח (פאנל החיוב, מעקב, פורטל, כלי AI). בדיקת-המשלוח הציבורית-הגלובלית ממשיכה להציג את ברירת המחדל הגלובלית. בנוסף (business-inbox): סימן המסירה (✓) בהודעה יוצאת מוקם כעת בצד שמאל של הבועה ב-RTL (כמו ב-WhatsApp) במקום בצד ימין. כמו כן נוספה תמונת פרופיל WhatsApp של הלקוח/הקבוצה ב-business-inbox: ה-adapter של Green API שולף את התמונה דרך getAvatar, היא נשמרת ב-cache על ה-session (avatar_url + avatar_checked_at, רענון עצל כל 6 שעות בפתיחת שיחה כדי לא להעמיס על Green API), ומוצגת גם בכותרת השיחה וגם ברשימת הצ׳אטים — עם נפילה אוטומטית לראשי-תיבות כשאין תמונה, היא מוסתרת, או ה-URL פג תוקף. כמו כן עוצב מחדש שדה הכתיבה ב-business-inbox כך שיתאים ל-/messages/inbox: תיבה מעוגלת עם בורר אימוג'י וכפתור שליחה עגול, ונוסף רקע צ׳אט ייחודי (דפוס אלכסוני עדין, שונה מדפוס הנקודות של ה-inbox כדי להבדיל בין הדפים). זהו שלב 1; צירוף קבצים והצגת מדיה נכנסת יגיעו בשלב הבא. בנוסף תוקנו שני פערי פריסה ב-business-inbox: (1) פער מיותר בתחתית הדף — הדף השתמש בגובה קבוע h-[calc(100vh-4rem)] במקום למלא את ה-main בפועל; הוחלף ל-flex-1 min-h-0 (כמו ב-/messages/inbox) כך שאין שוליים בתחתית. (2) רווח חיצוני בין סיידבר רשימת הצ׳אטים לסיידבר הראשי — רקע הסיידבר שונה מ-bg-muted/10 ל-bg-background כך שהוא נבלע ברווח של הסיידבר הצף ונראה צמוד אליו, והריפוד של 8px הפך לפנימי (הוסר md:ps-0). בנוסף נוסף md:-ms-2 ל-root (בדיוק כמו ב-inbox) שמושך את הדף 8px לתוך הרווח של הסיידבר הצף — כך שסיידבר רשימת הצ׳אטים ממש צמוד לסיידבר הראשי ללא שום רווח. כמו כן רוכך קלט החיפוש בראש הסיידבר כך שייטמע ברקע: מילוי muted עדין במקום bg-background לבן, border רך וללא shadow (לא נראה כקופסה צפה), ובפוקוס מבהיר ל-bg-background. שלב 2 (מדיה) הושלם ב-business-inbox: תמיכה בשליחה והצגה של קבצים דרך Green API. נוסף כפתור צירוף (אטב) בתיבת הכתיבה שפותח דיאלוג לבחירת קובץ (תמונה/וידאו/אודיו/מסמך עד 16MB) עם תצוגה מקדימה וכיתוב; הקובץ מועלה דרך sendFileByUpload של Green API (host המדיה הייעודי) ונשמר עם ה-urlFile שהספק מחזיר. מדיה נכנסת (תמונה/וידאו/אודיו/מדבקה/מסמך) שה-webhook קודם זרק — נלכדת כעת מ-fileMessageData.downloadUrl ונשמרת ב-inbox (ללא תגובת AI, כי הסוכן הטקסטואלי לא 'רואה' מדיה). הבועות מציגות תמונות/מדבקות לחיצות, נגן אודיו, נגן וידאו, וקישור למסמכים — עם נפילה לאייקון מתויג כשה-URL חסר. נוספו עמודות media_url + media_type לטבלת ai_chat_messages, וצבע כפתור השליחה שונה מ-zinc לצבע המותג (primary). הערה: שליחת/הצגת מדיה תלויות ב-endpoint וב-downloadUrl של Green API — דורש בדיקה חיה. כמו כן לחיצה על תמונה פותחת כעת חלון תצוגה מעוצב בתוך האפליקציה (lightbox עם זום/הורדה/פתיחה-בטאב/ESC) במקום טאב דפדפן חדש — נלקח מ-/messages/inbox. בנוסף הוגדלה התמונה/וידאו המוצגים בתוך הבועה בדסקטופ (תקרת גובה max-h-60→md:max-h-96) כדי שלא ייראו קטנים מדי, תוך שמירה על הגודל במובייל. כמו כן (תיבת ההודעות /messages/inbox): לכל הודעה יוצאת נוסף אייקון מקור המאפשר לעקוב אחר מי כתב כל תגובה — אווטאר הנציג ששלח (עם שמו בריחוף), אייקון AI לתשובות אוטומטיות של עוזר ה-AI, אייקון מערכת להודעות fallback שלא טופלו, ולוגו העסק להודעות אוטומטיות (טריגרים/מתוזמנות/פרואקטיביות). נוספה עמודה source לטבלת ההודעות שמסמנת את מקור כל שליחה. בנוסף רועננה רשימת הצ׳אטים בסיידבר של /messages/inbox: היא צמודה כעת לסרגל הניווט הראשי (בוטל רווח מיותר של 8px מול הסיידבר הצף), הכותרת שונתה ל'צ׳אט לקוחות קצה', הטאב 'לא טופל' שונה ל'הסלמות', ואזור תוויות הסיכום ('טופל היום' / 'לא טופל') שמעל הטאבים הוסר כמיותר. עוצבו גם שורות השיחה עצמן: לשיחה הפעילה נוסף פס-מבטא בצבע המותג בקצה ההתחלה וטבעת תואמת סביב האווטאר (במקום רק הדגשת רקע), והתצוגה המקדימה התעשרה — הודעה אחרונה יוצאת מקבלת קידומת 'את/ה:', והודעת מדיה ללא טקסט מוצגת עם אייקון לפי סוג (תמונה/וידאו/הודעה קולית/מסמך/מדבקה) במקום '[ מדיה ]'. לשם כך נוסף enrichment חסום ב-API של רשימת השיחות שמביא את סוג המדיה של ההודעה האחרונה לכל טלפון. בנוסף, הודעות מדיה מלקוח הקצה (תמונה/מדבקה/וידאו/מסמך) נכנסות כעת לסיווג ה-AI במקום להישאר תקועות כ'לא טופל': ה-webhook תופס גם את הכיתוב (caption) של המדיה ומתזמן סשן AI גם להודעות מדיה, וה-classifier מקבל את ההודעה כשורת הקשר מתויגת ('[sent an IMAGE]' וכו'). ה-AI מסווג לפי ההקשר — מדבקה כמעט תמיד נחשבת אישור חיובי ונסגרת כטופל, בעוד תמונה/וידאו/מסמך נסגרים כטופל רק כשההקשר חיובי בבירור; אחרת (או כשאין הקשר כלל) הם מוסלמים לבדיקה אנושית עם הערה, כדי לא לפספס למשל תמונה של חבילה פגומה. כמו כן עוצבו כרטיסיות רשימת הצ׳אטים ב-/messages/inbox בסגנון הקרוב ל-business-inbox: מעבר מרשימה צפופה עם קווים מפרידים לכרטיסים מעוגלים (rounded-xl) עם רווחים, מצב פעיל רך (רקע סגול עדין + טבעת פנימית במקום פס-מבטא בקצה), טבעת עדינה סביב האווטאר והקטנתו ל-h-9, ושעה שמתעמעמת בריחוף. כל הפיצ׳רים העשירים נשמרו (באדג' לא-נקרא, צ׳יפ מקור, באדג' AI, אייקון סטטוס מסירה, אייקון מדיה, סמן-כנקרא ותפריט קליק-ימני). בכיוון ההפוך — הובאו ל-business-inbox פיצ׳רים מ-/messages/inbox: (1) צ׳יפ טריאז' 'ממתין' (ענבר) כשהלקוח כתב אחרון ומחכה למענה, ובאדג' ✨ כשה-AI ענה אחרון — שני סיגנלים שנגזרים מ-lastMessageRole ללא backend; (2) תפריט קליק-ימני על כרטיס שיחה (העברה/שחזור מארכיון); (3) רספונסיביות מובייל מלאה — חילוף בין רשימת הצ׳אטים לתוכן הצ׳אט עם כפתור חזרה בכותרת (md:hidden), בדיוק כמו ב-/messages/inbox וב-WhatsApp. (זהו v1 של תוכנית ההשוואה ב-planning/business-inbox-parity-plan.md; שלב הביצועים לקנה מידה גדול ימשיך בנפרד.) בנוסף שופר זמן פתיחת שיחה ב-/messages/inbox (בעקבות תלונת משתמשים על השהיה קלה): נוסף cache משותף של הודעות עם stale-while-revalidate — פתיחה חוזרת של שיחה מציגה מיידית מה-cache ואז מרעננת ברקע; ה-prefetch מופעל מוקדם יותר (גם ב-pointerdown, כך שזה עובד גם במגע ולא רק ב-hover); ובוצע איחוד בקשות-תעופה (dedup) כך שה-prefetch וטעינת השיחה חולקים בקשה אחת במקום שתיים. ה-cache מתנקה אוטומטית במעבר tenant (התחזות) כדי שלא תוצג היסטוריה חוצת-לקוחות. כמו כן, בשני הצ׳אטים (לקוחות קצה ועסקיים) נוספה טבעת halo בצבע המותג סביב האווטאר של השיחה הפעילה ברשימה — סימון ויזואלי ברור של הצ׳אט הנבחר (ring-2 ring-primary עם ring-offset, במקום הטבעת העדינה הרגילה). בנוסף הושלם שלב הביצועים של business-inbox (Workstream 3 בתוכנית ההשוואה) — הדף מחזיק כעת עשרות אלפי צ׳אטים: ה-API עבר מ-take:100 קשיח + חיפוש client-side ל-keyset pagination על updatedAt עם infinite-scroll, וחיפוש server-side עמוק שמחפש גם בתוכן ההודעות (ILIKE מואץ ע״י אינדקס pg_trgm על ai_chat_messages.content) בנוסף לשם/טלפון/chatId/כותרת. נוספו אינדקסים: (tenant_id, context, is_archived, updated_at DESC) לסריקת הרשימה ו-(session_id, created_at DESC) לתת-שאילתת ההודעה-האחרונה (שגם מביאה כעת את סטטוס המסירה — ✓/✓✓ מוצג ברשימה). הטעינה מביאה עמוד-ראשון בלבד ומרחיבה בגלילה, עם poll שקט שממזג את העמוד החד ביותר כך שעמודים שנטענו בגלילה נשמרים. כמו כן תוקנה איטיות אמיתית בפתיחת צ׳אט ב-/messages/inbox: שאילתת ה-shipment שמלווה את טעינת ההודעות לא השתמשה באינדקס הטלפון — ה-ORDER BY createdAt גרם ל-planner לסרוק לאחור את אינדקס ה-createdAt ולסנן טלפון שורה-שורה (~10k שורות נסרקות, ~146ms ב-DB, וגרוע יותר ככל שיש יותר משלוחים בטננט). השאילתה נעטפה ב-CTE מסוג MATERIALIZED שמכריח את סינון הטלפון (idx_shipment_phone_digits) לרוץ קודם ואז ממיין רק את ההתאמות — צניחה ל-~12ms (פי ~12). זו הייתה השאילתה הדומיננטית בפתיחת צ׳אט (לעומת ~6ms של שליפת ההודעות עצמה). כמו כן תוקן באג בבורר קבוצות הוואטסאפ (בטריגרים ובכרטיס הנהג): אחרי החלפת מספר/instance ב-Green API הבורר המשיך להציג את קבוצות המספר הקודם, כי הדפדפן הגיש את תשובת ה-API מה-cache שלו והבקשה כלל לא הגיעה לשרת (גם ריענון קשיח לא עזר, כי הוא לא מכריח fetch מאוחר לעקוף cache). התיקון: הראוט /api/messages/green-api-groups מסומן force-dynamic ומחזיר Cache-Control: no-store, וה-fetch בבורר משתמש ב-cache: no-store ומושך רשימה טרייה בכל פתיחה. כמו כן תוקן באג מובייל בבחירת שליח/קבלן מתוך רשימת ה-Combobox (כרטיסיית המשימות בדף המשלוח, וכל בורר מבוסס Combobox): במובייל לחיצה על שורת שליח ברשימה פשוט לא בחרה כלום, בעוד בדסקטופ זה עבד. הסיבה — רכיב ה-Combobox (diceui) משחרר במגע את ה-pointer capture ומסתמך על אירוע click שהדפדפן מסנתז, שמותנה בכך ש-pointerdown ו-pointerup פוגעים באותו אלמנט; במובייל סגירת המקלדת בלחיצה גורמת ל-reflow של הרשימה המעוגנת, ה-pointerup פוגע במקום אחר וה-click אובד. התיקון מפעיל את הבחירה ישירות על השורה (currentTarget.click) ב-pointerup במגע, עם סף תנועה כדי שגלילה לא תבחר בטעות; מסלול העכבר בדסקטופ נשאר ללא שינוי.
- סיווג חריג/רגיל הפך לפר-לקוח (רשימת הישובים נשארת גלובלית)
- ברירת מחדל = המצב הגלובלי הקיים — אפס שינוי התנהגות עד שמסווגים מחדש
- פותר גביית-יתר אפשרית בתוספת 'יעד חריג' כשמגדירים מחירונים
- ה-override חל גם על החיוב וגם על זמן האספקה (מעקב, פורטל, AI)
- עריכה במסך 'זמני הפצה → ישובים' כותבת override פר-לקוח (יחיד + קבוצתי)
- אטריביוציה חזותית בתיבת ההודעות — אייקון מקור לכל הודעה יוצאת (נציג/AI/מערכת/טריגר) עם שם הנציג בריחוף
- הודעות מדיה מלקוח הקצה מסווגות ע״י AI לפי הקשר — מדבקת תודה נסגרת כטופל, תמונה ללא הקשר חיובי מוסלמת (שלא לפספס חבילה פגומה)
- תוקן בורר קבוצות הוואטסאפ שהציג את קבוצות ה-instance הקודם אחרי החלפת מספר ב-Green API (cache של הדפדפן) — no-store + רענון בכל פתיחה
- תוקן באג מובייל — בחירת שליח/קבלן מרשימת ה-Combobox (כרטיסיית משימות בדף משלוח) לא עבדה במגע; הבחירה נורית כעת ישירות ב-pointerup, מסלול הדסקטופ ללא שינוי
v01.93.000שיפורלקוחות / מחירוןminorמשלוח כפול / כפולה — תוספת על מחיר המסירה
תעריף 'משלוח כפול / כפולה' מטופל כעת כתוספת המתווספת מעל מחיר המסירה הרגיל — בדומה לתוספת גוביינא — ולא כמחיר נפרד.
פרטים נוספים ↓הסתר ↑
תעריף 'משלוח כפול / כפולה' מטופל כעת כתוספת המתווספת מעל מחיר המסירה הרגיל — בדומה לתוספת גוביינא — ולא כמחיר נפרד. מבחינה פונקציונלית: כל משלוח כפולה (isRoundtrip) מקבל אוטומטית תוספת בגובה roundtripRate בחישוב החיוב של הלקוח (משלים את תוספת 'כפולה' שהייתה מוגדרת אך לא חושבה). מבחינת תצוגה: מחירון הלקוח, הצעות מחיר, חוזים וייבוא המחירון מציגים כעת את השדה כתוספת ('תוספת'), עם הסבר ברור שמדובר בתוספת על המשלוח הרגיל.
- תוספת 'כפולה' אוטומטית = roundtripRate מעל מחיר המסירה (כמו גוביינא)
- מחושב במנוע החיוב ומופיע בפאנל החיוב של המשלוח ובדוח עמלות הסוכן
- תצוגה עקבית כ'תוספת' במחירון, הצעות מחיר, חוזים וייבוא מחירון
- עמודת 'מחירון' חדשה בטבלת הלקוחות — פתיחת המחירון ישירות מהרשימה ('ערוך מחירון' / 'לא הוגדר מחירון'), בדומה לטבלת השליחים
- פילטר 'מחירון' (הוגדר / לא הוגדר) ופעולת אצווה 'הגדר מחירון' לקבוצת לקוחות — אחידות מלאה עם טבלת השליחים
v01.92.002שיפורפורטל לקוחות / יצירת משלוחבחירת כתובת מוצא מהמועדפות בטופס יצירת המשלוח
בטופס יצירת המשלוח בפורטל נוסף כפתור 'מועדפות' גם לסקציית כתובת המוצא — עד כה ניתן היה לבחור כתובת שמורה רק ליעד.
פרטים נוספים ↓הסתר ↑
בטופס יצירת המשלוח בפורטל נוסף כפתור 'מועדפות' גם לסקציית כתובת המוצא — עד כה ניתן היה לבחור כתובת שמורה רק ליעד. כעת הלקוח יכול למלא גם את כתובת המוצא (שולח) מאותו מאגר הכתובות המועדפות.
v01.92.001תיקוןסנכרון משלוחים / איסוףתיקון שם השולח במשלוחי איסוף — תמיד שם החברה
במשלוחי איסוף, עמודת ה'שולח' הציגה לעיתים את שם נקודת האיסוף (צד שלישי) במקום את שם החברה (הלקוח).
פרטים נוספים ↓הסתר ↑
במשלוחי איסוף, עמודת ה'שולח' הציגה לעיתים את שם נקודת האיסוף (צד שלישי) במקום את שם החברה (הלקוח). הסיבה: חלק ממסלולי הסנכרון (cron / ניסיונות חוזרים) קוראים ל-upsert ללא שם הלקוח, ואז המערכת נפלה לשם המוצא מליונוויל. כעת שם השולח נפתר תמיד ממזהה החברה בליונוויל (company_id), גם כשהשם לא מועבר — כך ש'שולח' תמיד מציג את החברה. הפתרון משתמש ב-cache לפי מזהה חברה כדי לא להעמיס על נתיב הסנכרון.
v01.92.000חדשלקוחות / מחירוןminorייבוא מחירון לקוחות מאקסל
נוסף מסך ייבוא מחירון בכמות גדולה ללקוחות, בסגנון ייבוא המשלוחים.
פרטים נוספים ↓הסתר ↑
נוסף מסך ייבוא מחירון בכמות גדולה ללקוחות, בסגנון ייבוא המשלוחים. מעלים קובץ אקסל עם עמודת שם לקוח, תאריך תחולה, ושש עמודות תעריף (מסירה, איסוף, איסוף מבית העסק, משלוח כפול, תוספת גוביינא, תוספת יעד חריג) + מחירי חבילה שניה ושלישית והלאה. המערכת מזהה אוטומטית את הלקוחות לפי השם, ומאפשרת לתקן זיהוי כושל בעזרת חיפוש והשלמה אוטומטית מתוך רשימת הלקוחות. שדות חובה חסרים (מסירה/איסוף) או ערכים לא תקינים מסומנים כשגויים עם עריכה inline וטאב 'שגויות'. שורה עם תאריך תחולה יוצרת כלל מתוזמן (REPLACE) שנכנס אוטומטית בתאריך — בדיוק כמו עדכון תעריף בטופס הלקוח; שורה ללא תאריך מעדכנת מיידית את תעריפי הבסיס. תא ריק לא משנה את הערך הקיים. כל שינוי נכנס להיסטוריית המחירון של הלקוח כולל שם המשתמש שהעלה את הקובץ, ובנוסף נשמרת היסטוריית יבואים ייעודית עם אפשרות ייבוא חוזר. הייבוא מאפשר תאריכי תחולה היסטוריים (עד שנתיים אחורה) — ללא מגבלת 45 הימים של הטופס, כי זו פעולה יזומה של מנהל. שורות שנכשלו מוצגות כעת במסך הסיום עם הסיבה לכל שורה.
- זיהוי לקוחות אוטומטי לפי שם + תיקון עם חיפוש והשלמה אוטומטית
- ולידציה ועריכה inline לשדות חובה וערכים לא תקינים, עם טאב 'שגויות'
- תאריך תחולה → כלל מתוזמן (כמו בטופס); ללא תאריך → עדכון בסיס מיידי
- תמיכה בתאריכי תחולה היסטוריים (עד שנתיים אחורה) — ללא מגבלת 45 הימים של הטופס
- שורות שנכשלו מוצגות במסך הסיום עם הסיבה לכל שורה
- תמיכה במחירי חבילה שניה ושלישית והלאה
- כל שינוי נכנס להיסטוריית המחירון של הלקוח, כולל מי העלה את האקסל
- היסטוריית יבואים ייעודית עם ייבוא חוזר
v01.90.000חדשסוכנים / עמלות ושכרminorמערכת חישוב עמלות ושכר לסוכני מכירות
נבנה מנוע חישוב עמלות מלא לסוכני מכירות, התומך בכל סוגי ההתקשרות הנפוצים בענף.
פרטים נוספים ↓הסתר ↑
נבנה מנוע חישוב עמלות מלא לסוכני מכירות, התומך בכל סוגי ההתקשרות הנפוצים בענף. עד היום עמוד הסוכנים אִפשר רק להגדיר עמלה — כעת המערכת מחשבת בפועל את הסכום לתשלום לכל חודש, עם פירוט מלא וייצוא לאקסל. החישוב מבוסס על מנוע טהור (אגורות, ללא שגיאות עיגול) בתבנית של מנוע שכר הנהגים, עם 22 בדיקות יחידה. משלוח משויך לחודש שבו הגיע לראשונה לסטטוס מזכה (כל סטטוס פרט ל'משלוח חדש', 'שובץ לשליח איסוף' ו'בוטל'), דרך עמודת commissionableAt חדשה (sticky) שנקבעת אוטומטית בכל מסלולי שינוי הסטטוס. מקור החיוב לאחוז ולמרווח הוא החיוב הפנימי של הלקוח (CustomerPricingProfile). דוח העמלות, ניהול כללי העמלה וה-ledger לחיוב/זיכוי חד-פעמי זמינים בכרטיס הסוכן (דורש הרשאת 'צפייה בעמלות סוכנים').
- מודל מרווח: רווח הסוכן = חיוב הלקוח פחות עלות בסיס למשלוח
- סכום קבוע למשלוח, עם תעריף שונה פר-לקוח
- אחוז מהדוח החודשי של הלקוח, לתקופה מוגבלת (X חודשים מהשיוך או טווח תאריכים)
- בונוס חודשי קבוע + עלויות חוזרות שהסוכן נושא בהן (קבוע-חודשי או פר-משלוח)
- חיוב/זיכוי חד-פעמי בחשבון הסוכן (ledger)
- דוח חודשי עם בורר חודש, פירוט מלא וייצוא Excel
- דף סוכן ייעודי ומעוצב (/agents/[id]) — hero, נתוני מפתח, דוח עמלות, כללים, לקוחות ולידים; לחיצה על שורה פותחת אותו במקום חלון צד
v01.89.005תיקוןמשלוחים / טופס יצירהטופס יצירת משלוח מתאפס בכל פתיחה
תוקן באג שבו לאחר שמירת משלוח, פתיחת טופס חדש זכרה את הפרטים של המשלוח הקודם.
פרטים נוספים ↓הסתר ↑
תוקן באג שבו לאחר שמירת משלוח, פתיחת טופס חדש זכרה את הפרטים של המשלוח הקודם. הסיבה: לחלק משדות הטופס (שם נמען, כתובת יעד ועוד) לא הוגדר ערך ברירת מחדל, ולכן ה-reset לא ניקה אותם באופן אמין. כעת לכל השדות יש ברירת מחדל ריקה, ובנוסף הטופס מתאפס לחלוטין בכל פתיחה — ללא תלות באופן שבו נסגר (כפתור סגירה, לחיצה מחוץ, Esc, או לאחר שמירה).
v01.89.004תיקוןסוכן WhatsApp / AI Chatסוכן WhatsApp: תיקון JSON קטוע עם Gemini 2.5 Flash
Gemini 2.5 Flash מפעיל thinking tokens שאכלו את רוב תקציב ה-max_tokens (2048), וה-JSON נקטע באמצע.
פרטים נוספים ↓הסתר ↑
Gemini 2.5 Flash מפעיל thinking tokens שאכלו את רוב תקציב ה-max_tokens (2048), וה-JSON נקטע באמצע. הוגדל max_tokens ל-8192 כדי לאפשר לסוכן לסיים את התשובה גם כשהמודל חושב רבות. בנוסף: תיקון ברקודים ב-AI Chat — מזהה משלוח מוצג כעת כרכיב לחיץ עם אייקוני העתקה וניווט (כמו בכל שאר המערכת) ואינו מתבלבל עם מספרי טלפון או תאריכים. כמו כן תוקן עיצוב רשימת הצ׳אטים ב-business-inbox: טקסט ארוך (שם לקוח / תצוגה מקדימה של ההודעה האחרונה) גלש מחוץ לכרטיסיה ושבר את הפריסה. הסיבה: ScrollArea של Radix עוטף את התוכן ב-display:table שמתרחב לרוחב השורה הארוכה ביותר ומבטל את ה-truncate; נכפה display:block על המעטפת ונוסף min-w-0 לשם — וכעת הטקסט מתקצר כראוי. בנוסף הוקפד עקרון הריווח (עד 8px בין אלמנטים): צומצמו mb-3→mb-2 בכותרת ו-gap-2.5→gap-2 בין האווטאר לטקסט בכרטיסיה, וה-skeletons בטעינה יושרו ל-p-2/space-y-1 כך שיתאימו למיקום הכרטיסיות האמיתיות. גם שולי הכותרת צומצמו מ-px-4/py-3 (16px) ל-p-2 (8px), כך שתיבת החיפוש והכותרת מתיישרות עם מיכל הרשימה ושוליים אחידים של 8px לאורך כל הסיידבר. בנוסף תוקנה אסימטריה פנימית בכרטיסיות: ה-pe-5 (20px) שריפד רק את צד הסוף הוסר, כעת הריפוד סימטרי (px-3 משני הצדדים), ובריחוף חותמת הזמן מתפוגגת וכפתור הארכיון מופיע במקומה עם רקע נקי — ללא חפיפה על הטקסט וללא שמירת מקום קבועה. ולבסוף צומצם הרווח הכפול בין סיידבר הצ׳אטים לסיידבר הראשי: התוכן (חיפוש, כותרת, כרטיסיות) הוצמד לקצה בדסקטופ (md:ps-0) כך שהרווח של הסיידבר הצף (8px) הוא הרווח היחיד, במקום 16px (8px צף + 8px ריפוד פנימי). מאחר שהכרטיסיה צמודה כעת לקצה, ה-ring של הכרטיסיה הפעילה שונה ל-ring-inset כדי שלא ייחתך ע״י ה-overflow בקצה. כמו כן תוקן באג כפילות שיחות ב-business-inbox: ה-webhook של Green API חיפש session קיים רק בחלון של 24 שעות, כך ששיחה ששתקה מעל 24 שעות יצרה רשומת session חדשה לאותו chatId — ואותה שיחה הופיעה ככמה כרטיסיות. כעת מאותרת שיחה אחת לכל chatId (ללא חלון זמן), והקשר ה-AI מוגבל בנפרד ל-20 ההודעות האחרונות (תוקן גם סדר טעינת ההיסטוריה ל-desc במקום asc). בנוסף בוצע מיזוג חד-פעמי של הכפילויות הקיימות (איחוד ההודעות לשיחה הוותיקה, מחיקת הרשומות הכפולות). ולבסוף תוקן באג שקיפות בשליחה ידנית מה-inbox: ה-route החזיר success:true גם כשהמסירה ל-Green API נכשלה, כך שהנציג ראה ✓ למרות שההודעה לא נמסרה ללקוח. כעת המסירה נרשמת כ-failed/sent, ה-route מחזיר delivered, וה-UI מציג אייקון אדום + toast 'ההודעה נשמרה אך לא נמסרה' כשהמסירה נכשלה. ה-route גם מחזיר את סיבת הכשל (deliveryError) וה-toast מציג אותה, כך שהנציג רואה מיד *למה* נכשלה המסירה במקום לחפש בלוגים. ובאמצעות זה אותר ותוקן שורש הבעיה: שליחה ידנית מה-inbox נכשלה לחלוטין (TypeError 'Cannot read properties of undefined (reading _sendToChat)') כי ה-route שלף את adapter.sendToChat למשתנה וקרא לו לא-מאוגד, כך ש-this היה undefined. תוקן לקריאה ישירה על ה-adapter (כמו ב-driver-tools). רגרסיה מאז commit 776179e0.
v01.89.003תיקוןmessages/inboxתיקון: שיחות צ׳אט מפוצלות — היסטוריה לא הופיעה ללקוח שפנה
בחלק מהשיחות ב-/messages/inbox הופיעה רק התגובה האחרונה של הלקוח (למשל '👍' או 'תודה') בלי ההיסטוריה, כך שהנציג לא ידע על מה הפנייה.
פרטים נוספים ↓הסתר ↑
בחלק מהשיחות ב-/messages/inbox הופיעה רק התגובה האחרונה של הלקוח (למשל '👍' או 'תודה') בלי ההיסטוריה, כך שהנציג לא ידע על מה הפנייה. הסיבה: מספרי טלפון נורמלו לא-עקבית — הודעות יוצאות (התראות משלוח) נשמרו לעיתים במספר מקומי בן 9 ספרות ללא קידומת (5XXXXXXXX, כשמקור הנתון מליונוויל השמיט את ה-0 המוביל), בעוד תגובות נכנסות מ-Meta מגיעות בפורמט בינלאומי (9725XXXXXXXX) — מה שפיצל את אותה שיחה לשניים. כעת normalizePhone מזהה נייד ישראלי בן 9 ספרות ומוסיף 972, ובוצע מיזוג חד-פעמי של ההודעות וההיסטוריה הקיימת (1,647 הודעות אוחדו, 118 שיחות כפולות מוזגו). השיחות מציגות כעת את ההקשר המלא. בנוסף: שליפת המשלוח בצ׳אט (ברקוד לחיץ + שם לקוח/שולח) לא מצאה משלוחים שטלפונם שמור בפורמט 9-ספרות חשוף — כעת היא מתאימה את שלושת הפורמטים (972…, 0…, 5XXXXXXXX), והברקוד חזר להיות לחיץ.
v01.89.002תיקוןסנכרון משלוחים / אבטחת נתוניםתיקון קריטי: שיוך משלוח ללקוח לפי החברה בליונוויל ולא לפי שם המוצא
תוקן באג חמור שבו משלוחים נשייכו ללקוח השגוי (או נותרו ללא לקוח).
פרטים נוספים ↓הסתר ↑
תוקן באג חמור שבו משלוחים נשייכו ללקוח השגוי (או נותרו ללא לקוח). זיהוי הלקוח של משלוח נכנס מליונוויל התבסס על שם המוצא (השולח); בעקבות שינוי שגרם לשם המוצא להציג את נקודת האיסוף האמיתית, משלוחי איסוף — שבהם המוצא הוא כתובת של גורם אחר (למשל מחסן של לקוח אחר) — שויכו לפי שם נקודת האיסוף ולא לפי הלקוח שהזמין. כעת השיוך נקבע אך ורק לפי מזהה החברה בליונוויל (company_id → lionwheelId, ייחודי), עם נפילה לשם החברה בלבד — לעולם לא לפי שם המוצא. בנוסף תוקנו 561 משלוחים שנפגעו (שויכו מחדש ללקוח הנכון לפי החברה). כמו כן הוחזרה עמודת ה'שולח' להציג את שם החברה (הלקוח) במקום את נקודת האיסוף — ותוקנו 721 משלוחים שבהם הוצג שם נקודת האיסוף.
v01.89.001שיפורמשלוחים / איסוף / סנכרוןזיהוי איסוף אוטומטי גם כשליונוויל לא שולח את הסוג
ליונוויל החל לשלוח את שדה task_type ב-webhooks רק לאחרונה ובאופן לא עקבי, כך שמשלוחי איסוף שהגיעו לפני כן (או שהשדה הושמט עבורם) לא זוהו.
פרטים נוספים ↓הסתר ↑
ליונוויל החל לשלוח את שדה task_type ב-webhooks רק לאחרונה ובאופן לא עקבי, כך שמשלוחי איסוף שהגיעו לפני כן (או שהשדה הושמט עבורם) לא זוהו. נוספה רשת ביטחון בזמן הסנכרון: כשמגיע משלוח מליונוויל ללא task_type, המערכת גוזרת 'איסוף' אוטומטית אם כתובת היעד זהה לכתובת בית-העסק הרשומה — אותו עיקרון של ה-backfill, אך מיושם חי על כל משלוח נכנס. הבדיקה ממוקדת ויעילה (cache לכתובות העסק לפי מזהה החברה) כדי לא להעמיס על נתיב הסנכרון.
v01.89.000חדשמשלוחים / איסוףminorזיהוי וסימון משלוחי איסוף
משלוחי איסוף (שבהם היעד הוא כתובת בית-העסק) מזוהים ומסומנים כעת בפורטל: תווית 'איסוף' מופיעה ליד מספר המשלוח ברשימה ובדף המשלוח, ונוסף פילטר 'סוג משלוח' (מסירה / איסוף) לרשימת המשלוחים.
פרטים נוספים ↓הסתר ↑
משלוחי איסוף (שבהם היעד הוא כתובת בית-העסק) מזוהים ומסומנים כעת בפורטל: תווית 'איסוף' מופיעה ליד מספר המשלוח ברשימה ובדף המשלוח, ונוסף פילטר 'סוג משלוח' (מסירה / איסוף) לרשימת המשלוחים. הזיהוי מבוסס על השדה taskType מליונוויל; משלוחים שנוצרים מהטופס מקבלים את הסוג אוטומטית לפי מצב היצירה (מסירה/איסוף). בנוסף בוצע backfill למשלוחים היסטוריים: 2,175 משלוחים שבהם כתובת היעד זהה לכתובת בית-העסק סומנו כ'איסוף'. תוקן גם באג תצוגה: שם המוצא (sourceName) הציג את שם החברה במקום את שם המוצא האמיתי בסנכרון מליונוויל — קריטי במשלוחי איסוף שבהם המוצא הוא צד שלישי.
- תווית 'איסוף' ברשימת המשלוחים ובדף המשלוח בפורטל
- פילטר 'סוג משלוח' (מסירה / איסוף) ברשימה
- סימון אוטומטי לפי מצב היצירה + backfill ל-2,175 משלוחים היסטוריים
- תיקון שם המוצא (sourceName) שהוצג כשם החברה במקום המוצא האמיתי
v01.88.000שיפורmessages/inboxminorצ׳אט מהיר + חיפוש לפי תוכן הודעה
אופטימיזציה עמוקה לדף /messages/inbox + חיפוש לפי תוכן הודעה, וארכיטקטורה שמחזיקה גם בעשרות אלפי שיחות.
פרטים נוספים ↓הסתר ↑
אופטימיזציה עמוקה לדף /messages/inbox + חיפוש לפי תוכן הודעה, וארכיטקטורה שמחזיקה גם בעשרות אלפי שיחות. שלב א': תוקנה איטיות קריטית — עבור כל שיחה ברשימה בוצעה שאילתת LATERAL נפרדת על טבלת המשלוחים שלא ניצלה אינדקס, ובטאב "לא טופל" זה הצטבר ל-~54 שניות וגרם ל-timeout (הטאב לא נטען כלל). שלב ב': רשימת השיחות והמונים נקראים כעת מטבלת whatsapp_conversations הדנורמלית (שורה אחת לשיחה, מתוחזקת בכל הודעה נכנסת/יוצאת + backfill) — כל טאב הוא סריקת אינדקס יחידה עם דפדוף, בזמן קבוע ללא תלות בכמות ההודעות הכוללת.
- טאב "לא טופל" / "ללא מענה": מ-~54 שניות (timeout) ל-מילישניות
- תצוגת ברירת המחדל של רשימת השיחות: מ-~2.9 שניות ל-~21ms
- חיפוש חדש לפי מילות מפתח בתוך תוכן ההודעות (אינדקס pg_trgm — ~5ms במקום ~1.1 שניות)
- מקור-אמת דנורמלי (whatsapp_conversations) + מונה unread, עם דפדוף בכל הטאבים — נשאר מהיר גם בעשרות אלפי שיחות
- התיקון חל על כל מצבי הסינון: הכל, לא טופל, טופל, ללא מענה, חיפוש
v01.87.000חדשהודעות / צ׳אט עסקי / Green APIminorצ׳אט לקוחות עסקיים — מראה מלאה של כל הודעות ה-WhatsApp
צ׳אט הלקוחות העסקיים (תיבת ההודעות) משקף כעת את כל ההודעות היוצאות מה-WhatsApp העסקי — לא רק תשובות ידניות ותגובות סוכן ה-AI.
פרטים נוספים ↓הסתר ↑
צ׳אט הלקוחות העסקיים (תיבת ההודעות) משקף כעת את כל ההודעות היוצאות מה-WhatsApp העסקי — לא רק תשובות ידניות ותגובות סוכן ה-AI. עד כה הודעות אוטומטיות שנשלחו דרך טריגרים (התראות טעויות מיון, טריגרים מערכתיים וכו') נשלחו ישירות ל-WhatsApp ולא נרשמו כלל בצ׳אט, כך שהמשתמש ראה רק חלק מהשיחה. כעת כל הודעה יוצאת ש-Green API מהדהד אלינו נלכדת ברמת ה-webhook ונשמרת ב-inbox, כולל יצירת שיחה אוטומטית לצ׳אט/קבוצה שטרם הופיעו. הלכידה היא תוספת בלבד בצד קבלת ה-webhook — אינה נוגעת בנתיב שליחת ההודעות.
- הודעות טריגר אוטומטיות (טעויות מיון, טריגרים מערכתיים) מופיעות כעת בתוך הת׳רד של הקבוצה/הלקוח בצ׳אט העסקי
- הודעות שנשלחו ידנית מהטלפון יוצרות שיחה ב-inbox גם אם לא הייתה היסטוריה קודמת לאותו צ׳אט
- מניעת כפילויות: הודעות סוכן AI/שליחה ידנית לא יירשמו פעמיים (התאמה לפי externalId)
- הלכידה ברמת ה-webhook בלבד — אפס שינוי בנתיב שליחת ההודעות, ללא סיכון למשלוח
v01.86.002תיקוןmessages/inboxתיקון איטיות קריטית בצ׳אט — טאב "לא טופל" שלא נטען
שאילתות רשימת השיחות ב-/messages/inbox עברו אופטימיזציה משמעותית.
פרטים נוספים ↓הסתר ↑
שאילתות רשימת השיחות ב-/messages/inbox עברו אופטימיזציה משמעותית. הבעיה: עבור כל שיחה ברשימה בוצעה שאילתת LATERAL נפרדת על טבלת המשלוחים (לשליפת שם הלקוח/השולח), שלא ניצלה את האינדקס idx_shipment_phone_digits וסרקה את כל הטבלה מחדש בכל פעם. בטאב "לא טופל"/"ללא מענה" (ללא הגבלת מספר שורות) זה הצטבר ל-~54 שניות וגרם ל-timeout — הטאב לא הציג תוצאות כלל. התיקון מאחד את שליפת המשלוחים למעבר אינדקס אחד (= ANY(ARRAY(...))) לכל חמשת מצבי השאילתה (הכל, לא טופל, טופל, ללא מענה, חיפוש). הפלט זהה לחלוטין; זמן השרת ירד מ-~2.9 שניות ל-~21ms בתצוגת ברירת המחדל ומ-~54 שניות ל-~48ms בטאב "לא טופל".
v01.86.001שיפוראבטחה / Middlewareהקשחת CSRF — חסימת בקשות שינוי ללא מקור מאומת
בדיקת ה-CSRF על בקשות שינוי (POST/PUT/PATCH/DELETE) ל-API הפנימי הוקשחה: עד כה בקשה ללא כותרת Origin עברה ללא בדיקה.
פרטים נוספים ↓הסתר ↑
בדיקת ה-CSRF על בקשות שינוי (POST/PUT/PATCH/DELETE) ל-API הפנימי הוקשחה: עד כה בקשה ללא כותרת Origin עברה ללא בדיקה. כעת המערכת מאמתת את מקור הבקשה דרך Origin, ובהיעדרו נופלת אחורה ל-Referer; בקשה ללא אף אחד מהם — או עם מקור שאינו תואם לדומיין — נדחית. השינוי נוגע אך ורק ב-routes פנימיים מבוססי-session (דפדפן); אפליקציית המובייל (api/v1), ה-Public API וה-webhooks מוחרגים ואינם מושפעים. ללא שינוי בחוויית המשתמש בדפדפן.
v01.86.000חדשמשלוחים / טופס יצירהminorכתובת מוצא בטופס יצירת המשלוח — מסירה / איסוף / חופשי
בטופס יצירת המשלוח נוסף בורר סוג משלוח עם 3 מצבים (כמו בליונוויל): 'מסירה' — כתובת המוצא נלקחת אוטומטית מכתובת בית העסק הרשומה והיעד חופשי (התנהגות ברירת המחדל הקיימת); 'איסוף' — היעד הוא בית העסק והמוצא חופשי להזנה; 'חו…
פרטים נוספים ↓הסתר ↑
בטופס יצירת המשלוח נוסף בורר סוג משלוח עם 3 מצבים (כמו בליונוויל): 'מסירה' — כתובת המוצא נלקחת אוטומטית מכתובת בית העסק הרשומה והיעד חופשי (התנהגות ברירת המחדל הקיימת); 'איסוף' — היעד הוא בית העסק והמוצא חופשי להזנה; 'חופשי' — שתי הכתובות חופשיות. הצד של בית העסק ממולא אוטומטית מהכתובת הרשומה אך נשאר ניתן לעריכה. נוסף סקשן 'כתובת מוצא' (שם, טלפון, עיר, רחוב, מספר) שנשלח לליונוויל, ונשמר גם מקומית כדי שכתובת המוצא תוצג מיד. כתובת בית העסק נגזרת מנתוני החברה שכבר מסונכרנים מליונוויל (lionwheelData.location), ללא צורך בהגדרה נוספת.
- בורר 'מסירה / איסוף / חופשי' בטופס יצירת המשלוח (פורטל וצוות)
- סקשן 'כתובת מוצא' חדש שנשלח לליונוויל ונשמר מקומית
- צד בית-העסק ממולא אוטומטית מהכתובת הרשומה וניתן לעריכה
v01.85.000חדשפורטל לקוחות / כתובות מועדפותminorשמירת כתובת נמען בכתובות המועדפות
נוסף כפתור 'שמור במועדפות' שמאפשר ללקוח לשמור את כתובת הנמען לפנקס הכתובות שלו, ישירות מתוך טופס יצירת המשלוח (ליד בורר הכתובות המועדפות) וגם מתוך דף המשלוח (בכרטיס פרטי היעד, וגם בתצוגה המהירה).
פרטים נוספים ↓הסתר ↑
נוסף כפתור 'שמור במועדפות' שמאפשר ללקוח לשמור את כתובת הנמען לפנקס הכתובות שלו, ישירות מתוך טופס יצירת המשלוח (ליד בורר הכתובות המועדפות) וגם מתוך דף המשלוח (בכרטיס פרטי היעד, וגם בתצוגה המהירה). לחיצה פותחת דיאלוג קצר עם שם הכתובת (ברירת מחדל: שם הנמען) וסיכום הכתובת, ושמירה מוסיפה אותה לאותו מאגר שממנו ממלאים כתובת יעד בלחיצה אחת ביצירת משלוח. כך לקוחות יכולים לבנות בקלות פנקס נמענים קבועים מתוך משלוחים אמיתיים.
- כפתור 'שמור במועדפות' בטופס יצירת המשלוח ובדף המשלוח
- הכתובת נשמרת לאותו מאגר שממלא כתובת יעד בבחירה מהירה
- דיאלוג קצר עם שם כתובת ניתן לעריכה וסיכום הכתובת
v01.84.000חדשליקוט / משלוחיםminorסימון משלוחים לליקוט מסרגל הבחירה המרובה
בסרגל הפעולות של בחירה מרובה נוסף כפתור 'סמן לליקוט' שמאפשר לסמן את כל המשלוחים הנבחרים כמיועדים לליקוט בבת אחת, תוך בחירת תדירות (שוטף / מזדמן), סוג (עלונים / מוצרים), ובמקרה של עלונים — בחירת פרשה אחת או יותר (מהשבתות …
פרטים נוספים ↓הסתר ↑
בסרגל הפעולות של בחירה מרובה נוסף כפתור 'סמן לליקוט' שמאפשר לסמן את כל המשלוחים הנבחרים כמיועדים לליקוט בבת אחת, תוך בחירת תדירות (שוטף / מזדמן), סוג (עלונים / מוצרים), ובמקרה של עלונים — בחירת פרשה אחת או יותר (מהשבתות הקרובות או בהזנה ידנית). הפרשות נשמרות כפריטי המשלוח ויעד השבת נגזר מהפרשה הקרובה ביותר, בדיוק כמו במסך הליקוט. הכפתור זמין בכל הטבלאות שמשתמשות בסרגל — דף המשלוחים, מודל המשלוחים של לקוח, יעד שבת, יעד אקספרס ופערי הפצה.
- כפתור 'סמן לליקוט' בסרגל הבחירה המרובה — בדף המשלוחים ובמודל משלוחי הלקוח
- בחירת תדירות (שוטף/מזדמן) וסוג (עלונים/מוצרים) בפעולה אחת על כל הנבחרים
- לעלונים — בחירת פרשות מרובות שנשמרות כפריטי המשלוח, ויעד השבת נגזר אוטומטית
v01.82.000שיפורפורטל לקוחות / משלוחיםminorתצוגה מהירה של משלוח בפורטל + שינוי התנהגות שורה
בטבלת המשלוחים בפורטל, לחיצה על שורה כבר לא מנווטת אוטומטית לדף המשלוח — כדי למנוע מעבר בטעות תוך כדי בחירה או גלילה.
פרטים נוספים ↓הסתר ↑
בטבלת המשלוחים בפורטל, לחיצה על שורה כבר לא מנווטת אוטומטית לדף המשלוח — כדי למנוע מעבר בטעות תוך כדי בחירה או גלילה. במקום זה, ליד מספר המשלוח הופיעו שני אייקונים שמתגלים במעבר עכבר: אייקון העתקה (מימין) להעתקת מזהה המשלוח, ואייקון פתיחה (משמאל) לניווט לדף המשלוח המלא. לחיצה על מספר המשלוח עצמו פותחת תצוגה מהירה (Drawer) עם כל פרטי המשלוח — סטטוס, יעד, פרטי משלוח, תוויות, מפה, ציר מעקב והוכחת מסירה — בלי לעזוב את הרשימה. ההתנהגות זהה לזו שכבר קיימת בממשק הניהול. בנוסף תוקן באג סקופ ב-API: פתיחת משלוח של חשבון מקושר (לקוח-בן) החזירה 404. כמו כן נוסף אייקון העתקה ליד מספר המשלוח הגדול בראש דף המשלוח המלא. עיצוב התצוגה המהירה אוחד לזה של דף המשלוח — שניהם מרנדרים כעת את אותו רכיב משותף, כך שהתוכן והמראה זהים לחלוטין.
- לחיצה על שורה לא מנווטת יותר בטעות — הפעולות עברו לאייקונים ליד מספר המשלוח
- אייקון העתקה ואייקון ניווט לדף המלא מתגלים במעבר עכבר על המספר
- לחיצה על מספר המשלוח פותחת תצוגה מהירה (Drawer) עם כל פרטי המשלוח
v01.80.003ייעולחיפוש גלובלי / ביצועיםחיפוש גלובלי מהיר — אינדקסי trigram וריצה מקבילה
החיפוש הגלובלי בסרגל העליון היה איטי (עד ~900ms לכל הקלדה) אצל לקוחות עם כמות משלוחים גדולה.
פרטים נוספים ↓הסתר ↑
החיפוש הגלובלי בסרגל העליון היה איטי (עד ~900ms לכל הקלדה) אצל לקוחות עם כמות משלוחים גדולה. הסיבה: החיפוש סורק עמודות עם ILIKE '%טקסט%', וללא אינדקס מתאים Postgres ביצע סריקה מלאה של טבלת המשלוחים בכל בקשה — מורגש רק אצל ה-tenant הגדול ורק במונחים סלקטיביים (טלפון/ברקוד/אימייל) או באמצע הקלדה, ולכן 'לפעמים אצל חלק מהמשתמשים'. נוספו אינדקסי GIN trigram (pg_trgm) על כל העמודות שהחיפוש סורק, כך ש-Postgres משתמש ב-BitmapOr במקום סריקה מלאה — זמן השאילתה ב-DB צנח מ-~810ms לפחות ממילישנייה. בנוסף, שלוש שאילתות החיפוש (משלוחים/לקוחות/שליחים) רצות כעת במקביל במקום בטור, ותקרת הבקשות לדקה הועלתה מ-30 ל-60 כדי למנוע חסימה שגויה בהקלדה מהירה.
v01.80.002תיקוןwebhooks / Green API / אבטחהאימות webhook נכנס מ-Green API — תמיכה יציבה בסוד אימות
תוקן אימות ה-Authorization header של webhookים נכנסים מ-Green API.
פרטים נוספים ↓הסתר ↑
תוקן אימות ה-Authorization header של webhookים נכנסים מ-Green API. נמצא (מצרף של יומיים דגימות בפרודקשן) ש-Green API שולח את הסוד בפורמט 'Bearer <token>', בעוד שבמערכת הסוד נשמר לעיתים בלי ה-prefix — מה שגרם לדחיית כל ה-webhookים הנכנסים (תגובות שליחים, סטטוסי מסירה, תגובות AI) ברגע ש-tenant הגדיר סוד אימות. כעת ההשוואה מנרמלת את שני הצדדים (מסירה 'Bearer ' ורווחים) לפני ההשוואה הבטוחה — כך שהגדרת סוד אימות פשוט עובדת ויציבה, בלי לשבור webhookים ובלי להחליש את האבטחה.
v01.80.001תיקוןסוכן WhatsAppסוכן WhatsApp: שיחה מחוץ לחלון 24 שעות מועברת לנציג
תוקן באג: כאשר לקוח שלח הודעה שהסוכן סיווג כקטגוריה 2 או 3 (שאלת משלוח / הפניה לשולח) אך לא ניתן לשלוח תגובה כי החלון חופשי של Meta (24 שעות) נסגר, השיחה סומנה 'טופל' בעוד שהלקוח לא קיבל מענה.
פרטים נוספים ↓הסתר ↑
תוקן באג: כאשר לקוח שלח הודעה שהסוכן סיווג כקטגוריה 2 או 3 (שאלת משלוח / הפניה לשולח) אך לא ניתן לשלוח תגובה כי החלון חופשי של Meta (24 שעות) נסגר, השיחה סומנה 'טופל' בעוד שהלקוח לא קיבל מענה. כעת השיחה מסומנת 'לטיפול' עם הערה 'מחוץ לחלון 24 שעות — יש לשלוח תבנית ידנית', כך שהנציג מקבל התראה ויכול להמשיך ידנית.
v01.80.000חדשיבוא משלוחיםminorיבוא משלוחים רץ ברקע — אפשר להמשיך לעבוד בזמן ההעלאה
יבוא משלוחים מאקסל (גם בדף הצוות וגם בפורטל הלקוחות) רץ כעת ברקע: מיד עם תחילת הייבוא אפשר לנווט לכל דף אחר במערכת, וטוסט גלובלי בתחתית המסך מציג את קצב ההתקדמות (X/Y משלוחים, כולל מספר אצווה בקבצים גדולים).
פרטים נוספים ↓הסתר ↑
יבוא משלוחים מאקסל (גם בדף הצוות וגם בפורטל הלקוחות) רץ כעת ברקע: מיד עם תחילת הייבוא אפשר לנווט לכל דף אחר במערכת, וטוסט גלובלי בתחתית המסך מציג את קצב ההתקדמות (X/Y משלוחים, כולל מספר אצווה בקבצים גדולים). בסיום מוצג טוסט סיכום עם כפתור 'צפה בסיכום' שמחזיר לדף הייבוא, שם מוצג מסך התוצאות המלא. חזרה לדף באמצע ייבוא מתחברת מחדש להתקדמות החיה. צד השרת לא השתנה כלל — כל לוגיקת הייבוא, הכפילויות וההיסטוריה נשארה זהה.
- ניווט חופשי במערכת בזמן שהייבוא רץ — בלי להישאר 'תקועים' בדף
- טוסט התקדמות גלובלי ששורד מעבר בין דפים, בדומה לשידור SNL
- טוסט סיום עם כפתור 'צפה בסיכום' שמוביל חזרה למסך התוצאות
- חזרה לדף הייבוא באמצע ריצה מתחברת מחדש להתקדמות החיה
- אזהרת דפדפן לפני סגירת הטאב בזמן שייבוא פעיל — מניעת איבוד אצוות שטרם נשלחו
- חסימת ייבוא מקביל שני (כולל בין דף הצוות לפורטל) עד לסיום הריצה הפעילה
v01.79.002חדשעוזר AI / הודעות / תובנות / אודיוminorעוזר AI — שליחת תבנית Meta לנמען עם אישור
עוזר ה-AI תומך כעת בשליחת הודעות WhatsApp לנמענים (לקוחות קצה) דרך תבניות Meta מאושרות.
פרטים נוספים ↓הסתר ↑
עוזר ה-AI תומך כעת בשליחת הודעות WhatsApp לנמענים (לקוחות קצה) דרך תבניות Meta מאושרות. הזרימה: העוזר מציג למשתמש תצוגה מקדימה מדויקת של ההודעה הממולאת בנתוני המשלוח, עם כפתור 'אשר שליחה' ייעודי — ורק לאחר לחיצה על הכפתור ההודעה נשלחת. בנוסף, נוספה מערכת ניהול תובנות AI: דף ניהול מרכזי שמציג את כל התובנות שנאגרו על ידי העוזר, עם אפשרות אישור/דחייה ולמידה ידנית של תובנות חדשות. נוסף גם תמלול אודיו: מיקרופון בצ'אט הפנימי להקלטה ושליחה קולית, ותמלול הודעות קוליות נכנסות מ-WhatsApp — פר-טננט, דרך OpenAI Whisper.
- תצוגה מקדימה ויזואלית של ההודעה בועיל ירוקה (סגנון WhatsApp) לפני שליחה
- כפתור 'אשר שליחה' — העוזר אינו שולח אוטומטית ללא אישור מפורש
- דף ניהול תובנות AI (הגדרות → תובנות AI) — אישור/דחייה/מחיקה של תובנות
- לימוד ידני: הוספת תובנה חדשה מהדף, או על ידי אמירת 'תדע ש...' לעוזר בצ'אט
- תובנות כוללות כעת שדות status/source — ממתין לאישור / אושר / נדחה
- AiInsightsCard בכרטיסי שליח/לקוח/משלוח מציג כפתורי אישור/דחייה inline
- דוח חודשי למנהל שליחים: טבלת סיכום ביקורים לפי שליח עם פירוט מסירות / איסופים / החזרות
- מיקרופון בצ'אט AI פנימי — הקלטה קולית שמומרת לטקסט ומוכנסת לתיבת הקלד (זמין רק לאחר הגדרת מפתח OpenAI בהגדרות AI)
- תמלול הודעות קוליות ב-WhatsApp — הודעות audio/voice נכנסות מתומללות ומועברות לסוכן AI לניתוח
- סקציית 'תמלול אודיו' חדשה בהגדרות AI — הגדרת מפתח OpenAI פר-טננט עבור Whisper
- פורטל מנהל שליחים: כפתור 'לינק כניסה' ליצירת קישור הגדרת סיסמה ישיר (ניתן לשליחה בווצאפ) עבור מנהלים שלא קיבלו מייל
- פורטל שליחים: הרחבה לקבלנים פנימיים — יצירת חשבון פורטל ודוח הכנסות חודשי גם לשליחי CONTRACTOR ללא ניהול
- Idempotency לכלי-כתיבה של AI — שליחת WhatsApp, עדכון סטטוס ועוד לא יבוצעו פעמיים גם אם הבקשה שוגרה שוב (retry)
- דף שליח: מזהה השליח במערכת מוצג כעת בכרטיסיית פרטים אישיים, לצד מזהה Lionwheel
- תיקון בדיקת מסירה: שליחת הודעת 'האם קיבלת?' מחייבת כעת תבנית Meta מאושרת — אם לא מוגדרת תבנית, העוזר/הכפתור מחזירים שגיאה ברורה. ניתן לסמן תבנית כ'תבנית בדיקת מסירה' בהגדרות → הודעות → תבניות
- דף שליח-מנהל: כפתור 'תיוג היסטורי' בסקציית הסאב-שליחים — שיוך רטרואקטיבי של משלוחים שהסאבים ביצעו לפני יצירת הקישור לדוח המנהל, מתאריך נבחר, עם תצוגה מקדימה (כמה ביקורים יתויגו לכל סאב) לפני החלה
- תיקון ספירת משלוחים ב-AI: נוסף כלי countShipments לספירה מדויקת ללא הגבלת שורות. תוקנה הנחיית הסיסטם שגרמה ל-AI לספור 50 תוצאות מ-searchShipments ולדווח עליהן כסכום שגוי. תוקן בלבול בין operationalStatus ('נקלט במחסן') לבין deliveryStatus מ-Lionwheel
- הקשחת אבטחה: אימות הסוד ב-webhook של הלידים (/api/leads/webhook) עבר להשוואה בזמן-קבוע (timingSafeEqual) למניעת דליפת הסוד דרך הבדלי timing — השוואה זהה לוגית, ללא שינוי התנהגות עבור קוראים קיימים
- טופסי עריכת שליח ולקוח: כפתור 'בחר קבוצה' ליד שדה קבוצת ה-WhatsApp — בחירה מתוך רשימת הקבוצות הזמינות ב-Green API (כמו בטריגרי תפעול) במקום הקלדת chatId ידנית; הבחירה ממלאת אוטומטית גם את שם הקבוצה
- ניטור SSRF (log-only) ביעדים יוצאים: webhooks יוצאים, ספק AI חיצוני ולולאת סוכן ה-AI מתעדים כעת אזהרה כשכתובת היעד מצביעה ל-IP פנימי/שמור (loopback, RFC1918, link-local/metadata) — תיעוד בלבד, ללא חסימה וללא שינוי התנהגות, כהכנה לאכיפה עתידית
- הקשחת אבטחה: שדרוג Next.js ל-16.2.9 — סוגר סדרת חולשות אבטחה רשמיות בגרסאות 16.x, כולל עקיפת middleware (הרשאות), SSRF ו-DoS
- תיקון Permissions-Policy: מצלמה, מיקרופון ומיקום הותרו לאפליקציה עצמה (self) — עד כה ה-header חסם אותם גם לעמודי המערכת (סורק ברקוד, הקלטה קולית בצ'אט AI, מעקב שליח חי)
- נוסף header של Content-Security-Policy (frame-ancestors / object-src / base-uri) — משקף את מדיניות ה-framing הקיימת ומקשיח מפני XSS, ללא שינוי התנהגות
- שדרוג Anthropic SDK לגרסה מתוקנת (0.91.1) בעקבות advisory רשמי
- הגדרות → אינטגרציות: נוסף כפתור 'הסר את הסוד השמור' לשדה Webhook Secret — עד כה לא הייתה דרך למחוק סוד שמור דרך ה-UI (שדה ריק נשמר כ'ללא שינוי'), מה שחייב איפוס ידני ב-DB. כעת ניתן להסיר סוד webhook ולחזור ל-fail-open ישירות מהממשק
- פירוט מלא לפי סאב-שליח בדוחות מנהל: הסיכום לפי שליח (בפורטל המנהל ובדף השליח באדמין) מציג כעת לכל סאב בנפרד גם איסופים עסקיים (לפי עצירות) וחבילות נוספות, בנוסף למסירות/איסופים/החזרות — באותה סמנטיקה של הסיכום הכללי. עד כה איסופים עסקיים נבלעו בעמודת האיסופים וחבילות נוספות נבלעו בסכומים ללא ספירה
- פורטל מנהל שליחים: לחיצה על סאב-שליח ברשימת השליחים פותחת סיכום אישי מלא שלו (מסירות / איסופים / איסופים עסקיים / החזרות / חבילות נוספות + סה״כ), וטאב הביקורים מסונן להציג רק את הביקורים שלו — עם באנר ברור וכפתור 'הצג הכל' לחזרה לתצוגה המשותפת
v01.79.001חדשפורטל מנהלי שליחיםפורטל מנהל שליחים — יצירת חשבון והתחזות
מנהלי מערכת יכולים ליצור חשבון פורטל למנהל שליחים ישירות מדף השליח ללא Prisma Studio.
פרטים נוספים ↓הסתר ↑
מנהלי מערכת יכולים ליצור חשבון פורטל למנהל שליחים ישירות מדף השליח ללא Prisma Studio. כפתור 'צור חשבון פורטל' מופיע כשיש isManagerEnabled=true ואין עדיין חשבון: מזינים מייל, נוצר החשבון ונשלח מייל הגדרת סיסמה. לאחר שהמנהל מגדיר סיסמה, מופיע כפתור 'כניסה כמנהל' שמאפשר לאדמין להתחזות ולצפות בפורטל כמו המנהל. תוקן: עוזר AI כעת מציג תוכן הודעה מתוכנן ומבקש אישור לפני כל שליחת WhatsApp לשליח או לקוח — ללא אישור מפורש מהמשתמש אין שליחה אוטומטית. שיפור עיצוב פורטל מנהל שליחים: כרטיס גיבור עם סכום לתשלום בולט, כרטיסי סטטיסטיקה עם אייקונים ופירוט לפי שליח עם בר-יחסי, ביקורים מוצגים כקלפים במובייל.
v01.79.000חדשמשלוחים / מחסן / SNLminorתמונת מחסן — תצוגת עגלות לפי אזורי הפצה
נוספה תמיכה בחיפוש גלובלי לפי מספר משלוח פנימי (ID).
פרטים נוספים ↓הסתר ↑
נוספה תמיכה בחיפוש גלובלי לפי מספר משלוח פנימי (ID). עד כה ניתן היה לחפש רק לפי ברקוד — כעת חיפוש ב-6 ספרות ומעלה שהוא מספרי בלבד יתאים גם ל-ID הפנימי. דף חדש תחת לשונית משלוחים: תמונת מחסן. מציג את כל המשלוחים בסטטוס מחסן (IN_INVENTORY) מקובצים לפי אזור ההפצה של הישוב (מיפוי Lionwheel). כל אזור מוצג כ'עגלה' עם ספירת המשלוחים, אייקוני חבילה אינטראקטיביים, ממוצע ימי שהייה ותצבוע לפי עומס. לחיצה על עגלה פותחת כעת מודל עם שני חלקים: בראש — תמונת מצב עכשווית עם כל המשלוחים הנמצאים כרגע בעגלה (ממוין ישן לחדש, עם נקודת צבע לפי גיל), ובהמשך — היסטוריה שבועית עם אקורדיון לכל יום (נקלטו / יצאו להפצה). סדר העגלות ניתן לשינוי בגרירה ושחרור ונשמר פר-חשבון. כולל חיפוש ברקוד/שם/עיר, רענון אוטומטי כל 4 דקות ואפשרות הדפסה. בנוסף, נוסף אינבוקס לצ'אט לקוחות עסקיים (Green API) תחת מסרונים — רשימת שיחות עם חיפוש, חלון שיחה עם היסטוריה מלאה ואפשרות שליחה ידנית, בהמשך ישיר לתשתית ה-AI שכבר ענתה אוטומטית. תוקן: הערת AI בצ'אט WhatsApp נעלמה לאחר שניה — הבאנר כעת יציב (לא מושפע מ-pagination refresh) ומוצג לכל שיחה עם הערת AI שטרם טופלה. תוקן: API של תמונת מחסן סומן כ-force-dynamic למניעת cache של Next.js שגרם להצגת נתונים ישנים לאחר רפרש. תוקן: טולטיפ ממוצע ימי שהייה הציג רק שעה ללא תאריך — formatHe הופרד ל-toLocaleDateString + toLocaleTimeString לתצוגה אמינה של תאריך ושעה. שיפור אמינות סוכן WhatsApp: פרסינג JSON חזק יותר (מתמודד עם טקסט-פתיח לפני ה-JSON), הערת שגיאה בבאנר מציגה כעת את הסיבה האמיתית ולא הודעה גנרית. שיפור אמינות webhook SNL: נוסף fallback lookup לפי additionalData.snl_barcode — מבטיח שהמשלוח יימצא גם אם targetPartnerTaskId הוחלף בזמן שהיה ב-Lionwheel.
- כל אזור הפצה מוצג כעגלה עם אייקוני חבילה (hover = tooltip עם פרטי משלוח)
- תצבוע לפי עומס: ריק=אפור, 1-5=כחול, 6-15=כתום, 16+=אדום
- ממוצע ימי שהייה במחסן וחיווי משלוח ותיק ביותר
- לחיצה על עגלה: תמונת מצב עכשווית (כל המשלוחים כרגע, עם נקודת צבע לפי גיל) + היסטוריה שבועית
- גרירה ושחרור לסידור עגלות לפי מבנה המחסן — נשמר פר-חשבון
- חיפוש חי מדגיש עגלה רלוונטית ומעמעם את השאר
- אינבוקס לצ'אט לקוחות עסקיים (Green API): רשימת שיחות, חלון שיחה ושליחה ידנית — כעת מציג גם שיחות מאנשים לא מזוהים (מספר טלפון במקום שם לקוח), שם קבוצה אוטומטי, תמיכה ב-3 webhook types נוספים: הודעות יוצאות מהטלפון (role: staff, ירוק), הודעות יוצאות דרך API (שמירת idMessage), ועדכוני סטטוס מסירה (✓ sent / ✓✓ delivered / 🔵 read) — מוצגים ב-inbox בדומה ל-WhatsApp
- תוקן: סוכן WhatsApp שלח הודעת 'לא הצלחנו להבין' כשהמודל החזיר טקסט לפני ה-JSON — כעת נשלף ה-JSON הנכון תמיד
- תוקן: עגלות שמתחת לגובה המסך נחתכו ללא אפשרות גלילה — דף תמונת מחסן כעת גולל כראוי
- תוקן: אזור 19 הופיע עם משלוח אחד בלבד — נורמליזציה של regionCode (Lionwheel שולח 19 / '19 - אילת והערבה' לאותו אזור) אוחדה לאזור יחיד
- תוקן: פורטל לקוחות לא ניתן לגלילה במובייל — הלייאאוט השתמש ב-100vh שגדול מה-viewport הנראה כשסרגל הכתובת גלוי; הוחלף ל-svh
- חדש: פורטל מנהלי שליחים (/driver-portal) — כניסה ייעודית, דוח חודשי עם KPI cards, פירוט לפי סאב-שליח, טבלת ביקורים והתאמות ידניות
v01.78.001שיפורעוזר AI פנימי / שאלות ממתינותAI Nudge — תזכורת שאלות ממתינות בפתיחת שיחה
כשה-AI פותח שיחה חדשה ויש שאלה ממתינה לאישורך שטרם נענתה, הוא יזכיר אותה בסיום תשובתו הראשונה.
פרטים נוספים ↓הסתר ↑
כשה-AI פותח שיחה חדשה ויש שאלה ממתינה לאישורך שטרם נענתה, הוא יזכיר אותה בסיום תשובתו הראשונה. תוקן באג: הפרמטרים hasFailedVisitToday/hasDeliveredVisitToday לא הועברו לשאילתת המשלוחים בפועל — כך שחיפוש 'נכשלו היום' החזיר 50 המשלוחים האחרונים ללא סינון. בנוסף, כל ברקוד בצ'אט ה-AI הפך לניתן ללחיצה לפתיחת דראאר פרטי משלוח. מדבקות רב-חבילה מציגות badge עם מספר החבילה. ניתן לשנות רוחב פאנל ה-AI בשולחן עבודה — גרירת קצה הפאנל, הגדרה נשמרת. תוקן באג קריטי: כלי listDelayedShipments השתמש בשדה sendStatus (סטטוס שליחת WhatsApp) לסינון משלוחים 'שנמסרו' — sendStatus לא קשור כלל למסירה פיזית ולכן כל המשלוחים החזירו תוצאה שגויה. הסינון תוקן לשימוש ב-additionalData.status (סטטוס ספק ליונוויל) לפי ערכי COMPLETED/ROUNDTRIP_DELIVERED/CANCELED/FAILED/FINAL_FAILED. הבאג גרם ל-AI להציג עשרות משלוחים שנמסרו כ'מעוכבים 45 ימים'. תוקן: ה-AI לא הבין את הפיצ'ר 'יעד שבת' — שאל שאלות במקום לחפש. נוסף פילטר hasTargetShabbat ו-targetShabbat לכלי searchShipments, נוסף השדה targetShabbat לתוצאות, ונוספה הסבר ב-system prompt: יעד שבת הוא תיוג של משלוח שחייב להגיע לפני שבת ספציפית (שם פרשה). כעת 'כמה פתוחים על יעד שבת?' ישירות מריץ searchShipments({ hasTargetShabbat: true }).
v01.78.000חדשהגדרות AI / עוזר פנימי / פורטל לקוחות / עובדיםminorספקי AI מרובים לכל סוכן + מערכת מכסת שאלות
כל אחד משלושת סוכני ה-AI (WhatsApp, עוזר פנימי, פורטל לקוחות) תומך כעת בכל ספק — Anthropic, Google ו-OpenAI-compatible.
פרטים נוספים ↓הסתר ↑
כל אחד משלושת סוכני ה-AI (WhatsApp, עוזר פנימי, פורטל לקוחות) תומך כעת בכל ספק — Anthropic, Google ו-OpenAI-compatible. כשהטנאנט לא מגדיר מפתח משלו, המערכת משתמשת במפתח ה-Anthropic הכללי עם מגבלת 5 שאלות ליום. הצ'אט מציג פס התקדמות ונחסם כשהמכסה מוצתה. דף הגדרות AI עוצב מחדש לחלוטין — קטע נפרד לכל סוכן עם ניהול מפתח inline, נקודת סטטוס צבעונית ועיצוב ברור ואינטואיטיבי. נוסף גם הגדרת מדבקות ברמת החברה: ניתן להשבית ברקוד נפרד לכל חבילה (סיומת -1, -2...) — כל המשתמשים בחשבון ידפיסו ברקוד בסיסי ללא סיומת.
- כל ספק (Anthropic, Google Gemini, OpenAI-compatible) זמין לכל שלושת הסוכנים — ניתן לבחור ספק ומודל נפרד לכל סוכן
- מנוע אחיד (run-agent.ts) תומך בפרוטוקולי Anthropic SDK ו-OpenAI-compatible fetch כולל tool-calling מרובה תורות
- מכסת שאלות: 5 שאלות ביום כשמשתמשים במפתח המערכת — ללא מגבלה עם מפתח API מוגדר
- פס התקדמות בחלון הצ'אט מציג שאלות בשימוש/מגבלה; חסימת קלט ותיאור ברור כשהמכסה מוצתה
- דף הגדרות AI עודכן: בחירת ספק ומודל מלאה לעוזר הפנימי ולפורטל הלקוחות (לא רק מפתח API)
- סוכן פורטל הלקוחות תומך כעת בפתיחת פניות בירור (שאלות למחסן, צפי, אובדן, בחוסר, כלליות) ישירות מהצ'אט
- streaming אמיתי: תוצאות ה-AI מוצגות למשתמש token-by-token בזמן אמת — Anthropic דרך ה-streaming SDK, OpenAI-compatible דרך SSE
- הערות AI: הסוכן הפנימי יכול לשמור תובנות על משלוח (כשלים חוזרים, חריגות) בטבלה ייעודית — מוצגות בדף המשלוח ברצועה צהובה בולטת עם אפשרות העתקה
- sendWhatsappToDriverGroup: שליחה ממוקדת לקבוצת WhatsApp של שליח ספציפי; אם אין קבוצה מוגדרת — נפתח AdminTask לטיפול ידני אוטומטית
- עדכון סטטוס משלוח ע"י AI: confidence=certain מבצע מיד עם רשומה ב-AuditLog; confidence=low פותח AdminTask לבדיקה ידנית. כפתור 'בטל' בציר הזמן מאפשר ביטול כל שינוי AI בלחיצה אחת
- תובנות AI מצטברות (AiInsight): הסוכן בונה ידע ארוך-טווח על שליחים, לקוחות ומשלוחים — מוצג בדפי הישות ברצועה ענברית
- שאלות AI לאישור אנושי (AiPendingQuestion): הסוכן שומר שאלות כשיש חוסר ודאות; badge ב-FAB + פאנל תשובה עם כן/לא/דחה ישירות מהוידג'ט
- הנחיות מותאמות אישית לעוזר הפנימי: כל טנאנט יכול להגדיר טקסט חופשי שמוזרק ל-System Prompt — מגדיר כללים עסקיים ייחודיים (שעות, לקוחות מיוחדים, הגבלות) שהסוכן מכבד בכל שיחה
- AiLearningQueue — pipeline ללמידה מהודעות WhatsApp: אישורי/הכחשות מסירה נרשמות אוטומטית בתור לימוד; cron שעתי מנתח דפוסים לפי לקוח ומחלץ תובנות AI ארוכות-טווח
- התראות פרואקטיביות: cron פעמיים ביום מזהה משלוחים שחרגו מ-3+ ימים ושולח אוטומטית בקשת אישור מסירה ב-WhatsApp לנמען — תגובת הלקוח מעובדת על-ידי המערכת הקיימת
- כלי getDeliveryRiskScore לסוכן AI: מחשב ציון סיכון (low/medium/high/critical) לפי ימי חריגה, ניסיונות כושלים ונוכחות טלפון — מאפשר לסוכן לקבל החלטה מושכלת לגבי התערבות פרואקטיבית
- טופס עובד: הוספת סקשן שכר ותנאים — תעריף שעתי, שכר חודשי, סוג העסקה וקצובת נסיעות (יומי/חודשי). שדות אלה מאפשרים לחישוב שכר האוטומטי לפעול כראוי
v01.77.001שיפורהגדרות AIהגדרות AI פר-סוכן + תיקון retry על rate limit
כל אחד משלושת סוכני ה-AI (WhatsApp, עוזר פנימי, פורטל לקוחות) ניתן כעת להגדרה עם מפתח API נפרד.
פרטים נוספים ↓הסתר ↑
כל אחד משלושת סוכני ה-AI (WhatsApp, עוזר פנימי, פורטל לקוחות) ניתן כעת להגדרה עם מפתח API נפרד. בנוסף תוקנה בעיה שבה שגיאת 429 (rate limit) לא גרמה לניסיון חוזר — מה שגרם לכישלון מיידי של סוכן ה-WhatsApp כשהגיע למגבלת קצב של Google Gemini.
v01.77.000חדשפורטל לקוחות / מדבקותminorשיפורי פורטל לקוחות — הדפסה, כתובות, הערות
סדרת שיפורים לפורטל הלקוחות לפי בקשות משתמשים: הדפסת מדבקה מיידית לאחר יצירת משלוח, autocomplete לשדות עיר ורחוב, הצגת הערות על המדבקה, ואפשרות הדפסת 4 או 8 מדבקות בדף A4.
פרטים נוספים ↓הסתר ↑
סדרת שיפורים לפורטל הלקוחות לפי בקשות משתמשים: הדפסת מדבקה מיידית לאחר יצירת משלוח, autocomplete לשדות עיר ורחוב, הצגת הערות על המדבקה, ואפשרות הדפסת 4 או 8 מדבקות בדף A4.
- לאחר יצירת משלוח בפורטל — מוצג מסך הצלחה עם כפתור הדפסה מיידית (ללא חיפוש ידני)
- שדה עיר הפך ל-combobox עם ישובים מה-DB; שדה רחוב מציע השלמה אוטומטית מ-data.gov.il (debounce 300ms)
- הערות משלוח (הערת יעד + הערת משלוח) מוצגות כעת על המדבקה — מופעל כברירת מחדל
- הגדרות מדבקה: אפשרות להדפיס 4 מדבקות על A4 לאורך (2×2) או 8 מדבקות על A4 לרוחב (4×2) לגזירה עצמית
- אפשרות להוסיף עד 3 מספרי טלפון נוספים למשלוח ישירות מהפורטל
- עריכה וביטול משלוח בפורטל — זמין בלבד לפני שהנהג אסף את החבילה; לאחר עריכה מוצגת הנחיה להדפסת מדבקה חדשה
- משלוח עם מספר חבילות מדפיס מדבקה נפרדת לכל חבילה עם ברקוד ייחודי (למשל 12345678-1, 12345678-2); סריקה של ברקוד עם סיומת מזהה את המשלוח האב ורושמת את מספר החבילה ביומן
v01.76.000חדשמחסן / תפעולminorסריקות מחסן — קליטה ומיון חבילות
הוספת מערכת סריקות מחסן: עובד מחסן סורק ברקוד של חבילה כדי לסמן אותה כ"נקלט במחסן" (קליטה) או לשייך אותה לנהג (מיון).
פרטים נוספים ↓הסתר ↑
הוספת מערכת סריקות מחסן: עובד מחסן סורק ברקוד של חבילה כדי לסמן אותה כ"נקלט במחסן" (קליטה) או לשייך אותה לנהג (מיון). הממשק Web עובד עם סורק USB/Bluetooth שמזין ברקוד אוטומטית, כולל גם הקלדה ידנית. האפליקציה הניידת מאפשרת סריקה בקמרה עם רשימה מצטברת של פריטים שנסרקו ופידבק מיידי. בנוסף, בדף המשלוחים הוסף פילטר תאריך חדש: "תאריך קליטה במחסן" המציג רק משלוחים שנסרקו לתוך המחסן בטווח הנבחר; "תאריך קליטה" שונה שמו ל"תאריך איסוף" להבהרת המשמעות.
- דף Web חדש /shipments/scan עם שני מצבים: קליטה במחסן ומיון לנהג
- קלט אוטומטי מסורק USB/Bluetooth — מיקוד אוטומטי, Enter מפעיל סריקה
- מסך סריקה חדש באפליקציה הניידת (Admin) עם קמרה + בחירת נהג
- API endpoint POST /api/shipments/scan עם אימות tenant ורישום ביומן פעילות
- גישה מהירה מ-dashboard הניהולי במובייל
- פילטר תאריך חדש בדף המשלוחים: "תאריך קליטה במחסן" — מסנן לפי תאריך הסריקה הראשונה במחסן
- שינוי שם: "תאריך קליטה" ← "תאריך איסוף" (תאריך האיסוף מבית העסק)
- סריקת מיון עם נהג SNL — שידור אוטומטי לאחר כל סריקה עם תג סטטוס משדר/שובר על כל שורה
- סריקה בקמרה ב-Web — overlay מסך מלא עם מסגרת מרצדת, ניתוח ברקוד רציף ועצירה אוטומטית, תמיכה בפנס, עדיפות למצלמה אחורית בטאבלט
- תיקון: מיקוד אוטומטי בדף הסריקה לא חוסם יותר פוקוס על אלמנטים אחרים (חיפוש גלובלי, כפתורים וכו')
- שינוי מינוח: "אזור חלוקה" → "אזור הפצה" בכל ממשק המשתמש
- הכרזה קולית על אזור החלוקה בסריקת קליטה — הקול מבוסס שם ישוב (לא אזור ליונוויל), עם כפתור הפעלה/כיבוי ותג ויזואלי על כל שורה
- סריקת שיבוץ נהג: צליל אישור דיגיטלי על סריקה תקינה, וצליל אזעקה כאשר הנהג אינו מורשה לאזור החלוקה של המשלוח (טעות מיון בזמן אמת)
- שידור SNL — תמיכה במשימות איסוף ו-exchange (task_type): כתובות מוסחות בהתאם לסוג המשימה — נהג רואה רק מה שצריך, לא כתובת הלקוח האמיתית
- אבטחה: שדה כתובת המחסן (Warehouse Address) נשמר כעת בהגדרות אינטגרציה SNL ומשמש אוטומטית בשידורי איסוף ו-exchange
- תיקון אזהרת UX: ניתן כעת למחוק את ה-Webhook Secret בכרטיסיית שידורים בדף הנהג — כפתור 'מחק Secret' עם אינדיקציה ויזואלית לפני השמירה
- תיקון: webhook כשל מ-SNL עכשיו מסמן את ביקור המסירה כנכשל וכולל סיבת כשל בציר הזמן
- תיקון: עדכוני סטטוס מ-SNL נרשמים כעת ביומן הפעולות
- תיקון: מזהה יוצא של SNL לא נדרס יותר ע"י מזהה ליונוויל כאשר שניהם שידרו את אותו משלוח
- יומן סריקות בדף המשלוח — קטע חדש 'יומן סריקות' מציג את כל הסריקות: קליטה במחסן, שיוך שליח, וסריקות אפליקציית שליח (Lionwheel webhook)
v01.75.001תיקוןSNL / יומן פעולות / AIשידור SNL — תיקון יומן פעולות ועדכון ליונוויל
שידור SNL לא רשם רשומה ביומן הפעולות ולא דחף את סטטוס IN_TRANSFER לליונוויל.
פרטים נוספים ↓הסתר ↑
שידור SNL לא רשם רשומה ביומן הפעולות ולא דחף את סטטוס IN_TRANSFER לליונוויל. תוקן: כל שידור מוצלח רושם כעת רשומת 'שידור ל-SNL' עם מזהה הברקוד המוחזר מ-SNL, ומעדכן את ליונוויל לסטטוס 10 (בהעברה). בנוסף, תוקן סוכן ה-AI: כשמשתמש שואל על מספר משלוח מספרי (כגון 24955894) הסוכן מחפש אותו כברקוד ולא כמזהה פנימי.
v01.75.000חדשאינטגרציות / שידור משלוחיםminorשידור ישיר לש.נ.ל — אינטגרציה עם קבלן חיצוני
הוספת אינטגרציה ישירה עם ש.נ.ל פתח תקווה 13 לשידור משלוחים ללא תלות בליונוויל.
פרטים נוספים ↓הסתר ↑
הוספת אינטגרציה ישירה עם ש.נ.ל פתח תקווה 13 לשידור משלוחים ללא תלות בליונוויל. כאשר מאקצים נהג SNL למשלוח, המשלוח נשדר אוטומטית ל-API שלהם והסטטוס עובר להעברה. עדכוני סטטוס מ-SNL מתעדכנים גם אצלנו וגם בליונוויל. הוכחת מסירה (חתימה + שם חותם) נשמרת אוטומטית.
- שידור אוטומטי בעת שיוך נהג SNL — סטטוס עובר להעברה (IN_TRANSFER)
- Webhook endpoint /api/webhooks/snl/[tenantId] עם אימות HMAC-SHA256
- מיפוי מלא: imported → בהעברה, received_in_warehouse → נקלט במחסן, assigned_to_driver → יצא להפצה, in_transit → יצא לחלוקה (ACTIVE), delivered → הושלם, failed_attempt → נכשל; returned/cancelled לא משנים סטטוס פנימי — נשמרים רק ב-snl_status לצורכי שירות לקוחות
- תיקון: אימות Webhook timestamp מ-Unix שניות ל-Unix מילישניות (בהתאם לאישור מצוות ש.נ.ל)
- הוכחת מסירה: signature_url נשמר כ-ShipmentImage (pod/snl), signer_name ב-additionalData
- סינק סטטוס חזרה לליונוויל אחרי כל עדכון מ-SNL
- שדה providerCode בנהג — לסימון נהגי SNL
- תיקון קריטי: שידור כולל עכשיו את סכום הגביינא (cod_amount) מ-CodTracking — בעבר נשלח תמיד 0
- תיקון: עמודת destination_phone2 חסרה מטבלת cod_tracking — גרמה לשגיאת 500 בכל טעינת נתוני גוביינא לפי ברקוד
- עדכון מדיניות פרטיות: מילוי פרטי החברה (Shipnest, סירקין 5 בני ברק, יוסי סודרי), הוספת סעיף ייעודי לאפליקציית השליחים (מיקום, מצלמה, הקלטה, התראות)
- שיוך שליח באצווה מדף המשלוחים — בחירת מרובה + כפתור 'שיוך שליח' עם בחירת סוג משימה (מסירה/איסוף); אם הנהג הוא קבלן SNL, השידור לש.נ.ל מתבצע אוטומטית
v01.74.001חדשאינטגרציות / פורטל לקוחותאינטגרציה עם nopCommerce — פורטל לקוחות
הוספת תמיכה בחנויות nopCommerce בפורטל הלקוחות.
פרטים נוספים ↓הסתר ↑
הוספת תמיכה בחנויות nopCommerce בפורטל הלקוחות. הזמנות חדשות נמשכות כל 5 דקות, סטטוס ההזמנה מתעדכן אוטומטית עם מסירה.
v01.74.000חדשAI / פנימיminorעוזר AI פנימי — צ'אט חכם + תרחישי ETA וכישלון מסירה
עוזר AI פנימי לעובדי החברה עם שאלות בשפה טבעית.
פרטים נוספים ↓הסתר ↑
עוזר AI פנימי לעובדי החברה עם שאלות בשפה טבעית. תרחיש 1: AI שולח WhatsApp לשליח כשמשלוח מעוכב. תרחיש 2: שליח מדווח על כישלון → הודעה ללקוח העסקי + טיימר 30 דקות לסימון FAILED אוטומטי. תרחיש 3: שליחת בקשת אישור מסירה לנמען — תשובת 'כן' מסמנת DELIVERED אוטומטית.
- צ'אט SSE streaming בממשק היסטורי עם sidebar לניהול שיחות
- Shipment Agent עם 6 כלים: חיפוש, פרטים, טיימליין, ימי הפצה, פרטי לקוח, רשימת עיכובים
- היסטוריית שיחה מועברת ל-AI בכל הודעה — AI זוכר את ההקשר
- שמירת היסטוריית שיחות ב-DB (ai_chat_sessions / ai_chat_messages)
- הרשאות AI_CHAT_USE ו-AI_INSIGHTS_VIEW — OWNER/ADMIN/CS בלבד
- תרחיש 3: כפתור ממשק לבקשת אישור מסירה + AiDeliveryConfirmation + סיווג Haiku → DELIVERED אוטומטי
- שדות whatsappGroupChatId/Name בטופס עריכת שליח — Green API קבוצות נפרד מאינבוקס לקוחות
- תרחיש 4: כלי updateShipmentAddress — עדכון כתובת/טלפון מהצ'אט + סנכרון ליונוויל + הודעה לשליח
- כלל אבטחה: לעולם לא מזכיר 'קבלן' — תמיד 'שליח'
- עוזר AI פנימי הוסב לווידג'ט צף (FAB) בפינה שמאל-תחתית — נגיש מכל דף ללא ניווט נפרד
- הגדרות ספק AI: טוגל לכיבוי/הפעלת עוזר AI פנימי (settings.aiChat.enabled) — חוסם API + מציג מצב בממשק
- תיקון: כניסה עם Google נכשלה בניסיון ראשון עקב AdapterError — הסרת יצירת Account ידנית כפולה
- פורטל לקוח: הסרת מגבלת 10 מדבקות — הדפסת כמות בלתי מוגבלת (עד 250) כ-PDF אחד
- תיקון e-shop: הודעת שגיאה 401/403 הציגה HTML גולמי מה-API — כעת מוצגת הודעה נקייה ומובנת
- סוכן AI לקוחות עסקיים (Agent 3): ווידג'ט צף (FAB) בפורטל — שאילתות משלוחים בעברית טבעית
- Green API webhook: זיהוי הודעות נכנסות מלקוחות עסקיים — קבוצה (@g.us) לפי whatsappGroupChatId, פרטי (@c.us) לפי טלפון מנורמל
- Session שיחה מתמשך: session חי עד 24 שעות ממסרון אחרון, אחרת session חדש אוטומטית
- שדות whatsappGroupChatId/Name נוספו לטופס עריכת לקוח — קישור לקוח לקבוצת WhatsApp שלו
v01.73.000חדשWhatsApp / Inboxminorמענה אוטומטי WhatsApp — סוכן AI מטפל בהודעות נכנסות
הוספת מנוע סיווג AI שמטפל אוטומטית בכל הודעת WhatsApp נכנסת מלקוח קצה.
פרטים נוספים ↓הסתר ↑
הוספת מנוע סיווג AI שמטפל אוטומטית בכל הודעת WhatsApp נכנסת מלקוח קצה. הסוכן מסווג כל הודעה לאחת מ-4 קטגוריות ומחליט אם לענות אוטומטית, לעדכן שדות משלוח, או להעביר לטיפול אנושי.
- 4 קטגוריות סיווג: אישור/טריוויאלי, AI פותר, פנייה לשולח, הסלמה לאדם
- עדכון שדות משלוח אוטומטי: קומה, דירה, קוד כניסה, הוראות
- תגובה עם עיכוב אנושי (2-4 שניות) בתוך חלון 24 שעות
- טבלת WhatsappConversation חדשה לניהול סטטוס שיחות
- Inbox: החלפת tabs ל'לא טופל' / 'טופל' עם מדדי AI בזמן אמת
- תווית 🤖 על שיחות שטופלו, ובאנר הערה פנימית בקטגוריה 4
- שיחות שטופלו ע"י AI מסומנות אוטומטית כנקראות — רק הסלמות נשארות כלא נקרא
- תיקון קונפליקט ניתובים ב-/contracts שגרם למסך לבן לכלל המשתמשים — שינוי שם פרמטר הנתיב הציבורי מ-[token] ל-[id]
- אפשרות ידנית לסמן שיחה כ'טופל' דרך תפריט ימני-קליק, גם ללא מעורבות AI
- תיקון classifier: תגובות אוטומטיות/בוט של עסקים → קטגוריה 1 (לא הסלמה), הערות AI כעת בעברית
- classifier מכיר כעת את כל סטטוסי המשלוח ומשיב בהתאם — בוטל → פנייה לשולח, נמסר אך לא התקבל → הסלמה דחופה
- הגדרות ספק AI חדשות: בחירת Anthropic / Google Gemini / כלי תואם-OpenAI, הזנת מפתח API מוצפן, בחירת מודל
- תיקון: AI כעת תומך גם בעדכון רחוב (update_street) — נמנע ממצב שבו ה-AI הודיע על עדכון כתובת אך השינוי לא בוצע בפועל
- תיקון: תגובות אימוג'י (❤️, 👍 וכו') מעובדות כעת על ידי ה-AI — לפני כן נשארו כ'לא טופל' לנצח
- הסכמים: עמוד חתימה ציבורי (/contracts/[token]) — לקוח קצה קורא את ההסכם ב-HTML נקי וחותם דיגיטלית ללא צורך בהתחברות
- הסכמים: שדה 'איש קשר מטעם הלקוח' (contact person) — מופיע בסעיף 'לבין:' בהסכם ובקובץ PDF
- הסכמים: תבנית ברירת מחדל עם 30 סעיפי החוזה המלאים ונוסח 'הואיל ו...' — מאכלס את הטופס מיד בפתיחה
- מעקב משלוח: צפי מסירה מחושב רק לאחר האיסוף — לפני האיסוף מוצגת הודעה שהצפי יופיע מיד לאחר שהחבילה תאסף מהשולח
- תיקון קריטי AI: סוכן WhatsApp קיבל סטטוס אופרטיבי במקום סטטוס לוגיסטי — תוקן, כעת מועבר הסטטוס האמיתי (UNASSIGNED/ACTIVE וכו') שממנו הסוכן מבין באיזה שלב המשלוח
- תיקון AI: כשלקוח שולח כמה הודעות ברצף מהיר — כעת מעובד רק ה-session האחרון (במקום כל session בנפרד, שגרם לתגובה כפולה); פרומפט עודכן: שאלת 'יגיע היום?' כשטרם נאסף → תשובה ישירה ומפורשת
- שיפור מהימנות AI: בקריאות ל-Gemini שנכשלות עם 503 (עומס יתר) — retry עם 3 ניסיונות נוספים ו-exponential backoff (3s / 9s / 27s) במקום ניסיון יחיד
- הגדרת שעות פעילות בדף הגדרות AI: כשה-AI נכשל, נשלחת ללקוח הודעת fallback — בשעות פעילות: 'שיחתך הועברה לנציג'; מחוץ לשעות: 'נחזור אליך בתחילת יום העסקים הקרוב + שעות הפעילות'
- תיקון Inbox: פתיחת שיחה עכשיו גוללת לסמן 'הודעות חדשות' (100px מעל) כדי שנציגים יראו את ההודעה היוצאת האחרונה כהקשר — לפני כן גלל תמיד לתחתית ורקע השיחה נעלם מהתצוגה
- Inbox: ברקוד המשלוח מופיע בכותרת הצ'אט כלינק לחיץ — נציגים מזהים מיד במה לטפל גם כשהודעת ההתחלה אינה גלויה
v01.72.000חדשcrm/contractsminorשליחת הסכם — Send Dialog חדש עם התאמת מייל, CC, מעקב צפייה, תזכורות אוטומטיות, נעילה ו-WhatsApp
במקום 'אישור > שלח' של שורה אחת, הכפתור 'שלח לחתימה' פותח דיאלוג מלא בן 2 עמודות: בצד אחד תצוגת PDF חיה של ההסכם (endpoint חדש /api/contracts/[id]/preview-pdf), ובצד השני 4 סקציות הגדרה — הודעת המייל עם תחליפים ({name}, …
פרטים נוספים ↓הסתר ↑
במקום 'אישור > שלח' של שורה אחת, הכפתור 'שלח לחתימה' פותח דיאלוג מלא בן 2 עמודות: בצד אחד תצוגת PDF חיה של ההסכם (endpoint חדש /api/contracts/[id]/preview-pdf), ובצד השני 4 סקציות הגדרה — הודעת המייל עם תחליפים ({name}, {tenantName}, {contractNumber}) ל-subject ול-body, רשימת CC לעותק PDF החתום (מיילים נוספים שיקבלו את ההסכם החתום ברגע שייחתם), מעקב צפייה (מייל אוטומטי לסוכן ברגע שהלקוח פותח את הקישור — פעם אחת בלבד), ותזכורת אוטומטית + נעילה (cron יומי /api/cron/contract-reminders ששולח מייל חוזר אם לא נחתם תוך X ימים, ו-publicAccessExpiresAt שמחושב לפי autoLockDays). כל הגדרה נשמרת על ההסכם (Contract.customEmailSubject/Body/ccEmails/notifyOnView/notifyEmail/reminderEnabled/reminderDays/autoLockEnabled/autoLockDays) ונטענת מחדש בעת עריכה. כפתור 'פתח צ'אט WhatsApp' מוצג כשיש לליד טלפון — בונה wa.me link עם הודעה מוכנה, יפתח WhatsApp Web/App ויחסוך התקשרות. כל הזרימה נשמרת על-גבי כל מה שכבר עשינו (מותג-טננט, סעיפים ממוספרים, נספחים).
- Send Dialog 2-column עם תצוגת PDF חיה בצד אחד והגדרות בצד השני
- Subject + body עם תחליפים ({name}/{tenantName}/{contractNumber}) ניתנים לעריכה לפני שליחה
- CC לעותק של ה-PDF החתום — נוסף אוטומטית כ-BCC על מייל האישור
- התראת צפייה — מייל לסוכן ברגע שהלקוח פותח את הקישור (פעם אחת)
- תזכורת אוטומטית + נעילה — cron יומי, מייל ממותג עם hero 🔔, ו-publicAccessExpiresAt לפי autoLockDays
- כפתור פתיחת צ'אט WhatsApp עם הלקוח (wa.me + מספר ישראלי מומר לפורמט בינלאומי)
v01.71.000חדשcrm/leadsminorלידים — שדות הסמכה (כמות / משקלים / גדלים) כ-Selects עם bucket קנוני בכל המערכת
שלושת השדות 'כמות חודשית', 'טווח משקלים', 'טווח גדלים' עברו מ-input חופשי ל-Select עם רשימת ערכים סגורה — בכל המערכת: LeadForm הפנימי, ImportLeadsDialog, וטופס embed לאתר.
פרטים נוספים ↓הסתר ↑
שלושת השדות 'כמות חודשית', 'טווח משקלים', 'טווח גדלים' עברו מ-input חופשי ל-Select עם רשימת ערכים סגורה — בכל המערכת: LeadForm הפנימי, ImportLeadsDialog, וטופס embed לאתר. הערכים מוגדרים בקובץ משותף lead-options.ts: כמות חודשית (פחות מ-50 / 50-100 / 100-200 / 200-500 / יותר מ-500), משקלים (עד 5/5-10/10-20/יותר מ-20 ק״ג), גדלים (מעטפות / שקיות / קרטון עד 20×30×30 / קרטון 40×40×60). שינוי schema: monthlyExpectedQuantity עבר מ-Int? ל-String? (VARCHAR(100)) — הערך 'משוערת' זה לא מספר מדויק. POST /api/public/leads קיבל את שלושת השדות החדשים, ה-HTML snippet כולל אותם כ-selects עם ה-options מולאים, פרומט ה-AI מציין את הרשימות המדויקות (כולל הוראה לא לשנות גרשיים/רווחים), ודוגמת cURL מציגה ערכים אמיתיים. הסיבה: אמינות הנתונים — bucket תמיד יותר אמין ממספר חופשי שהמשתמש משער. גם משפר conversion על דפי נחיתה (לחיצה אחת במקום הקלדה).
- שלוש קבוצות bucket קנוניות, משותפות לפנים ולחוץ
- monthlyExpectedQuantity: Int → String (כי 'משוערת' זה לא מספר)
- POST /api/public/leads קיבל monthlyExpectedQuantity / weightRange / sizeRange
- HTML snippet בטופס embed כולל את שלושת ה-selects מולאים
- פרומט ה-AI מציג את הערכים בדיוק (גרשיים, רווחים, מקפים) ומבקש מה-AI לא לשנות
v01.70.002שיפורcrm/contractsעמוד חתימה — חזרה ללינק אחרי חתימה מציגה כעת מסך מקצועי עם הורדת PDF
כשלקוח לחץ שובה על קישור החתימה אחרי שכבר חתם, הוא ראה אותו מסך הצלחה גנרי בלי כפתור הורדה (ה-API לא החזיר את ה-URL של ה-PDF החתום על revisit).
פרטים נוספים ↓הסתר ↑
כשלקוח לחץ שובה על קישור החתימה אחרי שכבר חתם, הוא ראה אותו מסך הצלחה גנרי בלי כפתור הורדה (ה-API לא החזיר את ה-URL של ה-PDF החתום על revisit). עכשיו GET /api/public/contracts/[token] מחזיר signedPdfUrl, signedAt, signedByName ופרטי קשר של הטננט גם כשההסכם כבר נחתם, וה-/sign/[token] מציג מסך מעוצב חדש עם: Hero בצבע המותג, נתוני ההסכם (מספר, חותם, תאריך), כפתור הורדת PDF גדול, ושורת אמון עם פרטי הקשר של הטננט. הכותרת מתאימה עצמה — 'ההסכם נחתם בהצלחה' כשזה הרגע שאחרי חתימה, 'ההסכם כבר נחתם' כשזה revisit. שיפור על-פני חתימה ירוקה שמציגים מסך מינימלי בלי הורדת PDF.
v01.70.001שיפורcrm/emailsמיילים ללקוחות — עיצוב מותג-טננט (הצעה, שליחת הסכם, אישור חתימה)
שכתוב מלא של 3 המיילים שנשלחים ללקוחות חיצוניים: הצעת מחיר, שליחת הסכם לחתימה, ואישור חתימה.
פרטים נוספים ↓הסתר ↑
שכתוב מלא של 3 המיילים שנשלחים ללקוחות חיצוניים: הצעת מחיר, שליחת הסכם לחתימה, ואישור חתימה. במקום HTML פלפול כללי ש-'From' שלו 'Shipnest', המייל יוצא עכשיו בשם הטננט עצמו ('שיפינג ש.י.פ.' במקום 'Shipnest') והעיצוב מותאם פר-טננט: לוגו הטננט בכותרת (אם קיים), צבע המותג של הטננט ב-Hero panel, ופרטי הקשר של הטננט (טלפון, מייל, כתובת) כשורת אמון לפני קרדיט Shipnest קטן. כל מייל מציג Hero ייעודי (🧾 להצעה, 📝 להסכם, ✓ לאישור), כותרת ברורה, ברכה אישית עם שם הנמען, פרטי המסמך כ-facts grid, CTA גדול בצבע המותג, הסבר קצר על האקשן, והערה משפטית. תשתית משותפת renderBrandedEmail() מאחדת את כל ה-template. תאימות מלאה ל-Outlook + Gmail + Apple Mail (table-based, inline styles).
- 'From' header מציג את שם הטננט בולט במקום 'Shipnest' (גם בפרסום ב-inbox)
- צבע המותג של הטננט עובר ב-Hero ובכפתור CTA
- לוגו הטננט בכותרת (fallback: שם בצבע מותג)
- פרטי קשר של הטננט (טלפון/מייל/כתובת) כשורת אמון — חסר ב-2sign
- Hero panel ייעודי לכל סוג מייל עם icon רלוונטי
- תשתית renderBrandedEmail() משותפת — נוחה להוסיף עליה מיילים עתידיים
v01.70.000חדשcrm/contractsminorPDF הסכם — עיצוב משפטי-מקצועי + נספחים: ערבות אישית וביטוח
שכתוב מלא של ה-PDF של ההסכם כדי שייראה כמסמך משפטי רשמי.
פרטים נוספים ↓הסתר ↑
שכתוב מלא של ה-PDF של ההסכם כדי שייראה כמסמך משפטי רשמי. הדר נקי עם לוגו ממורכז + שם עסק + ע.מ. בכל עמוד, פתיח 'בין:/לבין:' עם פרטים מלאים של המוביל (משאבי הטננט) והלקוח, פסקת 'הואיל ו...' אופציונלית, וסעיפים ממוספרים אוטומטית (השורה הראשונה של כל פסקה ב-bold ככותרת). פוטר עם פרטי קשר ומספור דפים בכל עמוד. הוספת 3 נספחים: א' תעריפים מוסכמים בעמוד נפרד עם חתימה; ב' ערבות אישית (אם הוספו ערבים) עם נוסח קבוע + שדות מודבקים לכל ערב + שורת חתימה; ג' ביטוח חבילות (אם הופעל) עם סכומי כיסוי וחתימה. שדות חדשים בהסכם: preambleText, guaranteeText, insuranceText, insuranceEnabled, insuranceMonthlyAmount, insuranceCoverageAmount. מודל חדש ContractGuarantor (רבים פר-הסכם). ContractTemplate הורחב ב-preamble/guarantee/insurance כשדות תבנית. טופס יצירת/עריכת הסכם קיבל סקציות חדשות: 'הואיל ו...', רשימת ערבים עם הוסף/הסר, וטוגל ביטוח עם 2 שדות סכום.
- הדר נקי בכל עמוד עם לוגו + שם + ע.מ. ופוטר עם פרטי קשר ומספור
- פתיח 'בין:/לבין:' עם פרטים מלאים של המוביל והלקוח + ביטויי '(להלן: "...")'
- מספור אוטומטי של סעיפי תנאים — כל פסקה הופכת לסעיף עם השורה הראשונה ב-bold
- נספח א' תעריפים נפרד עם חתימת לקוח
- נספח ב' ערבות אישית — מודל חדש שתומך בריבוי ערבים, עם נוסח קבוע ושדות מודבקים
- נספח ג' ביטוח חבילות — עלות חודשית + תקרת כיסוי + שורת חתימה (אופציונלי)
- כל הנוסחים ניתנים לעריכה ברמת ContractTemplate
v01.69.003שיפורcrm/leadsטופס לאתר — הסבר מובנה למשתמש + פרומט מוכן ל-AI מפתח אתרים
ה-LeadEmbedDialog קיבל שתי שכבות חדשות שמיועדות למשתמש (לא מפתח): (1) Collapsible 'איך הטופס הזה עובד?' בראש הדיאלוג שמסביר במונחים יומיומיים מה זה עושה, איך זה זורם, מהן הגנות ה-anti-spam, ולמה הטוקן בטוח לשים באתר ציבו…
פרטים נוספים ↓הסתר ↑
ה-LeadEmbedDialog קיבל שתי שכבות חדשות שמיועדות למשתמש (לא מפתח): (1) Collapsible 'איך הטופס הזה עובד?' בראש הדיאלוג שמסביר במונחים יומיומיים מה זה עושה, איך זה זורם, מהן הגנות ה-anti-spam, ולמה הטוקן בטוח לשים באתר ציבורי. (2) Tab חדש 'פרומט ל-AI' (ברירת מחדל) — טקסט מפורט עם הטוקן וה-API URL מולאים אוטומטית, שהמשתמש מעתיק ומדביק בצ׳אט AI שמפתח לו את האתר (v0/Cursor/Claude/ChatGPT/Lovable) כדי לקבל טופס שמשתלב חזותית באתר במקום HTML גנרי. הפרומט כולל מבנה שדות, לוגיקת שליחה, טיפול בתגובות, חוויית משתמש מצופה, ו-CORS. סדר הטאבים: AI (מומלץ) → HTML מוכן → cURL. כפתור 'הסבר תיבת הצהובה' הישן הוסר (החליף אותו ה-Collapsible הכחול בראש).
v01.69.002שיפורcrm/leadsלידים — 'טופס לאתר' ו'ייבוא מ-Excel' עברו למודלים במקום דפים נפרדים
שני הכפתורים בסרגל הכלים של /leads (טופס לאתר, ייבוא מ-Excel) פתחו עד עכשיו דפים נפרדים (/leads/embed, /leads/import) שדרשו ניווט וחזרה — חוויה כבדה לשני flows קצרים.
פרטים נוספים ↓הסתר ↑
שני הכפתורים בסרגל הכלים של /leads (טופס לאתר, ייבוא מ-Excel) פתחו עד עכשיו דפים נפרדים (/leads/embed, /leads/import) שדרשו ניווט וחזרה — חוויה כבדה לשני flows קצרים. הם הומרו ל-Dialogs (מודלים) שנפתחים על דף הלידים, בהתאם לקונבנציית ה-Dialog של המערכת (header + flex-1 min-h-0 overflow-y-auto + footer). בייבוא: בסיום ייבוא מוצלח הטבלה מתרעננת אוטומטית. רכיבים חדשים: LeadEmbedDialog, ImportLeadsDialog. הדפים הנפרדים (/leads/embed/, /leads/import/) נמחקו.
v01.69.001שיפורcrm/leadsלידים — KPI Cards דחוסות יותר + הסרת 'המרות החודש'
כרטיסי ה-KPI מעל טבלת הלידים הוקטנו לחצי בערך: layout אופקי (מספר+תווית מימין, אייקון משמאל) במקום אנכי, padding מצומצם, טקסט קטן יותר, וגובה כולל פוחת מ-~110px ל-~50px.
פרטים נוספים ↓הסתר ↑
כרטיסי ה-KPI מעל טבלת הלידים הוקטנו לחצי בערך: layout אופקי (מספר+תווית מימין, אייקון משמאל) במקום אנכי, padding מצומצם, טקסט קטן יותר, וגובה כולל פוחת מ-~110px ל-~50px. הכרטיסיה 'המרות החודש' הוסרה (פידבק מהמשתמש — מיותרת). נשארו 4 כרטיסיות במקום 5: לידים פעילים, חדשים השבוע, מעקבים היום, באיחור.
v01.69.000חדשcrm/leadsminorלידים — תצוגת Kanban (לוח Pipeline) עם גרירה
דף חדש /leads/board עם תצוגת Kanban: 8 עמודות (סטטוסים), כרטיס פר ליד עם שם עסק, איש קשר, עיר, עדיפות, תאריך מעקב הבא (אדום אם באיחור), עד 3 תגיות, וכפתורי חיוג/WhatsApp/מייל.
פרטים נוספים ↓הסתר ↑
דף חדש /leads/board עם תצוגת Kanban: 8 עמודות (סטטוסים), כרטיס פר ליד עם שם עסק, איש קשר, עיר, עדיפות, תאריך מעקב הבא (אדום אם באיחור), עד 3 תגיות, וכפתורי חיוג/WhatsApp/מייל. גרירה ושחרור מעבירה ליד בין סטטוסים ומבצעת PATCH אוטומטי + יוצרת LeadActivity STATUS_CHANGE. עדכון אופטימי + rollback אם ה-API נכשל. כפתור 'תצוגת לוח' נוסף לסרגל הכלים של /leads ובחזרה כפתור 'תצוגת טבלה' ב-/board. מבוסס על רכיב ה-Kanban הקיים (dnd-kit) של המערכת. שלב 6 הושלם — Roadmap CRM-מלא-ללידים הושלם במלואו!
- Pipeline view ויזואלי לכל ה-funnel: 8 עמודות סטטוסים
- Drag-and-drop בין עמודות → עדכון סטטוס מיידי + activity log
- כרטיס לוח מציג עדיפות, תגיות, מעקב באיחור (אדום), כפתורי קשר
- Toggle דו-כיווני בין תצוגת טבלה ל-Pipeline
- מסיים את ה-Roadmap בן 6 השלבים שהתחיל היום — Shipnest = CRM מלא ללידים
v01.68.001חדשcrm/leadsלידים — צרופות (כרטיסי ביקור, הצעות מחיר ידניות, וכו')
בכרטיס פרטי ליד (LeadDetails) נוספה סקציית 'קבצים מצורפים' לפני היסטוריית הפעילויות.
פרטים נוספים ↓הסתר ↑
בכרטיס פרטי ליד (LeadDetails) נוספה סקציית 'קבצים מצורפים' לפני היסטוריית הפעילויות. ניתן להעלות עד 20MB פר קובץ, להוריד, ולמחוק. הקבצים נשמרים ב-Supabase Storage תחת leads/tenants/{tenantId}/{leadId}/. בעת העלאה נוצרת LeadActivity מסוג NOTE עם הסימן 📎 ושם הקובץ, כך שגם הציר הזמן מתעד מה צורף. נוסף model חדש LeadAttachment עם foreign keys ל-Lead ו-Tenant (CASCADE delete). 3 endpoints חדשים: GET /api/leads/[id]/attachments (LEADS_VIEW), POST /api/leads/[id]/attachments (LEADS_EDIT, multipart/form-data), DELETE /api/leads/[id]/attachments/[attachmentId] (LEADS_EDIT). Tenant scoping מלא + הגנת agent (רואה רק שלו). שלב 5.3 הושלם — שלב 5 (Growth) הושלם.
v01.68.000חדשcrm/leadsminorלידים — מחולל טופס embed לאתרי לקוחות
דף חדש /leads/embed שמספק טוקן פר-טננט וסניפט HTML מוכן להדבקה בכל אתר.
פרטים נוספים ↓הסתר ↑
דף חדש /leads/embed שמספק טוקן פר-טננט וסניפט HTML מוכן להדבקה בכל אתר. כל ליד שיישלח דרך הטופס נכנס ל-/leads עם status=NEW ומקור 'טופס באתר' או הדומיין הפונה. הזיהוי משתמש בטוקן ייעודי (לא ב-API keys הכלליים) — קל לסבב אם דלף. נוסף POST /api/public/leads (ללא auth, מוגדר עם CORS פתוח ו-rate limit 30/דקה), GET+POST /api/settings/lead-form-token (GET דורש LEADS_VIEW, POST דורש SETTINGS_MANAGE). זיהוי כפילויות פעיל כברירת מחדל + חלון 5 דקות אנטי-spam פר טלפון. מציג גם snippet cURL לחיבור Make/Zapier/n8n. כפתור 'טופס לאתר' נוסף לסרגל הכלים של /leads. שלב 5.2 הושלם.
- Snippet HTML מוכן להדבקה — JS וסטיילינג מוטמעים, ללא תלות בספרייה
- טוקן ייעודי פר-טננט הנשמר ב-Tenant.settings — סבב בלחיצה
- CORS פתוח על POST /api/public/leads — עובד מכל דומיין
- אנטי-spam: 5 דקות חלון פר טלפון + רשימת כפילויות מלאה (Phase 1.3)
- Tab נוסף עם cURL לחיבור אוטומציות (Make/Zapier/n8n)
v01.67.000חדשcrm/leadsminorלידים — ייבוא מ-Excel/CSV (עד 2000 שורות)
דף חדש /leads/import שמאפשר העלאת קובץ Excel (.xlsx/.xls) או CSV, זיהוי אוטומטי של עמודות לפי כותרות בעברית/אנגלית (שם, שם עסק, טלפון, מייל, עיר, סוג מוצר, טווח משקלים/גדלים, גוביינות, סוג הפצה, עדיפות, תגיות, מקור, הערו…
פרטים נוספים ↓הסתר ↑
דף חדש /leads/import שמאפשר העלאת קובץ Excel (.xlsx/.xls) או CSV, זיהוי אוטומטי של עמודות לפי כותרות בעברית/אנגלית (שם, שם עסק, טלפון, מייל, עיר, סוג מוצר, טווח משקלים/גדלים, גוביינות, סוג הפצה, עדיפות, תגיות, מקור, הערות, כמות חודשית), תצוגה מקדימה של 5 שורות עם בא׳ למיפוי כל עמודה, וצ׳קבוקס לאישור יצירת כפילויות. POST /api/leads/import מקבל עד 2000 לידים, בודק כפילויות לפי טלפון/מייל/שם עסק (אותם כללים כמו POST /api/leads), ומחזיר סיכום: נוצרו / דולגו (כפילויות) / שגיאות עם מספרי שורות. נורמליזציה אוטומטית של גוביינות (כן/לא/yes/no/1/0), סוג הפצה ועדיפות (לפי enum key או לייבל עברי), ותגיות (מופרדות בפסיק). כפתור 'ייבוא מ-Excel' נוסף לסרגל הכלים של /leads. שלב 5.1 הושלם.
- זיהוי אוטומטי של כותרות עברית/אנגלית — 15 שדות נתמכים
- תצוגה מקדימה לפני שליחה + עריכת מיפוי ידני
- שילוב מלא עם זיהוי הכפילויות (Phase 1.3) — בברירת מחדל מדלג
- סיכום מפורט בסוף: כמה נוצרו / דולגו / שגיאות עם מספרי שורות
- תקרה של 2000 שורות פר ייבוא + rate limit של 5 בקשות לדקה
v01.66.002שיפורportal / storesהוספת הסבר איתור URL לחיבור Shopify
בשלב הראשון של חיבור חנות Shopify נוסף הסבר קצר כיצד למצוא את כתובת ה-myshopify.com — מיועד למשתמשים לא טכנולוגיים שאינם מכירים את ה-URL הפנימי של החנות.
v01.66.001שיפורעריכת משלוח — חשיפת 3 שדות נוספים שמסתנכרנים לליונוויל
אחרי הבדיקה האמפירית של 82 שדות מול ליונוויל (ראה scripts/lionwheel-probe-results.json) זיהינו 3 שדות שעובדים בכתיבה בליונוויל אבל לא היו זמינים לעריכה אצלנו: מספר מסמך (document_number), אימייל נמען (destination_email),…
פרטים נוספים ↓הסתר ↑
אחרי הבדיקה האמפירית של 82 שדות מול ליונוויל (ראה scripts/lionwheel-probe-results.json) זיהינו 3 שדות שעובדים בכתיבה בליונוויל אבל לא היו זמינים לעריכה אצלנו: מספר מסמך (document_number), אימייל נמען (destination_email), וכפולה (is_roundtrip). שלושתם נוספו במלואם: (1) ל-Zod של PATCH /api/shipments/[id]; (2) ל-LIONWHEEL_TASK_FIELDS / LIONWHEEL_VISIT_FIELDS עם תוויות עבריות; (3) ל-Shipment TS type; (4) ל-UI: destinationEmail כשורת EditableField ב-DestinationCard ליד 'הערת יעד'; documentNumber כשורת EditableField בכרטיס פרטי המשלוח ליד 'הערה ארגונית'; isRoundtrip כ-Badge עם DropdownMenu (כן/לא) ליד 'השאר ליד הדלת'. בנוסף נוספה עמודה חדשה ל-DB: shipment.document_number VARCHAR(255) — לא הייתה קיימת קודם (השדה רק נשלח לליונוויל ביצירה ולא נשמר אצלנו).
v01.65.001חדשcrm/leadsלידים — תבניות WhatsApp מהשורה
כפתור ה-WhatsApp בעמודת הטלפון הפך ל-Dropdown: 'פתח צ׳אט ריק' + רשימה של כל תבניות ה-WhatsApp הפעילות מההגדרות.
פרטים נוספים ↓הסתר ↑
כפתור ה-WhatsApp בעמודת הטלפון הפך ל-Dropdown: 'פתח צ׳אט ריק' + רשימה של כל תבניות ה-WhatsApp הפעילות מההגדרות. בחירת תבנית פותחת wa.me?text=... עם הטקסט מולא אוטומטית והמשתנים {{name}}, {{businessName}}, {{contactName}}, {{city}}, {{agentName}}, {{phone}}, {{email}} מוחלפים בערכי הליד. נוסף endpoint חדש GET /api/leads/whatsapp-templates עם הרשאת LEADS_VIEW (להבדיל מ-/api/messages/templates שדורש SETTINGS_VIEW), מחזיר רק את גוף התבניות הפעילות בלי המטא-מידע של Meta Cloud API. אם אין תבניות מוגדרות — הכפתור פותח צ׳אט ריק כברירת מחדל (התנהגות זהה לקודם). שלב 3.2 הושלם — שלב 3 (Power) הושלם.
v01.65.000חדשcrm/leadsminorלידים — Bulk Actions (שינוי סטטוס/עדיפות/סוכן/מחיקה לרב-בחירה)
טבלת /leads קיבלה שורת בחירה ו-4 פעולות המוניות: שינוי סטטוס, שינוי עדיפות, הקצאת סוכן (לא לסוכנים), ומחיקה.
פרטים נוספים ↓הסתר ↑
טבלת /leads קיבלה שורת בחירה ו-4 פעולות המוניות: שינוי סטטוס, שינוי עדיפות, הקצאת סוכן (לא לסוכנים), ומחיקה. בחירה של 1+ שורות מציגה Bottom Dock עם הפעולות; לחיצה פותחת LeadsBulkActionDialog עם Select מתאים. כל פעולה רצה POST /api/leads/bulk עם תקרה של 500 לידים פר-בקשה. בעת שינוי סטטוס המוני נוצרת LeadActivity של STATUS_CHANGE אוטומטית לכל ליד שסטטוסו השתנה. הרשאות: סוכן רואה ויכול לעדכן רק לידים שלו, מחיקה דורשת LEADS_DELETE, הקצאת סוכן זמינה רק למנהלים. אחרי כל פעולה הטבלה + KPI + filter-options מתרעננים. שלב 3.1 הושלם.
- 4 פעולות המוניות: שינוי סטטוס, עדיפות, סוכן, מחיקה
- Endpoint יחיד /api/leads/bulk עם action+ids+value
- STATUS_CHANGE activity נוצר אוטומטית גם בעדכון המוני
- סוכן יכול לעדכן רק לידים שלו, הקצאת סוכן זמינה רק למנהלים
- תקרה של 500 לידים פר-בקשה
v01.64.001חדשcrm/leadsלידים — 5 כרטיסי KPI מעל הטבלה
נוספה שורת כרטיסים מעל טבלת /leads עם 5 מדדים: פעילים (סטטוסים שאינם CONVERTED/CLOSED_*), חדשים השבוע, מעקבים היום, באיחור (מעקב עבר + עדיין פעיל), והמרות החודש.
פרטים נוספים ↓הסתר ↑
נוספה שורת כרטיסים מעל טבלת /leads עם 5 מדדים: פעילים (סטטוסים שאינם CONVERTED/CLOSED_*), חדשים השבוע, מעקבים היום, באיחור (מעקב עבר + עדיין פעיל), והמרות החודש. 3 הכרטיסים האחרונים קליקיים — לחיצה על 'מעקבים היום' או 'באיחור' מחילה את פילטר 'מעקב הבא' המתאים; לחיצה על 'המרות החודש' מחילה statusFilter=CONVERTED. לחיצה שנייה מאפסת. הסטטיסטיקות נטענות מ-GET /api/leads/stats ומתרעננות אחרי יצירה/עריכה/מחיקת ליד. הסטטיסטיקות פר-טננט, וסוכן רואה רק את שלו. שלב 2.2 הושלם — שלב 2 (Visibility) הושלם. נדרש גם עדכון StatusFilter type שיכלול את כל 8 סטטוסי ה-enum (בעבר היו חסרים QUOTED/CONTRACT_*/CONVERTED).
v01.64.000חדשcrm/leadsminorלידים — סינון מתקדם (עדיפות, סוג הפצה, גוביינות, עיר, תגיות, מעקב הבא)
ה-FiltersPopover של דף /leads קיבל 6 קטגוריות סינון חדשות מעבר ל-status+agent הקיימים: עדיפות (HOT/WARM/COLD), סוג הפצה (4 הסוגים), גוביינות (כן/לא), עיר (multi-select מתוך הערים שיש בלידים בפועל), תגיות (multi-select מת…
פרטים נוספים ↓הסתר ↑
ה-FiltersPopover של דף /leads קיבל 6 קטגוריות סינון חדשות מעבר ל-status+agent הקיימים: עדיפות (HOT/WARM/COLD), סוג הפצה (4 הסוגים), גוביינות (כן/לא), עיר (multi-select מתוך הערים שיש בלידים בפועל), תגיות (multi-select מתוך כלל התגיות שיש), ו-'מעקב הבא' עם 4 קיצורים שימושיים: באיחור, היום, השבוע, ללא מעקב מוגדר. GET /api/leads תומך בפרמטרים החדשים: priority, distributionType, hasCod, city (חוזר), tag (חוזר), followUpStatus. נוסף endpoint חדש GET /api/leads/filter-options שמחזיר את רשימת הערים והתגיות הקיימות פר-טננט (כדי לאכלס את ה-Select). כל הסינונים פר-טננט וקובע אם מסנן לפי הרשאה (סוכן רואה רק שלו). שלב 2.1 מתוך roadmap CRM מלא ללידים.
- 6 קטגוריות סינון חדשות, חלקן multi-select
- סינון 'מעקב הבא' עם 4 קיצורים: באיחור, היום, השבוע, ללא
- Endpoint חדש /api/leads/filter-options שמאכלס דינמית עיר+תגיות
- סינון עיר ותגיות מתוך הערכים הקיימים בלידים — אין הצעות פנטום
- החיפוש החופשי הקיים (שם/עסק/עיר/טלפון/אימייל) משחק יחד עם הסינונים
v01.63.002חדשplatform/adminAdmin — בקשות שמות חלופיים: בחירה מרובה + אישור/דחייה bulk
בעמוד /admin/alias-requests נוספה עמודת checkbox (פעילה רק בטאב 'ממתינות') עם בחירת הכל בכותרת.
פרטים נוספים ↓הסתר ↑
בעמוד /admin/alias-requests נוספה עמודת checkbox (פעילה רק בטאב 'ממתינות') עם בחירת הכל בכותרת. כשמסומנת לפחות שורה אחת מופיע סרגל פעולות עם 'אשר את הנבחרים' / 'דחה את הנבחרים' / 'ביטול בחירה'. דיאלוג הסקירה נפתח במצב bulk עם שדה הערה אחד שמוחל על כל הנבחרים. ההפעלה רצה במקביל (Promise.allSettled), ה-toast מסכם הצליחו/נכשלו. הבחירה מתאפסת אוטומטית בהחלפת סטטוס. APPROVED/REJECTED נשארים read-only.
v01.63.001חדשcrm/leadsלידים — זיהוי כפילויות בעת יצירה
POST /api/leads בודק כפילויות פר-טננט לפי טלפון (התאמה מדויקת), אימייל ושם עסק (case-insensitive).
פרטים נוספים ↓הסתר ↑
POST /api/leads בודק כפילויות פר-טננט לפי טלפון (התאמה מדויקת), אימייל ושם עסק (case-insensitive). אם נמצאו עד 5 מועמדים — מחזיר 409 עם רשימת המועמדים (id/שם/עסק/טלפון/אימייל/עיר/סטטוס/תאריך יצירה/סוכן). הלקוח מציג דיאלוג DuplicateLeadDialog: 'נמצאו לידים דומים' עם כפתור 'פתח קיים' פר-מועמד שפותח את כרטיס הליד הקיים, ושני כפתורים תחתונים: 'צור ליד חדש בכל זאת' (שולח שוב עם confirmDuplicate=true) או 'ביטול' (חוזר לטופס). מונע יצירת לידים כפולים ביד או בייבוא עתידי. שלב 1.3 מתוך roadmap CRM מלא ללידים.
v01.62.002שיפורcrm/leadsלידים — עריכת סטטוס מהירה ישירות בשורה
ה-Badge של סטטוס בטבלת הלידים הפך לכפתור עם dropdown — לחיצה פותחת תפריט עם כל 8 הסטטוסים ובחירה מעדכנת את הליד בלי לפתוח את ה-form.
פרטים נוספים ↓הסתר ↑
ה-Badge של סטטוס בטבלת הלידים הפך לכפתור עם dropdown — לחיצה פותחת תפריט עם כל 8 הסטטוסים ובחירה מעדכנת את הליד בלי לפתוח את ה-form. עדכון אופטימי + rollback אם ה-PATCH נכשל + toast. כפתור ה-dropdown מוצג רק למשתמשים עם הרשאת LEADS_EDIT; לאחרים מוצג Badge רגיל. ה-PATCH endpoint כבר יוצר LeadActivity מסוג STATUS_CHANGE אוטומטית, כך שכל שינוי נשמר בטיימליין. רכיב חדש LeadStatusBadge מומלץ לשימוש חוזר. שלב 1.1 מתוך roadmap של 'CRM ללידים בלי מערכת חיצונית'.
v01.62.001שיפורcrm/pdfPDF הצעה / הסכם — תיקוני רינדור עברית ועיצוב סקציות
תיקון רינדור: react-pdf השמיט אותיות עברית סמוכות לרווח אסקי (תוספת ג…, חבילה נ…).
פרטים נוספים ↓הסתר ↑
תיקון רינדור: react-pdf השמיט אותיות עברית סמוכות לרווח אסקי (תוספת ג…, חבילה נ…). fixHe עודכן להחליף רווח בין שתי אותיות עבריות ב-NBSP — שומר את הרווח מפני re-ordering של מנוע הביידי בלי לפגוע במעבר השורות בטקסט מעורב. תווית 'משלוח כפול (החלפה)' שונתה ל'משלוח כפול והחלפה' (סוגריים בהקשר RTL גרמו ל-glyphs ליפול). עיצובית: כל סקציה (פרטי הלקוח / תעריפי בסיס / חבילות נוספות / הערות / תנאי שירות) קיבלה אותו טיפול של ה-info card — רקע SURFACE_50, פינות מעוגלות, ובורדר ימני בצבע מותג. שורת ה-info card עברה ל-RTL נכון (שם הנמען מימין, תוקף ההצעה משמאל). פונט עמודת הסכום במחירון עבר מ-700 ל-500 כך שלא 'צועק' מול שאר הטקסט.
v01.62.000חדשcrm/pricingminorמחירון — טירים מאוחדים לחבילות נוספות (החליפו 2 שדות נפרדים)
השדות 'חבילה נוספת במסירה' ו'חבילה נוספת באיסוף' אוחדו למערכת טירים אחת שחלה הן על מסירה והן על איסוף.
פרטים נוספים ↓הסתר ↑
השדות 'חבילה נוספת במסירה' ו'חבילה נוספת באיסוף' אוחדו למערכת טירים אחת שחלה הן על מסירה והן על איסוף. החבילה הראשונה תמיד בתעריף הבסיסי של האופרציה; מהשניה והלאה ניתן להגדיר טירים — כל טיר חל מ-fromIndex (2, 3, 4...) ואילך, עם בחירה בין 'כמו חבילה ראשונה' או 'מחיר אחר'. כך מתאפשרים שני דפוסים נפוצים שלא ניתן היה לבטא קודם: הנחה רק לחבילה השניה (חבילה 3 חוזרת לבסיס), או הנחה רציפה מהשניה והלאה. הטירים מסתנכרנים בין הצעת מחיר → הסכם → פרופיל מחירון של הלקוח ב-conversion ובעריכה ידנית, וגם ב-PDF, בעמוד ההצעה הציבורי, ובדיאלוג עריכת מחירון לקוח. השדות הישנים נשמרים ב-DB ל-backward compat אבל לא מוצגים בטפסים חדשים.
- מודל אחד לתעריפי חבילות נוספות במקום שני שדות נפרדים למסירה ולאיסוף
- טיר 'כמו חבילה ראשונה' או 'מחיר אחר' עם fromIndex (2, 3, 4...) — מאפשר 'הנחה רק על השניה' או 'הנחה רציפה'
- סינכרון מלא: הצעת מחיר → הסכם → פרופיל מחירון לקוח (יצירה אוטומטית ב-Convert + עריכה ידנית בדיאלוג)
- סקציית 'תעריפים נוספים' בדיאלוג מחירון לקוח: 'איסוף מבית העסק' ו'משלוח כפול / החלפה' זמינים גם לעריכה ידנית
- תצוגת טירים בעמודי הצעה / הסכם / עמוד ציבורי /q/[token] + ב-PDFs של ההצעה וההסכם
v01.61.000חדשcrm/leadsminorלידים — שדות פרופיל לקוח מורחב (עסק, עיר, סוג הפצה, מוצר, כמויות, גוביינות)
הרחבת ה-Lead model בשבעה שדות חדשים שמאפשרים לתעד את פרופיל המשלוחים של ליד מהרגע הראשון, ולא רק בשלב ההמרה ללקוח.
פרטים נוספים ↓הסתר ↑
הרחבת ה-Lead model בשבעה שדות חדשים שמאפשרים לתעד את פרופיל המשלוחים של ליד מהרגע הראשון, ולא רק בשלב ההמרה ללקוח. במקום השדה 'סכום צפוי' (expectedAmount) מופיע עכשיו 'כמות חודשית צפויה' (monthlyExpectedQuantity) — שינוי תפיסה ממדידת ערך כספי למדידת נפח. השדה הישן הוסר משכבת ה-app (העמודה expected_amount נשמרה ב-DB לתאימות לאחור). שדות חדשים: businessName (שם העסק), city (עיר), productType (סוג מוצר), weightRange (טווח משקלים), sizeRange (טווח גדלים), hasCod (האם יש גוביינות, boolean), distributionType (enum: ONGOING/ONE_TIME_PROJECT/OCCASIONAL_PROJECTS/SHABBAT_LEAFLETS). LeadForm קיבל סקציה חדשה 'פרופיל משלוחים' עם Switch ל-hasCod ו-Select ל-distributionType. LeadDetails מציג את כל השדות החדשים בסקציה נפרדת. טבלת /leads קיבלה 9 עמודות חדשות וסידור ברירת מחדל הגיוני: שם העסק → איש קשר → עיר → טלפון → אימייל → מקור → סוכן → סטטוס → תאריך קליטה → מעקב הבא → פעילויות → סוג הפצה → סוג מוצר → כמות חודשית → טווח משקלים → טווח גדלים → גוביינות. החיפוש הורחב לכלול businessName + city. ה-webhook /api/leads/webhook מקבל גם את השדות החדשים.
- שם העסק כעמודה ראשית (במקום שם איש קשר)
- כמות חודשית צפויה במקום סכום צפוי (משלוחים, לא ₪)
- סוג הפצה כ-enum: הפצה שוטפת / פרוייקט חד פעמי / פרוייקטים מזדמנים / עלוני שבת
- Boolean גוביינות (כן/לא) עם תצוגה כ-Badge בטבלה
- 9 עמודות חדשות בטבלה, סידור ברירת מחדל הגיוני, ניתן לשינוי על-ידי המשתמש
- חיפוש חופשי כולל גם שם העסק ועיר
v01.60.005שיפורcrm/leadsטבלת לידים — כפתורי חיוג / WhatsApp / מייל בכל שורה
האייקונים הדקורטיביים שהיו צמודים לטלפון ולמייל בטבלה הוחלפו בכפתורי פעולה אמיתיים.
פרטים נוספים ↓הסתר ↑
האייקונים הדקורטיביים שהיו צמודים לטלפון ולמייל בטבלה הוחלפו בכפתורי פעולה אמיתיים. בעמודת הטלפון: כפתור 'חיוג' (tel:{phone}) + כפתור WhatsApp (https://wa.me/{intl} בצבע emerald) שפותח שיחה רגילה בוואטסאפ של המשתמש מול הלקוח (לא צ'אט פנימי). מספרי טלפון ישראליים שמתחילים ב-0 מומרים אוטומטית ל-972. בעמודת המייל: כפתור 'שליחת מייל' (mailto:{email}) שפותח את לקוח המייל המוגדר. כל הכפתורים stopPropagation כדי לא לפתוח את כרטיס הליד בלחיצה. הכפתורים ממוקמים בצד הימני (start) של התא — כך הם מיושרים אנכית בין השורות ולא 'רוקדים' לפי אורך הטקסט.
v01.60.004תיקוןcrm/leadsלידים — תוויות עבריות לכל הסטטוסים
הסטטוסים QUOTED / CONTRACT_SENT / CONTRACT_SIGNED / CONVERTED (שנוספו עם CRM funnel) הופיעו באנגלית בטבלת הלידים, בפילטר הסטטוסים, בטופס עריכה, בכרטיס פרטי ליד וב-dashboard של סוכן — כי לא הוגדרו עבורם תוויות עבריות במפת…
פרטים נוספים ↓הסתר ↑
הסטטוסים QUOTED / CONTRACT_SENT / CONTRACT_SIGNED / CONVERTED (שנוספו עם CRM funnel) הופיעו באנגלית בטבלת הלידים, בפילטר הסטטוסים, בטופס עריכה, בכרטיס פרטי ליד וב-dashboard של סוכן — כי לא הוגדרו עבורם תוויות עבריות במפת LEAD_STATUS_LABELS. אוחדו 5 העתקים מקומיים של LEAD_STATUS_LABELS / LEAD_STATUS_COLORS למודול משותף יחיד (apps/web/app/(site)/leads/lead-status.ts) עם תוויות וצבעים לכל 8 סטטוסי ה-enum: 'הצעה נשלחה' / 'הסכם נשלח' / 'הסכם נחתם' / 'הומר ללקוח'.
v01.60.003תיקוןdelivery-times/settlement-name-alertsהתראות שמות לא מזוהים — שורה נעלמת מהטבלה אחרי שטננט הגיש בקשה
המשך מ-Phase G: כשטננט לחץ 'קישור לישוב' בטאב ההתראות, הבקשה הוגשה ל-/admin/alias-requests תקין, אבל ההתראה עצמה נשארה ב-status PENDING (כי רק admin יכול לסמן LINKED) — ולכן השורה לא נעלמה מהטבלה והטננט חשב שכלום לא קרה.
פרטים נוספים ↓הסתר ↑
המשך מ-Phase G: כשטננט לחץ 'קישור לישוב' בטאב ההתראות, הבקשה הוגשה ל-/admin/alias-requests תקין, אבל ההתראה עצמה נשארה ב-status PENDING (כי רק admin יכול לסמן LINKED) — ולכן השורה לא נעלמה מהטבלה והטננט חשב שכלום לא קרה. GET /api/delivery-times/settlement-name-alerts מסנן עכשיו החוצה כל alert שיש לו SettlementAliasRequest פעיל מאותו טננט. אם ה-admin ידחה את הבקשה, ההתראה תחזור להופיע. הסינון פר-טננט: טננט שיצר בקשה לא רואה את ה-alert, אבל טננטים אחרים כן.
v01.60.002שיפורcrm/leadsליצוב לידים — עמודת 'תאריך קליטה'
נוספה עמודה 'תאריך קליטה' לטבלת הלידים ב-/leads, ממופה לשדה createdAt שכבר היה מוחזר מה-API.
פרטים נוספים ↓הסתר ↑
נוספה עמודה 'תאריך קליטה' לטבלת הלידים ב-/leads, ממופה לשדה createdAt שכבר היה מוחזר מה-API. ממוקמת לפני 'מעקב הבא' כך ששני שדות התאריך מופיעים זה לצד זה. ניתן למיין לפי העמודה.
v01.60.001שיפורshipments/detailדף משלוח — כפתור קישורים להעתקה + טולטיפ על כפתור ההדפסה
בכרטיסיית 'זיהוי וסטטוס' (תצוגת desktop) נוסף כפתור חדש עם אייקון של קישור שפותח רשימה דומה לכפתור ההדפסה: 'לינק מעקב ללקוח' (/tracking/{public_id}) ו'לינק לעדכון פרטים' (/validate/{public_id}).
פרטים נוספים ↓הסתר ↑
בכרטיסיית 'זיהוי וסטטוס' (תצוגת desktop) נוסף כפתור חדש עם אייקון של קישור שפותח רשימה דומה לכפתור ההדפסה: 'לינק מעקב ללקוח' (/tracking/{public_id}) ו'לינק לעדכון פרטים' (/validate/{public_id}). לחיצה על כל פריט מעתיקה את ה-URL המלא ללוח עם toast. בנוסף, נוסף טולטיפ לכפתור ההדפסה ('הדפסה ושיתוף') — עד עכשיו היה היחיד באזור ללא טולטיפ בניגוד לשאר הכפתורים. שני הפריטים disabled כש-shipment.public_id חסר. נוסף שדה public_id ל-Shipment type ב-shipment-detail-types.ts.
v01.60.000חדשcrm/quotes,crm/contracts,crm/pricingminorהצעות מחיר: שליחה חוזרת, תוקף ברירת מחדל 14 ימים, 2 תעריפים חדשים, וכותרת משפטית
אוסף שינויים תוצרים ממשוב משתמש על דף הצעת מחיר ו-PDF: **(1) Resend tracking** — POST /api/quotes/[id]/send תומך עכשיו ב-resend מ-status SENT/VIEWED (לא רק DRAFT).
פרטים נוספים ↓הסתר ↑
אוסף שינויים תוצרים ממשוב משתמש על דף הצעת מחיר ו-PDF: **(1) Resend tracking** — POST /api/quotes/[id]/send תומך עכשיו ב-resend מ-status SENT/VIEWED (לא רק DRAFT). שדות חדשים ב-Quote: lastSentAt + sendCount. ב-resend ה-PDF והטוקן הקיימים נשמרים — רק המייל נשלח שוב. סטטוס לא משתנה (SENT נשאר SENT). באתר /quotes/[id]: כפתור 'שליחה חוזרת' מופיע ב-status=SENT/VIEWED עם confirm מעוצב; ב-timeline 'נשלחה' מציג 'X שליחות, אחרונה בתאריך Y' כש-sendCount>1. **(2) הצג כלקוח** — 'פתח כלקוח' → 'הצג כלקוח' (Eye icon במקום ExternalLink) ב-/quotes/[id] וב-/contracts/[id]. **(3) הסרת סיכום מיותר** — השורה 'סך X שירותים · סכום סה״כ' הוסרה מ-Rates card. **(4) תוקף ברירת מחדל** — QuoteForm.defaultValidUntil() מחזיר היום+14 ימים. **(5) Required rate fields** — כל שדות התעריפים חובה (גם 0 תקף, refine עם regex). QuoteForm + ContractForm קיבלו ולידציה חדשה. **(6) Legal disclaimer** — 'הצעה זו אינה מהווה התחייבות חוזית מצד הצדדים. הסכם מחייב טעון חתימה נפרדת.' מוצג בדף /quotes/[id] (כרטיס amber), בדף הציבורי /q/[token] (פס amber לפני footer), וב-PDF (View עם background amber-100 + border-right). **(7) שדות תעריף חדשים + 'החזרה' הוסר** — נוספו businessPickupRate ('איסוף מבית העסק') ו-roundtripRate ('משלוח כפול — החלפה'). הוסר returnRate מהטפסים החדשים (נשאר בסכמה לתאימות לאחור). מיגרציה הוסיפה business_pickup_rate + roundtrip_rate ל-customer_pricing_profile. RATE_LABELS עודכן ב-7 מקומות: QuoteForm, ContractForm, /quotes/[id], /contracts/[id], /q/[token] QuoteClient, render-quote-pdf, render-contract-pdf. Convert API מעדכן את שני הרייטים החדשים ב-profile. backfill: רשומות sent קיימות קיבלו sendCount=1, lastSentAt=sentAt.
- Resend מ-SENT/VIEWED עם sendCount + lastSentAt + תצוגה ב-timeline
- 'פתח כלקוח' → 'הצג כלקוח' (Eye icon)
- כל שדות התעריף בטופס = חובה; ברירת מחדל 0
- תוקף ברירת מחדל = היום + 14 ימים
- Legal disclaimer מופיע בדף, ב-/q/[token] וב-PDF
- 2 שדות תעריף חדשים: איסוף מבית העסק + משלוח כפול (החלפה); 'החזרה' הוסר מהטפסים
- הסרת סיכום 'X שירותים · סכום סה״כ' מהדף
v01.59.018שיפורsorting-errorsהתראת טעות מיון — שמות שדות ברורים יותר
בהתראת טעות מיון (SortingErrorAlertListener) שונו שני לייבלים כך שיהיה ברור שמדובר באזורי הפצה ולא באזורים גנריים: 'אזור שובץ' → 'אזור הפצה מאומת', ו'אזורים מורשים' → 'אזורי הפצה מורשים לשליח'.
v01.59.017תיקוןcrm/quotes,crm/contractsPDF הצעת מחיר/הסכם — 7 תיקוני עיצוב
תיקוני עיצוב מקבילים ב-render-quote-pdf ו-render-contract-pdf לפי משוב משתמש על ה-PDF הקודם.
פרטים נוספים ↓הסתר ↑
תיקוני עיצוב מקבילים ב-render-quote-pdf ו-render-contract-pdf לפי משוב משתמש על ה-PDF הקודם. **(1) Info card RTL נשבר** — האקסנט (border) היה בשמאל ובלוקי התוכן אובסטר alignItems=flex-start, מה שייצר 'תוקף ההצעה' בצד שמאל ו'מיועדת ל' בצד ימין הפוך מהציפייה. הוחלף ל-borderRightWidth + alignItems=flex-end → ה-accent בצד ימין (תחילת קריאה ב-RTL) ובלוקי המידע מיושרים ימינה. **(2) 'מיועדת ל-' → 'עבור'** — שמא ברורה ומקובלת יותר. **(3) NBSP נטוש לטובת RLM helper** — ה-NBSP הציר ב-'תוספת גוביינא' לקדם רחב מדי ודחף את הטקסט. הוחלף ב-fixHe(s) שמכניס U+200F (RLM mark) אחרי כל ASCII space — בלתי-נראה, רוחב 0, אבל גורם ל-bidi engine של react-pdf להתחיל RTL run חדש כך שהרווח לא נדחס. הוחל על כל ה-RATE_LABELS, KIND_LABELS, שמות נמענים, הערות, ותנאי שירות שמסופקים על-ידי המשתמש — כך גם 'כל מיני תנאים' שהיה מתרנדר כ'כלמיני תנאים' עכשיו נשמר. **(4) טיפוגרפיה דקה יותר** — title 22→19, brand sender 16→15, sectionTitle 12→11, table 10→9.5, number מ-700 ל-400, infoValue מ-700 ל-400. **(5) Footer border 3px brand → 0.5px BORDER_200** — היה דומיננטי מדי. הרקע השתנה ל-#fafafa עדין. **(6) Footer overlapping content** — הוסר ה-hack של Text מוסתר ב-position:absolute left:-9999 שהיה להחזיק את משתנה onBrand. במקום זאת onBrand נמחק מ-QuoteDocument (נכון רק ב-buildStyles closure). הוסף flexShrink ל-footerLeft/Right למניעת התנגשות. **(7) page numbers footer text קוצר** מ-'עמוד X מתוך Y' ל-'X / Y' — חוסך מקום. הוחל באופן זהה על PDF ההסכם.
- info card: accent עבר מצד שמאל לצד ימין (RTL)
- fixHe helper מתקן את ה-bidi collapse של רווחים ב-RTL — גם בלייבלים וגם בטקסט משתמש
- 'מיועדת ל' → 'עבור'
- טיפוגרפיה דקה יותר (כותרות וטבלאות)
- Footer: border עדין + מניעת התנגשויות
v01.59.016שיפורcrm/quotes,crm/contracts,customersהחלפת window.confirm() המכוער ב-ConfirmDialog המעוצב של המערכת
השתמשתי בטעות ב-native window.confirm() ב-9 מקומות ב-CRM לפעולות הרגישות (שליחת הצעה, שליחת הסכם לחתימה, מחיקת טיוטות, ביטול השעיה).
פרטים נוספים ↓הסתר ↑
השתמשתי בטעות ב-native window.confirm() ב-9 מקומות ב-CRM לפעולות הרגישות (שליחת הצעה, שליחת הסכם לחתימה, מחיקת טיוטות, ביטול השעיה). הוחלף ב-useConfirm hook מ-@/components/ui/confirm-dialog (כבר בשימוש ב-leads, customers, ועוד) — Dialog מעוצב עם icon variant + iconBgClass לפי סוג הפעולה, כותרת ארוכה יותר עם הסבר השלכות, ושמות אישור ספציפיים ('שלח', 'מחק' וכו'). וריאנטים שנקבעו: info (כחול) ל-שליחה/ביטול השעיה; danger (אדום) למחיקות.
- 9 confirm() native → ConfirmDialog מעוצב
- מקומות: /quotes/[id]+page, /contracts/[id]+page, /customers/[id] (handleUnsuspend)
- icons + variants לפי סוג הפעולה (Send/Trash2/CheckCircle2)
v01.59.015שיפורניסיון לסנכרון סטטוס גוביינא חזרה לליונוויל — בוטל
ניסיתי להוסיף סנכרון של מעבר ל-IN_SAFE/RETURNED בגוביינא חזרה לליונוויל.
פרטים נוספים ↓הסתר ↑
ניסיתי להוסיף סנכרון של מעבר ל-IN_SAFE/RETURNED בגוביינא חזרה לליונוויל. לאחר מימוש ובדיקה אמפירית מקיפה (כ-15 גרסאות body על PUT /tasks/{id}/update, ניסיון endpoints דדיקטיביים /cods/*, /tasks/{id}/cod/*, /money_collects/*, ועדכון ברמת /visits/{id}) — הוכח שה-API הציבורי של ליונוויל פשוט לא מקבל כתיבת סטטוס COD בשום צורה. ליונוויל מחזירה 'Saved Successfully' לכל body שולחים לה אבל מתעלמת בשקט מכל שדה COD שלא היא תיעדה (מה שהתבטא ב-cod.status נשאר 'unreturned' לאחר כל ניסיון, מאומת מול היומן בליונוויל). התיעוד הציבורי שלהם (github.com/lionwheel/api) חושף 14 endpoints בלבד ואף אחד לא קשור לכתיבת COD. בוטל מימוש הקריאה — אבל הגילוי מתועד למקרה שבעתיד תקבל ליונוויל תמיכת API ל-COD writes.
v01.59.014חדשcrm/quotes,crm/contractsעריכת טיוטות הצעת מחיר/הסכם — כפתור 'ערוך' בדף הפרטים
אחרי יצירת טיוטה לא הייתה דרך מ-UI לערוך אותה — אפשר היה רק לשלוח או למחוק.
פרטים נוספים ↓הסתר ↑
אחרי יצירת טיוטה לא הייתה דרך מ-UI לערוך אותה — אפשר היה רק לשלוח או למחוק. נוסף כפתור 'ערוך' (outline + Pencil icon) בדף /quotes/[id] ו-/contracts/[id] שמופיע כש-status=DRAFT (וקיימת הרשאת QUOTES_EDIT / CONTRACTS_EDIT). הכפתור פותח את אותו QuoteForm / ContractForm במצב עריכה — עם title='עריכת ...', submitLabel='שמירת שינויים', וערכי כל השדות pre-filled. המקור (ליד/לקוח/הצעה) ננעל ומוצג כ-Badge בלי X — שינוי מקור דורש מחיקת הטיוטה ויצירה חדשה, להגנה על integrity. שדות שניתן לערוך: בהצעה — תוקף, תעריפי בסיס, הערות, תנאי שירות snapshot. בהסכם — תקופה (effectiveFrom/To), autoRenew, תעריפי בסיס, תנאי שירות. Submit קורא ל-PATCH /api/quotes/[id] או PATCH /api/contracts/[id] בהתאמה (ה-endpoints כבר היו קיימים מפאזה 1/2), לא ל-POST. אחרי שמירה — load() מרענן את הדף, toast 'השינויים נשמרו'.
- כפתור 'ערוך' ב-/quotes/[id] ו-/contracts/[id] כשהסטטוס DRAFT
- QuoteForm + ContractForm תומכים ב-edit mode עם pre-fill מלא
- מקור (ליד/לקוח/הצעה) ננעל בעריכה ומוצג כ-Badge
- Submit מפנה ל-PATCH במקום POST
v01.59.013תיקוןcrm/contractsתיקון: יצירת הסכם נכשלה ב-Prisma — חסר @map בשדה actorType
POST /api/contracts נכשל ב-500 עם 'Invalid prisma.contractEvent.create() invocation: The column actorType does not exist in the current database.'.
פרטים נוספים ↓הסתר ↑
POST /api/contracts נכשל ב-500 עם 'Invalid prisma.contractEvent.create() invocation: The column actorType does not exist in the current database.'. בטבלת contract_events הטור actor_type כן קיים (לפי SQL ה-migration), אבל ב-schema.prisma שכחתי להוסיף @map("actor_type") לשדה actorType — אז Prisma ניסה לכתוב לטור בשם actorType מילולי שאינו קיים. הוחל ה-@map. שאר 3 הטבלאות (quotes, contracts, contract_templates) נבדקו ידנית מול ה-DB — הכל תקין שם.
- schema.prisma ContractEvent.actorType: הוספת @map("actor_type")
- אומת ש-actor_type כן קיים ב-DB; הבאג היה רק ב-ORM mapping
v01.59.012תיקוןshipments/messages,messaging/triggersאקורדיון מסרונים פר-משלוח: שליחות אוטומטיות מופיעות נכון + פלטפורמה אמיתית
באג שהתגלה במשלוח 25089988: הלקוחה קיבלה 3 הודעות (Meta task_created, Green-API delivery_failed, וגם שליחה ידנית), אך טבלת המסרונים בדף המשלוח הציגה שורה אחת בלבד עם timestamp שגוי ופלטפורמה Meta כשהשליחה האחרונה הייתה G…
פרטים נוספים ↓הסתר ↑
באג שהתגלה במשלוח 25089988: הלקוחה קיבלה 3 הודעות (Meta task_created, Green-API delivery_failed, וגם שליחה ידנית), אך טבלת המסרונים בדף המשלוח הציגה שורה אחת בלבד עם timestamp שגוי ופלטפורמה Meta כשהשליחה האחרונה הייתה Green-API. שלוש סיבות נמצאו: (1) sendTriggerMessage כתב את ה-id שחזר מ-Green-API (hex format כמו 3EB0...) לעמודה meta_message_id ב-whatsapp_message — מאכלס אותה גם בערוצים שאינם Meta. (2) ה-update על שורת ה-Shipment קבע metaMessageId רק כשהשליחה הייתה Meta, אך לא ניקה אותו כשהשליחה החדשה הייתה Green-API — לכן הערך הישן מה-task_created נשאר ו-UI חשב שזה Meta. (3) הטריגרים האוטומטיים לא כתבו כלל ל-AuditLog (entityType=shipment) שזה המקור שהאקורדיון קורא — לכן רק השליחה האחרונה הופיעה (מהיחס היחיד על שורת ה-Shipment, שנדרס בכל ירייה). תיקון: metaMessageId נכתב רק לערוצי Meta; ל-Green-API נשמר null. כל שליחה אוטומטית כותבת עכשיו audit log משלה עם autoTrigger=true בנתונים — האקורדיון מזהה ומציג עם triggerType ופלטפורמה אמיתיים, ומדלג על שורת ה-Shipment fallback כשיש כבר audit log אוטומטי.
v01.59.011תיקוןpublic/layoutsתיקון: עמודים ציבוריים /validate, /tracking, /t הוצגו עם header של 'מועדי הפצה'
שלושה עמודים ציבוריים שלא קשורים ל-/delivery-check ישבו תחת אותו route group (public) שה-layout שלו מציג header 'מועדי הפצה / בדיקת זמני משלוח' ו-footer 'שיפינג אנד שופינג'.
פרטים נוספים ↓הסתר ↑
שלושה עמודים ציבוריים שלא קשורים ל-/delivery-check ישבו תחת אותו route group (public) שה-layout שלו מציג header 'מועדי הפצה / בדיקת זמני משלוח' ו-footer 'שיפינג אנד שופינג'. ה-layout הזה נוצר במקור עבור /delivery-check בלבד, אבל ירש לעמוד עדכון פרטי מסירה (/validate/[id]), עמוד מעקב משלוח (/tracking/[id]) ועמוד משימת שליח (/t/[token]) — כל אחד מהם יש לו shell עצמאי משלו (min-h-screen, dir=rtl, header card משלו). הועברו ל-route group חדש (shipment-public) עם layout מינימלי שמחזיר children בלבד. נשמר הניתוב הציבורי דרך middleware (publicRoutes ב-middleware.ts כבר רושם את ה-URLs לפי prefix — route groups בסוגריים לא משפיעים על URL). תיקון בעקבות אותו דפוס שתוקן ל-/q ו-/sign ב-01.59.005.
- /validate/[id], /tracking/[id], /t/[token] יוצאים מ-(public) ל-(shipment-public)
- /delivery-check נשאר ב-(public) עם header 'מועדי הפצה' — שם זה רלוונטי
- metadata title נקי: 'עדכון פרטי מסירה | Shipnest' במקום '... | בדיקת זמני משלוח'
v01.59.010שיפורui/terminologyטרמינולוגיה: "מספר משלוח" → "מזהה משלוח"
אחידות בטרמינולוגיה — בכל מקום בממשק שבו הוצג שדה הברקוד של משלוח עם הלייבל "מספר משלוח" / "מס׳ משלוח" הוחלף ל"מזהה משלוח".
פרטים נוספים ↓הסתר ↑
אחידות בטרמינולוגיה — בכל מקום בממשק שבו הוצג שדה הברקוד של משלוח עם הלייבל "מספר משלוח" / "מס׳ משלוח" הוחלף ל"מזהה משלוח". כולל: כותרות בעמוד פרטי משלוח (admin ו-portal), label בטופס פתיחת tickets, placeholder בחיפוש משלוחים (QuickAssign, ShipmentBarcodeInput, OffsetDetails), label של משתנה {{barcode}} בעורך טמפלייטים, הגדרת מדבקת godex-50x50, ו-8 תבניות ברירת המחדל של הודעות WhatsApp ב-CUSTOM_TEMPLATES. מופעים שמשמעותם "כמות משלוחים" (לדוגמה {{shipment_count}}) לא שונו. הערה: tenants שכבר שמרו טמפלייט WhatsApp מותאם אישית ב-DB לא מושפעים — רק tenants חדשים שטרם הגדירו יקבלו את הניסוח החדש.
v01.59.009תיקוןcrm/contractsContractForm: בורר הצעת מחיר לא מצא הצעות SENT/VIEWED
בורר הצעות המחיר בטופס יצירת הסכם סינן ב-fetch לפי status=ACCEPTED בלבד, אבל ה-status הזה דורש פעולת אישור מפורשת שלא קיימת היום ב-UX (אין כפתור 'אשר' בעמוד ההצעה — היא נשארת SENT אחרי שליחה, ועוברת ל-VIEWED אוטומטית כשה…
פרטים נוספים ↓הסתר ↑
בורר הצעות המחיר בטופס יצירת הסכם סינן ב-fetch לפי status=ACCEPTED בלבד, אבל ה-status הזה דורש פעולת אישור מפורשת שלא קיימת היום ב-UX (אין כפתור 'אשר' בעמוד ההצעה — היא נשארת SENT אחרי שליחה, ועוברת ל-VIEWED אוטומטית כשהלקוח פותח את הקישור). התוצאה: כל ההצעות נסתרו, גם בחיפוש לפי מספר הצעה. הוסר ה-status filter מה-fetch; הקליינט מסנן עכשיו לכל הצעה שאינה DRAFT — מאפשר ליצור הסכם מהצעות ב-SENT, VIEWED, או ACCEPTED. ACCEPTED עדיין יוצג כשהוא קיים, אבל לא נדרש.
- בורר הצעת מחיר ב-ContractForm מציג עכשיו הצעות SENT/VIEWED/ACCEPTED (לא רק ACCEPTED)
v01.59.008תיקוןshared/SearchComboboxתיקון: SearchCombobox לא נסגר בלחיצה על הלייבל מעל האינפוט
ב-QuoteForm ו-ContractForm ה-SearchCombobox עטוף ב-FormControl, שמעביר id לאינפוט הפנימי, וה-FormLabel מקושר אליו דרך htmlFor.
פרטים נוספים ↓הסתר ↑
ב-QuoteForm ו-ContractForm ה-SearchCombobox עטוף ב-FormControl, שמעביר id לאינפוט הפנימי, וה-FormLabel מקושר אליו דרך htmlFor. כשלוחצים על הלייבל הדפדפן מפוקס אוטומטית את האינפוט (התנהגות סטנדרטית של label), וה-onFocus של SearchCombobox היה פותח שוב את הדרופדאון מיד אחרי שה-mousedown סגר אותו. הדרופדאון נשאר פתוח מנקודת המבט של המשתמש. ב-/credits לא הייתה הבעיה כי שם SearchCombobox משמש בלי FormControl ולא היה htmlFor. הוסף ref-flag justClosedByOutsideRef שמסומן ל-true כשהדרופדאון נסגר מ-mousedown בחוץ, ב-onFocus בודקים את הדגל ומדלגים על setOpen(true) למשך 200ms. אחרי החלון הזה ההתנהגות חוזרת לרגילה (לחיצה ישירה על האינפוט פותחת כרגיל).
- לחיצה על FormLabel/FieldLabel מעל SearchCombobox סוגרת את הדרופדאון נכון
- fallback של 200ms — לא משפיע על שימושים קיימים ב-/credits
v01.59.007תיקוןintegrations/meta-cloud-api,whatsapp/templatesתבניות Meta: שם ייחודי בכל הגשה — עוקף צינון מחיקה של 30 יום
סאבמיט של תבנית עברית טריה לאישור Meta נכשל ב-502 עם 'אי אפשר להוסיף תוכן חדש בעברית בזמן מחיקת התוכן הקיים — נסה שוב בעוד 3 שבועות'.
פרטים נוספים ↓הסתר ↑
סאבמיט של תבנית עברית טריה לאישור Meta נכשל ב-502 עם 'אי אפשר להוסיף תוכן חדש בעברית בזמן מחיקת התוכן הקיים — נסה שוב בעוד 3 שבועות'. הסיבה: slugifyToMetaName ייצר עבור שם עברי טהור hash דטרמיניסטי (template_<hash>) — כל ריצה חוזרת לאחר מחיקה הוציאה בדיוק אותו שם, ו-Meta אוכפת צינון של 30 יום על שם של תבנית שנמחקה. הוסף uniqueSuffix אופציונלי ל-slugifyToMetaName; הדיאלוג מייצר ערך טרי בכל פתיחה (Date.now().toString(36)) ושולח אותו ל-server מפורש כ-metaName; ה-route מוסיף defense-in-depth — אם metaName לא נשלח, מצרף סיומת זמן משלו. תוצאה: מחיקה של תבנית כדי לשפר ניסוח והגשתה מיד מחדש פשוט עובדת.
v01.59.006שיפורcrm/quotes,crm/contractsQuoteForm + ContractForm: בוררי ליד/לקוח/הצעת מחיר עם חיפוש (SearchCombobox)
טופס יצירת הצעת מחיר וטופס יצירת הסכם השתמשו ב-native Select dropdown לבחירת מקור (ליד / לקוח / הצעת מחיר ב-ContractForm) — בלי חיפוש, סקרול ארוך, חוויה גרועה כשיש עשרות לקוחות.
פרטים נוספים ↓הסתר ↑
טופס יצירת הצעת מחיר וטופס יצירת הסכם השתמשו ב-native Select dropdown לבחירת מקור (ליד / לקוח / הצעת מחיר ב-ContractForm) — בלי חיפוש, סקרול ארוך, חוויה גרועה כשיש עשרות לקוחות. הוחלף ב-SearchCombobox המשותף מ-@/components/shared/SearchCombobox (אותו רכיב ש-CreditNoteDialog משתמש בו ב-/credits): input חיפוש עם השלמה אוטומטית בזמן הקלדה, ניווט בחיצים, Enter לבחירה, Esc לסגירה, הצגת אייקון ותווית משני (אימייל/טלפון/סטטוס). כשפריט נבחר — הוא מוצג כ-Badge עם X לניקוי הבחירה. הוחל על: QuoteForm (לידים + לקוחות), ContractForm (הצעות מחיר + לידים + לקוחות). אייקונים צבועים פר-סוג: לידים violet, לקוחות blue, הצעות amber.
- QuoteForm: בורר ליד/לקוח עם חיפוש בזמן הקלדה (במקום dropdown סטטי)
- ContractForm: בורר הצעת מחיר/ליד/לקוח עם חיפוש
- בחירה מוצגת כ-Badge עם כפתור X לניקוי
- navigation עם חיצים + Enter + Esc (פונקציונליות SearchCombobox קיים)
v01.59.005שיפורcrm/quotes,crm/contracts,publicCRM: עיצוב PDF, דף הצעת מחיר ועמודים ציבוריים — 4 תיקונים + שיפורים
**(1) באג: עמודים ציבוריים /q ו-/sign הוצגו עם header של 'מועדי הפצה'** — שני הנתיבים היו תחת route group (public) שהלייאאוט שלו ייעודי ל-/delivery-check עם header 'מועדי הפצה' ו-footer של שיפינג אנד שופינג.
פרטים נוספים ↓הסתר ↑
**(1) באג: עמודים ציבוריים /q ו-/sign הוצגו עם header של 'מועדי הפצה'** — שני הנתיבים היו תחת route group (public) שהלייאאוט שלו ייעודי ל-/delivery-check עם header 'מועדי הפצה' ו-footer של שיפינג אנד שופינג. הועברו ל-route group חדש (brand) ללא chrome משותף, מה שמאפשר לעמוד ה-/q לרנדר את העיצוב הממותג שלו עם לוגו הטננט במלוא הרוחב. ה-(brand)/layout.tsx מינימלי + robots: noindex (לא רוצים שגוגל יקטלג קישורי הצעה ספציפיים). **(2) באג: 'תוספתגוביינא' בלי רווח ב-PDF** — בקומבינציית RTL 'ת' אחרי 'ת' אחרי רווח לפני 'ג', אלגוריתם bidi של react-pdf דחס את המילים. הוחלף ASCII space ב-NBSP (\u00A0) — מרכז את הרווח ממאפסי-bidi. הוחל בשני קבצי PDF (quote + contract). **(3) שיפור: עיצוב PDF הצעת מחיר** — נכתב מחדש מבסיס. header band בצבע מותג עם לוגו (data URI) או ראשי תיבות, כותרת גדולה עם אקסנט תחתון, info card עם תוקף, טבלת תעריפים עם header צבעוני וזברה, מקטעים עם border-right בצבע מותג, footer bar קבוע עם מספור עמודים. ה-send endpoint מביא את tenant.logoUrl + brandColor, מוריד את הלוגו ל-data URI (כשל בשקט אם לא נגיש). **(4) שיפור: דף /quotes/[id]** — header sticky עם פעולות, 2-column responsive grid (מובייל=1), כרטיסי נמען + ציר זמן עם חיוויי סטטוס (icons + colors), טבלת תעריפים hover-able עם סיכום של מספר שירותים + סה״כ, skeleton state אמיתי עם המבנה של הדף (לא טקסט 'טוען...'), empty state עם כפתור חזרה לרשימה.
- /q ו-/sign יוצאים מ-(public) ל-(brand) — נפטרים מ-header 'מועדי הפצה'
- 'תוספת גוביינא' עם NBSP — לא נדחס יותר ב-PDF (גם quote וגם contract)
- PDF הצעת מחיר עם header band בצבע מותג + לוגו + טבלת תעריפים זברה + footer bar
- /quotes/[id] redesign: sticky header, 2-col grid responsive, timeline visual, skeleton אמיתי
v01.59.004תיקוןcrm/quotes,crm/contractsתיקון: 'Cannot read properties of undefined (reading slice)' בשליחת הצעת מחיר/הסכם
@react-pdf/renderer.toBuffer() למרות שמו מחזיר NodeJS.ReadableStream ולא Buffer ב-version 4.5.x.
פרטים נוספים ↓הסתר ↑
@react-pdf/renderer.toBuffer() למרות שמו מחזיר NodeJS.ReadableStream ולא Buffer ב-version 4.5.x. ב-render-quote-pdf.tsx ו-render-contract-pdf.tsx החזרנו את ה-stream casted כ-Buffer, ואחרי כן ב-send endpoints ניסינו לקרוא ל-pdfBuffer.buffer.slice(...) — מה שזרק 500 (stream אין שדה .buffer). הקוד של רינדור label PDF הקיים כבר טיפל בזה דרך streamToBuffer helper. הוסף את אותו helper לשני קבצי ה-CRM PDF, ושני הרינדורים מחזירים עכשיו Buffer אמיתי. השליחה של quotes ו-contracts לחתימה אמורה לעבוד שוב.
- streamToBuffer helper מוסף ל-render-quote-pdf + render-contract-pdf
- POST /api/quotes/[id]/send חוזר לעבוד (יצירת PDF + העלאה ל-Supabase + מייל)
v01.59.003שיפורadmin/tenantsטופס עריכת חברה — 4 טאבים + header/footer קבועים בקונבנציה של המערכת
הטופס ב-/admin/tenants התרחב משמעותית עם הזמן (warehouse, crm_quotes, crm_contracts, signature provider, brand, billing, external) וגלש למעלה ממסך אחד.
פרטים נוספים ↓הסתר ↑
הטופס ב-/admin/tenants התרחב משמעותית עם הזמן (warehouse, crm_quotes, crm_contracts, signature provider, brand, billing, external) וגלש למעלה ממסך אחד. סודר לפי הקונבנציה של דיאלוגים ארוכים ב-CLAUDE.md: DialogContent עם max-w-2xl + flex flex-col max-h-[90vh] + overflow-hidden; DialogHeader עם shrink-0 (קבוע למעלה); הטופס נכנס לתוך div עם flex-1 min-h-0 overflow-y-auto (גולל באמצע); DialogFooter שיצאה החוצה מ-form עם shrink-0 (קבוע למטה), והכפתור 'שמור' עם form='tenant-form' מקושר ל-id של הטופס בפנים. נוספו 4 טאבים: 'כללי' (שם, slug, תוכנית, סטטוס, טלפון, ח.פ.), 'חיוב ומיתוג' (לוגו, אימייל חיוב, כתובת חיוב, צבע מותג — לוגו עבר לכאן מתחת ל-header), 'מודולים' (מחסן, הצעות מחיר, הסכמים, ספק חתימה — disabled עד שמירה ראשונה כדי שלא יבלבל בחברה חדשה), ו-'אינטגרציה' (מערכת חיצונית, API URL). כל TabsContent עם forceMount + data-[state=inactive]:hidden כדי שערכי השדות יישמרו בכל הטאבים גם כשטאב אחר פעיל (חשוב ל-react-hook-form כדי לא לאבד values בעת מעבר טאבים).
- header/footer קבועים + אמצע גולל לפי קונבנציית CLAUDE.md לדיאלוגים ארוכים
- 4 טאבים: כללי / חיוב ומיתוג / מודולים / אינטגרציה
- טאב 'מודולים' disabled בחברה חדשה — יתאפשר אחרי השמירה הראשונה
- forceMount על TabsContent — ערכי שדות נשמרים בעת מעבר בין טאבים
v01.59.002תיקוןadmin/tenantsתיקון: toggles המודולים ב-/admin/tenants נראו 'כבויים' אחרי שמירה
GET /api/tenants (רשימה) לא כלל את שדה settings ב-select, ולכן בפתיחת דיאלוג העריכה ה-tenant.settings היה undefined, וה-toggles (warehouse, crm_quotes, crm_contracts) ו-בוחר ספק החתימה התאתחלו אוטומטית ל-false/'internal'.
פרטים נוספים ↓הסתר ↑
GET /api/tenants (רשימה) לא כלל את שדה settings ב-select, ולכן בפתיחת דיאלוג העריכה ה-tenant.settings היה undefined, וה-toggles (warehouse, crm_quotes, crm_contracts) ו-בוחר ספק החתימה התאתחלו אוטומטית ל-false/'internal'. השמירה כן עבדה — ה-PUT נכנס לבסיס הנתונים — אבל בכל פתיחה הבאה הטופס דרס את הערך כי הוא התחיל מטעות. הוסף settings: true ל-select של GET /api/tenants/route.ts, וכעת ה-toggles נטענים נכון בפתיחה והשמירה משתמרת בין רענונים.
- settings נכלל ב-GET /api/tenants list — toggles של מודולים מסונכרנים נכון בפתיחת דיאלוג עריכה
v01.59.001שיפורcrm/docsתיעוד ציבורי ב-/docs/crm — הזרימה המלאה של ה-CRM מליד עד לקוח פעיל
פאזה 6 של CRM Funnel — Hardening: דף תיעוד ציבורי מלא ב-/docs/crm שמסביר את כל הזרימה לטננטים ולקוחותיהם.
פרטים נוספים ↓הסתר ↑
פאזה 6 של CRM Funnel — Hardening: דף תיעוד ציבורי מלא ב-/docs/crm שמסביר את כל הזרימה לטננטים ולקוחותיהם. **תוכן הדף:** סקירה כללית עם diagram טקסטואלי של המשפך, הוראות הפעלת המודולים מ-/admin/tenants, תיאור 4 השלבים (ליד → הצעת מחיר → הסכם → לקוח פעיל) עם סטטוסים וזרימות חיים, פירוט מה הלקוח רואה בעמודי /q/[token] ו-/sign/[token], הסבר משפטי על תוקף החתימה האלקטרונית הרגילה לפי חוק תשס״א-2001 והגדרת רמת ההתאמה (סטנדרטית מסחרית; לא מאושרת), זרימת המרה ללקוח עם פירוט הטרנזקציה האטומית (Customer + Pricing + Lead.CONVERTED + portal invite), זרימת השעיה (status=SUSPENDED + isActive=false + חסימה ב-3 מסלולי יצירה), הסבר על audit log (ContractEvent immutable), ותכניות עתידיות (Green Invoice adapter, ContractTemplate CRUD, per-contract provider override, addendums). **Navigation:** הדף נוסף לסיידבר ב-apps/web/app/docs/_components/nav-data.ts תחת קבוצת 'ניהול תפעולי' (אחרי 'לקוחות'), עם blurb תיאורי. **טכני:** ה-page משתמש בכל ה-primitives הסטנדרטיים של docs — DocsArticle, Prose, H2/H3 עם anchors, Callout (info), Bullets/Steps, ו-C ל-inline code (כולל יישור LTR אוטומטי). updated label: '1 ביוני 2026'.
- דף תיעוד מלא ב-/docs/crm — סקירה, הפעלה, 4 שלבים, חתימה, השעיה, audit
- הסבר משפטי על תוקף חתימה אלקטרונית רגילה לפי חוק תשס״א-2001
- פירוט הזרימה הטכנית של ההמרה האטומית (Customer + Pricing + portal invite)
- נוסף לסיידבר של /docs תחת 'ניהול תפעולי' עם blurb תיאורי
v01.59.000חדשdelivery-times/alias-requestsminorשמות חלופיים — מסלול בקשה לאישור אדמין + עמוד בקרה /admin/alias-requests
טננטים לא יוצרים יותר ישירות שמות חלופיים גלובליים — במקום זה הבקשה שלהם נכנסת לתור אישור ב-/admin/alias-requests.
פרטים נוספים ↓הסתר ↑
טננטים לא יוצרים יותר ישירות שמות חלופיים גלובליים — במקום זה הבקשה שלהם נכנסת לתור אישור ב-/admin/alias-requests. רק PLATFORM_ADMIN / SYSTEM_DEVELOPER יכולים לאשר/לדחות. אדמינים שמשתמשים באותה ה-UI של הטננט מקבלים אישור אוטומטי בלי לעבור דרך תור (זיהוי role בצד שרת).
- טבלה חדשה SettlementAliasRequest עם status (PENDING/APPROVED/REJECTED), tenantId, sourceName, targetSettlementId, unknownAlertId, requestedBy/At, reviewedBy/At/Notes, resultingAliasId/resultingMergeId
- POST /api/delivery-times/alias-requests — מבצע אוטומטית אם המבצע admin (יוצר alias + audit + מעדכן alert); אחרת PENDING
- GET /api/delivery-times/alias-requests — טננט רואה רק את הבקשות שלו
- GET /api/admin/alias-requests — אדמין רואה את כל הבקשות מכל הטננטים
- POST /api/admin/alias-requests/[id]/approve — יוצר alias + audit + מעדכן alert + מסמן APPROVED, בטרנזקציה אחת
- POST /api/admin/alias-requests/[id]/reject — REJECTED עם reviewNotes
- עמוד חדש /admin/alias-requests עם פילטרי סטטוס, חיפוש, דיאלוגי אישור/דחיה
- /delivery-times/settlement-name-alerts (handleLinkConfirm / handleCreateAndLink / handleBulkLink) עבר ל-/alias-requests — toast מבדיל בין 'קושר' (admin) ל'בקשה הוגשה לאישור' (tenant)
v01.58.003שיפורdelivery-times/settlement-name-alertsמיזוגי שמות חלופיים — תיעוד מי ביצע + הצגת השם בטאב המיזוגים
POST /api/delivery-times/settlement-merges מושך עכשיו את userId מ-session ושומר אותו ב-mergedById של רשומת ה-audit (היה null עד עכשיו).
פרטים נוספים ↓הסתר ↑
POST /api/delivery-times/settlement-merges מושך עכשיו את userId מ-session ושומר אותו ב-mergedById של רשומת ה-audit (היה null עד עכשיו). GET ה-merges מצרף בנוסף את firstName/lastName/email של המשתמש דרך db.user.findMany; הטאב 'מיזוגים אחרונים' מציג ע״י [שם המשתמש] מתחת לתאריך. מיזוגים היסטוריים (לפני התיקון) ימשיכו להציג רק את התאריך — אין דרך לשחזר מי עשה אותם.
v01.58.002חדשdrivers/linkedשליחים מקושרים — drill-down תשלום פר סאב-שליח
בדוח התשלום החודשי של מנהל, ליד הפירוט פר סאב-שליח (לביקורת), כעת ניתן ללחוץ על שורת סאב כדי לפתוח מודל עם הפירוט המלא של מה שהמנהל חייב לסאב הזה — לפי מחירון הקישור (לא מחירון המנהל).
פרטים נוספים ↓הסתר ↑
בדוח התשלום החודשי של מנהל, ליד הפירוט פר סאב-שליח (לביקורת), כעת ניתן ללחוץ על שורת סאב כדי לפתוח מודל עם הפירוט המלא של מה שהמנהל חייב לסאב הזה — לפי מחירון הקישור (לא מחירון המנהל). זה הכלי שהמנהל צריך להתחשבן עם הסאב פיזית: רואה כמה משלוחים מכל סוג (מסירה / איסוף / איסוף עסקי / החזרה / חבילות נוספות), טבלת ביקורים מלאה (תאריך, ברקוד, לקוח, יעד, חבילות, בסיס+תוספת, סה״כ), וסכום סופי לתשלום. המודל משתמש ב-computeManagerSubPayout הקיים (פאזה 1 של פיצ'ר השליחים המקושרים) דרך endpoint חדש /api/drivers/[id]/sub-drivers/[linkId]/payout.
- Endpoint חדש GET /api/drivers/[id]/sub-drivers/[linkId]/payout?from&to — tenant-scoped, מחזיר lines+totals באותה צורה כמו ה-earnings הראשי.
- SubPayoutDialog חדש — strip של 5 סיכומים (מסירות/איסופים/עסקי/החזרות/חבילות נוספות) + grand total סגול 'סה״כ לתשלום לסאב' + טבלה גוללת של כל הביקורים.
- EarningsCard: שורות הסאב ב-'פירוט פר סאב-שליח' הופכות כפתורים שלחיצה עליהם פותחת את המודל. שורת המנהל-עצמו נשארת לא-לחיצה (לא רלוונטי — אין לו linkId).
- טוען subLinks פעם אחת פר view של מנהל; lookup מ-subDriverId ל-linkId נעשה ב-client, בלי שינוי לתגובת ה-earnings הראשי.
- 924/924 בדיקות עוברות. typecheck + lint נקיים.
v01.58.001שיפורcrm/contracts/providersSignatureProvider abstraction — תשתית לספקי חתימה מרובים + stub ל-חשבונית ירוקה
פאזה 5 של CRM Funnel — abstraction של ספקי חתימה דיגיטלית כדי לאפשר adapters עתידיים מבלי לשנות את ה-flow הקיים.
פרטים נוספים ↓הסתר ↑
פאזה 5 של CRM Funnel — abstraction של ספקי חתימה דיגיטלית כדי לאפשר adapters עתידיים מבלי לשנות את ה-flow הקיים. **תשתית חדשה:** lib/integrations/signature/types.ts (interface SignatureProvider עם code/label/ready/description/sendForSignature; SendForSignatureInput/Result types), lib/integrations/signature/internal/index.ts (InternalSignatureProvider — עוטף את הלוגיקה הקיימת: randomBytes token, /sign/[token] URL, sendContractSignatureRequestEmail; ready=true), lib/integrations/signature/green-invoice/index.ts (STUB — ready=false, sendForSignature זורק שגיאה עם הודעה ברורה; כולל תיעוד הצעדים הנדרשים להפעלה: בירור מול Green Invoice support האם יש POST endpoint ל-Proposals Plus עם דורש-חתימה, מימוש sendForSignature, webhook ב-/api/webhooks/green-invoice/[tenantId], ו-fetchSignedDocument), lib/integrations/signature/registry.ts (getSignatureProvider() עם fallback ל-internal אם code לא קיים או ready=false; listSignatureProviders() ל-UI). **Helper:** lib/crm/signature-provider.ts — getSignatureProviderForTenant(tenantId) קורא מ-Tenant.settings.crm.signatureProvider; extractSignatureProviderCode() כעטיפה defensive. **Refactor:** POST /api/contracts/[id]/send לא יוצר token+מייל ישירות יותר — אלא קורא ל-getSignatureProviderForTenant(tenantId) ומאציל ל-provider.sendForSignature() את היצירה. ה-Contract.publicAccessToken מאוכלס מ-result.externalId (ל-internal — token; ל-external — provider's ID). ה-Contract.signatureProviderCode נשמר עם הקוד שבחר הספק; חוזר ל-internal אוטומטית אם בחירת ספק לא תקפה. **UI:** dropdown 'ספק חתימה דיגיטלית' ב-/admin/tenants tenant-form (מתחת ל-toggles של crm). מציע 'חתימה פנימית (Shipnest)' ו-'חשבונית ירוקה (בקרוב)' — האחרון disabled. הבחירה נשמרת ב-tenant.settings.crm.signatureProvider, ננעל כש-crm_contracts כבוי. **Behavior preserved:** המשתמש לא מבחין בשינוי — הזרימה זהה בדיוק לפאזה 2 (internal עוטף את אותה הלוגיקה). הבדל היחיד — Contract.signatureProviderCode עכשיו מאוכלס מתוך הספק שבחר במקום קבוע 'internal'.
- interface SignatureProvider + 2 adapters (internal ready, green-invoice stub)
- registry עם fallback בטוח ל-internal אם code לא תקף או provider לא ready
- Tenant settings.crm.signatureProvider + UI dropdown ב-/admin/tenants
- POST /api/contracts/[id]/send refactor — מאציל ל-provider במקום inline
- Green Invoice adapter מתועד עם הצעדים הנדרשים להפעלה (API + webhook + fetch signed)
v01.58.000חדשcrm/customers/suspensionminorהשעיית לקוחות — חסימת יצירת משלוחים, באנר אדום בכרטיס הלקוח ומייל אוטומטי
פאזה 4 של CRM Funnel — מעבר מ-isActive בוליאני לסטטוס מלא.
פרטים נוספים ↓הסתר ↑
פאזה 4 של CRM Funnel — מעבר מ-isActive בוליאני לסטטוס מלא. **API חדש:** POST /api/customers/[id]/suspend (דורש body.reason ≥5 תווים, מעדכן status=SUSPENDED + isActive=false + suspendedAt + suspendedById + suspendReason, שולח מייל ללקוח עם הסיבה — fire-and-forget) ו-POST /api/customers/[id]/unsuspend (משחזר ל-ACTIVE/isActive=true, מנקה שדות ההשעיה). שני ה-endpoints דורשים CUSTOMERS_EDIT. **חסימת יצירת משלוחים:** lib/shipments/create-shipment.ts (שגיאת 403 ברורה: 'הלקוח X מושעה כעת — לא ניתן ליצור משלוחים חדשים'), apps/web/app/api/v1/shipments/route.ts (Public API מחזיר 403 על customer SUSPENDED/ARCHIVED). שני המסלולים מוגנים — admin /create + portal /create (משתמשים ב-createShipmentForTenant) ו-v1 הציבורי. /api/customers/for-shipment-form כבר מסנן isActive=true, אז דרופדאון בחירת לקוח לא יציג מושעים. **Email:** lib/email.ts sendCustomerSuspendedEmail — מייל אדום עם הסיבה בתיבה הילייטד + הסבר שמשלוחים קיימים ימשיכו להיות מטופלים. **UI:** SuspendCustomerDialog (Dialog פשוט עם textarea לסיבה + אופציה לדלג על המייל). באנר אדום בולט בראש /customers/[id] כש-status=SUSPENDED — Ban icon, הסיבה ב-whitespace-pre-wrap, מתי הושעה, וכפתור 'בטל השעיה'. בסרגל הפעולות של כרטיס הלקוח — כפתור 'השעה' (אדום) כש-ACTIVE, או 'בטל השעיה' (ירוק) כש-SUSPENDED. הbadge בכותרת הוחלף ב-3-state (מושעה אדום / בארכיון אפור / פעיל ירוק). העמודה 'סטטוס' ברשימת הלקוחות הוחלפה בלוגיקה דומה. **תזרים:** isSuspended/isArchived computed מ-customer.status; כש-isSuspended=true גם כפתור 'השעה' מוסתר וגם הבאנר מוצג. handleUnsuspend עם confirm + router.refresh. הסטטוס נוסף ל-Customer type ב-_types.ts (LEAD/ACTIVE/SUSPENDED/ARCHIVED + suspendedAt/suspendedById/suspendReason).
- POST /api/customers/[id]/suspend + /unsuspend — דורש reason ≥5 תווים + מייל אוטומטי
- חסימת יצירת משלוחים ב-3 מסלולים: admin /api/shipments/create, portal, וגם v1 הציבורי
- באנר אדום בולט בראש כרטיס לקוח מושעה — Ban icon + הסיבה + תאריך + כפתור ביטול השעיה
- מייל ללקוח עם הסיבה והסבר שמשלוחים קיימים ימשיכו (אופציה לדלג ב-checkbox)
- Badge ב-3 מצבים (פעיל/מושעה/בארכיון) בכותרת + ברשימת הלקוחות
- סינון for-shipment-form (isActive=true) מסיר אוטומטית לקוחות מושעים מהדרופדאון
v01.57.000חדשdelivery-times/settlement-name-alertsminorעמוד שמות לא מזוהים — איחוד ל-4 טאבים: התראות / שמות חלופיים / מיזוגים / דאשבורד
ה-/delivery-times/settlement-name-alerts הקיים מוחלף בעמוד מאוחד עם 4 טאבים שמתאחדים מקודם בכמה דיאלוגים ועמודים נפרדים.
פרטים נוספים ↓הסתר ↑
ה-/delivery-times/settlement-name-alerts הקיים מוחלף בעמוד מאוחד עם 4 טאבים שמתאחדים מקודם בכמה דיאלוגים ועמודים נפרדים. (1) טאב 'התראות' — תוכן ה-page הקיים עם search/bulk/link/ignore, חוץ ממסלול ה-link שעבר ל-/api/delivery-times/settlement-merges (מבוצע באטומיות במקום ב-2 קריאות, כותב audit ל-undo). (2) טאב חדש 'שמות חלופיים' — רשימה של כל ה-aliases עם חיפוש, עריכה inline (PATCH), מחיקה, badge לפי מקור. (3) טאב חדש 'מיזוגים אחרונים' — audit log של כל המיזוגים (handleMergeSettlement, performBulkLink, ו-link-from-alert) עם כפתור 'ביטול' לכל אחד. checkbox 'הצג גם מיזוגים שבוטלו'. (4) טאב חדש 'דאשבורד' — סטטיסטיקות: ישובים פעילים, סה״כ aliases, התראות ממתינות, מיזוגים השבוע, פילוח aliases לפי מקור עם bars יחסיים, ועוד.
- טבלה חדשה SettlementMergeAudit (settlement_merge_audit) ל-3 שדות מסלול-מיזוג: sourceName, sourceSettlementId, targetSettlementId, אנדקס ל-undoneAt
- API חדש: POST /api/delivery-times/settlement-merges שמבצע alias-create + soft-delete + audit-log בטרנזקציה אחת
- API חדש: POST /api/delivery-times/settlement-merges/[id]/undo שמשחזר את הישוב, מוחק את ה-alias, וב-PENDING את alert אם היה
- API חדש: GET /api/delivery-times/settlement-stats לפיד הדאשבורד
- כל ה-flows הקיימים של merge/link עברו ל-endpoint החדש — עוקבים אחרי כל פעולה ב-DB ומאפשרים ביטול
v01.56.000חדשcrm/conversionminorהמרה ללקוח פעיל — סגירת המשפך: הסכם חתום ⇒ Customer + מחירון + פורטל
פאזה 3 של CRM Funnel — endpoint יחיד שסוגר את כל המשפך: הסכם חתום ⇒ לקוח פעיל עם מחירון מוטמע, עם אפשרות לפתוח גם משתמש פורטל באותה פעולה.
פרטים נוספים ↓הסתר ↑
פאזה 3 של CRM Funnel — endpoint יחיד שסוגר את כל המשפך: הסכם חתום ⇒ לקוח פעיל עם מחירון מוטמע, עם אפשרות לפתוח גם משתמש פורטל באותה פעולה. **API חדש:** POST /api/contracts/[id]/convert — מקבל body עם name/email/phone/address/parentCustomerId/createPortalUser/portalFirstName/portalLastName. דורש שני permissions (CONTRACTS_EDIT + CUSTOMERS_CREATE) ו-feature flag crm_contracts. רץ ב-db.$transaction אטומית: (1) Customer.create אם לא קיים, או update עם status=ACTIVE אם קיים; (2) CustomerPricingProfile.upsert עם 7 שדות התעריפים מ-contract.baseRates באגורות (מקביל מלא ל-schema הקיים); (3) CustomerPricingRule.create לכל customRule מההסכם (אם יש), עם kind/mode/amount/label/validFrom/validTo/createdById; (4) Contract.update → ACTIVE + customerId, ContractEvent.create({type: ACTIVATED, actor, metadata: createdNewCustomer flag}); (5) Lead.status → CONVERTED + convertedCustomerId + LeadActivity של STATUS_CHANGE עם תיאור 'הליד הומר ללקוח X בעקבות חתימת הסכם Y'. **CustomerUser invite:** אם createPortalUser=true והאימייל תקף — מחוץ לטרנזקציה (כדי לא להכשיל את ה-conversion על שגיאת מייל): createCustomerUserInvite (asOwner) עם permissions מלאות, sendInviteEmail עם portal/accept-invite/[token] link, וההזמנה מוחזרת ב-response. שגיאה במייל לא מבטלת את ה-conversion (try/catch + console.error). **UI:** [ConvertToCustomerDialog.tsx](apps/web/app/(site)/contracts/components/ConvertToCustomerDialog.tsx) — דיאלוג בן שתי סקציות: 'פרטי הלקוח' (שם/אימייל/טלפון/כתובת/parent customer dropdown) עם pre-fill אוטומטי מ-signedByName/signedByEmail של ההסכם, ו-'גישה לפורטל הלקוח' (checkbox + שם פרטי/משפחה כש-checkbox מסומן). validation: שם חובה (Zod min 2), אימייל חובה אם portal user. בדף /contracts/[id]: כפתור 'המר ללקוח פעיל' מוצג רק כש-status=SIGNED ו-canConvert (שני permissions). כשהסטטוס ACTIVE — כפתור 'לכרטיס הלקוח' שמנווט ל-/customers/[customerId]. **Status flow מלא:** ליד NEW → IN_TREATMENT → QUOTED (פאזה 1) → CONTRACT_SENT → CONTRACT_SIGNED (פאזה 2) → **CONVERTED** (פאזה 3, עם convertedCustomerId). הסכם DRAFT → SENT_FOR_SIGNATURE → VIEWED → SIGNED → **ACTIVE** (פאזה 3).
- Endpoint יחיד POST /api/contracts/[id]/convert סוגר את כל המשפך באטומית
- יצירת Customer + CustomerPricingProfile + CustomerPricingRules עם נתוני ההסכם
- אופציה לפתוח CustomerUser ראשון (owner) עם invite לפורטל בלחיצה אחת
- Lead מקבל CONVERTED + convertedCustomerId + LeadActivity של ההמרה
- Contract → ACTIVE + ContractEvent ACTIVATED עם metadata (קיים/חדש)
- טרנזקציה אטומית — הכל נשמר ביחד או כלום; שגיאת מייל לא חוסמת את ההמרה
- Dialog ב-/contracts/[id] עם pre-fill מ-signedByName/Email + parent customer dropdown
- כפתור 'לכרטיס הלקוח' מופיע ב-status=ACTIVE לניווט מהיר
v01.55.000חדשcrm/contractsminorהסכמים וחתימה דיגיטלית — מודול חדש: יצירה מהצעת מחיר, שליחה לחתימה, signature pad, ת״ז חובה ואודיט משפטי
פאזה 2 של CRM Funnel — מודול הסכמים end-to-end עם חתימה אלקטרונית רגילה תקפה משפטית בישראל (חוק חתימה אלקטרונית, תשס״א-2001).
פרטים נוספים ↓הסתר ↑
פאזה 2 של CRM Funnel — מודול הסכמים end-to-end עם חתימה אלקטרונית רגילה תקפה משפטית בישראל (חוק חתימה אלקטרונית, תשס״א-2001). **API:** GET/POST /api/contracts (רשימה+יצירה — מקבל quoteId/leadId/customerId, יוצר ContractEvent של CREATED), GET/PATCH/DELETE /api/contracts/[id] (עריכה ומחיקה רק ב-DRAFT), POST /api/contracts/[id]/send (מפיק PDF טיוטה, מעלה ל-Supabase, שולח מייל עם signing link, מסמן SENT_FOR_SIGNATURE, מקדם ליד מקושר ל-CONTRACT_SENT, ContractEvent SENT), GET /api/public/contracts/[token] (public, מסמן VIEWED), POST /api/public/contracts/[token]/sign (public — חתימה: מאמת ת״ז ישראלית עם ספרת ביקורת, לוכד IP + UA + signature image, מפיק PDF חתום עם תמונת חתימה משובצת ו-audit footer, מעדכן הסכם ל-SIGNED, מקדם ליד ל-CONTRACT_SIGNED, שולח מייל אישור עם PDF חתום ללקוח). **תשתית:** lib/crm/contract-number.ts (CON-YYYY-NNNN), lib/crm/israeli-id.ts (ולידציה לפי אלגוריתם 9 ספרות + ספרת ביקורת — תקף ל-ת״ז וח.פ.), lib/crm/render-contract-pdf.tsx (react-pdf עם דף חתימה כולל תמונת חתימה, פרטי החותם, IP/UA וטקסט אזהרה משפטי), 2 email helpers חדשים: sendContractSignatureRequestEmail + sendContractSignedConfirmationEmail. **הרשאות:** 6 חדשות CONTRACTS_VIEW/CREATE/EDIT/DELETE/SEND/TEMPLATES_MANAGE — לרולים מנהליים בלבד (לא AGENT). **UI:** /contracts — עמוד רשימה עם פילטר/חיפוש, ContractForm dialog שתומך ב-3 מקורות יצירה: מהצעת מחיר (מעתיק baseRates+terms אוטומטית), מליד, או מלקוח קיים. /contracts/[id] — עמוד פירוט עם כרטיסי לקוח+תקופה, כרטיס חתימה (מוצג רק אחרי חתימה — עם IP/ת״ז/שעה לאודיט), טבלת תעריפים, תנאי שירות, ויומן אירועים מלא (CREATED/SENT/VIEWED/SIGNED עם actor_type ו-IP). /sign/[token] (public, ללא login) — דף חתימה ממותג עם תוכן ההסכם, signature pad של react-signature-canvas (תומך עכבר+מסך מגע), שדות חובה: שם/אימייל/ת״ז (validation client-side + server-side)/טלפון (אופציונלי), checkbox קריאה ואישור, וטקסט הסבר על תוקף החתימה האלקטרונית. אחרי חתימה — מסך success עם הורדת PDF החתום. **אינטגרציות:** בעמוד הצעת מחיר (status≠DRAFT) — כפתור 'צור הסכם' מנווט ל-/contracts?quoteId=… ופותח דיאלוג עם pre-fill מההצעה (תעריפים + תנאי שירות snapshot). Sidebar: 'הסכמים' תחת לקוחות, gated ע״י crm_contracts flag (תלוי ב-crm_quotes). Middleware: /sign ו-/api/public/contracts/[token]/sign ללא auth. **Audit/Legal:** lib/crm/israeli-id.ts מאמת לפי אלגוריתם רשמי; שדה signedByIdNumber חובה (לא optional); IP נלכד מ-x-forwarded-for/x-real-ip/cf-connecting-ip; UA נשמר חתוך ל-1000 תווים; PDF החתום כולל את כל פרטי החותם + IP + UA + הצהרה משפטית על חוק חתימה אלקטרונית. ContractEvent מהווה immutable audit trail.
- מודול הסכמים מלא: יצירה מהצעת מחיר → שליחה לחתימה → חתימה דיגיטלית → PDF חתום
- Signature pad ב-/sign/[token] (react-signature-canvas) — עכבר ומסך מגע, ת״ז חובה עם validation
- ולידציית ת״ז ישראלית (9 ספרות + ספרת ביקורת) — client-side ו-server-side
- PDF חתום כולל תמונת חתימה משובצת + פרטי החותם + IP + UA + הצהרה משפטית
- Audit trail מלא: ContractEvent immutable + actor_type + IP + timestamp לכל פעולה
- ליד מתקדם אוטומטית CONTRACT_SENT → CONTRACT_SIGNED בעת השליחה והחתימה
- מייל אישור עם PDF חתום נשלח אוטומטית ללקוח אחרי חתימה
- כפתור 'צור הסכם' בהצעת מחיר (status≠DRAFT) → ContractForm עם pre-fill מההצעה
v01.54.003תיקוןdelivery-times/settlement-aliasesעריכת שם חלופי: PATCH אמיתי במקום DELETE+POST שדרס id/createdAt/source
ב-AliasesDialog (פתיחה מ-/delivery-times/settlements) הפעולה 'ערוך' עשתה DELETE+POST: מחקה את ה-alias ויצרה חדש.
פרטים נוספים ↓הסתר ↑
ב-AliasesDialog (פתיחה מ-/delivery-times/settlements) הפעולה 'ערוך' עשתה DELETE+POST: מחקה את ה-alias ויצרה חדש. תופעות הלוואי: id חדש (מאבד הפניות אם היו), createdAt חדש, source נדרס ל-'manual' גם אם המקור היה auto-quotes/merge/bulk-merge/canonical-cleanup, ובמקרה ש-POST נכשל אחרי DELETE — ה-alias נמחק לעולמים. נוסף route חדש PATCH /api/delivery-times/settlement-aliases/[id] שמעדכן alias וב-originalName באטומיות תוך שמירה על id/createdAt/source. במקביל נוסף invalidateSettlementCache() אחרי כל POST/PATCH/DELETE כדי שהפונקציה המאוחדת תראה את השינוי ב-lookup הבא במקום לחכות 5 דקות. הוחלפה הפונקציה המקומית normalizeSettlementName ב-API route בייבוא של normalize מ-lib/settlements/normalize כדי שהמפתח שנשמר במסד יהיה עקבי עם מה שהפונקציה מחפשת.
v01.54.002שיפורdelivery-times/settlement-name-alertsטבלת התראות שמות לא מזוהים — אוטומציה: 41 רשומות שהפונקציה החדשה כן יודעת לפתור סומנו כ-LINKED
סקריפט חדש resolve-pending-alerts.ts עובר על כל הרשומות ב-status PENDING ב-UnknownSettlementAlert ומריץ עליהן resolveSettlement.
פרטים נוספים ↓הסתר ↑
סקריפט חדש resolve-pending-alerts.ts עובר על כל הרשומות ב-status PENDING ב-UnknownSettlementAlert ומריץ עליהן resolveSettlement. אם הפונקציה החדשה פותרת את ה-originalName דטרמיניסטית (exact/light/medium/alias/stripped), הרשומה מסומנת כ-LINKED עם linkedSettlementId/Name, reviewedAt עכשיו, ו-reviewNotes שמציין 'Auto-resolved by resolveSettlement (via X)'. לא נוצר alias חדש — הפונקציה ממשיכה לפתור גם ללא רשומה במסד. substring match לא נכלל באוטו-resolve (יותר מדי סיכון לזיהוי שגוי, נשאר ידני). מתוך 370 התראות PENDING, 41 קיבלו טיפול אוטומטי — כל אלה שנוצרו לפני הרפורמה כשהקוד הישן לא ידע לקלף קידומות (מושב X, קיבוץ X, ישוב X), לנרמל מקפים (תל אביב – יפו, רמת- השרון), או יי/וו (גניי תקווה, נורדייה, בירייה). יתר 326 ההתראות נשארו ידניות.
v01.54.001תיקוןdelivery-times/settlementsתיקון באג בניקוי שמות חלופיים: שחזור 34 רשומות שנמחקו במקרה (mutual-cover)
בסקריפט cleanup-redundant-aliases.ts מ-Phase 2.D היה באג: כששני aliases מצביעים לאותו ישוב ושותפים אותו key מנורמל (למשל 'קרית ספר' ו'קריית ספר' שניהם מנורמלים ל'קרית ספר'), הניתוח של כל אחד מהם מצא ש'השני מכסה אותי' — א…
פרטים נוספים ↓הסתר ↑
בסקריפט cleanup-redundant-aliases.ts מ-Phase 2.D היה באג: כששני aliases מצביעים לאותו ישוב ושותפים אותו key מנורמל (למשל 'קרית ספר' ו'קריית ספר' שניהם מנורמלים ל'קרית ספר'), הניתוח של כל אחד מהם מצא ש'השני מכסה אותי' — אז שניהם נמחקו ונוצר חור. נכתב סקריפט restore-mutually-covered-aliases.ts שעובר על קובץ הגיבוי, בודק לכל רשומה האם הפונקציה החדשה במצב ה-DB הנוכחי עדיין פותרת את ה-originalName אל אותו ישוב, ואם לא — משחזרת את ה-alias. 34 aliases שוחזרו (כולל קרית ספר → מודיעין עילית, תל אביב - יפו → תל אביב יפו, ועוד וריאציות יידוע של ירושלים/חיפה/מודיעין עילית). הוסף הערה לסקריפט ה-cleanup על המגבלה.
v01.54.000חדשcrm/quotesminorהצעות מחיר — מודול חדש: יצירה, PDF בעברית, שליחה במייל ועמוד צפייה ציבורי
פאזה 1 של CRM Funnel — מודול הצעות מחיר מלא end-to-end.
פרטים נוספים ↓הסתר ↑
פאזה 1 של CRM Funnel — מודול הצעות מחיר מלא end-to-end. **API:** GET/POST /api/quotes (רשימה + יצירה כטיוטה), GET/PATCH/DELETE /api/quotes/[id] (עריכה ומחיקה רק ב-DRAFT), POST /api/quotes/[id]/send (יוצר PDF, מעלה ל-Supabase Storage, שולח מייל ללקוח, מסמן SENT, מקדם ליד מקושר לסטטוס QUOTED אוטומטית + LeadActivity), GET /api/public/quotes/[token] (Public, no auth — לדף הצפייה של הלקוח). **תשתית:** lib/crm/quote-number.ts (מספור QUO-YYYY-NNNN פר טננט), lib/crm/render-quote-pdf.tsx (react-pdf עם פונט Rubik לעברית RTL, תעריפים בש״ח, תנאי שירות, footer ממוספר), sendQuoteEmail ב-lib/email.ts. **הרשאות:** 5 הרשאות חדשות QUOTES_VIEW/CREATE/EDIT/DELETE/SEND, חולקו ל-3 רולים עם הרשאות leads מלאות + ל-AGENT (ללא DELETE). **UI:** /quotes — עמוד רשימה מלא עם פילטר סטטוס, חיפוש, pagination, row actions (פתח / שלח / העתק קישור ציבורי / פתח כלקוח / מחק טיוטה). /quotes/[id] — עמוד פירוט עם header פעולות, כרטיסי נמען+פרטי שליחה, טבלת תעריפים, הערות ותנאים. /q/[token] (public, ללא login) — דף ממותג RTL עם לוגו טננט, מספר הצעה, סטטוס, תעריפים, הערות, תנאים, וכפתור הורדת PDF. **אינטגרציות:** כפתור 'צור הצעת מחיר' בפעולות שורת ליד מנווט ל-/quotes?leadId=… ופותח דיאלוג עם הליד pre-selected; הסיידבר כולל קישור 'הצעות מחיר' תחת קבוצת לקוחות, מותנה בפיצ'ר flag crm_quotes; middleware מאפשר /q ו-/api/public/quotes ללא auth, ומוסיף /quotes ו-/api/quotes ל-AGENT allowed prefixes. **Status flow:** DRAFT → SENT (אחרי /send + PDF + email) → VIEWED (פעם ראשונה שהלקוח פותח). publicAccessToken (32 בייטים crypto.randomBytes) נוצר בשליחה.
- מודול הצעות מחיר מלא: API + UI + PDF + מייל + public link
- PDF עברית RTL עם react-pdf + Rubik (תעריפים, הערות, תנאים, מספור עמודים)
- /q/[token] — דף צפייה ציבורי ממותג עם לוגו טננט, ללא login
- כפתור 'צור הצעת מחיר' בפעולות ליד → דיאלוג עם pre-fill אוטומטי
- ליד שמקבל הצעה מקבל אוטומטית סטטוס QUOTED + LeadActivity של שליחה
- Feature flag crm_quotes מסתיר את המודול בטננטים שאינם פעילים
- טוגלי הפעלת מודולים ב-/admin/tenants — הצעות מחיר + הסכמים (הסכמים תלויים בהפעלת הצעות)
v01.53.016חדשcrmCRM Funnel — תשתית סכמה לפאזה 0 (ליד→הצעת מחיר→הסכם→לקוח)
פאזה 0 של תוכנית ה-CRM הכוללת שאושרה היום: נוצרה התשתית המלאה ב-DB schema עבור משפך הלידים שייבנה מעל.
פרטים נוספים ↓הסתר ↑
פאזה 0 של תוכנית ה-CRM הכוללת שאושרה היום: נוצרה התשתית המלאה ב-DB schema עבור משפך הלידים שייבנה מעל. אין שינוי נראה למשתמש כרגע — רק יסודות לפיתוח הבא. נוספו 4 מודלים חדשים ב-Prisma schema: Quote (הצעת מחיר עם snapshot של תעריפים, סטטוס DRAFT→SENT→VIEWED→ACCEPTED, PDF storage, communication tracking), Contract (הסכם הנגזר מהצעה, תוכן מלא, חתימה דיגיטלית עם signedByIdNumber/IP/UA לאודיט, draft+signed PDFs), ContractEvent (immutable audit log פר הסכם), ContractTemplate (תבניות פר-טננט עם placeholders). הוסף enum CustomerStatus (LEAD/ACTIVE/SUSPENDED/ARCHIVED) ושדות status/suspended_at/suspended_by_id/suspend_reason ל-customers, עם backfill: לקוחות עם is_active=false → ARCHIVED. הורחב enum LeadStatus עם QUOTED/CONTRACT_SENT/CONTRACT_SIGNED/CONVERTED, נוסף converted_customer_id ל-leads כדי לקשר ליד שהומר ללקוח. נוצרו 2 enums חדשים נוספים: QuoteStatus, ContractStatus. נוסף apps/web/lib/crm/feature-access.ts עם isCrmQuotesEnabled + isCrmContractsEnabled (פטרן זהה ל-warehouse/feature-access). 0 שגיאות חדשות ב-typecheck. השלב הבא: פאזה 1 (UI להצעות מחיר + PDF generation עם react-pdf).
- 4 מודלים חדשים: Quote, Contract, ContractEvent, ContractTemplate
- Customer.status enum מחליף הדרגתית את isActive עם backfill
- Lead.convertedCustomerId + הרחבת LeadStatus ב-4 ערכים חדשים
- feature flags: crm_quotes + crm_contracts (gated של פאזות הבאות)
v01.53.015שיפורdelivery-times/settlementsתשתית: פונקציית resolveSettlement מאוחדת + harness לאי-רגרסיה
התשתית הראשונה לרפורמת שמות חלופיים: מודול חדש apps/web/lib/settlements/ עם normalize.ts (פונקציות נרמול טהורות, client-safe) ו-resolve.ts (פונקציה אחת resolveSettlement עם cache בזיכרון, 5+1 שלבים: exact → light → mediu…
פרטים נוספים ↓הסתר ↑
התשתית הראשונה לרפורמת שמות חלופיים: מודול חדש apps/web/lib/settlements/ עם normalize.ts (פונקציות נרמול טהורות, client-safe) ו-resolve.ts (פונקציה אחת resolveSettlement עם cache בזיכרון, 5+1 שלבים: exact → light → medium → alias → stripped → substring fallback). שמירת גרש אחרי ג'/ז'/צ'/ת'/ד' (תעתיק ערבי) — תיקון באג שמיזג ג'ת עם גת. נוסף scripts/capture-resolution-baseline.ts ו-scripts/verify-resolution-parity.ts שמריצים את כל 4 ה-callsites הקיימים על שמות עיר אמיתיים מ-30 הימים האחרונים ומשווים מול הפונקציה החדשה, לסיווג MATCH/IMPROVED/REGRESSION. 49 unit tests עוברים. בעקבות הסקירה הוטמעה הפונקציה ב-4 ה-callsites הצד-שרת: customer-pricing.isExceptionalZone, public/tracking resolveDeliveryType, sorting-error.findZoneCodeByCity, settlement-service.findSettlement. שמות עם מקפים שלא היו מזוהים קודם (מעלות-תרשיחא, פרדס חנה-כרכור, בנימינה-גבעת עדה וכו') מזוהים עכשיו נכון. שמות עם קידומת מושב/קיבוץ/כפר/ישוב מתקלפים אוטומטית. ImportShipmentsClient autocomplete עדיין לא הומר (Phase 1.B.5 — דורש endpoint להעברת הרשימה ללקוח). [Phase 2] ניקוי נתונים: 3 כפילויות בקנוני אוחדו ל-aliases — קריית ארבע → קרית ארבע, מרכז חבר → חבר, הר-חלוץ → הר חלוץ. נמחקו 447 aliases מיותרים (34.4%) שהפונקציה החדשה מכסה דטרמיניסטית (גרשיים, יי/וו, קידומות מושב/קיבוץ/כפר/ישוב). נמחק generateQuoteAliases ו-route generate-quotes שכבר לא נחוצים. נמחק alias שגוי 'ישוב חבר' → 'מעלה חבר' (החדש מחזיר נכון 'חבר').
v01.53.014תיקוןdrivers/earningsדו״ח תשלום חודשי — שם קובץ בעברית עובד בפועל + עמודת 'מזהה יוצא' (לא externalOrderId)
תיקון שני באגים שעלו אחרי משוב משתמש: (1) שם הקובץ בעברית שהוגדר ב-Content-Disposition של השרת לא הופיע בפועל כי הלקוח (EarningsCard) דרס אותו עם a.download שכתוב בקשיחות.
פרטים נוספים ↓הסתר ↑
תיקון שני באגים שעלו אחרי משוב משתמש: (1) שם הקובץ בעברית שהוגדר ב-Content-Disposition של השרת לא הופיע בפועל כי הלקוח (EarningsCard) דרס אותו עם a.download שכתוב בקשיחות. ה-blob URL לא קורא Content-Disposition אוטומטית, ולכן ה-a.download חייב להכיל את השם בעצמו. הוסף helper filenameFromContentDisposition שמפרסר את filename*=UTF-8'' מתוך header התגובה ומזין אותו ל-a.download, עם fallback לשם ה-ASCII הקודם אם משהו במידע השרת חסר. (2) העמודה שנקראה בטעות 'מזהה חיצוני' השתמשה ב-Shipment.externalOrderId (זה מזהה הזמנה חיצוני שהמשתמש סיפק ביצירת המשלוח — דבר אחר לגמרי). השדה שהמשתמש מתכוון אליו הוא 'מזהה יוצא' שמופיע בדף המשלוח — Shipment.targetPartnerTaskId (מזהה משלוח אצל הספק שאליו הועבר, מליונוויל). העמודה ב-EarningsLine שונתה ל-targetPartnerTaskId ושם העמודה בגיליון Excel שונה ל'מזהה יוצא'.
v01.53.013שיפורdrivers/earningsדו״ח תשלום חודשי לנהג — מע״מ לקבלנים, עמודות כתובת/מזהה חיצוני, מירכוז גורף
סדרת שיפורים לאחר משוב על הקובץ שיורד: (1) גיליון 'סיכום' — שורת 'ת.ז./מזהה' הוסרה לחלוטין, עמודה B הורחבה פי 2 (28 wch) כדי לאכלס שמות שליחים ארוכים יותר; אצל שליחים מסוג EXTERNAL (קבלן חיצוני) נוסף בלוק VAT: 'סה״כ לפני…
פרטים נוספים ↓הסתר ↑
סדרת שיפורים לאחר משוב על הקובץ שיורד: (1) גיליון 'סיכום' — שורת 'ת.ז./מזהה' הוסרה לחלוטין, עמודה B הורחבה פי 2 (28 wch) כדי לאכלס שמות שליחים ארוכים יותר; אצל שליחים מסוג EXTERNAL (קבלן חיצוני) נוסף בלוק VAT: 'סה״כ לפני מע״מ', 'מע״מ 18%', והבאנר הסופי משתנה ל'סה״כ לתשלום (כולל מע״מ)' עם הסכום המגולם. שלא לקבלנים, הבאנר נותר 'סה״כ תשלום נטו'. (2) גיליון 'משלוחים' — נוספה עמודה 'כתובת' המאחדת destinationStreet + destinationNumber לטקסט אחד ('הרצל 12'); אצל EXTERNAL בלבד, נוספה עמודה 'מזהה חיצוני' (Shipment.external_order_id) צמודה לעמודת הברקוד. (3) שם הקובץ — מ-'driver-42-2026-06.xlsx' ל-'דוח תשלום - יוסי כהן - יוני 2026.xlsx' (RFC 5987 UTF-8 encoding ב-Content-Disposition עם fallback ל-ASCII; טווח שאינו חודש קלנדרי שלם יקבל 'DD.MM.YY – DD.MM.YY' במקום שם חודש). (4) עיצוב — כל תוכן התאים בכל הגיליונות (סיכום, משלוחים, איסופים עסקיים, התאמות) מוצג ממורכז אופקית וורטיקלית; helper חדש centerEverything שמופעל לאחר שאר ה-styling כדי לא לדרוס borders/colors. (5) הרחבה ב-driver-earnings.ts: שדות destinationStreet, destinationNumber ו-externalOrderId נוספו ל-VISIT_SHIPMENT_SELECT וזולגים ל-EarningsLine — שאר ה-callers (UI card, mobile API) ממשיכים לעבוד כי הם פשוט מתעלמים מהשדות החדשים.
v01.53.012חדשmiddleware/hostדומיינים מוגבלים לכניסה בלבד (maslul.space, alonim.store) — ללא landing וללא /docs
הוספת host-gate ב-middleware: בקשות לדומיינים שלנו שאינם shipnest.io — נכון לעכשיו maslul.space ו-alonim.store (כולל וריאנטי www) — שמכוונות ל-/landing או ל-/docs (כולל תתי-נתיבים) מופנות אוטומטית ל-/login באותו host.
פרטים נוספים ↓הסתר ↑
הוספת host-gate ב-middleware: בקשות לדומיינים שלנו שאינם shipnest.io — נכון לעכשיו maslul.space ו-alonim.store (כולל וריאנטי www) — שמכוונות ל-/landing או ל-/docs (כולל תתי-נתיבים) מופנות אוטומטית ל-/login באותו host. שאר הנתיבים — /login עצמו, /portal/login, /employee/login, ו-routes מאומתים — ממשיכים לעבוד רגיל. / עצמו לא נוגעים בו: page.tsx ממילא מפנה משתמשים לא מחוברים ל-/landing, וה-gate החדש תופס את ההפניה הזאת והופך אותה ל-/login. shipnest.io לא מושפע — הבדיקה מבוססת על host header, לא env. הרשימה מוגדרת כקבוע LOGIN_ONLY_HOSTS בראש middleware.ts כדי שיהיה קל להוסיף דומיינים נוספים בעתיד.
v01.53.011תיקוןmiddleware/agentסוכנים — שחרור /api/settlements ב-middleware (סיבת השגיאות הרבות ביבוא)
השורש האמיתי של 'אצל הסוכן יש יותר שגיאות יבוא מאשר אצל האדמין' (62 מול 22 בהתחלה, 143 מול 22 בהתחזות) נמצא ב-middleware: ה-allowedPrefixes של תפקיד AGENT לא כלל את /api/settlements.
פרטים נוספים ↓הסתר ↑
השורש האמיתי של 'אצל הסוכן יש יותר שגיאות יבוא מאשר אצל האדמין' (62 מול 22 בהתחלה, 143 מול 22 בהתחזות) נמצא ב-middleware: ה-allowedPrefixes של תפקיד AGENT לא כלל את /api/settlements. כל בקשה של אשף היבוא ל-GET /api/settlements/bulk חזרה 403 לפני שהגיעה בכלל ל-route handler. בצד הלקוח ה-fetch הצליח (קיבל תגובה), אבל Array.isArray(data?.settlements) נכשל, ומערך היישובים נשאר ריק — splitTrailingCity לא יכול היה לקלף עיר מסוף הכתובת, וכל שורה ללא עמודת 'עיר' מפורשת נכשלה בולידציה של 'עיר חובה'. אצל אדמין (OWNER/PLATFORM_ADMIN) ה-middleware לא חוסם, וה-fetch מצליח. תיקון: הוספת /api/settlements לרשימת ה-allowedPrefixes של AGENT (היישובים הם dictionary גלובלי לא רגיש — שמות יישובים בלבד, אותה הרשאה שכבר ניתנת לקריאה ב-route handler עצמו דרך SHIPMENTS_VIEW). שני התיקונים הקודמים שלי על race-condition בטעינת היישובים (settlementsReadyRef + poll) נשארים כ-safety net אבל לא היו הסיבה האמיתית.
v01.53.010שיפורdrivers/earningsדו״ח תשלום חודשי לנהג — עיצוב גיליון הסיכום + פירוט איסופים עסקיים
המעבר מ-xlsx ל-xlsx-js-style מאפשר כתיבת עיצוב ברמת התא.
פרטים נוספים ↓הסתר ↑
המעבר מ-xlsx ל-xlsx-js-style מאפשר כתיבת עיצוב ברמת התא. גיליון 'סיכום' מקבל כעת כותרת בולטת על רקע primary, שורת כותרת לטבלת הפירוט עם רקע אפור ו-borders, פורמט מטבע ילידי (#,##0.00 ₪) על כל הסכומים, ובאנר 'סה״כ תשלום נטו' עם רקע primary וטקסט לבן בגדול. נוסף גיליון חדש 'איסופים עסקיים' שמוצג רק כאשר יש לפחות איסוף עסקי אחד בתקופה — מציג שורה לכל ברקוד ששייך לקבוצת איסוף עסקי, עם קוד קבוצה (BP-1, BP-2, …), תאריך, לקוח, ברקוד, חבילות, סכום למשלוח, כמות בקבוצה וסה״כ קבוצה. הקבוצות מקבלות רקע אלטרנטיבי לזיהוי ויזואלי של גבולות הקבוצה. בנוסף נוספה עמודה 'קבוצת איסוף' בגיליון 'משלוחים' שמכילה את אותו קוד BP-N — כך אפשר לעקוב מכל שורה לאיזו קבוצת איסוף היא שייכת. כל הגיליונות ממשיכים להיפתח ב-RTL. עדכון נוסף: גיליון 'התאמות' מוסתר לחלוטין כשאין התאמות ידניות בתקופה (אותה מדיניות כמו 'איסופים עסקיים') — גיליון ריק רק מבלבל קוראים שחושבים שמשהו חסר. תיקון נוסף: דגל ה-RTL הוזז מ-sheet['!views'] (שנבלע בשקט ע״י xlsx-js-style) ל-workbook.Workbook.Views[0].RTL — כך הקובץ באמת נפתח באקסל בכיוון ימין-לשמאל בכל הגיליונות; הניסיון הקודם לא עבד.
v01.53.009שיפורdrivers/listרשימת שליחים — עמודת מחירון + פילטרים + סטטוס אינליין + נוכחות חיה
נוספה עמודה 'מחירון' לרשימת השליחים: כאשר הוגדר מחירון מופיע כפתור 'ערוך מחירון' שפותח את אותו דיאלוג עריכת מחירון של דף השליח; כאשר לא הוגדר — טקסט מעומעם 'לא הוגדר מחירון' שלחיצה עליו פותחת את הדיאלוג במצב הקמה ראשונה.
פרטים נוספים ↓הסתר ↑
נוספה עמודה 'מחירון' לרשימת השליחים: כאשר הוגדר מחירון מופיע כפתור 'ערוך מחירון' שפותח את אותו דיאלוג עריכת מחירון של דף השליח; כאשר לא הוגדר — טקסט מעומעם 'לא הוגדר מחירון' שלחיצה עליו פותחת את הדיאלוג במצב הקמה ראשונה. בנוסף, נוסף ל-toolbar כפתור 'פילטרים' (FiltersPopover משותף) עם שתי קטגוריות: 'מחירון' (הוגדר / לא הוגדר — בחירה יחידה) ו'אזורי חלוקה' (multi-select לפי zoneCode, נגזר מ-allowedZones של השליחים בטאב הנוכחי). עמודת הסטטוס (פעיל/לא פעיל) הפכה לעריכה אינליין דרך dropdown — באותה תבנית של עריכת סטטוס משלוח בדף המשלוחים. נוסף PATCH /api/drivers/[id] שמעדכן רק את isActive וגם מסנכרן את ה-employee המקושר (אם קיים). הוסר ניווט-בלחיצה-על-השורה כדי שלא יפתח דף שליח בטעות בעת תפעול אינליין; ניווט לדף השליח מתבצע עכשיו רק דרך אייקון ה-ExternalLink שמופיע משמאל לשם השליח בריחוף (DriverNameDisplay). נוספו פעולות קבוצתיות לסרגל הבחירה: 'סמן כפעיל', 'סמן כלא פעיל' (עם דיאלוג אישור — מסנכרן גם employees מקושרים), ו-'הגדר מחירון' שפותח דיאלוג קצר עם 6 שדות תעריף בסיס (₪) ומחיל אותם על כל השליחים שנבחרו דרך POST /api/drivers/batch/pricing (יוצר profile חדש לשליחים בלי מחירון, מעדכן רק שדות שהוזנו לשליחים עם profile קיים). הוסר כפתור 'ייצוא' מסרגל הבחירה — הייצוא לאקסל נשאר רק כפעולת טבלה ראשית. נוספה עמודת 'נוכחות' (אינדיקציה חיה): נקודה ירוקה מהבהבת = השליח בתוך משמרת פעילה כעת, כתומה = סיים משמרת בשעות האחרונות, אדומה = משמרת פתוחה > 12 שעות (כנראה שכח לסיים), אפורה = ידוע אך לא במשמרת, אפורה דקה = ללא נתוני נוכחות (שליח בלי Employee). הנוכחות נגזרת מה-Shift האחרון של ה-Employee המקושר; ה-API מחזיר אותה דרך single batched query (DISTINCT ON על shifts) כדי לא לפגוע בביצועים. הקטגוריה 'נוכחות' נוספה גם ל-FiltersPopover עם מונים לכל דלי, ועמודת הנוכחות ניתנת למיון לפי סדר עדיפות (פעיל → פיגור → לאחרונה → לא פעיל → ללא).
v01.53.008שיפורdrivers/earningsדו״ח תשלום חודשי לנהג — אקסל נפתח מימין-לשמאל
ייצוא ה-Excel של דו״ח התשלום החודשי (סיכום + משלוחים + התאמות) מקבל עכשיו את דגל ה-RTL של ה-sheet view ב-SheetJS.
פרטים נוספים ↓הסתר ↑
ייצוא ה-Excel של דו״ח התשלום החודשי (סיכום + משלוחים + התאמות) מקבל עכשיו את דגל ה-RTL של ה-sheet view ב-SheetJS. כתוצאה מכך הקובץ נפתח באקסל בפריסה ישראלית — עמודה A בקצה הימני, הזרימה ימין-לשמאל — בלי שהמשתמש צריך להחליף את כיוון הגיליון ידנית בכל פתיחה.
v01.53.007תיקוןauth/impersonationהתחזות לסוכן — מילוי agentId על-בסיס effective role במקום User.role
באג ב-JWT callback של ההתחזות: הבדיקה אם לטעון את ה-Agent record (כדי לאכלס token.agentId) הסתעפה על targetUser.role === 'AGENT'.
פרטים נוספים ↓הסתר ↑
באג ב-JWT callback של ההתחזות: הבדיקה אם לטעון את ה-Agent record (כדי לאכלס token.agentId) הסתעפה על targetUser.role === 'AGENT'. אצל רוב הסוכנים תפקיד ה-AGENT מאוחסן ב-TenantUser.role ולא ב-User.role (זה הפיצ'ר של תפקידים פר-טננט), ולכן הבלוק לא רץ בכלל. כתוצאה מכך, אדמין שעשה 'התחבר כסוכן' קיבל session עם role='AGENT' (נכון, דרך resolveEffectiveRole) אבל agentId=null — וכל ה-endpoints שמסננים ל-AGENT לפי הלקוחות המשויכים (api/customers, api/shipments, api/customers/for-shipment-form) החזירו רשימות ריקות. נראה כאילו לסוכן אין לקוחות ולא משלוחים, בעוד שכשהסוכן עצמו התחבר ישירות הכל עבד תקין (זרימת הלוגין הרגילה כבר בוחנת effectiveRole). תיקון: ב-auth.ts JWT callback (impersonation block) קוראים את ה-Agent record בלי תנאי מקדים — השאילתה ממילא מסננת לפי users.some.userId — ומאכלסים את agentId אך ורק אם targetEffectiveRole === 'AGENT' (כדי שמשתמש עם קישור AgentUser שאיננו עוד סוכן בפועל לא יקבל scope שגוי). ה-tenantId נבחר עדיין על-בסיס TenantUser תחילה (admin עם קישור AgentUser ישן לא ייכנס בטעות לטננט של הסוכן), עם fallback ל-agent.tenantId אם אין TenantUser בכלל. תוקנה גם בדיקת ה-gate המקדימה ב-/api/users/impersonate שהשתמשה באותו דפוס מוטעה.
v01.53.006חדשstores, integrationsסקפולד אינטגרציה e-shop.co.il
הונח הבסיס לאינטגרציה עם e-shop.co.il: client, mapper, delivery-sync, cron job (כל 5 דקות), ו-UI בדף החנויות.
פרטים נוספים ↓הסתר ↑
הונח הבסיס לאינטגרציה עם e-shop.co.il: client, mapper, delivery-sync, cron job (כל 5 דקות), ו-UI בדף החנויות. הקוד מסומן ב-TODO עד שיתקבלו פרטי ה-API הרשמיים מ-e-shop.
v01.53.005תיקוןshipments/importייבוא משלוחים — race condition: ניתוח הקובץ ממתין למילון היישובים
תיקון באג שגרם לאותו קובץ לתת מספר שונה של שגיאות תלוי במהירות המשתמש: handleFile קרא את settlementsRef.current באופן סינכרוני, אבל הוא מתאכלס רק אחרי ש-/api/settlements/bulk חזר.
פרטים נוספים ↓הסתר ↑
תיקון באג שגרם לאותו קובץ לתת מספר שונה של שגיאות תלוי במהירות המשתמש: handleFile קרא את settlementsRef.current באופן סינכרוני, אבל הוא מתאכלס רק אחרי ש-/api/settlements/bulk חזר. מי שהעלה קובץ לפני שהמילון הגיע פירס בלי dictionary, ו-splitTrailingCity לא הצליח לקלף עיר מסוף הכתובת ('השקד 7 מודיעין' → עיר ריקה). normalizeCities שרץ אחרי שהיישובים הגיעו לא ניסה שוב לפצל כתובות — רק נירמל שמות עיר שכבר נקלטו. הניסיון הראשון השתמש ב-Promise/resolver מתוך useState lazy initializer; זה נכשל ב-React StrictMode (lazy initializer רץ פעמיים, ה-ref דרס למצביע על Promise שני אבל ה-state שמר את ה-resolver של הראשון, וה-Promise השני אף פעם לא התפזר) ובמקרה של הסוכן גרם לכל השורות להיכשל. ה-fix הסופי משתמש בדפוס poll פשוט שעמיד ל-StrictMode/HMR: counter של 'כמה fetches סיימו' שמתעדכן בכל סיום (success או failure), ו-handleFile עושה poll עד שהמילון מאוכלס או שהבדיקה הסתיימה, עם timeout של 5 שניות שלא יחסום את ה-UI אם ה-endpoint שבור.
v01.53.003תיקוןdrivers/linkedשליחים מקושרים — תיקון: הפיכת שליח רגיל למנהל אפסה את הדוח החודשי
באג שהתגלה בייצור — כשאדמין הפך שליח קיים לקבלן-מנהל, הדוח החודשי שלו ירד מ-16,000₪ ל-0.
פרטים נוספים ↓הסתר ↑
באג שהתגלה בייצור — כשאדמין הפך שליח קיים לקבלן-מנהל, הדוח החודשי שלו ירד מ-16,000₪ ל-0. הסיבה: computeManagerTenantEarnings סינן רק visits עם Visit.managedByDriverId מתויג מפורשות, אבל המשלוחים ההיסטוריים של המנהל (שבוצעו לפני שהוא הפך למנהל) היו עם managedByDriverId=null. תיקון: ה-query מסנן עכשיו OR — visits מתויגים מפורשות כ-managedBy=manager, OR visits שבהם driverId=manager AND managedByDriverId IS NULL. השני הוא fallback לוגי-בטוח (אם אדם X ביצע משלוח ואף אחד לא תבע אחריות כספית עליו, אז זה של X) — בלי סיכון של misattribution. ה-fallback לא חל על סאבים — visit ישן של סאב יכול להיות מתקופה לפני שהוא נכנס לצוות או של מנהל אחר; לזה צריך backfill מפורש (scripts/backfill-managed-by.ts). שום צורך ב-backfill ידני אחרי toggle של מנהל — המנוע מטפל בזה אוטומטית.
- Engine fix: computeManagerTenantEarnings.where = { OR: [{managedByDriverId: manager}, {driverId: manager, managedByDriverId: null}], ... }. השאר זהה.
- Sub work בטוח לא מושפע — fallback חל רק על visits שה-driverId שלהם הוא המנהל עצמו.
- Test חדש: 'fallback: includes manager own untagged visits' — מבטיח שהבאג הזה לא יחזור. Test ישן עודכן לבדוק את ה-OR החדש.
- 875/875 בדיקות עוברות. ה-fix נכנס לתוקף מיד — אדמינים שיהפכו שליח למנהל יראו את הדוח שלו ללא שינוי.
מאי 2026
v01.53.002תיקוןlandingתיקון build — הסרת דף landing כפול שנותר ב-(site)
אחרי ה-refactor של 7ad4643a שהעביר את ה-landing ל-/landing, הקובץ הישן apps/web/app/(site)/landing/page.tsx נותר במקומו וגרם ל-Turbopack לכשול בdeploy עם 'You cannot have two parallel pages that resolve to the same path'.
פרטים נוספים ↓הסתר ↑
אחרי ה-refactor של 7ad4643a שהעביר את ה-landing ל-/landing, הקובץ הישן apps/web/app/(site)/landing/page.tsx נותר במקומו וגרם ל-Turbopack לכשול בdeploy עם 'You cannot have two parallel pages that resolve to the same path'. נמחק.
v01.53.001תיקוןdrivers/linkedשליחים מקושרים — פאזה 7: ביקורת אבטחה + תיקון webhook ל-managedByDriverId
פאזה 7 (סיום) של פיצ'ר השליחים המקושרים.
פרטים נוספים ↓הסתר ↑
פאזה 7 (סיום) של פיצ'ר השליחים המקושרים. ביקורת אבטחה מקיפה על כל ה-API שנגעו ב-Person/cross-tenant — לא נמצאו דליפות אמיתיות, אבל פער פונקציונלי אחד התגלה ותוקן: ה-createVisits ב-data-service.ts (מסלול ה-webhook של Lionwheel ליצירת ביקורים אוטומטית) לא הציב את Visit.managedByDriverId, כך שמשלוחים שנוצרו דרך webhook אוטומטי למנהל/סאב חושבו כעצמאיים בדוח החודשי. נוסף buildManagedByMap שמשמש את כל 3 ה-callers של createVisits (data-service.ts + upsertShipment), כך שעכשיו ה-webhook מגיע לאותה התנהגות כמו השיבוץ הידני (resolveManagedByDriverId שנוסף ב-2A). הפיצ'ר השליחים-מקושרים מושלם — 7 פאזות, 8 commits, 874/874 בדיקות עוברות, version 01.46.000→01.53.001.
- Audit: 7 נקודות נבדקו (Person APIs, DriverManagerLink scoping, webhook flow, earnings tenant guard, mobile inbox, PersonalStop access, backfill safety). NO leaks found.
- fix: data-service.ts createVisits — שלושת ה-callers (POST /api/v1/webhook upsertShipment ושני create paths נוספים) משתמשים עכשיו ב-buildManagedByMap, אז Visit.managedByDriverId מציב נכון לפי הקשר השליח (manager/sub/standalone) גם כשה-visit נוצר ע"י webhook.
- תאימות לאחור: לטננטים בלי אף isManagerEnabled, ה-managedByMap ריק וההתנהגות זהה למה שהיה לפני הפאזה.
- סוף הפרויקט: כל 7 הפאזות שודרו ב-2026-05-31. מוכן לשימוש בייצור — admin יכול להפעיל toggle 'קבלן-מנהל' על כל שליח ולהתחיל להוסיף לו סאב-שליחים.
v01.53.000חדשdrivers/linkedminorשליחים מקושרים — פאזה 6: Unified Inbox API (תשתית לתצוגה מאוחדת)
פאזה 6 של פיצ'ר השליחים המקושרים.
פרטים נוספים ↓הסתר ↑
פאזה 6 של פיצ'ר השליחים המקושרים. נוסף endpoint חדש GET /api/v1/mobile/inbox שמחזיר feed מאוחד של כל העצירות של ה-Person המאומת — מכל הטננטים שבהם יש לו Driver פעיל + כל ה-PersonalStops שלו. זוהי ההרכבה של הרעיון של 'אפליקציה של השליח, לא של החברה': המובייל יכול עכשיו להציג מסך אחד עם כל המשלוחים מ-N טננטים + העצירות הפרטיות באותה רשימה ממוינת, כל עצירה מתוייגת במקור (טננט / personal). ה-endpoint הקיים פר-טננט (mobile JWT עם driverId/tenantId) נשמר בלי שינוי לתאימות לאחור. שילוב מלא עם ה-route planner (העברת PersonalStops דרך optimize/activate) דחוי לפאזה 6.5 — דורש שינוי משותף עם קוד המובייל. הפאזה הזו מספקת את ה-data foundation בלבד.
- GET /api/v1/mobile/inbox: מאמת ע"י mobile JWT, מתרגם driverId→personId, מוצא את כל ה-drivers הפעילים של ה-Person בכל הטננטים, ושולף את כל ה-visits שלהם + כל ה-PersonalStops של ה-Person.
- תגובה מאוחדת: stops[] פולימורפי עם type='VISIT'|'PERSONAL', tenantId+tenantName (null ל-personal), address מובנה, coordinates (משלוח first, fallback ל-visit lat/lng), recipientName/Phone, packages, hasCOD+codAmount (agorot), notes, POD URLs.
- ברירת מחדל סינון: ?status=PENDING (כברירת מחדל). אופציות: COMPLETED, FAILED, ALL.
- סדר: מיון לפי scheduledAt עולה (visitAt ל-visits, scheduledFor ל-personal); null בסוף; stable.
- Cross-tenant safety: ה-endpoint לא חושף את הרשימה לאף Person אחר. אם אין personId על ה-Driver של ה-JWT — 403.
- 8 בדיקות חדשות לעוזרי המיון/סטטוס; סה״כ 874/874 עוברות.
- Phase 7 הבא: ביקורת הרשאות חוצות-טננט על כל הנקודות הרגישות + cleanup.
v01.52.000שיפורlandingminorדף נחיתה ציבורי — שדרוג מקיף
שכתוב כולל של דף הנחיתה הראשי (/) — Hero חזק יותר, סקציות חדשות, ותוכן כן יותר במקום פלייסהולדרים מומצאים.
פרטים נוספים ↓הסתר ↑
שכתוב כולל של דף הנחיתה הראשי (/) — Hero חזק יותר, סקציות חדשות, ותוכן כן יותר במקום פלייסהולדרים מומצאים. ה-Hero מציג עכשיו סטטוס גרסה חי, headline ממוקד ('הלוגיסטיקה שלך, בלי קובץ Excel אחד'), ו-trust strip קצר (ללא כרטיס אשראי, הקמה תוך 10 דקות, ענן ישראלי + RTL). שורת הלוגואים המומצאים ('לוגיסטיקה מהירה', 'ג'אמפ שליחויות' וכו') הוחלפה ב-marquee גלילה של קטגוריות אינטגרציה גנריות (מערכות משלוחים, מערכות הנה״ח, WhatsApp Cloud API, Excel, Webhook REST, Google Sheets, ERP) — בלי הזכרת שמות מותג ספציפיים. נוספה סקציית 'איך זה עובד' עם 3 שלבים ויזואליים (ייבוא → אוטומציה → אנליטיקה), סקציית WhatsApp showcase עם דמו טלפון, סקציית use-cases (חברות שליחויות / 3PL / e-commerce) שמחליפה את ההמלצות הפיקטיביות, וסקציית FAQ עם 6 שאלות נפוצות באמצעות details/summary. סקציית הסטטיסטיקות הומרה לערכים שאפשר לעמוד מאחוריהם (זמינות, זמן טעינה, תאימות RTL, ניטור) במקום מספרי לקוחות מומצאים. ה-Footer הורחב ל-4 עמודות (מותג, מוצר, משאבים, צור קשר) עם קופירייט שמתעדכן אוטומטית לפי שנה. נוספו media queries מפורשים ל-768px ו-480px שמטפלים בpadding, גודל גופן, וכפתורים בפריסה אנכית במובייל. כל קישורי 'התחל עכשיו' עברו מ-/shipments ל-/login כדי שמשתמשים לא מזוהים יגיעו ל-flow ההתחברות בצורה תקינה.
- Hero חזק יותר: badge עם dot חי שמציג את גרסת המערכת, headline ממוקד שמדבר על הכאב האמיתי ('בלי קובץ Excel אחד'), ו-trust strip בלי הבטחות שווא.
- החלפת לוגואים מומצאים ב-integrations marquee גלילה עם קטגוריות גנריות (מערכות משלוחים, מערכות הנה״ח, WhatsApp Cloud API, Excel, REST, Sheets, ERP) — בלי שמות מותג ספציפיים.
- סקציה חדשה 'איך זה עובד' (#how) — 3 כרטיסי שלבים עם מספור 01/02/03 גדול ברקע, אייקונים צבעוניים ותיאורים מדויקים.
- סקציית WhatsApp showcase חדשה — mock של טלפון עם שיחת WhatsApp אמיתית (קליטה → יציאה → קרבה → מסירה), typing indicator מונפש, ו-bullets על האוטומציה משמאל.
- סקציית use-cases מחליפה את ההמלצות הפיקטיביות (אבי כהן/מירי לוי וכו') ב-3 פרסונות עם מבנה 'האתגר → עם Shipnest' (חברות שליחויות, מחסני 3PL, e-commerce).
- סקציית FAQ חדשה (#faq) עם 6 שאלות נפוצות באמצעות details/summary נייטיב — accessibility מובנית, ללא state ולא client component.
- סטטיסטיקות כנות: 99.9% זמינות, <2s טעינה, 100% RTL, 24/7 ניטור — במקום '500+ עסקים פעילים' ו-'2M+ משלוחים' שלא ניתן להוכחה.
- Footer הורחב ל-4 עמודות (מותג + תיאור / מוצר / משאבים / צור קשר) עם © שנה דינמית במקום '© 2025' hard-coded.
- Responsive: media queries מפורשים @768px (padding, marquee מהיר יותר, dash-metrics אנכי) ו-@480px (כפתורי CTA full-width אנכיים, גופן קטן יותר).
- כל CTAs 'התחל עכשיו' מצביעים על /login במקום /shipments — משתמשים לא מזוהים מגיעים ל-flow ההתחברות נכון.
- Routing: הדף עבר מ-/ ל-/landing כדי שגם משתמשים מחוברים יוכלו לצפות בו (פתיחה ישירה של /landing). ה-/ עכשיו ניתוב טהור: מחובר → /shipments, אנונימי → /landing. /landing נוסף ל-publicRoutes ב-middleware כדי שאנונימיים לא יישלחו ל-/login.
- Typography: כותרת ה-Hero ומספרי ה-stats עברו מ-Rubik 900 ל-Heebo 700 עם letter-spacing פתוח יותר (-0.01em ל-Hero, -0.005em ל-stats) ו-line-height מרווח — מרגיש עגול, אוורירי וקצת יותר דק. Heebo נוסף ל-globals.css (היה מוצהר ב-tailwind אבל לא נטען בפועל).
- Marquee fix: ה-integrations marquee נעלם מהמסך באמצע הלולאה כי ב-RTL הקונטיינר נצמד לימין-pa-parent ו-translateX(-50%) הזיז אותו ימינה→שמאלה אל מחוץ למסך בלי שום תוכן שמחליק פנימה. תוקן ע"י כפיית direction:ltr על המסילה (extends right from left edge) ו-direction:rtl על כל pill (האייקון נשאר מימין לטקסט).
v01.51.000חדשdrivers/linkedminorשליחים מקושרים — פאזה 5: עצירות אישיות (PersonalStop)
פאזה 5 של פיצ'ר השליחים המקושרים.
פרטים נוספים ↓הסתר ↑
פאזה 5 של פיצ'ר השליחים המקושרים. מודל PersonalStop חדש — עצירה אישית של שליח שאינה שייכת לאף טננט (Circuit-style). הבעלים היחיד היא ה-Person, לא הטננט. השליח מוסיף עצירות אלו ב-app שלו כדי לערב משלוחים חיצוניים (פרילנס/אישיים) במסלול הקיים. כל הסכמה: כתובת מובנית + שדה fullAddress חופשי, geocoding אופציונלי, נמען, מס׳ חבילות, COD אופציונלי, scheduledFor אופציונלי, סטטוס מחזור-חיים (PENDING/COMPLETED/FAILED/CANCELED) עם timestamps נפרדים, POD אופציונלי (תמונה + חתימה). API מובייל מלא תחת /api/v1/mobile/personal-stops עם CRUD מאובטח: ה-mobile JWT נושא driverId, השרת מתרגם ל-personId דרך Driver.personId, וכל שאילתא מסוננת ל-personId של המבקש. עצירה של Person אחר מחזירה 404 בלי לדלוף שהיא קיימת. ה-status transition idempotent — קריאה עם אותו סטטוס לא יוצרת timestamp חדש מיותר. הפאזה הבאה (6) תשלב את ה-PersonalStops עם ה-route planner הקיים במובייל כך שהם ישולבו במסלול עם Visits מטננטים.
- PersonalStop model + enum PersonalStopStatus (PENDING/COMPLETED/FAILED/CANCELED) + 2 indexes (personId,status / personId,scheduledFor).
- FK ל-Person עם CASCADE — מחיקת Person מוחקת את העצירות שלו (לעולם לא מוחקים drivers בעקבות זה, רק עצירות אישיות).
- API מובייל מלא: GET/POST /api/v1/mobile/personal-stops + GET/PATCH/DELETE /[id]. כל אחד מאובטח דרך requireMobileAuth ומסונן ל-Person של המבקש.
- Status transitions idempotent: COMPLETED → completedAt + clear failed/canceled; FAILED → failedAt + clear; CANCELED → canceledAt + clear; PENDING → clear everything (re-open). קריאה עם אותו סטטוס = no-op.
- 6 בדיקות חדשות לזרימת ה-status (סה״כ 867/867 עוברות). typecheck + lint נקיים.
- Phase 6 הבאה: שילוב ה-PersonalStops ב-route planner הקיים במובייל — inbox מאוחד של Visits מכל הטננטים של ה-Person + PersonalStops אישיות.
v01.50.000חדשdrivers/linkedminorשליחים מקושרים — פאזה 4: קישור שקט חוצה-טננטים לפי אימייל
פאזה 4 של פיצ'ר השליחים המקושרים.
פרטים נוספים ↓הסתר ↑
פאזה 4 של פיצ'ר השליחים המקושרים. כשטננט מקים שליח חדש עם אימייל שכבר קיים על Person כלשהו במערכת (כולל בטננט אחר), המערכת מקשרת את ה-Driver החדש לאותו Person במקום ליצור חדש — בלי שהאדמין יודע. זוהי התשתית להמשך: באפליקציית הנייד (Phase 6) הסאב יראה משלוחים מכל הטננטים בהם הוא מוקם, וב-PersonalStop (Phase 5) יוכל להוסיף עצירות אישיות חוצות-טננטים. הקישור הוא לפי canonical email (lowercase + trim, ללא קיבוץ של + או . בחלק המקומי) — בכוונה לא לפי טלפון, כי טלפון יותר רגיש לטעויות הקלדה ו-false-match יכול לפתוח גישה לזר. אימייל הוא identifier of trust סטנדרטי לכל SaaS. אדמין שטועה באימייל = טעות אדמיניסטרטיבית, לא באג מערכתי. שום שינוי UX חשוף — האדמין רואה רק שליח חדש שנוסף לטננט שלו.
- Person.email + canonical_email: שדות חדשים על Person, indexed ולא unique (פרגמטי לבקפיל). canonicalEmail = lowercase + trim בלבד — אין +/. collapse כי הם משמעותיים אצל חלק מהספקים.
- lib/person.ts → canonicalizeEmail() + createPersonForNewDriver() עם merge מובנה: אם canonicalEmail תואם Person קיים, מוחזר ה-id שלו; אחרת נוצר Person חדש. אין auto-merge לפי טלפון — בכוונה.
- POST /api/drivers בכל 3 ה-branches (EMPLOYEE קיים, EMPLOYEE חדש, CONTRACTOR/EXTERNAL) מעבירים body.email ל-createPersonForNewDriver. ה-CONTRACTOR/EXTERNAL branch סוף-סוף שומר את האימייל (קודם נזרק לפח).
- Backfill script scripts/backfill-person-emails.ts מעתיק Employee.email → Person.email לכל Person שעדיין null. הופעל בייצור — אין נתונים לבקפיל כרגע (תיקין צעיר), אבל הסקריפט מוכן.
- 7 בדיקות חדשות ל-Person helpers (canonicalizeEmail + auto-merge happy path + miss path + no-email skip + phone-only no-merge). סה״כ 861/861 עוברות.
- Privacy: log INFO רושם 'Silent cross-tenant Person link via canonicalEmail' רק לשרת — האדמין לא יודע, אבל המערכת רואה. ניתן יהיה לאתר merge-by-mistake מאוחר יותר.
- Phase 5 הבאה: PersonalStop — עצירות אישיות של שליח שאינן שייכות לאף טננט (Circuit-style).
v01.49.000חדשdrivers/linkedminorשליחים מקושרים — פאזה 3: שכבת Person גלובלית (תשתית)
פאזה 3 של פיצ'ר השליחים המקושרים — הוספת שכבת Person גלובלית מעל ל-Driver.
פרטים נוספים ↓הסתר ↑
פאזה 3 של פיצ'ר השליחים המקושרים — הוספת שכבת Person גלובלית מעל ל-Driver. Person הוא הזהות של אדם פיזי, חוצה-טננטים: באותו Person אפשר להחזיק כמה Driver records בטננטים שונים (אצל מספר חברות במקביל). זוהי תשתית — אין שינוי UX חשוף בפאזה זו. בפאזה זו כל Driver מקבל Person משלו (1:1), בלי קיבוץ אוטומטי לפי טלפון. הקישור המפורש (אותו Person בכמה טננטים) יקרה רק דרך invite flow ידני בפאזה 4 — אנו לא מרכזים drivers אוטומטית כי false-match יכול לדלוף נתונים בין טננטים. נוסף גם canonicalPhone (צורה מנורמלת ב-972 prefix) שמאוחר יותר ישמש לחיפוש Person קיים בעת invite. backfill לכל 354 ה-drivers בייצור הושלם, ולכל driver חדש (גם דרך POST API, גם דרך sync של Lionwheel, גם דרך flow של 'שליח מזדמן') נוצר Person אוטומטית. תאימות לאחור מלאה — שום API קיים לא מחזיר/דורש personId, פשוט קיים ברקע.
- Person model חדש: id, phone (raw), canonical_phone (לחיפוש), display_name, drivers (1:N). אין unique על טלפון כדי לא לדרוס backfill בכפילויות.
- Driver.personId nullable + FK ל-Person בלי CASCADE (לעולם לא מוחקים drivers בעקבות מחיקת Person).
- Backfill script scripts/backfill-persons.ts עם dry-run + --apply. הופעל בייצור על 354 drivers בכל הטננטים — לכל אחד נוצר Person חדש משלו עם canonicalPhone מנורמל.
- lib/person.ts עם canonicalizePhone (קוד משותף עם פורמט WhatsApp) + createPersonForNewDriver + ensurePersonForExistingDriver.
- כל מסלולי יצירת Driver מטמיעים אוטומטית Person: POST /api/drivers (3 branches: EMPLOYEE קיים, EMPLOYEE חדש, CONTRACTOR/EXTERNAL), POST /api/drivers/sync-all (sync מ-Lionwheel), lib/driver-sync.ts (webhook flow — 2 paths), lib/casual-courier.ts (provisioning שליח מזדמן).
- 11 בדיקות חדשות ל-Person helpers (canonicalizePhone, createPersonForNewDriver, ensurePersonForExistingDriver). סה״כ 854/854 עוברות.
- Phase 4 הבאה: invite flow — טננט מזמין שליח לפי טלפון → אם canonicalPhone תואם Person קיים, מציעים לקשר במקום ליצור חדש.
v01.48.000חדשdrivers/linkedminorשליחים מקושרים — פאזה 2: UI מנהל-טננט מלא
פאזה 2 של פיצ'ר השליחים המקושרים — UI מלא לטננט לניהול קבלן-מנהל ↔ סאב-שליחים, על-גבי תשתית הסכמה והמנוע שנבנו בפאזה 1.
פרטים נוספים ↓הסתר ↑
פאזה 2 של פיצ'ר השליחים המקושרים — UI מלא לטננט לניהול קבלן-מנהל ↔ סאב-שליחים, על-גבי תשתית הסכמה והמנוע שנבנו בפאזה 1. כולל 3 commits בנפרד (2A backend APIs, 2B manager-side UI, 2C list polish) שיחד נותנים cycle UX מלא: אדמין מפעיל toggle 'קבלן-מנהל' בעריכת השליח → קופצת בעמוד הפרטים סקציית 'סאב-שליחים' (גלויה רק למנהלים) שמאפשרת להוסיף סאב מתוך השליחים הקיימים, להגדיר תמחור נפרד פר-קישור, ולהסיר (soft-delete חסום ע"י משלוחים פעילים). שיבוץ משלוח לשליח רגיל / מנהל / סאב מציב אוטומטית את Visit.managedByDriverId הנכון. הדוח החודשי של המנהל עובר לחישוב המאוחד (כל הצוות) עם פירוט פר-סאב לביקורת. ברשימת השליחים — סמל כתר על מנהלים ו-badge 'תחת [שם]' על סאבים. לא נוגעים בהתנהגות של טננטים שלא הפעילו את ה-feature — שום שינוי בחישובי שכר קיימים, שום שינוי בעמודים שאינם של מנהלים.
- Backend APIs (פאזה 2A): GET/POST /api/drivers/[id]/sub-drivers, GET/DELETE /api/drivers/[id]/sub-drivers/[linkId], GET/PUT /api/drivers/[id]/sub-drivers/[linkId]/pricing. כולם tenant-scoped עם DRIVERS_VIEW/DRIVERS_EDIT permission.
- helper חדש lib/driver-assignment.ts → resolveManagedByDriverId(): קובע את Visit.managedByDriverId לפי הקשר השליח (מנהל / סאב יחיד / סאב מרובה / עצמאי). מוטמע גם ב-/api/shipments/bulk-assign-driver וגם ב-/api/v1/mobile/admin/visits/[id]/assign.
- DriverForm: switch סגול 'קבלן-מנהל' בעריכה (לא ביצירה — נדרש שהשליח כבר קיים כדי להוסיף לו סאבים).
- Driver detail: SubDriversSection חדש (מוסתר אם לא מנהל) עם הוספה רב-בחירה (AddSubDriverDialog), תמחור פר-קישור (SubDriverPricingDialog), הסרה עם AlertDialog ו-409 guard למשלוחים פעילים.
- EarningsCard: כשהשליח הוא מנהל, /api/drivers/[id]/earnings מחליף ל-computeManagerTenantEarnings עם includeSubBreakdown=true — וכרטיס חדש סגול בכרטיסיה מציג פירוט פר-סאב לביקורת.
- רשימת שליחים: כתר סגול על שליחים שהם מנהלים, badge 'תחת [שם]' על סאבים (עם +N אם תחת כמה מנהלים). /api/drivers/list מחזיר עכשיו גם subLinks.
- ההסרה של סאב היא soft-delete (isActive=false + removedAt) — היסטוריה נשמרת, ביקורים שכבר רצים נסגרים נורמלי. הסרה חסומה אם יש visit פתוח תחת המנהל הזה.
- תאימות לאחור מלאה — טננט שלא הפעיל אף toggle 'קבלן-מנהל' חווה אפס שינוי. resolveManagedByDriverId מחזיר null במקרה הזה ו-Visit.managedByDriverId נשאר NULL כמו שהיה.
- 843 בדיקות עוברות (כולל 7 חדשות ל-resolveManagedByDriverId), typecheck נקי, lint נקי.
v01.47.000חדשshipments/importminorאשף ייבוא משלוחים מאוחד — צוות, פורטל לקוחות וסוכנים
אשף ייבוא המשלוחים פועל כעת באופן זהה עבור כל סוגי המשתמשים: צוות הטננט (/shipments/import), לקוח בפורטל (/portal/shipments/import) וסוכן (/shipments/import).
פרטים נוספים ↓הסתר ↑
אשף ייבוא המשלוחים פועל כעת באופן זהה עבור כל סוגי המשתמשים: צוות הטננט (/shipments/import), לקוח בפורטל (/portal/shipments/import) וסוכן (/shipments/import). אותה לוגיקה של פירוק קובץ, מיפוי עמודות מבוסס AI, תיקון שורות בעייתיות באמצעות AI, זיהוי כפילויות, ולידציה, ייבוא בזרם NDJSON ועדכון היסטוריה — בלי שום סניף קוד לכל role. בורר הלקוחות בכל מסך שואב את הלקוחות הרלוונטיים ל-session: צוות רואה הכל, סוכן רואה רק לקוחות עם Customer.agentId שלו, ולקוח פורטל רואה רק את עצמו (או את החשבונות המקושרים שלו).
- הרשאת SHIPMENTS_CREATE נוספה לתפקיד AGENT (כדי שה-POST של הייבוא והאשף עצמו ירוצו). כל שאר ההרשאות לתפקיד נשארו כשהיו.
- פריט סיידבר חדש 'ייבוא משלוחים' מתחת למשלוחים, מוצג רק לסוכנים (כי הסיידבר שלהם flat).
- הגנה בצד השרת: POST /api/shipments/import מאמת ש-customer_id שייך לסוכן (Customer.agentId === session.agentId) ומחזיר 403 אם לא — גם אם הלקוח עצמו תקין בטננט. מונע יצירת ייבוא דרך בקשה מזויפת מחוץ לאשף.
- scoping של היסטוריית ייבוא: GET/PATCH /api/shipments/import/history[/id] ו-/download מסננים ל-customerId IN (לקוחות הסוכן). סוכן רואה ומוריד רק את הייבואים של הלקוחות שלו.
- Helper חדש lib/auth/agent-scope.ts המרכז את הלוגיקה של 'מי הלקוחות של הסוכן הנוכחי' — כדי שכל endpoint עתידי שצריך הגבלה דומה ישתמש באותו דפוס.
- AI map / AI fix עבור הפורטל: עד עכשיו ה-client היה צורב נתיבי /api/shipments/import/ai-{map,fix} בקוד, ולקוחות פורטל לא יכלו להגיע אליהם (middleware חוסם CUSTOMER session מ-routes שמחוץ ל-/api/portal). נוספו /api/portal/shipments/import/ai-map ו-ai-fix — wrappers דקים על אותם lib/ai/import-column-mapper ו-import-row-fixer, מאומתים דרך requireCustomerPermission(CREATE_SHIPMENTS) במקום RBAC של הצוות. הקיימים תחת /api/shipments/import/ai-{map,fix} נשארו כפי שהיו עבור הצוות והסוכנים.
- ImportShipmentsConfig מקבל כעת aiMapEndpoint ו-aiFixEndpoint כך שה-client יודע באיזה pair endpoints להשתמש לפי ה-config שמועבר אליו. כל הנתיבים הצרובים הוסרו מהאשף.
- ה-POST של ה-ייבוא עצמו (שורת הזרם, עבודת ה-workers, פתיחת ה-audit row, סגירת הסטטוס, האירוע 'done') חולץ ל-lib/shipments/run-bulk-import.ts. שני ה-routes (/api/shipments/import ו-/api/portal/shipments/import) הפכו ל-wrappers דקים של כ-90 שורות שעושים רק auth, אימות שהלקוח שייך ל-actor (AGENT.agentId / linked group של הפורטל) וקריאה ל-runBulkImport. שינוי עתידי ב'איך ייבוא רץ' (concurrency, מבנה האירועים, התנהגות duplicates) נופל בקובץ אחד ומשפיע על שלושת הקהלים יחד.
- בדומה, check-duplicates חולץ ל-lib/shipments/find-import-duplicates.ts (כולל normalizeExternalIds המשותף) ושני ה-routes הפכו ל-wrappers; ההבדל היחיד הוא ה-customerIdScope שעובר (null לצוות, רשימת לקוחות מקושרים לפורטל).
v01.46.001תיקוןshipments/importייבוא משלוחים — אות כניסה אחרי המספר + זיהוי עיר לפי תחילית
שני תיקונים לזיהוי כתובות בקבצים ללא עמודות מובנות: (1) אות עברית בודדת אחרי המספר (כניסה/אגף) כמו 'ראובן 12/6 ב'' או 'גוש עציון 2/9 א' שברה את זיהוי מספר הבית והדירה — parseAddress מזהה כעת מספר=12, דירה=6 ואת האות כ'כנ…
פרטים נוספים ↓הסתר ↑
שני תיקונים לזיהוי כתובות בקבצים ללא עמודות מובנות: (1) אות עברית בודדת אחרי המספר (כניסה/אגף) כמו 'ראובן 12/6 ב'' או 'גוש עציון 2/9 א' שברה את זיהוי מספר הבית והדירה — parseAddress מזהה כעת מספר=12, דירה=6 ואת האות כ'כניסה', בלי לפגוע במקרים קיימים. (2) שם יישוב מקוצר/עם מקף כמו 'מודיעין-מכבים' לא זוהה כי במילון השם המלא הוא 'מודיעין מכבים רעות' — findCityMatch מקבל כעת התאמת תחילית רב-מילתית, אך ורק כשיש התאמה יחידה וחד-משמעית (שם בן מילה אחת לא מפעיל זאת, ותחילית של חצי-מילה לא נחשבת). דוגמאות שתוקנו: 'עמק חרוד 28 מודיעין-מכבים', 'מגדל הלבנון 26/5 מודיעין-מכבים', 'גוש עציון 2/9 א גבעת שמואל'. נוספו 8 בדיקות יחידה (סה"כ 91).
v01.46.000חדשdrivers/linkedminorשליחים מקושרים — פאזה 1: סכמה ומנוע חישוב (תשתית)
פאזה ראשונה (תשתית בלבד; ללא UI חשוף) של פיצ'ר שליחים מקושרים — קבלן-מנהל שמנהל סאב-שליחים, עם תמחור נפרד פר-קישור (מה הטננט משלם למנהל מול מה המנהל משלם לסאב).
פרטים נוספים ↓הסתר ↑
פאזה ראשונה (תשתית בלבד; ללא UI חשוף) של פיצ'ר שליחים מקושרים — קבלן-מנהל שמנהל סאב-שליחים, עם תמחור נפרד פר-קישור (מה הטננט משלם למנהל מול מה המנהל משלם לסאב). נוספו 4 טבלאות חדשות (DriverManagerLink וטבלאות תמחור פר-קישור) ושדות isManagerEnabled על Driver, managedByDriverId על Visit וב-CodTracking — כולם nullable/ברירת-מחדל בטוחה כך ששום נתון קיים לא נוגע ושום חישוב משכורת קיים לא משתנה. מנוע החישוב driver-earnings.ts עבר refactor פנימי: הליבה (resolveRate, applyRules, group business pickup, rollup) חולצה ל-helpers משותפים; הפונקציה הקיימת computeDriverEarnings שומרת את החתימה וההתנהגות הקודמות בדיוק; נוספו שתי פונקציות חדשות — computeManagerTenantEarnings (טננט→מנהל, אגרגציה של כל ה-visits שתחת המנהל ע"י פילטר על managedByDriverId, עם אופציית per-sub breakdown לביקורת) ו-computeManagerSubPayout (מנהל→סאב, משתמש במחירון הפר-קישור). נכתבו 54 בדיקות חדשות (29 characterization שמלכדות את ההתנהגות הקיימת + 25 לפונקציות החדשות). כל 824 הבדיקות עוברות, typecheck נקי, lint נקי. סך הכל פאזה זו אחת מתוך 7 — הפאזות הבאות יוסיפו UI לטננט, שכבת Person גלובלית (לתמיכה בשליח חוצה-טננטים), invite flow ועוד.
- סכמת DB: DriverManagerLink (N:N) + DriverLinkPricingProfile + DriverLinkZoneRate + DriverLinkPricingRule. תאימות לאחור מלאה — אין שינוי בכמות הביקורים/חישובים הקיימים.
- Driver.isManagerEnabled — toggle של אדמין שמסמן שליח כקבלן-מנהל היכול לנהל סאבים. ברירת מחדל false.
- Visit.managedByDriverId + CodTracking.managedByDriverId — מי אחראי כספית מול הטננט, כך שאפשר לעבוד עם תרחישים שבהם הסאב מבצע אבל המנהל מקבל את התשלום.
- מנוע חישוב: חילוץ ליבה ל-computeEarningsLines/rollupTotals; computeDriverEarnings נשארת בדיוק כפי שהיא.
- computeManagerTenantEarnings — מחשב את התשלום שטננט חייב למנהל, מאגד את כל הביקורים תחת המנהל (גם של המנהל עצמו וגם של הסאבים שלו), משתמש בפרופיל המנהל. תומך ב-includeSubBreakdown לפירוט פר-סאב.
- computeManagerSubPayout — מחשב כמה המנהל חייב לסאב ספציפי לפי מחירון הקישור (לא מחירון הסאב!), עם guard חוצה-טננטים שמסרב לחשב אם הקישור בטננט אחר.
- Phase 2 הבאה: UI לטננט (תיוג סאבים, סינון שיבוץ, breakdown בדוח הסטטוס).
v01.45.003תיקוןshipments/importייבוא משלוחים — זיהוי עיר בפורמטים נוספים (פסיקים ותווית דירה)
הורחבה שליפת העיר מתוך הכתובת (בקבצים ללא עמודת עיר) כדי לכסות עוד דפוסים מהשטח שזוהו בקובץ רשימת המתנה: (1) פורמט 'רחוב , עיר, מספר' — כשהמקטע האחרון אחרי פסיק הוא מספר בלבד, העיר היא המקטע שלפניו ("איזנברג , רחובות, 26…
פרטים נוספים ↓הסתר ↑
הורחבה שליפת העיר מתוך הכתובת (בקבצים ללא עמודת עיר) כדי לכסות עוד דפוסים מהשטח שזוהו בקובץ רשימת המתנה: (1) פורמט 'רחוב , עיר, מספר' — כשהמקטע האחרון אחרי פסיק הוא מספר בלבד, העיר היא המקטע שלפניו ("איזנברג , רחובות, 26" → רחובות). (2) פורמט 'עיר , רחוב מספר' — עיר ראשונה ("רהט , אלג׳זאר 13" → רהט), עם הגנה שמונעת בלבול בין רחוב לעיר. (3) תווית 'דירה N' אחרי העיר — עוגן-המספר מתעלם כעת ממספרי דירה/קומה, כך ש"לבונטין 32 ראשון לציון דירה 5" מזהה ראשון לציון ושומר את דירה 5 בכתובת. כל מועמד עדיין מאומת מול מילון היישובים, ושם-עיר שמופיע ברחוב לא נשלף בטעות. נוספו 12 בדיקות יחידה (סה"כ 80), כולל בדיקות הגנה ("רחוב, קומה 2" לא מחלץ עיר).
v01.45.002תיקוןshipments/importייבוא משלוחים — זיהוי עיר מתוך הכתובת (תיקון closure + עיגון למספר)
שליפת העיר מתוך הכתובת (בקבצים ללא עמודת עיר) לא עבדה בפועל: הפונקציה handleFile היא useCallback שנוצר פעם אחת ב-mount, ולכן ה-closure שלה על רשימת היישובים נשאר [] לתמיד — והתנאי settlements.length>0 תמיד נכשל.
פרטים נוספים ↓הסתר ↑
שליפת העיר מתוך הכתובת (בקבצים ללא עמודת עיר) לא עבדה בפועל: הפונקציה handleFile היא useCallback שנוצר פעם אחת ב-mount, ולכן ה-closure שלה על רשימת היישובים נשאר [] לתמיד — והתנאי settlements.length>0 תמיד נכשל. תוקן באמצעות settlementsRef שמחזיק את הרשימה החיה. בנוסף, splitTrailingCity שודרגה לפי החוקיות שהעיר תמיד מופיעה אחרי מספרי הכתובת: נוסף עוגן-מספר שלוקח כמועמד-עיר את כל מה שמופיע אחרי הטוקן האחרון שמכיל ספרה (תוך דילוג על אות-דירה כמו "ב'"), עם וללא פסיק. כל מועמד עדיין מאומת מול מילון היישובים — לכן לא נשלף דבר שאינו עיר אמיתית, ושם-עיר שמופיע ברחוב (לפני המספר) לא נחשב בטעות לעיר. דוגמאות שנתמכות כעת: "הבנים 10/5 אשדוד", "ראובן 12/6 ב' , בית שמש", "נחל קטלב 15/5 בית שמש". נוספו 13 בדיקות יחידה (סה"כ 73).
v01.45.001תיקוןshipments/importייבוא משלוחים — חיפוש לקוח מדויק (במקום fuzzy)
החיפוש בבורר הלקוח (וגם בבורר כתובת שמורה) בזרימת ייבוא המשלוחים השתמש במנגנון ה-fuzzy הדיפולטי של cmdk, שמותאם לאנגלית ובעברית התנהג גרוע: הקלדת חלק ממילה ("הילו") החזירה שמות לא-קשורים שבהם האותיות פזורות לאורך הטקסט, …
פרטים נוספים ↓הסתר ↑
החיפוש בבורר הלקוח (וגם בבורר כתובת שמורה) בזרימת ייבוא המשלוחים השתמש במנגנון ה-fuzzy הדיפולטי של cmdk, שמותאם לאנגלית ובעברית התנהג גרוע: הקלדת חלק ממילה ("הילו") החזירה שמות לא-קשורים שבהם האותיות פזורות לאורך הטקסט, בעוד שההתאמה המדויקת ("הילולה") לא דורגה למעלה עד שהוקלדה המילה במלואה. הוחלף בפילטר substring שמדרג לפי רלוונטיות: התאמת תחילית מלאה > מילה שמתחילה במחרוזת > הכלה כלשהי > מוסתר. הפילטר אדיש לסוג הגרש/מירכאות (בע"מ / בע״מ) ולאותיות גדולות/קטנות. שינוי תוסף-בלבד — כל חיפוש של רצף תווים רציף שעבד קודם ממשיך לעבוד; רק רעש הרצף-המפוזר נעלם. נוספו 10 בדיקות יחידה.
v01.45.000שיפורshipments/importminorייבוא משלוחים — תמיכה בקבצי WooCommerce, רשימות מתנה וכתובות מורכבות
מנגנון ייבוא המשלוחים הורחב משמעותית כדי לקלוט קבצים אמיתיים מהשטח שעד היום נכשלו, מבלי לשנות את ההתנהגות עבור התבנית הרגילה.
פרטים נוספים ↓הסתר ↑
מנגנון ייבוא המשלוחים הורחב משמעותית כדי לקלוט קבצים אמיתיים מהשטח שעד היום נכשלו, מבלי לשנות את ההתנהגות עבור התבנית הרגילה. מזוהות אוטומטית כותרות של ייצוא WooCommerce ושל רשימות מתנה/קהילה, כתובת חופשית מפוצלת לרחוב/מספר/קומה/דירה/כניסה, שורת הכותרת מאותרת גם כשהיא לא בשורה הראשונה, עיר נשלפת מתוך הכתובת כשאין עמודת עיר, ועמודת מספר סידורי מזוהה ולא מוחלפת במספר הבית. כל ההיוריסטיקות החדשות מגודרות כך שהן פועלות רק כשאין עמודה ייעודית — תבנית רגילה עוברת ללא כל שינוי.
- זיהוי כותרות WooCommerce ((Billing)/(Shipping)) + Customer Note, וכותרות רשימת מתנה (פרטי / משפחה / נייד / כתובת מגורים / הערות / כמות).
- parseAddress — פיצול כתובת חופשית לרחוב/מספר/קומה/דירה/כניסה: מספר בעקבות רחוב, מספר/דירה (4/4), חלקים בפסיקים, סדר הפוך ("6 התחדשות"), אנגלית ("8 Shamgar Street, 44"), ותוויות עברית (קומה/דירה/בניין/כניסה). תאימות מלאה לפרסר הישן כולל סיומת לטינית ("10A").
- detectHeaderRowIndex — איתור שורת הכותרת גם כשקודמות לה שורות כותרת/ריקות (נפילה בטוחה לשורה 0 לתבנית רגילה).
- splitTrailingCity — שליפת יישוב מוכר מסוף הכתובת כשאין עמודת עיר ("השקד 7 מודיעין" → עיר מודיעין). פועל רק ללא עמודת עיר, ולעולם לא צורך את כל המחרוזת.
- isSerialCounterColumn — זיהוי עמודת מספר סידורי (1,2,3…) שלא תחליף את מספר הבית האמיתי. מגודר להרצה רק לצד עמודת כתובת ועל רצף אורדינלי כמעט-מושלם.
- השינוי תוסף-בלבד ומגודר: כל היוריסטיקה חדשה פועלת רק בהיעדר עמודה ייעודית. נוספו 49 בדיקות יחידה (כולל בדיקות אי-רגרסיה לפרסר הישן).
v01.44.004חדשmobile authהתחברות עם גוגל באפליקציית השליחים
כפתור 'המשך עם Google' בדף הכניסה של אפליקציית המובייל הופעל.
פרטים נוספים ↓הסתר ↑
כפתור 'המשך עם Google' בדף הכניסה של אפליקציית המובייל הופעל. ה-idToken מגוגל מאומת בצד השרת מול tokeninfo API, ולאחר מכן מונפקים JWT tokens רגילים — זרימת האימות זהה להתחברות ע"י אימייל/סיסמה.
v01.44.003חדשlanding page, marketingאתר תדמית — דף נחיתה ל-shipnest.io
דף הבית (/) הפך לאתר תדמית מקצועי במקום ריידיירקט ישיר לאפליקציה.
פרטים נוספים ↓הסתר ↑
דף הבית (/) הפך לאתר תדמית מקצועי במקום ריידיירקט ישיר לאפליקציה. הדף כולל: Hero עם דשבורד מדומה ואנימציות, סקציית פיצ'רים, סטטיסטיקות, תמחור (3 מסלולים), חוות דעת לקוחות, CTA ו-Footer. עיצוב Midnight Neon — כהה עם אקסנט סגול/ציאן.
v01.44.002תיקוןshipments/picking, warehouse/inbound, analytics, messaging, customers, webhooks, recycle-binתיקון: 'הסר מליקוט' מציג הודעת שגיאה כשפעולה לא בוצעה
תוקן: בכל נקודות הקוד שקוראות ל-/api/picking/remove — בדף פרטי משלוח (שני מקומות), בתפריט פעולות ליקוט ובסרגל הפעולות הקבוצתיות — הקוד בודק כעת את removedCount בתגובת ה-API.
פרטים נוספים ↓הסתר ↑
תוקן: בכל נקודות הקוד שקוראות ל-/api/picking/remove — בדף פרטי משלוח (שני מקומות), בתפריט פעולות ליקוט ובסרגל הפעולות הקבוצתיות — הקוד בודק כעת את removedCount בתגובת ה-API. אם removedCount=0 (המשלוח לא נמצא ב-pending_picking), מוצגת הודעת שגיאה במקום הודעת הצלחה. כמו כן: ה-Prisma client עודכן (pnpm generate) — endpoint /api/warehouse/inbound החזיר HTTP 500 בגלל enum WhInboundStatus שחסר מהקליינט הישן. שיפורי ביצועים: נוספו 5 אינדקסים חסרים ל-analytics (regionCode, destinationCity, sourceProvider, urgency ו-covering index לraw SQL). נוסף Redis cache ל-analytics routes (TTL 5 דקות). שליחת WhatsApp מהירה ×100 — credentials מבוצעים פעם אחת ל-5 דקות (ביטול PBKDF2 חוזר). חיפוש לקוח לפי טלפון עבר מ-O(N) ל-O(1) — עמודת phone_normalized עם index. dispatch-webhooks נוסף ל-cron schedule (*/2). הפחתת delay בין הודעות WhatsApp מ-2000ms ל-500ms. הגדלת batch שליחת הודעות מ-10 ל-50 לתמיכה בטנאנטים עם עומסי שליחה גבוהים. נוספה מדיניות שמירה של 6 חודשים לסל המיחזור — cron יומי מוחק לצמיתות משלוחים שנמחקו לפני יותר מ-6 חודשים (ב-batches של 200), ה-UI מציג הודעה מתאימה.
v01.44.001שיפורshipments/table, layout/header, shipments/pickingאישור לפני שכפול משלוח — יחיד ומרובה
נוסף דיאלוג אישור מעוצב לפני כל פעולת שכפול משלוח: גם בשכפול משלוח יחיד (תפריט שורה) וגם בשכפול מרובה (סרגל פעולות בחירה).
פרטים נוספים ↓הסתר ↑
נוסף דיאלוג אישור מעוצב לפני כל פעולת שכפול משלוח: גם בשכפול משלוח יחיד (תפריט שורה) וגם בשכפול מרובה (סרגל פעולות בחירה). הדיאלוג מציין כמה משלוחים יישכפלו ומבקש אישור מפורש לפני הביצוע. כמו כן, האייקונים בסרגל העליון צופפו במובייל: כפתור מסך-מלא מוסתר, גודל אייקונים h-8 במובייל, ורווח מצומצם — לאפשר לשורת החיפוש הגלובלי מספיק מקום. תוקן: האפשרות 'הסר מליקוט' בתפריט הסטטוס מוסתרת עבור משלוחים בסטטוס נלקט (API תומך רק ב-pending_picking).
v01.44.000חדשwarehouse/3pl, warehouse/billing, db/schemaminorמחסן 3PL — פאזה 5: חיוב חודשי (אחסון + ליקוט + קליטה)
פאזה 5 וגם הפאזה הסוגרת של תכנית ה-3PL: השכבה הכספית.
פרטים נוספים ↓הסתר ↑
פאזה 5 וגם הפאזה הסוגרת של תכנית ה-3PL: השכבה הכספית. הטננט מגדיר תעריפים ללקוח (אחסון ליחידה ליום, ליקוט ליחידה, קליטה ליחידה); המערכת רושמת אירוע חיוב לכל ליקוט וקליטה אוטומטית, ו-cron יומי כותב snapshot של כמות הסחורה במלאי × תעריף האחסון. דוח חודשי מציג סיכום וכלל אירועי החיוב פר-לקוח. כל אירוע מקובע ב-unitRate שהיה תקף ברגע הרישום, כך ששינוי תעריף בעתיד לא משנה אירועים היסטוריים.
- schema: שני מודלים חדשים — WhBillingRate (per-customer, effective_from/to, decimal(10,4) לכל תעריף) ו-WhBillingEvent (immutable: type/quantity/unitRate/amount, אינדקסים [tenantId, customerId, occurredAt] ו-[tenantId, customerId, type, occurredAt] לדוחות מהירים). enum חדש WhBillingEventType: STORAGE_DAILY / PICK / RECEIVE.
- helper מרכזי lib/warehouse/billing.ts עם findActiveRate (החזרת התעריף התקף בנקודת זמן), createPickEvent, createReceiveEvent, ו-createStorageDailySnapshot. כל פונקציה fire-and-forget: catch בלוקאל, log לשגיאה, וחזרה null במקום לזרוק — כך שכישלון חיוב לעולם לא ישבור פעולה תפעולית.
- endpoints: GET/POST /api/warehouse/billing/rates (יצירת תעריף חדש סוגרת אוטומטית תעריף קודם פעיל באותו תאריך התחלה), GET /api/warehouse/billing/report (סיכום by-type לטווח תאריכים + רשימת events). הרשאות: WAREHOUSE_BILLING_VIEW לקריאה, WAREHOUSE_BILLING_MANAGE לכתיבת תעריפים (שתיהן הוגדרו בפאזה 0).
- hooks: pick-tasks/[id] confirmPick מפעיל createPickEvent, ו-inbound/[id]/receive מפעיל createReceiveEvent לכל פריט שנקלט. שניהם רצים אחרי כל ה-validation והעדכונים — אם נכשל, האירוע התפעולי נשמר אבל אין חיוב (נראה בלוג).
- cron יומי /api/cron/warehouse-billing-storage-snapshot ב-02:00 — סורק את כל ה-rates הפעילים, ועבור כל לקוח: סוכם sum(WhInventory.quantity) במיקומים של הטננט (location.ownerCustomerId IS NULL) שבהם המוצרים שייכים ללקוח. idempotent: אם כבר נכתב event STORAGE_DAILY לאותו (tenant, customer, day) — מדלג.
- UI: /warehouse/billing — דשבורד עם בוררי לקוח + חודש, 4 summary cards (אחסון/ליקוט/קליטה/סך הכל), תעריף פעיל מוצג בכרטיס נפרד, וטבלת events מפורטת. הוסף קישור 'חיוב 3PL' לסיידבר (DollarSignIcon, גלוי למי שיש WAREHOUSE_BILLING_VIEW).
- BillingRateDialog — טופס יצירת תעריף עם בורר לקוח, 3 שדות תעריף (אחסון/ליקוט/קליטה) ב-decimal(10,4), ובורר תחילת תוקף.
- פורטל לקוח: הדוח (/api/warehouse/billing/report) נגיש גם למשתמשי פורטל — מוצמד אוטומטית ל-customerId שלהם. (UI בפורטל יבוא בעתיד אם יהיה צורך.)
- vercel.json: cron חדש הוסף לרשימה. הצבת השעה ב-02:00 IL בכוונה — מאוחר מספיק שכל activity של היום סוכם, וגם מוקדם מספיק לפני שעות העבודה.
- סך הכל פאזה 5 סוגרת את כל 7 הפאזות של תכנית ה-3PL. הזרם המלא מסחורה ועד חיוב פועל end-to-end: לקוח שולח מלאי → קליטה (RECEIVE event) → אחסון יומי (STORAGE_DAILY events) → הזמנה מ-Shopify → ליקוט (PICK event) → דוח חודשי מסכם הכל.
v01.43.001שיפורcustomers/portal-users, warehouse/3pl, shipments/3pl, db/schema, shared/alertsפורטל לקוחות — איפוס חשבון בעלים במקום מחיקה
במסך משתמשי פורטל הלקוח, כפתור המחיקה (🗑️) לבעלים הוחלף בכפתור 'אפס חשבון' (↺).
פרטים נוספים ↓הסתר ↑
במסך משתמשי פורטל הלקוח, כפתור המחיקה (🗑️) לבעלים הוחלף בכפתור 'אפס חשבון' (↺). הסיבה: בעלים לא ניתן באמת למחוק — ה-GET של /api/customers/[id]/users מפעיל auto-provision מחדש מאימייל הלקוח, ולכן מחיקה רק יוצרת אותו מחדש מאפס. עכשיו הפעולה מפורשת: היא מנקה passwordHash, googleId, lastLoginAt ו-inviteAcceptedAt, מחזירה ל-PENDING ויוצרת inviteToken חדש ושולחת מייל הזמנה — שורת ה-CustomerUser עצמה נשמרת (כולל createdAt ומי הזמין). עבור משתמשים שאינם בעלים — כפתור המחיקה נשאר כפי שהיה. נוסף endpoint POST /api/customers/[id]/users/[userId]/reset (CUSTOMERS_EDIT, fails-closed עם 400 אם המשתמש לא owner-grade). שלושה ניקויים בכרטיס היעד: (א) איקון קוד כניסה — היה אמבר/כתום כדי להבליט לשליחים, סודר לאפור נייטרלי בעקבות הסימטריה החדשה עם שאר השדות. (ב) הוסרו כפתורי "העתק מספר" שהיו ליד כל טלפון — כפילות מיותרת כי יש העתקה כללית של כל הכרטיס בכפתור העליון. (ג) כפתור "+ הוסף טלפון" שהיה תופס שורה נפרדת מתחת לרשימת הטלפונים, הועבר ל-IconButton קטן (+) בצד הימני של השורה ליד הטלפונים — לא תופס שורה נוספת. (2) מחסן 3PL — פאזה 4a (התשתית לקישור משלוח↔מלאי): נוספו שני שדות חדשים ל-schema שמגשרים בין מודול ה-3PL למודול המשלוחים. (א) Shipment.pickMode VARCHAR(20) — שדה אופציונלי שמסמן את מצב הליקוט של משלוח: 'TENANT' (הטננט מלקט מהמלאי שמאוחסן אצלו), 'SELF' (הלקוח מלקט בעצמו), או null (לא רלוונטי לזרם 3PL — ברירת המחדל). אינדקס חדש [tenantId, pickMode] לתמיכה ב"מה ממתין לליקוט אצלי" בעתיד. (ב) OrderItem.whProductId TEXT עם FK ל-WhProduct (onDelete: SetNull) — מקשר line item של משלוח למוצר מזוהה בקטלוג המחסן. אופציונלי כדי לא לשבור משלוחים שאינם 3PL; כאשר pickMode=TENANT, הקישור הזה הוא מה שמאפשר ל-picker לדעת איזה מוצר במלאי לקחת. ב-API: PATCH /api/shipments/[id] מקבל את pickMode (TENANT/SELF/null) ב-schema הקיים, ו-FIELD_LABELS עודכן לתיעוד activity log. ב-UI: בסקציית הליקוט בעמוד פרטי המשלוח נוסף dropdown 'מצב 3PL' עם 3 ערכים, ליד הבוררים הקיימים של pickingType ו-pickingCategory. הזרם הקיים תואם לאחור — משלוחים ללא pickMode פשוט מציגים 'לא רלוונטי'. מה שטרם הוטמע: מימוש adapter לסנכרון אוטומטי של pickMode מ-Shopify/Woo, ועריכה של OrderItem.whProductId דרך UI (כרגע ניתן רק דרך API). זה יבוא בפאזה 4b. נוסף סטטוס חדש לגוביינא: "בוטלה" (CANCELED). ההגיון הקיים שמ-PENDING אי אפשר לשנות סטטוס ידנית (כדי שלא יסמנו "בידי השליח" לפני שהמשלוח באמת נמסר) נשמר — אבל יש עכשיו חריג אחד: ניתן לשנות מ-PENDING ל-CANCELED. ב-UI: כשהסטטוס PENDING, ה-dropdown מציג רק "בוטלה" כאופציה; בשאר הסטטוסים — "בוטלה" נוסף לרשימה הרגילה. CANCELED הוא final + settled (לא ניתן להמשיך ממנו, ולא מופיע ברשימת ה-open). מקרי שימוש: משלוח שבוטל לפני שיצא — הגוביינא נסגרת ללא גבייה. (4) מחסן 3PL — פאזה 4b (סוגרת את הלולאה ייבוא→ליקוט): השלמה של פאזה 4a בכמה דרכים. (א) Adapter — lib/warehouse/order-import.ts: כל הזמנה שמיובאת מ-Shopify/Woo דרך חיבור פר-לקוח מסומנת אוטומטית pickMode=TENANT (הטננט הוא ה-picker בכל הזרמים האלה — תואם גישה B). בנוסף, ה-line items מיוצאים עם whProductId מאוכלס לפי המיפוי שב-WhStoreProductLink, כך שה-picker בעמדת הליקוט יודע בדיוק איזה מוצר מהמלאי לקחת. SKUs שלא מקושרים נרשמים ב-WhStoreSyncLog (התנהגות קיימת) — אז הזרם נמשך כרגיל גם בלי הקישור. (ב) API — PUT /api/shipments/[id]/order-items מקבל עכשיו whProductId לכל פריט; ב-validate נאכף שבעלות המוצר המקושר תואמת ל-customerId של המשלוח (invariant: בעלים של ה-product = בעלים של ה-shipment — לא לערבב מוצרי לקוח X במשלוח של לקוח Y). מענה 422 כש-mismatch, 404 כש-product לא נמצא. (ג) GET /api/shipments/[id] מצרף את שדות whProduct (id/sku/name) לכל orderItem כדי שה-UI יוכל להציג שם מוצר ידידותי בלי קריאה נוספת. (ד) OrderItemsCard — כש-pickMode=TENANT, נוסף עמודה 'מוצר במחסן' עם dropdown שטוען את מוצרי הלקוח של המשלוח (דרך /api/warehouse/products?ownerCustomerId=...) ומאפשר קישור/ביטול קישור פר-פריט. במצב קריאה (canEdit=false) מציג badge עם SKU + שם. כש-pickMode != TENANT, העמודה מוסתרת לחלוטין כדי לא לעמיס על משלוחים שאינם 3PL. הזרם נשלם: הזמנה מ-Shopify → Shipment(pickMode=TENANT) + OrderItem(whProductId) → המלקט רואה את הקישור → מלקט מהמלאי במחסן. 702 tests עוברים, 0 lint errors. (5) תיקון התראת טעות-מיון: כש-Dialog/Drawer של Radix או Vaul פתוח, הם מגדירים pointer-events:none על ה-body — וה-overlay של ההתראה (z-9999) יורש את זה ולכן הלחצן 'סגור' לא הגיב והמשתמש היה חייב לרענן את העמוד ולאבד טופס באמצע עריכה. נוסף pointer-events-auto ל-overlays של SortingErrorAlertListener ו-ShipmentAlertListener (בשני המצבים — fullscreen ו-banner). (6) עמוד המעקב הציבורי של הלקוח (/tracking/[id]) — תוקנה הצגה מטעה של 'צפי מסירה' עם תאריך ושעה מתוך visit.visitAt, שגרמה למצבים שבהם משלוח בסטטוס 'בהעברה' (IN_TRANSFER, בין מחסנים) הראה ללקוח שהמשלוח יסופק היום ויצר ציפייה שגויה ותלונות. הוסרה תווית 'צפי מסירה' מסקציית 'יצא להפצה' בציר הזמן, ובמקומה נוסף כרטיס מובלט מתחת לתיאור הסטטוס: 'צפי הגעה משוער — עד <תאריך מקסימלי>' עם משנה-תיאור 'יעד רגיל — עד 3 ימי הפצה' או 'יעד חריג — עד 7 ימי הפצה' לפי deliveryType של היישוב (REGULAR/EXTENDED) מטבלת הישובים. החישוב משתמש ב-addBusinessDays(delayClockStartedAt ?? createdAt, 3|7) — נקודת ההתחלה היא תאריך הגורם (delay_clock_started_at) שנקבע במעבר ראשון לסטטוס פעיל, עם fallback ל-createdAt עבור משלוחים שטרם נכנסו לשרשרת. הצפי מוצג רק במשלוחים פתוחים (לא נמסר/נכשל/בוטל). חיפוש היישוב מתבצע מול Settlement (case-insensitive) עם נפילה ל-SettlementAlias; כשהיישוב לא מוכר נופלים בברירת מחדל ל-EXTENDED (7 ימים) — fail-safe כדי לא להבטיח ללקוח מועד מוקדם מדי. API endpoint /api/public/shipments/[publicId]/tracking הורחב להחזיר estimatedArrivalBy ו-estimatedDeliveryDays. (7) באותו עמוד מעקב, סטטוס IN_TRANSFER הוחלף מנקודת מבט הנמען מ"יצא להפצה" ל"במרכז המיון": IN_TRANSFER מסמן הברה בין מרכזי מיון *לפני* יציאה להפצה לאזור הלקוח, ולכן הצגתו כשלב 'יצא להפצה' (עם איקון משאית כחול) יצרה אצל הנמען ציפייה שהמשלוח כבר על משאית בדרך אליו. תוקן ב-STATUS_ACTIVE_STEP — IN_TRANSFER ממופה עכשיו לשלב 1 ('נקלט במרכז המיון') ולא לשלב 2; STATUS_LABEL הוחלף מ'בהעברה' ל'במרכז המיון'; STATUS_DESCRIPTION עודכן מ'החבילה בדרכה אליך דרך תחנת ביניים' ל'החבילה בתהליך מיון ומועברת בין מרכזי מיון לקראת היציאה להפצה'. גם תיאור IN_INVENTORY עודכן ל'החבילה במרכז המיון, בתהליך מיון לאזורך' (במקום 'מועברת לנציג ההפצה') כדי שלא יבטיח שלב הבא. השינוי קוסמטי לחלוטין בצד הלקוח — הסטטוס הפנימי במערכת ושאר הזרמים נשארים כפי שהיו.
v01.43.000חדשwarehouse/3pl, warehouse/inbound, db/schemaminorמחסן 3PL — פאזה 3: זרם קליטת סחורה רשמי (Inbound)
פאזה 3 של מודול ה-3PL מציגה לראשונה זרם קליטה רשמי לסחורת לקוחות פורטל.
פרטים נוספים ↓הסתר ↑
פאזה 3 של מודול ה-3PL מציגה לראשונה זרם קליטה רשמי לסחורת לקוחות פורטל. עד היום, הוספת מלאי על-שם לקוח הייתה רק דרך InventoryAdjustDialog (ad-hoc, ללא תיעוד שהסחורה הגיעה מהלקוח). עכשיו: הלקוח (או staff) יכול ליצור 'תעודת קליטה צפויה' מראש עם פירוט מוצרים וכמויות, ובהגעת הסחורה ה-staff מאשר קליטה פיזית ובוחר מיקום לכל פריט — והמערכת מעדכנת את ה-WhInventory אוטומטית. נוסף עמוד חדש /warehouse/inbound עם תיבת כניסה (פעילות / כל הסטטוסים / לפי סטטוס בודד), בורר scope (לקוח/הכל/טננט) למי שיש WAREHOUSE_MANAGE_CUSTOMER_DATA, ופעולות שורה: 'קלוט' / 'פרטים' / 'ביטול' (רק לפני קליטה).
- schema: שני מודלים חדשים WhInboundShipment + WhInboundShipmentItem עם enums WhInboundStatus (EXPECTED/PARTIAL/RECEIVED/CANCELLED) ו-WhInboundSource (CUSTOMER_PRENOTIFIED/TENANT_ADHOC). owner_customer_id NOT NULL כי קליטה תמיד שייכת ללקוח. אינדקסים לפי (tenant_id, owner_customer_id) ו-(tenant_id, status).
- endpoints: POST/GET /api/warehouse/inbound (יצירה + רשימה עם סיכום items+qty), GET/DELETE /api/warehouse/inbound/[id] (פרטים מלאים עם hydration של product/location, ביטול רק במצב EXPECTED), ו-POST /api/warehouse/inbound/[id]/receive שמבצע את הקליטה: מעדכן/יוצר WhInventory בתוך transaction, מעדכן status האב (PARTIAL/RECEIVED) לפי מה שנקלט בפועל, ופולט inventoryChangedEvents שמתפעלים סנכרון לחנויות מקושרות.
- ה-receive חוסם owner mismatch במיקום: אם כבר קיים מלאי באותו (location, product) עם בעלים אחר — 409. תואם invariant של פאזה 2.
- UI: InboundCreateDialog מאפשר ל-staff לבחור לקוח, מחסן, אסמכתה, תאריך צפוי, והוספת/הסרת שורות פריטים. InboundReceiveDialog מציג כל פריט עם expectedQty קודם + שדה receivedQty (ברירת מחדל: הנותר) + בורר מיקום לבחירה.
- הרשאה חדשה שכבר הוגדרה בפאזה 0 — WAREHOUSE_INBOUND_RECEIVE — נדרשת עכשיו לאישור הקליטה. סטף ללא ההרשאה יכול לראות וליצור תעודות צפויות אבל לא לאשר קליטה פיזית.
- הוספת קישור 'קליטה' לסיידבר בקבוצת המחסן, מתחת ל'מלאי'. גלוי לכל מי שיש לו WAREHOUSE_INVENTORY_VIEW.
- schema migration רץ דרך prisma db execute (Prisma Postgres אינו תומך ב-prisma migrate dev). הקובץ נשמר ב-packages/database/prisma/migrations/add_inbound_shipments.sql לתיעוד.
- אינטגרציה לחיוב (פאזה 5) — האירוע של RECEIVE עדיין לא נוצר כ-WhBillingEvent; הגעה לזה בפאזה 5. הזרם הנוכחי מספק את התשתית לכך.
- השלמת זרם end-to-end לדיווח לקוח: לקוח שולח 100 יח׳ ל-staff (ידנית/מערכת), נוצרת תעודה EXPECTED → staff קולט בפועל ובוחר מיקום → המלאי מופיע ב-/warehouse/inventory בסקופ של הלקוח.
v01.42.002תיקוןmobile/pod, mobile/dev-tooling, shipments/detail, shipments/sync, db/schema, shipments/activity-log, warehouse/inventory, warehouse/locations, warehouse/3pl, portal/storesmobile — כרטיסיית הוכחת מסירה ישירה בדף הביקור + תשתית ngrok, ותיקון מיפוי הערות לליונוויל
נוספה כרטיסיית 'הוכחת מסירה' ישירה בדף פרטי הביקור, מקבילה לכרטיסיות לקוח/כתובת/חבילה: הכרטיסייה מציגה תמונות POD קיימות, סטטוס חתימה, ואפשרות לצלם/עדכן ולהוסיף חתימה ישירות מהדף — ללא צורך לאתר את הכפתור בשורת הפעולות ה…
פרטים נוספים ↓הסתר ↑
נוספה כרטיסיית 'הוכחת מסירה' ישירה בדף פרטי הביקור, מקבילה לכרטיסיות לקוח/כתובת/חבילה: הכרטיסייה מציגה תמונות POD קיימות, סטטוס חתימה, ואפשרות לצלם/עדכן ולהוסיף חתימה ישירות מהדף — ללא צורך לאתר את הכפתור בשורת הפעולות הדביקה. תוקן גם: מסך סיכום ה-POD הפך ל-ScrollView כך שכפתור 'הוסף חתימה' גלוי גם כשהתוכן ממלא את המסך. הוספת @expo/ngrok ו-@expo/ngrok-bin לתשתית הפיתוח לתמיכה ב-Expo tunnel. הכנת האפליקציה לפרסום בחנויות: גרסה שודרגה ל-1.0.0, נוספו buildNumber (iOS) ו-versionCode (Android), הוגדרה שדה icon, ו-eas.json עודכן עם הגדרות submission לשתי החנויות. תוקן שורש שגיאת React hydration #418 שחזרה בכל דף מאומת: useInboxUnread אתחל את ה-badge של "הודעות" מ-localStorage ישירות כ-lazy useState initializer — השרת החזיר 0, הקליינט קרא ערך שמור; שינוי ל-useState(0) עם טעינת localStorage ב-useEffect מסנכרן את שני הצדדים. בנוסף — תוקן באג עמוק במיפוי הערות מול ליונוויל: השדה destinationNotes ("הערת מיקום") נשלח לליונוויל כ-notes ברמת ה-task, אבל אצל ליונוויל זה בעצם "הערת משלוח", לא הערת היעד. התוצאה: עריכת "הערת מיקום" אצלנו דרסה את הערת המשלוח בליונוויל, וב-webhook הבא הערך עבר אצלנו ל-shipmentNote בעוד destinationNotes התאפס. במקביל הערת היעד האמיתית של ליונוויל (DELIVERY visit notes) אף פעם לא הסתנכרנה. תיקון: destinationNotes → destination_notes (DELIVERY visit), shipmentNote → notes (task-level) — שני המיפויים אומתו empirit. נוסף שדה חדש sourceNotes ("הערת מוצא") הממופה ל-source_notes (PICKUP visit), עם עמודה חדשה ב-DB, סכמה ב-PATCH, ועריכה ב-SourceCard. השדה הקיים shipmentNote הפך לעריך (היה תצוגה בלבד) ומסתנכרן עכשיו ל-task-level notes של ליונוויל. שיוך השמות אצלנו אוחד עם ליונוויל: "הערת מיקום" → "הערת יעד" ב-9 מקומות בקוד; aliases של ייבוא קובץ נשמרו. הערה ארגונית (org_note) עדיין silent-fail בצד ליונוויל — מטופל מולם. (2) היסטוריית פעולות במשלוח — מבצע הפעולה הופרד מתיאור הפעולה. נוספו עמודות actor_type ו-actor_name לטבלת audit_logs, ובכל הכתיבות הקיימות (webhook ליונוויל ב-data-service.ts, recompute אוטומטי של roundtrip ו-return) נשמר עכשיו מי המבצע: webhook → actorType='webhook' actorName='Lionwheel', recompute אוטומטי → actorType='system'. ה-details נוקה ממידע על המקור — לא יותר 'נוצר מ-Webhook ליונוויל (task_created)' או 'עודכן מ-Webhook ליונוויל (manual_create): סוג משימה'; עכשיו רק 'משלוח נוצר' / 'עודכנו השדות: ...'. ב-UI של ActivityLogTimeline נוסף ActorBadge שמופיע במקום שם המשתמש כש-userName=null — עם איקון ליונוויל וטקסט 'Webhook מליונוויל' לכל פעולה שמגיעה מהוובהוק, או 'מערכת' / 'Cron' / 'API' לפי actorType. בכך, שינוי סטטוס שמגיע מליונוויל מציג בעמודת מבצע הפעולה 'Webhook מליונוויל' במקום להישאר ריק. חיזוק הודעת ה-toast לשדה הערה ארגונית: עד עכשיו נאמר רק "לעדכן ידנית בליונוויל", עכשיו ההודעה מזהירה גם שאם לא יעודכן שם בהקדם — הערך יידרס בוובהוק הבא של ליונוויל. שדות מוצא המשלוח (שם/עיר/רחוב/מספר/טלפון) הפכו לעריכים ב-SourceCard ומסתנכרנים לליונוויל (PICKUP visit) — עד עכשיו הם היו תצוגה בלבד למרות שה-API של ליונוויל תומך בכולם. השדה sourceName נשאר read-only כאשר המשלוח מקושר ללקוח (CustomerNameDisplay עם לינק לכרטיס לקוח), אחרת הוא עריך כמו השאר. (25) מחסן — תיקון פער UX: נוסף כפתור 'הוסף פריט מלאי' בטולבר של /warehouse/inventory ובפורטל הלקוח. עד היום היה אפשר רק לערוך כמות של פריט מלאי קיים בטבלה — לא היה מסלול UI ליצירת השורה הראשונה של מוצר במיקום (WhInventory). דיאלוג חדש InventoryCreateDialog עם בוררי מחסן/מיקום/מוצר וכמות, רמז לכמות נוכחית אם כבר קיים שילוב, ו-pre-fill מהפילטרים הנוכחיים של הטבלה. ה-API הקיים /api/warehouse/inventory/adjust (upsert) משמש בלי שינוי. ה-empty state המטעה שהפנה ל'עמוד המיקומים' (שלא היה בו את היכולת הזו) הוחלף בהנחיה ללחוץ על הכפתור החדש. (26) מחסן — יצירת מיקום הפכה פשוטה יותר: עד היום היו 3 שדות חובה (אזור, מעבר, מדף) — לא מתאים למחסנים קטנים שאין בהם היררכיה עמוקה. עכשיו רק 'אזור' חובה; מעבר/מדף/קומה/תא — אופציונליים. הסכמה ב-POST/PUT של /api/warehouse/locations הוקלה, הטופס מציין '(אופציונלי)' ב-placeholder, ה-section header מסביר שאזור חובה ושאר השדות אופציונליים, וטור הטבלה מציג '—' כשהשדה ריק (כמו שהיה בקומה/תא). buildLocationCode ממילא מסנן ערכים ריקים, אז קוד המיקום הוא 'A' למחסן עם רק שדה אזור. (27) מחסן 3PL — פאזה 0 (תשתית בלבד, אין שינוי משתמש עדיין): הוספת הבסיס הקודי לתמיכה ב-multi-customer warehouse שבה הטננט מאחסן ומלקט עבור לקוחות פורטל. ב-lib/warehouse/owner-scope.ts נוספו getReadScope ו-resolveWriteOwner (הראשון להחזרת Prisma where fragment עם תמיכה ב-'all' כדי לראות את כל הלקוחות של הטננט; השני להחלטה על ה-ownerCustomerId שיירשם בשדה החדש). ה-API של getOwnerScope המקורי נשאר תואם לאחור — כל ה-routes הקיימים ממשיכים לעבוד בלי שינוי. נוספו 4 הרשאות חדשות: WAREHOUSE_MANAGE_CUSTOMER_DATA, WAREHOUSE_INBOUND_RECEIVE, WAREHOUSE_BILLING_VIEW, WAREHOUSE_BILLING_MANAGE — עם תיאורים, קטגוריה והקצאה ל-OWNER/PLATFORM_ADMIN/WAREHOUSE_MANAGER. נוספו שני flags חדשים בכותרת isMultiCustomerWarehouseEnabled ו-isWarehouseBillingEnabled שקוראים מ-tenant.settings.modules.warehouse_multi_customer / warehouse_billing (שניהם דורשים גם את ה-flag הבסיסי של warehouse). 17 בדיקות חדשות עוברות (10 ל-scope helpers + 7 ל-feature flags), סה"כ 689 tests עוברים. נוספו ב-SourceCard 3 שדות חדשים סימטריים ליעד: קומה, דירה, אימייל. עמודות חדשות ב-DB, mapping ב-lionwheel-sync ל-PICKUP visit, וקליטה מה-webhook. אומת empirit על משלוח 25020207. (28) מחסן 3PL — פאזה 1 (קריאה רב-לקוחית): נחשפה לראשונה היכולת ל-staff לראות נתוני מלאי ומוצרים של לקוחות הטננט. נוסף helper API מרכזי (lib/warehouse/parse-read-scope.ts) שמטפל ב-validation מלאה: הרשאת WAREHOUSE_MANAGE_CUSTOMER_DATA, flag warehouse_multi_customer, ושייכות לקוח לטננט. endpoint חדש /api/warehouse/customers מחזיר רק לקוחות עם מודול warehouse מופעל, ומסמן enabled=false כשהתכונה כבויה — כך ה-UI מתחבא אוטומטית בלי endpoint נפרד לבדיקת flag. רכיב חדש CustomerScopeSelector מופיע בטולבר של /warehouse/inventory ו-/warehouse/products כשהתכונה מופעלת; ערכי הבחירה: הכל / טננט בלבד / לקוח ספציפי. ה-GET routes של אותם עמודים הוסבו מ-getOwnerScope ל-parseReadScope (תומך ב-?ownerCustomerId=all/tenant/customerId). שתי העמודות מציגות עמודה נוספת 'שייך ל-' עם תווית 'הטננט' או שם הלקוח. בעמוד המלאי, כפתור 'הוסף פריט מלאי' מוסתר כשבחורה תצוגה של לקוח מסוים — פאזה 2 תוסיף תמיכה בכתיבה על-שם לקוח. הזרם הקיים תואם לאחור: בלי המודול מופעל, אף משתמש לא רואה שינוי. (29) היסטוריית פעולות במשלוח — שיפור תצוגה לשדות ליקוט: עד היום, כששדה pickingCategory השתנה דרך PATCH /api/shipments/[id] (לדוגמה מעובד שמשנה קטגוריית ליקוט מ-'ברירת מחדל' ל-'שוטף'), היסטוריית הפעולות הציגה רק 'עודכנו השדות: קטגוריית ליקוט' בלי לחשוף את הערך הישן והחדש. הסיבה: ה-FIELD_LABELS של /api/shipments/[id]/activity-log/route.ts (נפרד מה-PATCH route) לא כלל את pickingCategory, ולכן השינוי סונן ב-extractChanges. נוסף ל-FIELD_LABELS, ונוספו VALUE_DISPLAY_MAPS לשלושת השדות (pickingStatus: ממתין לליקוט/נלקט, pickingType: עלוני שבת/מוצרים, pickingCategory: שוטף/מזדמן) — כך שהטבלה תציג 'ברירת מחדל ← שוטף' במקום 'null ← regular'. בנוסף נוסף סט DATETIME_FIELDS ופונקציית formatDateTime למניעת הצגת תאריכים כ-ISO גולמי (למשל pickedAt, addedToPickingAt, estimatedDelivery, actualDelivery, pickupDate, deliveryDate, visitAt). (30) דף משלוח — סקציית ליקוט אוחדה עיצובית: סטטוס הליקוט הראשי (ממתין/נלקט) הפך לבאדג' צבעוני (ענבר/ירוק) עם ChevronDownIcon במקום טקסט עם PenLineIcon כחול, כדי להתאים לשני הבוררים הסמוכים (סוג ליקוט — עלוני שבת/מוצרים; קטגוריה — שוטף/מזדמן) שגם בהם הוחלף אייקון העט בחץ. כפתורי ה-V (סמן כנלקט) וה-X (הסר מליקוט) הוסרו מצד שמאל של השורה, כי כל מעבר סטטוס זמין עכשיו דרך ה-dropdown של הבאדג'. צד שמאל מציג עכשיו רק את כפתור 'הוסף לליקוט' (כשאין סטטוס) או את כפתור עריכת הערת הליקוט יחד עם חותמת זמן ה-pickedAt (כשהמשלוח נלקט). הוסר גם ה-separator (•) שהיה בין בורר סוג ליקוט לבורר הקטגוריה — מיותר עכשיו כששניהם באדג'ים עם גבול ברור. אותו עיקרון יושם בשורת 'יעד שבת': הטקסט 'פרשת X' הפך לבאדג' סגול עם ChevronDownIcon שלחיצה עליו פותחת dropdown של 4 הפרשות הקרובות + פריט 'הסר יעד שבת' (אדום, מתחת ל-DropdownMenuSeparator). הכפתורים הנפרדים 'שנה' (PenLineIcon) ו-X (אדום) הוסרו לחלוטין מצד שמאל — כל הפעולות זמינות עכשיו דרך הבאדג'. כשאין יעד שבת מוגדר נשאר כפתור '+ הוסף יעד שבת' מימין, בדומה לדפוס של 'הוסף לליקוט'. בנוסף — תווית 'יעד שבת' והערך (באדג'/טקסט) הועברו לאותה שורה במקום שני שורות, כדי לחסוך גובה אנכי בכרטיסיית התפעול. אותו דפוס יושם בשורת 'יעד אקספרס': התאריך הפך לבאדג' אינדיגו עם ChevronDownIcon שלחיצה עליו פותחת את ה-Popover של ExpressBulkDateMenu (7 הימים הקרובים + 'תאריך אחר' עם calendar + 'הסר מאקספרס') — נוסף ל-ExpressBulkDateMenu prop אופציונלי trigger שמחליף את כפתור ברירת המחדל; כפתור ה-X האדום החיצוני הוסר (מיותר כי 'הסר מאקספרס' קיים בתוך התפריט עצמו), והתווית והערך יושבים על אותה שורה. כשאין יעד אקספרס נשאר כפתור '+ הוסף יעד אקספרס' מימין. גם סקציית 'פניות שירות' (ShipmentServiceTicketsSection) קיבלה אותו טיפול: התווית והערך אוחדו לשורה אחת — במקום label מעל summary מעל שורת צ'יפים, עכשיו label + צ'יפים על אותה שורה ('פניות שירות [chip1] [chip2] ...'). הטקסט 'X פתוחות' / 'אין פניות פתוחות' הוסר כי הצ'יפים עצמם מתארים מצב כל פנייה (סטטוס + קטגוריה, צבע סטטוס לפי הסכמה הקיימת + opacity 60 לפניות סגורות); רקע sky עדיין מסמן את המצב הכללי שיש פניות פתוחות. כשאין פניות נשאר 'לא הוגדר' inline, וכשטוען 'טוען...'. הצ'יפים flex-wrap-ים כשהשורה צרה כדי לא לדחוק את כפתור 'פנייה חדשה'. EditablePickingStatusText קיבל prop נוסף onRemove + פריט 'הסר מליקוט' (אדום, מתחת ל-DropdownMenuSeparator) בתחתית ה-dropdown של סטטוס הליקוט — בדפוס של 'הסר יעד שבת'. בעבר ניתן היה להסיר משלוח מליקוט רק דרך כפתור X חיצוני שהוסר בשינוי קודם, ולכן הפעולה הזו לא הייתה נגישה יותר; עכשיו היא חזרה אבל מאוחדת עם ה-dropdown של הבאדג'. (31) מחסן 3PL — פאזה 2 (כתיבה רב-לקוחית): staff עם הרשאה יכול עכשיו ליצור מוצרים ולעדכן מלאי על-שם לקוח. נוסף helper lib/warehouse/parse-write-owner.ts שעוטף את resolveWriteOwner ומוסיף את ה-validation מול ה-DB (הרשאה WAREHOUSE_MANAGE_CUSTOMER_DATA, flag warehouse_multi_customer, שייכות הלקוח לטננט והפעלת מודול מחסן ללקוח). POST /api/warehouse/products ו-POST /api/warehouse/inventory/adjust מקבלים שדה אופציונלי ownerCustomerId ב-body; כש-staff נותן ערך הוא עובר את כל ה-validation, וכשהוא לא נותן — חוזרים להתנהגות הישנה (tenant-owned). אכיפה חדשה ב-/inventory/adjust: (א) המיקום חייב להיות בטננט (לאו דווקא בבעלות הלקוח — בעקבות גישה B שמיקומים תמיד אצל הטננט); (ב) המוצר חייב להיות בבעלות הזהה לבעלי המלאי החדש (invariant: product.ownerCustomerId === inventory.ownerCustomerId); (ג) אם כבר קיימת שורת מלאי במיקום+מוצר עם בעלים שונה — 409 במקום upsert שקט. ב-UI: ProductForm ו-InventoryCreateDialog קיבלו בורר 'שייך ל-' שמופיע רק כשהתכונה מופעלת; בורר המוצרים ב-InventoryCreateDialog מסונן אוטומטית לפי הבעלים הנבחר. כפתור 'הוסף פריט מלאי' חזר להיות זמין בכל מצב — ה-defaults מועברים מה-scope הנוכחי של העמוד. ProductForm נועל את בורר הבעלים במצב עריכה (בעלות לא ניתנת לשינוי אחרי יצירה). 13 בדיקות חדשות ל-parseWriteOwner; סה"כ 702 tests עוברים. השלמת סימטריה מלאה בין כרטיסיות המוצא והיעד: 3 שדות חדשים בכל אחת. בכרטיס יעד נוספו עריכה למיקוד (destination_zip_code), כניסה (destination_entrance) וקוד כניסה (destination_entrance_code) — קודם הם הוצגו רק מתוך parse של הערת היעד, עכשיו הם עמודות נפרדות ב-DB שמסתנכרנות עם DELIVERY visit. בכרטיס מוצא נוספו עריכה לאיש קשר (source_recipient_name), טלפון 2 (source_phone2) ומיקוד (source_zip_code). אומת empirit ש-6 השדות מסתנכרנים מקצה לקצה. הערה: source_entrance ו-source_entrance_code לא נוספו כי ליונוויל silent-fail עליהם ב-PICKUP visit (זהה לדפוס של org_note). במקביל הוצף הרווח האנכי בין השורות: space-y-3 → space-y-1.5 ו-py-1 → py-0.5 בשתי הכרטיסיות, כדי שהגובה לא יזנק עם השדות החדשים. (32) פורטל לקוחות — בדיקת חיבור WooCommerce שדרגה אבחון: עד היום ה-test פנה ישירות ל-/wp-json/wc/v3/system_status, שהוא endpoint שדורש הרשאת admin ב-WP, ולכן החזיר 404 (rest_no_route) בכל מקרה שבו (א) הכתובת שגויה, (ב) Pretty Permalinks כבויים, (ג) WC לא מותקן, (ד) תוסף אבטחה חוסם את namespace ה-API, או (ה) המפתחות נוצרו עבור משתמש שאינו admin — וההודעה הסתמית 'WooCommerce החזיר שגיאה (404)' לא עזרה ללקוח לאבחן את הבעיה. הלוגיקה החדשה בודקת ב-3 שלבים: (1) probe ל-/wp-json/wc/v3 ללא auth — מאתר 404 ברמת ה-namespace ומציג הודעה שמפרטת את 4 השאלות שצריך לבדוק; (2) auth ל-/orders?per_page=1 (דורש רק Read key) — מבדיל בין credentials שגויים (401/403 עם הודעה ברורה) לבין שגיאות אחרות שמוצגות עם גוף התשובה לדיבוג; (3) ניסיון best-effort ל-system_status רק כדי לחלץ שם חנות לתצוגה, אבל כשלון בו לא מפיל את ה-test. נוסף גם validation שכתובת החנות חייבת להתחיל ב-http(s)://.
v01.42.001תיקוןdocs/public, security, mobile/map, mobile/earnings, mobile/home, messages/inbox, messages/templates, search, driver/track, mobile/pod, warehouse/ai, mobile/ux, mobile/scanner, mobile/signature, portal/import, shipments/activity-log, shipments/lionwheel-sync, shipments/partner-transfersתיעוד — סדר ה-navbar תוקן ב-RTL ונוסף toggle של דארק/לייט
(20) נוספה חתימה דיגיטלית (אופציונלית) לתהליך הוכחת המסירה: בסיכום ה-POD מוצגת סקציית "חתימת לקוח" עם canvas ציור מלא-מסך — הלקוח חותם בזמן המסירה, החתימה נשמרת ב-Supabase Storage (category=signature) ומועלית יחד עם תמונ…
פרטים נוספים ↓הסתר ↑
(20) נוספה חתימה דיגיטלית (אופציונלית) לתהליך הוכחת המסירה: בסיכום ה-POD מוצגת סקציית "חתימת לקוח" עם canvas ציור מלא-מסך — הלקוח חותם בזמן המסירה, החתימה נשמרת ב-Supabase Storage (category=signature) ומועלית יחד עם תמונות ה-POD; אם הנהג לא מוסיף חתימה — ה-POD מוגש עם תמונות בלבד כרגיל. (17.5) שדרוג מנוע סריקת הברקודים באפליקציית השליחים ובסורק המחסן: עבר מ-expo-camera ל-react-native-vision-camera v4. Frame Processor רץ על thread נייטיבי — ללא JS bridge — הסריקה מהירה יותר משמעותית, עובדת טוב יותר בזוויות ובתאורה חלשה ומזהה ברקודים שחוקים. נוספו פורמטים לוגיסטיים: PDF-417 (מדבקות שליחות) ו-ITF (GTIN-14 מחסן). expo-camera נשאר לצילום תמונות POD בלבד. (16) טאב 'היום' שונה ל'משימות': נוסף date navigator (חיצים קדימה/אחורה) לצפייה במשימות של ימים קודמים ועתידיים; ShiftBanner ו-COD banner מוצגים רק כשמוצג היום; endpoint תומך ב-?date=YYYY-MM-DD. (15) מודול מחסן — Phase 4D הושלם: דפי "חידוש מלאי" ו-"Slotting" חוברו לסיידבר בפורטל הטננט ובפורטל הלקוח. כלל ארבעת תכונות ה-AI של Phase 4D זמינות כעת: התראות חידוש מלאי, המלצות Slotting, הצעות אצוות חכמות (ב-BatchListView) ואינדיקטור עומס אזורים (ב-MetricsDashboard). (14) נוסף אקורדיון לשורות הפירוט בטאב הכנסות המובייל: לחיצה על מסירות / איסופים / חזרות פותחת רשימת עצירות בודדות עם שם לקוח, ברקוד, עיר, תאריך וסכום לכל שורה. (12) תוקן: קו המסלול על המפה לא התחיל במדויק מנקודת ההתחלה — הגיאומטריה של OSRM מחזירה נקודה ראשונה מוצמדת לכביש (offset קטן) או מיקום ה-GPS של הנהג בזמן ההפעלה (offset גדול). תוקן על-ידי עיגון נקודת הפתיחה של הקו לקואורדינטות המדויקות של עצירה #1: עבור offset קטן (<55m) — מוחלפת רק הנקודה הראשונה; עבור offset גדול — נמצאת הנקודה הקרובה ביותר בגיאומטריה לעצירה #1 ומשם חותכים את הקו ומעגנים את ראשיתו. (11) עיצוב מחדש של אפליקציית השליחים: כרטיס hero לעצירה הבאה עם גרדיאנט אינדיגו ו-"נווט עם Waze" בולט, progress bar לנסיעה, מספרי עצירות בכרטיסים, מקטע ה-done מתקפל, מצב ריק ומצב "הכל הושלם" עם איקונים, skeleton loading, ניקוי כרטיס שיתוף המיקום (הוסרו קואורדינטות גולמיות, נוסף גריד מהירות/סוללה ענק). (10) תוקן: מחיקת תבנית Meta מהרשימה מחקה מקומית בלבד — עכשיו כשמוחקים תבנית מקושרת ל-Meta, הדיאלוג מודיע שהמחיקה תתבצע גם מ-Meta, וה-endpoint נקרא אוטומטית. כפתור 'מחק מ-Meta' בעורך הוצג רק לתבניות שאינן APPROVED — ההגבלה הוסרה ועכשיו מוצג לכל סטטוס (פרט ל-NOT_FOUND). שני תיקונים ב-header של /docs: (1) הקישורים (בית / פורטל לקוחות / מה חדש) הופיעו בצד שמאל מרוחק מהלוגו, עם החיפוש ביניהם — לא טבעי בעברית. סודרו מחדש: הניווט עכשיו צמוד ללוגו בצד ימין כמקובל בעברית, והחיפוש בצד שמאל. (2) חסר כפתור החלפת ערכת נושא — נוסף ModeToggle של המערכת ליד החיפוש כך שגם דף ה-docs (שפתוח לכולם בלי התחברות) תומך באותו toggle דארק/לייט. בנוסף, תוקנו שתי פרצות אבטחה: (3) שאילתת $queryRaw ב-sorting-errors הוספה לה סינון tenant_id למניעת דליפה cross-tenant; (4) endpoint של messages/analytics הוגבל ל-ANALYTICS_VIEW permission. (5) תוקנה שגיאת React hydration #418: ShabbatGuard, LiveClock ו-LiveDuration עברו לאתחול ריק ועדכון ב-useEffect; החלפת Math.random() בערכים יציבים ב-global-search ו-sidebar skeleton. (6) תוקנה קריסת Expo Go: טאב המפה המוביילי גרם ל-Invariant Violation (MLRNCameraModule) עקב import סטטי של MapLibre שמנע אתחול המודול הנייטיבי. פתרון: לוגיקת המפה הועברה ל-MapScreen.tsx נפרד; map.tsx הפך ל-guard דק שמשתמש ב-require() מותנה — ב-Expo Go מוצג מסך fallback ובבuild נייטיבי נטען MapScreen. (6.5) נוספו טאבי סינון למפה המוביילית: הכותרת הצפה ('47 פעילים · 6 נמסרו') הוחלפה בשלושה טאבים — הכל / במסלול / נמסרו. כל טאב מסנן את המרקרים המוצגים ואת מה שניתן לבחור בציור פוליגון. (6) תוקן lockout במobile login: loginAttempts עכשיו עולה בכל כישלון סיסמה, lockedUntil מוגדר אחרי 5 ניסיונות כושלים (15 דקות), ובדיקת נעילה מועברת לפני bcrypt. (7) תוקן באג: מרקר נקודת הסיום (כתום) לא הופיע על מפת המובייל כשלא הוגדרה ברירת-מחדל (isDefault=false). האתחול שונה כך שכאשר אין מיקום מסומן כברירת מחדל — נבחר אוטומטית המיקום השמור הראשון הזמין. (8) תוקן webhook WooCommerce: חיבורים ללא webhookSecret נדחים (fail-closed) במקום להתקבל ללא אימות — בהתאם לאופן שבו Shopify כבר עבד באותו handler. (9) תוקן webhook Green API: כשחסר שדה instanceData בגוף הבקשה — הוובהוק נדחה (fail-closed) במקום לעקוף את אימות ה-instanceId. (13) תוקן אפליקציית השליח (mobile): המונח "POD" הוחלף בכל מקומות ה-UI בביטוי "הוכחת מסירה" בעברית — כותרת מסך המצלמה, כיתוב תמונה, כפתור שמירה, כותרת סיכום, תווית סטטוס, toast, alert אישור מסירה ללא תיעוד, הודעת הרשאת מצלמה; בנוסף תורגם "photos" ל-"תמונות" ב-badge הספירה. (17) שיפורי UX אפליקציית שליחים: הוספת haptic feedback (בחירת טאב, לחיצה ארוכה, סיום משמרת); אנימציית fade 80ms→150ms במעבר בין טאבים; כרטיס ה-hero (עצירה הבאה) מציג פעולות ישירות — התקשר, WhatsApp, נווט ב-Waze; לחיצה ארוכה על כל כרטיס פותחת action sheet (iOS) / dialog (Android) עם אותן אפשרויות; כפתור 'סיים משמרת' ב-AllDoneCard; טאב 'לא במסלול' ממוין לפי מרחק מהנהג; HomeHeader קיבל רקע תכלת עדין (primary[50]). (18) Live badge על אייקון הבית בשורת הטאבים — מציג את מספר המשלוחים הפתוחים ומתעדכן אוטומטית; badge נעלם כשאין משלוחים פתוחים. (19) Optimistic UI — סימון 'נמסר' או 'נכשל' מעדכן מיד את cache הבית (status, stats) לפני חזרה למסך הרשימה; הרשימה, הפרוגרס בר, הבאדג' ותיבת 'כל המשלוחים הושלמו' מגיבים מיידית ללא המתנה לשרת. (21) פורטל לקוחות — בייבוא משלוחים (/portal/shipments/import) נוסף בורר 'מלא מתוך כתובת מועדפת' ליד שדות כתובת המוצא, בקונפיגורציה הראשונית ובדיאלוג עריכת ההגדרות. הבורר טוען את הרשימה מ-/api/portal/saved-addresses (אותן כתובות שמנוהלות ב-/portal/settings/addresses), ובחירה ממלאת אוטומטית עיר/רחוב/מספר. כשאין כתובות שמורות מוצג קישור ישיר לעמוד ניהול הכתובות. בפורטל הצוות הפיצ'ר מוסתר. (22) היסטוריית פעולות במשלוח — שלוש רשומות 'יצירה'/'עדכון' שנכתבו לכל משלוח חדש (אחת מה-webhook הראשון של ליונוויל task_created, שנייה מ-webhook נוסף manual_create שגרם ל'עדכון' של שדה taskType מ-ריק ל'משלוח', ושלישית מה-import/UI עם שם המשתמש) — מתאחדות עכשיו לרשומת יצירה אחת. נוסף helper upsertCreateAudit ב-lib/audit-logger.ts שמזהה רשומת create קיימת לאותו entity וממזג אליה את ה-newData, ה-details ו-userId של מקור נוסף — במקום ליצור רשומה נוספת. בנוסף, ב-data-service.ts ה-update flow של ה-webhook מזהה updates שמגיעים תוך 90 שניות מהיצירה (ה-cascade הטיפוסי של ליונוויל) ואינם כוללים שינוי סטטוס — וממזג אותם גם הם לתוך ה-create. שינוי סטטוס אמיתי באותו חלון זמן ממשיך להירשם כ-status_change בפני עצמו. הפתרון מכסה את כל מסלולי היצירה (UI ידני, יבוא אקסל, שכפול, bulk-duplicate, וכל webhook מליונוויל) ושומר על כל המידע — שם המשתמש שיצר, מקור היצירה (ידני/אקסל/שכפול), וציון 'סונכרן עם ליונוויל' כשרלוונטי — בלי לפצל למספר פעולות. (23) סנכרון לליונוויל — שני ניקויים בעקבות בדיקה empirit מקיפה של כל שדות העדכון מול ה-API: (א) השדה weight עוגל לשלם לפני שליחה, כי ליונוויל קוטמת ערכים עשרוניים (שלחנו 5.5, נשמר 5) ואז הווידוא שלנו דיווח על מצב מסונכרן עם ערך שונה. (ב) ה-mapping sameDay→same_day הוסר מ-LIONWHEEL_TASK_FIELDS — השדה מקובל ב-/tasks/create אבל מתעלם ב-/tasks/{id}/update (silent-fail), ובכל מקרה טופס עריכת המשלוח אצלנו אינו מאפשר לשנות אותו. הערות בקוד הוסרו לגבי visit-fields שחשבנו שעלולים לא לעבוד — בבדיקה כולם (phone, customerName, city, street, number, floor, apartment, notes) עובדים מצוין. (24) מזהי משלוחים בין-ספקיים — נוספה תמיכה מלאה ב-2 הזרימות של ליונוויל לקליטת משלוחים מספק אחר: (א) partner-transfer (origin_partner_task_id) ו-(ב) baldar (wp_order_id). עד היום שמרנו רק את המקרה הראשון, ואת השני לא בכלל. בנוסף תוקן באג ב-task-processor.ts שבו ה-backfill של מזהי partner רץ רק כשמשלוח יוצא (status IN_TRANSFER/OUT_INVENTORY) ולא כשנכנס. תוויות ה-UI ב-4 המקומות (כרטיס משלוח, טבלה ראשית, חיפוש גלובלי) הוחלפו מ"חיצוני"/"משוגר" ל"מזהה יוצא"/"מזהה נכנס". Backfill לשני המשלוחים הטסט (24996858 מ-baldar → 1897556, 25000309 מ-transfer → 24966518) אימת את התיקון end-to-end.
v01.42.000חדשdocs/publicminorמרכז תיעוד ציבורי חדש ב-/docs — סיידבר, TOC, חיפוש ⌘K, וכפתור AI לכל עמוד
התיעוד הציבורי של Shipnest עבר רה־ארגון מלא.
פרטים נוספים ↓הסתר ↑
התיעוד הציבורי של Shipnest עבר רה־ארגון מלא. הדף הישן ב-/api היה רוחב מוגבל (max-w-3xl) על מסך רחב, מה שהוביל לחלל ריק בצד שמאל וניצול ירוד של המסך. הוחלף ב-hub חדש ב-/docs במבנה 3 עמודות: סיידבר ניווט (קבוצות נושאים), תוכן רחב לקריאה, ו-TOC צד שמאל שמתעדכן auto לפי כותרות בדף עם scroll-spy. נוסף חיפוש גלובלי בלחיצת ⌘K (cmdk) שמחפש לפי כותרות, blurbs וקבוצות מילות מפתח. כל דף חושף כפתור 'שאל AI על העמוד' שמעתיק את כל תוכן העמוד כפרומפט מובנה (עם system primer של Shipnest), ולחיצה על אחד הספקים (ChatGPT / Claude / Gemini) פותחת אותם במסך חדש. הסיידבר כולל widget של 3 הרשומות האחרונות מ-changelog.json שמתעדכן אוטומטית. אגב — תוקנו /legal/terms ו-/legal/privacy שהחזירו 404 (היו ב-route group (legal) שמוחק את ה-prefix מה-URL; הועברו ל-app/legal/ הרגיל). /api ממשיך לעבוד דרך 301 ל-/docs/api.
- /docs — landing עם כרטיסי נושאים (API, Webhooks, Changelog, ועוד 6 שמסומנים 'בקרוב' לעמודי תפעול כמו משלוחים/COD/לקוחות)
- /docs/api — תיעוד API מלא ומורחב: כוסו webhooks management endpoints וביטול משלוחים (5 endpoints נוספים שהיו חסרים בתיעוד הקודם), נוספו סקציות best-practices (ניהול secrets, polling vs webhooks, error handling)
- /docs/changelog — לוג גרסאות מסודר לפי חודשים, כל רשומה עם type badge (חדש/תיקון/שיפור/ייעול), description קצר ופרטים מורחבים ב-<details>
- DocsShell — layout client component עם sticky top header, sidebar (lg ומעלה inline; mobile drawer דרך Sheet), TOC צדדי, footer. רספונסיבי מלא: 3 עמודות במסכים גדולים, עמודה אחת במובייל עם hamburger
- DocsToc — TOC client שבונה את עצמו מ-h2/h3 ב-article (data-docs-article), עם IntersectionObserver לסימון הסעיף הנוכחי בזמן גלילה
- DocsSearch — CommandDialog (cmdk) שמופעל מ-⌘K או '/' globally. ה-value של כל פריט כולל title + blurb + keywords כדי שחיפוש בעברית/אנגלית יחזיר תוצאות רלוונטיות (למשל 'token' מוצא את סעיף האימות)
- AiPromptButton — קורא את תוכן ה-article מה-DOM (תוך השמטת elements עם data-ai-skip), בונה פרומפט עם system primer של Shipnest, מעתיק ללוח. dropdown נוסף עם links ל-ChatGPT/Claude/Gemini שפותחים עם short bootstrap prompt
- ChangelogWidget — server component בסיידבר שמציג את 3 הרשומות האחרונות מ-changelog.json, עם type badges וקישור להיסטוריה המלאה
- DocsTodo / DocsTodoPage — רכיבי marker לתוכן בכתיבה. בפיתוח מציג בנר אדום בולט, בפרודקשן הודעה רכה. מאפשר לפרסם stubs לעמודים שייכתבו בעתיד מבלי לפרסם דף ריק
- CLAUDE.md — נוסף סעיף 'עדכון תיעוד ציבורי — שיקול דעת לפני כל push' שמנחה את ה-AI מתי לעדכן /docs (שינוי endpoint/event/scope/rate limit) ומתי לדלג (refactor פנימי, feature flag, ops פנימי)
- Bug fix — /legal/terms ו-/legal/privacy החזירו 404. הקבצים היו ב-app/(legal)/ (route group) שמתעלם מה-prefix, ולכן ה-URLs בפועל היו /terms ו-/privacy. הועברו ל-app/legal/ הרגיל וה-URLs עובדים
- Bug fix — /api נראה רע בגלל max-w-3xl על מסך רחב. הופנה ל-/docs/api עם layout 3 עמודות שמנצל את כל הרוחב
v01.41.001שיפורportalפורטל API — הסתרת מפתחות מבוטלים כברירת מחדל
טבלת 'המפתחות שלי' מציגה כעת רק מפתחות פעילים (כמו Stripe / GitHub / Vercel).
פרטים נוספים ↓הסתר ↑
טבלת 'המפתחות שלי' מציגה כעת רק מפתחות פעילים (כמו Stripe / GitHub / Vercel). מפתחות מבוטלים מוסתרים אבל נשמרים לאודיט — כפתור 'הצג מבוטלים (N)' מופיע כשיש כאלה, מאפשר לראות את ההיסטוריה. גם עודכן הקישור 'תיעוד טכני מלא' מ-/api ל-/docs/api הישיר אחרי המעבר של הדף.
v01.41.000חדשportalminorפורטל — ניהול מפתחות API ב-self-service במקום פנייה לתמיכה
בכרטיס 'המפתחות שלי' בראש דף /portal/settings/api לקוחות יכולים עכשיו ליצור, לרשום ולבטל מפתחות API לבד, באותו דפוס של Stripe / GitHub / Vercel / OpenAI — בלי לעבור דרך התמיכה.
פרטים נוספים ↓הסתר ↑
בכרטיס 'המפתחות שלי' בראש דף /portal/settings/api לקוחות יכולים עכשיו ליצור, לרשום ולבטל מפתחות API לבד, באותו דפוס של Stripe / GitHub / Vercel / OpenAI — בלי לעבור דרך התמיכה. נוסף POST/GET /api/portal/api-keys ו-DELETE /api/portal/api-keys/[id]. נוספה הרשאת CUSTOMER חדשה MANAGE_API_KEYS שמופעלת כברירת מחדל לבעלים. גבול של 10 מפתחות פעילים לטננט. הטוקן הגולמי מוצג פעם אחת בלבד בדיאלוג אחרי יצירה (כמו אצל כל ספק רציני) — הסיבה היא threat-model: דליפת DB לא חושפת מפתחות חיים, session hijack לא מקבל אותם 'תמיד גלוי', וזה דרישה של SOC 2 / ISO 27001. אם איבדו — בטלו וצרו חדש בקליק במקום פנייה לתמיכה.
- POST /api/portal/api-keys — יצירה: name, scopes (חובה), expiresAt (אופציונלי, presets: ללא תפוגה / 30 / 90 / 365 ימים). מחזיר את הטוקן פעם אחת.
- GET /api/portal/api-keys — רשימת כל המפתחות של ה-tenant (פעילים + מבוטלים), כולל prefix, scopes, lastUsedAt, status. הטוקן הגולמי לא חוזר אף פעם.
- DELETE /api/portal/api-keys/[id] — soft-revoke (revokedAt + revokedByName). Idempotent.
- ApiKeysSection client component — טבלה עם סטטוס badges (פעיל / פג / בוטל), כפתור 'צור חדש' עם דיאלוג טופס, ודיאלוג 'הצג פעם אחת' עם כפתור 'העתק' שכולל fallback ל-execCommand.
- CUSTOMER_PERMISSIONS.MANAGE_API_KEYS — הרשאה חדשה, OWNER_PERMISSIONS כולל אותה. הדף + ה-endpoints גם יחד מאמתים את ההרשאה.
- Step 1 בכרטיס 'תחילת עבודה' עודכן: 'צרו מפתח בכרטיס למעלה' במקום 'פנו לתמיכה'. נשמרה הודעת ה-warning שהמפתח מוצג פעם אחת, אבל נוסחה מחדש כי כעת הפתרון הוא לבטל-וליצור-חדש ולא לפנות לתמיכה.
v01.40.004תיקוןapi/middlewareתיקון — דף תיעוד ה-API ב-/api נחסם 401 לאורחים
ה-middleware התייחס לכל מה שמתחיל ב-/api כ-API route ודחה 401 JSON, כולל דף התיעוד עצמו ב-/api (הקובץ apps/web/app/(legal)/api/page.tsx).
פרטים נוספים ↓הסתר ↑
ה-middleware התייחס לכל מה שמתחיל ב-/api כ-API route ודחה 401 JSON, כולל דף התיעוד עצמו ב-/api (הקובץ apps/web/app/(legal)/api/page.tsx). תוקן: בדיקה מפורשת שמטפלת ב-/api (בדיוק) כ-content page ולא כ-API route, נוסף ל-publicRoutes. במקביל הוחזר הכפתור 'תיעוד טכני מלא' בכותרת של /portal/settings/api שהוסר קודם כי הוא הוביל ל-401. אומת — GET /api כעת מחזיר 200 text/html עם הדף המלא (~223KB).
v01.40.003שיפורportalפורטל API — קלפי המדריך מתקפלים כאקורדיון
ששת הקלפים של דף /portal/settings/api הומרו מ-sections פתוחים תמיד ל-<details> native — כל קלף נסגר ונפתח בלחיצה על הכותרת, עם chevron שמסתובב.
פרטים נוספים ↓הסתר ↑
ששת הקלפים של דף /portal/settings/api הומרו מ-sections פתוחים תמיד ל-<details> native — כל קלף נסגר ונפתח בלחיצה על הכותרת, עם chevron שמסתובב. כל הקלפים סגורים כברירת מחדל (כולל 'תחילת עבודה') כדי לתת מבט מרוכז במקום קיר טקסט, והמשתמש פותח רק את מה שמעניין אותו. שימוש ב-HTML semantic native (לא Radix accordion) כדי לחסוך JS וכדי שהדף יעבוד גם לפני הידרציה.
v01.40.002תיקוןapi/middlewareOpenAPI spec — להסיר את ה-auth gate מ-/openapi.yaml
ה-spec שנוסף ב-01.40.001 היה מאחורי middleware ה-auth — בקשה ל-/openapi.yaml מ-Swagger UI / n8n / Postman / AI הייתה מוחזרת כ-redirect ל-/login והדפדפן/הכלי מקבל HTML במקום YAML.
פרטים נוספים ↓הסתר ↑
ה-spec שנוסף ב-01.40.001 היה מאחורי middleware ה-auth — בקשה ל-/openapi.yaml מ-Swagger UI / n8n / Postman / AI הייתה מוחזרת כ-redirect ל-/login והדפדפן/הכלי מקבל HTML במקום YAML. נוסף /openapi.yaml ל-publicRoutes ב-middleware.ts. אומת ידנית — הקובץ מוגש עכשיו כ-text/yaml תקין (~31KB) ללא session. בנוסף — הוסר כפתור 'תיעוד טכני מלא' שהיה ב-header של דף הגדרות ה-API בפורטל; הוא הצביע על /api (route לא קיים) וגרם ללקוחות לקבל 401 Unauthorized מה-middleware. הדף עצמו כבר מהווה את התיעוד המלא, וכפתור OpenAPI spec הסמוך מספק את ה-spec הטכני.
v01.40.001חדשportal/apiOpenAPI spec זמין להורדה מהפורטל
נוסף קובץ OpenAPI 3.1 סטטי ב-/openapi.yaml (879 שורות) שמתעד את כל ה-endpoints של /api/v1, וכפתור חדש בראש דף הגדרות ה-API בפורטל שמקשר אליו.
פרטים נוספים ↓הסתר ↑
נוסף קובץ OpenAPI 3.1 סטטי ב-/openapi.yaml (879 שורות) שמתעד את כל ה-endpoints של /api/v1, וכפתור חדש בראש דף הגדרות ה-API בפורטל שמקשר אליו. מאפשר ללקוחות לייבא את ה-spec ישירות ל-Postman / Insomnia / n8n / Make ולקבל auto-completion ו-validation במקום לקרוא ידנית את התיעוד.
v01.40.000חדשportalminorפורטל — דף מדריך API ו-Webhooks מובנה לצד אזורי השימוש
נוסף לפורטל הלקוח דף ייעודי תחת 'הגדרות → API ו-Webhooks' שמסביר למתכנתי הלקוח איך להתחבר ל-Shipnest, באותו הקונספט של מדריכי החיבור הקיימים ל-WooCommerce / קונימבו / CashCow / Shopify — המדריך חי באזור השימוש, לא בעמוד …
פרטים נוספים ↓הסתר ↑
נוסף לפורטל הלקוח דף ייעודי תחת 'הגדרות → API ו-Webhooks' שמסביר למתכנתי הלקוח איך להתחבר ל-Shipnest, באותו הקונספט של מדריכי החיבור הקיימים ל-WooCommerce / קונימבו / CashCow / Shopify — המדריך חי באזור השימוש, לא בעמוד תיעוד נפרד שצריך לחפש. הדף בנוי כשישה קלפים ממוספרים: תחילת עבודה (עם curl ראשון מותאם ל-Base URL של הלקוח), טבלת scopes, רשימת endpoints מלאה (shipments, customers, webhook subscriptions), הסבר על Outbound Webhooks (אירועים, אימות HMAC, retries) ב-details collapsible, שיטות עבודה מומלצות (אבטחה, idempotency, polling vs webhooks, rate limits), ופרומפט מוכן ל-AI שמותאם דינמית עם tenantId ו-baseUrl של הלקוח וכפתור 'העתק' מובנה.
- נתיב חדש /portal/settings/api עם server-rendered SSR — Base URL ו-tenantId נשלפים מה-session ומה-request host, אז ה-curl examples וה-AI prompt מותאמים אישית
- כפתור 'העתק פרומפט' עם fallback ל-execCommand('copy') לדפדפנים ישנים / contexts לא מאובטחים
- AI prompt בן כ-60 שורות כולל auth, response envelope, רשימת endpoints מלאה (15 endpoints), קטלוג ה-events, פורמט החתימה, ו-conventions (כסף ב-agorot, זמנים UTC) — מוכן להדבקה כ-system prompt ב-Claude/ChatGPT/Cursor
- סעיף Outbound Webhooks משתמש ב-details/summary native כדי לחסוך אורך דף — 4 sub-cards (אירועים, headers, אימות חתימה ב-Node.js, retries) מתקפלים
- נוסף פריט סייד-בר 'API ו-Webhooks' עם CodeIcon מתחת ל'הגדרות', לפני warehouse module
v01.39.001תיקוןui/themeתיקון הבזק שחור בכותרת טבלת המשלוחים במצב לייט
משתמשים עם Windows ב-Dark וכרום שמציג ב-Light ראו הבזק שחור בשורת הכותרת של טבלת המשלוחים בכל רענון/ניווט, ממש לפני שהתוכן עלה.
פרטים נוספים ↓הסתר ↑
משתמשים עם Windows ב-Dark וכרום שמציג ב-Light ראו הבזק שחור בשורת הכותרת של טבלת המשלוחים בכל רענון/ניווט, ממש לפני שהתוכן עלה. הסיבה: תיקון FOUC קודם הזריק משתני dark דרך @media (prefers-color-scheme: dark) על :root:not(.light), אבל ה-class="light" של next-themes נוסף ל-html רק אחרי שה-ThemeProvider בתוך ה-body נטען — בפער הזה ה-CSS פתר את bg-muted/95 ו-bg-card של ה-thead לערכי כהים, ואז התהפך לבן. עכשיו ה-theme של המשתמש נשמר ב-cookie 'shipnest-theme' ו-layout.tsx (server component) מצמיד את ה-class ל-<html> כבר ב-SSR — אין רגע ש-CSS פוגע במשתני dark עבור משתמש light. ה-media query הותאם לעבוד רק כש-:root:not(.light):not(.dark) (visitor חדש בלי cookie). נוסף גם ThemeCookieSync שמסנכרן את ה-resolvedTheme ל-cookie בכל שינוי ערכה.
v01.39.000חדשwebhooksminorOutbound webhooks — הרחבת קטלוג האירועים מ-4 ל-8
קטלוג ה-events של ה-webhooks הציבוריים הוכפל: נוספו shipment.assigned (שיוך / העברת משלוח לשליח), shipment.failed (סימון כשל ע"י השליח, כולל סיבה ב-payload), visit.completed (השלמת ביקור — מקביל ל-shipment.delivered אבל …
פרטים נוספים ↓הסתר ↑
קטלוג ה-events של ה-webhooks הציבוריים הוכפל: נוספו shipment.assigned (שיוך / העברת משלוח לשליח), shipment.failed (סימון כשל ע"י השליח, כולל סיבה ב-payload), visit.completed (השלמת ביקור — מקביל ל-shipment.delivered אבל ברמת ה-Visit), ו-customer.created (יצירת לקוח חדש דרך API ציבורי או UI ניהול). בנוסף נסגר פער ותיק שבו ה-endpoint של המסירה ב-mobile (POST /api/v1/mobile/driver/visits/[id]/deliver) לא פלט shipment.delivered — עד כה האירוע הזה נפלט רק מנתיב הקליטה של Lionwheel, מה שהסביר שמשלוחים שנמסרו דרך האפליקציה לא הפעילו workflows חיצוניים.
- shipment.assigned נפלט מ-POST /api/v1/mobile/admin/visits/[id]/assign (גם שיוך ראשון וגם העברה משליח לשליח — payload מבחין עם action: 'assigned' | 'transferred')
- shipment.failed נפלט מ-POST /api/v1/mobile/driver/visits/[id]/fail עם reasonCode (no_answer / wrong_address / refused / closed / other) + reason טקסטואלי
- visit.completed + shipment.delivered נפלטים יחד מ-POST /api/v1/mobile/driver/visits/[id]/deliver (visit.completed מתאר את המאורע הטכני, shipment.delivered את המאורע העסקי)
- customer.created נפלט מ-POST /api/v1/customers (source: 'api') וגם מ-POST /api/customers הפנימי (source: 'admin') — מאפשר ל-CRM חיצוני להישאר מסונכרן ללא תלות בערוץ היצירה
- עודכנו רשימת ה-events ב-(platform)/admin/webhooks/page.tsx ובדף התיעוד הציבורי (legal)/api/page.tsx — שתיהן רואות עכשיו את שמונת ה-events
v01.38.002שיפורdistribution-gapsפערי הפצה — חגים יהודיים וערבי חג אינם נחשבים ימי עסקים
חישוב ימי העסקים בכל הטאבים של פערי הפצה (פערי מסירה / קריטיים / יום אחרון / פערי איסוף / כפולות) דילג עד היום רק על שישי ושבת.
פרטים נוספים ↓הסתר ↑
חישוב ימי העסקים בכל הטאבים של פערי הפצה (פערי מסירה / קריטיים / יום אחרון / פערי איסוף / כפולות) דילג עד היום רק על שישי ושבת. נוסף דילוג על חגים יהודיים אסורים במלאכה ועל ערבי החג שלהם: ר"ה (2 ימים), יוה"כ, סוכות יום א', שמיני עצרת, פסח יום א', שביעי של פסח, שבועות — כולם כולל ערב; וגם יום העצמאות ותשעה באב (ללא ערב). שינוי זה מתקן מצב שבו טאב 'יום אחרון להפצה' הציג רק 2 משלוחים השבוע כי תאריך הגורם נפל ביום חמישי 21.5 שהיה ערב שבועות. אחרי השינוי הטאב מציג כ-60 משלוחים — התפלגות חלקה במקום אנומליה.
v01.38.001תיקוןsecurityתיקון חקר אבטחה — הצגת משתמשים מחוברים מוגבלת לטננט
סריקת אבטחה חשפה 5 נקודות דליפה cross-tenant: (1) /api/users/online/stream החזיר משתמשים מכל הטננטים; (2-4) שלושת ה-endpoints של sorting-errors (GET/PATCH/POST/stats) לא סיננו לפי tenantId — אפשרו קריאה ועדכון של שגיאות …
פרטים נוספים ↓הסתר ↑
סריקת אבטחה חשפה 5 נקודות דליפה cross-tenant: (1) /api/users/online/stream החזיר משתמשים מכל הטננטים; (2-4) שלושת ה-endpoints של sorting-errors (GET/PATCH/POST/stats) לא סיננו לפי tenantId — אפשרו קריאה ועדכון של שגיאות מיון משל טננטים אחרים; (5) /api/debug-sorting לא סינן sortingError לפי tenantId. כולם תוקנו.
v01.38.000חדשportalminorאינטגרציה עם Shopify — חיבור חנויות בינלאומיות מהפורטל
לקוחות הפורטל יכולים כעת לחבר את חנות ה-Shopify שלהם לצד WooCommerce, קונימבו ו-CashCow.
פרטים נוספים ↓הסתר ↑
לקוחות הפורטל יכולים כעת לחבר את חנות ה-Shopify שלהם לצד WooCommerce, קונימבו ו-CashCow. בניגוד ל-CashCow/Konimbo (polling), Shopify דוחפת אלינו הזמנות חדשות בזמן אמת דרך webhook (orders/create, orders/paid, orders/updated) שעובר אימות HMAC-SHA256 base64 בעזרת ה-signing secret של החנות. עם מסירה — שיפנסט יוצרת Fulfillment על ההזמנה ב-Shopify דרך FulfillmentOrders API עם מספר המעקב, כך שהלקוח הסופי מקבל מייל אישור אוטומטי.
- ShopifyClient (apps/web/lib/integrations/shopify/client.ts) — Admin REST API 2024-01, X-Shopify-Access-Token; metoder: listRecentOrders, getShop (לtestConnection), createFulfillmentFromOpenOrders (שולף FulfillmentOrders פתוחים ויוצר Fulfillment משולב עם tracking_info)
- Webhook handler משותף ב-/api/webhooks/store/[connectionId] מזהה את platform=shopify אוטומטית, מאמת HMAC-SHA256 base64 על raw body (zero-trust — אם אין secret מוגדר ל-tenant ה-webhook נדחה)
- PLATFORMS extended: 'Shopify' כאופציה רביעית בטופס; supportsWebhook=true (לא polling); requiresSecret=false (טוקן יחיד shpat_); validation מחמירה ש-storeUrl חייב להסתיים ב-.myshopify.com (Admin API לא תומך ב-custom domains)
- ShopifyGuide חדש בפאנל הצדדי של הדיאלוג: מדריך דו-שלבי עם deep links ל-Apps/Develop apps ו-Settings/Notifications מבוסס על ה-storeUrl, רשימת scopes נדרשים (read_orders, write_orders, read_fulfillments, write_fulfillments, read_assigned_fulfillment_orders), אזהרת 'הטוקן נחשף פעם אחת בלבד'
- testConnection מציג שם החנות מ-/shop.json + הודעות שגיאה ספציפיות ל-401/403/404/429 עם body snippet מהשרת
- notifyShopifyDelivered חוברה ל-/api/v1/mobile/driver/visits/[id]/deliver כ-fire-and-forget לצד WC/Konimbo/CashCow
v01.37.000חדשapi/v1minorPublic API — השלמת CRUD ל-shipments ו-customers
נוספו 3 endpoints שהיו חסרים תחת /api/v1/, בהמשך לסבב ה-webhook subscriptions: ביטול משלוח כ-state transition נפרד (לא PATCH רגיל, כי הוא צריך להיות idempotent ולפלוט אירוע), וקריאה/עדכון של לקוח בודד לפי id.
פרטים נוספים ↓הסתר ↑
נוספו 3 endpoints שהיו חסרים תחת /api/v1/, בהמשך לסבב ה-webhook subscriptions: ביטול משלוח כ-state transition נפרד (לא PATCH רגיל, כי הוא צריך להיות idempotent ולפלוט אירוע), וקריאה/עדכון של לקוח בודד לפי id. עם אלה, ה-set של פעולות CRUD שמותרות דרך מפתח API מספיק כדי לבנות workflow מלא ב-n8n/Make: ליצור משלוח, לעדכן אותו, לבטל אותו, ולנהל את כרטיס הלקוח שלו.
- POST /api/v1/shipments/[barcode]/cancel — קובע canceledAt, idempotent (קריאה שנייה מחזירה alreadyCancelled=true ב-meta בלי לפלוט שוב את ה-webhook), פולט shipment.status_changed עם newStatus=canceled, ותומך ב-body אופציונלי { reason } שמתווסף ל-orgNote
- GET /api/v1/customers/[id] — קריאת לקוח בודד, scoped אוטומטית ל-tenant של ה-API key, scope customers:read
- PATCH /api/v1/customers/[id] — עדכון name/email/phone/address/isActive בלבד; שדות פנימיים (lionwheelId, summitId, picking config, accounting visibility) חסומים בכוונה. מאפשר null מפורש לעמודות nullable
- כל ה-endpoints החדשים שומרים על הקונבנציות הקיימות: withPublicApi, .strict() ב-zod, tenant מ-API key בלבד, ולא מחזירים שדות פנימיים אפילו אם הם קיימים במודל
v01.36.001תיקוןui/themeתיקון הבהוב שחור בכותרת טבלאות במצב לייט מוד
בעת טעינת דפי טבלה (כמו /shipments), משתמשים שמערכת ההפעלה שלהם dark אבל בחרו במצב לייט באפליקציה ראו הבהוב שחור חזק בשורת הכותרת לפני ש-next-themes הספיק להוסיף את class="light" ל-html.
פרטים נוספים ↓הסתר ↑
בעת טעינת דפי טבלה (כמו /shipments), משתמשים שמערכת ההפעלה שלהם dark אבל בחרו במצב לייט באפליקציה ראו הבהוב שחור חזק בשורת הכותרת לפני ש-next-themes הספיק להוסיף את class="light" ל-html. תיקון ה-FOUC הקודם לדארק היה מוסיף משתני צבע כהים דרך @media (prefers-color-scheme: dark) על :root:not(.light), מה שגרם לפער קצר שבו bg-card ו-bg-muted נפתרו לערכים כהים. נוסף inline script ב-<head> של ה-layout שרץ סינכרונית, קורא את localStorage.theme (או prefers-color-scheme אם system), ומוסיף את class="light"/"dark" ל-<html> לפני שה-CSS מתפרש. ה-media query הקיים נשאר כ-fallback כש-JS כבוי.
v01.36.000חדשapi/v1minorPublic API — ניהול webhook subscriptions דרך מפתח API
נוספו endpoints תחת /api/v1/webhooks המאפשרים ליצור, לרשום, לעדכן, למחוק ולבדוק webhook subscriptions באמצעות מפתח API (bearer ship_live_…), במקום רק דרך לוח האדמין הפנימי.
פרטים נוספים ↓הסתר ↑
נוספו endpoints תחת /api/v1/webhooks המאפשרים ליצור, לרשום, לעדכן, למחוק ולבדוק webhook subscriptions באמצעות מפתח API (bearer ship_live_…), במקום רק דרך לוח האדמין הפנימי. זו אבן הבניין הראשונה לקראת חיבור Shipnest לפלטפורמות אוטומציה חיצוניות (n8n, Make) — שני אלה דורשים endpoint רישום דינמי כדי שה-Trigger nodes שלהם יוכלו להירשם לאירועים.
- GET /api/v1/webhooks — רשימת ה-subscriptions של ה-tenant; לא מחזיר את ה-secret
- POST /api/v1/webhooks — יצירת subscription חדש; מחזיר את ה-secret פעם אחת בלבד (חתימת HMAC-SHA256 על payloads יוצאים)
- GET/PATCH/DELETE /api/v1/webhooks/[id] — קריאה, עדכון (name/url/events/isActive), ומחיקה
- POST /api/v1/webhooks/[id]/test — שליחת ping בדיקה ל-URL הרשום (rate-limited ל-10/60s כי זה side-effect חיצוני)
- כל ה-endpoints דורשים scope webhooks:manage, scoped אוטומטית ל-tenant של מפתח ה-API, ועוברים דרך withPublicApi (X-Timestamp, rate-limit פר-מפתח)
- מקור היצירה נרשם כ-api_key:<id> ב-createdById לצורך audit trail (מבדיל מ-subscriptions שנוצרו דרך לוח האדמין)
v01.35.000חדשportalminorאינטגרציה עם CashCow — חיבור חנויות מהפורטל
לקוחות הפורטל יכולים כעת לחבר חנות CashCow לצד WooCommerce וקונימבו, באותה זרימה.
פרטים נוספים ↓הסתר ↑
לקוחות הפורטל יכולים כעת לחבר חנות CashCow לצד WooCommerce וקונימבו, באותה זרימה. כמו קונימבו — CashCow אינה תומכת ב-webhooks ב-Shipnest כרגע, ולכן מומשה זרימת polling: cron הרץ כל 5 דקות מושך מ-https://api.cashcow.co.il/Api/Stores/Orders את ההזמנות החדשות (DaysFromNow=1 לרגיל, 7 בריצה ראשונה כדי לכסות backlog) וממיר אותן ל-Shipments. המסירה מסונכרנת חזרה ל-CashCow: כשנהג מסמן visit כ-delivered, נשלח POST ל-/Api/Stores/SendOrderUpdate עם order_status_type=6 (Delivered) ועם barcode כ-tracking_code.
- שכבת אינטגרציה חדשה ב-apps/web/lib/integrations/cashcow/: client (REST), types (כולל enum סטטוסים מספריים של CashCow), mapper (כתובת בשדות נפרדים — StreetNameAndNumber, City, ApartmentNumber, FloorNumber), delivery-sync
- Cron חדש /api/cron/sync-cashcow-orders בלו"ז 5 דקות; pagination עד 10 דפים×20 הזמנות per store; ברירת מחדל מסננת רק status=4 (שולם)
- PLATFORMS extended: 'CashCow' מופיע כאופציה שלישית לצד WooCommerce וקונימבו
- טופס דינמי: עבור CashCow מוצגים API Token + שדה Store ID מספרי חובה (זיהוי החנות הספציפית בחשבון)
- הגדרות אופציונליות פר-חנות: storeId (חובה) ו-orderStatusFilter (קודי סטטוס מופרדים בפסיקים — 4=שולם, 6=נמסר, 7=בבדיקה, 9=נדרש החזר)
- CashCowGuide חדש בפאנל הצדדי של הדיאלוג — מדריך 2 שלבים: קבלת Access Token מ-app.cashcow.co.il → Account settings → API/Webhooks settings, ואיתור Store ID מ-URL
- validation ב-zod: storeId חובה כאשר platform=cashcow (refine נוסף ב-createSchema)
- test-connection חדש ל-CashCow מטפל ב-401/403/429 עם הודעות מפורטות בעברית, surfaces את ה-body של CashCow
- delivery-sync שולף את email מ-additionalData (CashCow דורש email_address ב-SendOrderUpdate כאימות ownership של ההזמנה)
v01.34.000חדשportalminorאינטגרציה עם קונימבו — חיבור חנויות ישראליות מהפורטל
לקוחות הפורטל יכולים כעת לחבר את חנות הקונימבו שלהם דרך לשונית 'החנויות שלי' באותה זרימה כמו WooCommerce.
פרטים נוספים ↓הסתר ↑
לקוחות הפורטל יכולים כעת לחבר את חנות הקונימבו שלהם דרך לשונית 'החנויות שלי' באותה זרימה כמו WooCommerce. בניגוד ל-WooCommerce (push דרך webhooks), קונימבו לא תומכת ב-webhooks ולכן מומשה זרימת polling — cron הרץ כל 5 דקות מושך מ-https://api.konimbo.co.il/v1/orders את כל ההזמנות החדשות מאז ה-lastSyncAt וממיר אותן ל-Shipments. המסירה מסונכרנת חזרה לקונימבו: כשנהג מסמן visit כ-delivered, סטטוס חדש מתווסף להזמנה בקונימבו דרך PUT /v1/orders/{id}.
- שלוש שכבות חדשות: KonimboClient (REST), mapper (כתובת עברית מפורקת מ-string מאוחד), delivery-sync
- Cron חדש /api/cron/sync-konimbo-orders בלו"ז 5 דקות; מסונכרן רק מ-lastSyncAt (או createdAt בריצה ראשונה — בלי לייבא היסטוריה)
- PLATFORMS extended: 'קונימבו' מופיע כאופציה בבחירת פלטפורמה לצד WooCommerce
- טופס דינמי: עבור Konimbo רק 'API Token' (Consumer Secret/Webhook Secret מוסתרים)
- הגדרות אופציונליות פר-חנות (JSONB חדש על CustomerStoreConnection): paymentStatusFilter (default 'שולם,אשראי - מלא'), shippedStatusText (default 'נשלח')
- KonimboGuide חדש בפאנל הצדדי של הדיאלוג — מדריך 2 שלבים עם deep link לאזור הניהול
- תיקון deep links במדריך קונימבו — admin מרוכז ב-secure.konimbo.co.il (לא תלוי-חנות); קישור ישיר ל-/admin/api_users במקום ניווט ידני
- הנחיות מדויקות במסך יצירת משתמש API בקונימבו — איזה כפתור הרשאה להפעיל ('Orders - Used Externally'), מה לרשום ב'שם משתמש'/'מצב משתמש'/'access control allow origin', איפה הטוקן מופיע אחרי שמירה
- מודל DB: עמודה settings JSONB חדשה ב-customer_store_connections (גנרי לפלטפורמות עתידיות)
- מובייל — מרקרי מפתח בולטים: מרקר שליח כחול (42×42, halo שקוף + אייקון navigation + תווית 'אני') בכל המצבים פרט לציור; מרקר סיום כתום (flag, halo כתום, תווית שם המיקום); מרקר התחלה ירוק — עיגול עם play icon כשהמסלול התחיל מ-GPS, או טבעת ירוקה (startPinHalo) על ה-teardrop pin של הביקור הראשון ב-kind:visit/auto; state RouteStart מאפס בכל handleClear/handleEdit/enterDrawing/handleStartRoute
- מחסן Phase 4D.5 — זיהוי עומס אזורים: congestion.ts פונקציה טהורה מחשבת ציון עומס פר WhLocation.zone (משימות ממתינות + מלקטים פעילים×2 + סגמנטים IN_PROGRESS×3); סף 5 → אזור מסומן כעמוס
- GET /api/warehouse/congestion — חישוב live ללא טבלה, owner-scoped, עטוף ב-withWarehouseApi; ZoneCongestionPanel ב-MetricsDashboard עם רמות clear/moderate/high, רענון אוטומטי כל 2 דקות
- אינטגרציה אדיטיבית ב-batch-suggestion.ts: groupFulfillmentsByProximity מקבל congestedZones?:Set<string>; קנס של 10 נקודות פר אזור עמוס; ה-API contract לא השתנה
- 25 טסטים חדשים ל-congestion.ts — כיסוי מלא: ציון, ספירת מלקטים ייחודיים, מיון יורד, סף מותאם
- פורטל — מדריך חיבור WooCommerce state-aware: Step 1 מסומן ✓ כשהמפתחות מוזנים, Step 2 (Webhook) נפתח אוטומטית; מצב עריכה — Step 1 תמיד ✓, Step 2 פתוח עם ה-URL האמיתי; StoreCard — אחרי בדיקת חיבור מוצלחת מוצג CTA ישיר למדריך ה-Webhook
v01.33.000חדשwarehouseminorמחסן — סנכרון מלאי יוצא לחנויות eCommerce
Phase 4A.6 — שינוי מלאי במחסן מתעדכן אוטומטית בחנויות Shopify ו-WooCommerce המחוברות.
פרטים נוספים ↓הסתר ↑
Phase 4A.6 — שינוי מלאי במחסן מתעדכן אוטומטית בחנויות Shopify ו-WooCommerce המחוברות. כל שינוי ב-WhInventory (התאמה ידנית, ליקוט מאושר) מפעיל דחיפת רמת מלאי דרך ה-connector המתאים עם retry ו-backoff. כשל דחיפה מבודד לחלוטין — לא מפיל פעולות מחסן, נרשם ב-WhStoreSyncLog. פאנל יומן סנכרון מציג רשומות נכנסות ויוצאות per-connection עם מצב הצלחה/כשל. סנכרון מלאי ידני זמין ברשימת החיבורים. בנוסף: מערכת offline sync במובייל — פעולות COD/POD/Fail נשמרות בתור כשאין חיבור ומסונכרנות כשהחיבור חוזר; SyncChip בכותרת מציג סטטוס סנכרון בזמן אמת. שיפורי route optimizer — תמיכה ב-startVisitId וב-endLocationId, הוסף endLeg לתוצאה.
- inventoryChangedEvents — event-bus חדש; יורה מ-inventory/adjust ומ-pick-tasks confirmPick
- inventory-sync.ts: pushProductInventoryToStores עם retry 3 ניסיונות + backoff; כשל מתמשך ⇒ lastError על החיבור
- ליקוט מאושר (confirmPick) גם מפחית WhInventory.quantity ⇒ push אוטומטי לחנות
- POST /api/warehouse/connections/[id]/sync-inventory — דחיפת כל המוצרים הממופים ידנית
- GET /api/warehouse/connections/[id]/sync-logs — לוג סנכרון owner-scoped
- ConnectionSyncLogPanel — Sheet עם יומן סנכרון, כפתור 'סנכרן מלאי עכשיו', lastSyncAt
- פעולת 'יומן סנכרון' נוספה לרשימת החיבורים
- 9 טסטים חדשים — computeAvailableQuantity + buildSyncLogMessage; סה"כ 472 טסטים
- מובייל: offline-queue-store + net-status listener — פעולות משלוח עובדות ללא רשת
- מובייל: SyncChip בHomeHeader — מציג pending/syncing/failed בזמן אמת
- מובייל: RTL self-heal reload עם AsyncStorage guard למניעת לולאה אינסופית
- Route optimizer: תמיכה ב-startVisitId (נקודת התחלה מקובעת) ו-endLocationId, הוסף endLeg לתוצאה
- מובייל — מסך מפה: RouteSetupSheet — רשימת המשלוחים הנבחרים עם כפתור 'התחל מכאן' לכל משלוח (startVisitId), כפתורי 'התחל ממיקומך' (GPS) ו'בחירה אוטומטית' בפוטר; בורר נקודת סיום משפיע על שלושתם; ספינר per-button בזמן חישוב; מרקר ירוק של נקודת הסיום על המפה
- מובייל — מסך 'מיקומים שמורים' (4B): ניהול מלא — הוספה/עריכה/מחיקה עם אישור; geocoding בשמירה; מתג ברירת מחדל; כניסה מהגדרות; queryKey משותף עם המפה → עדכון אוטומטי של בורר נקודת הסיום
- מובייל: useVisitSyncStatus מומש — קורא את תור ה-offline פר-ביקור ומחזיר pending/failed/action; קודם היה stub שהחזיר false קבוע, כך שה-badge 'ממתין' וחסימת הכפתורים במסך הביקור לא פעלו
- מובייל: תיקון fallback ה-offline במסך פרטי ביקור — useVisit עבר מ-placeholderData ל-initialData; placeholderData נזרק כשהשאילתה נכשלת, מה שגרם למסך ליפול ל-ErrorState אחרי ~3ש offline
- מובייל: התחלת משמרת עמידה לכשל מעקב מיקום — startShift לא נכשל יותר כש-startLocationUpdatesAsync זורק, ו-restoreTrackingIfNeeded לא מסיים משמרת פעילה; מעקב מיקום הוא best-effort
- מובייל: זמן מסירה/כשל נרשם לפי הרגע שהשליח ביצע את הפעולה במכשיר (occurredAt מתור ה-offline) ולא לפי זמן הסנכרון; שעון מכשיר עתידי עובר clamp ל-now
- Phase 4B.1 — DB: WhTote + WhZoneSegment (+enums WhToteStatus/WhZoneSegmentStatus) — בסיס ל-Cluster & Zone picking
- Phase 4B.2 — CLUSTER picking: WhPickType הורחב (CLUSTER/ZONE); יצירת אצווה CLUSTER מקבצת N הזמנות, יוצרת WhTote פר-fulfillment, מנתבת S-shape על האיחוד; GET /batches/[id] מחזיר tote פר-משימה; 11 טסטים חדשים ל-cluster-utils
- Phase 4B.3 — מסך ליקוט מובייל תומך ב-CLUSTER: TaskCard מציג שורת 'שבץ לטוטה' (קוד טוטה, אייקון inbox) כשtote != null; snapshot API מחזיר WhTotes לאצוות CLUSTER (additive — מערך ריק עבור SINGLE/BATCH); buildTasksFromSnapshot ממפה fulfillmentId→tote; GET /api/v1/mobile/warehouse/batches/[id] כולל tote פר-משימה; WhBatchSummary.type הורחב ל-CLUSTER|ZONE
- Phase 4B.4 — ZONE picking engine: batch POST מחלק משימות לפי WhLocation.zone, ניתוב S-shape פר-אזור בנפרד, סדר ביקור אזורים מ-S-shape הגלובלי, יצירת WhZoneSegment פר-אזור (ראשון READY, שאר PENDING), WhTote יחידה לבאצ'ה; zone-segment-progression.ts — לוגיקה טהורה: nextSegmentToActivate + areAllSegmentsCompleted; GET /api/warehouse/zone-segments?batchId + PATCH /api/warehouse/zone-segments/[id] (assign/start/complete עם מעבר תוטה ACTIVE↔STAGED↔CLOSED); 16 טסטים חדשים; סה"כ 499 טסטים
- Phase 4B.5 — Zone picking במובייל: GET /api/v1/mobile/warehouse/batches/[id] מחזיר zoneSegments לאצוות ZONE; PATCH /api/v1/mobile/warehouse/zone-segments/[id] (start/complete) — auth מובייל, auto-assign pickerId, זהה לתוגיקת Zone engine; [batchId].tsx — מסך בורר אזורים (READY: 'התחל', IN_PROGRESS: 'המשך', PENDING: נעול, COMPLETED: ✓), משימות מסוננות לאזור הפעיל, מסך 'סיים אזור ומסור טוטה' לאחר השלמת כל משימות האזור; כפתור download מוחלף באייקון wifi — אצוות ZONE אינן offline-capable; zone field נוסף ל-OfflineLocation + snapshot API (additive)
- Phase 4B.6 — UI יצירה וניטור אצוות: BatchCreateDialog — 4 כרטיסי אסטרטגיה (SINGLE/BATCH/CLUSTER/ZONE) עם תיאור, SINGLE מגביל לבחירה אחת; BatchDetailSheet — פאנל טוטות ל-CLUSTER (קוד, סטטוס ACTIVE/STAGED/CLOSED, התקדמות משימות פר-הזמנה), פאנל סגמנטי אזור ל-ZONE (רצף, שם אזור, סטטוס, מלקט, התקדמות), עמודת סוג בטבלה צבעונית; GET /api/warehouse/batches/[id] מחזיר totes + zoneSegments + zone בלוקיישן; רכיב אחד משותף לצוות ולפורטל
- פורטל — מדריך חיבור WooCommerce עודכן על-בסיס בדיקה חיה: הוסף אזהרת סדר פעולות (חיבור בפורטל קודם, הגדרת webhook אחר-כך), הדגשה על העתקת ה-URL המלא דרך כפתור ההעתקה, והבהרה ש-WooCommerce ממלא את שדה ה-Secret אוטומטית — יש להשאיר ריק
- בדיקות אוטומטיות Feature C (route endpoints): 11 טסטים חדשים ל-optimizer.test.ts (startVisitId+endLocation combo, end-never-in-middle, single-visit edge cases, startLocation precedence) + קובץ route-api-helpers.ts עם 3 פונקציות טהורות (startVisitIdInList, startVisitSurvivedFilter, buildMatrixPoints) + 19 טסטים ב-route-api-helpers.test.ts
- תיקון באג: geocoding מיקום שמור עלול להחזיר עיר שגויה כשרחוב קיים בכמה ערים — הקו על המפה נמשך לעיר הלא נכונה. נוספה isGeoWithinCity ב-geocoding.ts (haversine בין תוצאת geocoding למרכז היישוב מה-Settlement table, סף 10 ק"מ); POST ו-PATCH של saved-locations מחזירים 422 GEOCODE_CITY_MISMATCH כשהפער גדול; כשהיישוב לא נמצא בטבלה — אין דחייה. סה"כ 590 טסטים
- Phase 4D.1 — DB: WhReplenishmentAlert + WhSlottingRecommendation (+enums WhAlertStatus/WhRecommendationStatus) owner-scoped — בסיס לאנליטיקה היוריסטית; שלד analytics/ (consumption/slotting/batch-suggestion/congestion) — פונקציות טהורות בלבד, ללא DB ישיר, ללא ML
- פורטל — ויזרד חיבור חנות דו-שלבי: שלב 1 בחירת פלטפורמה + URL + שם; שלב 2 מפתחות API עם מדריך ישיר ולינקים חיים; פאנל ימין מציג תקציר מה נדרש בשלב 1 ומדריך מלא בשלב 2; edit mode עוקף לשלב 2 ישירות
- Phase 4D.2 — התראות חידוש מלאי: consumption.ts — 5 פונקציות טהורות (computeDailyConsumptionRate, computeDaysToDepletion, shouldRaiseAlert, buildReplenishmentRationale, computeReplenishmentForProduct) + 30 טסטים; POST /api/warehouse/replenishment/run — ניתוח owner-scoped, קצב מ-WhPickTask (חלון 30 יום), upsert להתראה, auto-resolve כשסיכון חלף; GET /api/warehouse/replenishment — רשימת התראות עם שם מוצר; PATCH /api/warehouse/replenishment/[id] — ACKNOWLEDGED/RESOLVED; ReplenishmentAlertsView — כרטיסי מוצר עם מלאי/קצב/ימים-נותרים, urgency צבע, 'הרץ ניתוח עכשיו'; 564 טסטים
- Phase 4D.3 — Slotting — אופטימיזציית מיקום מלאי: slotting.ts — 6 פונקציות טהורות (computeLocationTravelCosts — שימוש חוזר ב-S-shape, computePickVelocities, computeAffinityPairs, computeVelocityRecommendations, computeAffinityRecommendations, computeSlottingRecommendations) + 26 טסטים; inventory-adjust-helper.ts — applyInventoryAdjust + moveInventoryBetweenLocations שעוטפים נתיב ה-Phase 1 (upsert+event) בלי שכפול; POST /api/warehouse/slotting/run — ניתוח owner-scoped, velocity + affinity, upsert המלצות; GET /api/warehouse/slotting — רשימה עם שמות מוצר ומיקום; PATCH /api/warehouse/slotting/[id] — apply (העברת מלאי דרך moveInventoryBetweenLocations+invalidate inventory) | dismiss; SlottingRecommendationsView — כרטיסי המלצה עם מיקום נוכחי/מוצע, חיסכון משוער, נימוק, Confirm Dialog לאישור, 'הרץ ניתוח עכשיו'; 590 טסטים
- Phase 4D.4 — הצעות אצוות חכמות: batch-suggestion.ts — 6 פונקציות טהורות (computeZoneJaccard, countSharedZones, selectStrategy, buildRationale, computeGroupScore, groupFulfillmentsByProximity) + 46 טסטים; groupFulfillmentsByProximity — אלגוריתם greedy clustering לפי חפיפת אזורים; selectStrategy — SINGLE/BATCH/CLUSTER/ZONE לפי מספר אזורים ו-fulfillments; GET /api/warehouse/batch-suggestions — מחושב live ללא טבלה, owner-scoped, מפשר שמות מוצר ← WhInventory → WhLocation → zones; BatchCreateDialog — פאנל 'הצעות חכמות' מעל כרטיסי האסטרטגיה, קליק על הצעה ממלא selectedIds+pickType, יצירה דרך מנוע POST /api/warehouse/batches הקיים — ללא שכפול ולא עקיפה; 636 טסטים
v01.32.000חדשcreditsminorזיכויים — טאב היסטוריית עריכה
הוספת מעקב אחר כל הפעולות על זיכוי: יצירה, עריכה, אישור וביטול.
פרטים נוספים ↓הסתר ↑
הוספת מעקב אחר כל הפעולות על זיכוי: יצירה, עריכה, אישור וביטול. כל פעולה נשמרת ב-AuditLog עם שם המשתמש, זמן הפעולה, ורשימת השדות שהשתנו (לפני/אחרי). בפאנל הזיכוי נוסף טאב 'היסטוריה' עם ציר זמן מקובץ לפי תאריך.
- logAudit ב-create, update, approve ו-cancel — כולל diff שדות לעדכון
- GET /api/credit-notes/[id]/activity-log — מחזיר רשומות עם שם משתמש ושינויי שדות מפורמטים
- CreditNoteSheet — טאב 'פרטים' / 'היסטוריה'; ציר זמן מקובץ לפי תאריך עם badge צבעוני לכל פעולה
- AuditAction type הורחב ל-approve ו-cancel
- טופס זיכוי — רשימת הלקוחות מצטמצמת אוטומטית לשולחי המשלוחים שנבחרו (intersection לפי customerId). שדה הלקוח והשליח עצמאיים — סינון רק על לקוח כי השליח האמיתי במסירה עשוי להיות אחר. שדה customerId נחשף ב-/api/shipments barcode lookup וב-global search
v01.31.000חדשmobileminorמובייל — חיווי סנכרון offline ומסך ניהול תור
Phase E של מנוע ה-Offline: שכבת UX גלובלית מעל המנוע הקיים.
פרטים נוספים ↓הסתר ↑
Phase E של מנוע ה-Offline: שכבת UX גלובלית מעל המנוע הקיים. באנר amber מופיע מתחת ל-SafeArea כשאין חיבור לרשת. chip סנכרון ב-HomeHeader מציג מצב התור בסדר עדיפויות: כשלים (אדום) > מסנכרן > ממתינים (amber). לחיצה על ה-chip פותחת מסך סנכרון ייעודי עם רשימות pending/failed, כפתורי 'נסה שוב' ו'מחק' פר-פריט, וכפתור 'סנכרן עכשיו' גלובלי. הוספו actions חדשים לסטור: retryOne(id) ו-removeAction(id). store קישוריות חדש (connectivity-store) מוזן על-ידי מאזין NetInfo הקיים. תיקון: שגיאת Meta 131048 (הגבלת מספר טלפון עקב ספאם) מוצגת כעת בעברית. מסגרת הסריקה (scan.tsx ו-[batchId].tsx) הוגדלה לדינמית — 85% מרוחב המסך במקום 250px קבועים.
- OfflineBanner — פס amber מתחת ל-safe-area, מופיע/נעלם לפי קישוריות
- SyncChip ב-HomeHeader — עדיפות: כשלים > מסנכרן > ממתינים > מוסתר
- מסך sync-status: רשימות pending/failed, retry/delete פר-פריט, 'סנכרן עכשיו'
- connectivity-store: Zustand ללא persist, מוזן מ-NetInfo הקיים
- retryOne(id) ו-removeAction(id) נוספו ל-offline-queue-store
- Phase F — תשתית vitest: 4 סוויטות בדיקות (33 טסטים) לכיסוי connectivity-store, offline-queue-store, net-status ו-sync-processor; checklist בדיקה ידנית ל-5 תרחישי offline
- מפה: בורר סגנון מתעדכן מיידית עם חזרה לטאב (useFocusEffect)
- מפה: תיקון ציור פוליגון — worldSize 512 + unproject מ-MapLibre בסיום הגרירה
- מפה: markers שוחזרו לעיצוב pin טיפתי (עיגול + גוף מחודד) עם צל
- מפה: תיקון offset ציור פוליגון — mapLayout הומר מ-state ל-ref כדי למנוע closure מושרש בגובה חלון מלא
- webhook: תיקון סנכרון פריטי הזמנה מליונוויל — כאשר ה-webhook לא כולל order_items, מביאים אותם ישירות מה-API; מעתה מסתנכרנים גם ביצירת משלוח חדש ולא רק בעדכון
- סכמה — תכונה C שלב 1: מודל DriverSavedLocation (מיקומים שמורים פר-שליח לנקודות סיום מסלול) + שדה endLocationId ב-RouteBuild; Prisma client עודכן
- אופטימייזר מסלול — תכונה C שלב 2: startLocation/endLocation כצמתים אמיתיים במטריצה; חתימה חדשה optimizeRoute(visits, options?, matrix?); haversine fallback מאוחד לנתיב יחיד; endLeg בתגובה; 31 בדיקות יחידה
- תכונה C שלב 3A — API מיקומים שמורים לשליח: GET/POST /api/v1/mobile/driver/saved-locations + PATCH/DELETE /[id]; geocoding חובה ביצירה ובעדכון כתובת; cap 20 מיקומים; טרנזקציית isDefault; קליינט מובייל saved-locations.ts
- תכונה C שלב 3B — route/optimize + route/activate: startVisitId, endLocationId; מטריצה כוללת endPoint; geometry מסתיים בנקודת הסיום; endLeg בתגובה (finalLegKm/Minutes); RouteBuild שומר endLocationId; קליינט מובייל מורחב
- ייבוא משלוחים — כפתור "ייבא מחדש" נפרד: טוען את כל השורות מייבוא קודם (כולל הצלחות) לייבוא חוזר של אותה עבודה שבועית; "ייבא כשלים" ממשיך לעבוד כרגיל ומוצג רק כשיש כשלים
- Phase 4A.1 — eCommerce Store Connections: 4 מודלים חדשים (WhStoreConnection, WhStoreProductLink, WhStoreOrderLink, WhStoreSyncLog) + 4 enums; CRUD /api/warehouse/connections owner-scoped, credentials מוצפנים AES-256-GCM, לעולם לא מוחזרים ב-API
- Phase 4A.2 — Connector abstraction: ממשק EcommerceConnector אחיד; ShopifyConnector (Admin API + HMAC) ו-WooCommerceConnector (REST Basic Auth + HMAC) — אדישים-לפלטפורמה, ללא if-platform בלוגיקת הליבה; factory createConnector; POST /api/warehouse/connections/[id]/test מעדכן status/lastError; domain types ב-apps/api/src/modules/warehouse/; 22 טסטי יחידה (HMAC + נרמול הזמנה) — ירוקים
- Phase 4A.3 — מסך חיבורי חנות: StoreConnectionsView (UnifiedTable, badges פלטפורמה/סטטוס, tooltip שגיאה, זמן סנכרון יחסי) + StoreConnectionForm (password fields, toggle show/hide, credentials לעולם לא מוחזרים ב-UI); פעולות שורה: בדיקה / עריכה / השבתה / מחיקה; דפי staff+portal thin wrappers; ניווט sidebar (staff + portal)
- Phase 4A.4 — מיפוי מוצרים (SKU auto-match + ידני): autoMatchProducts() — פונקציה טהורה עם 11 טסטים (התאמת SKU case-insensitive, מניעת כפילויות, owner-scope isolation); API: GET/POST /product-links, POST /product-links/import (fetchProducts + bulk match), DELETE+PATCH /product-links/[linkId]; StoreProductMappingView — טבלת מיפויים, banner תוצאות ייבוא, רשימת unmatched למיפוי ידני; StoreProductMapDialog — חיפוש WhProduct + אישור; דף staff+portal; פעולת 'מיפוי מוצרים' ברשימת החיבורים
- Phase 4A.5 — ייבוא הזמנות: webhook POST /api/webhooks/warehouse/[connectionId] קולט order.created מ-Shopify ו-WooCommerce (אימות HMAC, flag check, אידמפוטנטיות דרך WhStoreOrderLink); ייבוא ידני POST /api/warehouse/connections/[id]/import-orders עם פרמטר days; לוגיקת ייבוא ב-order-import.ts: parseStreetAddress/normalisePhone/findUnmatchedSkus — פונקציות טהורות עם 20 טסטים; יצירת Shipment + WhFulfillment(NEW) + WhStoreOrderLink + WhStoreSyncLog בטרנזקציה אחת; שורה ללא מיפוי SKU — נרשמת ב-SyncLog כאזהרה ולא מפילה ייבוא; פעולת 'ייבא הזמנות' נוספה לרשימת החיבורים
- מובייל: מרחק בכל כרטיס ביקור מחושב עתה client-side מ-GPS הנהג (haversine) — מוצג לכל ביקור כולל 'לא במסלול'; fallback לערך ה-API כאשר GPS אינו זמין
- תיקון offline — מסך פרטי ביקור נטען מ-cache של 'היום שלי' כשאין רשת (placeholderData ב-useVisit; error gate שונה ל-isError && !data בכל מסכי הביקור)
- תצוגת ציר זמן — תיקון: 'יציאה' מוצגת רק לאחר שה-visit בוצע בפועל (נמסר/כשל) או שהתקבל OUT_INVENTORY; קודם cron rollover-open-visits היה מזיז את visitAt להיום וגורם לתאריך כוזב
- תיקון: סינון לפי סטטוס נכשל (ואחרים) גרם ל-500 — עמודת error_code הוזכרה בשאילתת raw SQL בשם "errorCode" (camelCase במרכאות) במקום error_code, מה שגרם ל-Postgres לזרוק שגיאה; תוקן ל-s.error_code as "errorCode"
- ציר זמן — מיון אירועים לפי תאריך עם עדיפות לוגית לבו-זמניים (ליקוט ← איסוף ← קליטה); קו סגול בין אירועים שקרו באותה דקה
- תיקון: חיפוש משלוח בטופס יצירת זיכוי לא החזיר תוצאות — API דרש תאריכים גם לחיפוש לפי ברקוד; נוסף early-return ייעודי ל-barcode query שעוקף את חובת טווח התאריכים
- מחירון לקוח — תיקון מילוי אוטומטי: מעבר מ-onBlur ל-onChange עם מצב autoFillSource; כל עוד המשתמש מקליד בשדה הראשון, כל שאר השדות מתעדכנים בזמן אמת; עם יציאה מהפוקוס המילוי האוטומטי מסתיים ולכל שדה ערך עצמאי
- תיקון: שדות סכומים בטופס זיכוי — אייקון ₪ ומספר נצמדו שמאלה ודרסו אחד את השני; תוקן ל-right-3/pr-8 בהתאם לפטרן במחירון הלקוח
v01.30.001תיקוןmobileמובייל — תיקון תוויות מפה לעברית (MapLibre vector)
הפרמטר &language=he ב-URL של style.json אינו מלקלל תוויות וקטוריות — הן מרונדרות בצד הלקוח לפי text-field של שכבות הסמלים.
פרטים נוספים ↓הסתר ↑
הפרמטר &language=he ב-URL של style.json אינו מלקלל תוויות וקטוריות — הן מרונדרות בצד הלקוח לפי text-field של שכבות הסמלים. התיקון: טעינת style JSON, מעבר על כל שכבות symbol, והחלפת text-field ל-coalesce name:he/name:latin/name. בנוסף תוקנו שלוש רגרסיות: (1) הגנת usesNameField — רק שכבות מבוססות name משוכתבות, שלטי כבישים (ref), גובה (ele) ומספרי בית נשמרים ללא שינוי. (2) לחיצה על marker של עצירה פותחת כרטיס פרטים — הוספת markerJustPressedRef למניעת ביטול ידי Map.onPress. (3) markers הוגדלו ל-22px עם צל לשיפור נראות וקלות לחיצה.
v01.30.000חדשwarehouse, integrationsminorמחסן — חוויית offline מלאה + מסך הכרעת קונפליקט
Phase 4C.6: חוויית offline שקופה למלקט.
פרטים נוספים ↓הסתר ↑
Phase 4C.6: חוויית offline שקופה למלקט. באנר קישוריות אדום בכל מסכי המחסן בעת חוסר רשת. תג סטטוס offline פר-אצווה ברשימה (זמינה offline / ממתינה לסנכרון / מסונכרנת / קונפליקט). מסך הכרעת קונפליקט: כשmutations נדחו — הצגת הפעולות שנדחו ואפשרות לבטל ולנקות. אזהרת קישוריות כשמנסים לגשת לאצווה לא מורדת בלי רשת. בנוסף: עיצוב מחדש של דף האינטגרציות — קיבוץ לפי קטגוריה (משלוחים / הנה"ח / WhatsApp) עם דיאלוגים ייעודיים לכל קטגוריה.
- ConnectivityBanner — מוצג בכל מסכי המחסן בעת חוסר רשת
- תג offline פר-אצווה ב-BatchCard עם צבע לפי סטטוס סנכרון
- ConflictResolutionSheet — הצגת פעולות מתנגשות + ביטול מאושר
- אזהרה מונעת גישה לאצווה לא מורדת בלי רשת
- לחיצה על סטריפ קונפליקט במסך הליקוט פותחת את מסך ההכרעה
- דף אינטגרציות: קיבוץ לפי קטגוריה — ספק משלוחים (כרטיסייה אחת עם radio), הנה"ח, WhatsApp
- DeliveryDialog: radio לבחירת Lionwheel/בלדר/SmארטRun + toggle משלוחים עצמאיים
- AccountingDialog: ספק הנה"ח (Summit + עתידי) בדיאלוג ייעודי
v01.29.000שיפורintegrationsSummit — חיבור דרך דף האינטגרציות
אינטגרציית Summit עברה מקריאת מפתחות ממשתני סביבה גלובליים לטעינה per-tenant מ-TenantProviderConfig, בדיוק כמו Lionwheel ו-Meta.
פרטים נוספים ↓הסתר ↑
אינטגרציית Summit עברה מקריאת מפתחות ממשתני סביבה גלובליים לטעינה per-tenant מ-TenantProviderConfig, בדיוק כמו Lionwheel ו-Meta. נוסף שורת ExternalProvider ל-Summit ב-DB, וכעת ניתן להגדיר את המפתחות דרך דף הגדרות > אינטגרציות.
v01.29.000חדשwarehouseminorמחסן — מנוע סנכרון offline + זיהוי קישוריות
Phase 4C.5: מנוע סנכרון לאצוות שהורדו לעבודה offline.
פרטים נוספים ↓הסתר ↑
Phase 4C.5: מנוע סנכרון לאצוות שהורדו לעבודה offline. זיהוי קישוריות ב-NetInfo מפעיל סנכרון אוטומטי בחזרת רשת. outbox מנוקז לפי sequence ל-POST /api/v1/mobile/warehouse/sync עם retry ו-backoff מעריכי. תוצאות פר-mutation (applied/duplicate → synced, conflict → conflict). אינדיקטור סטטוס סנכרון ב-UI: ממתין לסנכרון / מסנכרן / מסונכרן / קונפליקט. כפתור 'סנכרן עכשיו' ב-UI. קוד הסנכרון בשרת הועבר ל-sync-core.ts משותף ל-web ו-mobile. אין רגרסיה בזרימה המקוונת מ-Phase 2.
- NetInfo — סנכרון אוטומטי בחזרת קישוריות
- Retry + exponential backoff (1s/2s/5s/10s/20s) על כשלי רשת ו-5xx
- idempotency: mutationId ב-WhSyncLog — duplicate מוחזר ולא מיושם פעמיים
- SyncBanner ב-batch list + sync strip ב-picking screen
- sync-core.ts — קוד מיושם ב-web וב-mobile route משותף
v01.28.000חדשmobileminorמובייל — בורר סגנון מפה בהגדרות
הוספת בורר סגנון מפה בדף הפרופיל.
פרטים נוספים ↓הסתר ↑
הוספת בורר סגנון מפה בדף הפרופיל. המשתמש יכול לבחור מ-4 סגנונות MapTiler: רחובות, שטח, לוויין, בהיר. הבחירה נשמרת ב-AsyncStorage וטעונה עם אתחול המפה. כל הסגנונות מוצגים בעברית מלאה (language=he).
- 4 סגנונות MapTiler לבחירה: streets-v2 / outdoor-v2 / hybrid / basic-v2
- Bottom-sheet Modal — חוויה נייטיב, ניתן לסגור בהקשה מחוץ
- בחירה נשמרת ב-AsyncStorage וטעונה עם כניסה למפה
- כל הסגנונות נטענים עם language=he — עברית תמיד
v01.27.000שיפורmobileminorמובייל — מעבר למפה וקטורית MapLibre (תוויות עברית)
שכתוב מסך המפה מ-react-native-maps למפה וקטורית אמיתית עם @maplibre/maplibre-react-native.
פרטים נוספים ↓הסתר ↑
שכתוב מסך המפה מ-react-native-maps למפה וקטורית אמיתית עם @maplibre/maplibre-react-native. תוויות המפה בעברית מלאה ב-streets-v2 של MapTiler ללא תלות בשפת המכשיר. כל ההתנהגות הקיימת נשמרה: סיכות ממוספרות, קווי מסלול עם גאומטריית OSRM, ציור פוליגון במגע, ושני מצבי route. המרת קואורדינטות בציור הועברה למשוואת Mercator שמחשבת מ-zoomLevel + center.
- מפה וקטורית — תוויות חדות בכל zoom, RTL-aware
- GeoJSONSource + Layer לקווי מסלול ופוליגון ציור
- Marker component לסיכות — תמיכה ב-custom views ממוספרים
- screenToLatLng מבוסס-Mercator — ציור פוליגון דיוק זהה
- בורר סגנון: mapStyle prop מ-AsyncStorage עם fallback
v01.26.001שיפורmobileמובייל — התקנת MapLibre (תשתית)
התקנת @maplibre/maplibre-react-native ^11.2.1 והוספת ה-plugin ל-app.json.
פרטים נוספים ↓הסתר ↑
התקנת @maplibre/maplibre-react-native ^11.2.1 והוספת ה-plugin ל-app.json. expo-dev-client כבר היה מותקן. הכנה לשכתוב מסך המפה ממפת הבסיס הנייטיב למפה וקטורית.
v01.26.000חדשwarehouse / mobileminorמחסן — ליקוט offline מלא למלקט המובייל (Phase 4C.4)
מלקט המובייל יכול עכשיו להוריד אצווה לעבודה ללא קישוריות.
פרטים נוספים ↓הסתר ↑
מלקט המובייל יכול עכשיו להוריד אצווה לעבודה ללא קישוריות. לחיצה על כפתור הורדה בראש מסך האצווה שומרת את כל נתוני הסנפשוט מקומית (AsyncStorage). בזמן ליקוט offline — כל פעולה (ליקוט, דילוג, חריגה, סיום) מתעדכנת מיידית ב-store המקומי ומצטברת ב-outbox לסנכרון מאוחר. הזרימה המקוונת מ-Phase 2 נשמרת ללא רגרסיה.
- כפתור 'הורד לעבודה offline' בכותרת מסך האצווה — endpoint חדש /api/v1/mobile/warehouse/batches/[id]/snapshot
- מסך הליקוט עובד מול store מקומי בלבד עבור אצווה שהורדה (ללא תלות ברשת)
- אימות ברקוד ומיקום offline מול נתוני הסנפשוט המקומי
- BATCH_START אוטומטי לפני הפעולה הראשונה; BATCH_COMPLETE בסיום כל המשימות
- הזרימה המקוונת מ-Phase 2 נשמרת ללא שינוי
v01.25.000חדשintegrationsminorאינטגרציה עם WooCommerce
חיבור חנויות WooCommerce לשיפנסט: כל הזמנה חדשה נוצרת אוטומטית כמשלוח, ועם מסירה — סטטוס ההזמנה בחנות מתעדכן אוטומטית ל-Completed.
פרטים נוספים ↓הסתר ↑
חיבור חנויות WooCommerce לשיפנסט: כל הזמנה חדשה נוצרת אוטומטית כמשלוח, ועם מסירה — סטטוס ההזמנה בחנות מתעדכן אוטומטית ל-Completed. הגדרה מתבצעת הן בדשבורד הלקוח (admin) והן בפורטל הלקוח — כל לקוח יכול לחבר חנויות מרובות עצמאית.
- יבוא הזמנות אוטומטי דרך WooCommerce Webhook (order.created / order.updated)
- מיפוי כתובת ישראלית: פיצול רחוב/מספר, דירה, עיר
- בדיקת חיבור מובנית (Test Connection) עם Consumer Key + Secret
- עדכון סטטוס הזמנה ל-Completed בחנות עם מסירת המשלוח
- אימות חתימת HMAC-SHA256 על webhooks נכנסים
- פורטל לקוח — עמוד 'החנויות שלי': חיבור וניהול חנויות מרובות לכל לקוח
- Webhook URL ייחודי לכל חנות (per-connection) מוצג ישירות בפורטל
v01.24.002שיפורmobileמובייל — תוויות מפה קבועות בעברית דרך אריחי MapTiler
מפת הנהג מציגה עכשיו תוויות בעברית ללא תלות בשפת המכשיר.
פרטים נוספים ↓הסתר ↑
מפת הנהג מציגה עכשיו תוויות בעברית ללא תלות בשפת המכשיר. הוטמע רכיב UrlTile מ-react-native-maps המציג אריחי raster של MapTiler (streets-v2 + language=he) מעל מפת הבסיס. על Android הוסף customMapStyle שמסתיר תוויות POI/כביש נייטיב כדי למנוע כפילות. אם EXPO_PUBLIC_MAPTILER_KEY לא מוגדר — מפת הבסיס הנייטיב נשארת ללא קריסה.
v01.24.001שיפורmobileמובייל — ציור קו מסלול מבוסס-כביש על המפה
אפליקציית המובייל מציגה כעת קו מסלול לאורך הכבישים במקום קו ישר.
פרטים נוספים ↓הסתר ↑
אפליקציית המובייל מציגה כעת קו מסלול לאורך הכבישים במקום קו ישר. בעת אופטימיזציית מסלול — גאומטריית OSRM מוחזרת מה-API ומוצגת ב-Polyline; כשה-geometry חסר (OSRM לא זמין) — fallback לקו ישר בין העצירות. המסלול הפעיל (view mode) טוען את הגאומטריה השמורה מ-route_build דרך activeRoute.geometry ב-today API. activateRoute שולח את הגאומטריה ל-backend לשמירה. עדכוני טיפוסים: geometry + routingSource ב-OptimizedRouteData; ActiveRoute interface חדש ב-TodayData.
v01.24.000חדשwarehouseminorמודול מחסן — Phase 2.6: UI תור ליקוט, אצוות, אריזה וחריגות
תיקון 4 כשלוני טסטים pre-existing: הודעות ברירת מחדל ב-api-errors עודכנו לעברית (לא מורשה / אין הרשאה / לא נמצא); ציפיית EMPLOYEE_MANAGER בטסט role-permissions תוקנה לשקף שיש לו SHIPMENTS_VIEW (כפי שמוגדר בקוד).
פרטים נוספים ↓הסתר ↑
תיקון 4 כשלוני טסטים pre-existing: הודעות ברירת מחדל ב-api-errors עודכנו לעברית (לא מורשה / אין הרשאה / לא נמצא); ציפיית EMPLOYEE_MANAGER בטסט role-permissions תוקנה לשקף שיש לו SHIPMENTS_VIEW (כפי שמוגדר בקוד). Phase A — אידמפוטנטיות ב-Backend לפעולות שטח במובייל: מודל Prisma חדש MobileActionIdempotency (טבלה mobile_action_idempotency, @@unique[tenantId, idempotencyKey]); helper withMobileIdempotency — קורא Idempotency-Key header, תופס את המפתח ב-INSERT, מריץ handler פעם אחת ושומר status+body, בקריאה חוזרת מחזיר תגובה שמורה מילה-במילה; in-flight → 409; חסר header → backward-compatible. שכבה שנייה: deliver מחזיר 200 אם כבר deliveredAt (לא 409); fail מחזיר 200 אם כבר failedAt. כל 4 endpoints (deliver/fail/cod/pod) חוברו דרך ה-helper. cron cleanup-logs מנקה רשומות ישנות מ-7 ימים. 8 טסטי vitest כוסו את כל הנתיבים (no-key, claim+cache, replay, in-progress, race, diff-keys, handler-throws, non-P2002-error). 4 רכיבים משותפים חדשים ב-components/warehouse/: FulfillmentQueueView (תור + הוספה לתור מרשימת משלוחים זמינים), BatchListView (יצירת אצווה, בחירת fulfillments + type, BatchDetailSheet עם משימות לפי sequence), PackingView (אריזה + מעבר PICKED→PACKING→PACKED), ExceptionListView (רשימת חריגות + ExceptionStatusSheet לעדכון סטטוס ושיוך מטפל). כל רכיב עצמאי — צוות ופורטל מייבאים אותו ישירות ללא שכפול קוד. הוספת 4 עמודים לצוות ו-4 לפורטל. עדכון ניווט: 4 פריטים חדשים בתפריט המחסן של הצוות ו-4 בפורטל (גדורים ב-warehouseEnabled). עדכון API: GET /fulfillments מצרף נתוני משלוח; GET /batches/[id] מצרף location + product לכל pick task. Phase 2.7: הרחבת mobile auth לקבלת CustomerUser (מלקט-לקוח) — login/me/refresh. 3 endpoints מובייל חדשים: GET /warehouse/batches, GET /batches/[id] (auto-assign OPEN→ASSIGNED), PATCH /pick-tasks/[id] (confirmPick/skip/reportException). אפליקציית מובייל: route group (warehouse) עם מסך רשימת אצוות ומסך ליקוט מלא (סריקת מיקום → סריקת מוצר → haptic → הבאה).
- רכיבים משותפים — צוות ופורטל משתמשים באותו קוד, כל צד רק ה-owner שלו
- FulfillmentAddDialog — חיפוש משלוחים זמינים בזמן אמת
- BatchCreateDialog — multi-select fulfillments + בחירת סוג ליקוט
- PackingView — מעבר PICKED→PACKING→PACKED עם toast ברקוד המשלוח
- ExceptionListView — פילטר status/type + Sheet לטיפול וסגירה
- Mobile: CustomerUser auth — מלקט-לקוח מתחבר עם אותו JWT, skip device check
- Mobile: זרימת ליקוט — סרוק מיקום → סרוק מוצר → haptic success → משימה הבאה
- Mobile: דיווח חריגה ממסך הליקוט + skip
- תיקון: loading skeleton של דף עדכונים תואם כעת למבנה ה-changelog (major/minor/patch cards)
- תיקון: הסרת 'use client' מדף עדכונים — מרונדר בשרת, ללא הבהוב בהיר במעבר מהסקלטון
- תיקון: FOUC במצב דארק — @media prefers-color-scheme:dark מגדיר border/bg מיידית לפני ה-JS; html מקבל background-color ישיר
- תיקון: הבזק בורדר לבן בשורות טבלה — TableRow/Header/Footer מחליפים border-neutral-200 dark:border-neutral-800 ב-border-border (CSS variable, מוגן כבר ע"י media query)
- ניתוב מבוסס-כביש: OSRM client (lib/routing/osrm-client.ts) — getTravelMatrix + getRouteGeometry עם timeout 5s ו-fallback null; optimizer.ts תומך ב-TravelMatrix (NN+2-opt על זמני נסיעה אמיתיים, legs ממטריצת distances/durations); optimize endpoint מחזיר geometry ו-routingSource; activate שומר geometry ב-route_build; today מחזיר activeRoute.geometry לאפליקציה
- תיקון Phase 2.8 — לוגיקת מעברי סטטוס חולצה ל-lib/warehouse/status-progression.ts (פונקציות טהורות ניתנות לטסט); 23 טסטי vitest מכסים את כל מקרי הקצה
- תיקון Phase 2.8 — layout route group (warehouse) במובייל כולל כעת בדיקת דגל פיצ'ר (GET /api/v1/mobile/warehouse/enabled) בנוסף לבדיקת role; כשהדגל כבוי — redirect אוטומטי גם ל-WAREHOUSE_WORKER
- Phase 3.1 — שדות חותמת-זמן למדידה (nullable, אדיטיביים): WhBatch.startedAt/completedAt, WhFulfillment.pickingStartedAt/pickedAt/packedAt, WhPickTask.pickedAt; מאוכלסים אוטומטית בנקודות מעבר הסטטוס הקיימות
- Phase 3.2 — GET /api/warehouse/metrics: KPI cards (fulfillments לפי סטטוס, קצב ליקוט, זמן מחזור, זמן אצווה, אחוז חריגות, חריגות פתוחות), משפך סטטוסים, סדרת זמן יומית, טבלת מלקטים — הכול owner-scoped, עטוף ב-withWarehouseApi
- Phase 3.3 — דשבורד ביצועים משותף (MetricsDashboard) — רכיב אחד משמש הן לצוות (/warehouse/dashboard) והן לפורטל (/portal/warehouse/dashboard); 6 KPI cards, AreaChart יומי, PieChart משפך, טבלת מלקטים — כולם Recharts כמו מודול האנליטיקס; DateRangeFilter עם ברירת מחדל this_month; פריט ניווט דשבורד נוסף לסרגל הצוות ולפורטל
- חיזוק withMobileIdempotency: 5xx לא נשמר — רשומה נמחקת ו-response מוחזר כמות שהוא לניסיון חוזר; json() עטוף ב-try/catch — כשל גורם למחיקת הרשומה ו-502 במקום נעילת המפתח; רשומה in-flight ישנה מ-2 דקות נמחקת (key reclaimable) ומוחזר 409; 3 טסטים נוספים (11 סה"כ)
- Mobile Phase B (offline queue engine): stores/offline-queue-store.ts — Zustand+persist (shipnest_offline_queue, AsyncStorage); QueuedAction עם id=UUID/Idempotency-Key, type, visitId, payload, photoUris, attempts, status; derived: pendingCount/syncingId/failedCount; guard כפיל type+visitId; onRehydrateStorage מאפס syncing→pending לאחר app kill. lib/offline/sync-processor.ts — flushQueue() סדרתי FIFO, isFlushing guard; 4xx permanent → markFailed+המשך, רשת/5xx/409 → incrementAttempt+עצור. lib/offline/net-status.ts — initOfflineSync() מנוי NetInfo+AppState, flush בחיבור ו-foreground. endpoints (delivery/cod/pod): +idempotencyKey פרמטר, httpStatus בשגיאות. התקנת @react-native-community/netinfo ו-expo-crypto
- Mobile Phase C (חיווט מסכים דרך התור): fail.tsx ו-cod.tsx הפכו סינכרוניים — אין spinner/loading, enqueue() מיידי + toast + navigate. visit/[id].tsx: useVisitSyncStatus + useOfflineQueueStore; handleMarkDelivered סינכרוני עם enqueue; COD בתור → deliveryState מוכפה READY_TO_DELIVER; כפתורי פעולה נחסמים כשיש pending; HeroSection מציג תווית ממתין/נכשל; syncBanner מעל כפתורים; CODCard מציג 'גביה הושלמה' כש-pendingSync=true. VisitCard: תג syncBadge (pending/failed). מסך היום: useVisitSyncStatus בכל VisitRow → syncBadge מוצג בכרטיסיה
- Mobile Phase D (חיווט POD דרך התור): pod.tsx הפך סינכרוני — הסרת uploadPod/useQueryClient/ActivityIndicator/isUploading/uploadProgress; handleFinish מבצע enqueue({type:'pod',...,photoUris}) + toast + back; upload overlay ו-retry dialog הוסרו; קבצי תמונה לא נמחקים ב-enqueue (sync-processor מוחק בהצלחה, handleClose מוחק ביציאה). חסימת כפתורים עודנה: deliver/fail/cod pending → חוסם את כל 4 הכפתורים; pod pending → חוסם רק כפתור POD (מסירה/כישלון/גוביינא נשארים זמינים); syncLabel מציג 'POD ממתין לסנכרון' בנפרד
- תיקון רגרסיית RTL: _layout.tsx — self-heal אחרי native rebuild; _didForceRTL מאפשר reload אוטומטי פעם אחת (DevSettings.reload); guard ב-AsyncStorage (rtl_reload_done) מונע לולאת reload אינסופית ב-Expo Go; console.log אבחוני [RTL] isRTL+appOwnership בהפעלה
- Phase 3.4 — ליטוש מסכי פורטל המחסן: מצב שגיאה עם כפתור 'נסה שנית' ב-9 מסכים (FulfillmentQueueView, BatchListView, PackingView, ExceptionListView, MetricsDashboard, מחסנים, מיקומים, מוצרים, מלאי); זרימת onboarding 3-שלבית בדף מחסנים ריק; aria-label לשדות חיפוש
- Phase 3.5 — אימות: 22 טסטים חדשים ל-warehouse-metrics (owner-scope isolation, time series, funnel, KPI calculations, BigInt conversion); 20 קבצי טסט עוברים (369 סה"כ); tsc + lint נקיים
- Phase 4C.1 — DB: טבלת WhSyncLog (wh_sync_log) לאידמפוטנטיות offline-sync; GET /api/warehouse/batches/[id]/snapshot — קריאה אחת עם snapshot מלא (batch+tasks+fulfillments+locations+products+snapshotAt), owner-scoped ומגבילה למלקט המשויך
- Phase 4C.2 — POST /api/warehouse/sync: מקבל מערך mutations (BATCH_START/TASK_PICK/TASK_SKIP/TASK_EXCEPTION/BATCH_COMPLETE) מסודר לפי sequence; כל mutation בטרנזקציה עצמאית — בדיקת WhSyncLog לאידמפוטנטיות, בדיקת owner+pickerId לגילוי קונפליקט, החלת מעברי סטטוס דרך status-progression.ts הקיים; מחזיר תוצאה פר-mutation (applied/duplicate/conflict); קונפליקט בודד אינו עוצר את שאר ה-mutations; 19 טסטי vitest חדשים (getCallerPickerId, checkBatchEligibility, אידמפוטנטיות, קונפליקט, owner-scope)
- Phase 4C.3 — מובייל: שכבת persistence מקומית (תשתית בלבד, ללא שינוי זרימת הליקוט הקיימת): lib/warehouse/types.ts — טיפוסי OfflineSnapshot (batch/pickTasks/products/locations/fulfillments) ו-OutboxMutation; lib/warehouse/db.ts — AsyncStorage DAO (saveSnapshot/loadSnapshot/deleteSnapshot/listCachedBatchIds); stores/warehouse-store.ts — Zustand+AsyncStorage slice לפי דפוס offline-queue-store: מטא-דאטה לאצוות offline, outbox mutations עם mutationId (UUID), sequence מונוטוני, markMutationSynced/Conflict, pendingCount
v01.23.000חדשwarehouseminorמודול מחסן — Phase 2.4–2.5: ליקוט, חריגות ואריזה
Phase 2.4: PATCH /api/warehouse/pick-tasks/[id] — שלוש פעולות דרך discriminatedUnion: confirmPick (ולידציית סריקת מיקום + ברקוד), skip, reportException.
פרטים נוספים ↓הסתר ↑
Phase 2.4: PATCH /api/warehouse/pick-tasks/[id] — שלוש פעולות דרך discriminatedUnion: confirmPick (ולידציית סריקת מיקום + ברקוד), skip, reportException. התקדמות סטטוס אוטומטית בטרנזקציה: משימה ראשונה → WhBatch=IN_PROGRESS + WhFulfillment=PICKING; כל tasks הושלמו → WhFulfillment=PICKED; כל fulfillments → WhBatch=COMPLETED. GET/POST /exceptions + PATCH /exceptions/[id] — פילטר status/type, ולידציית ownership. Phase 2.5: GET /packing — WhFulfillments בסטטוס PICKED/PACKING עם נתוני משלוח לאיפוס מדבקה. PATCH /packing/[id] — PICKED→PACKING→PACKED; בהגעה ל-PACKED משדר אירוע wh.fulfillment.packed דרך warehouseEvents (TenantEventBus); handler מינימלי ב-lib/warehouse/fulfillment-packed-handler.ts; Shipment.status לא מעודכן ישירות. Audit log על כל פעולת ליקוט ואריזה.
- ליקוט עם ולידציית סריקה — אי-התאמה מיקום/ברקוד מחזירה שגיאה 422 ברורה
- Auto-progression אטומי בטרנזקציה: task→fulfillment→batch
- API חריגות: GET/POST/PATCH עם ownership isolation
- אריזה + אירוע wh.fulfillment.packed — ללא פגיעה בלוגיקת Shipment
v01.23.000חדשwarehouseminorמודול מחסן — Phase 2.3: מנוע אצוות + S-shape routing
Phase 2.3: API אצוות תחת /api/warehouse/batches — GET רשימה עם מונה tasks; POST יצירת אצווה סינכרונית מ-fulfillmentIds[]: לכל OrderItem מתבצע חיפוש WhProduct לפי name (case-insensitive, owner-scoped), אם לא נמצא מוצר/מלא…
פרטים נוספים ↓הסתר ↑
Phase 2.3: API אצוות תחת /api/warehouse/batches — GET רשימה עם מונה tasks; POST יצירת אצווה סינכרונית מ-fulfillmentIds[]: לכל OrderItem מתבצע חיפוש WhProduct לפי name (case-insensitive, owner-scoped), אם לא נמצא מוצר/מלאי נוצר WhException(MISSING_PRODUCT). WhPickTask נוצרות עם locationId מה-WhInventory עם הכמות הגבוהה ביותר, ומסודרות לפי S-shape (serpentine). lib/warehouse/s-shape.ts — פונקציה טהורה: מסדרונות ממוינים aisle-ascending, מסדרון אי-זוגי rack/shelf עולים, זוגי יורדים; natural sort עם Intl.Collator. GET/PATCH /batches/[id] — אצווה + tasks לפי sequence + pickerId/pickerType. WhFulfillments מעודכנות ל-BATCHED עם batchId. הכול בעסקת $transaction אחת. 8 טסטי יחידה למנוע ה-S-shape.
- מנוע בניית אצווה סינכרוני — resolves products/inventory/locations בבלק אחד
- S-shape (serpentine) routing — פונקציה טהורה עם 8 טסטים
- MISSING_PRODUCT exception אוטומטי לפריטים ללא קטלוג/מלאי
- הכל בעסקת DB אחת — אטומי
v01.23.000חדשwarehouseminorמודול מחסן — Phase 2.1–2.2: מנוע פולפילמנטים
Phase 2.1: ארבעה מודלי Prisma חדשים — WhFulfillment (1:1 למשלוח, shipmentId @unique ללא relation מאולץ), WhBatch (אצוות ליקוט עם code ייחודי), WhPickTask (משימות ליקוט עם sequence ו-pickedQuantity), WhException (חריגות ע…
פרטים נוספים ↓הסתר ↑
Phase 2.1: ארבעה מודלי Prisma חדשים — WhFulfillment (1:1 למשלוח, shipmentId @unique ללא relation מאולץ), WhBatch (אצוות ליקוט עם code ייחודי), WhPickTask (משימות ליקוט עם sequence ו-pickedQuantity), WhException (חריגות עם type/status). שישה enums: WhFulfillmentStatus (NEW/BATCHED/PICKING/PICKED/PACKING/PACKED/EXCEPTION), WhBatchStatus, WhTaskStatus, WhPickType, WhExceptionType, WhExceptionStatus. טבלאות DB נוצרו דרך SQL ישיר (Prisma Postgres). Phase 2.2: API תחת /api/warehouse/fulfillments — GET eligible (משלוחים ללא WhFulfillment עדיין, scoped לפי בעלים), GET/POST queue, GET/PATCH [id] עם OrderItems מהמשלוח ו-validated status transitions. כל route עטוף ב-withWarehouseApi, owner-scoped דרך getOwnerScope, ולידציית Zod.
- ארבעה מודלים + שישה enums (Phase 2 DB layer)
- API תור ליקוט: eligible shipments, יצירת fulfillment, פרטים עם OrderItems
- Transition guard על סטטוסי WhFulfillment
- Owner isolation מלא — assertOwner על כל קריאת [id]
v01.22.000חדשcreditsminorמערכת זיכויים וניכויים
תיקון קריטי: טריגרים אוטומטיים שנחסמו בשבת כעת מתוזמנים לאחר ההבדלה (ScheduledMessage) במקום להירשם ככישלון סופי — ה-cron שולח אותם אחרי השבת.
פרטים נוספים ↓הסתר ↑
תיקון קריטי: טריגרים אוטומטיים שנחסמו בשבת כעת מתוזמנים לאחר ההבדלה (ScheduledMessage) במקום להירשם ככישלון סופי — ה-cron שולח אותם אחרי השבת. הוסף blockedReason ל-MessageSendResult. הודעות שגיאה ב-WhatsApp מוצגות כעת בעברית ב-inbox — תרגום ב-frontend גם עבור רשומות ישנות בבסיס הנתונים. מערכת מלאה לניהול זיכויים ללקוחות וניכויים משליחים. תהליך טיוטה → אישור עם מספור אוטומטי ZK-YYYY-NNN. כל זיכוי כולל: סיבה (שבר/אובדן/איחור/קנס/אחר), סכומי צד לקוח (משלוח + תכולה + פיצוי), סכומי צד שליח (ספיגת זיכוי + קנס), קישור למשלוחים ספציפיים (עיקרי/תחליפי), ותמיכה בהעלאת מסמכי הוכחת נזק. ממשק: עמוד ייעודי עם UnifiedTable, בורר תאריכים, חיפוש וטאבים לפי סטטוס. Sheet פרטים נפתח בלחיצה על שורה — כולל צפייה בסכומים, מסמכים מצורפים עם הורדה ומחיקה, אישור עם בורר חודש, ועריכה מלאה של טיוטות. אפליקציה מובייל (Sprint 9c): שער משמרת חוסם פעולות שטח (מסירה, כישלון, COD, POD) ללא משמרת פעילה; hook use-shift-gate.ts מציג Alert + מאפשר פתיחת משמרת on-the-fly. ShiftBanner עוצב מחדש: בנר ענבר להתרעה כשהמשמרת כבויה, בנר ירוק עם שעת התחלה כשפעילה. תוקן באג: שליח הנוכחי מסונן מרשימת היעד בהעברת ביקור (אסור לבחור בעצמו). Sprint 5e (מפה ופרטי ביקור): (א) תוקן מונה ק"מ שהציג 0 תמיד — endpoint /today מחשב כעת מרחק הוורסין בין עצירות רצופות במסלול הפעיל; (ב) Polyline של מסלול פעיל מצוייר ב-view mode של המפה עם markers ממוספרים לפי רצף; (ג) MiniMap בכרטיס כתובת הוחלף ב-MapView אמיתי (150px, non-interactive) — הקשה פותחת ניווט ב-Waze/Google Maps. ביקורת: תוקן פגם ב-pod.tsx — אחרי העלאת POD נוסף invalidateQueries לפי visitQueryKey לצד setQueryData הקיים, כדי שנתוני הביקור (כולל notes שהשרת עדכן) יישמרו עדכניים.
- CreditNote + CreditNoteShipment + CreditNoteAttachment — מודלים חדשים ב-Prisma
- API routes: CRUD, אישור עם appliedMonth, ביטול, קישורי משלוחים, העלאת קבצים ל-Supabase Storage
- CreditNoteDialog — טופס יצירה ועריכה עם SearchCombobox לחיפוש לקוח/שליח/משלוח
- CreditNoteSheet — side sheet עם פרטים מלאים, מסמכים, אישור עם month picker ועריכה
- עמוד /credits — PageToolbar, UnifiedTable, DateRangeSplitButton, ServerPagination, Excel export
- עמוד /messages — עיצוב מחדש: הוסר אקורדיון+טאבים; טבלה שטוחה כמו /shipments עם עמודת פעולות מוצמדת, תצוגת עמודות, ייצוא Excel; הודעות נכשלות מציגות Info-Popover (ריחוף/לחיצה) עם סיבת הכשל וכפתור שליחה מחדש
- SettlementApprovalDrawer — תיקון עיצוב: border-border/40, rounded-md, space-y-4, p-4/px-4 py-3 במקום p-6/space-y-8; הוסרו title= (הוחלפו ב-Tooltip); עודכן CLAUDE.md עם קונבנציית drawer
- תשתית OSRM (infra/osrm/): docker-compose, Caddyfile (reverse proxy + TLS + X-OSRM-Key auth), prepare.sh ו-refresh.sh לעיבוד מפת ישראל+פלסטין, .env.example ו-README עברי
- תיקון תשתית OSRM: registry שונה מ-osrm/osrm-backend (Docker Hub נטוש) ל-ghcr.io/project-osrm/osrm-backend; הוסף שלב אימות גרסה ל-README; הערת אזהרה ב-.env.example שהתג תלוי-גרסה וחייב להתאים בין עיבוד להרצה
- תשתית OSRM — TLS: עבר מ-ACME אוטומטי לתעודת Origin של Cloudflare (tls explicit ב-Caddyfile, volume certs/:ro ב-compose, certs/.gitignore); הוסר פורט 80 מ-compose ומ-ufw; נוסף שלב הנפקת Origin Certificate ל-README עם הסבר על Full (strict)
v01.21.003חדשwarehouseמודול מחסן — Phase 0: תשתית דגלי פיצ'ר
תיקון עקביות RTL: ~25 קבצים עברו המרה של class-ים פיזיים ל-logical equivalents; נמחק DateRangeSelector.tsx (dead code); DriverDetails/EmployeeDetails/LeadDetails/AgentDetails: SheetHeader text-right → text-end; bulk-hist…
פרטים נוספים ↓הסתר ↑
תיקון עקביות RTL: ~25 קבצים עברו המרה של class-ים פיזיים ל-logical equivalents; נמחק DateRangeSelector.tsx (dead code); DriverDetails/EmployeeDetails/LeadDetails/AgentDetails: SheetHeader text-right → text-end; bulk-history TableHead: text-right × 4 → text-end; CustomerDetailClient: text-right × 2 + mr-1 → text-end/ms-1; summit dialogs (pl-10/ml-1/ml-2 → ps-10/ms-1/ms-2); admin pages (ml-auto/mr-auto/ml-1 → ms/me); text-left/text-right → text-start/text-end בכל הרכיבים. בתוך אלמנטי RTL בלבד; איסים dir="ltr" (inputs מספריים, כתובות, מזהים חיצוניים) נותרו ללא שינוי. תוקן: SortingErrorsTable — בורר טווח תאריכים גולמי (Calendar+Popover מקומי) הוחלף ב-DateRangeFilter המשותף מ-components/shared; נוספו presets (היום/שבוע/חודש/הכל) והלוגיקה עברה ל-handleDatePresetChange/handleCustomDateRangeChange. הנחת תשתית אופציונליות דו-שכבתית למודול מחסן (warehouse). עמודת settings נוספה לטבלת customers (JSONB, default '{}') לאחסון דגלי מודולים. שלוש פונקציות גישה ב-lib/warehouse/feature-access.ts (isWarehouseEnabledForTenant / isWarehouseEnabledForCustomer / canStaffAccessWarehouse) — מקור אמת יחיד לכל gating. Wrapper API withWarehouseApi מחזיר 404 (לא 403) כשהמודול כבוי. קודי הרשאה גרנולריים נוספו: WAREHOUSE_INVENTORY_VIEW, WAREHOUSE_INVENTORY_MANAGE, WAREHOUSE_PICK, WAREHOUSE_BATCH_MANAGE — משויכים ל-OWNER, ADMIN, WAREHOUSE_MANAGER, WAREHOUSE_WORKER. מתג הפעלה ברמת טננט נוסף ל-tenant-form.tsx (קורא ושומר settings.modules.warehouse עם deep merge). מתג הפעלה ברמת לקוח נוסף ל-CustomerDetailClient.tsx — מוצג רק כשהטננט הפעיל את המודול, שומר דרך PATCH עם deep merge. Route groups ריקים עם שומרים: (site)/warehouse/layout.tsx (canStaffAccessWarehouse → redirect '/') ו-portal/(app)/warehouse/layout.tsx (isWarehouseEnabledForCustomer → redirect '/portal'); שני placeholder pages עם 'בקרוב'. Gating בניווט: קבוצת 'מחסן' ב-app-sidebar.tsx מתרנדרת רק כשwarehouseEnabled מועבר מה-server layout; פריט 'מחסן' ב-PortalSidebar.tsx עם requiresFlag warehouseEnabled; portal layout מחשב warehouseEnabled במקביל ל-accountingAvailableCount. Phase 1 tasks 1.1–1.2: ארבעה מודלי Prisma חדשים (WhWarehouse, WhLocation, WhProduct, WhInventory) — כל אחד נושא tenantId + ownerCustomerId? (NULL=טננט, מאוכלס=לקוח), timestamps, @@unique ו-@@index owner-scoped; ארבע טבלאות DB נוצרו (wh_warehouses, wh_locations, wh_products, wh_inventory) דרך SQL ישיר; lib/warehouse/owner-scope.ts עם getOwnerScope(session) + assertOwner(session, record) — מקור אמת יחיד לפילטרי בעלות בכל ה-routes. Phase 1 tasks 1.3 + 1.5 (חלקי): API routes מלאים תחת /api/warehouse/warehouses ו-/api/warehouse/locations — CRUD מלא עטוף ב-withWarehouseApi, כל שאילתה דרך getOwnerScope, ולידציית Zod, פורמט { success, data, meta }; בניית קוד-מיקום אוטומטית מהרכיבים (zone-aisle-rack-shelf-bin); רכיבי טופס משותפים WarehouseForm + LocationForm (יעשה בהם שימוש חוזר בפורטל); עמוד /warehouse עם UnifiedTable + יצירה/עריכה/מחיקה; עמוד /warehouse/[warehouseId] — רשימת מיקומים עם ניווט חזרה + תצוגת קוד בזמן-אמת. Phase 1 tasks 1.4 + 1.5 (קטלוג + מלאי): API routes — products CRUD עם חיפוש חופשי (sku/barcode/name); inventory GET עם סינון locationId/productId; inventory/adjust POST עם upsert לפי @@unique([locationId,productId]) + רישום audit log; רכיבי טופס משותפים ProductForm + InventoryAdjustDialog; עמוד /warehouse/products — קטלוג עם UnifiedTable + יצירה/עריכה/מחיקה; עמוד /warehouse/inventory — תצוגת מוצר↔מיקום↔כמות עם סינון מחסן/מיקום/מוצר + badge ירוק/ענבר/אדום לפי כמות; ניווט צד עדכן: מחסנים, קטלוג מוצרים, מלאי. Phase 1 task 1.6 — UI פורטל: 4 דפי פורטל מחסן (warehouses, locations, products, inventory) שמשתמשים חוזרים ברכיבי הטופס המשותפים (WarehouseForm, LocationForm, ProductForm, InventoryAdjustDialog) ומפנים ל-/portal/warehouse/...; PortalSidebar עודכן עם קישורי מחסנים/קטלוג/מלאי; owner-scope נאכף לחלוטין ב-API — לקוח רואה רק את הנתונים שלו.
v01.21.002חדשSprint 9b Phase 1-4 — Push Notifications: רישום token
Sprint 9b מלא — Push Notifications.
פרטים נוספים ↓הסתר ↑
Sprint 9b מלא — Push Notifications. Phases 1-4: expo-notifications מותקן, app.json עודכן (POST_NOTIFICATIONS + plugin), lib/notifications/push.ts עם setNotificationHandler + registerForPushNotifications (graceful אם EAS לא מקושר), POST /api/v1/mobile/notifications/register-token שומר token ב-user_devices. Phases 5-8: lib/notifications/expo-push.ts עם sendPushNotifications + notifyDriver helper, trigger ב-assign route (שליח חדש: 'משלוח חדש', שליח ישן בהעברה: 'משלוח הועבר ממך'), tap handler ב-_layout.tsx לניווט deep link ל-/visit/[id]. NOTES.md עודכן עם הוראות EAS projectId.
v01.21.001חדשSprint 9a — GPS מעקב מיקום ברקע (Mobile)
הטמעת מעקב GPS לאפליקציה המובייל: TaskManager background task עם battery-aware intervals (2 דק' רגיל, 5 דק' כשסוללה מתחת ל-20%), תור offline ב-AsyncStorage עם flush אוטומטי בחזרת קישוריות.
פרטים נוספים ↓הסתר ↑
הטמעת מעקב GPS לאפליקציה המובייל: TaskManager background task עם battery-aware intervals (2 דק' רגיל, 5 דק' כשסוללה מתחת ל-20%), תור offline ב-AsyncStorage עם flush אוטומטי בחזרת קישוריות. Backend: שני endpoints חדשים — POST /api/v1/mobile/location/ping ו-/batch (עם skipDuplicates). JWT: driverId נוסף ל-access token לחיסכון ב-DB lookup. Shift Store (Zustand) עם persist ל-AsyncStorage ושחזור tracking אוטומטי בהפעלה מחדש של האפליקציה. Phase 5: ShiftBanner ב-Today screen — כפתור ירוק לתחילת משמרת, בנר ירוק עם אפשרות סיום כשבמשמרת, dot ירוק על Avatar ב-HomeHeader. Backward compat: fallback DB lookup לtokens ישנים ב-ping/batch endpoints.
v01.21.000שיפורui/tablesminorאחוד טבלאות — מעבר מלא ל-UnifiedTable
כל 13 הטבלאות הגולמיות במערכת הוחלפו ב-UnifiedTable (TanStack React Table wrapper).
פרטים נוספים ↓הסתר ↑
כל 13 הטבלאות הגולמיות במערכת הוחלפו ב-UnifiedTable (TanStack React Table wrapper). כל הטבלאות נהנות כעת מ-skeleton loading אחיד, empty state עם אייקון ותיאור, server-side pagination מובנית, בחירת שורות עם bulk actions, ו-sticky actions column. הדפים שעברו מעבר: ShipmentMessagesCard, ShipmentDetails (פריטים), admin/api-keys, admin/webhooks (כולל deliveries dialog), settings/audit-log, drivers/[id] (zone rates), attendance/payroll, attendance/reports (4 טאבים), admin/sync-errors.
- admin/sync-errors — selection עם renderBulkActions (retry/dismiss/delete/restore) וו-confirmation dialogs מבוססי pendingTargetsRef
- attendance/payroll ו-reports — server pagination מובנית, skeleton loading, 4 טבלות בטאבים
- admin/webhooks — deliveries dialog עם UnifiedTable פנימי וו-6 עמודות
- כל טבלה: emptyState עם אייקון ו-accentColor מותאמים לתוכן
v01.20.001תיקוןsettings/updatesדף עדכונים — מחיצת תאריך לפני רשומות minor/major
תוקן: רשומות minor ו-major לא הציגו כותרת תאריך, מה שגרם להן להיראות כשייכות לתאריך מאוחר יותר.
פרטים נוספים ↓הסתר ↑
תוקן: רשומות minor ו-major לא הציגו כותרת תאריך, מה שגרם להן להיראות כשייכות לתאריך מאוחר יותר. כעת מוצגת מחיצת תאריך לפני כל רשומה שהתאריך שלה שונה מהקודמת. רשומות patch שמופיעות אחרי minor/major באותו תאריך לא מציגות כותרת כפולה.
v01.20.000חדשmobile/adminminorSprint 8b — Admin Mode Mobile UI
ממשק מנהל מלא באפליקציה המובייל: ד-שבורד ניהולי עם KPI, רשימת שליחים וקלפי ביצוע.
פרטים נוספים ↓הסתר ↑
ממשק מנהל מלא באפליקציה המובייל: ד-שבורד ניהולי עם KPI, רשימת שליחים וקלפי ביצוע. מסך ביקורים לא משויכים עם שיוך מהיר לשליח. מסך פרטי שליח עם כל ביקוריו ואפשרות העברה. מסך בחירת שליח (שיוך/העברה) עם עיצוב dark-header אחיד. תוקנו חוסרי עקביות עיצוביים: `<a href>` פנימי הוחלף ב-Link, `<select>` גולמי (מפה + ToolbarTabs) הוחלף ב-shadcn Select.
- AdminDashboard — KPI grid (פתוחים/בשטח/נמסרו/ממתינים), banner ביקורים לא משויכים, DriverCards עם progress bar ופעולות מהירות
- UnassignedScreen — רשימת ביקורים ממתינים לשיוך, כפתור 'שייך' לכל ביקור
- DriverDetailScreen — כל ביקורי השליח ליום, כפתור 'העבר' לביקורים פתוחים
- AssignScreen — בחירת שליח עם radio selection + confirm (שיוך/העברה)
- HomeHeader — טוגל מנהל/שליח מוצג רק למשתמשים עם role ניהולי
- Admin API hooks — useAdminDrivers, useUnassignedVisits, useDriverVisits, useAssignVisit
v01.19.006שיפורshipments/timeline, ui/consistencyציר זמן משלוח — ליקוט ויציאה להפצה נפרדים
הוספת עמודה outInventoryAt לטבלת shipment — נכתבת בפעם הראשונה שמגיע webhook עם סטטוס OUT_INVENTORY (sticky, לא מתאפס).
פרטים נוספים ↓הסתר ↑
הוספת עמודה outInventoryAt לטבלת shipment — נכתבת בפעם הראשונה שמגיע webhook עם סטטוס OUT_INVENTORY (sticky, לא מתאפס). הוספת עמודה inTransferAt לטבלת shipment — נכתבת בפעם הראשונה שמגיע webhook עם סטטוס IN_TRANSFER (sticky, לא מתאפס). ציר הזמן בדף המשלוח מציג כעת 7 אירועים נפרדים: הקמה → איסוף → קליטה במחסן → ליקוט → בהעברה → יציאה להפצה → כשל/מסירה. ציר הזמן תומך במחזורי הפצה מרובים (כשל + נסיון מחדש) — מבוסס על כלל visits מסוג DELIVERY לפי סדר כרונולוגי; כשל ופרטי הסיבה מוצגים בשורה אחת. תוקן: Lionwheel לא שולח failed_at בנתוני ה-visit — הוסף לוגיקה ב-data-service שכותבת failedAt=now() על DELIVERY visit שלא הושלם כשמגיע webhook עם סטטוס FAILED/FINAL_FAILED; הוסף fallback בציר הזמן להציג כשל גם ל-visits ישנים ללא failedAt. תוקן: שמות ה-labels מוצגים בצורה עסקית ברורה יותר. תוקנו כל ה-tooltip הפרימיטיביים (title=) במערכת — הוחלפו ב-Tooltip/TooltipTrigger/TooltipContent מ-shadcn. אוחד שימוש בלוחות שנה: כל 6 מקומות שהשתמשו ב-Calendar + Popover מקומי הוחלפו ב-DatePicker המשותף מ-components/shared. תוקן: בר הפגינציה בדף הזדכויות (settlements) הוצג ללא border/card — נוסף wrapper זהה לשאר הדפים.
v01.19.005חדשmobile/adminSprint 8a — Admin Mode Backend
הרחבת seed לשלושה משתמשים: דני כהן (EMPLOYEE_DRIVER), יוסי לוי (EMPLOYEE_DRIVER, driverId=473), רון מנהל (DISTRIBUTION_MANAGER, ללא driver record); 3 משלוחים ליוסי + 3 ללא שיוך (driverId=null).
פרטים נוספים ↓הסתר ↑
הרחבת seed לשלושה משתמשים: דני כהן (EMPLOYEE_DRIVER), יוסי לוי (EMPLOYEE_DRIVER, driverId=473), רון מנהל (DISTRIBUTION_MANAGER, ללא driver record); 3 משלוחים ליוסי + 3 ללא שיוך (driverId=null). נוספו 4 endpoints admin למובייל: GET /api/v1/mobile/admin/drivers (כל שליחי ה-tenant + סטטיסטיקות יומיות + isOnShift מ-DriverLocation); GET /api/v1/mobile/admin/visits/unassigned (visits היום ללא שיוך + shipment info); GET /api/v1/mobile/admin/drivers/:driverId/visits (visits יומיות של שליח ספציפי + stats); POST /api/v1/mobile/admin/visits/:id/assign (שיוך/העברת visit לשליח — מאפס inActiveRoute+routeSequence, logAudit עם פרטי ממי למי). Role check על כל endpoint: DISTRIBUTION_MANAGER/ADMIN/OWNER/WAREHOUSE_MANAGER/EMPLOYEE_MANAGER; שליח רגיל מקבל 403. דף משלוח: ציר זמן (timeline) במקום גריד שטוח — כל אירוע (הקמה, קליטה, איסוף, ליקוט, כשל, מסירה) מוצג עם נקודה וקו מחבר; כשל ופרטי הסיבה מוצגים בשורה אחת; כשל מוצג היסטורית גם אחרי שינוי סטטוס (מבוסס על Visit.failedAt); אייקון העתקה לכל שורה.
v01.19.004שיפורmobile/settlement, web/settlementהזדכות COD — פר משלוח במקום בחירה מרובה
שינוי מדיניות הזדכות: במקום לסמן כמה גוביינאות ולהזדכות על כולן יחד, כל הזדכות היא על משלוח בודד.
פרטים נוספים ↓הסתר ↑
שינוי מדיניות הזדכות: במקום לסמן כמה גוביינאות ולהזדכות על כולן יחד, כל הזדכות היא על משלוח בודד. מסך הרשימה הופך לרשימה ניתנת-ללחיצה; לחיצה על שורה מעבירה ישירות למסך ההעלאה עם פרטי אותו משלוח. כפתור 'שלח' שינה לטקסט 'שלח לאישור מנהל' להבהרה שההזדכות עוברת אישור לפני סגירה. תוקן גם import של expo-file-system/legacy (EncodingType). Sprint 7 Phase 1 — נוספו 4 endpoints web-admin: GET /api/settlements (רשימה+ספירות לפי סטטוס), GET /api/settlements/:id (פרטים+CodTrackings), POST /api/settlements/:id/approve (אישור→IN_SAFE), POST /api/settlements/:id/discrepancy (פער→DISCREPANCY); נוספו permissions SETTLEMENT_VIEW/SETTLEMENT_APPROVE. תוקן: שורת הסיכום (KPI) בכרטסת חשבונאית מציגה עכשיו יתרה תואמת לאחר ביטול כפל-חיוב — computeBalanceTotals קיבלה גם היא את מנגנון suppression של דרישות תשלום מכוסות (הוריסטיקת סכום+תאריך). Sprint 7 Phases 2-5 — ממשק web-admin לאישור הזדכויות: דף /settlements עם טאבים לפי סטטוס (AWAITING_APPROVAL/APPROVED/DISCREPANCY), רשימת הזדכויות עם שם שליח/סכום/תאריך, sheet צד עם גלריית תמונות+lightbox (Dialog), רשימת גוביינות, טופס אישור עם שדות 'מזומן בפועל'/'צ׳קים בפועל' ממולאים מראש בסכומים המוצהרים, חישוב פער חי (כלומר 'פער: -₪50' באדום), כפתור משתנה בין 'אישור תואם'↔'דיווח פער'+חובת הערה. הוספת הרשאות SETTLEMENT_VIEW/SETTLEMENT_APPROVE לתפקידים OWNER/ADMIN/ACCOUNTING/CUSTOMER_SERVICE. הוספת 'הזדכויות' לתפריט הצד (קבוצת תפעול). שדרוג UX: כל שימושי title= פרימיטיביים הוחלפו בטולטיפים מעוצבים (shadcn Tooltip) ב-8 קבצים — ShipmentRow (עמודות truncated), ShipmentDetailClient, תבניות Meta, AdminTaskBellButton, admin/webhooks, admin/users, DistributionGapsTable, integration-dialog.
v01.19.003חדשmobile/settlementSprint 6b — Settlement Mobile UI Phase 1-3
Phases 1-6 של ממשק ההזדכות בנייד: (1) API layer — settlement.ts עם getPendingObligations/createSettlement/getSettlements/getSettlement + use-settlements.ts עם hooks; (2) Today banner ענבר מעל ה-tabs שמציג את סכום וכמות ח…
פרטים נוספים ↓הסתר ↑
Phases 1-6 של ממשק ההזדכות בנייד: (1) API layer — settlement.ts עם getPendingObligations/createSettlement/getSettlements/getSettlement + use-settlements.ts עם hooks; (2) Today banner ענבר מעל ה-tabs שמציג את סכום וכמות חובות WITH_DRIVER; (3) Stage 1 — מסך רשימת חובות עם checkboxes, summary card (מזומן/צ'קים), toggle all, sticky bottom עם count+total+כפתור 'המשך להזדכות'; (4) Stage 2 — מסך העלאת תמונות עם photo grid 5 slots (מצלמה/גלריה), thumbnails עם מחיקה, textarea הערות, כפתור 'שלח הזדכות'; (5) מסך סטטוס ההזדכות — 3 מצבים: AWAITING_APPROVAL/APPROVED/DISCREPANCY עם hero icon, כרטיס פרטים, רשימת חובות, כפתורי ניווט; (6) מסך היסטוריית הזדכויות + קישור ממסך הפרופיל.
v01.19.002חדשmobile/settlementsSprint 6a — Settlement Backend (הזדכות COD)
נוספה תשתית backend מלאה לתהליך הזדכות COD בין שליח לחברה.
פרטים נוספים ↓הסתר ↑
נוספה תשתית backend מלאה לתהליך הזדכות COD בין שליח לחברה. נוסף מודל CodSettlement (טבלת cod_settlements) עם שדות: settlementNumber, declaredCashAmount/CheckAmount/Total, proofImageUrls, status (AWAITING_APPROVAL→APPROVED/DISCREPANCY). נוסף שדה settlementId לטבלת cod_tracking. נוספו 4 endpoints ל-API v1 mobile: GET /settlements/pending (חובות WITH_DRIVER ללא settlementId), POST /settlements (יצירת הזדכות: אימות בעלות, העלאת תמונות ל-Supabase, יצירת רשומה, עדכון CodTrackings ל-AWAITING_SAFE), GET /settlements (היסטוריה), GET /settlements/:id (פרטים + רשימת COD).
v01.19.001תיקוןmobile/mapמפה ניידת — ציור פולגון + אופטימיזציית מסלול + מסלול פעיל
תוקן ממשק ציור הפולגון במפת השליח: הציור הקודם התבסס על לחיצות (onPress) שגרמו לעיכובים ולתגובה לא אחידה.
פרטים נוספים ↓הסתר ↑
תוקן ממשק ציור הפולגון במפת השליח: הציור הקודם התבסס על לחיצות (onPress) שגרמו לעיכובים ולתגובה לא אחידה. הוחלף ב-PanResponder שמנטר תנועת אצבע בזמן אמת (דגימה כל 8px) ללא עיכוב. הוסף אלגוריתם אופטימיזציית מסלול (Nearest-Neighbor + 2-opt, Haversine) ב-TypeScript עם חישוב ETA לכל עצירה, API endpoint ו-UI מלא: Polyline על המפה + שיט תחתי עם רשימת עצירות ממוינת + ETA + מרחק. הוסף endpoint להפעלת מסלול פעיל (POST /route/activate) עם transaction: reset + set, ושדות DB חדשים: inActiveRoute, routeBuildId, routeSequence, estimatedArrival. Today מציג 4 טאבים: במסלול / לא במסלול / נמסרו / נכשלו; ברירת מחדל חכמה — נפתח ב-'במסלול' אם יש מסלול פעיל; VisitCard בטאב 'במסלול' מציג bubble כחול עם מספר רצף + ETA מהמסלול; empty state "במסלול" עם כפתור מעבר למפה. תוקן עמודת יתרה בכרטסת לקוח (פורטל ואדמין): מסמכים מאותו תאריך קיבלו יתרת ביניים שרירותית כתוצאה מסדר מיון לא-יציב — חשבוניות זיכוי מאותו יום מוינו לפני חשבוניות חוב ויצרו ערכים מטעים (חובה שירדה ואח"כ עלתה). כעת כל מסמך מציג יתרה אישית אחרי התנועה שלו, עם סדר מוסכם: חשבוניות חוב מעובדות לפני חשבוניות זיכוי (שתיהן ממוינות לפי מספר מסמך עולה); טבלת התצוגה ממוינת בסדר הפוך (זיכויים למעלה, חובות למטה) כך שקריאה מלמטה למעלה מספרת נרטיב קוהרנטי: חוב עולה עם כל חשבונית, ואז יורד עם כל זיכוי. תוקן תצוגה מקדימה של מסמכים בכרטסת לקוח: הוחלף iframe ב-react-pdf (PDF.js) שמרנדר כל עמוד כ-canvas — עובד על כל הדפדפנים כולל iOS ו-Android שבהם iframe מיירט PDF ופותח בטאב חדש. שודרגה טבלת הכרטסת החשבונאית ל-UnifiedTable: עמודת פעולות מוצמדת, כותרות עמודות עם dropdown לסיווג ומיון, נעיצת עמודה, הסתרת/הצגת עמודות, ייצוא Excel, עימוד תחתון מעוצב — עקבי עם כל שאר הטבלאות במערכת. אייקון צפייה עבר לתוך תפריט הפעולות; נוסף כפתור FileText ליד מספר המסמך לפתיחה מהירה של תצוגה מקדימה.
- תוקן: /today חזר מ-select→include כדי למנוע שגיאת Prisma 'Unknown field' בסביבת ייצור לאחר migration
- תוקן: query key ב-map.tsx עודכן מ-['today'] ל-['driver','today'] כדי להתאים ל-TODAY_QUERY_KEY
- תוקן: activate/route.ts — הוחלף $transaction בפעולות עצמאיות (Prisma Postgres לא תומך ב-batch $transaction) + נוסף try-catch מלא שמחזיר JSON במקום HTML 500
- תוקן: optimize/route.ts — סינון ביקורים שכבר נמסרו (deliveredAt) או נכשלו (failedAt) לפני חישוב המסלול
- הסרת קוד דיבאג זמני: console.error ב-normalizeDocument ו-endpoint /api/debug/summit-raw שנוצרו לחקירת שדות API של סאמיט
- תיקון כפל-חיוב בכרטסת: דרישת תשלום סגורה (IsClosed=true) שמקושרת לחשבונית מס באותו סכום (בטווח 180 יום) מוצגת כניטרלית בעמודת תנועה ויתרה — ה-Invoice הוא שנושא את החיוב. הלוגיקה ב-buildCoveredPaymentRequestIds (התאמת סכום + תאריך הוריסטית, ללא שדה קישור ב-API של סאמיט)
v01.19.000חדשfinance/creditsminorמערכת זיכויים וניכויים
הוספת מערכת מלאה לניהול זיכויים ללקוחות וניכויים משליחים.
פרטים נוספים ↓הסתר ↑
הוספת מערכת מלאה לניהול זיכויים ללקוחות וניכויים משליחים. ניתן ליצור זיכוי על שבר, אובדן, איחור, קנס או סיבה אחרת — מלא או חלקי, על עלות משלוח, ערך תכולה ופיצוי נוסף. הזיכוי מופיע בחשבון החודשי של הלקוח, והניכוי/קנס נגזר ממנו ומופיע בדוח התשלום החודשי של השליח. ניתן לקשר זיכויים למשלוחים ספציפיים (עיקרי/תחליפי) או להשאירם עצמאיים. תהליך טיוטה ← אישור מובנה. תוקן (שורש הבעיה — כרטסת פורטל): קוד הסנכרון עם API סאמיט סרק רק 10 דפים × 500 = 5,000 מסמכים. ב-DB של סאמיט יש ~14,500 מסמכים; חשבוניות מס/קבלה של לקוחות מסוימים מופיעות בדפים 10-11 ולכן לא נסרקו. ה-maxPages הוגדל מ-10 ל-60 (30,000 מסמכים). כמו כן תוקן מיזוג תוצאות byId+byName לכיסוי מסמכים ללא CustomerID. תוקן: מודל תצוגה מקדימה של מסמכים (PDF) בכרטסת הלקוח — הוצג צר מאוד עקב override של max-width ב-Tailwind; כעת מוצג ב-95% רוחב המסך. תוקן חישוב יתרת לקוח בכרטסת: (א) חשבונית זיכוי וחשבונית זיכוי/קבלה (types 5,6) — Summit מחזיר ערכים שליליים ולכן `return -doc.total` שיגר חיוב כפול; תוקן ל-`-Math.abs(total)`. (ב) קבלת זיכוי (type 7) גם כן תוקנה לתנועת זכות. (ג) הופרדה לוגיקת תצוגה (`balanceImpact`) מלוגיקת חישוב יתרה פתוחה (`openBalanceImpact`): דרישת תשלום שסגורה (IsClosed=true) אינה נחשבת חוב כי ה-Invoice/InvoiceReceipt שהוּפק אחריה מייצג את ההתחייבות; חשבונית מס/קבלה (type 1) מטופלת כנטרלית ביתרה הפתוחה (invoice+תשלום בוצעו במקביל — נטו 0).
- עמוד ייעודי /credits לניהול כל הזיכויים במערכת
- טאב 'זיכויים' בכרטיס לקוח עם סיכום חודשי
- ניכויי זיכויים מוצגים בדוח הרווחים החודשי של שליח
- קישור זיכויים למשלוחים ספציפיים (עיקרי / תחליפי)
- תהליך אישור — DRAFT ← APPROVED
- מספור אוטומטי ZK-YYYY-NNN
- עמוד /credits עוצב מחדש בסגנון אחיד — PageToolbar, UnifiedTable, ServerPagination, ייצוא Excel
- דף משלוח: הוספת שורת תאריך כשל (מ-failedAt של הביקור) מעל סיבת הכשל; אייקון העתקה לכל שורת תאריך/סיבה
v01.18.001שיפורmessages/triggersכרטיסי טריגר — badge ערוץ לכל נמען + אזהרת תבנית בעיה
כרטיס הטריגר ברשימה מציג כעת badge ערוץ לכל נמען: סגול 'Meta' כשיש תבנית מאושרת, ענבר 'Meta' אם התבנית נדחתה/לא נמצאה, אפור 'Green' לטקסט חופשי.
פרטים נוספים ↓הסתר ↑
כרטיס הטריגר ברשימה מציג כעת badge ערוץ לכל נמען: סגול 'Meta' כשיש תבנית מאושרת, ענבר 'Meta' אם התבנית נדחתה/לא נמצאה, אפור 'Green' לטקסט חופשי. אם אחד מהנמענים מוגדר עם תבנית Meta שאינה מאושרת, מופיעה על הכרטיס כולו באנר אזהרה ענבר עם פירוט הנמען הבעייתי. תוקן שדה channel ב-DB: בשמירת טריגר, הערוץ מחושב מה-templateIds בפועל (meta-cloud-api אם לפחות נמען אחד מקושר לתבנית Meta, green-api אחרת) — עד כה נשמר תמיד green-api כברירת מחדל. תוקן: webhook כשלון מ-Meta עם קוד 131026 (Message Undeliverable) יציג עכשיו את הסיבה 'המספר אינו רשום ב-WhatsApp או שגוי' במקום הטקסט הגולמי של Meta; כמו כן נשמר errorCode על השורת משלוח לצורך תצוגה מדויקת ב-UI. תוקן תצוגת הודעות נכנסות בתיבת הדואר: reaction מוצג כאמוג'י (במקום טקסט 'reaction' עם אייקוני מסמך/לינק), מדבקה (sticker) מוצגת כתמונה. תוקן טאב 'משלוחים פתוחים' בדף נהג: משלוחים עם סטטוס טרמינלי (COMPLETED, CANCELED, FINAL_FAILED) לא יופיעו עוד כפתוחים גם אם ה-Visit לא סומן isDone=true — הסטטוס מ-additionalData משמש כ-override. תוקן הבאדג' בסיידבר: הציג בעבר מספר שיחות 'לא נענו', כעת מציג שיחות 'לא נקראו' בפועל (לפי זמן הצפייה האחרון ב-localStorage).
v01.18.000שיפורmessages/triggersminorעיצוב מחדש של טופס הטריגר — ערוץ שליחה ברור וגלוי
ממשק הטריגרים עוצב מחדש כדי להפוך את ערוץ השליחה (Meta / Green-API) לגלוי ומפורש.
פרטים נוספים ↓הסתר ↑
ממשק הטריגרים עוצב מחדש כדי להפוך את ערוץ השליחה (Meta / Green-API) לגלוי ומפורש. עד כה הבחירה בין ערוצים הייתה שקטה ונגזרת מקיום קישור תבנית — מה שגרם לטריגרים לצאת דרך Green-API בלי ידיעת המשתמש. כעת כל לשונית נמען (נמען/שולח/מותאם) מציגה שתי כרטיסיות ערוץ בחלק העליון: 'תבנית Meta' (ירוק, עם אינדיקטור חיבור) ו'טקסט חופשי' (ענבר, עם אזהרה). כרטיסיית Green-API מציגה באנר ענבר שמסביר את המגבלה ומציע קישור לתבנית Meta. כרטיסיית Meta מציגה תצוגה מקדימה של התבנית המקושרת עם אפשרות החלפה/ניתוק. נוסף picker מוטמע לחיפוש ובחירת תבנית מאושרת מתוך הטופס עצמו, ללא צורך לצאת לדף התבניות. תוקן באג בRecipientChannelBadge: שני הענפים השתמשו באותו צבע (סגול) — כעת Meta=ירוק, Green-API=ענבר, גיבוי=ענבר כהה. תוקן ה-validation: כשתבנית Meta מקושרת לנמען, המערכת לא דורשת עוד inline body (הגוף נלקח מהתבנית). תוקן באג multi-tenancy בדף פרטי נהג: שליפת הנהג לא סיננה לפי tenantId — משתמש יכול היה לגשת לנהג של tenant אחר לפי ID.
- שתי כרטיסיות ערוץ גלויות בכל לשונית נמען — Meta (ירוק) ו-Green-API (ענבר)
- אינדיקטור חיבור בזמן אמת לכל ערוץ (מחובר / לא מחובר)
- picker מוטמע לחיפוש ובחירת תבנית Meta מאושרת ישירות מהטופס
- באנר אזהרה ענבר בטקסט חופשי עם קישור לשדרוג לתבנית Meta
- תיקון validation — תבנית Meta מקושרת פוטרת מחובת inline body
v01.17.005תיקוןdistribution-gapsתיקון: פילטרים ועימוד בפערי הפצה
עימוד שרת הוסר — כעת השרת מחזיר את כל הנתונים בבקשה אחת והעימוד וכל הפילטרים מתבצעים בצד הלקוח.
פרטים נוספים ↓הסתר ↑
עימוד שרת הוסר — כעת השרת מחזיר את כל הנתונים בבקשה אחת והעימוד וכל הפילטרים מתבצעים בצד הלקוח. הספירות שליד הפילטרים (משובץ/לא משובץ, שליח, סטטוס) מחושבות כעת לפי cross-filtering נכון: כל קטגוריה מציגה ספירות לפי הפילטרים האחרים הפעילים בלבד, ומתעדכנת בזמן אמת כשמשנים סינון. נוסף פילטר עיר לפערי הפצה — מציג ספירות לכל עיר מסונכרנות עם שאר הפילטרים הפעילים (בלשונית פערי איסוף מסנן לפי עיר המקור). נוסף פילטר אזור חלוקה עם תמיכה ב-ללא אזור. שונה שם הפילטר שיבוץ שליח ל-משובץ עם ערכים כן/לא. אפליקציה: cron חדש (00:00 שעון ישראל) מגלגל ביקורים פתוחים מיום קודם ליום הנוכחי תוך שמירה על שעת הביקור המקורית. שיפור ביצועים: שאילתת דף ההודעות מביאה כעת רק שדות orderItems/visits/shipmentSetting הנדרשים לתצוגה, במקום כל העמודות. תוקן באג multi-tenancy: MessagesContent לא סינן לפי tenantId בטעינה הראשונית. שאילתת סטטוס משלוח בדף שגיאות מיון עברה ל-$queryRaw שמחלץ רק שדה status מה-JSON במקום שליפת additionalData המלא. תוקן באג multi-tenancy בדף הגדרות הטריגרים (SettingsContent לא סינן לפי tenantId); שתי השאילתות עברו ל-Promise.all. ארבע שאילתות עצמאיות בדף פרטי נהג עברו ל-Promise.all (חיסכון של ~3 round-trips לDB). אפליקציה Sprint 5a: מפה אמיתית (react-native-maps 1.20.1) עם 8 markers ממוספרים וצבועים לפי סטטוס, callout עם פרטי לקוח, ניווט ל-visit detail בטאפ, floating header עם ספירת פעילים/נמסרו.
v01.17.004שיפורmessages/inboxחיפוש inbox בצד שרת על כלל השיחות
חיפוש הצ'אטים עבד עד כה רק על 20 השיחות הטעונות (client-side).
פרטים נוספים ↓הסתר ↑
חיפוש הצ'אטים עבד עד כה רק על 20 השיחות הטעונות (client-side). כעת, כל חיפוש שולח בקשה לשרת עם debounce של 300ms, מחזיר עד 50 תוצאות מכלל השיחות, ומציג skeleton loading בזמן החיפוש. תוקן: שאילתת החיפוש הראשונה סרקה את כל ה-messages ואז סיננה — הוחלפה בגישת matching_phones שמזהה קודם את הטלפונים הרלוונטיים (לפי שם לקוח / מספר טלפון) ורק אז שולפת פרטי שיחה עבורם. הוסר חיפוש לפי גוף הודעה אחרון שגרם לתוצאות שגויות. נוסף: חיפוש לפי ברקוד משלוח — הקלדת מספר ברקוד מוצאת את שיחת הלקוח הקשורה למשלוח.
v01.17.003תיקוןwebhooks/green-api, sorting-errorsתיקון: התראות מיון לא נסגרו למרות 👍
race condition בין כרון החזרת התראות לבין ה-webhook של ה-👍: הכרון שלח הודעה חדשה ועדכן את ה-messageId ב-DB לפני שהריאקציה הגיעה — כך שחיפוש לפי messageId נכשל ו-acknowledgedAt לא הוגדר.
פרטים נוספים ↓הסתר ↑
race condition בין כרון החזרת התראות לבין ה-webhook של ה-👍: הכרון שלח הודעה חדשה ועדכן את ה-messageId ב-DB לפני שהריאקציה הגיעה — כך שחיפוש לפי messageId נכשל ו-acknowledgedAt לא הוגדר. תוקן: fallback לחיפוש לפי chatId כאשר messageId לא נמצא, כך שלחיצת 👍 על כל הודעה בקבוצה (גם ישנה) תסגור את ה-alert. תוקן: בדיקת האמוג'י שינוי מ-=== ל-startsWith כדי לתמוך ב-👍 עם גוון עור (skin tone). תוסף: polling fallback בקרון החזרה — לפני כל שליחה חוזרת הקרון מושך את הסטוריית הצ'אט מ-Green API ובודק אם כבר הוגב ב-👍 על ההודעה האחרונה; אם כן, הוא מסמן את ה-alert כ-acknowledged ולא שולח עוד (מגן מפני כשלי webhook). תיקון תוכן הודעה ב-/messages: הודעות תבנית Meta נשמרות עם הטקסט המרונדר (displayBody) — במקום ה-placeholder הישן [תבנית Meta: ...]. פאנל הפרטים כעת מציג שם תבנית ותצוגה מקדימה של גוף התבנית עבור הודעות ללא טקסט שמור. אחידות צבעים בטפסי עריכת טריגרים: כל הצבעים (כתום, כחול, ירוק) הוחלפו בצבע הסגול של המערכת.
v01.17.002חדשshipments, target-shabbat, mobile, messagesשכפול משלוח + הוספת משלוחים ליעד שבת
אבחון כשלי הודעות: נוסף שדה error_code לטבלת shipment — המערכת שומרת קטגוריה מובנית לכל כשל שליחה (אין WhatsApp, הגבלת קצב, חלון 24ש׳ פג, שגיאת תבנית, שגיאת הרשאה, שגיאת ספק, שגיאת רשת).
פרטים נוספים ↓הסתר ↑
אבחון כשלי הודעות: נוסף שדה error_code לטבלת shipment — המערכת שומרת קטגוריה מובנית לכל כשל שליחה (אין WhatsApp, הגבלת קצב, חלון 24ש׳ פג, שגיאת תבנית, שגיאת הרשאה, שגיאת ספק, שגיאת רשת). בטבלת ההודעות מוצג badge עם סיבת הכשל ישירות בשורה ללא צורך לפתוח פרטים. נוסף פילטר "סיבת כשל" בסרגל הכלים לסינון לפי קטגוריה. טריגרי מערכת לעובדים: נוספו שלושה טריגרי WhatsApp לעובדים — LEAVE_REQUEST_SUBMITTED (שידור למנהלים), LEAVE_REQUEST_APPROVED ו-LEAVE_REQUEST_REJECTED (שליחה ישירה לעובד). ניתן להגדיר את הטריגרים דרך /messages/triggers; טריגרי אישור/דחייה אינם דורשים רשימת טלפונים כי הנמען נקבע אוטומטית. כניסה עם Google לפורטל עובדים: כפתור 'המשך עם Google' בדף הלוגין — העובד חייב להיות רשום במערכת עם אותו אימייל; עובד PENDING מופעל אוטומטית בכניסה הראשונה. תיקון: דף הפעלת חשבון עובד נכשל כי params.token היה undefined — ב-Next.js 15 params הוא Promise ולא חיכו לו. תוקן ל-async page + await params. אפשרות לשכפל משלוח בודד דרך תפריט הפעולות בשורה, ושכפול מרובה דרך ה-bulk actions bar. השכפול יוצר משלוח חדש בליונוויל עם כל שדות הנמען, הכתובת, ההגדרות הארגוניות ושדות מותאמים — ללא העתקת ברקוד, סטטוס, תאריכי מסירה, או מידע תפעולי. בנוסף, נוסף כפתור "הוספת משלוחים" בדף יעד שבת — פותח מודל עם אינפוט מרובה שורות לברקודים (הדבקה מאקסל) ובחירת פרשה, מאפשר לעדכן יעד שבת למשלוחים שאינם מוצגים כרגע בטבלה. שני המודלים עוצבו מחדש לפי שפת העיצוב של המערכת: כותרת דבוקה עם אייקון גרדיאנט + badge, מיין גלילה, תחתית דבוקה, כותרות קטעים עם צ'יפ צבעוני, שדות עם תוויות uppercase. תיקון: פיקר תבניות Meta המאושרות בטפסי הטריגרים לא אפשר גלילה ברשימה — נוספה max-h-64 overflow-y-auto ל-CommandList בהתאם לדפוס שנהוג בשאר הפיקרים. תיקון: דף /messages/triggers הציג חלל ריק גדול בתחתית — הוסר flex-1 מה-wrapper של SettingsContent והוחלף ב-pb-2 בלבד. תיקון: אינבוקס — כשהיה פוקוס על שדה ההקלדה בתחתית הצ'אט הופיע רינג כפול (על ה-wrapper וגם על הטקסטאריה הפנימית) — הוסרה בורדר הפוקוס הפנימית עם !important.
- סטטיסטיקות מסרונים: דף חדש /messages/analytics עם עלויות Meta Cloud API בדולר ושקל (שער חי), חלוקה לשיחות בתשלום מול שיחות שירות חינם (חלון 24h), פירוט ספקים (Meta/Green API), גרף לפי יום/שבוע/חודש, וטבלה מפורטת לפי תקופה
- תיקון ניווט: /analytics עבר ללשונית הגדרות (במקום מסרונים); /messages/analytics הוא הדף הסטטיסטי החדש
- טריגרי עובדים: 3 טריגרי WhatsApp חדשים — בקשת חופשה הוגשה (שידור למנהלים), אושרה/נדחתה (לעובד ישירות)
- אפליקציה: תיקון cache POD — שימוש ב-refetchQueries (במקום invalidateQueries) כדי שהcache יהיה עדכני לפני router.back(); sticky bottom מתעדכן מיד ללא pull-to-refresh
- אפליקציה: POD הפך ל-optional — state machine הסיר NEED_POD; sticky מציג 'צלם POD' + 'דווח כשל' כשניות כשאין POD ב-READY_TO_DELIVER
- אפליקציה: אישור מסירה ללא POD — Alert עם 3 אפשרויות: ביטול / צלם POD / אשר ללא POD; עם POD — Alert פשוט
- Backend: הוסר בדיקת POD_REQUIRED מ-deliver endpoint; COD_NOT_COLLECTED נשאר כדרישה עסקית
- טריגר טעות מיון עם חזרה אוטומטית: אפשרות לשלוח התראת טעות מיון לקבוצת WhatsApp (Green API) ולהפעיל לולאת חזרה כל 5 דקות — נעצרת כשאחד מחברי הקבוצה מסמן 👍 על ההודעה
- אפליקציה: מסך Today — Filter Chips הוחלפו ב-3 לשוניות עליונות (פעילים/נמסרו/נכשלו) בסגנון pill עם מונה; טאב עם 0 פריטים מואפל; empty state מותאם לכל לשונית
- אפליקציה: VisitCard — שורת badges קומפקטית חדשה: דחוף (אדום), אקספרס (סגול), שבת (טורקיז), COD עם סכום (ענבר); מחליפה את שורת ה-COD הגדולה הישנה
v01.17.001ייעולmessages-inboxייעול טעינת צ׳אט ב-inbox
שני שלבי ייעול לטעינת הצ׳אט: (1) הפחתת round trips לDB — הודעות ומשלוחים נשלפים במקביל, ובדיקת חלון 24ש מחושבת מהשורות שכבר הוחזרו ללא שאילתה נוספת.
פרטים נוספים ↓הסתר ↑
שני שלבי ייעול לטעינת הצ׳אט: (1) הפחתת round trips לDB — הודעות ומשלוחים נשלפים במקביל, ובדיקת חלון 24ש מחושבת מהשורות שכבר הוחזרו ללא שאילתה נוספת. (2) אינדקס פונקציונלי חדש על shipment(tenant_id, regexp_replace(phone)) — שאילתת חיפוש משלוח לפי טלפון עברה מ-full table scan עם regex per-row לחיפוש אינדקס, קיצור מ-6 שניות להאצה משמעותית. גם messages/route.ts וגם conversations/route.ts עודכנו להשתמש בביטוי תואם לאינדקס (IN במקום CASE).
v01.17.000חדשemployee-portalminorפורטל עובדים — ממשק מלא
ווידג'ט נוכחות ב-Navbar: משתמשי STAFF המקושרים לרשומת עובד מקבלים ממשק כניסה/יציאה ממשמרת ישירות מהנאבר — טיימר חי בעת משמרת פעילה, כפתור 'כנס' בעת חוסר משמרת עם בחירת סוג עבודה אם יש יותר מאחד.
פרטים נוספים ↓הסתר ↑
ווידג'ט נוכחות ב-Navbar: משתמשי STAFF המקושרים לרשומת עובד מקבלים ממשק כניסה/יציאה ממשמרת ישירות מהנאבר — טיימר חי בעת משמרת פעילה, כפתור 'כנס' בעת חוסר משמרת עם בחירת סוג עבודה אם יש יותר מאחד. גישה לניהול הנוכחות חסומה לאותם משתמשים כברירת מחדל (אפילו אם הם ADMIN); ניתן להפעיל עבור אחראי שכר דרך טאב חדש 'משתמשי מערכת' בהגדרות הנוכחות. שיפורי עיצוב כותרת סיידבר: הגדלת אייקון הלוגו, padding פנימי לכפתור, ורווחים עקביים של 8px בין כל האלמנטים בכותרת. פורטל לקוח — טבלת משלוחים: שדרוג מלא לרמת טבלות האדמין — sticky actions column, בחירה מרובה, bulk actions bar מתחת לטבלה (העתק ברקודים / הדפס מדבקות / ייצא לאקסל), הרחבת עמודות בגרירה, ניהול עמודות ו-Excel עברו לתפריט בכותרת עמודת הפעולות, נקודות לאורך, סקרולבר אופקי, toolbar נקי. תיקון: קישורי הזמנת עובד נבנים כעת מ-AUTH_URL/NEXTAUTH_URL/NEXT_PUBLIC_APP_URL ולא מה-origin header — מונע מצב שה-URL ריק ומייל ההזמנה לא ניתן ללחיצה. שדרוג מקיף של פורטל העובד: דף נוכחות עם תצוגת סטטוס חיה, טיימר משמרת, כפתורי כניסה/יציאה, דיווח רטרואקטיבי ומשמרות בהמתנה. היסטוריית משמרות מקובצת לפי יום עם Accordion, ניווט חודשי/שבועי. בקשות חופשה עם עיצוב מחדש ותצוגה מדורגת. משימות עם הערת עובד, badge איחור וקטע הושלמו מתקפל. הוסרה דף 'חברי צוות' מההגדרות — הפונקציונליות קיימת ב'משתמשים'. שדרוג דף המשתמשים: pagination server-side (תוקן באג שהסתיר משתמשים מעל 50), עמודת טלפון, שינוי תפקיד inline בטבלה, סינון 'מחוברים עכשיו', ואיפוס סיסמה עם סיסמה זמנית. עמודת 'כניסות' כעת מציגה session count — כל חזרה לאפליקציה אחרי פער של 5+ דקות נספרת ככניסה. עמודת מעורבות חדשה עם badge צבעוני (פעיל/מדי פעם/רדום/חדש). פאנל נוכחות חיה (SSE) — מציג מי מחובר ובאיזה דף. פילטר רדומים (30+ יום) עם חסימה קבוצתית. כתיבה מחדש של דף הגדרות הנוכחות: שדות מסודרים לפי המודל הנכון (regularDayHours, overtimeThreshold125/150, מכפילי שבת), טאב שעות נוספות מורחב, טאב בונוסים חדש עם ניהול בונוסים לכל עובד. שיפורי Meta API: קודי שגיאה ידידותיים (130429/131026/131047/190 ועוד), banner תבניות לא פעילות, בדיקת תוקף Token בדף אינטגרציות, זמן נותר לחלון 24ש ב-inbox, retry אוטומטי על rate-limit.
- דף נוכחות: dot מהבהב, טיימר חי, דיווח רטרו עם dialog, פאנל משמרות ממתינות/נדחות
- היסטוריה: Accordion לפי יום, toggle חודש/שבוע, ניווט קדימה/אחורה
- חופשות: עיצוב מחדש עם אייקונים, תצוגה נפרדת ממתין/היסטוריה
- משימות: הערת עובד, badge באיחור, קטע הושלמו מתקפל
- ניווט תחתון: 5 כרטיסיות — נוכחות, היסטוריה, חופשות, מסמכים, משימות
- API חדשים: me, retro-shifts (GET/POST/DELETE), shifts/history
- ממשק מנהל: דיאלוג עריכת משמרת (שעות, הפסקה, סטטוס, הערת מנהל), דחייה עם סיבה חופשית, כפתור 'ממתינות לאישור' עם ספירה בטולבר
- דשבורד נוכחות: כרטיס 'ממתינות לאישור' עם ספירה ולינק ישיר לדף המשמרות המסונן
- דף משמרות: אישור כולל (bulk approve) עם progress counter, ייצוא CSV עם BOM לתאימות Excel, פילטר סטטוס מ-URL query param
- פורטל עובד: בחירת סוג עבודה inline לפני כניסה — כפתורי toggle עם צבע, ברירת מחדל אוטומטית, מוסתר כשיש פחות מ-2 סוגים; שם סוג העבודה מוצג גם בכרטיס הסטטוס ובמשמרות האחרונות
- הגדרות נוכחות: כתיבה מחדש עם שדות נכונים מהסכמה (regularDayHours, overtimeThreshold125/150, autoBreakAfterHours/Minutes, sabbathMultiplier*), API GET/PUT מחובר כראוי
- בונוסים לעובדים: API GET/POST/DELETE, טאב ייעודי בהגדרות עם פתיחת/קריסת רשימת בונוסים לכל עובד, דיאלוג הוספת בונוס עם סוג/סכום/תאריכים/הערה
- לוח שנה: תצוגת גריד חודשי עם ניווט חודש/שנה, הדגשת שבת/יום נוכחי, לחיצה על יום מציגה אירועיו בפאנל צדדי; toggle בין גריד ורשימה שנתית
- דף תבניות Meta: תיקון באג — תבניות שטרם נשלחו ל-Meta מוצגות עכשיו ברשימה (badge 'טרם נשלחה'); חיפוש וסינון בצד הלקוח לפי שם/קטגוריה/סטטוס; stats panel ב-aside (מאושרות/ממתינות/נדחו/טרם נשלחו); dropdown פעולות במובייל; תאריך עדכון אחרון בכל שורה; empty state מותאם לפילטר עם כפתור ניקוי; 4 skeletons טעינה; הסרת קוד מת (VARIABLE_KEYS, MetaBinding, MetaTemplateOption, MetaStatusBadge)
- build fix: הסרת prop לא חוקי dir מ-DropdownMenuContent בדף תבניות (TypeScript error בדיפלוי)
- שכר מנהל: תוקן מנוע חישוב — שימוש ב-TenantWorkRules לסף שעות נוספות ומכפילים, תמיכה בבונוסים (חודשי/שעתי/חד-פעמי), חישוב bulk לכל עובדים; דיאלוג פירוט בלחיצה על שורה עם breakdown שעות+סכומים ואישור; ייצוא CSV; כרטיסי סיכום
- שכר עובד: API עצמי (/api/attendance/self/payroll) + card שכר בדף הנוכחות מציג 3 חודשים אחרונים עם שעות, שעות נוספות וסהכ
- ווידג'ט נוכחות ב-navbar: משתמשי מערכת (STAFF) המקושרים לרשומת עובד רואים טיימר משמרת פעילה (ציר ירוק מהבהב + שעון) וכפתור כניסה/יציאה ממשמרת ישירות מהנאב. חסימת גישה לממשק ניהול הנוכחות למשתמשי מערכת עם employeeId — ניתן לביטול חסימה ספציפית per-user דרך טאב 'משתמשי מערכת' בהגדרות הנוכחות.
- מסמכים עובד: פורטל העלאת מסמכים עצמי עם UploadDialog, הצגת בקשות ממתינות/שהושלמו, סינון וגבול 5 מסמכים עם toggle; API PENDING_APPROVAL לכל העלאה
- מסמכים מנהל: ניהול בקשות מסמכים (יצירה/הפעלה/מחיקה) דרך דיאלוג ייעודי בטולבר; תיקון באג fullName → firstName+lastName בטבלה, חיפוש ו-preview dialog
- אישור/דחיית מסמכים: API PATCH לעדכון סטטוס + כפתורי אשר/דחה לשורות PENDING_APPROVAL בדף מסמכים מנהל + dialog דחייה עם סיבה
- dashboard נוכחות: כרטיס מסמכים פגי תוקף (30 יום) עם לינק לדף המסמכים; grid שונה ל-6 עמודות
- אישור חופשה חלקי: dialog אישור עם DatePicker לעריכת תאריכים לפני אישור; API approve מקבל startDate/endDate אופציונליים ומחשב totalDays מחדש; dialog דחייה עם שדה סיבה
- דף דוחות: כתיבה מחדש עם נתונים אמיתיים — 4 טאבים (משמרות/שכר/חופשות/מסמכים פגי תוקף), פילטר חודש+שנה, ייצוא CSV עם BOM, טבלת סיכום בתחתית שכר
- כרטיסי סיכום בדשבורד נוכחות: עיצוב מחדש לפי דפוס ShipmentStats — אייקון בקונטיינר רקע צבעוני עם rounded-lg, מספרים ניטרליים font-bold, ביטול גבולות צבעוניים על כרטיסים
- כרטיסי סיכום בדפי דוחות ושכר: אחידות עם הדשבורד — אותו דפוס icon-in-bg-container בכל דפי הנוכחות
- שכר — טבלה: הסרת צבעי טקסט מוגזמים משורות הטבלה; תיקון באג כפתור מחיקה שלא הופיע (חסר class='group' על שורת הטבלה)
v01.16.001חדשemployees, messagesהתחזות לעובד מדף העובדים
הוספת יכולת התחזות לעובד עבור מנהלי מערכת (OWNER ומעלה).
פרטים נוספים ↓הסתר ↑
הוספת יכולת התחזות לעובד עבור מנהלי מערכת (OWNER ומעלה). לחיצה על 'התחבר כעובד' בטבלת העובדים מעבירה את המנהל לפורטל העובד תחת זהות העובד, עם באנר התחזות ואפשרות חזרה מיידית. בנוסף הורחב פורטל העובד לממשק מלא: דף בקשות חופשה, דף מסמכים אישיים, דף משימות — עם ניווט תחתון בין הדפים. תיקון שגיאת Meta #132001 — תבניות שאינן קיימות ב-Meta מסומנות כ-'לא נמצאת ב-Meta' עם כפתור ניתוק קישור; שם התבנית נוסף ל-log כדי לאפשר אבחון.
v01.16.000חדשmobileminorאפליקציה ניידת — טופס גביית גוביינא (COD)
נוסף מסך גביית גוביינא באפליקציית השליח.
פרטים נוספים ↓הסתר ↑
נוסף מסך גביית גוביינא באפליקציית השליח. אמצעי התשלום נקבע ע"י ה-Operator מראש (מזומן/צ'ק) — השליח מאשר בלבד ואינו בוחר. לצ'ק: toggle בין צילום הצ'ק להקלדה ידנית — אחד מהם מספיק. Backend מוסק סכום ואמצעי מ-DB ומאכף עליהם.
- מזומן: הצגת הסכום read-only + אישור בלחיצה אחת
- צ'ק: toggle בין מצב צילום (Phase 3) למצב הקלדה ידנית — אחד מספיק
- Backend: אמצעי תשלום נשלף מ-CodTracking.codType (לא נשלח מ-client)
- Seed עודכן: SHP00001+SHP00007=cash, SHP00005=check
- /today API מחזיר codType לכל ביקור עם גוביינא
- Phase 3: מצלמה אמיתית דרך expo-image-picker + דחיסה ל-1600px / 0.85 quality עם expo-image-manipulator; thumbnail מוצג לאחר צילום
- Phase 4: submitCod() שולח base64 (photo) או 5 שדות (manual); הצלחה → Alert + invalidateQueries + router.back(); שגיאה → Alert עם 'נסה שוב'
- אקורדיון מסרונים בדף משלוח: הצגת מסרונים שנכשלו גם כשה-message ריק (sendStatus=FAILURE)
- דף תבניות Meta: header עטוף (flex-wrap) ב-mobile, כפתורים icon-only במסכים צרים, Dialog גלילה ב-mobile
- דף שינוי סיסמה: נוסף /auth/change-password עם אימות סיסמה נוכחית + API route POST /api/users/change-password
- Sprint 4d Phase 1: POST /api/v1/mobile/driver/visits/:id/deliver — בדיקת POD + COD לפני סימון + update isDone/deliveredAt
- Sprint 4d Phase 1: POST /api/v1/mobile/driver/visits/:id/fail — reasonCode + notes (חובה) + העלאת תמונת תיעוד אופציונלית; failureReason נשמר ב-shipment
- Sprint 4d Phase 2: delivery.ts — markDelivered() + markFailed() עם base64 photo conversion ו-typed error codes
- תיקון deriveStatus: failedAt לבד (ללא isDone) מחזיר status='failed' — תואם ל-fail שלא מסמן isDone
- תיקון middleware: /employees (ניהול) זוהה בטעות כנתיב פורטל עובדים — גרם להפניה ל-/shipments; תוקן startsWith ל-/employee/
- שדות תאריך התחלה/סיום בטופס עובד הוחלפו בבורר תאריכים עם לוח שנה (Popover + Calendar mode=single) — כולל כפתור ניקוי לתאריך הסיום
- Sprint 4d Phase 3: state machine חכם ב-sticky bottom — NEED_POD/NEED_COD/READY_TO_DELIVER; HeroSection בצבעי כחול/ירוק/אדום לפי status; handleMarkDelivered עם Alert אישור
- Sprint 4d Phase 4: מסך fail.tsx — radio picker לסיבת כשל, textarea חובה, תמונת תיעוד אופציונלית עם מצלמה; submit → markFailed() + Alert + router.replace
v01.15.001שיפורtasksלוח משימות — תיקון רווחים לעיקרון 8px אחיד
תוקנו כל חריגות הרווח בתצוגת הדסקטופ של לוח הקנבן: gap בין אזורים, gap בסרגל הפילטרים, gap בין עמודות, padding בכותרת העמודה, gap בשורת המטאדאטה בכרטיס ובאזור הפעולות — הכל עבר ל-8px בדיוק.
פרטים נוספים ↓הסתר ↑
תוקנו כל חריגות הרווח בתצוגת הדסקטופ של לוח הקנבן: gap בין אזורים, gap בסרגל הפילטרים, gap בין עמודות, padding בכותרת העמודה, gap בשורת המטאדאטה בכרטיס ובאזור הפעולות — הכל עבר ל-8px בדיוק. בוטל padding פנימי ברירת-מחדל (10px) שקומפוננט KanbanColumn הוסיף מסביב לתוכן העמודה.
v01.15.000חדשcustomer-portalminorפורטל לקוחות — כתובות מועדפות ולינק מעקב
לקוחות בפורטל יכולים כעת לשמור כתובות מועדפות (מחסן, משרד וכו') ולטעון אותן בלחיצה אחת בטופס יצירת משלוח.
פרטים נוספים ↓הסתר ↑
לקוחות בפורטל יכולים כעת לשמור כתובות מועדפות (מחסן, משרד וכו') ולטעון אותן בלחיצה אחת בטופס יצירת משלוח. הכתובות משותפות בין כל המשתמשים תחת אותו חשבון לקוח. בנוסף, בדף פרטי המשלוח נוסף כפתור 'לינק מעקב' שמעתיק ללוח את הקישור הציבורי של המשלוח לשליחה ללקוח הקצה. חיפוש בדף הלקוחות עבר לעבודה בצד הלקוח — מיידי, ללא debounce וללא בעיות focus.
- עמוד 'כתובות מועדפות' תחת הגדרות הפורטל — ניהול מלא (הוסף, ערוך, מחק)
- כפתור 'מועדפות' בטופס יצירת משלוח — מאכלס אוטומטית את כל שדות הכתובת
- כתובות משותפות ברמת חשבון הלקוח — לא אישיות למשתמש
- כפתור 'לינק מעקב' בדף פרטי משלוח — מעתיק את כתובת הטראקינג הציבורית
- הגבלת 50 כתובות לחשבון
v01.14.000חדשattendanceminorפורטל עובדים — כניסה עצמאית לנוכחות
עובדים שכירים מנוהלים עכשיו בנפרד ממשתמשי המערכת, בדיוק כמו לקוחות ושליחים.
פרטים נוספים ↓הסתר ↑
עובדים שכירים מנוהלים עכשיו בנפרד ממשתמשי המערכת, בדיוק כמו לקוחות ושליחים. המנהל שולח הזמנה לאימייל העובד מתוך דף הניהול. העובד מגדיר סיסמה ונכנס דרך /employee/login — עמוד login ייעודי לפורטל עובדים. לאחר הכניסה הוא מגיע לדף נוכחות אישי בלבד, ללא גישה לשאר המערכת. תומך גם בשחזור סיסמה.
- פורטל עובדים עצמאי בכתובת /employee/login — כניסה נפרדת ממשתמשי מערכת
- שדה 'שלח הזמנה לפורטל' בפעולות שורה בדף ניהול עובדים — שולח מייל עם קישור הגדרת סיסמה
- flow הזמנה + קבלת הזמנה + שחזור סיסמה מלא
- הוספת שדות auth ל-Employee: status, passwordHash, inviteToken, passwordResetToken
- EMPLOYEE userType בסשן — בידוד מלא מ-STAFF ו-CUSTOMER
v01.13.000חדשattendanceminorפורטל נוכחות עצמי לשליח שכיר
שליח שכיר (EMPLOYEE_DRIVER) מקבל כעת ממשק ייעודי ומבודד לנוכחות עצמית.
פרטים נוספים ↓הסתר ↑
שליח שכיר (EMPLOYEE_DRIVER) מקבל כעת ממשק ייעודי ומבודד לנוכחות עצמית. עם הכניסה למערכת הוא מופנה אוטומטית לדף נוכחות פשוט שמאפשר החתמת כניסה ויציאה בכפתור אחד, ורואה את היסטוריית המשמרות שלו ב-30 הימים האחרונים. אין גישה לאף חלק אחר במערכת — middleware חוסם את כל הניתובים האחרים. הוספו הרשאות חדשות ATTENDANCE_SELF_VIEW ו-ATTENDANCE_SELF_CLOCK לתפקיד זה.
- דף נוכחות מבודד ללא sidebar — חוויה נקייה ופשוטה
- כפתור כניסה/יציאה בודד עם שעון חי ומשך המשמרת הנוכחית
- טבלת משמרות אחרונות (30 יום) עם שעות וסיכום זמן
- middleware חוסם גישה לכל שאר העמודות — העובד רואה רק את הנוכחות שלו
- הוספו הרשאות ATTENDANCE_SELF_VIEW ו-ATTENDANCE_SELF_CLOCK
v01.12.002שיפורtasksדף משימות עוצב מחדש כלוח Kanban
דף המשימות האישי עוצב מחדש כלוח Kanban עם 4 עמודות (ממתין, בטיפול, הושלם, בוטל).
פרטים נוספים ↓הסתר ↑
דף המשימות האישי עוצב מחדש כלוח Kanban עם 4 עמודות (ממתין, בטיפול, הושלם, בוטל). ניתן לגרור כרטיסיות בין עמודות לעדכון סטטוס. פילטרי תאריך ועדיפות עובדים על כל העמודות. עמודת 'בוטל' מוצגת לצפייה בלבד ללא אפשרות גרירה אליה. שיפורי עיצוב נוספים בצ׳אט inbox: שדה הקלדה שוכתב לשורה אחת — כפתור תבנית Meta כאייקון בתוך שורת ה-input (במקום שורה שלמה נפרדת), בצמוד לבורר אימוג'י ולכפתור שליחה; badges של לא-נקראו/לא-נענו שונו לצבע שחור סולידי (zinc-800); נוסף dot-grid pattern עדין לרקע אזור השיחה; הוסר טקסט כפול 'מחוץ לחלון 24 השעות' שהופיע מתחת לאינפוט (הטקסט כבר קיים ב-placeholder של שדה ההקלדה). תוקן באג קריטי: preferences route דרסה את כל שדות ה-settings שאינם העדפות (whatsappTemplates, messagingChannel וכו') בכל עדכון העדפות. תוקן באג קריטי בפורטל לקוחות: חיפוש גלובלי קרס עם TypeError — sendStatus לא נכלל ב-API של /api/portal/shipments, גרם ל-undefined.toLowerCase() בעת רנדור תוצאות. הוחלף ב-additionalData + getDeliveryStatusBadge בהתאם לשאר הפורטל. תוקנה שגיאת 403 בפורטל: BrandColorInjector ב-root layout קרא ל-useCurrentTenant שניסה לפנות ל-/api/settings/profile גם עבור משתמשי CUSTOMER — הוסף guard שמונע את ה-fetch לגמרי עבור משתמשי פורטל. תוקנה אזהרת גרף בדשבורד פורטל: ResponsiveContainer עם height="100%" קרא -1 לפני שה-layout הסתיים — הוחלף ב-height={256} מפורש.
v01.12.001חדשmessaging/cronחלון שליחה: שעות פעילות, שבת וחגים לפי טננט
עיצוב מחדש של צ׳אט inbox לסגנון שחור-לבן סולידי: הודעות יוצאות עברו מירוק לרקע כהה (zinc-900) עם טקסט לבן; הודעות נכנסות — רקע muted עדין; כפתור שליחה — שחור; כל badges ה-source/window בצבעי ניטרל; מפריד 'הודעות חדשות' —…
פרטים נוספים ↓הסתר ↑
עיצוב מחדש של צ׳אט inbox לסגנון שחור-לבן סולידי: הודעות יוצאות עברו מירוק לרקע כהה (zinc-900) עם טקסט לבן; הודעות נכנסות — רקע muted עדין; כפתור שליחה — שחור; כל badges ה-source/window בצבעי ניטרל; מפריד 'הודעות חדשות' — ניטרל; הצגת שיחה פעילה בסרגל — bg-muted; dot של lא-נקרא ידני — אפור. הצבעים היחידים שנשארו: צבעי אווטר, badge ירוק לא-נקראו, badge amber לא-נענו, צ׳ק כחול READ. הודעות מתוזמנות כעת נשארות בתור כשאינן בחלון הזמן המותר — לא מאבדות ניסיון. כל טננט יכול להגדיר שעות שליחה מותרות ולסמן חסימה בחגים. ה-cron job בודק את החלון לפני כל הודעה; אם השעה מחוץ לטווח, ביום שבת פעיל, או ביום טוב — ה-message נשמר בתור ל-cron הבא. ספריית @hebcal/core משמשת לבדיקת חגי ישראל (מצב ישראל, ימי חג בלבד). תוקן באג ב-AI delivery parser: Claude החזיר JSON עטוף ב-markdown code block (` ```json ``` `) למרות ההנחייה — הוסף strip של העטיפה לפני ה-parse. תוקנה בחירת משלוח ב-AI delivery: במקום לבחור את המשלוח האחרון לפי createdAt, מחפשים כעת את המשלוח שאליו נשלחה ההודעה האחרונה ב-WhatsApp לאותו טלפון — כי הלקוח מגיב לאותה הודעה ספציפית. פערי הפצה: כפתורי שיבוץ/לא משובץ הוחלפו ב-FiltersPopover כללי עם שלוש קטגוריות — שיבוץ שליח (single-select), שליח ספציפי, וסטטוס משלוח. נוספה עמודת סטטוס תפעול לטבלת פערי הפצה. פילטר שיבוץ שליח מאותחל ל"משובץ" ומציג מספרים מעודכנים לפי הסינון הנוכחי. שיפור ביצועים בדף inbox: שאילתת conversations שוכתבה לCTEs — unread_count חושב בנפרד פעם אחת (O(N)) במקום sub-query מתואם לכל שיחה (O(N²)); קריאות DB בpages/messages צומצמו מ-4 סדרתיות ל-2 מקביליות; תדירות polling הופחתה מ-5 שניות ל-15. pagination בסרגל השיחות: API מחזיר 20 שיחות ראשונות (cursor-based), גלילה לתחתית טוענת דף נוסף דרך IntersectionObserver. prefetch on hover: ריחוף מעל שיחה 150ms מתחיל לטעון הודעות ברקע עם cache של 30 שניות — כניסה לשיחה instant אם ה-cache מוכן. ברקודי משלוח בצ׳אט: כל מספר משלוח שמופיע בגוף הודעה הופך ללחיץ — אייקוני העתקה וניווט בריחוף, ולחיצה על הברקוד פותחת drawer עם פרטי המשלוח (זהה לחוויה בטבלאות). סימון כנקרא/לא נקרא: בריחוף על שיחה מוצג כפתור MailIcon שמחליף את השעה — לחיצה מסמנת כנקרא/לא נקרא; לחיצה ימנית על שיחה פותחת תפריט הקשר עם אותן אפשרויות. שיחות מסומנות כ"לא נקראו" מציגות נקודה כחולה במקום badge, ומופיעות בטאב 'לא נקראו'. מצב נשמר ב-localStorage.
v01.12.000חדשmessaging/inboxminorהצגת מדיה נכנסת (תמונות, שמע, וידאו) בצ׳אט
תיקון שתי בעיות בצ׳אט inbox: (1) הודעות יוצאות מטריגרים לא הופיעו — shipment-trigger-processor שלח דרך sendWhatsApp אבל לא קרא ל-recordOutboundMessage, כך שלא נכתב שום דבר ל-WhatsappMessage; נוספה קריאה לאחר כל שליחה מיי…
פרטים נוספים ↓הסתר ↑
תיקון שתי בעיות בצ׳אט inbox: (1) הודעות יוצאות מטריגרים לא הופיעו — shipment-trigger-processor שלח דרך sendWhatsApp אבל לא קרא ל-recordOutboundMessage, כך שלא נכתב שום דבר ל-WhatsappMessage; נוספה קריאה לאחר כל שליחה מיידית. (2) תמונות נכנסות הציגו רק placeholder — גישת Supabase לא עבדה ללא אחסון מוגדר; הוחלפה ב-proxy route: ה-webhook שומר /api/messages/inbox/media/{mediaId} ב-mediaUrl, ה-proxy מביא את הקובץ מ-Meta Graph API עם access token של ה-tenant ומחזירו לדפדפן עם cache לשעה. שיפורים נוספים: הודעת תבנית Meta מציגה גוף מרונדר עם ערכי משתנים אמיתיים; לחיצה על תמונה פותחת מודל מסך-מלא עם כפתור הורדה.
- trigger processor: recordOutboundMessage אחרי כל שליחה מיידית — גם הצלחה וגם כשל
- proxy route: GET /api/messages/inbox/media/[mediaId] — auth דרך session, Meta Graph API
- תמיכה ב-image / audio / voice / video / document / sticker ללא תלות ב-Supabase
- Cache-Control: private, max-age=3600 — הדפדפן לא מוריד שוב לשעה
- Sprint 4b-1 Phase 1+2: POD Screen — Mode A (Camera) עם rule-of-thirds grid + amber focus brackets + top bar (flash/pill/done✓) + shutter + switch-cam + gallery thumb; expo-file-system/legacy + expo-image-manipulator; כפתור StickyActions ב-Visit Detail מנווט ל-/visit/[id]/pod
- Sprint 4b-1 Phase 3+4+5: POD Preview + Summary + Submit — Mode B (preview עם timestamp + delete/save) + Mode C (grid 3 עמודות + empty slots + add slot + note textarea + sticky CTA ירוק); storage cleanup: מחיקת קבצים ב-FileSystem בעת יציאה ומחיקת תמונה; compression: quality 1 → manipulateAsync 0.85 JPEG 1920px
- Sprint 4b-2: POD Real Upload — POST /api/v1/mobile/driver/visits/:id/pod (Zod validation, driver ownership check, base64→Buffer→Supabase Storage, ShipmentImage records); uploadPod() ב-mobile עם onProgress; progress overlay + ActivityIndicator + progress bar ב-Summary; queryClient invalidateQueries אחרי הצלחה; cleanup local files post-upload
v01.11.000חדשpublic/validateminorדפים ציבוריים: עדכון פרטי מסירה ומעקב משלוח
נבנו שני דפים ציבוריים (ללא login) שנפתחים מקישור וואטסאפ.
פרטים נוספים ↓הסתר ↑
נבנו שני דפים ציבוריים (ללא login) שנפתחים מקישור וואטסאפ. /validate/[id]: מאפשר ללקוח לעדכן פרטים חסרים (קומה, דירה, הערות לשליח) — שדות חסרים מסומנים בצהוב, עדכון נשמר ב-DB דרך PATCH. /tracking/[id]: מציג ציר זמן ויזואלי של מצב המשלוח — 4 שלבים (נוצר / עובד במחסן / יצא להפצה / נמסר) עם צבעים דינמיים לפי סטטוס, כולל טיפול בכשל, ביטול ומסירה.
- דף RTL פשוט ונקי — מותאם למובייל, ללא auth
- API route: GET + PATCH /api/public/shipments/[publicId] — גישה רק לפי public_id
- שדות עריכתיים: קומה, דירה, הערות לשליח — עם הדגשה ויזואלית על חסרים
- הגנת whitelist: רק שדות מסירה עריכתיים — שם, טלפון, ברקוד לא נחשפים לעריכה
- /validate הוסף ל-publicRoutes ב-middleware
- Sprint 4a Phase 1+2: Barcode Scanner — expo-camera + expo-haptics, CameraView + animated scan line + L-corners reticle + torch toggle; Permission denied screen עם openSettings; app.json permissions
- דף מעקב /tracking/[id] — ציר זמן ויזואלי עם 4 שלבים, צבעים דינמיים לפי סטטוס (ירוק=נמסר, אדום=כשל/בוטל, סגול=בדרך)
- API route: GET /api/public/shipments/[publicId]/tracking — נתוני מעקב בלבד, ללא חשיפת מידע רגיש
- /tracking הוסף ל-publicRoutes ב-middleware
- Sprint 4a Phase 3+4: חיבור detection — onBarcodeScanned + debounce 1 שניה + freeze לאחר זיהוי + haptic success/error + Alert 3 אפשרויות לברקוד לא מוכר
- כפתורי CTA ב-Meta: תמיכה מלאה בכפתור URL מוטמע בהודעה — הגדרה בדיאלוג ההגשה, שמירה ב-LocalTemplate, שליחה בטריגרים ו-resend עם dynamic suffix מ-public_id/barcode
- public_id נוסף כמשתנה ל-buildVariables — מאפשר שימוש בו כסיומת דינמית בכפתורי URL
- סנכרון לליונוויל מ-/validate: עדכון לקוח בקומה/דירה/הערות מסתנכרן אוטומטית לליונוויל (fire-and-forget); שדות כתובת נשלחים עם prefix destination_* בפורמט native של ליונוויל
- AI auto-reply: לקוח שמגיב בצ'אט עם קומה/דירה/ליד הדלת מקבל עדכון אוטומטי במערכת ובליונוויל + הודעת אישור חוזרת — Claude Haiku מחלץ פרטים מעברית חופשית עם debounce של 60 שניות לצבירת הודעות מרובות
v01.10.001תיקוןmessaging/triggersתיקון: טריגרי משלוח לא הופעלו
שדות ריקים בפרמטרים של תבנית Meta מוצגים כ-*חסר* (מודגש בוואטסאפ) במקום '-'.
פרטים נוספים ↓הסתר ↑
שדות ריקים בפרמטרים של תבנית Meta מוצגים כ-*חסר* (מודגש בוואטסאפ) במקום '-'. קובץ trigger-processor.ts נמחק בהגירה מ-NestJS ולא שוחזר — טריגרי ShipmentSetting מעולם לא הופעלו. שוחזר כ-shipment-trigger-processor.ts ומחובר ל-processTaskDeferred. תוקנו גם resend ו-cron שלא העבירו metaTemplateComponents לתבניות Meta עם פרמטרים. תוקן בנוסף: ספירת מיקודים בטבלת הליקוט כללה משלוחים ממחוזרים — נוסף deletedAt: null ל-baseWhere כדי שיחול גם על nested relation filter ב-db.visit.groupBy. תוקן: טריגרים שולחים תמיד דרך Green-API למרות שנבחרה תבנית Meta — setting.channel נשמר תמיד כ-green-api (ופגם בטופס). התיקון: קריאה ל-loadChannelContext + resolveChannelForTemplate לפי metaTemplateName של התבנית המקושרת, בנפרד לכל recipient. תוקן: buildMetaComponents השתמשה ב-variables map פנימי שהחסיר tracking_url, validation_url ועוד — Meta דחה בשגיאה 131008 על ערכים ריקים. עכשיו משתמש ב-buildVariables מ-template-renderer.ts שכולל את כל המשתנים. נוסף: log מפורט לפני שליחת trigger שמציג channel, metaParamMap, וערכי הפרמטרים — לאבחון מהיר של שגיאות 131008. תוקן: הודעות אוטומטיות מ-cron scheduled-messages לא הופיעו ב-inbox הצ'אט — חסרה קריאה ל-recordOutboundMessage אחרי שליחה מוצלחת.
v01.10.000חדשmobileminorMobile Driver API — היום שלי + פרטי ביקור
Sprint 3a+3b: backend endpoints + מסך Today מלא לאפליקציית השליח.
פרטים נוספים ↓הסתר ↑
Sprint 3a+3b: backend endpoints + מסך Today מלא לאפליקציית השליח. GET /driver/today מחזיר את כל ביקורי היום לפי timezone ישראל, כולל stats, COD batch query, סינון soft-deleted. GET /driver/visits/:id מחזיר פרטים מלאים + timeline + relatedActions עם security isolation. Sprint 3b: TanStack Query setup, API hooks (useToday/useVisit), Tab Navigator עם RouterTabBar wrapper, ומסך Today המלא — FlatList עם React.memo, 6 filter chips client-side, stats bar, loading skeletons, error state, empty state.
- GET /api/v1/mobile/driver/today — visits של היום, order לפי dailyOrder, batch COD query
- GET /api/v1/mobile/driver/visits/:id — פרטים מלאים, timeline, relatedActions
- Security: 403 על visit של driver אחר (לא 404, לא מחשיף קיום)
- Tab Navigator עם custom TabBar (RouterTabBar wrapper) + FAB scan button
- מסך Today: FlatList + React.memo, 6 filter chips, stats bar, pull-to-refresh
- Loading skeletons (5×), error state עם retry, empty state לפי filter
- Seed script מורחב: 8 shipments + visits + 3 COD records + Driver record
- Sprint 3c: Visit Detail Screen — Hero gradient (כחול/ירוק לפי סטטוס), Customer Card עם 4 action buttons, Address Card עם mini-map, Shipment Card (grid + notes), COD Card (amber/green), Timeline Card (TimelineEventCard), Delivery Summary (POD grid), Sticky bottom bar (סרוק/נזק/טעות שיבוץ)
v01.09.001תיקוןmobile/apiMobile Auth — בדיקת revocation ב-/me
תיקון: GET /me לא בדק אם המכשיר נותק לאחר logout.
פרטים נוספים ↓הסתר ↑
תיקון: GET /me לא בדק אם המכשיר נותק לאחר logout. הוספת בדיקת userDevice.revokedAt לפני החזרת המשתמש — עכשיו /me מחזיר 401 DEVICE_REVOKED לאחר logout או revoke.
v01.09.000חדשmobile/apiminorMobile Auth — Full Backend + Mobile Integration
Sprint 2b+2c Phase 1-3: auth מלא לאפליקציית השליחים.
פרטים נוספים ↓הסתר ↑
Sprint 2b+2c Phase 1-3: auth מלא לאפליקציית השליחים. Backend: user_devices, JWT, login+refresh+logout+me endpoints. mobile-auth-middleware.ts לשיתוף בין endpoints. Token rotation ב-refresh (hash comparison). Mobile: device-id.ts, api-client.ts עם fetch אמיתי + timeout. Phase 3: ApiClient (auto-refresh + single-flight), auth-store מחדש (restore אופטימיסטי + verifySession ברקע + silentLogout אידמפוטנטי), login.tsx sessionMessage banner.
- POST /api/v1/mobile/auth/login — timing-safe bcrypt, lock/active/tenant checks
- POST /api/v1/mobile/auth/refresh — token rotation, hash verification, DEVICE_REVOKED check
- POST /api/v1/mobile/auth/logout — revoke device ב-DB
- GET /api/v1/mobile/auth/me — verify token + user data עדכני
- mobile-auth-middleware.ts — Bearer extraction + verifyAccessToken משותף
- ApiClient (lib/api/client.ts) — auto-refresh on 401, single-flight (refreshPromise), dynamic import לauth-store למניעת circular dep
- auth-store.ts — restore אופטימיסטי מ-SecureStore + verifySession ברקע, silentLogout אידמפוטנטי, sessionMessage state
- login.tsx — InfoBanner לsessionMessage (session expired), clearSessionMessage ב-unmount
v01.08.000חדשmobileminorMobile Auth Foundation — Mock Login + Routing
Sprint 2a Phase 1+2+3: הוקמה תשתית Auth מלאה לאפליקציית השליחים.
פרטים נוספים ↓הסתר ↑
Sprint 2a Phase 1+2+3: הוקמה תשתית Auth מלאה לאפליקציית השליחים. types/auth.ts, token-storage.ts (SecureStore), api-client.ts (mock), auth-store.ts (Zustand). Routing guards ב-3 layouts. Phase 3: (auth)/login.tsx — מסך Login עם כרטיס לבן על רקע כחול-בהיר, Email+Password עם keyboard flow מלא (Enter → קפיצה לסיסמה → login), error banner עברית בתוך הכרטיס, KeyboardAvoidingView פר-פלטפורמה, Google button disabled. נוסף autoFocus ל-Input component.
- expo-secure-store — tokens ומשתמש נשמרים מוצפנים; נשארים בין הפעלות
- Zustand store — isLoading: true בהתחלה, restore() קורא ל-SecureStore לפני הצגת UI
- Routing guards — ניתוב אוטומטי לפי auth state ב-3 layouts
- Typed routes — router.d.ts עודכן ידנית עם routes חדשים (יוחלף ב-expo start בעתיד)
v01.07.000חדשmobileminorDesign System Primitives — 10 רכיבי UI לאפליקציית השליחים
נבנו 10 רכיבים אטומיים ב-apps/mobile/components/ui/ כבסיס ל-Design System של Shipnest Mobile.
פרטים נוספים ↓הסתר ↑
נבנו 10 רכיבים אטומיים ב-apps/mobile/components/ui/ כבסיס ל-Design System של Shipnest Mobile. כל הרכיבים עם TypeScript strict, RTL מלא, accessibility (accessibilityRole/State/Label), ו-StyleSheet מבוסס tokens מ-lib/theme.ts. בנוסף: playground מסך לבדיקה חזותית של כל הרכיבים, עם ניווט מהמסך הראשי ב-DEV mode. תוקנו באגי RTL ב-Input (textAlign hardcoded ל-right) וב-Switch (מיקום thumb hardcoded לאפליקציה עברית). הוחל Sprint 1b (Phase 1): EmptyStateCard, KPICard, NotificationCard. הוחל Sprint 1b (Phase 2): CODObligationCard, EarningsSummaryCard. הוחל Sprint 1b (Phase 3): TimelineEventCard (connecting line RTL-safe, 6 event types, isCompleted state), VisitCard (urgent strip, sequence+ETA+badge, COD badge, action buttons עם IconButton) — כל 7 cards מוכנים. הוחל Sprint 1c (Phase 1): MockStatusBar (visual mockup של status bar עם battery/signal), StandardHeader (3-column layout, back+title+rightActions, transparent mode), SearchHeader (back button + SearchInput flex, autoFocus עם setTimeout). הוחל Sprint 1c (Phase 2): ActionsHeader (title + מערך actions גמיש עם badge dots), HomeHeader (Avatar+greeting+date + segmented Driver/Admin control), TransparentHeader (position absolute, glass back button עם shadow, pill title). הוחל Sprint 1c (Phase 3 — Final): TabBar (5 slots, FAB 56×56px עם shadow בולט יוצא 16px מעל ה-bar, slot 3 ריק, badge על profile, useSafeAreaInsets לrespect home indicator).
- lib/theme.ts — כל טוקני העיצוב מ-04-tokens.json כ-TypeScript `as const` עם type inference
- lib/rtl.ts — פונקציות עזר RTL-safe: marginStart/End, absoluteEnd/Start, rtlFlip
- Button: 4 variants × 3 sizes, touch scale animation (Animated.spring 0.97), loading spinner, min 44px touch
- Input: forwardRef, animated border color 200ms, error + focus states, clear button RTL-safe
- PasswordInput: secureTextEntry toggle, textContentType=password לstability iOS keyboard
- Playground ב-app/playground.tsx — כל הרכיבים interactive, גישה מהmenu ב-__DEV__
v01.06.000חדשmobileminorהשקת apps/mobile — Scaffolding אפליקציית שליחים
הוקמה apps/mobile — אפליקציית React Native (Expo) לשליחים בתוך המונורפו.
פרטים נוספים ↓הסתר ↑
הוקמה apps/mobile — אפליקציית React Native (Expo) לשליחים בתוך המונורפו. הבסיס כולל: Expo SDK 54 עם React Native 0.81.5 ו-React 19, Expo Router v6 (file-based routing), NativeWind v4 עם design tokens מלאים מ-04-tokens.json, פונט Heebo נטען דינמית, RTL מוגדר ב-I18nManager, SplashScreen עד לטעינת הפונטים, וtypecheck עובר ללא שגיאות.
- Expo SDK 54 + React Native 0.81.5 + React 19 — stack מעודכן
- NativeWind v4 עם tokens (primary, accent, slate, success, warning, error, background, text)
- RTL force מובנה: I18nManager.forceRTL(true) + android.supportsRtl
- Expo Router v6 file-based routing — מבנה דומה ל-Next.js App Router
- מונורפו: metro.config.js עם watchFolders לpackages/, @workspace/shared בtsconfig
v01.05.002שיפורdistribution-gaps, shipments, importהערת מעקב בדף פרטי משלוח + תיקון עדכון תאריך
הערת המעקב (מפערי הפצה) מופיעה עכשיו גם בדף פרטי המשלוח: ניתן ליצור, לערוך ולמחוק את ההערה ישירות מהדף, וכל שמירה מתועדת בהיסטוריה.
פרטים נוספים ↓הסתר ↑
הערת המעקב (מפערי הפצה) מופיעה עכשיו גם בדף פרטי המשלוח: ניתן ליצור, לערוך ולמחוק את ההערה ישירות מהדף, וכל שמירה מתועדת בהיסטוריה. עמודת 'עדכון הערה' בטבלת פערי ההפצה מתרעננת אוטומטית לאחר שמירה, וה-popover ההיסטוריה טוען מחדש את הרשומות העדכניות. בנוסף, ביבוא משלוחים: שורה שחסר בה טלפון ראשי אבל קיים טלפון נוסף — הטלפון הנוסף מקודם אוטומטית לטלפון הראשי ולא נחסמת כשגויה.
v01.05.001שיפורsettings/updatesעיצוב מחדש של דף עדכונים ושיפורים
דף העדכונים עוצב מחדש עם מראה פרמיום: גרסאות מרכזיות מקבלות כרטיס hero עם glow הנפשה, blob gradients, watermark גרסה ברקע ואייקון זוהר; עדכונים משמעותיים מקבלים אקסנט עבה צבעוני בצד ימין עם gradient עדין; patches כוללים …
פרטים נוספים ↓הסתר ↑
דף העדכונים עוצב מחדש עם מראה פרמיום: גרסאות מרכזיות מקבלות כרטיס hero עם glow הנפשה, blob gradients, watermark גרסה ברקע ואייקון זוהר; עדכונים משמעותיים מקבלים אקסנט עבה צבעוני בצד ימין עם gradient עדין; patches כוללים hover state מלוטש עם scale animation על האייקון. בנוסף תוקן רוחב מלא — הוסרה הגבלת max-w-2xl.
v01.05.000חדשportal, customers, shipments, target-shabbatminorשחזור סיסמה עצמאי לפורטל הלקוחות
משתמשי הפורטל יכולים כעת לאפס את הסיסמה שלהם בעצמם.
פרטים נוספים ↓הסתר ↑
משתמשי הפורטל יכולים כעת לאפס את הסיסמה שלהם בעצמם. נוספו דפי 'שכחת סיסמה?' ו-'איפוס סיסמה' בפורטל, קישור 'שכחת סיסמה?' בדף הכניסה לפורטל, ו-API endpoints תואמים. הטוקן תקף 60 דקות. בנוסף, כשמשתמש פורטל נוצר אוטומטית מהאימייל של הלקוח — נשלח אליו אימייל הזמנה. נוסף פילטר 'סוכן' במצבת הלקוחות — ניתן לסנן לפי סוכן ספציפי או להציג רק לקוחות ללא שיוך. בטולבר בחירה מרובה של משלוחים הוסר כפתור 'העתקה בלי כתובת' — נותר רק כפתור 'העתקה' שמעתיק תמיד עם כתובת. בסנכרון מליונוויל נוסף טוסט מעקב התקדמות חי (X / סה"כ משלוחים) שמתעדכן אחרי כל batch ומוחלף בטוסט הסופי בסיום. נוספה מערכת תוויות למשלוחים — תוויות צבעוניות דינמיות לסינון וארגון ביעד שבת ובדף המשלוח הבודד.
- דף 'שכחת סיסמה?' ייעודי לפורטל הלקוחות
- קישור מהיר בדף הכניסה לפורטל
- אימייל הזמנה אוטומטי גם בעת auto-provisioning ראשוני
- פילטר סוכן במצבת לקוחות — לפי סוכן ספציפי או 'ללא שיוך'
- הסרת דשבורד פיננסי (לא פעיל); כפתור סנכרון מליונוויל הועבר לתפריט פעולות הטבלה עם אייקון ליונוויל
- טולבר בחירה מרובה עוצב מחדש — בר שורה עם כפתורי outline, ספירה וצבעי primary במקום floating dock
- עמודת סוכן בטבלת לקוחות עם עריכה אינליין — Popover + Command לבחירה/חיפוש סוכן ישירות מהתא
- מערכת תוויות: תוויות צבעוניות per-tenant עם יצירה/מחיקה מהפופאובר עצמו
- סינון לפי תוויות בדף יעד שבת — כפתורי פילטר צבעוניים עם ספירות
- הקצאת תווית בבחירה מרובה מהטולבר, ועריכה בדף המשלוח הבודד
v01.04.000שיפורimportminorיבוא משלוחים ללא הגבלת כמות + תיקון UX עריכת שורות
הוסרה ההגבלה של 500 משלוחים ביבוא — המערכת מפצלת אוטומטית לאצוות ברקע.
פרטים נוספים ↓הסתר ↑
הוסרה ההגבלה של 500 משלוחים ביבוא — המערכת מפצלת אוטומטית לאצוות ברקע. בנוסף: תוקן באג שגרם לשורה לקפוץ מהטאב 'שגויות' לטאב 'תקינות' תוך כדי הקלדה — עכשיו הטאב מתעדכן רק בעת יציאה מהתא (blur). תוקן באג במיפוי עמודות AI שגרם לאבד שורות מעבר ל-500 בקבצים גדולים — עכשיו כל השורות עוברות מיפוי. הוסרה חסימת שורות בגלל 'עיר לא מוכרת' — גילוי עיר לא מזוהה ממשיך להוצג כאזהרה חזותית (גבול כתום) אך לא מונע יבוא. תוקנו שלוש בעיות UX: תאים ריקים עם מקף מ-Excel ('-') לא מוצגים יותר כ'-' — מטופלים כריקים; עריכת הגדרות ייבוא (תאריך/דחיפות/פרשות/הערה) נפתחת בחלון קופץ ייעודי מבלי לאבד שינויים בתצוגה המקדימה; בסרגל ההגדרות מוצגות עכשיו גם פרשות יעד.
- ניתן לייבא אלפי משלוחים בפעולה אחת ללא הגבלה
- פיצול אוטומטי לאצוות ברקע — שקוף למשתמש
- שורה לא קופצת טאב תוך כדי הקלדה — רק אחרי יציאה מהתא
- מיפוי AI כעת מכסה את כל שורות הקובץ, לא רק 500 הראשונות
- שורות עם עיר לא מוכרת ניתנות לייבוא — האזהרה הכתומה עדיין מוצגת
- תאים ריקים עם מקף מ-Excel מטופלים כריקים — לא גורמים ל'-1' בעת עריכה
- עריכת הגדרות ייבוא בחלון קופץ — ללא אובדן עבודה בתצוגה המקדימה
- פרשות יעד מוצגות בסרגל ההגדרות בשלב התצוגה המקדימה
- תוקן: הערת יעד לא נשלחת יותר לליונוויל פעמיים — הפרדה נקייה בין destination_notes ל-notes
- תוקן: אצווה לא ממשיכה בשקט לאצווה הבאה אם השרת נכשל — BATCH_SIZE הוקטן ל-150 שורות ויש זיהוי timeout
- שיפור: ייבא מחדש מההיסטוריה טוען רק שורות שנכשלו (לא הכל) — כפתור מציג ספירה ואזהרה ל-timed_out
- שיפור: אחרי ייבוא מחדש מוצלח, יבוא המקורי מסומן אוטומטית עם הערת 'הושלם ע״י ייבוא חוזר' ומוצגת בירוק בהיסטוריה
- תיקון AI: טקסט שיורי בכתובת (שם משפחה, הוראות מסירה) מועבר עכשיו לשדה הערת יעד במקום להימחק
- שיפור AI: זיהוי וקיזום קידומת סוג ישוב (מושב/קיבוץ/כפר/ישוב) משם העיר לפני חיפוש התאמה
- שיפור AI: נרמול טלפון — פורמט בינלאומי (+972), מקפים/רווחים, שני טלפונים בתא
- שיפור AI: הסרת קידומת רחוב/שד' משדה הכתובת, פיצול מספר דבוק לשם הרחוב
- שיפור AI: המרת אות לטינית במספר בניין לעברית (10a→10א), ניקוי סכום COD מסמל מטבע
- שיפור: בורר תאריכים בהיסטוריית פעולות הוחלף ל-DateRangeFilter הסטנדרטי (עם פריסטים וכפתור לוח שנה); סלקט הפעולות תוקן לגובה 7 — תואם את שאר הפקדים בסרגל
- שיפור UX: טופס יצירת משימה — עיצוב מחודש עם sections, PlainFieldLabel ואייקונים; תאריך יעד מפוצל לשדות תאריך+שעה נפרדים; dropdown חיפוש ישות מעוצב כ-combobox עם border רך ו-shadow
- פערי הפצה: עמודת 'עדכון הערה' מציגה תאריך עדכון + אייקון שעון שפותח היסטוריית הערות מלאה (מה נכתב, מי ערך, מתי)
- שיפור UX: דף הייבוא מכסה עכשיו את מלוא גובה המסך — אין חלל ריק מתחת לכרטיסיות ההגדרות
- פיצ׳ר: אפשרות להגדיר כתובת מוצא (עיר / רחוב / מס׳) בהגדרות הייבוא — מועברת לליונוויל כנקודת איסוף לכל המשלוחים בפעולה
v01.03.001שיפורinbox, messagesשדרוג ממשק WhatsApp Inbox
שדרוג מקיף של דף ה-Inbox להיות דומה יותר ל-WhatsApp Web: אווטארים צבעוניים לפי האש של מספר הטלפון, מפרידי תאריכים בין הודעות, מפריד הודעות חדשות, הצגת מדיה (תמונה/אודיו/וידאו/מסמך) ישירות בבועה, textarea שמתרחב אוטומטית,…
פרטים נוספים ↓הסתר ↑
שדרוג מקיף של דף ה-Inbox להיות דומה יותר ל-WhatsApp Web: אווטארים צבעוניים לפי האש של מספר הטלפון, מפרידי תאריכים בין הודעות, מפריד הודעות חדשות, הצגת מדיה (תמונה/אודיו/וידאו/מסמך) ישירות בבועה, textarea שמתרחב אוטומטית, בורר emoji, פאנל פרטי לקוח בלחיצה על האווטאר, כפתור גלילה לתחתית עם מונה הודעות ממתינות, סינון שיחות הכל/לא נקרא, סמל סטטוס הודעה אחרונה ברשימת השיחות, עדכון כותרת הדפדפן עם מספר ההודעות שלא נקראו, והתראות דסקטופ עבור שיחות לא פעילות. בנוסף: שם הנמען נשלף אוטומטית מהמשלוח האחרון אם אין לקוח מקושר, ותווית 'שולח' מציגה את שם השולח של המשלוח בכותרת השיחה.
v01.03.000חדשportal, customersminorשליחת אימייל הזמנה אוטומטית לפורטל הלקוחות
בעת הוספת משתמש חדש לפורטל הלקוחות, המערכת שולחת אוטומטית אימייל הזמנה עם קישור ייחודי להגדרת סיסמה.
פרטים נוספים ↓הסתר ↑
בעת הוספת משתמש חדש לפורטל הלקוחות, המערכת שולחת אוטומטית אימייל הזמנה עם קישור ייחודי להגדרת סיסמה. האימייל נשלח גם בעת חידוש קישור הזמנה שפג תוקפו. הודעת ה-toast ועיצוב הדיאלוג עודכנו בהתאם.
- אימייל הזמנה אוטומטי עם קישור ייחודי ותוקף
- שליחה מחודשת בעת חידוש טוקן
- fallback ידני — הקישור ממשיך להיות מוצג ב-UI
v01.02.011שיפורmanagement, bulk-history, distribution-gapsעיצוב מחדש של דף היסטוריית פעולות — פריסת טבלה סטנדרטית
דף היסטוריית הפעולות עוצב מחדש בהתאם לדפוס הטבלאות הקיים במערכת: סרגל עליון (PageToolbar) עם פילטרים, טבלה מלאת-רוחב עם כותרת דביקה וגלילה פנימית, סרגל תחתון לנאביגציה, עמודת פעולות דביקה בקצה, ורספונסיביות מלאה.
פרטים נוספים ↓הסתר ↑
דף היסטוריית הפעולות עוצב מחדש בהתאם לדפוס הטבלאות הקיים במערכת: סרגל עליון (PageToolbar) עם פילטרים, טבלה מלאת-רוחב עם כותרת דביקה וגלילה פנימית, סרגל תחתון לנאביגציה, עמודת פעולות דביקה בקצה, ורספונסיביות מלאה. הוסרה עטיפת SettingsShell וכותרת הדף. בנוסף, נוספה עמודת הערת מעקב לטבלת פערי ההפצה — נציגי שירות יכולים לרשום הערה על כל פער (לחיצה על אייקון עיפרון פותחת popover לעריכה), ההערה נשמרת בין ימים ומוצגת לכל משתמשי הטנאנט.
v01.02.010שיפורmessages, inbox, shipments, recycle-bin, customers, distribution-gapsאינבוקס רספונסיבי למובייל
דף הצ'אט עבר לתצוגה רספונסיבית: במובייל מוצגת רשימת שיחות בלבד, ולחיצה על שיחה פותחת אותה במסך מלא עם כפתור חזרה.
פרטים נוספים ↓הסתר ↑
דף הצ'אט עבר לתצוגה רספונסיבית: במובייל מוצגת רשימת שיחות בלבד, ולחיצה על שיחה פותחת אותה במסך מלא עם כפתור חזרה. בדסקטופ השתנה הממשק בדיוק כמו קודם. בנוסף, הורחב הצגת מספר חבילות: בבר בחירת משלוחים ("X משלוחים נבחרו"), בכותרת דיאלוג משלוחי לקוח, בסל המיחזור (כותרת ובר תחתון). כמו כן, אוחד מנגנון הסנכרון מליונוויל: כל הטבלאות משתמשות כעת בסנכרון מאוחד דרך `/api/sync-shipment-status` הכולל מעקב התקדמות, מודל לטיפול במשלוחים שנמחקו מליונוויל, ניסיון חוזר לנכשלים, ודיווח על עדכוני שליח.
v01.02.009חדשmessages, inbox, shipments, picking, distribution-gaps, target-express, target-shabbatכפתור שיחה חדשה באינבוקס
נוסף כפתור + בסרגל השיחות באינבוקס המאפשר פתיחת שיחת WhatsApp חדשה עם כל מספר טלפון, ללא צורך שהלקוח ייזום קודם.
פרטים נוספים ↓הסתר ↑
נוסף כפתור + בסרגל השיחות באינבוקס המאפשר פתיחת שיחת WhatsApp חדשה עם כל מספר טלפון, ללא צורך שהלקוח ייזום קודם. בנוסף, בכל מקום שמוצג סיכום מספר משלוחים (בר תחתון של טבלאות) מוצג כעת גם סה"כ חבילות בסוגריים — למשל: 500 משלוחים (521 חבילות). עודכן בדפי: משלוחים, ליקוט, פערי הפצה, יעד אקספרס, יעד שבת.
v01.02.008שיפורbulk-operations, managementשיפור דיאלוג פרטי פעולה ב-bulk history
דיאלוג הפרטים מציג כעת ברקודים של המשלוחים המעורבים, אזור המקור (מאיזה עמוד בוצעה הפעולה), ערכים שהוחלו (metadata) לעומת ערכים קודמים (snapshot) — במקום JSON גולמי אחד.
פרטים נוספים ↓הסתר ↑
דיאלוג הפרטים מציג כעת ברקודים של המשלוחים המעורבים, אזור המקור (מאיזה עמוד בוצעה הפעולה), ערכים שהוחלו (metadata) לעומת ערכים קודמים (snapshot) — במקום JSON גולמי אחד. כל נתיבי ה-bulk מעדכנים את ה-snapshot לכלול barcode לצד שדות השינוי.
v01.02.007שיפורbulk-operations, picking, shipmentsשילוב מלא של כל נתיבי ה-bulk עם מערכת ההיסטוריה
כל פעולות ה-batch במערכת מחוברות כעת למערכת היסטוריית הפעולות: ייבוד יעד שבת, שיבוץ שליח, סימון ליקוט/ביטול ליקוט, שינוי קטגוריית ליקוט, הסרה מרשימת ליקוט, עדכון פריטי ליקוט (כולל שחזור order items מלא), שיבוץ סוכן ללקוח…
פרטים נוספים ↓הסתר ↑
כל פעולות ה-batch במערכת מחוברות כעת למערכת היסטוריית הפעולות: ייבוד יעד שבת, שיבוץ שליח, סימון ליקוט/ביטול ליקוט, שינוי קטגוריית ליקוט, הסרה מרשימת ליקוט, עדכון פריטי ליקוט (כולל שחזור order items מלא), שיבוץ סוכן ללקוח, ותשלום מרובה לעובדים יומיים. נוסף גם סוג פעולה חדש UPDATE_PICKING_ITEMS עם שחזור מלא של order items. בנוסף, שיפורי ביצועים: קריאות ה-DB לסוכן וללקוח בשיוך לקוח לסוכן מקביליות; בדיקת ייחודיות אימייל ווידוא טננט ביצירת משתמש (admin) מקביליות.
v01.02.006חדשmanagement, bulk-operationsminorהיסטוריית פעולות batch עם ביטול נקודתי
מערכת חדשה לרישום ומעקב אחר פעולות batch (פעולות על בחירה מרובה): מחיקה, שחזור, שיבוץ שליח, קביעת יעד, שינוי סטטוס ועוד.
פרטים נוספים ↓הסתר ↑
מערכת חדשה לרישום ומעקב אחר פעולות batch (פעולות על בחירה מרובה): מחיקה, שחזור, שיבוץ שליח, קביעת יעד, שינוי סטטוס ועוד. כל פעולה מוקלטת עם snapshot של המצב הקודם, ובעל החברה יכול לבטל כל פעולה בנפרד — גם אם בוצעו פעולות נוספות אחריה. הדף נגיש מקבוצת ניהול בסידבר (OWNER בלבד). תוקן: המרת driverId ל-number בביטול ASSIGN_DRIVER (גרם לכשל בילד).
- רישום אוטומטי של כל פעולת batch עם snapshot מלא
- ביטול נקודתי — מבטל פעולה ספציפית ללא תלות בסדר הכרונולוגי
- דף היסטוריה עם סינון לפי סוג פעולה וטווח תאריכים
- דיאלוג אישור לפני ביטול עם הסבר על השפעת הדריסה
- הרשאות נפרדות: VIEW ו-REVERT — OWNER בלבד
v01.02.005שיפורnavigation, settingsיומן פעילות הועבר לקבוצת ניהול בסידבר
קישור יומן הפעילות עבר מקבוצת הגדרות לקבוצת ניהול בסידבר — נגיש לבעלים בלבד.
פרטים נוספים ↓הסתר ↑
קישור יומן הפעילות עבר מקבוצת הגדרות לקבוצת ניהול בסידבר — נגיש לבעלים בלבד. זאת בהמשך להעברת הלוגים הטכניים לקונטרול פאנל, כחלק מסידור מחדש של אזורי ניהול ומעקב. בנוסף, שולבו ייעולי ביצועים נוספים: analytics נהגים — טעינת שמות הנהגים ופירוט הביקורים מקביליים (Promise.all); דשבורד סוכן — שאילתות לידים מקביליות לטעינת מזהי לקוחות; הסרת console.log אוטומטית בבילד פרודקשן.
v01.02.003שיפורadmin, debug-logs, securityלוגים טכניים הועברו לקונטרול פאנל
דף הלוגים הטכניים (debug logs) הועבר מממשק הטננט לקונטרול פאנל, תחת /admin/debug-logs.
פרטים נוספים ↓הסתר ↑
דף הלוגים הטכניים (debug logs) הועבר מממשק הטננט לקונטרול פאנל, תחת /admin/debug-logs. כמו כן, נוסף אבטחה ל-API: קריאות GET ו-DELETE ל-/api/debug-log מוגנות כעת ברמת PLATFORM_ADMIN/SYSTEM_DEVELOPER בלבד. POST נשאר פתוח לכתיבה פנימית. הדף הוסר מהסידבר של הטננט.
v01.02.002שיפורshipments-import, attendance, webhooksמדריך עמודות Excel — דיאלוג מקובץ ונקי
רשימת העמודות הנתמכות בייבוא עוצבה מחדש: במקום רשימה שטוחה ומבולבלת, הרשימה עברה לדיאלוג ייעודי מאחורי כפתור 'עמודות נתמכות'.
פרטים נוספים ↓הסתר ↑
רשימת העמודות הנתמכות בייבוא עוצבה מחדש: במקום רשימה שטוחה ומבולבלת, הרשימה עברה לדיאלוג ייעודי מאחורי כפתור 'עמודות נתמכות'. הדיאלוג מקבץ את השדות לפי קטגוריות (פרטי נמען, כתובת, פרטי הזמנה, הערות), מציג תגי חובה/אופציונלי, תיאור קצר לכל שדה, ושמות חלופיים בגופן mono. בנוסף, שופרו ביצועי מסד הנתונים: שאילתות validation במחלקות הפכו למקביליות (Promise.all), מאחסן החגים בלוח השנה עבר לשאילתת batch אחת במקום N+1, ו-webhook dispatch כעת טוען subscription בשאילתה הראשונה ומייתר re-fetch לכל delivery.
v01.02.001תיקוןsettingsתיקון דף עדכונים — שגיאת forwardRef
דף עדכונים ושיפורים קרס בשל העברת רכיב Lucide (forwardRef) כ-prop מ-Server Component ל-Client Component.
פרטים נוספים ↓הסתר ↑
דף עדכונים ושיפורים קרס בשל העברת רכיב Lucide (forwardRef) כ-prop מ-Server Component ל-Client Component. תוקן עם הוספת 'use client'.
v01.02.000חדשshipments-importminorשיפור מקיף להיסטוריית יבוא משלוחים
הוספת אפשרות הורדת קובץ Excel מהיסטוריית יבוא ישן (כולל תיקוני שגיאות שבוצעו לפני הייבוא), כפתור ייבוא מחדש שטוען את הקובץ כולו חזרה לאשף הייבוא, ופילטרים מהירים (חיפוש חופשי, טווח תאריכים, סטטוס).
פרטים נוספים ↓הסתר ↑
הוספת אפשרות הורדת קובץ Excel מהיסטוריית יבוא ישן (כולל תיקוני שגיאות שבוצעו לפני הייבוא), כפתור ייבוא מחדש שטוען את הקובץ כולו חזרה לאשף הייבוא, ופילטרים מהירים (חיפוש חופשי, טווח תאריכים, סטטוס). עיצוב מחודש של פאנל ההיסטוריה — כרטיסיות ברורות עם נתוני סטטיסטיקה ופעולות ייעודיות לכל ייבוא.
- הורדת Excel מתוך היסטוריית ייבוא — כולל תיקוני AI שבוצעו לפני הייבוא
- כפתור 'ייבא שוב' שמטעין את שורות הייבוא הישן חזרה לאשף
- פילטרים: חיפוש חופשי, טווח תאריכים (היום/7/30 ימים/הכל), סטטוס
- עיצוב מחודש: כרטיסיות עם מידע ברור ופעולות נגישות
- תמיכה מלאה בפורטל לקוחות
v01.01.015תיקוןtasksתיקון חלון המשימות במובייל
חלון המשימות הצידי (AdminTaskBellButton) יצא מגבולות המסך במכשירים ניידים בשל רוחב קבוע של 460px.
פרטים נוספים ↓הסתר ↑
חלון המשימות הצידי (AdminTaskBellButton) יצא מגבולות המסך במכשירים ניידים בשל רוחב קבוע של 460px. תוקן ל-w-full max-w-115 — רספונסיבי מלא עד מקסימום 460px.
v01.01.014שיפורcasual-tripsשיפורים במסך נסיעות מזדמנות
תוקנה כתובת שגויה בדף פרטי הנסיעה לביקורי איסוף; badge סוג הביקור עכשיו בעברית; הוסף כפתור 'העתק לינק' ו'שלח WhatsApp' בדף הפרטים; ניתן להוסיף משלוחים לנסיעה פעילה; תוקנה ניווט עמודים מעבר לעמוד 5; ב-BUSINESS_PICKUP כתוב…
פרטים נוספים ↓הסתר ↑
תוקנה כתובת שגויה בדף פרטי הנסיעה לביקורי איסוף; badge סוג הביקור עכשיו בעברית; הוסף כפתור 'העתק לינק' ו'שלח WhatsApp' בדף הפרטים; ניתן להוסיף משלוחים לנסיעה פעילה; תוקנה ניווט עמודים מעבר לעמוד 5; ב-BUSINESS_PICKUP כתובת יעד כעת אופציונלית.
v01.01.013חדשcasual-tripsיצירת משלוח חדש מתוך מסך הנסיעות הזמניות
נוסף מצב 'משלוח חדש' שמאפשר ליצור משלוח ב-Lionwheel וטיול בפעולה אחת, ישירות ממסך הנסיעות הזמניות.
v01.01.012תיקוןuiתיקון לחיצה על Combobox בתוך Drawer
תוקנה בעיה שגרמה ל-Combobox לא להגיב ללחיצות כאשר הוא רץ בתוך Vaul Drawer.
v01.01.011תיקוןcasual-tripsהצגת כתובת איסוף בעמוד השליח
תוקנה בעיה שגרמה לכתובת האיסוף להיעלם בעמוד השליח בנסיעות זמניות.
v01.01.010תיקוןdriversשחזור נהג זמני בחיפוש שיבוץ
תוקנה בעיה שהסתירה נהגים זמניים מרשימת השיבוץ בעקבות כפילות שנפתרה ברמת ה-DB.
v01.01.009חדשmapsכרטיס מידע על מסמני מסלול במפה
לחיצה על מסמן מסלול בעמוד הנהג פותחת כרטיס מידע מפורט עם פרטי העצירה.
v01.01.008שיפורtasksminorעיצוב מחדש מלא של פאנל המשימות
פאנל המשימות עוצב מחדש מהיסוד: כותרת גרדיאנט, מסנני תאריך ועדיפות, רשת 8px עקבית, פאנל רחב יותר ומצב ריק משופר.
פרטים נוספים ↓הסתר ↑
פאנל המשימות עוצב מחדש מהיסוד: כותרת גרדיאנט, מסנני תאריך ועדיפות, רשת 8px עקבית, פאנל רחב יותר ומצב ריק משופר.
- כותרת גרדיאנט עם מידע על המשימה הפתוחה
- סינון לפי תאריך יעד ורמת עדיפות
- עיצוב עקבי על בסיס רשת 8px
- מצב ריק מעוצב עם הנחיה לפעולה
v01.01.007שיפורalertsעיצוב מחדש של כרטיס שגיאת מיון
פריסת כרטיס שגיאת המיון עוצבה מחדש לנראות ובהירות טובות יותר.
v01.01.006ייעולcleanupייעול Dry-Run בניקוי — ספירה מיידית מה-DB
ה-dry-run של כלי הניקוי עובר כעת ספירה מיידית מה-DB ללא קריאות ל-Lionwheel, ובלי timeout.
v01.01.005חדשdistribution-gapsסף פערי חלוקה ברמת Tenant
נוספה אפשרות להגדיר ברמת ה-Tenant ערכי ברירת מחדל לפערי חלוקה ולסף קריטי.
v01.01.004תיקוןimportשיפור תיקון כתובות AI
תיקון AI לכתובות בודק כעת גם שדה כתובת לעיר מוסתרת, שולח את כל כינויי הישוב ומנרמל שם קנוני בעת יישום.
v01.01.003חדשcod-trackingאירועי COD משוקפים להיסטוריית משלוח
אירועי ביקורת של מעקב גוביינא משוקפים כעת להיסטוריית פעולות המשלוח.
v01.01.002תיקוןimportפאנל פעילות אחרונה ממלא גובה זמין
תוקנה בעיה שגרמה לפאנל הפעילות האחרונה שלא לגדול לאורך המלא בזמן ייבוא.
v01.01.001שיפורmapsאיחוד עיצוב מסמנים ושיפור UX פופאפ במפה
עיצוב המסמנים במפה אוחד ו-UX הפופאפ שופר לחוויה עקבית יותר.
v01.01.000חדשplatformmajorהשקת Shipnest — מעבר לתשתית ייצור מלאה
המערכת עברה ריברנד מלא מ-send-whatsapp ל-Shipnest ועלתה לאוויר בדומיין shipnest.io.
פרטים נוספים ↓הסתר ↑
המערכת עברה ריברנד מלא מ-send-whatsapp ל-Shipnest ועלתה לאוויר בדומיין shipnest.io. הועברה כל התשתית ל-Vercel, Namecheap ו-Cloudflare. מהגרסה הזו כל ה-backend רץ כ-Next.js API routes בלבד — ה-NestJS הישן הוסר לחלוטין.
- דומיין ייצור: shipnest.io (Namecheap + Cloudflare + Vercel)
- מעבר מלא ל-Next.js API routes — ללא NestJS
- ריברנד מלא: לוגו, שם, צבעים
- Multi-tenancy יציב עם הצפנת סודות AES-256-GCM
- Public REST API עם bearer token ו-rate limiting
- אינטגרציות: Lionwheel, Summit, WhatsApp Cloud API