פרק כג: עושים סדר: חלק ב: שדרוגים שונים, פונקציונאליות לחצני רשומה, ורגע למחשבה.
תזכורת:
בפרק הבא ניצור גם את טופס כרטיס המשפחה, ונוסיף פונקציונאליות ללחצנים שיצרנו היום.
גילוי נאות: בפרק זה עדיין לא נוסיף פונקציונאליות ללחצנים הנ"ל, אלא ללחצנים אחרים שניצור.
שלב א: שדרוג כרטיס המשפחה
בואו ניזכר, כי בפרק יד יצרנו כבר טופס בשם frmFamily המציג את פרטי המשפחה, אם כי הוא לא מעורר תיאבון במיוחד:

שינוי השם
ראשית נשנה את שמו לשם חדש: frmFamilyCard (כרטיס משפחה), שמתאים יותר לתכנית שלנו מהפרק הקודם:

הצגת מידע על התרומות המשוייכות
מלבד תצוגה של כל פרטי המשפחה, אני רוצה להציג בטופס זה גם סיכום קצר על התרומות שמשוייכות למשפחה זו.
כבסיס, אפשר להשתמש בשאילתת סיכום התרומות למשפחה שיצרנו בעבר: qryFamily_DonationSummary, אבל היא לא מספיק מפורטת, כי היא מציגה רק את סכום כל התרומות למשפחה, מבלי להתייחס לסטטוס התרומות.
לדוגמא, עבור משפחת אשרי, מופיע שקיימות 2 תרומות על סך כולל 300 ₪:

אבל אין שום מידע על סטטוס התרומות, מה בוצע ומה לא.
לצורך המחשת העניין, נפתח את טבלת התרומות ונשנה את הסטטוס של התרומה הראשונה ל-בוצעה:

כעת למשפחת אשרי יש 100 ₪ שבוצעו ו-200 ₪ שממתינים לביצוע, אך שאילתת הסיכום הנ"ל לא מציגה שום מידע על השינוי שעשינו כעת:

שלב ב: שדרוג שאילתת סיכום התרומות
בואו נשדרג את שאילתת הסיכום, כך שתציג עוד ארבע עמודות חדשות:
- AwaitingDonationCount מספר התרומות הממתינות לביצוע (=המיועדות)
- AwaitingDonationSum סכום התרומות הממתינות לביצוע (=המיועדות)
- DoneDonationCount מספר התרומות שבוצעו בפועל
- DoneDonationSum סכום התרומות שבוצעו בפועל
נתחיל ע"י פתיחת השאילתה בתצוגת עיצוב, וניצור שדה חדש בשם AwaitingDonationCount:

אבל מה תהיה הנוסחה שבה נשתמש?
יש כאן קושי מסויים, כי לחישוב השדה הזה אי אפשר להשתמש בפונקצית Count רגילה, מפני שאז נקבל את מספר כל הרשומות, ואנו מעוניינים לקבל רק את מספר התרומות שמיועדות אך לא בוצעו עדיין, דהיינו עם סטטוס=2.
הפתרון הוא ליצור שדה עם ביטוי מחושב. נפתח את הבונה:

ונרשום את הנוסחא הבאה:
AwaitingDonationCount: Sum(IIf([StatusID]=2,1,0))
הסבר: נוסחא זו מחשבת סכום לפי תנאי IIF: עבור כל רשומה, אם StatusID=2 (התרומה מיועדת), מוסיפים לסכום את הערך 1, אחרת – מוסיפים 0.
נסגור את הבונה, ונשנה את המאפיין "סך הכל" מ-"קבץ לפי" ל-"Expression", כדי להורות לשאילתה שכאן מדובר בביטוי מחושב ולא בשדה רגיל:

נעבור לתצוגת גליון נתונים ונראה כי הערך המופיע עבור משפחת אשרי הוא 1, דהיינו שיש לה תרומה אחת משוייכת:

יפה. כעת ניצור את השדה השני: סכום התרומות המשוייכות - בצורה דומה:
נוסיף שדה חדש בשם AwaitingDonationSum, אך הפעם במקום להוסיף לסכום המחושב את הערך 1, נוסיף את סכום התרומה הנוכחית ברשומה:
AwaitingDonationSum: Sum(IIf([StatusID]=2,[Amount],0))
ושוב, נשנה את סוג השדה ל-Expression.
כעת מוצג גם הסכום של התרומות המיועדות:

באופן דומה נוסיף את שתי העמודות הנותרות, עם הנוסחאות הבאות:
מספר התרומות שבוצעו בפועל: DoneDonationCount: Sum(IIf([StatusID]=3,1,0))
סכום התרומות שבוצעו בפועל: DoneDonationSum: Sum(IIf([StatusID]=3,[Amount],0))
הערה: למרות שאפשרי (ואפילו יותר יעיל), לחשב את שתי השדות האחרונים בלי להשתמש בעוד פונקציית סכום, אלא פשוט על ידי ההפרש בין שתי השדות הקודמים, דהיינו:
DoneDonationCount: TotalDonationCount - AwaitingDonationCount
לא כדאי לנו לעשות כן, כי אם נחליט אי פעם להוסיף למערכת ערך סטטוס תרומה חדש, למשל: "בוטלה" (כשהתרומה הוקלדה בטעות, או שהבנק לא אישר את ההעברה), השדה המחושב יתעלם מסטטוס זה, ועלול להציג ערך שגוי.
והנה התוצאה הסופית:

