אקסס למתחילים: יצירת מערכת לניהול תורמים
-
חלק יד: פתרונות לשיעורים הקודמים ויישום של התיבה המשולבת
צור שני טפסים חדשים, אחד עבור המשפחות, ואחד עבור טבלת הזוגות.
טופס המשפחות
שני קליקים וסיימנו:
שימו לב טוב שאקסס הוסיפה עבורנו בתחתית הטופס פקד המכיל טבלה. מזהים? זו רשימת התורמים שמשוייכים למשפחה הזו בטבלת הזוגות Family_Donor.
אקסס זיהתה לבד את סוג הקשר שיש בין טבלת המשפחות וטבלת התורמים (רבים לרבים) ובחרה ליצור עבורנו את התצוגה הזו, כדי שנוכל לנהל בקלות את היחס ביניהם. חכם מצידה.
טופס הזוגות:
לכאורה כמו קודם, נבחר את הטבלה, נלחץ על הכפתור ליצירת טופס - והנה:
אבל רגע.
שימו לב שזה טופס שמציג בכל פעם רשומה אחת בלבד. זה מאוד לא נוח לעבודה שלנו.הייתי רוצה טופס שמציג את כל הרשומות, שורה אחר שורה, כך הרבה יותר קל לנהל את רשימת הזוגות. לשם כך ניצור טופס טבלאי. זהו טופס שמכיל רשומות מרובות, שורה אחר שורה.
בואו נמחק את הטופס שיצרנו, והפעם נשתמש באשף ליצירת טופס טבלאי.
כמו קודם, ראשית נבחר את הטבלה Familiy_Donor, אך הפעם נבחר מתפריט העיצוב: "טפסים נוספים" > "פריטים מרובים":
נקבל את הטופס הזה:
נשמור אותו בשם frmFamily_Donor.
יופי. הלאה.
צרו שאילתא חדשה: qryFamily_FullName ובה שני שדות: המזהה של המשפחה ID, ושדה מחושב FamilyFullName שמחבר את שם המשפחה, שם האב, וא"ו החיבור ושם האם.
פתרון:
עברית קשה שפה:
יש כאן חלק מעצבן עם וא"ו החיבור, כששמים אותה באותה מחרוזת עם תו הרווח, זה נראה כאילו האות קודמת לרווח. אהבתי את הפתרון היצירתי של @בערל, להוסיף את הרווח בנפרד ואחר כך את האות וא"ו.
שתי נקודות!בטבלה Family_Donor שנו את הגדרות השדה FamilyID כך שיציג (בתיבה משולבת) את שם המשפחה המלא במקום את המספר FamilyID (השתמשו כמובן בשאילתא שהכנתם בסעיף א), ואת הגדרות השדה DonorID כך שיציג את שם התורם.
הנה מאפייני השדה FamilyID:
מאפייני השדה DonorID כמעט זהים, אלא שמקור השורה שם הוא שונה, כמובן, דהיינו qryDonor_FullName.
והנה התוצאה:
קריא וברור!אבל יש בונוס: הואיל וביצענו שינוי בהגדרות תצוגת השדה DonorID בטבלת הזוגות, כך שיציג תיבה משולבת ולא תיבת טקסט פשוטה, כעת השתנתה גם התצוגה בטופס המשפחה לתיבה משולבת, ושם התורם מופיע במלואו:
כמה קל עכשיו לשייך תורמים למשפחה!אבל משום מה בטופס הזוגות, התצוגה לא השתנתה, ומוצגים שם רק המזהים של המשפחות והתורמים, בתיבת טקסט פשוטה ולא בתיבה משולבת. תראו:
למה?
כי יצרנו את הטופס לפני ששינינו את הגדרות התצוגה לשדות אלו.
מה נעשה?
אפשר למחוק את הטופס וליצור שוב. הפעם אקסס תזהה את הגדרות התצוגה העדכניות של השדות ותיצור תיבות משולבות. זה הפתרון הכי קל.אני לא רוצה לבחור בדרך ההרסנית הזו... כי מה קורה אם כבר בינתיים הוספנו עוד דברים בטופס, כגון לשנות את הכותרת שלו לדבר יותר קריא, למשל:
ומה אם כבר נקשרנו לטופס הזה, ואנחנו רוצים לחוס על חייו?בואו נלמד עכשיו איך אפשר להגדיר את השינוי בתצוגה באופן ידני בטופס הקיים, מבלי לאבד את מעשה ידינו עד כה.
פתחו את הטופס בתצוגת עיצוב.
הקליקו קליק ימני על תיבת הטקסט של FamilyID > שנה ל: > תיבה משולבת:
תיבת הטקסט הפכה לתיבה משולבת. עכשיו נגדיר את המאפיינים של התיבה המשולבת:
- בכרטסת נתונים הגדירו את מקור השורה להיות: qryFamily_FullName:
2.בכרטסת תבנית הגדירו את מונה העמודות להיות 2, ואת רוחב העמודות ל 4;0:
(נשמע מוכר, לא?)
אני משאיר לכם להבין איך הפכתי גם את תיבת הטקסט השנייה של DonorID לתיבה משולבת.
הנה התוצאה (לאחר קוסמטיקה קלה של הרחבת העמודות בטופס):
את הפוסט הבא נקדיש לטיפול יסודי ולכמה שינויים בטבלת התרומות ובטופס של התרומות, כך שנוכל לשייך כל תרומה חדשה למשפחה, כפי שתכננו.
בתור הכנה לשלב הבא:
- הוסיפו שדה חדש לטבלת התרומות, בשם: TargetFamilyID, מטיפוס מספר שלם ארוך, שיכיל את המזהה של המשפחה שעבורה מיועדת התרומה.
- קיבעו את ערך ברירת המחדל לשדה זה להיות NULL במקום 0 (למתבוננים: מדוע?)
- הגדירו את קשרי הגומלין עבור שדה זה.
- בכרטסת נתונים הגדירו את מקור השורה להיות: qryFamily_FullName:
-
חלק טו: שדרוג התרומות
קודם כל פתרונות:
- הוסיפו שדה חדש לטבלת התרומות, בשם: TargetFamilyID, שיכיל את המזהה של המשפחה שעבורה מיועדת התרומה.
- קיבעו את ערך ברירת המחדל לשדה זה להיות NULL במקום 0.
פתרון:
(למתבוננים: מדוע?)
כי אין משמעות לערך 0. הרי מעולם לא תהיה משפחה עם ID 0, לכן יותר מדוייק להקצות את NULL, שפירושו: כלום.
הערה: גם אם אקסס לא מאפשרת ערך 0 בשדה מספור אוטומטי, והוא תמיד מתחיל מ-1, יש מערכות DB שמאפשרות ליצור שדה מספור אוטומטי שמתחיל מ-0 (או אפילו מערך אחר). במקרה כזה הנתונים שלנו עלולים להטעות אותנו!
גם לגבי פונקצית ספירה Count, שדה המכיל ערך 0 - נספר, בעוד שדה המכיל NULL - לא נספר.בתור פרקטיקה טובה, נשתמש בכלל הבא:
בכל מקום שאין משמעות לערך ברירת המחדל 0, נעדיף את NULL.אם אנחנו כבר כאן, אז על הדרך בואו נשנה גם את המאפיינים של השדה כך שיציג את שם המשפחה המיועדת כתיבה משולבת:
- הגדירו את קשרי הגומלין עבור שדה זה.
פתרון:
יש לנו הרבה עבודה היום, אז קדימה:
נפתח את טופס התרומות, ונראה כי שדה המשפחה המיועדת אינו קיים בו כלל, לכן פשוט נמחק את הטופס.
הערה: הפעם נמחק וניצור טופס חדש, כי הטופס הקיים הוא בתצוגת גליון נתונים, משולל כל עיצוב וחן, וממילא לא נפסיד כלום אם פשוט ניפטר ממנו.
הנה הטופס החדש, שימו לב לשדה של משפחת הייעד:
הערה: בפעם הראשונה שיצרנו את טופס התרומות, שינינו את תבנית העיצוב שלו לגליון נתונים. שימו לב שהפעם, בניגוד לטופס הקודם, נשאיר את הטופס בתבנית שלו, שמציגה רשומה בודדת.
הסיבה לכך היא כי אני מעוניין להשתמש בטופס הזה כדי להקליד נתונים. ובדרך כלל מכניסים תרומה בודדת בכל פעם.
תיכף ניצור טופס נוסף עבור התרומות, אשר יהיה בתצוגת גליון נתונים, המציגה רשומות מרובות.נשמור את הטופס החדש בשם frmDonationCard.
נחזור על השלב האחרון שוב, ניצור טופס לתרומות כמו קודם, אך הפעם נשנה את תבנית העיצוב שלו לגליון נתונים, וכן נמחק את הפקדים של ID ו- DonorID:
את הטופס השני נשמור בשם: frmDonationList.
עדכון הטופס של התורם
נסו לפתוח את טופס התורמים frmDonor. תקבלו הודעת שגיאה:
השגיאה נוצרה כי מחקנו את הטופס frmDonation, שהופיע כטופס משנה בטופס התורם, ואקסס לא מוצאת אותו.
לחצו על אישור, שימו לב כי נותר חלל ריק במקום שבו היה טופס המשנה:
בואו נתקן את הבעיה. נעבור לתצוגת עיצוב (אקסס שוב תתריע על השגיאה. נלחץ אישור)
נבחר את פקד טופס המשנה:
ובמאפיינים, נשנה את אובייקט המקור של הפקד להיות frmDonationList:
שימו לב שקישור שדות האב עדיין מכיל את הערך ID, וקישור שדות הצאצא עדיין מכיל את הערך DonorID:
הערה חשובה: אני מקווה שחלקכם מרימים גבה ושואלים:
כיצד יתכן שטופס המשנה יהיה מקושר על ידי השדה DonorID? והלא מחקנו את הפקדים של DonorID מטופס המשנה!
שאלה מצויינת!
התשובה היא, שלמרות שמחקנו את הפקדים מן הטופס - לא מחקנו את השדה DonorID עצמו.
הטופס מכיל את ערכי כל השדות של הטבלה שעליה הוא מבוסס, גם אם הוא אינו מציג את כולם.
הפקדים משמשים רק לתצוגה של השדות, ומחיקת פקד מאוגד אינה מוחקת את השדה שאליו הוא מאוגד, בדיוק כמו שסגירת התריס אינה מסירה את השמש מן הרקיע.נעבור לתצוגת טופס ונראה שהכל תקין:
נסגור ונשמור.עדיין חסרה לנו פונקציה עיקרית וחשובה.
הגבלת רשימת התורמים לתורם
נזכר באפיון המערכת שהגדרנו בחלק יב:התורם הנדיב, בבואו לקיים מצווות צדקה, יוכל לבחור מתוך שתי אפשרויות של תרומה:
א. לייעד את התרומה עבור משפחה מסויימת, מתוך רשימת המשפחות שמשודכות אליו.
או
ב. לתרום תרומה כללית לקופה, ללא ייעוד למשפחה מסויימת. במקרה הזה, תהיה לי האפשרות, כמנהל של המערכת (נרו יאיר), לייעד את התרומה לאיזו משפחה שארצה.נתחיל באפשרות א:
רצינו שכל תורם יוכל לשייך את תרומותיו רק אל המשפחות המשוייכות אליו, וכרגע מוצגות לכל תורם כל המשפחות הקיימות.
לשם כך אנחנו רוצים לסנן את התיבה המשולבת של TargetFamilyID שבטופס המשנה frmDonationList, כך שתציג רק המשפחות המשוייכות לתורם הנוכחי בטופס האב frmDonor.אבל קודם כל, נפתח את הטופס frmFamily_Donor ונמלא קצת יותר נתונים:
המשפחות "ראובני אברהם ושרה" וכן "שמעוני יצחק ורבקה" מקושרות לכל התורמים. לעומתן, המשפחות "זבולוני יעקב ולאה", וכן "אשרי אדם וחוה", משוייכות כל אחת רק לתורם אחד, כדלקמן:
שלב א: יצירת שאילתת עזר לתיבה המשולבת
כרגע, התיבה המשולבת מכילה רק נתונים לגבי המשפחות, ולא לגבי התורמים המשוייכים אליהן. כדי להציג רק בתיבה המשולבת רק את המשפחות המשוייכות לתורם הנוכחי, אנו צריכים קריטריון לסינון.למעשה, אנו מבקשים להציג שלש שדות:
ID, FamilyFullName
מתוך השאילתה qryFamily_FullNameDonorID
מתוך הטבלה Family_Donor
לשם כך ניצור שאילתת עזר, שמצרפת את שתי הטבלאות הנ"ל.
הערה: מי שאזניו רגישות (או יותר נכון, עיניו) שם לב שאני לפעמים קורא לשאילתה בשם "טבלה". מבחינת אקסס, אין הבדל עקרוני בין השניים. ניתן לבסס טפסים הן על שאילתות והן על טבלאות, ניתן לבסס שאילתות על שאילתות וכו'.
יצירה > עיצוב שאילתה > ונבחר להציג את הכל:
יתכן והתצוגה בגרסת אקסס שלכם שונה מעט. הפאנל האפור שבתמונה נכנס רק לאחרונה כשדרוג לאקסס.עובדה מעניינת:
למרות שבשנת 2017, כשאקסס הגיעה לגיל המופלג של 25 שנה, מייקרוסופט הודיעו קבל עם ועדה כי הם מתכוונים לזנוח סופית את אקסס, (ואף קבעו לה תאריך פטירה), בשקט אלגנטי הם שינו את דעתם, הוציאו גרסאות חדשות בשנים 2019 ו-2020, ועדיין ממשיכים לעדכן את אקסס.נחזור לענייננו.
נוסיף למשטח העבודה את הטבלה Family_Donor, ואת השאילתא qryFamily_FullName.
נגדיר את הצירוף הפנימי בין השדות ID ו-FamilyID, ונוסיף לשאילתא את השדות לפי הסדר הבא:- ID
- FamilyFullName
- DonorID
נעבור לתצוגת עיצוב ונראה שליד כל משפחה מופיעה גם עמודת התורם המשוייך אליה:
מעולה.נשמור את השאילתה בשם: qryFamily_FullName_Donor.
שלב ב: החלפת מקור השורה של התיבה המשולבת בטופס המשנה
נפתח את טופס המשנה qryDonationList בתצוגת עיצוב, נסמן את התיבה המשולבת של משפחת הייעד, ובמאפיין מקור שורה נמחק את הכתוב לגמרי, כך:
כעת נלחץ על שלושת הנקודות משמאל למקור השורה:
אקסס תכניס אותנו למסך עורך השאילתות. נוסיף למשטח העבודה את שאילתת העזר שלנו: qryFamily_FullName_Donor, ונוסיף את השדות לפי הסדר כמקודם:
כעת לשלב המכריע:
נעמוד על הקריטריון של DonorID, ונפתח את הבונה:
- בחלונית שנפתחה, נבחר מצד ימין את הקובץ של אקסס שלנו > Forms > כל הטפסים > frmDonorCard. (טופס האב)
- מקטגוריות הביטויים נבחר את ID. (מזהה התורם)
- ומתוך ערכי הביטויים נקליק פעמיים על "<ערך>" (הפריט הראשון)
אקסס תבנה עבורינו את הביטוי הבא:
[Forms]![frmDonor]![ID]
להלן ממוספרים כל שלבי התהליך:
נלחץ על אישור ונסגור את עורך השאילתות.
שימו לב לקריטריון שנוסף לעמודה DonorID:
הנתיב
[Forms]![frmDonor]![ID]
מציין את השדה ID בטופס frmDonor.פירוש הדבר הוא שאקסס תסנן עבורנו את תוצאות השאילתא, ותציג רק את הרשומות שבהם DonorID זהה לשדה ID שמופיע בטופס frmDonor.
יתר על כן, אקסס תדאג לכך שכל פעם שערך השדה ID ישתנה בטופס האב (דהיינו כשנציג תורם אחר) - השאילתה תרוץ מחדש, תעדכן בטופס הצאצא את התיבה המשולבת המבוססת על השאילתא הזו, ותספק לנו נתונים עדכניים לתורם החדש.הערה: כבר הזכרנו לעיל שאקסס משלבת בין בסיס נתונים, וממשק משתמש בצורה הכי חלקה וקלה שאפשר. היכולת של אקסס לערבב בין נתונים של שדות טופס לבין קריטריונים של שאילתות מקנה לה עוצמה רבה, והיא המאפשרת לנו ליצור את הלוגיקה העסקית של המערכת, מבלי לכתוב שורה אחת של קוד.
וזה רבותי - פשוט מדהים.כעת נסגור את עורך השאילתות, על ידי האיקס למעלה:
אקסס תשאל האם אנו רוצים לשמור את השינויים שעשינו במאפיין של מקור השורה:
נבחר כן.
נסגור את תצוגת העיצוב של הטופס, ונשמור כמובן את השינויים שעשינו בו.נפתח את טופס האב frmDonor, ונראה לאילו משפחות ניתן כעת לשייך את התרומות של כהן אברהם:
נשים לב שאלו המשפחות המשוייכות אליו בטבלה Family_Donor:
נעבור ללוי יצחק, ונראה שאצלו קיימות 3 משפחות אפשריות, כי אקסס עידכנה את התיבה המשולבת עבורו:
זו בדיוק הפונקציונאליות שביקשנו!
אבל היתה עוד אפשרות:ב. לתרום תרומה כללית לקופה, ללא ייעוד למשפחה מסויימת. במקרה הזה, תהיה לי האפשרות, כמנהל של המערכת (נרו יאיר), לייעד את התרומה לאיזו משפחה שארצה.
פירוש: אם התורם לא יבחר משפחה ביעד, לי כמנהל תהיה האפשרות לשייך בעצמי את התרומה, גם למשפחה שאינה משודכת עמו.
נראה כי אופציה זו כבר קיימת בטופס התרומה frmDonationCard, כי שם לא ביצענו שום סינון על השדה TargetFamilyID.
למשל עבור אברהם כהן, נוכל לבחור גם את משפחת זבולוני או אשרי:
המשימה הושלמה בהצלחה!
ננוח קצת, כי כבר עלו בדעתי שתי בעיות שעלולות לצוץ במערכת שלנו.
אולי אתם כבר יודעים? (אם כן, ציינו בתגובות)
בפוסט הבא נציין אותן, ונציע שיפורים חשובים למערכת שיצרנו כאן, כפתרון לשתי הבעיות האלו.
עד כאן להיום, שבת שלום!
-
חלק טז: מחשב מסלול מחדש
הערה: החלק הזה במדריך לכאורה חורג מעט מההדרכה, ומנסה לתאר מצב שאליו אנו עלולים להגיע כמפתחים. הוא נולד כתוצאה משיחה שהיתה לי אמש עם מפתח אחר, עם זאת, אני רואה בו חלק חשוב מהמדריך, וחושב שהתועלת ממנו רבה למפתחים בכלל, לא רק באקסס.
אז אם אתם מחכים רק לפתרון של החלק הקודם, תמחלו לי.
אני מרגיש נוח באכסניה הזו להשתמש לפעמים בסגנון בלוגי משהו...איפה עצרנו?
בחלק הקודם הזכרתי שיש שתי בעיות במערכת שיצרנו.
- כשהמנהל ישייך תרומה למשפחה שאינה קשורה לתורם, אז ברשימת התרומות שבתחתית טופס התורם לא יופיע שם המשפחה עבור התרומה הזו (כיוון שכך הגדרנו את התיבה המשולבת שמציגה את שם המשפחה, שלא תציג רשומות עבור משפחות שאינן קשורות לתורם).
זה עלול ליצור את הרושם כאילו התרומה לא משוייכת לאף משפחה, ואז יתכן שמשתמש רגיל (שאינו מנהל) ינסה בתמימות לשייך את המשפחה לאחת המשפחות שכן קשורות לתורם, ובעצם יעביר את התרומה מהמשפחה שיועדה על ידי המנהל למשפחה אחרת... בקיצור תוהו ובוהו.
מה שמוביל אותי לבעיה השנייה והעקרונית:
- האם בכלל אפשר להעביר תרומה ממשפחה אחת לאחרת? כל זמן שהתרומה לא בוצעה בפועל, כלומר שהתורם העביר למערכת את הכסף, אך אנחנו עדיין לא העברנו אותו למשפחה – ייתכן ונרצה לאפשר לשנות את הייעוד. אבל אם התרומה כבר הועברה למשפחה, אסור שתהיה אפשרות לשנות את הייעד.
אל תדאגו, יש פתרון לבעיות הנ"ל, חשבתי אפילו על כמה.
אז מה עושים?
(אזהרה: הקטע הבא עלול לגרום לחלק מהקוראים תחושת בלבול, זה מכוון ואני מתנצל מראש. ניסיתי להעלות על הכתב מה שעבר במוחי הקודח כשצצו בו הבעיות הנ"ל.)
פתרון ראשון: בלי חרטות.
אפשר להחליט לנעול רשימת תרומה שכבר מיועדת למשפחה, כך שלא תהיה אפשרות בכלל לשנות את הייעד.זו האופציה הכי קלה. החיסרון הגדול שלה הוא שאם בחרנו בטעות משפחה לא נכונה – השדה יינעל ולא נוכל לשנות את הייעד. נצטרך למחוק את הרשומה וליצור חדשה.
נוכל לשפר את האפשרות הזו על ידי אפשרות פתיחת הנעילה על ידי הקשת קוד מנהל.
אבל נצטרך להשתמש בקוד VBA (או במאקרו רח"ל) - ואין לנו עדיין שום הכירות עם התחומים האלו.
מה נעשה, נתפשר על נעילה וזהו?
במחשבה שניה זה לא נורא, בטופס של כרטיס התרומה הרי לא ננעל את שדה משפחת היעד, כך נוכל לשנות אותה אם נרצה.
רגע, ואם נשנה בטעות?
אוף!פתרון 2: אזהרה
אפשר להציג ליד כל תרומה שמיועדת למשפחה שאינה שייכת לתורם, סימן התראה כלשהו שמציין שאע"פ ששדה המשפחה נראה ריק, אין האמת כן, אלא ידע הנציג שהתרומה כבר משוייכת ולא יעז לגעת בה.זה ממש פתרון עלוב, כי הוא משאיר את האחריות ביד המשתמש.
ומה אם הוא לא ישים לב לסימן?
בעיני זה שווה ערך ל"פתרון" שבו נתלה מצידי המסך פשקאווילים שמזהירים באותיות קידוש לבנה: "אסור לשייך תרומה לפני שבדקת בשבע בדיקות וחקירות האם המשפחה שייכת לתורם זה. ראה הוזהרת!"
שוב אוף!פתרון שלישי: אחורה פנה
אפשר להחזיר את המצב לקדמותו, לבטל את הסינון מהתיבה המשולבת, כך שתשוב להציג את כל המשפחות, אבל כאשר נבחר משפחה שאינה משוייכת לתורם, המערכת תגיב באחת מהדרכים הבאות:
- לא תאפשר לנו בכלל
- תאפשר לנו, אבל תציג הערת אזהרה
- תבקש סיסמת מנהל על מנת לאפשר את החריגהאפשר לשפר את האפשרות הזו, על ידי שילוב עם הפתרון השני, הצגת כל המשפחות בתיבה המשולבת, אבל להוסיף סימון מיוחד ליד משפחות שאינן קשורות לתורם, כך שמראש ידע הנציג שנדרש עבורם אישור מנהל.
קצת כואב לי שכל העמל של החלק הקודם ירד לטמיון. אבל לכאורה זה הפתרון הכי טוב.
אבל הוא דורש ידע בכתיבת קוד VBA, שלעת עתה אין לנו. אולי נעזוב הכל ונתחיל ללמוד VBA?
וואו. כואב לי הראש.כל השקלא וטריא הזו מעלה נקודה מאוד חשובה.
סוף מעשה במחשבה תחילה
השאלות האלו היו אמורות לצוץ כבר בשלב התכנון.
את ההדרכה פתחתי במשפט מעין: השלב הראשון, והחשוב ביותר ביצירת מערכת הוא התכנון.
אבל לא שמעתי לעצתי, התחלתי מיד ליצור טבלאות, טפסים, שאילתות, וכו' וכו'.
למה?
כי לדעתי זו דרך מצויינת ללמוד. ליגרס איניש והדר ליסבר.
אבל זו דרך גרועה מאוד ליישם למעשה את מה שלמדתי.
באופן אישי, הדחף הראשוני שלי כמתכנת, הוא ישר להתחיל ליצור. אני כבר רוצה לראות את הכל עובד, בלי להיכנס לדקדוקי עניות איך בדיוק כל פיפס ייושם. אבל, אם אני מתפתה לזה, אני מזמין לעצמי צרות.
כי להכניס שינויים במערכת קיימת זה כמעט תמיד הרבה יותר מסובך מאשר לבנות אותה כך מעיקרא.
אמת היא, כי יש גם צד שני למטבע, והוא להיתקע עד אין סוף בשלב התכנון, מבלי להוריד את הרעיון מעולם האצילות לעולם המעשה.
אבל הניסיון מוכיח שהאופציה הראשונה היא הכי גרועה, ושתכנון טוב ואפיון מדוקדק מחזיר את ההשקעה בכפלי כפליים.תשבו ותתכננו עם דף ועפרון:
- מה המטרות של המערכת
- אילו ישויות קיימות בה (תורמים, משפחות, נציגים, מנהלים, וכו' וכו')
- סרטטו על דף סקיצה כללית של המסכים השונים
- מה הפונקציונאליות שתתאפשר למשתמש בכל מסך
כשהכל על הנייר, אין בעיה לשנות, למחוק, ולהתחיל דף חדש בלי נקיפות מצפון.
אם עברתם את השלב הזה, אתם על דרך המלך.
זהו לבינתיים.ולעניין עצם ההדרכה והפתרון, בפוסט הבא (בקרוב בלי נדר) אני אציג פתרון שאפשר ליישם בלי VBA. הוא לא מושלם, אבל נוכל ללמוד ממנו עוד על אקסס, וזו המטרה העיקרית של המדריך הזה.
- כשהמנהל ישייך תרומה למשפחה שאינה קשורה לתורם, אז ברשימת התרומות שבתחתית טופס התורם לא יופיע שם המשפחה עבור התרומה הזו (כיוון שכך הגדרנו את התיבה המשולבת שמציגה את שם המשפחה, שלא תציג רשומות עבור משפחות שאינן קשורות לתורם).
-
חלק י"ז: פתרון (חלקי) לבעיית שיוך התרומות
נתחיל דווקא מהבעיה השניה:
האם בכלל אפשר להעביר תרומה ממשפחה אחת לאחרת?
וניישם את הכלל שחשבנו עליו:
כל זמן שהתרומה לא בוצעה בפועל, כלומר שהתורם העביר למערכת את הכסף, אך אנחנו עדיין לא העברנו אותו למשפחה – ייתכן ונרצה לאפשר לשנות את הייעוד. אבל אם התרומה כבר הועברה למשפחה, אסור שתהיה אפשרות לשנות את הייעד.לשם כך אנו צריכים שדה נוסף שמציין את הסטטוס של התרומה.
לאחר מחשבה, החלטתי שאני מעוניין בשלוש אפשרויות לשדה הסטטוס:- זמינה התרומה נכנסה למערכת, אך עדיין לא מיועדת עדיין לשום משפחה. בידינו ליעדה למי שנרצה.
- מיועדת התרומה כבר מיועדת לאחת המשפחות, אך עדיין לא הועברה אליה בפועל. כעת רק מנהל יוכל לשנות את ייעוד התרומה.
- בוצעה התרומה הועברה כבר למשפחה, ואין לשנות בה שום דבר.
יתר על כן,
אני רוצה שהסטטוס ייקבע על ידי התוכנה, ולא באופן ידני, כדי למנוע את כל הטעויות שהזכרנו. לשם כך ניצור מנגנון האחראי לעדכון שדה הסטטוס, ולא נאפשר שינוי ישיר שלו.שלב א: הוספת שדה סטטוס תרומה
ניצור טבלה חדשה בשם DonationStatus עם השדות הבאים:
ID
מטיפוס מספור אוטומטי. זהו שדה המפתח כמובן.
StatusDesc
מטיפוס טקסט קצר, אורך מקסימלי 10:
עברו לתצוגת גליון נתונים והכניסו את השורות הבאות:
נפתח את הטבלה Donation ונוסיף שדה חדש StatusID מטיפוס מספר שלם ארוך. ונקצה לו ערך ברירת מחדל 1 (כלומר סטטוס=זמינה):
גם כאן, נעצב את התצוגה של השדה להראות תיבה משולבת, כך שיוצג התיאור של הסטטוס במילים ולא ע"י מספר:
נעבור לתצוגת קשרי גומלין ונקשר את הטבלה החדשה לשדה החדש:
יפה.בואו נפתח את טבלת התרומות בתצוגת גליון נתונים, ונראה כי השדה נוסף וברירת המחדל היא "זמינה":
אבל,שימו לב כי עבור התרומות שכבר קיימות במערכת, השדה ריק. מדוע? כי באקסס, כאשר מגדירים ערך ברירת מחדל לשדה, ההגדרה תקפה רק עבור רשומות חדשות שיתווספו מעתה, ולא מעדכנת עמודות ישנות.אבל ברור שהמצב הזה לא מקובל. אני מעוניין שלכל תרומה יהיה ערך תקין בשדה סטטוס. לכן אין לי ברירה אלא לטפל בעצמי בסטטוס של רשומות ישנות.
שלב ב: עדכון שדה הסטטוס עבור הרשומות הקיימות
דקה למחשבה:
לשלושת התרומות הראשונות אנו יכולים לבחור כל אחד משני הסטטוסים: "מיועדת" או "בוצעה", כי הן כבר מיועדות למשפחה. אבל הסטטוס של התרומה הרביעית יוכל להיות רק "זמינה", כי היא לא מיועדת עדיין לשום משפחה.
כרגע לא נסמן שום תרומה כ"בוצעה".בדוגמא שלנו יש רק 4 שורות, ונוכל לעבור שורה שורה באופן ידני ולשנות אותה.
אבל בדרך כלל, לא נעשה כן.
מה יקרה אם יש 40 שורות? ואם יש 4000? ואם בטעות נסמן סטטוס "בוצעה" לתרומה לא משוייכת? צריך מנגנון שיעשה זאת באופן אוטומטי ונטול טעויות. לשם כך נכיר סוג נוסף של שאילתא:שאילתת עדכון
יצירה > עיצוב שאילתא > הוסיפו את טבלת התרומות, ומתוכה את השדה StatusID לרשימת השדות למטה. כעת בחרו סוג שאילתה "עדכן" מהתפריט:
שימו לב שנוספה כותרת שורה: "עדכן ל:"
בשדה זה אנו אמורים לציין את הערך המעודכן של השדה StatusID. אבל כפי שציינתי, ערך זה תלוי:- אם התרומה לא משוייכת למשפחה, אנו רוצים שערך השדה יתעדכן למספר 1 (=זמינה)
- אחרת, השדה יתעדכן למספר 2 (=מיועדת)
יש יותר מאפשרות אחת לעשות כן. היום נשתמש בפונקצית התנאי הכפול IIF
פונקצית התנאי IIF מקבלת שניים או שלושה פרמטרים, ומחזירה ערך לפי ההגיון הבא:
אם ערך הפרמטר הראשון הוא אמת – יוחזר ערך הפרמטר השני. אם לא – יוחזר ערך הפרמטר השלישי (אם צייננו פרמטר שלישי. אחרת יוחזר NULL)
כנסו לשדה ופתחו את הבונה, ושם כתבו את הנוסחא הבאה:
IIf([TargetFamilyID] Is Null, 1, 2)
חשוב מאד: שימו לב להוסיף את הסוגריים המרובעות מסביב לשם השדה TargetFamilyID, אחרת אקסס תפרש אותו בתור מחרוזת, ותוסיף לו גרשיים, והתוצאות יהיו שגוייות, כי התנאי בפרמטר הראשון תמיד יחזיר שקר.
זהו.
כל מה שנדרש זה פשוט להריץ את השאילתה.
בחרו מהתפריט למעלה: "הפעל". אקסס תשאל האם אתם בטוחים שברצונכם לעדכן 4 שורות:
נבחר כן. ואקסס תעשה את העבודה בשבילנו.
כעת נסגור את השאילתא. אקסס תשאל אם ברצוננו לשמור:
נחשוב: האם אנו צריכים לשמור את השאילתה? כנראה שלא. אנו לא אמורים לשנות באופן גורף את ערך השדה בעתיד.
נבחר "לא".
נפתח את טבלת התרומות ונבדוק שכל הרשומות עודכנו בצורה נכונה:
נראה מצוין!
רבע שעה הפסקה ונמשיך.שלב ג: הגדרת שדה הסטטוס כשדה חובה
למרות שאקסס מספקת לנו ערך ברירת מחדל לסטטוס, עדיין המשתמש יכול למחוק לגמרי את ערך השדה, ולהשאירו על NULL. זה מצב שאנו רוצים לחסום. לכן נגדיר את הסטטוס כשדה נדרש.
שדה נדרש
אקסס מאפשרת לנו להגדיר כי שדה מסויים הוא נדרש, כלומר: מוכרח להכיל ערך. אם השדה הוגדר כנדרש, אקסס לא תאפשר לנו לשמור את הרשומה מבלי לספק ערך תקין לשדה.
כנסו לטבלת התרומות, בחרו את שדה הסטטוס, ושנו במאפיינים את המאפיין נדרש לערך "כן":
שמרו את הטבלה ביציאה ממעצב הטבלה. אקסס תציג את ההודעה הבאה:
הודעה זו מופיעה בכל פעם שמגדירים שדה כנדרש וכבר קיימות רשומות בטבלה.
אקסס שואלת האם ברצוננו לבדוק שהרשומות הקיימות מקיימות את ההגדרות החדשות, או במקרה שלנו, שהשדה StatusID מכיל ערך עבור כל רשומה ורשומה.נבחר "כן", ואקסס תבדוק שהרשומות תקינות. וכיוון שכבר דאגנו לכך מראש, לא תהיה שום בעיה, ואקסס תסגור את מעצב הטבלה.
אם הנתונים היו שגויים (כלומר, היה חסר ערך לשדה הסטטוס בחלק מהרשומות), אקסס הייתה שואלת מה ברצוננו לעשות: לבטל את ההגבלה על השדה, או להתעלם מחוסר התקינות של הנתונים הקיימים, ורק לדאוג לכך מכאן ולהבא (וכן, זה באמת נשמע רעיון מסוכן, ורק לעתים נדירות נבחר בו, לא ניכנס כעת לפרטים).
שלב ד: הוספת שדה הסטטוס לטופס רשימת התרומות ונעילתו
אנו רוצים להציג למשתמש את הסטטוס של כל תרומה. נפתח את הטופס frmDonationList בתצוגת עיצוב, ונבחר מהתפריט למעלה: "הוסף שדות קיימים":
אקסס תציג את כל השדות בטבלה שעליה מבוסס הטופס, דהיינו טבלת התרומות Donation. נגרור את השדה StatusID לתוך הטופס, לפני שדה משפחת הייעד:
כעת ננעל את השדה לשינויים:
נעבור לתצוגת המאפיינים של השדה על ידי מקש F4, ובלשונית נתונים נשנה את ערך המאפיין נעול ל-כן:
שלב ד: ביטול שדה משפחת היעד עבור רשומות עם סטטוס שונה מ"זמינה"
אנו רוצים שהמשתמש לא יוכל לשנות את היעד עבור משפחה שהסטטוס של שונה מ-"זמינה". לשם כך נשתמש בעיצוב מותנה על שדה TargetFamilyID:
סמנו את השדה ובחרו בתפריט "עיצוב" את האפשרות "עיצוב מותנה":
בחרו כלל חדש ובעצב רק תאים שבהם: את האפשרות הביטוי הינו
ובתיבת הביטוי הקלידו
[StatusID] <> 1
נסמן את האפשרות הפוך לזמין, ונשים לב כי התצוגה המקדימה מראה פקד אפור (=לא זמין)
נבחר אישור פעמיים כדי להחיל את כלל העיצוב שיצרנו.
נסגור את הטופס ונשמור. כעת נפתח את טופס כרטיס התורם, ונראה שהשדה החדש מופיע, וכן נראה שכעת יש אינדיקציה על תרומה המיועדת למשפחה שאינה משוייכת לתורם הנוכחי:
נסו לשנות את הסטטוס עבור משפחה כלשהי. שימו לב שאין אפשרות כי השדה נעול.
הוסיפו כעת עוד תרומה לתורם הנדיב כהן אברהם, ייעדו את התרומה עבור "ראובני אברהם ושרה".
שימו לב כי למרות שהתרומה מיועדת, הסטטוס עדיין נמצא על "זמינה":
שלב ה: שאילתה לעדכון הסטטוס
נצטרך ליצור מנגנון שמשנה את הסטטוס עבור כל תרומה כזו. הטוב ביותר הוא לשנות את ערך הסטטוס ברגע שמעדכנים את המשפחה בשדה היעד, אך לצורך זה אנו צריכים להשתמש בקוד VBA. כיוון שמדריך זה באופן עקרוני לא אמור לכלול קוד VBA, נשתמש בשאילתת עדכון.
השאילתה תהפוך את שדה הסטטוס למיועדת עבור כל תרומה שיש לה משפחה בשדה הייעד, אך הסטטוס שלה עדיין נמצא על ברירת המחדל "זמינה".צרו שאילתת עדכון חדשה, והוסיפו למשטח העבודה את טבלת התרומות.
הוסיפו לרשימת השדות למטה את שני השדות StatusID ו- TargetFamilyID.
כעת השלימו את הפרמטרים כפי שמופיע בתמונה:
הגדרה זו פירושה:
עדכן את השדה StatusID כאשר הוא מכיל את הערך 1 (=זמינה) וגם שדה משפחת היעד אינו ריק.
לשם שעשוע, עברו לתצוגת SQL וראו את תחביר השאילתה:
UPDATE Donation SET Donation.StatusID = 2 WHERE (((Donation.StatusID)=1) AND ((Donation.TargetFamilyID) Is Not Null));
המילה UPDATE מציינת כי ברצוננו לעדכן את הטבלה Donation. הפקודה SET… פירושה קבע את שדה Donation.StatusID לערך 2. המילה WHERE מציינת את התנאי לעדכון, והמילה AND מציינת כי שני חלקי התנאי צריכים להתקיים. פשוט, לא?שמרו את השאילתה בשם: qryUpdate_SetDonationStatusToTargeted.
שימו לב, כי אקסס מקצה סמל מיוחד של עיפרון עם סימן קריאה עבור שאילתות עדכון:
כדי לציין שהשאילתה משנה ערכי שדות, ועלינו להשתמש בה בזהירות.
כדי להריץ את השאילתה, נלחץ עליה פעמיים. אקסס תשאל האם עשינו זאת בכוונה:
נבחר כן.
אקסס תבדוק כמה רשומות עתידות להיות מושפעות כתוצאה מהרצת השאילתה (במקרה שלנו רק הרשומה האחרונה שיצרנו), ותתריע על השינויים הצפויים:
שוב נבחר כן.
נפתח את טופס התורם ונראה כי הסטטוס לתרומה האחרונה השתנה למיועדת, ושדה משפחת היעד ננעל:
אנחנו מתקדמים.לסיום
הסרת האפשרות לשינוי הסטטוס על ידי המשתמש היא צעד חשוב. אך עדיין יש עוד מה לעשות:- להריץ את שאילתת העדכון כשהדבר מתבקש, למשל עם סגירת הטופס. (ככל הנראה לא יהיה מנוס משימוש ב-VBA למרות הכל...)
- לאפשר שינוי הסטטוס ל"בוצעה" במקרה שאכן התרומה הועברה למשפחה.
נשאיר זאת לפעם הבאה.
שיעורי בית: עריכת טופס כרטיס התרומה.
על פי התכנון, טופס כרטיס התרומה frmDonationCard מיועד למנהל המערכת בלבד. אבל בטופס הזה חסרים שני דברים חשובים:- לא מוצג שדה הסטטוס.
- אנו מעוניינים לאפשר למנהל לשנות רק תרומה זמינה, ולא שכבר מיועדת או בוצעה.
למעשה, זה בדיוק אותו תהליך כפי שעשינו עם הטופס של רשימת התרומות.
בהצלחה! -
חלק י"ח: VBA - חלק א: היכרות, עורך הקוד, ואירועים
המערכת שלנו מוגנת מטעויות אנוש של העברת תרומות בטעות בין משפחות, כי דאגנו לנעול את השדה של היעד ברגע שהסטטוס השתנה למשוייכת. את הסטטוס עצמו לא ניתן לשנות בחופשיות על ידי המשתמש, כי השדה נעול בכל הטפסים.
ברור כי אפשר להכנס לטבלת התרומות ולשנות שם כל דבר. במערכת אמיתית אין למשתמש גישה ישירה לטבלאות בשום אופן. כרגע נתעלם מהנושא, ונניח שהמשתמש יכול לתקשר עם הנתונים רק על ידי הטפסים.
יצרנו שאילתה שמעדכנת את הסטטוס של כל התרומות המשוייכות מ-זמינה ל-משוייכת. כעת עלינו לדאוג שהשאילתא אכן תרוץ בזמן המתאים. זמן טוב לכך הוא כאשר המשתמש סוגר את טופס התורם. כמובן שהתהליך צריך להתבצע באופן אוטומטי.
בתור קו מנחה, נמנעתי מלהשתמש בשפת VBA, הואיל וכתיבת קוד (בכל שפה) הוא נושא מורכב, ורציתי לפשט את המדריך עד כמה שיתן. אבל המציאות הוכיחה שהפתרון הכי טוב הוא בקוד. ולכן אני שמח לחזור בי ולהזמין לבמה את VBA.
60 שניות על VBA
על קצה המזלג, VBA הוא קיצור של Visual Basic for Applications. זוהי שפת תכנות, שמשמשת בעיקר בתוך היישומים הנפוצים של אופיס, דהינו וורד, אקסל וכמובן אקסס.(למעשה זו תת-גרסא ל Visual Basic או בקיצור VB, שפת תכנות ותיקה שפותחה על ידי מייקרוסופט בשנות התשעים (ואשר בעצמה מבוססת על שפת Basic...), ועברה כמה גלגולים עד שנתמזגה לתוך פרוייקט הדוט נט של מייקרוסופט)
ל-VBA כמה יתרונות חשובים:
- היא מאפשרת לנו להרחיב את היכולות של ישומי אופיס מעבר לגבולות הקיימים.
ניתן למשל להוסיף פונקציה לאקסל, (למשל המרת תאריך לועזי לעברי), ולהשתמש בה בדיוק כאילו היא פונקציה מובנית של אקסל, כמו למשל SUM(). - היא שפה מונחית-אירועים, דהיינו אפשר לקבוע שקוד ירוץ רק באירוע מסוים, למשל סגירה של טופס.
- היא מאפשרת לשנות את הקוד בצורה דינמית, כלומר גם בזמן הריצה, דבר שמאוד מקל בפיתוח.
- היא כוללת סביבת פיתוח מובנית עם עורך קוד בשם VBE הכולל כלי פיתוח בסיסיים ומועילים.
כדי לשמור על המדריך פשוט, נשתמש בה רק בטעימות פה ושם. אבל מומלץ למי שמשתמש קבוע ביישומי אופיס להכיר אותה לעומק. זה פותח עולם שלם של אפשרויות.
נחזור לענייננו:
אנו רוצים שבסגירת טופס רשימת התרומות, יקרה התהליך הבא:
- המערכת תבדוק האם קיימות ברשימת התרומות רשומות לעדכון, כלומר משוייכות למשפחה אך הסטטוס = 1 (זמינה).
- המערכת תשאל את המשתמש האם הוא רוצה לעדכן את הסטטוס של התרומות המשוייכות. ואם כן,
- המערכת תריץ את שאילתת עדכון התרומות שיצרנו בפרק שעבר.
כדי לא לגרום לפוסט כבד ועמוס, הפוסט הזה יטפל רק בשלב הראשון בתהליך.
האזנה לאירוע סגירת הטופס
כפי שהזכרנו, ניתן להצמיד קוד לארוע מסויים. לכל פקד (תיבת טקסט, לשונית וכו') קיימים אירועים שונים ומשונים שאפשר להאזין להם. אנו מעוניינים להאזין לארוע הסגירה של פקד הטופס, דהיינו הטופס עצמו.פתחו את טופס רשימת התרומות (טופס המשנה) frmDonationList בתצוגת עיצוב.
שימו לב: כשפותחים טופס בתצוגת עיצוב, כברירת מחדל, אקסס בוחרת את פקד הטופס.
סימן ההיכר לכך הוא הריבוע השחור בפינה השמאלית העליונה, ליד הסרגל של הטופס:
אם בטעות לחצתם ובחרתם פקד אחר, עליכם לבחור בפקד הטופס, על ידי לחיצה על הפינה הנ"ל, או בחירה של פקד הטופס מסרגל המאפיינים:
יפה, כעת בחרו מסרגל המאפיינים את הלשונית "אירוע". שימו לב לרשימה הארוכה של האירועים האפשריים בפקד הטופס:
בחרו את הארוע: "בעת סגירה". בסוף השורה, לחצו על שלושת הנקודות
...
ובחלון בחירת הבונה בחרו באפשרות האחרונה: בונה קוד, ולחצו על אישור:
אקסס תפתח עבורנו את עורך ה-VBA:
כברירת מחדל, מראה עורך ה VBA שלכם שונה מהתצלום המצורף. אני שיניתי את ההגדרות לפי טעמי.
בואו נתעלם מכל מה שמסביב ונתמקד בשתי שורות הקוד הבאות:
אקסס יצרה את השורות האלו באופן אוטומטי כשביקשנו להצמיד קוד לארוע סגירת הטופס. שורות אלו מהוות תבנית כללית (ריקה) של תהליך.
בכל תוכנה, מחלקים משימות מורכבות לתהליכים קטנים, כדי להקל על התחזוקה, הקריאות, והיעילות של הקוד.
- השורה הראשונה היא משפט פתיחה של תת-שגרה, כלומר תהליך קטן, או באנגלית SubRoutine. (כאן השימוש בעברית מסרבל יותר מאשר מקל...)
- השורה האחרונה היא משפט סיום של התהליך הזה.
- במקום הריק שבין שתי שורות אלו אנו נכתוב את הקוד שהתהליך אמור לבצע.
ב- VBA יש הבחנה בין שני סוגי תהליכים: Sub-Routine (תת-שגרה) ו- Function (פונקציה), כאשר הסוג ראשון לא מחזיר ערך, והאחרון כן. בשפות מודרניות כיום מקובל להשתמש רק במושג פונקציה עבור שני הסוגים הנ"ל (את הסוג הראשון, מכנים בשם פונקציה שלא מחזירה ערך). לשם פשטות אני לא אדקדק בלשוני ואשתמש גם אני רק במילה פונקציה, אא"כ אהיה מוכרח אחרת.
הלאה,
- המילה Private היא מילה שמורה בשפת VB, שמציינת כי הפונקציה היא פרטית, כלומר: אין אליה גישה מחוץ לתחומי הטופס שלנו (טפסים אחרים, למשל, לא יוכלו לגשת אליה).
- המילה השמורה Sub מציינת כי הפונקציה לא תחזיר ערך.
- צמד המילים עם שני הסוגריים Form_Close() מציינות את שם הפונקציה.
במקרה שלנו, השם Form_Close שמור באקסס כמוסכמה והוא קובע שהקוד יוצמד לאירוע הסגירה של הטופס. - הסוגריים הריקות מציינים כי הפונקציה לא מקבלת פרמטרים מבחוץ (לא נרחיב בעניין בינתיים)
יצירת קוד לריצה באירוע סגירת הטופס
בואו נקפוץ למים, ונוסיף בין השורות הנ"ל את הקוד הבא:
הסבר:
- השורה הראשונה מתחילה במילה השמורה
Dim
שמשמעותה הגדרת משתנה חדש. משתנה הוא אובייקט בזיכרון המחשב שיכול להכיל ערך.
כאשר מגדירים משתנה, חובה לתת לו שם. שם המשתנה מופיע מיד אחרי המילהDim
.
במקרה שלנו, נתתי לו את השםdonationsToUpdate
.
ככלל, נקצה שמות משמעותיים למשתנים, אשר מעידים על תוכנם, ולא נשתמש בקיצורים או ראשי תיבות, או שמות עלומים כגון
x,y
וכו'.לאחר שם המשתנה, באה המילה השמורה
As
ולאחריה מציינים את טיפוס הנתונים של המשתנה (איזה סוג נתונים הוא יכול להכיל). במקרה שלנו הואLong
שפירושו מספר שלם ארוך.המילה השמורה
As
היא קידומת לשונית שהוכנסה לשפת VB. שפת Basic תוכננה עם כל מיני מילים מיותרות כאלו, כדי להקל על קריאות הקוד.- השורה השנייה מבצעת קריאה לפונקציית המנייה DCount ומכניסה את הערך שהחזירה הפונקציה לתוך המשתנה donationsToUpdate.
DCount היא פונקציה ייחודית לאקסס ואינה חלק משפת VBA. הפונקציה מונה את כל הערכים של השדה בטבלה, ומחזירה את מספרם.
לפונקציה ניתן להעביר 2 או 3 פרמטרים, לפי הסדר הזה:
- שם השדה שאותו אנו מונים:
TargetFamilyID
- שם הטבלה שמכילה את השדה:
Donation
- תנאי למניית השדה:
StatusID=1
הפרמטר האחרון של התנאי הוא אופציונאלי, ואם נשמיט אותו, הפונקציה תחזיר את מספר כל הרשומות שבהם יש ערך לשדה.
שימו לב שלא הוספנו בתנאי בדיקה אם השדה TargetFamilyID אינו ריק (כלומר התרומה משוייכת למשפחה), מדוע?
כפי שציינתי בעבר, פונקציות מנייה ממילא מתעלמות משדה המכיל Null.
הואיל והשדה TargetFamilyID הוא בעצמו השדה הנמנה, אין צורך להוסיף בפירוש בתנאי כי הוא אינו ריק, הבדיקה ממילא מבוצעת על ידי הפונקציה DCount.- השורה האחרונה והחביבה מדפיסה את ערך המשתנה לחלון הערכים המיידיים
Window Immediate, כמו שנראה מיד. חלון זה משמש אותנו להדפסה של פלט, להרצה ידנית של קוד במקרה הצורך, ועוד.
רוצים לראות מה עשינו?
בואו נחזור לטופס שלנו.
נלחץ על Alt+F11 או על הסמל של אקסס (הסמל העתיק...) בצד שמאל למעלה בחלון עורך ה-VBA:
אפשר גם לסגור לגמרי את העורך כדי לחזור לאקסס.
נסגור את הטופס וכמובן נשמור את השינויים.כעת נפתח את טופס כרטיס התורם.
ומיד לאחר מכן נסגור אותו.
שמתם לב מה קרה?
כלום.רגע, בואו נחזור לעורך ה-VBA (על ידי הקשה שוב על Alt+F11).
שימו לב לחלונית ה Immediate בתחתית העורך: רואים את האפס הקטן?
זהו הפלט שיצרה הפונקציה בסגירת הטופס. הוא כרגע 0 כי אין לנו תרומות לטיפול (עם סטטוס =1 שמשוייכות למשפחה). אז בואו וניצור רשומה כזו.
חיזרו שוב לאקסס והוסיפו תרומה חדשה לכהן אברהם, על סך 72 שקלים, ושייכו אותה למשפחה:
סיגרו את הטופס, וחיזרו לעורך הקוד. שימו לב שעתה נוספה שורה מתחת לאפס ובה מופיע המספר 1:
שמציין כי כעת זוהתה רשומה אחת לטיפול.צעד גדול למתכנת המתחיל.
בפוסט הבא ב"ה נסיים את הפונקציה.
שיעורי בית
- הוסיפו לפונקציה שיצרנו עוד משתנה מסוג Double בשם amountToUpdate, והכניסו לתוכו את סכום התרומות שעומדות לעדכון. השתמשו בפונקצית הסכום DSum (וכן, היא קרובת משפחה של DCount)
- הדפיסו בחלון ה-immediate גם את ערך המשתנה שיצרתם, מתחת למספר הרשומות לעדכון.
- צרו משתנה שלישי בשם userMessage מסוג מחרוזת String, שיכיל בתוכו את המשפט הבא:
"ברשימת התרומות קיימות לעדכון X תרומות על סך כולל של Y"
כאשר במקום X ו-Y יופיעו הערכים הנכונים (השתמשו במשתנים שיצרתם ובשרשור מחרוזות על ידי האופרטור&
. הדפיסו גם את המשפט בחלון הפלט.
בהצלחה!
- היא מאפשרת לנו להרחיב את היכולות של ישומי אופיס מעבר לגבולות הקיימים.
-
פרק יט: VBA - חלק ב: כתיבה נכונה של קוד, הערות
ראשית, פתרון שיעורי הבית
כל הכבוד ל @בערל החרוץ!)- הוסיפו לפונקציה שיצרנו עוד משתנה מסוג Double בשם amountToUpdate, והכניסו לתוכו את סכום התרומות שעומדות לעדכון. השתמשו בפונקצית הסכום DSum (וכן, היא קרובת משפחה של DCount).
- הדפיסו בחלון ה-immediate גם את ערך המשתנה שיצרתם, מתחת למספר הרשומות לעדכון.
פתרון:
שימו לב לכמה נקודות:
- כאן צריך להוסיף בפירוש את התנאי
TargetFamilyID Is Not Null
כי הפונקציה Sum סופרת את השדה Amount ולא את TargetFamilyID. - הקפתי בסוגריים כל חלק של התנאי בנפרד, למרות ששפת VBA תפרש נכון את התנאי גם בלי סוגריים מסביב לכל חלק (כי קדימות המילה AND היא נמוכה ביחס לקדימות לאופרטורים
=
אוIs
). זה פשוט בתור הרגל טוב המשפר את קריאות הקוד. - במקרה שלא נמצאו כלל רשומות המקיימות את התנאי, הפונקציה Sum לא תחזיר את הערך 0 אלא תחזיר NULL. ואז תיווצר לנו שגיאה בזמן הריצה של הקוד, כי המשתנה
amountToUpdate
הוא מטיפוסLong
ואינו יכול לקבל ערך Null. לכן משתמשים בפונקציית ההמרהNz()
. הפונקציה בודקת את ערך הפרמטר הראשון (במקרה שלנו = הערך המוחזר מ-DSum), ואם הוא Null, היא מחזירה את ערך הפרמטר השני. (במקרה שלנו = 0).
בפונקציה DCount לא השתמשנו בבדיקה זו, כי בשונה מ-DSum, הפונקציה DCount, תחזיר 0 גם במקרה שלא נמצאו שום רשומות המקיימות את התנאי. אם תחשבו על כך, זה מאוד הגיוני, כי תפקידה לספור רשומות המקיימות את התנאי, ויתכן שלא יהיו כאלו. מה שאין כן DSum מחשבת סכום של שדה מסויים, ואם אין רשומות – הפונקציה אמורה לא להחזיר שום ערך, כי אין שום סכום (0 נחשב סכום).
- צרו משתנה שלישי בשם userMessage מסוג מחרוזת String, שיכיל בתוכו את המשפט הבא:
"ברשימת התרומות קיימות לעדכון X תרומות על סך כולל של Y"
כאשר במקום X ו-Y יופיעו הערכים הנכונים (השתמשו במשתנים שיצרתם ובשרשור מחרוזות על ידי האופרטור&
. הדפיסו גם את המשפט בחלון הפלט.
פתרון:
כמה מוסכמויות בכתיבת קוד:
-
עבור שמות פונקציות יש להשתמש ב upper camel case, או בתרגום חופשי: כתיבת גמל (?) או כתיבה גמלונית(?)
, דהיינו להתחיל כל מילה בשם הפונקציה באות גדולה, למשל:
()GetUserName
או
()CleanMyDesk
-
עבור שמות משתנים או שמות פרמטרים (לפונקציה) על ידי lower camel case דהיינו להתחיל כל מילה באות גדולה, למעט המילה הראשונה בשם המשתנה, שמתחילה באות קטנה, למשל:
donationsToUpdate
או
MakeMeSomeCoffee(addSugar As Boolean, numberOfCups As Long)
למרות ש-VBA לא תמיד שומרת על מוסכמויות אלו בעצמה (נו נו נו VBA...), כדאי להתרגל בהן כבר מתחילת הדרך. הדבר ישתלם בהמשך, כשנלמד עוד מוסכמויות או נרצה לעבור לשפה אחרת.
וכעת להשלמת פונקציית הארוע:
כעת נותר לנו להשלים את שני הצעדים הבאים:- המערכת תשאל את המשתמש האם הוא רוצה לעדכן את הסטטוס של התרומות המשוייכות. ואם כן,
- המערכת תריץ את שאילתת עדכון התרומות שיצרנו.
נוסיף לפונקצית הארוע את השורות הבאות:
הסבר הקוד:
- השורה הראשונה היא בדיקת תנאי. כדי לבדוק אם דבר מסויים הוא נכון, משתמשים בפקודה
If
שפירושה: "אם" ולאחריה מופיע התנאי לבדיקה, ואחריו המילהThen
שפירושה "אז" (שוב תוספת מיותרת בשפה כדי להקל על קריאות הקוד)
אם התנאי נכון, יבוצע כל בלוק הקוד שמופיע לאחר ה-Then
עד לפקודהEnd If
.
על מקטעים או בלוקים:
שימו לב שבדוגמא שלנו יש שתי בדיקות תנאי
If
מדורגות (Cascaded), ולכן יש שתי פקודותEnd If
. במקרה כזה, המהדר (=הקומפיילר, החלק שאחראי על פיענוח הקוד) יפרש את סיום התנאי לפי מדיניות המחסנית (בדומה למחסנית של קליעים, בה הקליע הראשון שנכנס למחסנית הוא האחרון שיוצא, או באנגלית: Last In First Out או בקיצור LIFO) לכן התנאי הראשון מסתיים בEnd If האחרון, והתנאי השני מסתיים ב End If שלפני האחרון וכו'.כדי להקל את קריאות הקוד ולציין בפירוש מה דירוגו של כל קטע קוד, אנו משתמשים בהזחה (Indentation) ימינה בכל פעם שנכנסים למקטע (Scope) עם דירוג חדש, וחוזרים שמאלה בסוף המקטע.
ברוב השפות אין חשיבות להזחה מבחינת הפעולה של הקוד, אך זוהי מיומנות בסיסית חשובה והכרחית בכל כתיבת קוד, שמקילה מאוד על הקריאות ומונעת טעויות רבות, ולרוב סביבת הפיתוח משתדלת לבצע את כל זה עבורנו (זה מאד מפליא ומוזר לראות לפעמים כאלו שלא מקפידים על כך)- השורה השניה היא יצירת ההודעה שברצוננו להציג למשתמש. אנו מחברים את ההודעה שיצרנו קודם, עם התו המיוחד
vbNewLine
, שמשמש לירידת שורה (כי אי אפשר להכניס אנטר בתוך מחרוזת בצורה רגילה בקוד), ומוסיפים שאלה למשתמש. את כל העסק הזה אנו מכניסים לתוך המשתנהuserMessage
.
אף על פי שהשתמשנו במשתנה userMessage בשני צדדי סימן השווה
=
, אין בכך בעיה של הפניה מעגלית, כי הקומפיילר מעריך קודם כל את הצד הימני של הפקודה (לתוך משתנה אנונימי בזיכרון), ואחר כך מבצע השמה של התוצאה לתוך המשתנה userMessage.-
השורה השלישית מציגה תיבת דו-שיח (דיאלוג) למשתמש על ידי קריאה לפונקציה MsgBox, כאשר הפרמטר הראשון הוא ההודעה שברצוננו להציג, והשני הוא דגלי עיצוב של תיבת הדיאלוג. ישנם דגלים רבים ומגוונים, אך השתמשנו כעת בדגל vbYesNo, שמעצב את תיבת הדיאלוג כך שתציג אחרי ההודעה את האפשרויות כן ו-לא.
הפונקציה MsgBox מחזירה את תגובת המשתמש לדיאלוג, ואז מתבצעת השוואה לערך השמור vbYes, בכך אנו מוודאים שהמשתמש בחר באפשרות כן. ואם התוצאה אמת, מתבצע הבלוק הבא – השורה הרביעית. -
השורה הרביעית מבצעת קריאה לפונקצית ההרצה Execute על בסיס הנתונים הנוכחי CurrentDb, ומפעילה את שאילתת העדכון שלנו.
להלן הקוד בשלמותו, אחר הסידור:
Private Sub Form_Close() Dim donationsToUpdate As Long Dim amountToUpdate As Double Dim userMessage As String donationsToUpdate = DCount("TargetFamilyID", "Donation", "StatusID=1") Debug.Print donationsToUpdate amountToUpdate = Nz(DSum("Amount", "Donation", "(TargetFamilyID Is Not Null) AND (StatusID=1"), 0) Debug.Print amountToUpdate userMessage = "ברשימת התרומות קיימות לעדכון " & donationsToUpdate & " תרומות על סך כולל של " & amountToUpdate Debug.Print userMessage If donationsToUpdate > 0 Then userMessage = userMessage & vbNewLine & "האם ברצונך לעדכן את הסטטוס עבורן?" If MsgBox(userMessage, vbYesNo) = vbYes Then CurrentDb.Execute "qryUpdate_SetDonationStatusToTargeted", dbFailOnError End If End If End Sub
למרות שאין הדבר בגדר חיוב, מקובל לסדר את כל ההגדרות של המשתנים בתחילת הפונקציה, כדי להפריד בין החלק ההגדרתי לחלק התפעולי.
מוכנים לראות מה עשינו?
פתחו את טופס התורם frmDonor הוסיפו כמה תרומות ושייכו אותן למשפחה:
לאברהם כהן:
וללוי יצחק:
סיגרו את הטופס. אקסס תציג את ההודעה הבאה:
בחרו כן.
פתחו את הטופס שוב ובידקו כי הסטטוס עודכן עבור הרשומות החדשות:
לאברהם כהן:
וללוי יצחק:
מעולה!שינוי כיוון הכתב בתיבת הדיאלוג
מסתמא שמתם לב כי אקסס מציגה את ההודעה בתיבת הדיאלוג משמאל לימין. כדי להפוך את כיוון התצוגה, יש להוסיף לקוד את הדגלים הבאים:If MsgBox(userMessage, vbYesNo + vbMsgBoxRtlReading + vbMsgBoxRight) = vbYes Then
כעת ההודעה תוצג נכון:
הוספת הערות בקוד
פתחו שוב את עורך ה-VBA, והתבוננו בקוד שכתבנו, נשים לב כעת כי יש בו כמה שורות מיותרות, סימנתי אותן להלן:
אין לנו יותר צורך בהדפסת התוצאות בחלון הערכים המיידיים. זה שימש אותנו רק כשלב ביניים כדי לבדוק את התוצאות שמתקבלות מהפונקציות.
נוכל פשוט למחוק אותן, אבל ייתכן ונרצה אותן בעתיד, ולכן במקום למחוק אותן, נבטל את השורות על ידי הפיכתן להערות (Comments).
הערות משמשות לביטול שורות קוד באופן זמני, או להוספת הסברים בתוך הקוד.
בשפה נמוכה (כגון אסמבלר) הסברים בקוד הם הכרחיים, אך בשפה גבוהה (כמעט כל השפות הנפוצות) עדיף למעט בהערות, ובמקום, להתאמץ ככל שניתן שהקוד יהיה כתוב בצורה שתהיה מובנת גם ללא הערות.בשפת VBA, כל הערה מתחילה בתו הגרש:
'
.הוסיפו את התו
'
לפני שלושת השורות הנ"ל.ניתן גם לעמוד על שורה ולבחור מהתפריט למעלה את לחצן בלוק ההערות:
שימו לב שהעורך משנה את צבע שורת ההערה (ייתכן והצבעים אצלכם שונים):
מעתה, הקומפיילר יתעלם משורות אלו בהרצת הקוד.
עד כאן להיום.
בפרק הבא אי"ה נוסיף עוד רובד למערכת שלנו, והוא הפקת קבלות לתורמים.
על הדרך נכיר את מחולל הדוחות, ונלמד על עוד סוג קשר חשוב בין טבלאות: יחיד ליחיד. -
פרק כ: הפקת קבלות: חלק א: אפיון, קשר של יחיד ליחיד ועוד קצת VBA
אני רוצה להוסיף למערכת שלנו יכולת הפקה של קבלות לתורמים.
אני מעוניין באפיון הבא:- לכל תרומה תהיה רק קבלה אחת ויחידה.
- לקבלות יהיה מספר רץ ייחודי.
- לקבלה יהיה תאריך הנפקה.
- אפשרות להוספת הודעת טקסט אישית על הקבלה.
לכאורה, הפתרון הפשוט הוא להוסיף עוד כמה שדות לטבלת התרומות:
ReceiptNumber
ReceiptDate
ReceiptMessage
ולמלא אותם בכל פעם שיוצרים קבלה.
למה לטבלת התרומות?
כי כל קבלה שייכת רק לתרומה אחת בלבד.אבל אם נחשוב על כך, לפתרון זה ישנם שני חסרונות גדולים:
- אני צריך לנהל בעצמי את המספר הרץ. וזה לא כל כך טריוויאלי. בכל הנפקת קבלה חדשה אצטרך לסרוק את כל טבלת התרומות, למצוא את הערך המקסימלי, להוסיף 1 ולהקצות אותו לקבלה החדשה. זה מסורבל.
במקרה שיש כמה משתמשים במערכת, זה אפילו מסוכן, כי יתכן ושניהם ינפיקו בו זמנית את אותו המספר לשתי קבלות שונות... - גם ניהול תאריך הנפקת הקבלה יהיה רק באחריותי, ולא על ידי המערכת, כי לא אוכל להקצות ערך ברירת מחדל לשדה, שהרי אם כן, הוא ייקבע ברגע שאצור תרומה חדשה, ומסתמא תאריך הנפקת הקבלה לא תמיד זהה לתאריך התשלום. בנוסף אני עלול לשכוח למלא את הנתון, מה שיגרום לי סיבוך מיותר עם מס הכנסה...
אבל אל דאגה, יש פתרון יעיל בהרבה.
נכון שלכאורה פרטי הקבלה שייכים באופן הגיוני לתרומה, אך בעצם, תרומות וקבלות הן שתי ישויות נפרדות, וראויות להיות נפרדות גם בנתונים.
אנחנו נפריד בין נתוני התרומות לבין נתוני הקבלות, וניצור ביניהן יחס של אחד לאחד.
יחס של אחד לאחד קובע כי לכל רשומה בטבלה א תיתכן אך ורק רשומה אחת קשורה בטבלה ב.
ניתן להמשיל זאת לפריסת הטבלה על ידי סכין לשתי טבלאות נפרדות: אחת לתרומות ואחת לקבלות.
באופן עקרוני תכנון טוב של בסיס נתונים רלציונאלי מושתת על הפרדה של ישויות בנתונים. להפרדת ישויות על ידי יחס אחד לאחד יש כמה יתרונות:
- צמצום בנעילת רשומות
כאשר משתמש עובד על רשומה מסויימת, אותה רשומה ננעלת עבור שאר המשתמשים. בהפרדת הנתונים לשתי טבלאות אנו מאפשרים עבודה על שתי הטבלאות במקביל ללא חשש. - חיסכון בשטח אחסון
לעתים אנו לא מעוניינים בשמירת כל הנתונים עבור כל הרשומות, אלא רק עבור חלקן.
הפרדת הנתונים תאפשר לנו לשמור את הנתונים רק עבור הרשומות שבחרנו.
במקרה שלנו זה לא רצוי, כי אנחנו כן רוצים ליצור קבלה עבור כל תרומה (יש כאן מישהו ממס הכנסה?) אבל ייתכנו מקרים אחרים שבהם זה שייך. - הקטנה בצריכת משאבים
הרבה מבסיסי הנתונים משתמשים במנגנון מטמון Cache עבור אחסון זמני של נתונים. גם בבחירת חלק מהעמודות (למשל על ידי שאילתת בחירה), לרוב ה-DB מייבא את כל השורה למטמון. הפרדת נתונים תקטין את המשאבים הנדרשים עבור כך.
במקרה שלנו זה ממש לא קריטי, אבל תארו לעצמכם טבלה עם 200 או 300 עמודות (וכן, יש דברים כאלו), מסתמא המערכת נדרשת רק לחלק מהעמודות ברוב הקריאות ל-DB. - יצירת טריגרים מבוססי טבלה מדוייקים יותר (מחוץ לתחום הדרכה זו)
חשוב לציין כי להפרדת נתונים על ידי יחס אחד לאחד יש גם חסרונות, בעיקר אם תמיד נצטרך לייבא את שתי הטבלאות על ידי JOIN. במקרה כזה עדיף למקם את כל הנתונים בטבלה אחת.
אז בואו נראה כיצד עושים זאת:
ניצור טבלה חדשה עבור הקבלות, עם השדות הבאים:
DonationID – מפתח ראשי, מסוג מספר שלם ארוך. ערך ברירת מחדל Null
ReceiptNumber – מספור אוטומטי
ReceiptDate – תאריך\שעה. ערך ברירת מחדל =Now()
ReceiptMessage – מסוג טקסט קצר.
נשמור את הטבלה בשם Receipt.שימו לב כי בשיטת עיצוב זו פתרנו בקלילות את שתי הבעיות שהעלנו לעיל: ניהול מספר רץ עבור הקבלה, וניהול תאריך ההנפקה, והעברנו את האחריות על כך למערכת בלבד. בכך חסכנו לעצמנו כאב ראש ושגיאות.
הגדרת יחסי הגומלין
נפתח את עורך יחסי הגומלין, ונייבא את טבלת הקבלות.
כעת נגרור את שדה ID מטבלת Donation אל השדה DonationID בטבלת הקבלות, ונבחר בחלון עריכת קשרי הגומלין: "אכוף שלמות הקשרים בין הטבלאות".
שימו לב שאקסס יצרה יחס של אחד לאחד בין הטבלאות:
מהיכן היא יודעת לעשות זאת?
אקסס מזהה כי יצרנו קשר גומלין בין שני שדות מפתח. מציאות זו תיתכן רק בקשר של אחד לאחד.
סיגרו את החלון ושימרו את השינויים.יצירת טופס להקלדת נתוני קבלה
כעת ניצור את הטופס עבור הקבלות, שוב באופן אוטומטי: נבחר את טבלת התרומות, ומהתפריט למעלה: יצירה > טופס. נעבור לתצוגת עיצוב ונערוך בטופס זה כמה שינויים: נסיר את שדה המפתח, ונשנה את גודל הכותרת, תיבות הטקסט ונקטין גם את הטופס:
כעת נבחר את תיבת הטקסט של ההודעה לתורם, ובמאפיינים תחת הלשונית "אחר" נשנה את המאפיין "התנהגות מקש Enter" ל: שורה חדשה בשדה:
ברירת המחדל של מקש Enter בטופס היא מעבר לשדה הבא, אולם בהודעה האישית לתורם, ייתכן ונרצה להכניס ירידות שורה. הגדרה זו קובעת כי אם נרצה להוסיף ירידת שורה בהודעה האישית, נוכל לעשות זאת על ידי מקש אנטר, ואקסס תכניס תו ירידת שורה בשדה, במקום לעבור לשדה הבא.
נסגור ונשמור את הטופס בשם: frmReceipt.
כעת נוסיף את הטופס כטופס משנה לטופס כרטיס התרומה: frmDonationCard, על ידי פתיחת הטופס של כרטיס התרומה וגרירת הטופס החדש לשם:
במאפיינים של פקד טופס המשנה, נעבור ללשונית "נתונים", ונשים לב כי אקסס קבעה את הקשר בין הטפסים לפי הקשר שיצרנו:
נעבור ללשונית "תבנית", ונגדיר את המאפיין עוגן אופקי ל-ימין, כדי שטופס המשנה יוצג בצד הנכון של טופס האב:
נסגור את טופס האב ונשמור את השינויים. לאחר מכן נפתח אותו שוב, ונכניס את ההודעה אישית עבור התרומה הראשונה:
שימו לב שאקסס הקצתה את מספר הקבלה ואת תאריך ההנפקה באופן אוטומטי:
סגרו את הטופס ופתחו את טבלת הקבלות, שימו לב שאקסס גם קבעה נכון את שדה המפתח, כך שיתאים לתרומה:
בדק בית
המערכת עובדת די טוב, אך יש עוד כמה שיפורים לבצע:-
אין אפשרות ליצור קבלה מבלי להכניס ערך כלשהו. ומה אם איננו מעוניינים להוסיף טקסט אישי לקבלה?
את בעיה זו נשאיר לכם לפתור בשיעורי הבית. -
בטופס המשנה, לא אמורה להיות יותר מקבלה אחת עבור כל תרומה, ולכן לחצני הניווט שם מיותרים. אם ננסה להכניס יותר מקבלה אחת עבור תרומה מסויימת, נקבל שגיאה.
בואו נראה:
פתחו שוב את טופס כרטיס התרומה, ובטופס המשנה של הקבלה, לחצו על לחצן הרשומה החדשה, והכניסו טקסט כלשהו בהודעה:
נסו לסגור את הטופס. אקסס לא תאפשר לשמור את הרשומה, ותציג הודעת שגיאה:
לחצו על אישור.
אקסס תציג את השאלה הבאה:
בחרו כן.
כתוצאה מהמהלך הזה, הנתונים שהכנסתם יאבדו. גם המספר הרץ של הקבלות יקפוץ על המספר 2, והקבלה הבאה תקבל את המספר 3, כי המספר 2 כבר "שרוף" במחיקת הרשומה השגויה.
פתרון לבעיה
כדי להימנע מלהיכנס בכלל למצב שכזה, נבצע כמה שינויים.
אנו מעוניינים לחסום אפשרות להוספת קבלה לתרומה שיש לה קבלה קיימת. לשם כך נשתמש במאפיין "אפשר תוספות" או באנגלית "AllowAdditions" של הטופס. כאשר מאפיין זה מכיל ערך True, ניתן להוסיף רשומות. וכאשר הוא מכיל את הערך False, אין אפשרות להוסיף רשומות.כיון שהרשות להוספת קבלה לתרומה נקבעת על ידי התרומה הנוכחית (אנו רוצים לאפשר זאת רק אם אין לה כבר קבלה), נשתמש בפונקציית VBA שנצמיד לארוע של התרומה הנוכחית בטופס האב.
פתחו את טופס האב frmDonationCard בתצוגת עיצוב, וצרו פונקציה עבור הארוע "בנוכחי":
הקלידו את הקוד הבא:Private Sub Form_Current() If Me.frmReceipt.Form.Recordset.RecordCount = 0 Then Me.frmReceipt.Form.AllowAdditions = True Else Me.frmReceipt.Form.AllowAdditions = False End If End Sub
הקוד הנ"ל בודק את מספר הרשומות בקבוצת הרשומות של טופס המשנה (שורה 2), ואם הוא שווה 0 (=אין קבלה), אז קובע את ערך המאפיין AllowAdditions בטופס המשנה ל-True, וכך מאפשר הוספת רשומה חדשה (שורה 3), אחרת, הוא קובע את המאפיין ל False, ובכך חוסם את האפשרות (שורה 5).
סגרו ושמרו.עדיין יש כאן פתח לשגיאות, כי כאשר ניצור תרומה חדשה, המאפיין AllowEditions בטופס המשנה יהיה על True, ונוכל להכניס עוד תרומה. עלינו לדאוג להעבירו למצב False לאחר ההוספה. לשם כך נשתמש בפונקציה נוספת שנצמיד כמובן לארוע "לאחר הוספה" של טופס המשנה.
פתחו את טופס המשנה frmReceipt בתצוגת עיצוב, וצרו פונקציה עבור הארוע "לאחר הוספה":
הקלידו את הקוד הבא:
Private Sub Form_AfterInsert() Me.AllowAdditions = False End Sub
חיזרו לטופס על ידי הקשה על Alt+F11, עיברו ללשונית תבנית, והסירו את לחצני הניווט מהטופס, הם מיותרים כעת:
תוספת: הכינוי Me
כדי לגשת לטופס הפתוח אנו משתמשים במילה השמורה Me. המילה Me היא כינוי למופע של מחלקת הטופס שהפעיל את האירוע.מה הפשט?
הטופס הוא בעצם סוג של תבנית. הוא דוגמא שממנה אנו יכולים ליצור כמה מופעים של אותו הטופס, בדיוק כמו תבנית צורה של עוגיה, שממנה ניתן ליצור כמה עוגיות זהות.
לדוגמא, במערכת ההפעלה ניתן לפתוח במקביל כמה מסמכים של וורד, בכל פתיחה של מסמך חדש, מערכת ההפעלה יוצרת מופע חדש של תוכנת וורד.
בשפת תכנות, לתבנית קוראים בשם מחלקה (או מודל). ולצורות שיוצרים ממנה קוראים מופעים.
כל מופע מכיל את כל הפונקציונאליות והמאפיינים של המחלקה שממנה נוצר.
אם נחשוב על כך, כל טופס יכול באופן עקרוני להיות פתוח בו זמנית כמה וכמה פעמים.
נתאר לעצמנו מצב דמיוני שבו הטופס פתוח ב-10 מופעים. כאשר נסגור מופע אחד, אותו מופע יפעיל את הארוע "סגירת טופס", אפילו שתשעת המופעים האחרים פתוחים. כיצד נדע מתוך הקוד, איזה מופע של הטופס נסגר?לשם כך שפת VBA מספקת לנו את המאפיין Me ככינוי שבאמצעותו ניגשים ישירות למופע הפעיל של הטופס שהריץ את הקוד.
או אם תרצו: אם אין אני לי – Me לי.שיעורי בית:
כדי להתחמק מן הבעיה הראשונה שהזכרנו לעיל, נכריח את המשתמש להכניס נתון כלשהו בקבלה: אמצעי השליחה.-
צרו טבלה חדשה בשם DeliveryMethod שתכיל שדה מזהה ID ושדה נוסף בשם Description.
טבלה זו תכיל את אפשרויות השליחה של הקבלה, כאשר 1 מציין שהקבלה תישלח בדואר רגיל, ו-2 מציין כי הקבלה תישלח בדואר אלקטרוני. -
הוסיפו שדה נוסף לטבלת הקבלות בשם DeliveryMethodID מטיפוס מספר ארוך. הערכים לשדה זה יילקחו מהטבלה בסעיף א, על ידי תיבה משולבת. קבעו את ערך ברירת המחדל של השדה ל-Null.
-
בטבלת הקבלות, הפכו את כל השדות לנדרשים, למעט השדה של ההודעה האישית.
-
הוסיפו פקד תיבה משולבת עבור השדה הנ"ל בטופס הקבלה.
-
פרק כא: הפקת קבלות: חלק ב: עיקרון ה-DRY, מחולל הדוחות
כדי שנוכל להדפיס את הקבלות שיצרנו, אנו צריכים ליצור דוח.
באופן עקרוני, דוח באקסס מאוד דומה לטופס:- הוא יכול להיות מאוגד לטבלה, או לא מאוגד
- הוא יכול להכיל את רוב הפקדים שיכולים להימצא בטופס
- יש לו מגוון של ארועים שניתן להצמיד אליהם קוד VBA
- הוא יכול להכיל דוחות משנה
היתרון הגדול ביצירת דוח הוא היכולת להציג נתונים בצורה נוחה להדפסה.
אז בואו וניצור את הדוח הראשון שלנו: דוח עבור קבלות לתורמים.
נחשוב:
אילו נתונים צריכים להופיע על הקבלה?
מסתמא נרצה את כל הנתונים שבטבלת הקבלות, אך חסרים שם הרבה פרטים, כמו סכום ותאריך התרומה, ושם התורם והכתובת שלו.למעשה, אין לנו שדה כתובת בטבלת התורמים עדיין, אז בואו ניצור אחד כזה:
כדי להציג את כל פרטי הקבלה, ניצור שאילתת צירוף.שלב א: יצירת שאילתת צירוף להצגת פרטי הקבלה
ניצור שאילתה חדשה, ונוסיף את שלושת הטבלאות: קבלות, תרומות, ותורמים, למשטח העבודה, אקסס תזהה את הצירוף בין הטבלאות לפי מה שהגדרנו ביחסי הגומלין:
נבחר להציג את השדות הבאים:
הממ... עדיין חסר לי את שדה שם התורם.
אני יכול להציג את שם המשפחה ואת השם הפרטי בנפרד, אבל אני רוצה כמובן להציג את שם התורם המלא.
לא בעיה!
אני יכול ליצור שדה מחושב FullName שמחבר את השם הפרטי ושם המשפחה של התורם.אבל רגע, זה נשמע לי מוכר...
יש לי כבר שאילתה שעושה זאת: qryDonor_FullName.במקום ליצור שוב שדה מחושב, בואו ונשתמש בה.
למה?עיקרון ה-DRY
לפני כעשרים שנה, פירסמו שני מתכנתים אמריקאים את העיקרון הבא בתכנון מערכות:
Don't Repeat Yourself (אל תחזור על עצמך) או בקיצור DRY. הנוסח הרשמי לעיקרון זה הוא:
לכל פיסת ידע צריך להיות ייצוג יחיד, חד משמעי וסמכותי במערכת.
במילים פשוטות, זה אומר שלא נשכפל שום חלק במערכת אלא אם כן הדבר הכרחי.עקרון זה נועד כדי להפחית כפילויות מיוצרות במערכת, ועל ידי כך ליצור מערכת קלה לתחזוקה, כי שינוי של גורם יחיד במערכת לא יאלץ שינויים נוספים בגורמים אחרים שאינם קשורים אליו לוגית.
ניקח לדוגמא את המקרה שלנו:
כרגע, שם התורם המלא מוצג לפי הסדר הבא: שם משפחה + רווח + שם התורם. נניח שבעוד שנה ירצה בעל הבית להציג את שמות התורמים המלאים בסדר הפוך, דהיינו במקום כהן אברהם – אברהם כהן וכו'.
אם הרכבת השם המלא של התורם מבוצעת רק במקום יחיד במערכת, דהיינו בשאילתה הנ"ל, כל שעלינו לעשות הוא לשנות את הגדרת השדה המחושב שם – והשינוי ישתקף בכל מקום במערכת.
אבל, אם בכל פעם יצרנו שוב את השדה המחושב מחדש – זה הופך לעבודת נמלים מייגעת...אז יופי, בואו ונוסיף גם את השאילתה למשטח העבודה, והופ – נראה שהפעם אקסס לא הייתה כל כך מבריקה, והחליטה לצרף את השדה ID מטבלת התרומות לשדה ID בשאילתה, כפי שמוצג בדרמטיות בתמונה הבאה:
מדוע זה קרה?
אקסס זיהתה את שם השדה ID הזהה בשתי מקורות הנתונים, והחליטה "לעזור לנו" על ידי יצירת הצירוף עבורנו. אבל זה כמובן לא נכון!אם היינו קוראים לשדה המפתח בכל טבלה בשם מלא, כפי שנהגנו במפתח הזר, למשל DonationID במקום ID בטבלת התרומות, או DonorID במקום ID בטבלת התורמים וכו', המנגנון של אקסס אכן היה עוזר לנו.
זו אכן סכמה נפוצה, ובאמת בתחילת דרכי כך נהגתי, אבל היא פחות מקובלת, ובקוד היא נעשית מסורבלת עוד יותר, כי ניגשים לשדה המזהה כך: Donor.DonorID במקום כך: Donor.ID.
לכן אני מעדיף לוותר על העזרה של אקסס כאן, וליצור ידנית את הצירוף, תמורת הרווח של קוד ושאילתות יותר קריאים.בואו נתקן את הצירוף:
נבחר את החץ ונמחק אותו על ידי הקשה על מקש Delete, ובמקום, נגרור את שדה ID מטבלת התורמים לשדה ID בשאילתת השם המלא, כך:
כעת נוסיף גם את השדה FullName לחגיגה:
נעבור לתצוגת גליון נתונים כדי לראות שהכל תקין. על הדרך, בואו נוסיף עוד הודעות אישיות לתרומות של כהן אברהם:
נשמור את השאילתה בשם: qryReceipt_Details (=פרטי קבלה)
כעת ניתן סוף סוף לעבור לשלב יצירת הדוח.שלב ב: יצירת דוח עבור הקבלה
בחרו מתפריט היצירה > עיצוב דוח:
אקסס תייצר עבורנו דוח ריק. שימו לב שהדוח מחולק לשלושה אזורים:
- כותרת עליונה בעמוד: חלק זה יודפס בראש כל עמוד חדש.
- פירוט: החלק העיקרי של הדוח.
- כותרת תחתונה בעמוד: חלק זה יודפס כמובן בתחתית כל עמוד.
ישנם עוד חלקים נוספים שאפשר לערוך בדוח, כפי שנראה אי"ה בהמשך.
כדי שהטופס יציג לנו את נתוני הקבלות, צריך לאגד אותו לשאילתה שיצרנו. לשם כך נבחר בפקד הדוח, על ידי לחיצה על הפינה השמאלית העליונה (בדומה לפקד טופס, זהו הפקד שנבחר אוטומטית בתצוגת עיצוב):
כעת נלחץ על מקש F4 כדי להציג את חלונית המאפיינים, ניגש לכרטיסית הנתונים, ובמאפיין מקור רשומה נבחר את שאילתת פרטי הקבלות qryReceipt_Details:
נבחר מהתפריט למעלה את האפשרות: הוסף שדות קיימים כדי להציג את השדות שנמצאים בטבלה:
נבחר את כל השדות ונגרור אותם לתוך חלק הפירוט של הטופס:
נעצב את הדוח כיד הדמיון הטובה עלינו:
כשסיימנו, נבחר מתפריט העיצוב את התצוגה: הצג לפני הדפסה:
כדי לראות את התוצאה:
שימו לב כי בתצוגה זו מוצגת בכל עמוד קבלה אחת בלבד. כדי לעבור לעמוד הבא, נשתמש בסרגל הניווט למטה:
נשמור את הדוח בשם: rptReciept_Details.
זהו לבינתיים, מקווה שנהנתם!
בפרק הבא אי"ה נלמד על טפסים רציפים.
אני משאיר כמה שיעורי בית, בעיקר בעיצוב (מהקל למאתגר):
- הסירו את המסגרות מסביב לתיבות הטקסט בדוח.
- הוסיפו בכותרת התחתונה של העמוד את הכיתוב: "הודפס על ידי מערכת לניהול תורמים גירסא 0.01"
- הוסיפו את שעת ההדפסה המדוייקת בכותרת העליונה.
- הצמידו את ראשי התיבות נ"י לשם התורם כך שיופיעו מיד לאחריו בצורה יפה בלי רווח גדול.
- הסירו את השעה מתאריך החשבונית ומתאריך התרומה.
בהצלחה!
-
פרק כב: עושים סדר: חלק א: אפיון מדיוק של המערכת, טפסים רציפים
המערכת שלנו בינתיים מסוגלת לכמה דברים, אבל לא מאורגנת בכלל.
נו, אז הגיע הזמן לעשות קצת סדר.הזכרתי כבר שבמערכת אמיתית, שלב התכנון הוא הראשון, עוד בטרם ניגשים בכלל למחשב. פשוט לוקחים דף ועט ומציירים על הנייר. אין צורך לסרטט את הכל במחשב, לפחות לא בהתחלה. זה סתם מסרבל.
אז לאחר שישבתי וחשבתי, הגעתי לאפיון המופיע בסרטוט הבא:
המלבנים מייצגים טפסים והסנפירים למטה מייצגים את הפונקציונאליות שאני מבקש בטפסים אלו.
את הדף הזה אני אתלה מול עיני בזמן הפיתוח, כך תהיה לי דרך סלולה ללכת בה, והכי חשוב: אני אדע ברור מתי כבר סיימתי. מעתה נשתדל להיצמד במדריך לאפיון הנ"ל.אגב, את התרשים הנ"ל יצרתי בעזרת כלי נפלא בשם Draw.io. זהו כלי מדהים ליצירת תרשימי זרימה, סרטוטים ועוד, קל ונוח, עם אפשרות לעברית מלאה, וקיים בגרסא מקוונת או להורדה, והוא חינמי לחלוטין!
למי שעוקב אחרי ההדרכה, הנה הקובץ של הסרטוטתוכלו להכניס בו שינויים כרצונכם.
סדר הפיתוח הנכון
אני מאוד ממליץ להתחיל לבנות את המערכת מלמטה למעלה, כלומר קודם כל לבנות את הרבדים הנמוכים של המערכת, ומשם לטפס למעלה.נתחיל ברגל ימין עם צד ימין של המשפחות.
ציינתי שיש שם טופס שמציג את רשימת המשפחות.
מה אני רוצה שיראו בו?
כמובן את פרטי המשפחה, ולפי האפיון, גם את סיכום התרומות למשפחה.לשם כך ניצור שאילתת עזר, שתקבץ את הנתונים של התרומות לפי משפחה.
שלב א: יצירת שאילתת סיכום תרומות למשפחה
נפתח שאילתה חדשה, נהפוך לשאילתת קיבוץ, נייבא את טבלת התרומות, ונגדיר את השדות לתצוגה באופן הבא:
נשמור את השאילתה בשם qryFamily_DonationSummary
שלב ב: יצירת השאילתת הבסיס לרשימת המשפחות:
השאילתה הזו תושתת על צירוף של שתי השאילתות שיצרנו:
- שאילתה א: שמות המשפחה המלאים: qryFamily_FullName
- שאילתה ב: סיכום התרומות למשפחה: qryFamily_DonationSummary.
נתבונן ונשים לב, שעלינו לצרף אותן בצירוף חיצוני, כי למרות שכל המשפחות מופיעות בשאילתה א (שמות המשפחה המלאים), שהרי היא מבוססת על טבלת המשפחות, יתכן ויש משפחה שלא תופיע בשאילתה ב (סיכום התרומות למשפחה), מפני ששאילתה ב מבוססת על טבלת התרומות, ותכיל רק משפחות שכבר יועדו להן תרומות.
להלן מבנה השאילתה:
נשמור את השאילתה בשם: qryFamily_FullName_DonationSummary
כעת הכל מוכן ליצירת טופס רשימת המשפחות.תצוגת גליון נתונים .VS תצוגת טפסים רציפים
לאקסס יש שתי אפשרויות להציג רשומות מרובות בטופס יחיד:
- תצוגת גליון נתונים
- תצוגת טפסים רציפים
תצוגת גליון נתונים מציגה את הנתונים בטבלה פשוטה, בדומה לגליון אקסל. ולמרות שלעתים זה מספק, לרוב נרצה להשתמש בתצוגת טפסים רציפים, שמאפשרת גמישות רבה יותר.
בואו נראה:
נסמן את השאילתה שיצרנו, ונבחר מתפריט העיצוב: "טפסים נוספים" > "פריטים מרובים". אקסס תיצור טופס ותפתח אותו בתצוגת פריסה:
נעבור לתצוגת עיצוב, ונראה כי באמת הטופס מחולק לשלושה חלקים (בדומה לדוח בפרק שעבר):
- כותרת עליונה: משמשת בעיקר להצגת כותרות העמודות
- פירוט: מציג את הרשומה הנוכחית
- כותרת תחתונה: בה נשתמש להצגת סיכומי העמודות
הערה: למרות שחלק הפירוט מראה שורה אחת בלבד, כשנעבור לתצוגת טופס יוצגו בפירוט כל הרשומות. כל עיצוב שנבצע בחלק זה - ישפיע על כל שורה ושורה בתצוגת הטופס.
שלב ג: הוספת סיכום עמודות
בואו ונרחיב את החלק של הכותרת התחתונה של הטופס, על ידי גרירה מטה של הגבול:
כעת נוסיף שם שלוש תיבות טקסט לא מאוגדות, ותווית אחת עם הכיתוב "סיכום", כפי שמופיע בתמונה הבאה:
בכל אחת מתיבות טקסט אני רוצה שיופיע סיכום של כל העמודה שמעליה.
אבל לפני כן אנחנו חייבים לטפל בדבר חשוב.
מי אני ומה שמי?
לאקסס יש הרגל מגונה לתת לפקד מאוגד בטופס שם זהה לשם השדה בטבלה שאליו הוא מאוגד. למשל, תיבת הטקסט של שם המשפחה קיבלה את השם FullFamilyName, שזהה בדיוק לשם השדה FullFamilyName בטבלה שעליה מבוסס הטופס.
בכך אקסס גורמת לי חוסר ודאות כמפתח המערכת, האם FullFamilyName מתייחס לתיבת הטקסט או לשדה בטבלה? וחוסר ודאות - זה מתכון מצוין לבעיות....
לשם כך אני תמיד רגיל ביצירת טופס להפריד בין השניים, ולשנות את שם הפקד המאוגד לשם שונה משם השדה, על ידי הוספת קידומת מיוחדת לשם הפקד, לפי סוג הפקד.
לדוגמא:
- לתיבת טקסט אני אוסיף קידומת "txt"
- לתווית אני אוסיף קידומת "lbl"
- לתיבה משולבת אני אוסיף קידומת "cbo" וכו'
זוהי אמנם עבודה מעט מרגיזה, אבל כמו תמיד, היא משתלמת מאוד כשמתחילים להוסיף קוד בטופס.
לשם כך אני אשנה את שמות ארבע תיבות הטקסט המאוגדות בחלק הפירוט של הטופס, לפי הטבלה הבאה:
שם קודם שם חדש ID txtID FullFamilyName txtFullFamilyName TotalDonationCount txtTotalDonationCount TotalDonationAmount txtTotalDonationAmount על ידי כניסה למאפיינים של הפקד (בלשונית "אחר" או בלשונית "הכל") והוספת הקידומת "txt" לשם הפקד, לדוגמא:
הערה: יש הנוהגים להוסיף סיומת לשם הפקד (במקום קידומת) דהינו FullFamilyNameTextBox וכו', זה בהחלט אפשרי, אך לטעמי מסורבל יותר. והבוחר יבחר.
למעשה, היינו צריכים להנהיג הנהגה זו כבר מתחילת המדריך, מהטופס הראשון שעשינו, אך נמנעתי מלהזכיר זאת עד עתה, פן ינחם העם בראותם מלחמה...
על כל פנים, מעתה זו תהיה הנחת היסוד: מכאן והלאה לבצע את שינוי השם לכל שדה מאוגד בטופס.
את הטיפול בכל הטפסים הקודמים נשאיר לשיעורי הבית (מסכן @בערל...)סיימתם?
כעת נוכל לבנות את הנוסחא לסיכום העמודות, נתחיל עם עמודת שמות המשפחה, שם אנו רוצים להציג את מספר המשפחות בטופס. נבחר את תיבת הטקסט שבכותרת התחתונה של העמודה של שמות המשפחה:
במאפייני הפקד, בלשונית "נתונים" בחרו את המאפיין "מקור הפקד" ולחצו על שלושת הנקודות כדי לפתוח את בונה הביטויים:
הקלידו בבונה את הנוסחה הבאה:Count(ID)
עברו לתיבת הטקסט בתחתית העמודה השנייה, ושם תכניסו באותו אופן את הנוסחה:
Sum(TotalDonationCount)
ובתיבה האחרונה את הנוסחה:
Sum(TotalDonationAmount)
שנו גם את תבנית העיצוב של תיבה זו ל"מטבע":
שלב ד: קצת קוסמטיקה:
כבר הזכרתי שאני משתדל להימנע מלמרוח את הזמן על עיצובים, אך לפעמים זה פשוט נותן הרגשה טובה ונעימה יותר להמשיך בפיתוח, אז אני חורג מהרגלי פה ושם. עם זאת, אני משתדל לא להתפתות להתחיל לשנות את עיצוב כל הטפסים במערכת בשלב זה, כדי שלא להיתקע על קו עיצוב מסויים ולהיות משועבד לו מכאן ואילך, בפרט כשלרוב מחליטים לשנות אותו בהמשך...
בחרו את כל הפקדים בכותרת התחתונה, ושנו את סגנון הרקע לשקוף, ואת צבע הכתב לשחור, עם סגנון מודגש:
לחצו על הפס האפור שעליו כתוב "כותרת תחתונה בטופס", והגדירו את צבע הרקע לתכלת:
שנו גם הכותרת הראשית ואת תוויות הכותרת של העמודות לעברית.
כעת עברו לתצוגת טופס, ושימו לב כי מופיעים סיכומים של העמודות בתחתית הטופס:
שלב ה: הוספת לחצנים לפונקציונאליות של הטופס
כיוון שבא לידינו עיצוב טופס זה, נוסיף לו עוד שלושה לחצנים, אחד בכותרת העליונה להוספת משפחה למערכת, ושנים בחלק הפירוט: אחד לפתיחת כרטיס המשפחה ואחד למחיקת המשפחה מהמערכת (את הפונקציונאליות שלהם נממש בהמשך)
נחזור לתצוגת עיצוב.
בתפריט העיצוב, נבחר את לחצן הפקודה מסרגל הפקדים:
נקליק בכותרת העליונה של הטופס במקום שבו אנו רוצים ליצור את הלחצן:
אקסס תיצור לחצן עם השם "פקודהXX" ותפתח את אשף לחצני הפקודות, שאליו נסרב להתייחס ופשוט נסגור אותו בנימוס, על ידי לחיצה על "ביטול":
האשף הנ"ל מציע ליצור עבורנו פקודות מאקרו לבניית הפונקציונאליות של הלחצנים, אך אנו נכתוב זאת בעצמנו על ידי קוד VBA בהמשך.
נשנה את שם הלחצן ל cmdNewFamily ואת הכיתוב עליו ל"משפחה חדשה":
נוסיף עוד שני לחצנים בחלק הפירוט של הטופס, אחד מימין בראש השורה, בשם cmdOpenFamilyCard ועם הכיתוב "הצג", ואחד משמאל בסוף השורה בשם cmdDeleteFamily עם הכיתוב "מחק":
כעת אין צורך בבוררי הרשומות ולחצני הניווט של הטופס, אז בואו נסיר גם אותם (בחרו קודם בפקד הטופס, בפינה השמאלית העליונה, זוכרים?)
כשנסיים, נשמור את הטופס בשם frmFamilyList.
והנה הטופס המוכן, אחרי עוד כמה קנעטשים:
בפרק הבא ניצור גם את טופס כרטיס המשפחה, ונוסיף פונקציונאליות ללחצנים שיצרנו היום.
-
פרק כג: עושים סדר: חלק ב: שדרוגים שונים, פונקציונאליות לחצני רשומה, ורגע למחשבה.
תזכורת:
בפרק הבא ניצור גם את טופס כרטיס המשפחה, ונוסיף פונקציונאליות ללחצנים שיצרנו היום.
גילוי נאות: בפרק זה עדיין לא נוסיף פונקציונאליות ללחצנים הנ"ל, אלא ללחצנים אחרים שניצור.
שלב א: שדרוג כרטיס המשפחה
בואו ניזכר, כי בפרק יד יצרנו כבר טופס בשם 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 בסך הכל מציינת קשר גומלין של רבים לרבים, ומחיקת רשומה ממנה אינה משפיעה על טבלת נתוני התורמים בשום צורה, ובוודאי שאינה מוחקת שום תורם, לכן היא בטוחה.המשך יבוא אי"ה...
-
פרק כד: עושים סדר: חלק ג: תפריטים ופקדי ניווט, פונקציונאליות ברשימת המשפחות
שלום לכולם! מקווה שנהנתם מהחופש הגדול...
היום הגענו לחלק די קל ומהנה (יחסית...) והוא בניית התפריטים.
אחד הדברים שחסרים מאוד במערכת שלנו הוא התפריט הראשי, (או הדשבורד), שדרכו ננווט את דרכנו בין הטפסים השונים, במקום לבחור בכל פעם את הטופס מהפנל מימין.
אני רוצה ליצור משהו כזה:
למרבה המזל, לאקסס יש מנגנון מובנה לניהול הניווט בין טפסים, בשם פקד הניווט.
אז בואו ניצור משהו כזה.שלב א: יצירת טופס תפריט המשפחות
נפתח טופס חדש ריק, על ידי יצירה > עיצוב טופס.
נבחר מתפריט העיצוב את פקד הניווט:
ואז פשוט נקליק בתוך הטופס קרוב לפינה הימנית העליונה:
אקסס תיצור עבורנו פקד ניווט:
לפקד הניווט שני חלקים:
- החלק העליון הוא פקד הניווט עצמו, זהו בעצם התפריט, שמכיל את הלחצנים לבחירת הטפסים לתצוגה.
- החלק התחתון הוא פקד טופס המשנה לניווט, ותפקידו להציג בתוכו את הטפסים שבחרנו מן התפריט.
הערה חשובה: כברירת מחדל, שם פקד טופס המשנה הוא "NavigationSubform". למרות שאפשר לשנות את שמו לכל שם שנרצה, לעת עתה נשאיר אותו עם שם זה. פרט זה יתגלה כחשוב בהמשך הדרך.
נוסיף לחצן לפקד הניווט על ידי לחיצה על המקום שמסומן [הוסף חדש], ונרשום בתוכו "רשימת משפחות". אקסס יצרה עבורנו לחצן חדש:
נגדיל את הלחצן כך שיציג את כל הכיתוב:
נוסיף עוד לחצן עם הכיתוב "כרטיס משפחה":
כעת נותר לנו להגדיר איזה טופס ייפתח על ידי כל לחצן.
ניכנס למאפיינים של הלחצן הראשון (רשימת המשפחות), ובלשונית הנתונים נבחר את המאפיין "שם יעד ניווט", ונבחר מתיבת הרשימה המשולבת את טופס רשימת המשפחות frmFamilyList:
באופן דומה, גם בלחצן השני, נבחר את שם יעד הניווט להיות טופס כרטיס המשפחה frmFamilyCard:
לסיום, נסיר מן הטופס את הפקדים המיותרים: בוררי הרשומות, לחצני ניווט ופסי הגלילה: (להלן: "שלשת המעצבנים")
ונצמצם את השוליים המיותרים מסביב לפקד הניווט (מסומנים בכתום):
נסגור את הטופס ונשמור אותו בשם navFamilies.
כעת פתחו את הטופס שיצרנו ובידקו את לחצני הניווט:
שלב ב: יצירת טופס התפריט הראשי
בדומה לשלב הקודם, נפתח טופס חדש ריק, על ידי יצירה > עיצוב טופס, ונוסיף אליו פקד ניווט, ונוסיף שלושה לחצנים כפי שמופיע בתמונה הבאה:
כעת נהפוך את הכיוון של התפריט לאנכי.
לשם כך נבחר את פקד הניווט כולו (זוכרים? החלק העליון) על ידי הקלקה במקום הריק (לא על לחצן):
שימו לב שכל פקד הניווט מסומן כעת.
מתוך המאפיינים בלשונית התבנית נגדיר את המאפיין "מרחב" ל-"אנכי". שימו לב לשינוי בפריסת הלחצנים, כעת הם מופיעים מלמעלה למטה:
אך עדיין פקד הניווט מופיע בצמוד לחלק העליון של טופס המשנה, ולא מצידו הימני כמו בתכנון.
אל דאגה, נשנה זאת מיד: נגרור את כל פקד הניווט, כך שיופיע בצמוד לדופן הימנית של טופס המשנה.
שימו לב לגרור אותו אל הדופן הימנית ממש, כך שיוצג קו בצבע ורוד חלש בצד ימין:
כעת נקטין את רוחב פקד הניווט לגודל יותר סביר:
נגדיר את שם יעד הניווט עבור לחצן המשפחות להיות טופס תפריט המשפחות שיצרנו היום: navFamilies. (בינתיים יש לנו רק את תפריט המשפחות, את שני התפריטים של התורמים והתרומות ניצור בהמשך.)
לסיום – גם כאן נסיר את הפקדים המיותרים בטופס (שלשת המעצבנים כדלעיל), ואת השוליים המיותרים מסביב לפקד הניווט.
נשמור את הטופס בשם navMain.להלן התוצאה:
נהדר, זה לא היה קשה, נכון?
קחו אויר, כי החלק הבא קצת יותר מורכב.
שלב ג: הוספת פונקציונאליות ללחצנים שבטפסים
נשאר לנו כעת לחבר את הלחצנים שבטפסי המשפחה (אלו שיצרנו בפרק כב). נתחיל עם טופס רשימת המשפחות.לחצן תצוגת המשפחה
פתחו את הטופס בתצוגת עיצוב, ובחרו את לחצן הצגת המשפחה. שנו את שמו ל: cmdViewFamilyCard:
כעת נגדיר פונקצית אירוע לחיצה:
בלשונית אירוע, בחרו את האירוע בעת לחיצה, ופתחו את בונה הקוד על ידי לחיצה על שלושת הנקודות…
ובחירה באפשרות בונה הקוד.
אקסס תיצור תבנית עבור פונקצית הארוע של הלחצן.
הוסיפו בגוף הפונקציה את השורה הבאה:DoCmd.BrowseTo acBrowseToForm, "frmFamilyCard", "navMain.NavigationSubform>navFamilies.NavigationSubform", "ID=" & Me.ID
הסבר הקוד:
אנו משתמשים כאן באובייקט DoCmd שראינו בעבר, המכיל פונקציות שונות באקסס. מתוכו אנו מפעילים את הפונקציה BrowseTo שתפקידה לנווט אל טופס מסויים. הפונקציה מקבלת כמה פרמטרים:
- הפרמטר הראשון acBrowseToForm מציין את סוג האובייקט לתצוגה. במקרה שלנו הוא טופס.
כדי להציג דוח במקום טופס, יש לציין acBrowseToReport במקום acBrowseToForm.
- הפרמטר השני
"frmFamilyCard"
מציין את שם הטופס לפתיחה הלא הוא כרטיס המשפחה. - הפרמטר השלישי
"navMain.NavigationSubform>navFamilies.NavigationSubform"
מציין את הנתיב שבו ייפתח הטופס.
כיוון שבנינו מערכת של תפריטי ניווט מקוננים (תפריט ראשי>תפריט המשפחות) אנו צריכים לציין במפורש את כל המסלול שדרכו נגיע ליעד המבוקש (טופס המשנה של תפריט המשפחות). התחביר הוא כך:
שם_טופס_הניווט.שם_פקד_טופס_המשנה (כברירת מחדל הוא NavigationSubform)
אם יש כמה טפסי ניווט אחד בתוך השני (כמו אצלנו), מפרידים ביניהם על ידי הסימן "גדול מ"<
.
כך מתקבלת התוצאה הנ"ל.
שימו לב שיש להוסיף גרשיים מסביב לנתיב, כי הוא צריך להיות מועבר לפונקציה כמחרוזת.
- הפרמטר הרביעי
"ID=" & Me.ID
מציין קריטריון לחיפוש רשומה מבוקשת לתצוגה.
פרמטר זה הוא אופציונאלי, וכאשר מציינים אותו, אקסס תחפש בטופס שנפתח את הרשומה המתאימה לקריטריון, ותקפוץ אליה בפתיחת הטופס.
לדוגמא, אם אנו רוצים להציג את המשפחה שהמזהה ID שלה הוא 7, עלינו להעביר לפונקציה את הקריטריון: "ID=7".
הואיל וברצוננו להציג את המשפחה המתאימה לשורה שבה לחצנו על הלחצן, ואנחנו לא יודעים מה יהיה המזהה שלה, נוכל לקבל את המזהה שלה על ידי האובייקט Me.ID וניצור את הקריטריון המתאים על ידי חיבור מחרוזות.
הערה: ההסבר לעיל עלול לבלבל מאוד מתכנתים בתחילת הדרך.
אם אתם כאלו, זה נורמלי לחלוטין, ואין צורך להיבהל. כתיבת והבנת קוד, כמו כל מיומנות, הולכת ומשתפרת עם תרגול.
כמובן שישנה גם האפשרות שההסבר שלי לא מספיק ברור...
בכל מקרה, אל תזיעו יותר מידי. אני מבטיח שהעסק נהיה יותר קל עם הזמן.שימרו את הטופס.
כעת פיתחו את טופס התפריט הראשי navMain, בחרו את תפריט המשפחות, וברשימת המשפחות בידקו שהלחצן "הצג" עובד כראוי.טעות בכתובת
הניווט של אקסס אינו מושלם.
נסו לפתוח ישירות את הטופס של רשימת המשפחות frmFamilyList, וללחוץ על הלחצן "הצג".
למרבה התסכול, במקום להציג את הטופס, אקסס תציג הודעת שגיאה:
מה קרה?
ההודעה הזו מוצגת מפני שאנו מנסים לגשת לנתיב שאינו קיים.
הרי בקוד צייננו שהנתיב המבוקש הוא"navMain.NavigationSubform>navFamilies.NavigationSubform"
. אבל הטפסים של הניווט navMain ו-navFamilies לא פתוחים כעת, ולכן הנתיב שגוי.ישנה אפשרות על ידי קוד לפתור את הבעיה הנ"ל, אך לעת עתה נניח לה במקומה, ולא נלחץ על הכפתור הצג אם התפריטים לא פתוחים.
לחצן יצירת משפחה חדשה
בואו ניצור גם את הלחצן של יצירת משפחה חדשה. נבחר את הלחצן, וניצור עבורו פונקצית אירוע בעת לחיצה:
לשמחתנו, הקוד הנדרש דומה לקוד הקודם (נוכל להעתיק ולהדביק, ולשנות כנדרש):
DoCmd.BrowseTo acBrowseToForm, "frmFamilyCard", "navMain.NavigationSubform>navFamilies.NavigationSubform", , , acFormAdd
הסבר הקוד:
השמטנו את הפרמטר הרביעי (כי אנו לא רוצים לקפוץ לרשומה מסויימת), וגם את הפרמטר החמישי (לא רלוונטי כעת).- הפרמטר השישי
acFormAdd
מורה לאקסס לפתוח את הטופס המבוקש במצב הוספת נתונים, ולהציג רשומה חדשה בטופס.
שימרו את הטופס, פתחו את התפריט הראשי ובידקו שלחצן יצירת משפחה חדשה עובד כראוי.
לחצן מחיקת המשפחה
מחיקת משפחה היא תהליך מורכב, הואיל ויש לכל משפחה קשרי גומלין שונים. בנוסף, אני לא בטוח שכדאי לאפשר מחיקת משפחה מתצוגת הרשימה. לכן אני אחזור בי ואסיר את לחצן המחיקה מרשימת המשפחות, כדי לאפשר מחיקת משפחה רק מכרטיס המשפחה:
את הקוד עבור לחצני כרטיס המשפחה (כולל מחיקת משפחה) נשאיר לפעם הבאה אי"ה.
-
O OdedDvir התייחס לנושא זה ב
-
O OdedDvir התייחס לנושא זה ב
-
O OdedDvir התייחס לנושא זה ב
-
צ צבי-ש התייחס לנושא זה ב
-
O OdedDvir התייחס לנושא זה ב
-
A A0533057932 התייחס לנושא זה ב
-
-
O OdedDvir התייחס לנושא זה ב
-
מ מלא התייחס לנושא זה ב
-
-
O OdedDvir התייחס לנושא זה ב