→ كل القوالب
تعليم

كنترول ومتابعة مدرسية

مؤسسات وفروع وفصول وفترات دراسية، قواعد نجاح وغياب، ملفات طلاب ورصد درجات وشهادات.

طريقة الاستخدام: الملف المحمّل (.vbs) لا يُفتح من أكسيس. انقر عليه نقراً مزدوجاً من مجلد التنزيلات، فيُنشئ ملف SchoolControl.accdb بجواره ويعرض عليك فتحه في أكسيس. إذا منعه ويندوز: كليك يمين ← خصائص ← Unblock.

مخطط العلاقات

1∞1∞1∞1∞1∞1∞1∞1∞1∞1∞1∞1∞1∞1∞1∞Companies🔑 CompanyIDترقيم تلقائيCompanyNameنص مختصرRegistrationNoنص مختصرTaxNumberنص مختصرPhoneنص مختصرAddressنص مختصرBranches🔑 BranchIDترقيم تلقائيCompanyIDرقمBranchNameنص مختصرPhoneنص مختصرAddressنص مختصرSchoolRules🔑 RuleIDترقيم تلقائيCompanyIDرقمRuleNameنص مختصرPassPercentرقم عشريAbsenceLimitرقمNotesنص مختصرClasses🔑 ClassIDترقيم تلقائيCompanyIDرقمBranchIDرقمClassNameنص مختصرGradeنص مختصرTerms🔑 TermIDترقيم تلقائيCompanyIDرقمRuleIDرقمTermNameنص مختصرAcademicYearنص مختصرStartDateتاريخ/وقتEndDateتاريخ/وقتStudents🔑 StudentIDترقيم تلقائيCompanyIDرقمBranchIDرقمStudentNameنص مختصرClassIDرقمSeatNoنص مختصرBirthDateتاريخ/وقتAddressنص مختصرParentNameنص مختصرParentPhoneنص مختصرSubjects🔑 SubjectIDترقيم تلقائيCompanyIDرقمSubjectNameنص مختصرMaxScoreرقم عشريGrades🔑 GradeIDترقيم تلقائيStudentIDرقمSubjectIDرقمTermIDرقمScoreرقم عشريMaxScoreرقم عشريPassPercentرقم عشريPercentageرقم عشريPassedنعم/لاAbsence🔑 AbsIDترقيم تلقائيStudentIDرقمTermIDرقمAbsDateتاريخ/وقتExcusedنعم/لا

المميزات

  • إعدادات المؤسسة وقواعد النجاح والغياب
  • فروع وفصول وملفات طلاب
  • فترات دراسية مرتبطة بالقواعد
  • حدود درجات ومنع تكرار الرصد
  • نتائج وشهادات وتنبيه الغياب

الشاشات والنماذج

  • frmCompanies

    ملف الشركات والمؤسسات

  • frmBranches

    الفروع والتبعية للشركات

  • frmSchoolRules

    قواعد النجاح وحد الغياب

  • frmClasses

    الفصول والفروع

  • frmTerms

    الفترات والسنوات الدراسية

  • frmSubjects

    المواد والدرجات العظمى

  • frmStudents

    ملف الطالب وولي الأمر

  • frmGrades

    رصد الدرجات والنتيجة

  • frmAbsence

    الغياب والأعذار

  • rptCertificate

    نتيجة الطالب لكل فصل دراسي

  • rptAbsenceAlert

    تنبيه تجاوز حد الغياب

معاينة الشاشات الاحترافية

كنترول ومتابعة مدرسية

القائمة الرئيسية

خروج من البرنامج

كود إنشاء الشاشات تلقائياً (VBA)

بعد إنشاء القاعدة: افتحها ← Alt+F11 ← Insert ← Module ← الصق الكود ← ضع المؤشر داخل BuildAllScreens واضغط F5.

SchoolControl_Screens.bas
' ===== Module: modBuildScreens  (SchoolControl) =====
' Run BuildAllScreens (put the cursor inside it and press F5)
' Close all open forms and reports before running.

Private Const TW As Long = 567            ' 1 cm in twips
Private Const PRIMARY As Long = &H5A3A12  ' deep navy (BGR)
Private Const ACCENT As Long = &H2E9BF0   ' amber (BGR)
Private Const SURFACE As Long = &HF7F4F2
Private Const WHITE As Long = &HFFFFFF
Private Const MUTED As Long = &H7A706A
Private Const LINE_C As Long = &HE2DCD6

Public Sub BuildAllScreens()
    DoCmd.Hourglass True
    SetCaptions

    BuildForm "frmCompanies", "Companies", ChrW(1605) & ChrW(1604) & ChrW(1601) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1575) & ChrW(1578) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1572) & ChrW(1587) & ChrW(1587) & ChrW(1575) & ChrW(1578), True, "", "", ""
    BuildForm "frmBranches", "Branches", ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1608) & ChrW(1593) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1576) & ChrW(1593) & ChrW(1610) & ChrW(1577) & " " & ChrW(1604) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1575) & ChrW(1578), True, "", "", ""
    BuildForm "frmSchoolRules", "SchoolRules", ChrW(1602) & ChrW(1608) & ChrW(1575) & ChrW(1593) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1580) & ChrW(1575) & ChrW(1581) & " " & ChrW(1608) & ChrW(1581) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576), True, "", "", ""
    BuildForm "frmClasses", "Classes", ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1608) & ChrW(1604) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1608) & ChrW(1593), True, "", "", ""
    BuildForm "frmTerms", "Terms", ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1578) & ChrW(1585) & ChrW(1575) & ChrW(1578) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1587) & ChrW(1606) & ChrW(1608) & ChrW(1575) & ChrW(1578) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & ChrW(1577), True, "", "", ""
    BuildForm "frmSubjects", "Subjects", ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1608) & ChrW(1575) & ChrW(1583) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1575) & ChrW(1578) & " " & ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1592) & ChrW(1605) & ChrW(1609), True, "", "", ""
    BuildForm "frmStudents", "Students", ChrW(1605) & ChrW(1604) & ChrW(1601) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1608) & ChrW(1608) & ChrW(1604) & ChrW(1610) & " " & ChrW(1575) & ChrW(1604) & ChrW(1571) & ChrW(1605) & ChrW(1585), True, "", "", ""
    BuildForm "frmGrades", "Grades", ChrW(1585) & ChrW(1589) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1575) & ChrW(1578) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1578) & ChrW(1610) & ChrW(1580) & ChrW(1577), True, "", "", ""
    BuildForm "frmAbsence", "Absence", ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1571) & ChrW(1593) & ChrW(1584) & ChrW(1575) & ChrW(1585), True, "", "", ""
    BuildReport "rptCertificate", "SELECT s.StudentName, t.TermName, t.AcademicYear, Sum(g.Score) AS TotalScore, Sum(g.MaxScore) AS TotalMax, Round(Sum(g.Score)/Sum(g.MaxScore)*100,2) AS Percentage, Min(g.PassPercent) AS PassPercent FROM (Grades AS g INNER JOIN Students AS s ON g.StudentID=s.StudentID) INNER JOIN Terms AS t ON g.TermID=t.TermID GROUP BY s.StudentID, s.StudentName, t.TermID, t.TermName, t.AcademicYear", ChrW(1606) & ChrW(1578) & ChrW(1610) & ChrW(1580) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1604) & ChrW(1603) & ChrW(1604) & " " & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610), True
    BuildReport "rptAbsenceAlert", "SELECT s.StudentName, t.TermName, Count(a.AbsID) AS AbsenceDays, r.AbsenceLimit FROM ((Absence AS a INNER JOIN Students AS s ON a.StudentID=s.StudentID) INNER JOIN Terms AS t ON a.TermID=t.TermID) INNER JOIN SchoolRules AS r ON t.RuleID=r.RuleID WHERE a.Excused=False GROUP BY s.StudentID, s.StudentName, t.TermID, t.TermName, r.AbsenceLimit HAVING Count(a.AbsID)>r.AbsenceLimit", ChrW(1578) & ChrW(1606) & ChrW(1576) & ChrW(1610) & ChrW(1607) & " " & ChrW(1578) & ChrW(1580) & ChrW(1575) & ChrW(1608) & ChrW(1586) & " " & ChrW(1581) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576), True
    BuildMenu
    DoCmd.Hourglass False
    MsgBox ChrW(1578) & ChrW(1605) & " " & ChrW(1573) & ChrW(1606) & ChrW(1588) & ChrW(1575) & ChrW(1569) & " 9 " & ChrW(1588) & ChrW(1575) & ChrW(1588) & ChrW(1577) & " " & ChrW(1608) & " 2 " & ChrW(1578) & ChrW(1602) & ChrW(1585) & ChrW(1610) & ChrW(1585) & " + " & ChrW(1575) & ChrW(1604) & ChrW(1602) & ChrW(1575) & ChrW(1574) & ChrW(1605) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1585) & ChrW(1574) & ChrW(1610) & ChrW(1587) & ChrW(1610) & ChrW(1577) & " " & ChrW(1576) & ChrW(1606) & ChrW(1580) & ChrW(1575) & ChrW(1581), vbInformation
    DoCmd.OpenForm "frmMain"
