fix invoice and enrollment fees
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 48s
Tests / PHPUnit (push) Failing after 1m19s

This commit is contained in:
root
2026-08-27 18:34:36 -04:00
parent 0ae9993d82
commit 38644b32ae
29 changed files with 1836 additions and 1555 deletions
+642
View File
@@ -0,0 +1,642 @@
yes
# Implementation Documentation
## Al Rahma Sunday School Mobile App - Flutter Implementation
This document describes the complete implementation of the Al Rahma Sunday School mobile application built with Flutter for iOS and Android platforms.
---
## Project Overview
**Project Name:** Al Rahma Sunday School Mobile App
**Platform:** Flutter (iOS & Android)
**Language:** Dart
**State Management:** Provider
**API Integration:** RESTful API with JWT Authentication
**Architecture:** Service-Oriented Architecture with Provider Pattern
---
## Project Structure
```
alrahma_phone_app/
├── lib/
│ ├── core/
│ │ ├── config/
│ │ │ └── app_config.dart # App configuration and constants
│ │ ├── models/
│ │ │ ├── api_response.dart # API response wrapper models
│ │ │ └── user_model.dart # User data model
│ │ ├── providers/
│ │ │ └── auth_provider.dart # Authentication state management
│ │ ├── router/
│ │ │ └── app_router.dart # App navigation routing
│ │ ├── services/
│ │ │ ├── api_service.dart # HTTP client with JWT authentication
│ │ │ ├── auth_service.dart # Authentication business logic
│ │ │ └── storage_service.dart # Secure storage and preferences
│ │ └── theme/
│ │ └── app_theme.dart # Material Design 3 theme
│ ├── features/
│ │ ├── auth/
│ │ │ └── screens/
│ │ │ ├── login_screen.dart # Login screen with validation
│ │ │ └── register_screen.dart # Registration screen
│ │ ├── dashboard/
│ │ │ └── screens/
│ │ │ └── dashboard_screen.dart # Main dashboard with quick actions
│ │ └── splash/
│ │ └── screens/
│ │ └── splash_screen.dart # Splash/loading screen
│ └── main.dart # App entry point
├── android/ # Android platform configuration
├── ios/ # iOS platform configuration
├── assets/
│ ├── images/ # Image assets
│ └── icons/ # Icon assets
├── pubspec.yaml # Dependencies and project config
└── README.md # Project documentation
```
---
## Core Implementation
### 1. Configuration (`lib/core/config/`)
#### `app_config.dart`
- **Base URL Configuration:** API endpoint configuration
- **API Timeout Settings:** Request timeout configuration (30 seconds)
- **Token Storage Keys:** Secure storage key constants
- **App Metadata:** App name and version information
**Key Features:**
- Centralized configuration management
- Easy environment switching (dev/staging/production)
- Constants for API paths and keys
---
### 2. Services Layer (`lib/core/services/`)
#### `api_service.dart` - HTTP Client Service
**Implementation Details:**
- Built on Dio HTTP client with interceptors
- **JWT Authentication:** Automatic token injection via `Authorization: Bearer <token>` header
- **Request Interceptors:** Automatically adds authentication token and timezone headers
- **Error Interceptors:** Handles 401 unauthorized responses and clears tokens
- **Response Logging:** Pretty logging in debug mode using `pretty_dio_logger`
- **Error Handling:** Custom `ApiException` class with status codes and error messages
**Methods Implemented:**
- `get()` - GET requests with query parameters
- `post()` - POST requests with body data
- `put()` - PUT requests for updates
- `delete()` - DELETE requests
**Features:**
- Automatic token refresh handling
- Timezone support via `X-Timezone` header
- Request/response interceptors
- Comprehensive error handling
#### `storage_service.dart` - Local Storage Service
**Implementation Details:**
- **Secure Storage:** Uses `flutter_secure_storage` for sensitive data (JWT tokens)
- **Preferences Storage:** Uses `shared_preferences` for user data and settings
- **Token Management:** Secure storage for authentication tokens
- **User Data Persistence:** JSON serialization for user profile data
- **Timezone Storage:** User timezone preference storage
**Methods:**
- `saveToken()` / `getToken()` / `deleteToken()` - Token management
- `saveUserData()` / `getUserData()` / `deleteUserData()` - User data management
- `saveTimezone()` / `getTimezone()` - Timezone management
- `clearAll()` - Complete data clearing on logout
#### `auth_service.dart` - Authentication Service
**Implementation Details:**
- **Login:** Email/password authentication with JWT token retrieval
- **Registration:** New user account creation
- **Profile Management:** Get and update user profile
- **Token Management:** Automatic token storage on successful login
- **Logout:** Complete session clearing
**API Endpoints Integrated:**
- `POST /api/v1/login` - User authentication
- `POST /api/v1/register` - User registration
- `GET /api/v1/profile` - Get user profile
- `PUT /api/v1/profile` - Update user profile
**Response Handling:**
- Uses `ApiResponse<T>` wrapper for consistent response handling
- Success/error state management
- User data parsing and storage
---
### 3. Models (`lib/core/models/`)
#### `api_response.dart`
**Models:**
- `ApiResponse<T>` - Generic wrapper for API responses
- `success` (bool) - Response status
- `data` (T?) - Response data
- `message` (String?) - Response message
- `errors` (dynamic) - Validation errors
- `PaginatedResponse<T>` - Paginated list responses
- `data` (List<T>) - List of items
- `pagination` (PaginationInfo) - Pagination metadata
- `PaginationInfo` - Pagination metadata
- `currentPage`, `perPage`, `total`, `totalPages`
- Helper methods: `hasNextPage`, `hasPreviousPage`
#### `user_model.dart`
**User Model:**
- `User` class with properties:
- `id`, `firstname`, `lastname`, `name`, `email`, `cellphone`
- `roles` (UserRoles object) or `rolesList` (List<String>)
- Helper properties: `fullName`, `initials`
- Role checks: `isParent`, `isTeacher`, `isAdmin`
- `UserRoles` class:
- Boolean flags: `parent`, `teacher`, `admin`
- `LoginResponse` class:
- `token` (String) - JWT token
- `user` (User) - User information
**JSON Serialization:**
- `fromJson()` factory constructors
- `toJson()` methods for data persistence
---
### 4. State Management (`lib/core/providers/`)
#### `auth_provider.dart` - Authentication Provider
**State Variables:**
- `_user` (User?) - Current logged-in user
- `_isLoading` (bool) - Loading state indicator
- `_isAuthenticated` (bool) - Authentication status
**Methods:**
- `checkAuthStatus()` - Checks for existing authentication on app start
- `login()` - Handles login flow with state updates
- `register()` - Handles registration flow
- `logout()` - Clears authentication state
- `refreshProfile()` - Refreshes user profile data
**Features:**
- Reactive state management with `ChangeNotifier`
- Automatic state updates on authentication changes
- Loading state management for UI feedback
---
### 5. Navigation (`lib/core/router/`)
#### `app_router.dart`
**Routes Defined:**
- `/` (splash) - Splash screen
- `/login` - Login screen
- `/register` - Registration screen
- `/dashboard` - Main dashboard
**Implementation:**
- `generateRoute()` - Route generator function
- Named route navigation
- Material page transitions
---
### 6. Theme (`lib/core/theme/`)
#### `app_theme.dart`
**Theme Configuration:**
- **Material Design 3** implementation
- **Light Theme:**
- Primary color: Blue (#2196F3)
- Secondary color: Cyan (#03A9F4)
- Background: Light gray (#FAFAFA)
- **Dark Theme:**
- Primary color: Blue (#2196F3)
- Dark background: #121212
- Surface color: #1E1E1E
**Components Styled:**
- AppBar with elevation and colors
- Cards with rounded corners (12px radius)
- Elevated buttons with padding and rounded corners
- Input fields with focused states and error styling
- Consistent color scheme throughout
**Features:**
- System theme mode support (auto light/dark)
- Consistent spacing and typography
- Material 3 design language
---
## Features Implementation
### 1. Splash Screen (`lib/features/splash/screens/splash_screen.dart`)
**Functionality:**
- Displays app branding and loading indicator
- Checks authentication status on app launch
- Automatically navigates to:
- Dashboard if user is authenticated
- Login screen if not authenticated
- 2-second delay for smooth transition
**UI Elements:**
- App icon (school icon)
- App name: "Al Rahma Sunday School"
- Loading spinner
---
### 2. Authentication Screens
#### Login Screen (`lib/features/auth/screens/login_screen.dart`)
**Features:**
- Email and password input fields
- Form validation:
- Email format validation
- Required field validation
- Password visibility toggle
- Loading state during authentication
- Error handling with user-friendly messages
- Navigation to registration screen
- Automatic navigation to dashboard on success
**UI Components:**
- Material Design inputs
- Elevated button with loading state
- Error snackbar notifications
#### Registration Screen (`lib/features/auth/screens/register_screen.dart`)
**Features:**
- Complete registration form:
- First name
- Last name
- Email
- Password (with strength validation)
- Confirm password (with matching validation)
- Form validation:
- All fields required
- Email format validation
- Password minimum 8 characters
- Password confirmation matching
- Password visibility toggles
- Loading state during registration
- Success/error feedback
- Navigation back to login on success
---
### 3. Dashboard Screen (`lib/features/dashboard/screens/dashboard_screen.dart`)
**Features:**
- User profile card:
- User avatar with initials
- Full name display
- Email address
- Quick actions grid:
- Students management
- Messages
- Events
- Homework
- Payments
- Notifications
- Logout functionality
- Responsive grid layout (2 columns)
**UI Components:**
- Material cards with elevation
- Circular avatar with initials
- Icon-based action cards
- Color-coded action categories
**Future Enhancements:**
- Each quick action card is ready for navigation implementation
- Placeholder functionality for "Coming soon" features
---
## Platform Configuration
### Android Configuration
#### Files Created:
1. **`android/app/build.gradle`**
- Application ID: `com.alrahma.alrahma_app`
- Min SDK: 21 (Android 5.0)
- Target SDK: 34 (Android 14)
- Kotlin support
- Flutter integration
2. **`android/app/src/main/AndroidManifest.xml`**
- Internet permission
- Main activity configuration
- Launch configuration
- App label: "Al Rahma"
3. **`android/app/src/main/kotlin/com/alrahma/alrahma_app/MainActivity.kt`**
- Flutter activity integration
- Kotlin implementation
4. **`android/app/src/main/res/values/styles.xml`**
- Launch theme configuration
- Normal theme configuration
5. **`android/app/src/main/res/drawable/launch_background.xml`**
- Launch screen background
- App icon centering
6. **`android/build.gradle`**
- Kotlin version: 1.9.0
- Android Gradle Plugin: 8.1.0
- Repository configuration
7. **`android/settings.gradle`**
- Flutter plugin loader
- Project configuration
8. **`android/gradle.properties`**
- JVM arguments
- AndroidX enablement
- Jetifier enablement
---
### iOS Configuration
#### Files Created:
1. **`ios/Runner/Info.plist`**
- Bundle identifier: `com.alrahma.alrahmaApp`
- Display name: "Al Rahma"
- App Transport Security configuration
- Supported orientations
- Launch screen configuration
2. **`ios/Runner/AppDelegate.swift`**
- Swift implementation
- Flutter plugin registration
- Application lifecycle management
3. **`ios/Podfile`**
- iOS deployment target: 12.0
- CocoaPods configuration
- Flutter integration
- Framework settings
4. **`ios/Runner.xcodeproj/project.pbxproj`**
- Xcode project configuration
- Build settings
- Swift version: 5.0
- Debug/Release/Profile configurations
---
## Dependencies
### Core Dependencies:
- **flutter:** SDK
- **provider:** ^6.1.1 - State management
- **dio:** ^5.4.0 - HTTP client
- **pretty_dio_logger:** ^1.3.1 - Request/response logging
- **shared_preferences:** ^2.2.2 - Local preferences storage
- **flutter_secure_storage:** ^9.0.0 - Secure token storage
- **json_annotation:** ^4.8.1 - JSON serialization annotations
- **intl:** ^0.18.1 - Internationalization
- **timezone:** ^0.9.2 - Timezone handling
- **connectivity_plus:** ^5.0.2 - Network connectivity checking
### UI Dependencies:
- **cupertino_icons:** ^1.0.6 - iOS-style icons
- **flutter_svg:** ^2.0.9 - SVG image support
- **cached_network_image:** ^3.3.0 - Network image caching
### Dev Dependencies:
- **flutter_test:** Testing framework
- **flutter_lints:** ^3.0.1 - Linting rules
- **build_runner:** ^2.4.7 - Code generation
- **json_serializable:** ^6.7.1 - JSON code generation
---
## Security Features
### 1. JWT Token Management
- Secure token storage using `flutter_secure_storage`
- Automatic token injection in API requests
- Token expiration handling (401 response)
- Automatic logout on token expiration
### 2. Secure Storage
- Sensitive data (tokens) stored in secure storage
- User data stored in encrypted preferences
- Complete data clearing on logout
### 3. API Security
- HTTPS support (configured in Info.plist)
- Authorization headers for all protected requests
- Request/response validation
---
## Error Handling
### 1. API Error Handling
- Custom `ApiException` class
- HTTP status code handling:
- 400: Bad Request
- 401: Unauthorized (automatic logout)
- 403: Forbidden
- 404: Not Found
- 422: Validation Error
- 500: Server Error
- Validation error parsing
- User-friendly error messages
### 2. Network Error Handling
- Network connectivity checking
- Timeout handling (30 seconds)
- Offline state management
### 3. UI Error Feedback
- Snackbar notifications for errors
- Loading states during async operations
- Form validation feedback
---
## API Integration
### Implemented Endpoints:
1. **Authentication:**
- `POST /api/v1/login` - User login
- `POST /api/v1/register` - User registration
2. **Profile:**
- `GET /api/v1/profile` - Get user profile
- `PUT /api/v1/profile` - Update user profile
### Ready for Implementation:
Based on `API_DOCUMENTATION.md`, the following endpoints are ready to be integrated:
- Messaging system
- Students management
- Parents management
- Classes management
- Attendance tracking
- Scores & Grades
- Payments & Invoices
- Notifications
- Events & Calendar
- Dashboard data
- Homework & Assignments
- Quizzes & Exams
- And 50+ additional endpoints
---
## Code Quality
### Linting:
- Flutter lints package configured
- Analysis options with strict rules:
- Prefer const constructors
- Avoid print statements
- Prefer single quotes
- Require trailing commas
### Code Organization:
- Feature-based folder structure
- Separation of concerns (services, models, providers, UI)
- Reusable components
- Consistent naming conventions
### Best Practices:
- Null safety enabled
- Type-safe API responses
- Error handling at all levels
- Loading states for async operations
- Form validation
- Secure storage for sensitive data
---
## Testing Readiness
### Structure Ready For:
- Unit tests for services
- Widget tests for screens
- Integration tests for flows
- Provider tests for state management
### Test Files Location:
- `test/` directory (to be created)
- Service tests
- Model tests
- Provider tests
- Widget tests
---
## Build Configuration
### Android Build:
- **APK:** `flutter build apk --release`
- **App Bundle:** `flutter build appbundle --release`
- **Debug:** `flutter run`
### iOS Build:
- **IPA:** `flutter build ios --release`
- **Xcode:** Open `ios/Runner.xcworkspace`
### Environment Configuration:
- Update `lib/core/config/app_config.dart` with production API URL
- Configure signing certificates for release builds
- Update bundle identifiers if needed
---
## Next Steps / Future Enhancements
### Immediate:
1. Update API base URL in `app_config.dart`
2. Test authentication flow
3. Add remaining feature screens
4. Implement messaging system
5. Add student management features
### Short-term:
1. Implement push notifications
2. Add offline data caching
3. Implement image upload functionality
4. Add localization support
5. Implement deep linking
### Long-term:
1. Add biometric authentication
2. Implement advanced analytics
3. Add social features
4. Implement real-time updates
5. Add advanced reporting features
---
## File Summary
### Total Files Created: 30+
**Core Files:**
- 8 service/model files
- 3 provider/router/theme files
- 1 main entry point
**Feature Files:**
- 4 screen implementations
- Organized by feature modules
**Configuration Files:**
- 8 Android configuration files
- 4 iOS configuration files
- 2 asset directories
- Project configuration files
**Documentation:**
- README.md
- IMPLEMENTATION.md (this file)
- API_DOCUMENTATION.md (reference)
---
## Conclusion
A complete Flutter mobile application foundation has been implemented with:
- ✅ Complete authentication system
- ✅ Secure API integration
- ✅ Professional UI/UX
- ✅ State management
- ✅ Platform-specific configurations
- ✅ Error handling
- ✅ Security best practices
- ✅ Scalable architecture
The app is ready for feature expansion and can be built for both iOS and Android platforms.
---
**Last Updated:** 2025-01-15
**Version:** 1.0.0
**Status:** Production Ready (Foundation Complete)
File diff suppressed because it is too large Load Diff
+241
View File
@@ -0,0 +1,241 @@
# Balanced Student Section Distribution Plan
## 1. Purpose
This plan describes how already-promoted students will be distributed into sections for the next academic year.
Promotion, deliberation, passing decisions, and student eligibility have already been completed and are outside the scope of this process.
The distribution process must ensure that:
* Students are divided as equally as possible among sections.
* Each section contains a balanced number of students from every score range.
* Section average scores are reasonably close.
* User-defined minimum and maximum section sizes are respected.
## 2. Required User Inputs
Before starting the distribution, the user must enter:
| Input | Description |
| ---------------------------- | --------------------------------------------- |
| Number of Sections | Number of sections to be created |
| Minimum Students per Section | Minimum permitted section size |
| Maximum Students per Section | Maximum permitted section size, if applicable |
The student list must already include each students final score from the previous academic year.
## 3. Score Range Classification
Students must be classified using their previous-year final scores:
| Score Group | Score Range |
| ----------- | -----------: |
| Group 1 | 90100 |
| Group 2 | 8089 |
| Group 3 | 7079 |
| Group 4 | 69 and below |
Each student must belong to exactly one score group.
## 4. Validate the Number of Sections
Let:
* **N** = Total number of students to distribute.
* **S** = Number of sections entered by the user.
* **M** = Minimum number of students per section.
* **X** = Maximum number of students per section.
The requested section count is valid only when:
**S × M ≤ N**
When a maximum section size is used, the following condition must also be satisfied:
**N ≤ S × X**
Therefore, the complete validation rule is:
**S × M ≤ N ≤ S × X**
If no maximum section size is used, only the minimum condition applies.
## 5. Validation Results
The system should return one of the following results:
### Insufficient Students
The requested number of sections cannot be created because the total number of students is below the required minimum.
### Capacity Exceeded
The requested sections cannot contain all students without exceeding the maximum section size.
### Valid Setup
The entered number of sections and section-size limits are valid, and distribution may proceed.
The system must not automatically change the number of sections entered by the user.
## 6. Determine the Target Section Sizes
Divide the total number of students by the number of sections.
Each section should receive:
**Base section size = N divided by S**
Any remaining students should be assigned one at a time to different sections.
The difference between the largest and smallest section should not exceed one student.
Example:
* Total students: 61
* Number of sections: 2
Result:
* Section 1: 31 students
* Section 2: 30 students
## 7. Calculate the Score-Group Allocation
For each score group:
1. Count the number of students in that group.
2. Divide the group total by the number of sections.
3. Assign the base number to every section.
4. Distribute any remaining students across different sections.
For a score group containing **G** students:
**Base allocation = G divided by S**
**Remaining students = G modulo S**
Example:
* Students scoring 90100: 11
* Sections: 2
Result:
* Section 1: 6 students
* Section 2: 5 students
The difference between sections within the same score group should not exceed one student.
## 8. Mathematical Limitation
Exact equality is not always possible.
For example, nine students from one score group cannot be divided equally between two sections. One section must receive five students and the other must receive four.
Therefore, the required rule is:
* Equal distribution when mathematically possible.
* A maximum difference of one student when exact equality is impossible.
## 9. Student Assignment Method
Within each score group:
1. Sort students from highest score to lowest score.
2. Assign them using a snake-distribution method.
3. Reverse the assignment direction after each round.
For two sections, use:
* First round: Section 1, Section 2
* Second round: Section 2, Section 1
* Third round: Section 1, Section 2
For three sections, use:
* First round: Section 1, Section 2, Section 3
* Second round: Section 3, Section 2, Section 1
This prevents one section from repeatedly receiving the highest-scoring students.
## 10. Rotate Remaining Students
Additional students caused by uneven division should not always be assigned to the first section.
The section receiving the extra student should rotate between score groups.
Example with two sections:
| Score Range | Section Receiving the Extra Student |
| ------------ | ----------------------------------- |
| 90100 | Section 1 |
| 8089 | Section 2 |
| 7079 | Section 1 |
| 69 and below | Section 2 |
This helps maintain equal total section sizes.
## 11. Balance the Average Scores
After the initial distribution, calculate the average previous-year score for every section.
The difference between the highest and lowest section averages should preferably not exceed one percentage point.
If the difference is too large, students may be exchanged between sections when:
* They belong to the same score range.
* The exchange improves the average-score balance.
* Section sizes remain valid.
* Minimum and maximum limits are still satisfied.
## 12. Additional Distribution Considerations
After academic balancing, the distribution may also be reviewed for:
* Gender balance, where applicable.
* Special educational needs.
* Language-support requirements.
* Behavioral considerations.
* Documented student-separation requirements.
* Medical or accessibility needs.
Any adjustment should preserve the score-range and section-size balance as much as possible.
## 13. Final Distribution Summary
The final result should include:
| Section | Total Students | 90100 | 8089 | 7079 | 69 and Below | Average Score |
| --------- | -------------: | -----: | ----: | ----: | -----------: | ------------: |
| Section 1 | 30 | 5 | 8 | 9 | 8 | 78.4 |
| Section 2 | 30 | 5 | 7 | 9 | 9 | 78.2 |
## 14. Final Validation Checklist
Before confirming the distribution, verify that:
* The number of sections was entered by the user.
* The minimum section size was entered by the user.
* The maximum section size was entered, when applicable.
* All students have been assigned exactly once.
* No section is below the minimum size.
* No section exceeds the maximum size.
* Total section sizes differ by no more than one student.
* Score-group counts differ by no more than one student.
* Average scores are reasonably balanced.
* No student is missing or duplicated.
## 15. Final Rule
The system distributes students only after promotion and deliberation are complete.
The user determines the number of sections and the section-size limits.
The system validates those values and creates the most balanced possible distribution based on:
* Total student count.
* Score-range counts.
* Section-size limits.
* Average section scores.
+170
View File
@@ -0,0 +1,170 @@
http://localhost:8080/printables_reports/badge_form (add position column to teacher_class table) fixed
http://localhost:8080/administrator/class_assignment (add position column to teacher_class table) fixed
Teacher Class assignment (Access Denied) fixd
invoice_management (Access Denied) fixd
Login Activity (Access Denied) fixd
User List (Access Denied) fixd
Permission Management (Access Denied) fixd
Score management is not editable
#######################################TO_FIX#########################################
attendance:
teacher side submit attendance once and add message saying you cannot change attendance after submitting (fixed)
uppercase first letter
icon books menu
fix books category display
registration date is a datetime but the db has only date no time?
office supplies opens new tab why
attendance managemnt side add 'add' button to add new entry for 'late' students (fixed)
staff view is not updating
scores:
$this->semesterScoreService->recalc($studentId, $classSectionId, $schoolYear, $semester); (this line is used in attendance to calculate scores, need to check it when fixing scores)
fix score update managment side
###############################################################################
*Day 1 readiness*
DROP TABLE `final_exam`, `final_score`, `homework`, `midterm_exam`, `participation_score`, `project`, `quiz`, `score_comments`, `semester_scores`;
-- Multi-entry tables
CREATE TABLE IF NOT EXISTS homework_scores (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id INT UNSIGNED NOT NULL,
class_section_id INT UNSIGNED NOT NULL,
school_year VARCHAR(16) NOT NULL,
semester ENUM('Spring','Fall') NOT NULL,
score DECIMAL(5,2) NOT NULL, -- 0..100
notes VARCHAR(255) NULL,
created_by INT UNSIGNED NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS quiz_scores (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id INT UNSIGNED NOT NULL,
class_section_id INT UNSIGNED NOT NULL,
school_year VARCHAR(16) NOT NULL,
semester ENUM('Spring','Fall') NOT NULL,
score DECIMAL(5,2) NOT NULL,
notes VARCHAR(255) NULL,
created_by INT UNSIGNED NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS project_scores (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id INT UNSIGNED NOT NULL,
class_section_id INT UNSIGNED NOT NULL,
school_year VARCHAR(16) NOT NULL,
semester ENUM('Spring','Fall') NOT NULL,
score DECIMAL(5,2) NOT NULL,
notes VARCHAR(255) NULL,
created_by INT UNSIGNED NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- Single-entry tables (one row per term)
CREATE TABLE IF NOT EXISTS participation_scores (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id INT UNSIGNED NOT NULL,
class_section_id INT UNSIGNED NOT NULL,
school_year VARCHAR(16) NOT NULL,
semester ENUM('Spring','Fall') NOT NULL,
score DECIMAL(5,2) NOT NULL,
updated_by INT UNSIGNED NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_participation (student_id, class_section_id, school_year, semester)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS midterm_scores (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id INT UNSIGNED NOT NULL,
class_section_id INT UNSIGNED NOT NULL,
school_year VARCHAR(16) NOT NULL,
semester ENUM('Spring','Fall') NOT NULL,
score DECIMAL(5,2) NOT NULL,
updated_by INT UNSIGNED NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_midterm (student_id, class_section_id, school_year, semester)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS final_scores (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id INT UNSIGNED NOT NULL,
class_section_id INT UNSIGNED NOT NULL,
school_year VARCHAR(16) NOT NULL,
semester ENUM('Spring','Fall') NOT NULL,
score DECIMAL(5,2) NOT NULL,
updated_by INT UNSIGNED NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_final (student_id, class_section_id, school_year, semester)
) ENGINE=InnoDB;
Larbi:
1- Payment, status changed to enrolled, email goes with all payment details (fixed)
6- Make php translation of sticker routine from vb excel (fixed)
3- Classroom prep: add adjustables (only on tables), show relevant info on paper (white board, trash bin, small tables, large tables, small chairs, regular chairs, teacher chairs), ignore 0 entries. + tie everything to inventory (fixed)
#Extra charges fix invoice pdf display and make all transaction are visible + fix the date of those transactions
#email does not reflect the discount
#Refunds should automaticallu scan for negative balance
2- Inventory + Assigning books to students tested and working
4- Train Amin on inventory + Grade box labeling
5- Contact Aziz to make layout for classes once sections are final
7- Fix grading + notification for entering grading + item not assigned if hw/test… not done that week.
8- Tardy slips readiness (automated printing)
9- Laptops readiness (homepage: alrahmaisgl) + 4 laptops
10- Print badges
Amin:
1- Enter categories of books (including teachers books)
2- Print grade labels + classes layout
3- Charge laptops
Outmane:
1- Process remaining enrollments
2- Assign students to class sections
3- Check inventory excel + Buy books
4- Test features (classroom prep)
5- Continue presentation
################################################################################
Work to Do in production:
dump roles table with data to add dynamic role names.
dump nav_items table with data to add new dynamic admins navbar
dump role_permissions table data
dump permission table
update teacher_class table
+629
View File
@@ -0,0 +1,629 @@
# Payments Logic and Data Repair Plan
## 1. Purpose
This plan fixes two separate problems:
1. **Application logic** that creates inconsistent or misleading payment rows.
2. **Existing data** in the `payments` table without deleting financial evidence or inventing transactions.
The repair must treat `payments` as an auditable ledger. A payment row should represent money actually received, not an invoice adjustment, fee assignment, discount, balance correction, or repeated attempt to make a balance reach zero.
---
## 2. Findings from the supplied dump
The dump contains **321 rows across 120 invoices**.
Key findings:
- `installment_seq` is `NULL` in all 321 rows.
- `number_of_installments` is actually being used as the payment sequence number. It matches chronological row order for every invoice.
- 65 invoices have more than one `total_amount` value across their payment history.
- 168 rows have `paid_amount = 10.00`, mostly recorded as cash during April and May 2026.
- 116 of 120 invoice histories are internally consistent once a fixed pre-existing paid amount or credit is allowed.
- 33 internally consistent invoices have a non-zero implied opening paid amount. This means the table is not a complete ledger for those invoices.
- Four invoice histories contain an unexplained balance jump:
| Invoice | Payment row where jump begins | Unexplained change |
|---:|---:|---:|
| 25 | 201 | -590.00 |
| 65 | 304 | -573.75 |
| 75 | 92 | +90.00 |
| 96 | 232 | -20.00 |
These four changes may represent missing payments, credits, reversals, adjustments, or incorrect stored balances. They must be reconciled against receipts, invoice history, audit logs, and bank or cash records before destructive correction.
---
## 3. Target accounting rules
After the repair, the following rules must always hold.
### 3.1 Payment rules
- One row in `payments` represents one actual receipt of money.
- A payment is immutable after posting, except for controlled metadata corrections.
- A mistaken payment is reversed with a linked reversal row. It is not deleted or silently overwritten.
- `paid_amount` must be greater than zero for a normal payment.
- `transaction_id` or an idempotency key must be unique and non-null.
- `parent_id`, `school_year`, and invoice ownership must come from the invoice, not from unchecked form input.
- Check-specific fields are required only for check payments.
- A payment cannot create a negative invoice balance unless explicit overpayment or account-credit logic is enabled.
### 3.2 Invoice rules
- `invoices.total_amount` is the current authoritative invoice total.
- `invoices.paid_amount` is the sum of valid posted payments plus approved opening credit or migrated paid balance.
- `invoices.balance = invoices.total_amount - invoices.paid_amount`.
- Invoice fees, discounts, waivers, penalties, and event charges are stored as adjustments, not payments.
- Invoice status is derived from the current balance instead of being independently guessed.
### 3.3 Installment rules
- `installment_seq` is the chronological payment sequence within one invoice.
- `number_of_installments` must not be used as both a sequence and a total count.
- Recommended final naming:
- `installment_seq`: this payment's sequence number.
- `installment_count`: total posted payment count, only if the application genuinely needs it.
---
## 4. Correct application logic
## 4.1 Record a payment atomically
All operations must run inside one database transaction. The invoice row must be locked so two users cannot post against the same balance simultaneously.
```text
BEGIN TRANSACTION
1. Load invoice FOR UPDATE.
2. Reject missing, cancelled, or wrong-school-year invoice.
3. Read authoritative parent_id, school_year, total_amount, paid_amount, and balance from invoice.
4. Validate payment amount and payment method.
5. Reject duplicate idempotency key or transaction ID.
6. Reject amount greater than current balance unless explicit credit handling is enabled.
7. Calculate next installment_seq while the invoice is locked.
8. Insert one payment row.
9. Update invoice paid_amount, balance, and status.
10. Write an audit record.
COMMIT
```
Representative MySQL logic:
```sql
START TRANSACTION;
SELECT
id,
parent_id,
total_amount,
paid_amount,
balance,
school_year,
status
INTO
@invoice_id,
@invoice_parent_id,
@invoice_total_amount,
@invoice_paid_amount,
@invoice_balance,
@invoice_school_year,
@invoice_status
FROM invoices
WHERE id = :invoice_id
FOR UPDATE;
-- Application validations before continuing:
-- :paid_amount > 0
-- :paid_amount <= invoice.balance unless credits are explicitly supported
-- invoice.school_year = active school year
-- invoice.status is not cancelled/void
-- transaction_id/idempotency_key does not already exist
SELECT COALESCE(MAX(installment_seq), 0) + 1
INTO @next_installment_seq
FROM payments
WHERE invoice_id = :invoice_id;
SET @new_paid_amount = @invoice_paid_amount + :paid_amount;
SET @new_balance = @invoice_total_amount - @new_paid_amount;
INSERT INTO payments (
parent_id,
invoice_id,
paid_amount,
installment_seq,
transaction_id,
check_file,
check_number,
payment_method,
payment_date,
school_year,
status,
updated_by,
created_at,
updated_at
) VALUES (
@invoice_parent_id,
@invoice_id,
:paid_amount,
@next_installment_seq,
:transaction_id,
:check_file,
:check_number,
LOWER(:payment_method),
:payment_date,
@invoice_school_year,
'recorded',
:updated_by,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
UPDATE invoices
SET paid_amount = @new_paid_amount,
balance = @new_balance,
status = CASE
WHEN @new_balance = 0 THEN 'paid'
WHEN @new_balance > 0 AND @new_paid_amount > 0 THEN 'partial'
WHEN @new_paid_amount = 0 THEN 'unpaid'
ELSE 'credit'
END,
updated_by = :updated_by,
updated_at = CURRENT_TIMESTAMP
WHERE id = :invoice_id;
COMMIT;
```
The exact status strings must match the values supported by the application. Do not introduce `partial` or `credit` until reporting and validation code understands them.
## 4.2 Record an invoice adjustment
A fee, discount, waiver, event charge, or correction changes the invoice total. It does not create a payment.
Create a dedicated adjustment table rather than forcing `payments` to perform accounting theatre:
```sql
CREATE TABLE invoice_adjustments (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
invoice_id INT UNSIGNED NOT NULL,
adjustment_type ENUM(
'charge',
'discount',
'waiver',
'credit',
'reversal',
'correction'
) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
description VARCHAR(255) NOT NULL,
source_reference VARCHAR(100) DEFAULT NULL,
school_year VARCHAR(9) NOT NULL,
created_by INT UNSIGNED DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_invoice_adjustments_invoice (invoice_id),
UNIQUE KEY uniq_invoice_adjustment_source (invoice_id, source_reference),
CONSTRAINT fk_invoice_adjustments_invoice
FOREIGN KEY (invoice_id) REFERENCES invoices(id),
CONSTRAINT chk_invoice_adjustment_amount
CHECK (amount <> 0)
) ENGINE=InnoDB;
```
Adjustment transaction:
```text
1. Lock invoice FOR UPDATE.
2. Insert exactly one adjustment row using an idempotent source reference.
3. Recalculate total_amount from invoice base charges plus all adjustments.
4. Recalculate balance as total_amount minus paid_amount.
5. Update invoice status.
6. Commit.
```
This eliminates the likely cause of repeated `$10.00` payment rows when the system is actually adding charges or closing small balances.
## 4.3 Prevent duplicate submissions
Add an idempotency key generated once by the client or server per payment request.
```sql
ALTER TABLE payments
ADD COLUMN idempotency_key CHAR(36) DEFAULT NULL AFTER transaction_id,
ADD UNIQUE KEY uniq_payments_idempotency_key (idempotency_key);
```
On retry, return the existing payment instead of inserting a second one.
## 4.4 Reverse rather than delete
Recommended columns:
```sql
ALTER TABLE payments
ADD COLUMN reversal_of_payment_id INT UNSIGNED DEFAULT NULL,
ADD COLUMN voided_at DATETIME DEFAULT NULL,
ADD COLUMN voided_by INT UNSIGNED DEFAULT NULL,
ADD COLUMN void_reason VARCHAR(255) DEFAULT NULL,
ADD KEY idx_payments_reversal (reversal_of_payment_id);
```
A reversal should insert an explicit reversing transaction or mark the original row void while preserving the audit trail. The selected policy must be consistent across invoice totals, cash reports, and receipts.
---
## 5. Existing-data repair strategy
## 5.1 Repair principles
The migration must not automatically:
- Delete `$10.00` rows.
- Merge rows merely because they share a date or amount.
- Change `paid_amount` without receipt evidence.
- Convert negative balances to zero using `GREATEST()`.
- Recalculate every historical balance from the current invoice total.
- Assume the first stored payment is the first payment ever made.
Those shortcuts make reports look tidy while making the ledger less truthful. Aesthetic consistency is not accounting integrity.
## 5.2 Maintenance procedure
1. Put payment creation and editing into maintenance mode.
2. Take a database backup and verify restoration on a separate database.
3. Copy `payments`, `invoices`, and relevant audit tables into dated backup tables.
4. Run the audit queries in the companion SQL script.
5. Reconcile the four unexplained transitions against external evidence.
6. Apply safe sequence and normalization updates.
7. Apply only approved balance corrections.
8. Rebuild invoice summaries.
9. Run all validation queries.
10. Deploy corrected application logic before reopening writes.
---
## 6. Data-repair SQL design
The companion SQL script creates these working tables:
- `payments_backup_20260718`: exact pre-repair copy.
- `payment_repair_analysis`: row-level sequence, running totals, and implied opening paid amount.
- `payment_repair_invoice_review`: invoice-level consistency summary.
- `payment_repair_transition_review`: unexplained changes between consecutive rows.
- `payment_repair_decisions`: reviewed instructions for each anomalous transition.
### 6.1 Sequence repair
Safe automatic correction:
```sql
UPDATE payments p
JOIN payment_repair_analysis a ON a.payment_id = p.id
SET p.installment_seq = a.expected_installment_seq
WHERE p.installment_seq IS NULL
OR p.installment_seq <> a.expected_installment_seq;
```
After the application has switched to `installment_seq`, repurpose the misleading field:
```sql
UPDATE payments p
JOIN (
SELECT invoice_id, COUNT(*) AS installment_count
FROM payments
WHERE status = 'recorded'
GROUP BY invoice_id
) c ON c.invoice_id = p.invoice_id
SET p.number_of_installments = c.installment_count;
```
Do not run the second update before confirming no application code still expects `number_of_installments` to be the current sequence.
### 6.2 Parent and school-year normalization
First inspect differences:
```sql
SELECT
p.id,
p.invoice_id,
p.parent_id AS payment_parent_id,
i.parent_id AS invoice_parent_id,
p.school_year AS payment_school_year,
i.school_year AS invoice_school_year
FROM payments p
JOIN invoices i ON i.id = p.invoice_id
WHERE p.parent_id <> i.parent_id
OR NOT (p.school_year <=> i.school_year);
```
After review, normalize from the authoritative invoice:
```sql
UPDATE payments p
JOIN invoices i ON i.id = p.invoice_id
SET p.parent_id = i.parent_id,
p.school_year = i.school_year
WHERE p.parent_id <> i.parent_id
OR NOT (p.school_year <=> i.school_year);
```
### 6.3 Balance-transition audit
For every row after the first payment on an invoice, the expected transition is:
```text
new balance
= previous balance
+ change in invoice total
- current payment
+ explicit non-payment adjustment not already reflected in total_amount
```
The supplied dump contains four unexplained transitions. The migration records a decision for each one:
```sql
CREATE TABLE payment_repair_decisions (
payment_id INT UNSIGNED NOT NULL,
invoice_id INT UNSIGNED NOT NULL,
decision ENUM(
'balance_is_wrong',
'missing_payment',
'missing_credit',
'missing_charge',
'missing_reversal',
'leave_unchanged'
) NOT NULL,
correction_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
evidence_reference VARCHAR(255) NOT NULL,
approved_by INT UNSIGNED NOT NULL,
approved_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
notes TEXT,
PRIMARY KEY (payment_id)
) ENGINE=InnoDB;
```
Rules:
- `balance_is_wrong`: correct the balance at that row, then recalculate all later balances for the invoice.
- `missing_payment`: insert a real payment using receipt evidence, then resequence.
- `missing_credit`: insert an approved credit adjustment.
- `missing_charge`: insert an approved charge adjustment.
- `missing_reversal`: insert or link a reversal.
- `leave_unchanged`: permitted only with written justification.
### 6.4 Preserve migrated opening paid balances
The dump proves that some invoices were already partially or fully paid before their first surviving `payments` row. Do not invent cash, card, or check transactions to fill that gap. Preserve the migrated amount separately with approval evidence.
```sql
CREATE TABLE invoice_opening_paid_balances (
invoice_id INT UNSIGNED NOT NULL,
amount DECIMAL(10,2) NOT NULL,
effective_before_payment_id INT UNSIGNED DEFAULT NULL,
school_year VARCHAR(9) NOT NULL,
evidence_reference VARCHAR(255) NOT NULL,
approved_by INT UNSIGNED NOT NULL,
approved_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
notes TEXT,
PRIMARY KEY (invoice_id),
CONSTRAINT fk_opening_paid_invoice
FOREIGN KEY (invoice_id) REFERENCES invoices(id),
CONSTRAINT chk_opening_paid_amount
CHECK (amount >= 0)
) ENGINE=InnoDB;
```
The internally consistent candidates are produced by:
```sql
SELECT
invoice_id,
minimum_implied_opening_paid AS candidate_opening_paid
FROM payment_repair_invoice_review
WHERE is_internally_consistent = 1
AND minimum_implied_opening_paid > 0.01
ORDER BY invoice_id;
```
The supplied dump produces 33 candidates. Each amount still requires approval because internal consistency proves only that the arithmetic repeats consistently, not that the original transaction actually occurred.
### 6.5 Correct a bad balance and recalculate forward
For an approved bad-balance decision, calculate the corrected row from the previous row:
```sql
UPDATE payments current_payment
JOIN payment_repair_transition_review review
ON review.payment_id = current_payment.id
JOIN payment_repair_decisions decision
ON decision.payment_id = current_payment.id
SET current_payment.balance = review.expected_balance
WHERE decision.decision = 'balance_is_wrong';
```
Then rebuild later balances for that invoice in chronological order. The companion script creates a staged result table first, allowing review before the final update.
Do not attempt this with an unordered multi-row variable update. SQL does not owe anyone deterministic behavior merely because the rows looked sorted in phpMyAdmin.
### 6.6 Rebuild invoice summaries
After payment, opening-balance, and adjustment reconciliation:
```sql
UPDATE invoices i
LEFT JOIN (
SELECT
invoice_id,
SUM(CASE
WHEN status = 'recorded' AND voided_at IS NULL
THEN paid_amount
ELSE 0
END) AS ledger_paid
FROM payments
GROUP BY invoice_id
) p ON p.invoice_id = i.id
LEFT JOIN invoice_opening_paid_balances o
ON o.invoice_id = i.id
SET i.paid_amount =
COALESCE(o.amount, 0) + COALESCE(p.ledger_paid, 0),
i.balance =
i.total_amount
- (COALESCE(o.amount, 0) + COALESCE(p.ledger_paid, 0)),
i.status = CASE
WHEN i.total_amount
- (COALESCE(o.amount, 0) + COALESCE(p.ledger_paid, 0)) = 0
THEN 'paid'
WHEN COALESCE(o.amount, 0) + COALESCE(p.ledger_paid, 0) = 0
THEN 'unpaid'
WHEN i.total_amount
- (COALESCE(o.amount, 0) + COALESCE(p.ledger_paid, 0)) > 0
THEN 'partial'
ELSE 'credit'
END,
i.updated_at = CURRENT_TIMESTAMP;
```
Discounts, waivers, and invoice credits must change `invoices.total_amount` through the adjustment ledger. They must not be counted as money paid. Opening paid balances are included only because they represent approved historical payments that predate the surviving ledger.
---
## 7. Schema hardening after cleanup
Apply only after data passes validation.
```sql
ALTER TABLE payments
MODIFY transaction_id VARCHAR(100) NOT NULL,
MODIFY installment_seq INT NOT NULL,
MODIFY school_year VARCHAR(9) NOT NULL,
ADD UNIQUE KEY uniq_payments_invoice_sequence (invoice_id, installment_seq),
ADD CONSTRAINT chk_payments_paid_amount CHECK (paid_amount > 0),
ADD CONSTRAINT chk_payments_method CHECK (
payment_method IN ('cash', 'card', 'check', 'bank_transfer', 'online')
),
ADD CONSTRAINT chk_payments_check_fields CHECK (
payment_method <> 'check'
OR check_number IS NOT NULL
);
```
Recommended foreign key after confirming every reference is valid:
```sql
ALTER TABLE payments
ADD CONSTRAINT fk_payments_invoice
FOREIGN KEY (invoice_id) REFERENCES invoices(id);
```
Do not add a parent foreign key until the signed/unsigned types of both columns match.
---
## 8. Validation checklist
The repair is complete only when all checks pass.
### Row and amount preservation
```sql
SELECT
(SELECT COUNT(*) FROM payments_backup_20260718) AS before_rows,
(SELECT COUNT(*) FROM payments) AS after_rows,
(SELECT SUM(paid_amount) FROM payments_backup_20260718) AS before_paid,
(SELECT SUM(paid_amount) FROM payments) AS after_paid;
```
Any difference must be explained by approved inserted reversals, recovered payments, or documented corrections.
### Duplicate transaction IDs
```sql
SELECT transaction_id, COUNT(*)
FROM payments
GROUP BY transaction_id
HAVING transaction_id IS NULL OR COUNT(*) > 1;
```
### Duplicate or missing installment sequences
```sql
SELECT invoice_id, installment_seq, COUNT(*)
FROM payments
GROUP BY invoice_id, installment_seq
HAVING installment_seq IS NULL OR COUNT(*) > 1;
```
### Payment-to-invoice ownership mismatch
```sql
SELECT p.id, p.invoice_id, p.parent_id, i.parent_id
FROM payments p
JOIN invoices i ON i.id = p.invoice_id
WHERE p.parent_id <> i.parent_id;
```
### Invoice summary mismatch
```sql
SELECT
i.id AS invoice_id,
i.total_amount,
i.paid_amount,
i.balance,
SUM(CASE WHEN p.status = 'recorded' THEN p.paid_amount ELSE 0 END) AS ledger_paid
FROM invoices i
LEFT JOIN payments p ON p.invoice_id = i.id
GROUP BY i.id, i.total_amount, i.paid_amount, i.balance
HAVING ABS(i.balance - (i.total_amount - i.paid_amount)) > 0.01;
```
### Remaining unexplained transitions
Recreate `payment_repair_transition_review` after corrections. It should return no unexplained transition unless the difference is linked to an explicit adjustment, credit, reversal, or approved migration record.
---
## 9. Deployment order
1. Add new nullable columns and adjustment/reversal structures.
2. Deploy application code that dual-writes the old and new installment fields.
3. Freeze payment writes.
4. Backup and run audit scripts.
5. Reconcile the four anomalous invoices and all opening paid balances.
6. Apply data repair.
7. Rebuild invoice summaries.
8. Run validations and business-report comparisons.
9. Deploy application code that reads only the corrected fields.
10. Add NOT NULL, UNIQUE, CHECK, and foreign-key constraints.
11. Reopen payment writes.
12. Monitor duplicate attempts, negative balances, and reconciliation failures.
---
## 10. Required tests
At minimum, automated tests must cover:
- Full payment.
- Partial payment.
- Final installment.
- Concurrent payments on the same invoice.
- Duplicate browser submission.
- Check payment without a check number.
- Payment greater than balance.
- Invoice charge after partial payment.
- Invoice discount after payment.
- Payment reversal.
- School-year mismatch.
- Invoice transferred to another parent or corrected ownership.
- Repeated event-fee processing with the same source reference.
- Status transitions from unpaid to partial to paid.
The concurrency test is non-negotiable. Without it, two perfectly valid requests can both read the same balance and produce one invalid ledger, because computers are extremely obedient even when asked to race into a wall.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
# School Year and Semester Schema Audit
Source: `scool_view2.sql`
Tables found: 73
## Result
The schema is not fully fixed.
## Required financial tables
Confirmed with `school_year`:
- payment_notification_logs
- payment_transactions
- reimbursement_batch_items
Absent from this dump and therefore not verifiable:
- discount_usages
- event_charges
- invoice_event
- payment_error
## School-year defects
- `archived_paypal_transactions.school_year` is `VARCHAR(20)` instead of `VARCHAR(9)`.
- Tables with `school_year` but no direct school-year index:
- archived_paypal_transactions
- class_progress_reports
- exams
- missing_score_overrides
- payments
- placement_batches
- print_requests
- report_card_acknowledgements
- semester_scores
- staff_attendance
- teacher_attendance_data
- whatsapp_group_links
## Tables where `semester` should be removed
- badge_print_logs
- classes
- contactus
- emergency_contacts
- inventory_items
- invoice_students_list
- ip_attempts
- notification_recipients
- notifications
- parents
- support_requests
- user_notifications
- whatsapp_group_links
## Tables requiring business-rule confirmation before removing `semester`
- archived_paypal_transactions
- discount_vouchers
- payment_transactions
- reimbursement_batch_items
- scan_log
## Notes
`semester` should remain on semester-owned academic and operational records such as exams, quizzes, grades, class sections, attendance, semester reports, and semester-specific charges or payments.
Child tables should not duplicate `semester` when it can be obtained reliably through their parent record, unless immutable historical snapshots are an explicit requirement.