שלב ג: יצירת טופס משנה לתרומות
כעת ניצור טופס המציג את השדות, נתחיל כמו בעבר על ידי בחירת השאילתא ויצירת טופס אוטומטי:

ונבצע את השינויים הבאים:
בתיבות הטקסט: הוספת קידומת "txt", הסרת הגבול, הקטנת הרוחב ל-3 ס"מ.
בטופס: מחיקת תיבת הטקסט והתווית של משפחת היעד, הסרת בוררי הרשומות, הסרת לחצני הניווט, שינוי הכותרת, הקטנת הרוחב ל-8 ס"מ.
התוצאה:

שמרו את הטופס בשם: frmFamily_DonationSummary.
נוסיף אותו כטופס משנה לכרטיס המשפחה, על ידי גרירה:

נעבור למאפייני פקד טופס המשנה, ונראה שאקסס לא ביצעה קישור בין נתוני טופס המשנה לטופס האב:

מדוע המאפיינים ריקים?
כשהטפסים מבוססים על טבלאות, ויש בין הטבלאות קשרי גומלין, אקסס מזהה את השדה המקשר ומגדירה את הקישור אוטומטית. אך כאן טופס המשנה מבוסס על שאילתה ולא על טבלה, ולכן לא זכינו לפינוק הזה.
נו טוב, נעשה זאת בעצמנו, אבל אל דאגה, אקסס עוזרת לנו גם כאן, ומספקת מנגנון פשוט לקישור בין הטפסים.
נבחר את אחד המאפיינים קישור שדות אב או קישור שדות צאצא, ונלחץ על שלש הנקודות:

ייפתח החלון הבא:

הופה!
אקסס ניסתה לנחש את השדות המקושרים ו... הצליחה!
יתרה מכך, אקסס מתארת עבורנו את התוצאה שתתקבל מקישור זה:

נשמע טוב.
לנו נשארה המלאכה המפרכת ללחוץ על אישור... ואקסס תעדכן את המאפיינים:

נסיים את העבודה על הטופס הזה:
- נשנה את העוגן האופקי של פקד טופס המשנה ל: ימין, כך שיוצג בצמוד לפרטי המשפחה:

- נסיר את השדה המזהה ID.
- נעביר את תיבת הטקסט של CreatedOn לראש, ונשנה את המאפיין "נעול" ל-כן, והמאפיין "אפשר" ל-לא. כך נמנע מהמשתמש לבצע בה שינוי בטעות. אני גם אוהב להסיר לגמרי את הגבול מתיבות טקסט לקריאה בלבד, ולכן אעשה גם את זה.
- נשנה את הכיתוב בתוויות.
- נוסיף עוד שני לחצנים בכותרת, אחד בשם cmdNew לפתיחת כרטיס חדש, ואחד בשם cmdDelete למחיקת הכרטיס הנוכחי.
הנה התוצאה:

נעבור למשפחת אשרי ונוודא שטופס המשנה מתעדכן נכון כשעוברים ממשפחה למשפחה, ומציג את פרטי התרומות הנכונים:

נפלא!
שלב ד: הוספת פונקציונאליות בלחצני כרטיס המשפחה
נבחר את לחצן יצירת כרטיס חדש cmdNew שיצרנו הרגע, ובמאפיינים בלשונית "אירוע", נבחר את האירוע: "בעת לחיצה", ונבחר את האפשרות "פרוצדורת אירוע":

נלחץ על שלושת הנקודות …
אקסס תפתח את עורך הקוד, ותיצור שלד לפונקציית האירוע.

שימו לב לשם הפונקציה: הוא מתחיל בשם הפקד cmdNew, ומסתיים בסיומת Click כסימן שהפונקציה משוייכת לארוע הלחיצה של הפקד.
נרשום את הקוד הבא:
Private Sub cmdNew_Click()
DoCmd.GoToRecord , , acNewRec
End Sub
הסבר הקוד:
בשפת VBA קיימים אובייקטים שונים.
אובייקט הוא מושג בסיסי במדעי המחשב המתאר משתנה, פונקציה, מבנה נתונים או שילוב של הנ"ל.
אחד האובייקטים הנכבדים באקסס הוא DoCmd שמאגד בתוכו פונקציות רבות ומגוונות, כגון שליטה על חלונות וטפסים.
אחת מהפונקציות שהוא מכיל היא: GoToRecord שתפקידה, כמובן, לבצע מעבר בין רשומות.
ניתן לספק לה 4 פרמטרים, וכולם אופציונאליים.
אנחנו דילגנו על שני הפרמטרים הראשונים על ידי ציון של פסיק, וסיפקנו רק את הפרמטר השלישי: acNewRec שפירושו "רשומה חדשה".
נחזור לאקסס על ידי Alt+F11, נעבור לתצוגת טופס, ונבדוק שלחיצה על הלחצן מעבירה אותנו לרשומה חדשה.
פשוט וקל!
כעת ללחצן המחיקה
נחזור לתצוגת עיצוב, נבחר כעת את לחצן המחיקה, וניצור פונקציית אירוע גם עבור ארוע הלחיצה שלו:

הפעם נרשום את הקוד הבא:
Private Sub cmdDelete_Click()
DoCmd.RunCommand acCmdDeleteRecord
End Sub
הסבר: כאן אנו קוראים לפונקציה אחרת (באובייקט DoCmd) ששמה RunCommand, ומספקים לה פרמטר אחד: acCmdDeleteRecord, שפירושו: מחיקת הרשומה הנוכחית.
בואו נבדוק את הלחצן. נחזור לאקסס Alt+F11, נעבור לתצוגת טופס וננסה למחוק את משפחת ראובני...
אופס! אקסס תציג את הודעת השגיאה הבאה:

מה קרה? שימו לב ללשון ההודעה.
בעצם הכל תקין. אקסס לא מאפשרת לנו למחוק את הרשומה, ויש לכך סיבה טובה מאוד! אנו מנסים בעצם למחוק את הרשומה של משפחת ראובני מהטבלה Family, אך ניזכר כי הטבלה Family מקושרת בקשר גומלין לטבלת הזוגות Family_Donor, וזו מכילה רשומות השייכות למשפחת ראובני (כי ישנם תורמים המשוייכים למשפחה זו).
כיון שבהגדרת קשרי הגומלין, הגדרנו לאכוף את שלימות קשרי הגומלין בין הטבלאות, אם נמחק את משפחת ראובני רק מטבלת המשפחות, נהפוך את כל הרשומות של משפחת ראובני בטבלה Family_Donor לרשומות יתומות, כי אין להם שדה אב מתאים בטבלת Family. בסיס הנתונים יכיל נתונים לא שלמים.
אקסס דואגת שלא נגיע לידי כך, וזה דבר טוב.
אז מה עושים?
מחיקות הירארכיות
כדי לאפשר מחיקה של רשומה בטבלת המשפחות, אנו חייבים להורות בפירוש לאקסס למחוק גם את כל הרשומות הקשורות אליה בשאר הטבלאות, בהתאם להירארכיית הקשרים. לשם כך נערוך את קשרי הגומלין בין הטבלאות.
נפתח את עורך קשרי הגומלין ונקליק פעמיים על החץ המקשר בין הטבלאות Family ו-Family_Donor:

ייפתח חלון עורך קשרי הגומלין, ונסמן ב-V את האפשרות: מחק רשומות קשורות זו לזו בהתאם להירארכיית הקשרים:

נלחץ על אישור.
זהו.
כעת ברגע שנמחק משפחה, אם יש לה תורמים משוייכים, אקסס פשוט תמחק אוטומטית את הרשומות המתאימות גם בטבלה Family_Donor.
רגע, חושבים!
שימו לב כי הטבלה Family מקושרת בקשרי גומלין גם לטבלת התרומות Donation, וגם שם בחרנו לאכוף את שלמות הקשרים בין הטבלאות.
לכאורה זה אומר שפתרנו רק חצי בעיה, כי אם תהיה למשפחה גם תרומה משוייכת – לא נוכל למחוק אותה.
מה הבעיה, אפשר להגדיר מחדש גם את קשר הגומלין הזה, וכאשר נמחק משפחה - לאפשר לאקסס למחוק את התרומות המשוייכות אליה, לא?
אז זהו, שממש לא.
חישבו, האם באמת כדאי לנו לאפשר מחיקה של נתוני תרומות? ומה אם נרצה להוציא דו"ח על כל התרומות שעברו בעמותה שלנו? ואם נצטרך להגיש דוח למס הכנסה?!

לכן - לא ניגע בקשר הגומלין הזה. ואם יש למשפחה תרומה משוייכת - לא נאפשר מחיקה שלה בכלל.
מכאן עולה המסקנה הבאה:
שימוש במחיקות הירארכיות הוא מנגנון מסוכן, ומצריך מחשבה מקדימה. למעשה ישנם מתכנתים הנמנעים מלאפשר זאת על ידי קשרי הגומלין, ומעדיפים להשאיר את האחריות בידיים שלהם. כאשר הדבר נצרך, כגון הכא - הם מבצעים מחיקה של הרשומות בטבלאות הקשורות, על ידי קוד, ורק לאחר שבע בדיקות שאין כאן טעות.
ומדוע אפשרנו זאת במקרה של התורמים המשוייכים?
מפני שטבלת הזוגות Family_Donor בסך הכל מציינת קשר גומלין של רבים לרבים, ומחיקת רשומה ממנה אינה משפיעה על טבלת נתוני התורמים בשום צורה, ובוודאי שאינה מוחקת שום תורם, לכן היא בטוחה.
המשך יבוא אי"ה...