End Sub

' ---------- Arabic captions ----------
Private Function Cap(ByVal n As String) As String
    Select Case n
        Case "Companies": Cap = ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1575) & ChrW(1578)
        Case "CompanyID": Cap = ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1577)
        Case "CompanyName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1577)
        Case "RegistrationNo": Cap = ChrW(1575) & ChrW(1604) & ChrW(1587) & ChrW(1580) & ChrW(1604) & " " & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1580) & ChrW(1575) & ChrW(1585) & ChrW(1610)
        Case "TaxNumber": Cap = ChrW(1575) & ChrW(1604) & ChrW(1585) & ChrW(1602) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1590) & ChrW(1585) & ChrW(1610) & ChrW(1576) & ChrW(1610)
        Case "Phone": Cap = ChrW(1575) & ChrW(1604) & ChrW(1607) & ChrW(1575) & ChrW(1578) & ChrW(1601)
        Case "Address": Cap = ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1606) & ChrW(1608) & ChrW(1575) & ChrW(1606)
        Case "Branches": Cap = ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1608) & ChrW(1593)
        Case "BranchID": Cap = ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1593)
        Case "BranchName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1593)
        Case "SchoolRules": Cap = ChrW(1602) & ChrW(1608) & ChrW(1575) & ChrW(1593) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1593) & ChrW(1604) & ChrW(1610) & ChrW(1605)
        Case "RuleID": Cap = ChrW(1602) & ChrW(1575) & ChrW(1593) & ChrW(1583) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1605) & ChrW(1604)
        Case "RuleName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1602) & ChrW(1575) & ChrW(1593) & ChrW(1583) & ChrW(1577)
        Case "PassPercent": Cap = ChrW(1606) & ChrW(1587) & ChrW(1576) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1580) & ChrW(1575) & ChrW(1581) & " (%)"
        Case "AbsenceLimit": Cap = ChrW(1581) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576) & " " & ChrW(1594) & ChrW(1610) & ChrW(1585) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1593) & ChrW(1584) & ChrW(1608) & ChrW(1585)
        Case "Notes": Cap = ChrW(1605) & ChrW(1604) & ChrW(1575) & ChrW(1581) & ChrW(1592) & ChrW(1575) & ChrW(1578)
        Case "Classes": Cap = ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1608) & ChrW(1604)
        Case "ClassID": Cap = ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604)
        Case "ClassName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604)
        Case "Grade": Cap = ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1585) & ChrW(1581) & ChrW(1604) & ChrW(1577)
        Case "Terms": Cap = ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1578) & ChrW(1585) & ChrW(1575) & ChrW(1578) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & ChrW(1577)
        Case "TermID": Cap = ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1578) & ChrW(1585) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & ChrW(1577)
        Case "TermName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1578) & ChrW(1585) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & ChrW(1577)
        Case "AcademicYear": Cap = ChrW(1575) & ChrW(1604) & ChrW(1587) & ChrW(1606) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & ChrW(1577)
        Case "StartDate": Cap = ChrW(1576) & ChrW(1583) & ChrW(1575) & ChrW(1610) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1573) & ChrW(1580) & ChrW(1575) & ChrW(1586) & ChrW(1577)
        Case "EndDate": Cap = ChrW(1606) & ChrW(1607) & ChrW(1575) & ChrW(1610) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1573) & ChrW(1580) & ChrW(1575) & ChrW(1586) & ChrW(1577)
        Case "Students": Cap = ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1604) & ChrW(1575) & ChrW(1576)
        Case "StudentID": Cap = ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576)
        Case "StudentName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576)
        Case "SeatNo": Cap = ChrW(1585) & ChrW(1602) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1580) & ChrW(1604) & ChrW(1608) & ChrW(1587)
        Case "BirthDate": Cap = ChrW(1578) & ChrW(1575) & ChrW(1585) & ChrW(1610) & ChrW(1582) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1610) & ChrW(1604) & ChrW(1575) & ChrW(1583)
        Case "ParentName": Cap = ChrW(1608) & ChrW(1604) & ChrW(1610) & " " & ChrW(1575) & ChrW(1604) & ChrW(1571) & ChrW(1605) & ChrW(1585)
        Case "ParentPhone": Cap = ChrW(1607) & ChrW(1575) & ChrW(1578) & ChrW(1601) & " " & ChrW(1608) & ChrW(1604) & ChrW(1610) & " " & ChrW(1575) & ChrW(1604) & ChrW(1571) & ChrW(1605) & ChrW(1585)
        Case "Subjects": Cap = ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1608) & ChrW(1575) & ChrW(1583)
        Case "SubjectID": Cap = ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1575) & ChrW(1583) & ChrW(1577)
        Case "SubjectName": Cap = ChrW(1575) & ChrW(1587) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1575) & ChrW(1583) & ChrW(1577)
        Case "MaxScore": Cap = ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1592) & ChrW(1605) & ChrW(1609)
        Case "Grades": Cap = ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1575) & ChrW(1578)
        Case "GradeID": Cap = ChrW(1585) & ChrW(1602) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1585) & ChrW(1589) & ChrW(1583)
        Case "Score": Cap = ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1577)
        Case "Percentage": Cap = ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1587) & ChrW(1576) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1574) & ChrW(1608) & ChrW(1610) & ChrW(1577)
        Case "Passed": Cap = ChrW(1606) & ChrW(1575) & ChrW(1580) & ChrW(1581)
        Case "Absence": Cap = ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576)
        Case "AbsID": Cap = ChrW(1585) & ChrW(1602) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576)
        Case "AbsDate": Cap = ChrW(1578) & ChrW(1575) & ChrW(1585) & ChrW(1610) & ChrW(1582) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576)
        Case "Excused": Cap = ChrW(1576) & ChrW(1593) & ChrW(1584) & ChrW(1585)
        Case Else: Cap = n
    End Select
End Function

Private Function LookupSql(ByVal key As String) As String
    Select Case key
        Case "Branches.CompanyID": LookupSql = "SELECT [CompanyID], [CompanyName] FROM [Companies] ORDER BY [CompanyName]"
        Case "SchoolRules.CompanyID": LookupSql = "SELECT [CompanyID], [CompanyName] FROM [Companies] ORDER BY [CompanyName]"
        Case "Classes.CompanyID": LookupSql = "SELECT [CompanyID], [CompanyName] FROM [Companies] ORDER BY [CompanyName]"
        Case "Classes.BranchID": LookupSql = "SELECT [BranchID], [BranchName] FROM [Branches] ORDER BY [BranchName]"
        Case "Terms.CompanyID": LookupSql = "SELECT [CompanyID], [CompanyName] FROM [Companies] ORDER BY [CompanyName]"
        Case "Students.CompanyID": LookupSql = "SELECT [CompanyID], [CompanyName] FROM [Companies] ORDER BY [CompanyName]"
        Case "Students.BranchID": LookupSql = "SELECT [BranchID], [BranchName] FROM [Branches] ORDER BY [BranchName]"
        Case "Subjects.CompanyID": LookupSql = "SELECT [CompanyID], [CompanyName] FROM [Companies] ORDER BY [CompanyName]"
        Case "Terms.RuleID": LookupSql = "SELECT [RuleID], [RuleName] FROM [SchoolRules] ORDER BY [RuleName]"
        Case "Students.ClassID": LookupSql = "SELECT [ClassID], [ClassName] FROM [Classes] ORDER BY [ClassName]"
        Case "Grades.StudentID": LookupSql = "SELECT [StudentID], [StudentName] FROM [Students] ORDER BY [StudentName]"
        Case "Grades.SubjectID": LookupSql = "SELECT [SubjectID], [SubjectName] FROM [Subjects] ORDER BY [SubjectName]"
        Case "Grades.TermID": LookupSql = "SELECT [TermID], [TermName] FROM [Terms] ORDER BY [TermName]"
        Case "Absence.StudentID": LookupSql = "SELECT [StudentID], [StudentName] FROM [Students] ORDER BY [StudentName]"
        Case "Absence.TermID": LookupSql = "SELECT [TermID], [TermName] FROM [Terms] ORDER BY [TermName]"
        Case Else: LookupSql = ""
    End Select
End Function

' Arabic column captions on the tables themselves (datasheets, queries, exports)
Private Sub SetCaptions()
    Dim db As DAO.Database, tdf As DAO.TableDef, fld As DAO.Field, c As String
    Set db = CurrentDb
    On Error Resume Next
    For Each tdf In db.TableDefs
        If Left(tdf.Name, 4) <> "MSys" And Left(tdf.Name, 1) <> "~" Then
            For Each fld In tdf.Fields
                c = Cap(fld.Name)
                If c <> fld.Name Then
                    Err.Clear
                    fld.Properties("Caption") = c
                    If Err.Number <> 0 Then Err.Clear: fld.Properties.Append fld.CreateProperty("Caption", dbText, c)
                End If
            Next
        End If
    Next
End Sub

' ---------- helpers ----------
Private Sub L(ByRef m As String, ByVal s As String)
    m = m & s & vbCrLf
End Sub

Private Sub SafeDelete(ByVal n As String)
    On Error Resume Next
    DoCmd.Close acForm, n, acSaveNo
    DoCmd.DeleteObject acForm, n
End Sub

Private Sub SafeDeleteRpt(ByVal n As String)
    On Error Resume Next
    DoCmd.Close acReport, n, acSaveNo
    DoCmd.DeleteObject acReport, n
End Sub

Private Sub FinishForm(frm As Form, ByVal frmName As String)
    Dim tmp As String: tmp = frm.Name
    DoCmd.Close acForm, tmp, acSaveYes
    DoCmd.Rename frmName, acForm, tmp
End Sub

Private Sub Txt(c As Control, ByVal sz As Integer, ByVal clr As Long, ByVal bold As Boolean)
    On Error Resume Next
    c.FontName = "Segoe UI": c.FontSize = sz: c.ForeColor = clr: c.FontBold = bold
    c.ReadingOrder = 1   ' right-to-left
End Sub

Private Sub StyleBtn(b As Control, ByVal primary As Boolean)
    On Error Resume Next
    b.UseTheme = False
    b.FontName = "Segoe UI": b.FontSize = 11: b.FontBold = True
    b.BackColor = IIf(primary, PRIMARY, &HF1ECE8)
    b.ForeColor = IIf(primary, WHITE, &H3A3A3A)
    b.HoverColor = IIf(primary, ACCENT, &HE3DBD5)
    b.HoverForeColor = IIf(primary, WHITE, &H3A3A3A)
    b.PressedColor = ACCENT
    b.BorderStyle = 0
    b.Shape = 1          ' rounded corners (Access 2013+)
    b.CursorOnHover = 1  ' hand cursor
End Sub

Private Function Btn(frm As Object, ByVal sec As Integer, ByVal n As String, ByVal cap As String, _
                     ByVal x As Double, ByVal y As Double, ByVal w As Double, ByVal h As Double, ByVal primary As Boolean) As Control
    Dim b As Control
    Set b = CreateControl(frm.Name, acCommandButton, sec, , , x * TW, y * TW, w * TW, h * TW)
    b.Name = n: b.Caption = cap: b.OnClick = "[Event Procedure]"
    StyleBtn b, primary
    Set Btn = b
End Function

Private Sub Header(frm As Form, ByVal title As String, ByVal subTitle As String, ByVal w As Double)
    Dim c As Control
    DoCmd.RunCommand acCmdFormHdrFtr
    frm.Section(acHeader).Height = 2.4 * TW
    frm.Section(acHeader).BackColor = PRIMARY
    Set c = CreateControl(frm.Name, acRectangle, acHeader, , , 0, 2.25 * TW, w * TW, 0.15 * TW)
    c.BackStyle = 1: c.BackColor = ACCENT: c.BorderStyle = 0
    Set c = CreateControl(frm.Name, acLabel, acHeader, , , (w - 10) * TW, 0.3 * TW, 9.5 * TW, 1 * TW)
    c.Caption = title: c.TextAlign = 3: Txt c, 18, WHITE, True
    Set c = CreateControl(frm.Name, acLabel, acHeader, , , (w - 10) * TW, 1.35 * TW, 9.5 * TW, 0.6 * TW)
    c.Caption = subTitle: c.TextAlign = 3: Txt c, 10, &HE8D9C8, False
End Sub

' textbox / combo / checkbox bound to a field (combo for foreign keys)
Private Function FieldCtl(frm As Form, ByVal src As String, fld As DAO.Field, ByVal x As Double, ByVal y As Double, ByVal w As Double, Optional ByVal parent As String = "") As Control
    Dim ctl As Control, sql As String
    sql = LookupSql(src & "." & fld.Name)
    If fld.Type = dbBoolean Then
        Set ctl = CreateControl(frm.Name, acCheckBox, acDetail, parent, fld.Name, (x + w - 0.6) * TW, (y + 0.2) * TW)
    ElseIf sql <> "" Then
        Set ctl = CreateControl(frm.Name, acComboBox, acDetail, parent, fld.Name, x * TW, y * TW, w * TW, 0.8 * TW)
        ctl.RowSourceType = "Table/Query": ctl.RowSource = sql
        ctl.ColumnCount = 2: ctl.BoundColumn = 1: ctl.ColumnWidths = "0;" & CStr(CLng(w * TW))
        ctl.LimitToList = True: ctl.ListRows = 12
        ctl.BorderStyle = 1: ctl.BorderColor = &HD5CEC7: ctl.TextAlign = 3
        Txt ctl, 11, &H222222, False
    Else
        Set ctl = CreateControl(frm.Name, acTextBox, acDetail, parent, fld.Name, x * TW, y * TW, w * TW, 0.8 * TW)
        ctl.BorderStyle = 1: ctl.BorderColor = &HD5CEC7: ctl.TextAlign = 3
        Txt ctl, 11, &H222222, False
        If (fld.Attributes And dbAutoIncrField) Then ctl.Locked = True: ctl.TabStop = False: ctl.BackColor = &HF0ECE9
        If fld.Type = dbCurrency Then ctl.Format = "#,##0.00": ctl.TextAlign = 1
        If fld.Type = dbDate Then
            If fld.Name = "ApptDate" Or fld.Name = "ApptEnd" Then
                ctl.Format = "yyyy/mm/dd hh:nn"
            ElseIf fld.Name Like "Check*" Or fld.Name Like "Shift*" Then
                ctl.Format = "Short Time"
            Else
                ctl.Format = "Short Date": ctl.ShowDatePicker = 1
            End If
        End If
        If fld.Type = dbMemo Then ctl.Height = 1.6 * TW: ctl.ScrollBars = 2
    End If
    If fld.Type = dbDouble Then ctl.Format = "0.00"
    If fld.Name = "LateMinutes" Or fld.Name = "OvertimeHours" Or fld.Name = "LeaveDays" Then ctl.Locked = True
    If fld.Required Then ctl.BorderColor = ACCENT
    ctl.Name = "txt" & fld.Name
    Set FieldCtl = ctl
End Function

Private Sub BindEvents(frm As Form, ByVal screen As String)
    Select Case screen
        Case "frmSchoolRules"
            frm.BeforeUpdate = "[Event Procedure]"
        Case "frmClasses"
            frm.BeforeUpdate = "[Event Procedure]"
        Case "frmTerms"
            frm.BeforeUpdate = "[Event Procedure]"
        Case "frmSubjects"
            frm.BeforeUpdate = "[Event Procedure]"
        Case "frmStudents"
            frm.BeforeUpdate = "[Event Procedure]"
        Case "frmGrades"
            frm.BeforeUpdate = "[Event Procedure]"
        Case "frmAbsence"
            frm.BeforeUpdate = "[Event Procedure]"
    End Select
End Sub

' ---------- datasheet subform ----------
Private Sub BuildSub(ByVal frmName As String, ByVal src As String)
    Dim frm As Form, ctl As Control, lbl As Control, fld As DAO.Field
    Dim db As DAO.Database, tdf As DAO.TableDef, y As Double, m As String
    Set db = CurrentDb: Set tdf = db.TableDefs(src)
    SafeDelete frmName
    Set frm = CreateForm()
    frm.RecordSource = src
    frm.DefaultView = 2   ' datasheet
    frm.Orientation = 1
    frm.DatasheetFontName = "Segoe UI": frm.DatasheetFontHeight = 11
    For Each fld In tdf.Fields
        Set ctl = FieldCtl(frm, src, fld, 4, y, 5)
        Set lbl = CreateControl(frm.Name, acLabel, acDetail, ctl.Name, , 0, y * TW, 3.8 * TW, 0.8 * TW)
        lbl.Caption = Cap(fld.Name)
        y = y + 1
    Next
    Select Case src

    End Select
    If Len(m) > 0 Then frm.Module.InsertText m
    FinishForm frm, frmName
End Sub

Private Function EmployeeGroup(ByVal n As String) As Integer
    Select Case n
        Case "EmpID", "EmpCode", "FullName", "NationalID", "BirthDate", "Gender", "MaritalStatus", "Address", "Phone", "Email", "EmergencyContact", "EmergencyPhone": EmployeeGroup = 0
        Case "CompanyID", "BranchID", "DeptID", "JobTitle", "HireDate", "ContractEnd", "EmploymentType", "ShiftID", "LeavePolicyID", "IsActive": EmployeeGroup = 1
        Case "BasicSalary", "NonTaxableVariable", "InsurableSalary", "FixedAllowance", "InsuranceNumber", "InsuranceStartDate", "BankName", "BankAccount", "Notes": EmployeeGroup = 2
        Case Else: EmployeeGroup = 0
    End Select
End Function

' ---------- data-entry screen ----------
Private Sub BuildForm(ByVal frmName As String, ByVal src As String, ByVal title As String, _
                      ByVal hasSearch As Boolean, ByVal subName As String, ByVal linkFld As String, ByVal extraBtn As String)
    Const W As Double = 21
    Dim frm As Form, ctl As Control, lbl As Control, fld As DAO.Field
    Dim db As DAO.Database, tdf As DAO.TableDef
    Dim i As Integer, rowsN As Integer, x As Double, y As Double, m As String
    Dim tabs As Control, pg As Page, groupN As Integer, counts(0 To 2) As Integer, parent As String
    Set db = CurrentDb: Set tdf = db.TableDefs(src)
    SafeDelete frmName

    Set frm = CreateForm()
    With frm
        .RecordSource = src: .Caption = title
        .Orientation = 1: .AutoCenter = True: .AutoResize = True
        .RecordSelectors = False: .DividingLines = False
        .ScrollBars = 2: .NavigationButtons = False
        .Width = W * TW
        .Section(acDetail).BackColor = SURFACE
    End With
    Header frm, title, ChrW(1573) & ChrW(1583) & ChrW(1575) & ChrW(1585) & ChrW(1577) & " " & Cap(src), W

    ' header: close + search
    Btn frm, acHeader, "btnClose", ChrW(1573) & ChrW(1594) & ChrW(1604) & ChrW(1575) & ChrW(1602) & "  " & ChrW(215), 0.4, 0.75, 2.2, 0.8, False
    If hasSearch Then
        Set ctl = CreateControl(frm.Name, acTextBox, acHeader, , , 2.9 * TW, 0.75 * TW, 5.2 * TW, 0.8 * TW)
        ctl.OnChange = "[Event Procedure]"
        ctl.Name = "txtSearch": ctl.BorderStyle = 0: ctl.BackColor = WHITE: ctl.TextAlign = 3
        Txt ctl, 11, &H333333, False
        Set lbl = CreateControl(frm.Name, acLabel, acHeader, "txtSearch", , 8.2 * TW, 0.8 * TW, 2 * TW, 0.7 * TW)
        lbl.Caption = ChrW(1576) & ChrW(1581) & ChrW(1579) & " " & ChrW(1587) & ChrW(1585) & ChrW(1610) & ChrW(1593): Txt lbl, 11, WHITE, True
    End If

    rowsN = (tdf.Fields.Count + 1) \ 2
    If src = "Employees" Then rowsN = 6
    If src = "Employees" Then
        Set tabs = CreateControl(frm.Name, acTabCtl, acDetail, , , 0.3 * TW, 0.3 * TW, (W - 0.6) * TW, (rowsN * 1.3 + 1.7) * TW)
        tabs.Name = "tabEmployee"
        Do While tabs.Pages.Count < 3
            tabs.Pages.Add
        Loop
        tabs.Pages(0).Caption = ChrW(1575) & ChrW(1604) & ChrW(1576) & ChrW(1610) & ChrW(1575) & ChrW(1606) & ChrW(1575) & ChrW(1578) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1582) & ChrW(1589) & ChrW(1610) & ChrW(1577)
        tabs.Pages(1).Caption = ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1576) & ChrW(1593) & ChrW(1610) & ChrW(1577) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1608) & ChrW(1592) & ChrW(1610) & ChrW(1601) & ChrW(1577)
        tabs.Pages(2).Caption = ChrW(1575) & ChrW(1604) & ChrW(1585) & ChrW(1575) & ChrW(1578) & ChrW(1576) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1571) & ChrW(1605) & ChrW(1610) & ChrW(1606) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1589) & ChrW(1585) & ChrW(1601)
    Else
        Set lbl = CreateControl(frm.Name, acLabel, acDetail, , , (W - 8.5) * TW, 0.4 * TW, 7.8 * TW, 0.7 * TW)
        lbl.Caption = ChrW(1576) & ChrW(1610) & ChrW(1575) & ChrW(1606) & ChrW(1575) & ChrW(1578) & " " & Cap(src): lbl.TextAlign = 3: Txt lbl, 12, PRIMARY, True
    End If
    For Each fld In tdf.Fields
        parent = ""
        If src = "Employees" Then
            groupN = EmployeeGroup(fld.Name): i = counts(groupN)
            parent = tabs.Pages(groupN).Name
            counts(groupN) = counts(groupN) + 1
        End If
        x = IIf(i Mod 2 = 0, 11.2, 0.8)
        y = 1.4 + (i \ 2) * 1.3
        Set ctl = FieldCtl(frm, src, fld, x, y, 5.1, parent)
        Set lbl = CreateControl(frm.Name, acLabel, acDetail, ctl.Name, , (x + 5.2) * TW, y * TW, 3.9 * TW, 0.9 * TW)
        lbl.Caption = Cap(fld.Name) & IIf(fld.Required, " *", ""): lbl.TextAlign = 3
        Txt lbl, 10, MUTED, True
        i = i + 1
    Next
    y = rowsN * 1.3 + 2.4

    ' linked details (subform)
    If subName <> "" Then
        Set lbl = CreateControl(frm.Name, acLabel, acDetail, , , (W - 8.3) * TW, y * TW, 8 * TW, 0.7 * TW)
        lbl.Caption = Cap(Mid(subName, 5)) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1585) & ChrW(1578) & ChrW(1576) & ChrW(1591) & ChrW(1577): lbl.TextAlign = 3: Txt lbl, 12, PRIMARY, True
        Set ctl = CreateControl(frm.Name, acSubform, acDetail, , , 0.3 * TW, (y + 0.8) * TW, (W - 0.6) * TW, 5 * TW)
        ctl.Name = "subDetails"
        ctl.SourceObject = "Form." & subName
        ctl.LinkChildFields = linkFld: ctl.LinkMasterFields = linkFld
        y = y + 6.2
    End If
    frm.Section(acDetail).Height = y * TW

    ' footer: actions + record navigation
    frm.Section(acFooter).Height = 1.6 * TW
    frm.Section(acFooter).BackColor = WHITE
    Btn frm, acFooter, "btnSave", ChrW(1581) & ChrW(1601) & ChrW(1592), 18.2, 0.35, 2.4, 0.9, True
    Btn frm, acFooter, "btnNew", ChrW(1580) & ChrW(1583) & ChrW(1610) & ChrW(1583) & " +", 15.6, 0.35, 2.4, 0.9, False
    Btn frm, acFooter, "btnDelete", ChrW(1581) & ChrW(1584) & ChrW(1601), 13, 0.35, 2.4, 0.9, False
    Btn frm, acFooter, "btnPrint", ChrW(1591) & ChrW(1576) & ChrW(1575) & ChrW(1593) & ChrW(1577), 10.4, 0.35, 2.4, 0.9, False
    If extraBtn <> "" Then
        Set ctl = Btn(frm, acFooter, "btnExtra", extraBtn, 7.3, 0.35, 2.9, 0.9, True)
        ctl.BackColor = ACCENT: ctl.HoverColor = PRIMARY
    End If
    Btn frm, acFooter, "btnLast", ">>", 0.4, 0.35, 1, 0.9, False
    Btn frm, acFooter, "btnNext", ">", 1.5, 0.35, 1, 0.9, False
    Set lbl = CreateControl(frm.Name, acLabel, acFooter, , , 2.6 * TW, 0.5 * TW, 2.2 * TW, 0.6 * TW)
    lbl.Name = "lblCount": lbl.Caption = "0 / 0": lbl.TextAlign = 2: Txt lbl, 10, MUTED, True
    Btn frm, acFooter, "btnPrev", "<", 4.9, 0.35, 1, 0.9, False
    Btn frm, acFooter, "btnFirst", "<<", 6, 0.35, 1, 0.9, False

    ' event code inside the form module
    L m, "Private Sub Form_Current()"
    L m, "  On Error Resume Next"
    L m, "  Me.lblCount.Caption = IIf(Me.NewRecord, ChrW(1587) & ChrW(1580) & ChrW(1604) & ChrW(32) & ChrW(1580) & ChrW(1583) & ChrW(1610) & ChrW(1583), Me.CurrentRecord & "" / "" & Me.RecordsetClone.RecordCount)"
    L m, "End Sub"
    L m, "Private Sub btnNew_Click()"
    L m, "  On Error Resume Next"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.GoToRecord , , acNewRec"
    L m, "End Sub"
    L m, "Private Sub btnSave_Click()"
    L m, "  On Error GoTo E"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  MsgBox ChrW(1578) & ChrW(1605) & ChrW(32) & ChrW(1575) & ChrW(1604) & ChrW(1581) & ChrW(1601) & ChrW(1592) & ChrW(32) & ChrW(1576) & ChrW(1606) & ChrW(1580) & ChrW(1575) & ChrW(1581), vbInformation"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation"
    L m, "End Sub"
    L m, "Private Sub btnDelete_Click()"
    L m, "  If Me.NewRecord Then Me.Undo: Exit Sub"
    L m, "  If MsgBox(ChrW(1607) & ChrW(1604) & ChrW(32) & ChrW(1578) & ChrW(1585) & ChrW(1610) & ChrW(1583) & ChrW(32) & ChrW(1581) & ChrW(1584) & ChrW(1601) & ChrW(32) & ChrW(1607) & ChrW(1584) & ChrW(1575) & ChrW(32) & ChrW(1575) & ChrW(1604) & ChrW(1587) & ChrW(1580) & ChrW(1604) & ChrW(1567), vbYesNo + vbQuestion) = vbYes Then DoCmd.SetWarnings False: DoCmd.RunCommand acCmdDeleteRecord: DoCmd.SetWarnings True"
    L m, "End Sub"
    L m, "Private Sub btnClose_Click()"
    L m, "  DoCmd.Close acForm, Me.Name"
    L m, "End Sub"
    L m, "Private Sub btnFirst_Click(): On Error Resume Next: DoCmd.GoToRecord , , acFirst: End Sub"
    L m, "Private Sub btnPrev_Click(): On Error Resume Next: DoCmd.GoToRecord , , acPrevious: End Sub"
    L m, "Private Sub btnNext_Click(): On Error Resume Next: DoCmd.GoToRecord , , acNext: End Sub"
    L m, "Private Sub btnLast_Click(): On Error Resume Next: DoCmd.GoToRecord , , acLast: End Sub"
    frm.OnCurrent = "[Event Procedure]"
    BindEvents frm, frmName
    FormCode frmName, m
    frm.Module.InsertText m
    FinishForm frm, frmName
End Sub

' search + business logic for each screen
Private Sub FormCode(ByVal frmName As String, ByRef m As String)
    Select Case frmName
        Case "frmCompanies"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[CompanyName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
        Case "frmBranches"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[BranchName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
        Case "frmSchoolRules"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[RuleName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  If Nz(Me!PassPercent, -1)<0 Or Nz(Me!PassPercent, 101)>100 Or Nz(Me!AbsenceLimit, -1)<0 Then MsgBox """ & ChrW(1585) & ChrW(1575) & ChrW(1580) & ChrW(1593) & " " & ChrW(1606) & ChrW(1587) & ChrW(1576) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1580) & ChrW(1575) & ChrW(1581) & " " & ChrW(1608) & ChrW(1581) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
        Case "frmClasses"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[ClassName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  If Nz(DLookup(""CompanyID"", ""Branches"", ""BranchID="" & Nz(Me!BranchID, 0)), 0) <> Nz(Me!CompanyID, 0) Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1593) & " " & ChrW(1604) & ChrW(1575) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1581) & ChrW(1583) & ChrW(1583) & ChrW(1577) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
        Case "frmTerms"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[TermName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  If Nz(DLookup(""CompanyID"", ""SchoolRules"", ""RuleID="" & Nz(Me!RuleID, 0)), 0) <> Nz(Me!CompanyID, 0) Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1575) & ChrW(1582) & ChrW(1578) & ChrW(1610) & ChrW(1575) & ChrW(1585) & " " & ChrW(1604) & ChrW(1575) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1581) & ChrW(1583) & ChrW(1583) & ChrW(1577) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  If IsNull(Me!StartDate) Or IsNull(Me!EndDate) Or Me!EndDate<Me!StartDate Then MsgBox """ & ChrW(1585) & ChrW(1575) & ChrW(1580) & ChrW(1593) & " " & ChrW(1578) & ChrW(1575) & ChrW(1585) & ChrW(1610) & ChrW(1582) & ChrW(1610) & " " & ChrW(1576) & ChrW(1583) & ChrW(1575) & ChrW(1610) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1608) & ChrW(1606) & ChrW(1607) & ChrW(1575) & ChrW(1610) & ChrW(1578) & ChrW(1607) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
        Case "frmSubjects"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[SubjectName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  If Nz(Me!MaxScore, 0)<=0 Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1592) & ChrW(1605) & ChrW(1609) & " " & ChrW(1610) & ChrW(1580) & ChrW(1576) & " " & ChrW(1571) & ChrW(1606) & " " & ChrW(1578) & ChrW(1603) & ChrW(1608) & ChrW(1606) & " " & ChrW(1605) & ChrW(1608) & ChrW(1580) & ChrW(1576) & ChrW(1577) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
        Case "frmStudents"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[StudentName] Like '*"" & s & ""*'"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  If Nz(DLookup(""CompanyID"", ""Branches"", ""BranchID="" & Nz(Me!BranchID, 0)), 0) <> Nz(Me!CompanyID, 0) Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1593) & " " & ChrW(1604) & ChrW(1575) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1581) & ChrW(1583) & ChrW(1583) & ChrW(1577) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  If Nz(DLookup(""CompanyID"", ""Classes"", ""ClassID="" & Nz(Me!ClassID, 0)), 0) <> Nz(Me!CompanyID, 0) Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1575) & ChrW(1582) & ChrW(1578) & ChrW(1610) & ChrW(1575) & ChrW(1585) & " " & ChrW(1604) & ChrW(1575) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1581) & ChrW(1583) & ChrW(1583) & ChrW(1577) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  If Nz(DLookup(""BranchID"", ""Classes"", ""ClassID="" & Me!ClassID), 0)<>Me!BranchID Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1604) & ChrW(1575) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & " " & ChrW(1601) & ChrW(1585) & ChrW(1593) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
        Case "frmGrades"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[StudentID] IN (SELECT [StudentID] FROM [Students] WHERE [StudentName] Like '*"" & s & ""*')"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  Dim company As Long, rule As Long"
    L m, "  company = Nz(DLookup(""CompanyID"", ""Students"", ""StudentID="" & Nz(Me!StudentID, 0)), 0)"
    L m, "  If company=0 Or Nz(DLookup(""CompanyID"", ""Subjects"", ""SubjectID="" & Nz(Me!SubjectID, 0)), 0)<>company Or Nz(DLookup(""CompanyID"", ""Terms"", ""TermID="" & Nz(Me!TermID, 0)), 0)<>company Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1575) & ChrW(1583) & ChrW(1577) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & " " & ChrW(1610) & ChrW(1580) & ChrW(1576) & " " & ChrW(1571) & ChrW(1606) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & ChrW(1608) & ChrW(1575) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1572) & ChrW(1587) & ChrW(1587) & ChrW(1577) & " " & ChrW(1606) & ChrW(1601) & ChrW(1587) & ChrW(1607) & ChrW(1575) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Me!MaxScore = Nz(DLookup(""MaxScore"", ""Subjects"", ""SubjectID="" & Me!SubjectID), 0)"
    L m, "  If Me!MaxScore<=0 Or Nz(Me!Score, -1)<0 Or Nz(Me!Score, -1)>Me!MaxScore Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1577) & " " & ChrW(1610) & ChrW(1580) & ChrW(1576) & " " & ChrW(1571) & ChrW(1606) & " " & ChrW(1578) & ChrW(1603) & ChrW(1608) & ChrW(1606) & " " & ChrW(1576) & ChrW(1610) & ChrW(1606) & " " & ChrW(1589) & ChrW(1601) & ChrW(1585) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1592) & ChrW(1605) & ChrW(1609) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  If DCount(""*"", ""Grades"", ""StudentID="" & Me!StudentID & "" AND SubjectID="" & Me!SubjectID & "" AND TermID="" & Me!TermID & "" AND GradeID<>"" & Nz(Me!GradeID, 0))>0 Then MsgBox """ & ChrW(1578) & ChrW(1605) & " " & ChrW(1585) & ChrW(1589) & ChrW(1583) & " " & ChrW(1607) & ChrW(1584) & ChrW(1607) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1575) & ChrW(1583) & ChrW(1577) & " " & ChrW(1604) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1601) & ChrW(1610) & " " & ChrW(1607) & ChrW(1584) & ChrW(1575) & " " & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1576) & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1593) & ChrW(1604) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  rule = Nz(DLookup(""RuleID"", ""Terms"", ""TermID="" & Me!TermID), 0)"
    L m, "  Me!PassPercent = Nz(DLookup(""PassPercent"", ""SchoolRules"", ""RuleID="" & rule), 0)"
    L m, "  Me!Percentage = Round(Me!Score/Me!MaxScore*100, 2): Me!Passed = (Me!Percentage>=Me!PassPercent)"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
        Case "frmAbsence"
    L m, "Private Sub txtSearch_Change()"
    L m, "  Dim s As String: s = Replace(Me.txtSearch.Text, ""'"", ""''"")"
    L m, "  If Len(s) = 0 Then"
    L m, "    Me.FilterOn = False"
    L m, "  Else"
    L m, "    Me.Filter = ""[StudentID] IN (SELECT [StudentID] FROM [Students] WHERE [StudentName] Like '*"" & s & ""*')"": Me.FilterOn = True"
    L m, "  End If"
    L m, "  Me.txtSearch.SetFocus: Me.txtSearch.SelStart = Len(Me.txtSearch.Text)"
    L m, "End Sub"
    L m, "Private Sub btnPrint_Click()"
    L m, "  If Me.Dirty Then Me.Dirty = False"
    L m, "  DoCmd.SelectObject acForm, Me.Name: DoCmd.PrintOut acSelection"
    L m, "End Sub"
    L m, "Private Sub Form_BeforeUpdate(Cancel As Integer)"
    L m, "  On Error GoTo E"
    L m, "  If Nz(DLookup(""CompanyID"", ""Students"", ""StudentID="" & Nz(Me!StudentID, 0)), 0)<>Nz(DLookup(""CompanyID"", ""Terms"", ""TermID="" & Nz(Me!TermID, 0)), 0) Then MsgBox """ & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & " " & ChrW(1604) & ChrW(1575) & " " & ChrW(1610) & ChrW(1578) & ChrW(1576) & ChrW(1593) & ChrW(1575) & ChrW(1606) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1572) & ChrW(1587) & ChrW(1587) & ChrW(1577) & " " & ChrW(1606) & ChrW(1601) & ChrW(1587) & ChrW(1607) & ChrW(1575) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  If IsNull(Me!AbsDate) Or Me!AbsDate<DLookup(""StartDate"", ""Terms"", ""TermID="" & Me!TermID) Or Me!AbsDate>DLookup(""EndDate"", ""Terms"", ""TermID="" & Me!TermID) Then MsgBox """ & ChrW(1578) & ChrW(1575) & ChrW(1585) & ChrW(1610) & ChrW(1582) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576) & " " & ChrW(1582) & ChrW(1575) & ChrW(1585) & ChrW(1580) & " " & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  If DCount(""*"", ""Absence"", ""StudentID="" & Me!StudentID & "" AND AbsDate=#"" & Format(Me!AbsDate, ""mm/dd/yyyy"") & ""# AND AbsID<>"" & Nz(Me!AbsID, 0))>0 Then MsgBox """ & ChrW(1578) & ChrW(1605) & " " & ChrW(1578) & ChrW(1587) & ChrW(1580) & ChrW(1610) & ChrW(1604) & " " & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1601) & ChrW(1610) & " " & ChrW(1607) & ChrW(1584) & ChrW(1575) & " " & ChrW(1575) & ChrW(1604) & ChrW(1610) & ChrW(1608) & ChrW(1605) & """, vbExclamation: Cancel = True: Exit Sub"
    L m, "  Exit Sub"
    L m, "E: MsgBox Err.Description, vbExclamation: Cancel = True"
    L m, "End Sub"
    End Select
End Sub

' ---------- reports ----------
Private Sub BuildReport(ByVal rptName As String, ByVal src As String, ByVal title As String, ByVal tabular As Boolean)
    Const W As Double = 19
    Dim rpt As Report, ctl As Control, lbl As Control, fld As DAO.Field, rs As DAO.Recordset, tmp As String
    Dim i As Integer, n As Integer, x As Double, y As Double, cw As Double, rowsN As Integer, hasFooter As Boolean
    SafeDeleteRpt rptName
    Dim db As DAO.Database
    Set db = CurrentDb
    Set rs = db.OpenRecordset(src, dbOpenSnapshot)
    n = rs.Fields.Count
    Set rpt = CreateReport()
    rpt.RecordSource = src: rpt.Caption = title: rpt.Width = W * TW

    ' page header: title band
    rpt.Section(acPageHeader).Height = IIf(tabular, 3.2, 2.4) * TW
    Set ctl = CreateReportControl(rpt.Name, acRectangle, acPageHeader, , , 0, 0, W * TW, 1.9 * TW)
    ctl.BackStyle = 1: ctl.BackColor = PRIMARY: ctl.BorderStyle = 0
    Set ctl = CreateReportControl(rpt.Name, acLabel, acPageHeader, , , 6 * TW, 0.25 * TW, 12.6 * TW, 0.9 * TW)
    ctl.Caption = title: ctl.TextAlign = 3: Txt ctl, 18, WHITE, True
    Set ctl = CreateReportControl(rpt.Name, acLabel, acPageHeader, , , 6 * TW, 1.1 * TW, 12.6 * TW, 0.6 * TW)
    ctl.Caption = ChrW(1603) & ChrW(1606) & ChrW(1578) & ChrW(1585) & ChrW(1608) & ChrW(1604) & " " & ChrW(1608) & ChrW(1605) & ChrW(1578) & ChrW(1575) & ChrW(1576) & ChrW(1593) & ChrW(1577) & " " & ChrW(1605) & ChrW(1583) & ChrW(1585) & ChrW(1587) & ChrW(1610) & ChrW(1577): ctl.TextAlign = 3: Txt ctl, 10, &HE8D9C8, False
    Set ctl = CreateReportControl(rpt.Name, acTextBox, acPageHeader, , , 0.4 * TW, 0.6 * TW, 5 * TW, 0.6 * TW)
    ctl.ControlSource = "=Format(Date(),""yyyy/mm/dd"")": ctl.BorderStyle = 0: ctl.BackStyle = 0: ctl.TextAlign = 1: Txt ctl, 10, WHITE, False
    Set ctl = CreateReportControl(rpt.Name, acRectangle, acPageHeader, , , 0, 1.9 * TW, W * TW, 0.12 * TW)
    ctl.BackStyle = 1: ctl.BackColor = ACCENT: ctl.BorderStyle = 0

    If tabular Then
        On Error Resume Next
        DoCmd.RunCommand acCmdReportHdrFtr
        hasFooter = (Err.Number = 0)
        Err.Clear
        On Error GoTo 0
        If hasFooter Then rpt.Section(acHeader).Height = 0
        cw = W / n
        For Each fld In rs.Fields
            x = W - (i + 1) * cw
            Set lbl = CreateReportControl(rpt.Name, acLabel, acPageHeader, , , x * TW, 2.3 * TW, cw * TW, 0.7 * TW)
            lbl.Caption = Cap(fld.Name): lbl.TextAlign = 2: Txt lbl, 10, PRIMARY, True
            Set ctl = CreateReportControl(rpt.Name, acTextBox, acDetail, , fld.Name, x * TW, 0.1 * TW, cw * TW, 0.6 * TW)
            ctl.BorderStyle = 0: ctl.TextAlign = 2: Txt ctl, 10, &H222222, False
            If fld.Type = dbCurrency Or fld.Type = dbDouble Then ctl.Format = "#,##0.00"
            If fld.Type = dbDate Then ctl.Format = "yyyy/mm"
            If hasFooter And fld.Type = dbCurrency Then
                Set ctl = CreateReportControl(rpt.Name, acTextBox, acFooter, , , x * TW, 0.3 * TW, cw * TW, 0.7 * TW)
                ctl.ControlSource = "=Sum([" & fld.Name & "])": ctl.Format = "#,##0.00"
                ctl.BorderStyle = 0: ctl.TextAlign = 2: Txt ctl, 11, PRIMARY, True
            End If
            i = i + 1
        Next
        Set ctl = CreateReportControl(rpt.Name, acLine, acPageHeader, , , 0, 3.1 * TW, W * TW, 0)
        ctl.BorderColor = PRIMARY: ctl.BorderWidth = 2
        rpt.Section(acDetail).Height = 0.8 * TW
        rpt.Section(acDetail).AlternateBackColor = SURFACE
        If hasFooter Then
            rpt.Section(acFooter).Height = 1.2 * TW
            Set lbl = CreateReportControl(rpt.Name, acLabel, acFooter, , , (W - 4) * TW, 0.3 * TW, 3.8 * TW, 0.7 * TW)
            lbl.Caption = ChrW(1575) & ChrW(1604) & ChrW(1573) & ChrW(1580) & ChrW(1605) & ChrW(1575) & ChrW(1604) & ChrW(1610): lbl.TextAlign = 3: Txt lbl, 11, PRIMARY, True
        End If
    Else
        ' one card per record (e.g. payslip)
        rowsN = (n + 1) \ 2
        Set ctl = CreateReportControl(rpt.Name, acRectangle, acDetail, , , 0.1 * TW, 0.3 * TW, (W - 0.2) * TW, (rowsN * 0.85 + 0.4) * TW)
        ctl.BackStyle = 0: ctl.BorderColor = LINE_C: ctl.BorderWidth = 1
        For Each fld In rs.Fields
            x = IIf(i Mod 2 = 0, 9.8, 0.4)
            y = 0.5 + (i \ 2) * 0.85
            Set lbl = CreateReportControl(rpt.Name, acLabel, acDetail, , , (x + 5.2) * TW, y * TW, 3.6 * TW, 0.7 * TW)
            lbl.Caption = Cap(fld.Name): lbl.TextAlign = 3: Txt lbl, 10, MUTED, True
            Set ctl = CreateReportControl(rpt.Name, acTextBox, acDetail, , fld.Name, x * TW, y * TW, 5 * TW, 0.7 * TW)
            ctl.BorderStyle = 0: ctl.TextAlign = 3: Txt ctl, 11, &H222222, (fld.Name = "NetSalary")
            If fld.Type = dbCurrency Or fld.Type = dbDouble Then ctl.Format = "#,##0.00"
            If fld.Type = dbDate Then ctl.Format = "yyyy/mm"
            If fld.Name = "NetSalary" Then ctl.BackStyle = 1: ctl.BackColor = &HE6F4EA: ctl.FontSize = 13
            i = i + 1
        Next
        rpt.Section(acDetail).Height = (rowsN * 0.85 + 1.2) * TW
        rpt.Section(acDetail).KeepTogether = True
        Set lbl = CreateReportControl(rpt.Name, acLabel, acDetail, , , (W - 6) * TW, (rowsN * 0.85 + 0.75) * TW, 5.8 * TW, 0.5 * TW)
        lbl.Caption = ChrW(1578) & ChrW(1608) & ChrW(1602) & ChrW(1610) & ChrW(1593) & " " & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1587) & ChrW(1578) & ChrW(1604) & ChrW(1605) & ": ..................": lbl.TextAlign = 3: Txt lbl, 9, MUTED, False
    End If
    rs.Close

    ' page footer: page numbers
    rpt.Section(acPageFooter).Height = 0.9 * TW
    Set ctl = CreateReportControl(rpt.Name, acTextBox, acPageFooter, , , 0, 0.2 * TW, W * TW, 0.6 * TW)
    ctl.ControlSource = "=""" & ChrW(1589) & ChrW(1601) & ChrW(1581) & ChrW(1577) & " "" & [Page] & "" / "" & [Pages]": ctl.BorderStyle = 0: ctl.TextAlign = 2: Txt ctl, 9, MUTED, False

    tmp = rpt.Name
    DoCmd.Close acReport, tmp, acSaveYes
    DoCmd.Rename rptName, acReport, tmp
End Sub

' ---------- main menu ----------
Private Sub Kpi(frm As Form, ByVal idx As Integer, ByVal cnt As Integer, ByVal cap As String, ByVal expr As String)
    Dim c As Control, tw_ As Double, x As Double
    tw_ = (18 - 0.6 * (cnt + 1)) / cnt
    x = 18 - 0.6 - (idx + 1) * tw_ - idx * 0.6
    Set c = CreateControl(frm.Name, acRectangle, acDetail, , , x * TW, 0.5 * TW, tw_ * TW, 2 * TW)
    c.BackStyle = 1: c.BackColor = WHITE: c.BorderColor = LINE_C
    Set c = CreateControl(frm.Name, acRectangle, acDetail, , , (x + tw_ - 0.15) * TW, 0.5 * TW, 0.15 * TW, 2 * TW)
    c.BackStyle = 1: c.BackColor = ACCENT: c.BorderStyle = 0
    Set c = CreateControl(frm.Name, acTextBox, acDetail, , , x * TW, 0.7 * TW, (tw_ - 0.4) * TW, 1 * TW)
    c.ControlSource = expr: c.BorderStyle = 0: c.BackStyle = 0: c.TextAlign = 3: c.Locked = True: c.TabStop = False
    Txt c, 20, PRIMARY, True
    Set c = CreateControl(frm.Name, acLabel, acDetail, , , x * TW, 1.75 * TW, (tw_ - 0.4) * TW, 0.6 * TW)
    c.Caption = cap: c.TextAlign = 3: Txt c, 10, MUTED, True
End Sub

Private Sub MenuBtn(frm As Form, ByRef m As String, ByVal idx As Integer, ByVal target As String, ByVal cap As String, ByVal isReport As Boolean)
    Dim x As Double, y As Double
    x = IIf(idx Mod 2 = 0, 9.3, 0.6)
    y = 3.1 + (idx \ 2) * 1.6
    Btn frm, acDetail, "btnOpen" & idx, cap, x, y, 8.1, 1.3, Not isReport
    L m, "Private Sub btnOpen" & idx & "_Click()"
    If isReport Then
        L m, "  DoCmd.OpenReport """ & target & """, acViewPreview"
    Else
        L m, "  DoCmd.OpenForm """ & target & """"
    End If
    L m, "End Sub"
End Sub

Private Sub BuildMenu()
    Dim frm As Form, m As String, db As DAO.Database
    SafeDelete "frmMain"
    Set frm = CreateForm()
    With frm
        .Caption = ChrW(1603) & ChrW(1606) & ChrW(1578) & ChrW(1585) & ChrW(1608) & ChrW(1604) & " " & ChrW(1608) & ChrW(1605) & ChrW(1578) & ChrW(1575) & ChrW(1576) & ChrW(1593) & ChrW(1577) & " " & ChrW(1605) & ChrW(1583) & ChrW(1585) & ChrW(1587) & ChrW(1610) & ChrW(1577)
        .Orientation = 1: .AutoCenter = True: .AutoResize = True
        .RecordSelectors = False: .NavigationButtons = False: .ScrollBars = 2
        .Width = 18 * TW
        .Section(acDetail).BackColor = SURFACE
    End With
    Header frm, ChrW(1603) & ChrW(1606) & ChrW(1578) & ChrW(1585) & ChrW(1608) & ChrW(1604) & " " & ChrW(1608) & ChrW(1605) & ChrW(1578) & ChrW(1575) & ChrW(1576) & ChrW(1593) & ChrW(1577) & " " & ChrW(1605) & ChrW(1583) & ChrW(1585) & ChrW(1587) & ChrW(1610) & ChrW(1577), ChrW(1604) & ChrW(1608) & ChrW(1581) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1581) & ChrW(1603) & ChrW(1605) & " " & ChrW(1575) & ChrW(1604) & ChrW(1585) & ChrW(1574) & ChrW(1610) & ChrW(1587) & ChrW(1610) & ChrW(1577), 18
    Kpi frm, 0, 4, ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1575) & ChrW(1578), "=DCount(""*"",""Companies"")"
    Kpi frm, 1, 4, ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1608) & ChrW(1593), "=DCount(""*"",""Branches"")"
    Kpi frm, 2, 4, ChrW(1602) & ChrW(1608) & ChrW(1575) & ChrW(1593) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1593) & ChrW(1604) & ChrW(1610) & ChrW(1605), "=DCount(""*"",""SchoolRules"")"
    Kpi frm, 3, 4, ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1608) & ChrW(1604), "=DCount(""*"",""Classes"")"
    frm.Section(acFooter).Height = 0.8 * TW
    frm.Section(acFooter).BackColor = WHITE
    MenuBtn frm, m, 0, "frmCompanies", ChrW(1605) & ChrW(1604) & ChrW(1601) & " " & ChrW(1575) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1575) & ChrW(1578) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1572) & ChrW(1587) & ChrW(1587) & ChrW(1575) & ChrW(1578), False
    MenuBtn frm, m, 1, "frmBranches", ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1608) & ChrW(1593) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1578) & ChrW(1576) & ChrW(1593) & ChrW(1610) & ChrW(1577) & " " & ChrW(1604) & ChrW(1604) & ChrW(1588) & ChrW(1585) & ChrW(1603) & ChrW(1575) & ChrW(1578), False
    MenuBtn frm, m, 2, "frmSchoolRules", ChrW(1602) & ChrW(1608) & ChrW(1575) & ChrW(1593) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1580) & ChrW(1575) & ChrW(1581) & " " & ChrW(1608) & ChrW(1581) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576), False
    MenuBtn frm, m, 3, "frmClasses", ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1589) & ChrW(1608) & ChrW(1604) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1585) & ChrW(1608) & ChrW(1593), False
    MenuBtn frm, m, 4, "frmTerms", ChrW(1575) & ChrW(1604) & ChrW(1601) & ChrW(1578) & ChrW(1585) & ChrW(1575) & ChrW(1578) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1587) & ChrW(1606) & ChrW(1608) & ChrW(1575) & ChrW(1578) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610) & ChrW(1577), False
    MenuBtn frm, m, 5, "frmSubjects", ChrW(1575) & ChrW(1604) & ChrW(1605) & ChrW(1608) & ChrW(1575) & ChrW(1583) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1575) & ChrW(1578) & " " & ChrW(1575) & ChrW(1604) & ChrW(1593) & ChrW(1592) & ChrW(1605) & ChrW(1609), False
    MenuBtn frm, m, 6, "frmStudents", ChrW(1605) & ChrW(1604) & ChrW(1601) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1608) & ChrW(1608) & ChrW(1604) & ChrW(1610) & " " & ChrW(1575) & ChrW(1604) & ChrW(1571) & ChrW(1605) & ChrW(1585), False
    MenuBtn frm, m, 7, "frmGrades", ChrW(1585) & ChrW(1589) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1583) & ChrW(1585) & ChrW(1580) & ChrW(1575) & ChrW(1578) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1606) & ChrW(1578) & ChrW(1610) & ChrW(1580) & ChrW(1577), False
    MenuBtn frm, m, 8, "frmAbsence", ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576) & " " & ChrW(1608) & ChrW(1575) & ChrW(1604) & ChrW(1571) & ChrW(1593) & ChrW(1584) & ChrW(1575) & ChrW(1585), False
    MenuBtn frm, m, 9, "rptCertificate", ChrW(1606) & ChrW(1578) & ChrW(1610) & ChrW(1580) & ChrW(1577) & " " & ChrW(1575) & ChrW(1604) & ChrW(1591) & ChrW(1575) & ChrW(1604) & ChrW(1576) & " " & ChrW(1604) & ChrW(1603) & ChrW(1604) & " " & ChrW(1601) & ChrW(1589) & ChrW(1604) & " " & ChrW(1583) & ChrW(1585) & ChrW(1575) & ChrW(1587) & ChrW(1610), True
    MenuBtn frm, m, 10, "rptAbsenceAlert", ChrW(1578) & ChrW(1606) & ChrW(1576) & ChrW(1610) & ChrW(1607) & " " & ChrW(1578) & ChrW(1580) & ChrW(1575) & ChrW(1608) & ChrW(1586) & " " & ChrW(1581) & ChrW(1583) & " " & ChrW(1575) & ChrW(1604) & ChrW(1594) & ChrW(1610) & ChrW(1575) & ChrW(1576), True
    Btn frm, acDetail, "btnExit", ChrW(1582) & ChrW(1585) & ChrW(1608) & ChrW(1580) & " " & ChrW(1605) & ChrW(1606) & " " & ChrW(1575) & ChrW(1604) & ChrW(1576) & ChrW(1585) & ChrW(1606) & ChrW(1575) & ChrW(1605) & ChrW(1580), 0.6, 11.1, 8.1, 1.3, False
    L m, "Private Sub Form_Activate()"
    L m, "  Me.Recalc"
    L m, "End Sub"
    L m, "Private Sub btnExit_Click()"
    L m, "  If MsgBox(ChrW(1607) & ChrW(1604) & ChrW(32) & ChrW(1578) & ChrW(1585) & ChrW(1610) & ChrW(1583) & ChrW(32) & ChrW(1575) & ChrW(1604) & ChrW(1582) & ChrW(1585) & ChrW(1608) & ChrW(1580) & ChrW(1567), vbYesNo + vbQuestion) = vbYes Then DoCmd.Quit"
    L m, "End Sub"
    frm.Section(acDetail).Height = 13.0 * TW
    frm.OnActivate = "[Event Procedure]"
    frm.Module.InsertText m
    FinishForm frm, "frmMain"

    ' open frmMain automatically when the database starts
    Set db = CurrentDb
    On Error Resume Next
    db.Properties("StartUpForm") = "frmMain"
    If Err.Number <> 0 Then
        Err.Clear
        db.Properties.Append db.CreateProperty("StartUpForm", dbText, "frmMain")
    End If
End Sub

دليل الاستخدام خطوة بخطوة

  1. 1

    احتفظ بنسخة احتياطية؛ أنشئ قاعدة جديدة بالسكربت ثم شغّل BuildAllScreens. لا تُنقل بيانات الإصدار القديم تلقائياً؛ أدخل الشركة والفروع ثم قواعد العمل قبل تسجيل الحركات.

  2. 2

    أدخل نسبة النجاح وحد الغياب غير المعذور، والفروع والفصول والفترات الدراسية المرتبطة بالقواعد.

  3. 3

    أدخل المواد والدرجات العظمى ثم الطلاب وفصولهم وبيانات ولي الأمر.

  4. 4

    ارصد درجة الطالب للمادة والفترة؛ يتحقق من الدرجة العظمى ويمنع التكرار ويحسب النسبة ونجاح المادة وفق القاعدة. النتيجة تجمع المواد المرصودة فقط، وليست شهادة معتمدة باكتمال المواد.

  5. 5

    سجّل الغياب داخل تواريخ الفصل الدراسي وراجع تقرير تجاوز الحد؛ الأعذار لا تدخل في عدد الغياب غير المعذور.

كود SQL للجداول

SchoolControl.sql
-- SchoolControl.accdb — Microsoft Access SQL
-- ملاحظة: محرر SQL في أكسيس ينفذ جملة واحدة في كل مرة. انسخ كل جملة على حدة.

CREATE TABLE [Companies] (
    [CompanyID] COUNTER,
    [CompanyName] TEXT(255) NOT NULL,
    [RegistrationNo] TEXT(255),
    [TaxNumber] TEXT(255),
    [Phone] TEXT(255),
    [Address] TEXT(255),
    CONSTRAINT [PK_Companies] PRIMARY KEY ([CompanyID])
);

CREATE TABLE [Branches] (
    [BranchID] COUNTER,
    [CompanyID] LONG NOT NULL,
    [BranchName] TEXT(255) NOT NULL,
    [Phone] TEXT(255),
    [Address] TEXT(255),
    CONSTRAINT [PK_Branches] PRIMARY KEY ([BranchID])
);

CREATE TABLE [SchoolRules] (
    [RuleID] COUNTER,
    [CompanyID] LONG NOT NULL,
    [RuleName] TEXT(255) NOT NULL,
    [PassPercent] DOUBLE NOT NULL,
    [AbsenceLimit] LONG NOT NULL,
    [Notes] TEXT(255),
    CONSTRAINT [PK_SchoolRules] PRIMARY KEY ([RuleID])
);

CREATE TABLE [Classes] (
    [ClassID] COUNTER,
    [CompanyID] LONG NOT NULL,
    [BranchID] LONG NOT NULL,
    [ClassName] TEXT(255) NOT NULL,
    [Grade] TEXT(255),
    CONSTRAINT [PK_Classes] PRIMARY KEY ([ClassID])
);

CREATE TABLE [Terms] (
    [TermID] COUNTER,
    [CompanyID] LONG NOT NULL,
    [RuleID] LONG NOT NULL,
    [TermName] TEXT(255) NOT NULL,
    [AcademicYear] TEXT(255) NOT NULL,
    [StartDate] DATETIME NOT NULL,
    [EndDate] DATETIME NOT NULL,
    CONSTRAINT [PK_Terms] PRIMARY KEY ([TermID])
);

CREATE TABLE [Students] (
    [StudentID] COUNTER,
    [CompanyID] LONG NOT NULL,
    [BranchID] LONG NOT NULL,
    [StudentName] TEXT(255) NOT NULL,
    [ClassID] LONG NOT NULL,
    [SeatNo] TEXT(255),
    [BirthDate] DATETIME,
    [Address] TEXT(255),
    [ParentName] TEXT(255),
    [ParentPhone] TEXT(255),
    CONSTRAINT [PK_Students] PRIMARY KEY ([StudentID])
);

CREATE TABLE [Subjects] (
    [SubjectID] COUNTER,
    [CompanyID] LONG NOT NULL,
    [SubjectName] TEXT(255) NOT NULL,
    [MaxScore] DOUBLE NOT NULL,
    CONSTRAINT [PK_Subjects] PRIMARY KEY ([SubjectID])
);

CREATE TABLE [Grades] (
    [GradeID] COUNTER,
    [StudentID] LONG NOT NULL,
    [SubjectID] LONG NOT NULL,
    [TermID] LONG NOT NULL,
    [Score] DOUBLE NOT NULL,
    [MaxScore] DOUBLE,
    [PassPercent] DOUBLE,
    [Percentage] DOUBLE,
    [Passed] YESNO,
    CONSTRAINT [PK_Grades] PRIMARY KEY ([GradeID])
);

CREATE TABLE [Absence] (
    [AbsID] COUNTER,
    [StudentID] LONG NOT NULL,
    [TermID] LONG NOT NULL,
    [AbsDate] DATETIME NOT NULL,
    [Excused] YESNO,
    CONSTRAINT [PK_Absence] PRIMARY KEY ([AbsID])
);

ALTER TABLE [Branches] ADD CONSTRAINT [FK_Branches_Companies]
    FOREIGN KEY ([CompanyID]) REFERENCES [Companies] ([CompanyID]);

ALTER TABLE [SchoolRules] ADD CONSTRAINT [FK_SchoolRules_Companies]
    FOREIGN KEY ([CompanyID]) REFERENCES [Companies] ([CompanyID]);

ALTER TABLE [Classes] ADD CONSTRAINT [FK_Classes_Companies]
    FOREIGN KEY ([CompanyID]) REFERENCES [Companies] ([CompanyID]);

ALTER TABLE [Classes] ADD CONSTRAINT [FK_Classes_Branches]
    FOREIGN KEY ([BranchID]) REFERENCES [Branches] ([BranchID]);

ALTER TABLE [Terms] ADD CONSTRAINT [FK_Terms_Companies]
    FOREIGN KEY ([CompanyID]) REFERENCES [Companies] ([CompanyID]);

ALTER TABLE [Students] ADD CONSTRAINT [FK_Students_Companies]
    FOREIGN KEY ([CompanyID]) REFERENCES [Companies] ([CompanyID]);

ALTER TABLE [Students] ADD CONSTRAINT [FK_Students_Branches]
    FOREIGN KEY ([BranchID]) REFERENCES [Branches] ([BranchID]);

ALTER TABLE [Subjects] ADD CONSTRAINT [FK_Subjects_Companies]
    FOREIGN KEY ([CompanyID]) REFERENCES [Companies] ([CompanyID]);

ALTER TABLE [Terms] ADD CONSTRAINT [FK_Terms_SchoolRules]
    FOREIGN KEY ([RuleID]) REFERENCES [SchoolRules] ([RuleID]);

ALTER TABLE [Students] ADD CONSTRAINT [FK_Students_Classes]
    FOREIGN KEY ([ClassID]) REFERENCES [Classes] ([ClassID]);

ALTER TABLE [Grades] ADD CONSTRAINT [FK_Grades_Students]
    FOREIGN KEY ([StudentID]) REFERENCES [Students] ([StudentID]);

ALTER TABLE [Grades] ADD CONSTRAINT [FK_Grades_Subjects]
    FOREIGN KEY ([SubjectID]) REFERENCES [Subjects] ([SubjectID]);

ALTER TABLE [Grades] ADD CONSTRAINT [FK_Grades_Terms]
    FOREIGN KEY ([TermID]) REFERENCES [Terms] ([TermID]);

ALTER TABLE [Absence] ADD CONSTRAINT [FK_Absence_Students]
    FOREIGN KEY ([StudentID]) REFERENCES [Students] ([StudentID]);

ALTER TABLE [Absence] ADD CONSTRAINT [FK_Absence_Terms]
    FOREIGN KEY ([TermID]) REFERENCES [Terms] ([TermID]);