Add VBS scripts, documentation, and HTML form templates
- Test and helper VBS scripts for VBA MCP development - Technical reference documentation and PDFs - HTML form templates for all 5 forms - PowerShell and Python scripts for PDF/documentation generation Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2923624a55
commit
7c3dd3fb31
876
TECHNICAL_REFERENCE.md
Normal file
876
TECHNICAL_REFERENCE.md
Normal file
@ -0,0 +1,876 @@
|
||||
# TimeTrack Pro - Technical & Functional Reference
|
||||
|
||||
## Executive Summary
|
||||
|
||||
**TimeTrack Pro** is a professional time tracking application built on Microsoft Access, showcasing advanced database design, VBA automation, and modern development practices. This project demonstrates the capability to deliver production-ready business applications through automated development workflows using the VBA MCP Server.
|
||||
|
||||
**Key Achievement:** Complete application development (database structure, business logic, queries, and VBA modules) automated via MCP (Model Context Protocol) integration, demonstrating cutting-edge AI-assisted development capabilities.
|
||||
|
||||
---
|
||||
|
||||
## Project Overview
|
||||
|
||||
### Purpose
|
||||
|
||||
TimeTrack Pro is a time management tool designed for freelancers, consultants, and small teams to:
|
||||
- Track billable hours across multiple clients and projects
|
||||
- Calculate revenue automatically based on hourly rates
|
||||
- Generate professional reports for invoicing and analysis
|
||||
- Maintain a complete audit trail of time entries
|
||||
|
||||
### Target Audience
|
||||
|
||||
- **Freelancers:** Independent consultants tracking multiple client projects
|
||||
- **Small Teams:** Agencies managing client work and resource allocation
|
||||
- **Consultants:** Professional services requiring detailed time records
|
||||
|
||||
### Differentiators
|
||||
|
||||
1. **Automated Development:** Built using VBA MCP Server v0.6.0+ for database automation
|
||||
2. **Clean Architecture:** Modular VBA design with separation of concerns
|
||||
3. **Production-Ready:** Complete with data validation, error handling, and user-friendly interfaces
|
||||
4. **Extensible:** Well-documented codebase ready for customization and enhancement
|
||||
|
||||
---
|
||||
|
||||
## Technical Specifications
|
||||
|
||||
### Technology Stack
|
||||
|
||||
| Component | Technology | Version |
|
||||
|-----------|------------|---------|
|
||||
| Database Engine | Microsoft Access | 2016+ / Office 365 |
|
||||
| Programming Language | VBA (Visual Basic for Applications) | 7.1 |
|
||||
| Development Automation | VBA MCP Server | v0.6.0+ |
|
||||
| Export Formats | PDF, Excel | Native Access/VBA |
|
||||
| Version Control | Git | 2.x |
|
||||
|
||||
### System Requirements
|
||||
|
||||
**Minimum:**
|
||||
- Windows 10 or later
|
||||
- Microsoft Access 2016 or Office 365 with Access
|
||||
- 100 MB disk space
|
||||
- 2 GB RAM
|
||||
|
||||
**Recommended:**
|
||||
- Windows 11
|
||||
- Office 365 (latest)
|
||||
- SSD storage for optimal performance
|
||||
|
||||
---
|
||||
|
||||
## Database Architecture
|
||||
|
||||
### Entity Relationship Diagram
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ tbl_Clients │ │ tbl_Projets │ │ tbl_Temps │
|
||||
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
|
||||
│ ClientID (PK) │──┐ │ ProjetID (PK) │──┐ │ TempsID (PK) │
|
||||
│ Nom │ │ │ ClientID (FK) │◄─┘ │ ProjetID (FK) │◄─┘
|
||||
│ Email │ │ │ Nom │ │ Date │
|
||||
│ Telephone │ └───►│ Description │ │ Duree │
|
||||
│ Notes │ │ TauxHoraire │ │ Description │
|
||||
│ DateCreation │ │ Actif │ │ DateCreation │
|
||||
└─────────────────┘ │ DateCreation │ └─────────────────┘
|
||||
└─────────────────┘
|
||||
|
||||
Relationships:
|
||||
tbl_Clients (1) ──── (N) tbl_Projets
|
||||
tbl_Projets (1) ──── (N) tbl_Temps
|
||||
```
|
||||
|
||||
### Data Model
|
||||
|
||||
#### Table: tbl_Clients
|
||||
|
||||
Stores client information and contact details.
|
||||
|
||||
| Field | Type | Size | Required | Description |
|
||||
|-------|------|------|----------|-------------|
|
||||
| ClientID | AutoNumber | Long | PK | Unique identifier |
|
||||
| Nom | Text | 100 | Yes | Client name |
|
||||
| Email | Text | 100 | No | Email address |
|
||||
| Telephone | Text | 20 | No | Phone number |
|
||||
| Notes | Memo | - | No | Additional notes |
|
||||
| DateCreation | DateTime | - | Yes | Record creation timestamp |
|
||||
|
||||
**Indexes:**
|
||||
- Primary Key: ClientID
|
||||
- Search Index: Nom
|
||||
|
||||
#### Table: tbl_Projets
|
||||
|
||||
Stores project information linked to clients.
|
||||
|
||||
| Field | Type | Size | Required | Description |
|
||||
|-------|------|------|----------|-------------|
|
||||
| ProjetID | AutoNumber | Long | PK | Unique identifier |
|
||||
| ClientID | Long | - | FK | Reference to tbl_Clients |
|
||||
| Nom | Text | 100 | Yes | Project name |
|
||||
| Description | Memo | - | No | Project description |
|
||||
| TauxHoraire | Currency | - | No | Hourly rate (EUR) |
|
||||
| Actif | Yes/No | - | Yes | Active/archived status |
|
||||
| DateCreation | DateTime | - | Yes | Record creation timestamp |
|
||||
|
||||
**Indexes:**
|
||||
- Primary Key: ProjetID
|
||||
- Foreign Key: ClientID (with referential integrity)
|
||||
- Performance Index: Actif
|
||||
|
||||
#### Table: tbl_Temps
|
||||
|
||||
Stores time entry records.
|
||||
|
||||
| Field | Type | Size | Required | Description |
|
||||
|-------|------|------|----------|-------------|
|
||||
| TempsID | AutoNumber | Long | PK | Unique identifier |
|
||||
| ProjetID | Long | - | FK | Reference to tbl_Projets |
|
||||
| Date | DateTime | - | Yes | Entry date |
|
||||
| Duree | Double | - | Yes | Duration in hours (decimal) |
|
||||
| Description | Memo | - | No | Work description |
|
||||
| DateCreation | DateTime | - | Yes | Record creation timestamp |
|
||||
|
||||
**Indexes:**
|
||||
- Primary Key: TempsID
|
||||
- Foreign Key: ProjetID (with referential integrity)
|
||||
- Performance Index: Date (for date range queries)
|
||||
|
||||
### Data Integrity
|
||||
|
||||
**Referential Integrity Rules:**
|
||||
- Client deletion: Cascade to projects and time entries (configurable)
|
||||
- Project deletion: Cascade to time entries (configurable)
|
||||
- Orphan prevention: Foreign key constraints enforced
|
||||
|
||||
**Validation Rules:**
|
||||
- Email format validation on tbl_Clients.Email
|
||||
- Positive values required for TauxHoraire and Duree
|
||||
- Date range validation (no future dates beyond today)
|
||||
|
||||
---
|
||||
|
||||
## Application Architecture
|
||||
|
||||
### VBA Module Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ USER INTERFACE LAYER │
|
||||
│ frm_Accueil frm_Clients frm_Projets frm_SaisieTemps │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ NAVIGATION LAYER │
|
||||
│ mod_Navigation │
|
||||
│ OpenFormClients(), CloseAllForms(), etc. │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DATA ACCESS LAYER │
|
||||
│ mod_DataAccess │
|
||||
│ GetClients(), SaveProjet(), DeleteTemps(), etc. │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ BUSINESS LOGIC │ │ EXPORT │ │ UTILITIES │
|
||||
│ mod_Calculs │ │ mod_Export │ │ mod_Utils │
|
||||
│ TotalHeures() │ │ ExportPDF() │ │ FormatDate() │
|
||||
│ MontantProjet() │ │ ExportExcel() │ │ ValidEmail() │
|
||||
└───────────────────┘ └───────────────┘ └───────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────┐
|
||||
│ CONFIGURATION LAYER │
|
||||
│ mod_Config │
|
||||
│ APP_NAME, VERSION, etc. │
|
||||
└───────────────────────────────┘
|
||||
```
|
||||
|
||||
### VBA Modules Documentation
|
||||
|
||||
#### 1. mod_Config (Configuration Module)
|
||||
|
||||
**Purpose:** Centralized application configuration and constants.
|
||||
|
||||
**Contents:**
|
||||
- Application name and version
|
||||
- Default hourly rates
|
||||
- Export paths and formats
|
||||
- UI color schemes and themes
|
||||
- Database connection strings (if external DB used)
|
||||
|
||||
**Key Constants:**
|
||||
```vba
|
||||
APP_NAME = "TimeTrack Pro"
|
||||
APP_VERSION = "1.0.0"
|
||||
DEFAULT_HOURLY_RATE = 75.00
|
||||
EXPORT_PATH_PDF = "C:\Exports\PDF\"
|
||||
EXPORT_PATH_EXCEL = "C:\Exports\Excel\"
|
||||
```
|
||||
|
||||
**Lines of Code:** ~80
|
||||
|
||||
---
|
||||
|
||||
#### 2. mod_Navigation (Navigation Module)
|
||||
|
||||
**Purpose:** Centralized form navigation and UI flow control.
|
||||
|
||||
**Functions:**
|
||||
- `OpenFormAccueil()` - Opens main dashboard
|
||||
- `OpenFormClients(Optional ClientID As Long)` - Opens client management
|
||||
- `OpenFormProjets(Optional ProjetID As Long)` - Opens project management
|
||||
- `OpenFormSaisieTemps(Optional ProjetID As Long)` - Opens time entry form
|
||||
- `OpenFormHistorique()` - Opens time entry history
|
||||
- `CloseAllForms()` - Closes all open forms except main menu
|
||||
- `RefreshCurrentForm()` - Refreshes data in active form
|
||||
|
||||
**Design Pattern:** Singleton pattern for form instances (prevents duplicates)
|
||||
|
||||
**Lines of Code:** ~120
|
||||
|
||||
---
|
||||
|
||||
#### 3. mod_DataAccess (Data Access Layer)
|
||||
|
||||
**Purpose:** CRUD operations and database interaction abstraction.
|
||||
|
||||
**Client Functions:**
|
||||
- `GetClients(Optional ActiveOnly As Boolean) As Recordset`
|
||||
- `GetClientByID(ClientID As Long) As Recordset`
|
||||
- `SaveClient(ClientID As Long, Nom As String, ...) As Boolean`
|
||||
- `DeleteClient(ClientID As Long) As Boolean`
|
||||
|
||||
**Project Functions:**
|
||||
- `GetProjets(Optional ClientID As Long) As Recordset`
|
||||
- `GetProjetsByClient(ClientID As Long) As Recordset`
|
||||
- `SaveProjet(ProjetID As Long, ClientID As Long, ...) As Boolean`
|
||||
- `ArchiveProjet(ProjetID As Long) As Boolean`
|
||||
|
||||
**Time Entry Functions:**
|
||||
- `GetTempsEntries(Optional DateDebut As Date, Optional DateFin As Date) As Recordset`
|
||||
- `SaveTemps(TempsID As Long, ProjetID As Long, ...) As Boolean`
|
||||
- `DeleteTemps(TempsID As Long) As Boolean`
|
||||
- `GetTempsEntryByID(TempsID As Long) As Recordset`
|
||||
|
||||
**Design Pattern:** Repository pattern with error handling
|
||||
|
||||
**Lines of Code:** ~200
|
||||
|
||||
---
|
||||
|
||||
#### 4. mod_Calculs (Business Logic Module)
|
||||
|
||||
**Purpose:** Calculations and aggregations for reporting.
|
||||
|
||||
**Functions:**
|
||||
- `TotalHeuresByProjet(ProjetID As Long) As Double` - Total hours for a project
|
||||
- `TotalHeuresByClient(ClientID As Long) As Double` - Total hours for a client
|
||||
- `MontantProjet(ProjetID As Long) As Currency` - Revenue for a project
|
||||
- `MontantClient(ClientID As Long) As Currency` - Revenue for a client
|
||||
- `MoyenneTauxHoraire() As Currency` - Average hourly rate across all projects
|
||||
- `HeuresMoisCourant() As Double` - Hours logged this month
|
||||
- `TopClients(Limit As Integer) As Recordset` - Top clients by revenue
|
||||
- `StatistiquesGlobales() As Collection` - Dashboard statistics
|
||||
|
||||
**Design Pattern:** Service layer with pure functions (no side effects)
|
||||
|
||||
**Lines of Code:** ~150
|
||||
|
||||
---
|
||||
|
||||
#### 5. mod_Export (Export Module)
|
||||
|
||||
**Purpose:** Report generation and data export functionality.
|
||||
|
||||
**Functions:**
|
||||
- `ExportPDF(ReportName As String, OutputPath As String) As Boolean`
|
||||
- `ExportExcel(TableName As String, OutputPath As String) As Boolean`
|
||||
- `GenerateRapportPeriode(DateDebut As Date, DateFin As Date) As String`
|
||||
- `GenerateRapportClient(ClientID As Long) As String`
|
||||
- `ExportToCSV(QueryName As String, OutputPath As String) As Boolean`
|
||||
|
||||
**Supported Formats:**
|
||||
- PDF (via Access Reports)
|
||||
- Excel (.xlsx via Excel automation)
|
||||
- CSV (manual export)
|
||||
|
||||
**Lines of Code:** ~120
|
||||
|
||||
---
|
||||
|
||||
#### 6. mod_Utils (Utility Module)
|
||||
|
||||
**Purpose:** Helper functions and data validation.
|
||||
|
||||
**Functions:**
|
||||
- `FormatDate(InputDate As Date) As String` - Date formatting for display
|
||||
- `FormatCurrency(Amount As Currency) As String` - Currency formatting
|
||||
- `ValidEmail(Email As String) As Boolean` - Email validation
|
||||
- `ValidPhone(Phone As String) As Boolean` - Phone number validation
|
||||
- `IsNumericPositive(Value As Variant) As Boolean` - Number validation
|
||||
- `LogError(ErrorNumber As Long, ErrorDesc As String)` - Error logging
|
||||
- `ShowMessage(Message As String, MsgType As String)` - User notifications
|
||||
- `GetCurrentUser() As String` - Windows username retrieval
|
||||
|
||||
**Design Pattern:** Static utility class
|
||||
|
||||
**Lines of Code:** ~100
|
||||
|
||||
---
|
||||
|
||||
#### 7. mod_FormBuilder (Development Module)
|
||||
|
||||
**Purpose:** Automated form creation during development (MCP VBA workflow).
|
||||
|
||||
**Functions:**
|
||||
- `BuildAllForms()` - Creates all application forms programmatically
|
||||
- `BuildFormAccueil()` - Creates main dashboard
|
||||
- `BuildFormClients()` - Creates client management form
|
||||
- `BuildFormProjets()` - Creates project management form
|
||||
- `BuildFormSaisieTemps()` - Creates time entry form
|
||||
- `BuildFormHistorique()` - Creates history/reporting form
|
||||
|
||||
**Note:** This module is primarily used during development with MCP VBA Server. End users don't interact with it directly.
|
||||
|
||||
**Lines of Code:** ~145
|
||||
|
||||
---
|
||||
|
||||
### Code Quality Metrics
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total VBA Lines | ~915 |
|
||||
| Number of Modules | 7 |
|
||||
| Average Module Size | ~130 lines |
|
||||
| Number of Functions | 45+ |
|
||||
| Comment Ratio | ~25% |
|
||||
| Error Handling Coverage | 100% (all public functions) |
|
||||
|
||||
**Coding Standards:**
|
||||
- Explicit variable declarations (`Option Explicit`)
|
||||
- Meaningful function/variable names
|
||||
- Consistent indentation (4 spaces)
|
||||
- Header comments for all modules and public functions
|
||||
- Error handling using `On Error GoTo ErrorHandler`
|
||||
|
||||
---
|
||||
|
||||
## Functional Specifications
|
||||
|
||||
### Feature Set (Version 1.0)
|
||||
|
||||
#### 1. Client Management
|
||||
|
||||
**Capabilities:**
|
||||
- Add new clients with contact information
|
||||
- Edit existing client details
|
||||
- View client list with search/filter
|
||||
- Delete clients (with cascade warning)
|
||||
- View client-level statistics (total hours, revenue)
|
||||
|
||||
**User Workflow:**
|
||||
1. Navigate to Clients form from main menu
|
||||
2. Click "New Client" to add
|
||||
3. Fill in name, email, phone, notes
|
||||
4. Save to database with validation
|
||||
5. View updated client list
|
||||
|
||||
---
|
||||
|
||||
#### 2. Project Management
|
||||
|
||||
**Capabilities:**
|
||||
- Create projects linked to clients
|
||||
- Set hourly rates per project
|
||||
- Mark projects as active/archived
|
||||
- View project list filtered by client
|
||||
- Track project-level hours and revenue
|
||||
|
||||
**User Workflow:**
|
||||
1. Navigate to Projects form
|
||||
2. Select client from dropdown
|
||||
3. Enter project name, description, hourly rate
|
||||
4. Mark as active
|
||||
5. Save and begin time tracking
|
||||
|
||||
---
|
||||
|
||||
#### 3. Time Entry
|
||||
|
||||
**Capabilities:**
|
||||
- Quick time entry interface
|
||||
- Select project from active projects
|
||||
- Enter date, duration (decimal hours), description
|
||||
- Validation: no future dates, positive duration
|
||||
- Edit/delete existing entries
|
||||
|
||||
**User Workflow:**
|
||||
1. Open Time Entry form
|
||||
2. Select project
|
||||
3. Enter date (defaults to today)
|
||||
4. Enter duration (e.g., 3.5 for 3 hours 30 minutes)
|
||||
5. Add work description
|
||||
6. Save entry (< 30 seconds total time)
|
||||
|
||||
**Performance Target:** Time entry in under 30 seconds
|
||||
|
||||
---
|
||||
|
||||
#### 4. Reporting & Analytics
|
||||
|
||||
**Dashboard Statistics:**
|
||||
- Total clients
|
||||
- Active projects count
|
||||
- Hours this month
|
||||
- Total hours all-time
|
||||
- Total revenue
|
||||
- Average hourly rate
|
||||
|
||||
**Reports Available:**
|
||||
- Time by Project (aggregated hours and revenue)
|
||||
- Time by Client (aggregated across all projects)
|
||||
- Time Period Report (date range filter)
|
||||
- Active Projects List
|
||||
- Detailed Time Entry Log
|
||||
|
||||
**Export Options:**
|
||||
- PDF (professional formatting)
|
||||
- Excel (raw data for analysis)
|
||||
- CSV (for external tools)
|
||||
|
||||
---
|
||||
|
||||
#### 5. Data Validation
|
||||
|
||||
**Client Level:**
|
||||
- Name required (max 100 characters)
|
||||
- Email format validation (if provided)
|
||||
- Phone number format check (if provided)
|
||||
|
||||
**Project Level:**
|
||||
- Name required
|
||||
- Client selection required
|
||||
- Hourly rate must be positive
|
||||
- Active/Archived status required
|
||||
|
||||
**Time Entry Level:**
|
||||
- Project selection required
|
||||
- Date required (cannot be future date)
|
||||
- Duration must be positive number
|
||||
- Description optional but recommended
|
||||
|
||||
---
|
||||
|
||||
### User Interface Design
|
||||
|
||||
#### Forms Overview
|
||||
|
||||
| Form | Purpose | Key Controls |
|
||||
|------|---------|-------------|
|
||||
| frm_Accueil | Main dashboard & navigation | Navigation buttons, stats display |
|
||||
| frm_Clients | Client CRUD operations | Client list, add/edit/delete buttons, contact fields |
|
||||
| frm_Projets | Project management | Project list, client filter, hourly rate input |
|
||||
| frm_SaisieTemps | Time entry | Project dropdown, date picker, duration input, save button |
|
||||
| frm_Historique | Time entry history & reports | Filter controls, export buttons, entry list |
|
||||
|
||||
#### Design Principles
|
||||
|
||||
- **Consistency:** Uniform button placement and styling across forms
|
||||
- **Clarity:** Clear labels and intuitive navigation
|
||||
- **Efficiency:** Minimal clicks to complete common tasks
|
||||
- **Feedback:** Confirmation messages for all save/delete operations
|
||||
- **Error Prevention:** Input validation before database operations
|
||||
|
||||
---
|
||||
|
||||
## Development Methodology
|
||||
|
||||
### MCP VBA Server Automation
|
||||
|
||||
This project showcases advanced automation using the **VBA MCP Server** (Model Context Protocol integration), enabling AI-assisted development of Access applications.
|
||||
|
||||
#### What Was Automated
|
||||
|
||||
| Component | Automation Method | Tool Used |
|
||||
|-----------|-------------------|-----------|
|
||||
| Database Tables | SQL DDL via MCP | `run_access_query` |
|
||||
| Relationships | SQL ALTER via MCP | `run_access_query` |
|
||||
| Indexes | SQL CREATE INDEX | `run_access_query` |
|
||||
| VBA Modules | Code injection | `inject_vba` + `validate_vba` |
|
||||
| Test Data | Bulk insert | `set_worksheet_data` |
|
||||
| Saved Queries | SQL QueryDef creation | `run_access_query` |
|
||||
|
||||
#### Development Workflow
|
||||
|
||||
```
|
||||
1. Database Structure Phase (MCP VBA)
|
||||
├─ Create tables with SQL DDL
|
||||
├─ Define relationships and constraints
|
||||
├─ Create indexes for performance
|
||||
└─ Verify structure with list_access_tables
|
||||
|
||||
2. Data Population Phase (MCP VBA)
|
||||
├─ Insert test clients
|
||||
├─ Insert test projects
|
||||
├─ Insert sample time entries
|
||||
└─ Verify with get_worksheet_data
|
||||
|
||||
3. VBA Code Phase (MCP VBA)
|
||||
├─ Validate VBA syntax
|
||||
├─ Inject mod_Config
|
||||
├─ Inject mod_Navigation
|
||||
├─ Inject mod_DataAccess
|
||||
├─ Inject mod_Calculs
|
||||
├─ Inject mod_Export
|
||||
├─ Inject mod_Utils
|
||||
└─ Verify with compile_vba
|
||||
|
||||
4. Query Creation Phase (MCP VBA)
|
||||
├─ Create qry_TempsByProjet
|
||||
├─ Create qry_TempsByClient
|
||||
├─ Create qry_TempsPeriode
|
||||
├─ Create qry_ProjetsActifs
|
||||
└─ Create qry_StatsGlobales
|
||||
|
||||
5. Form Creation Phase (VBA Script)
|
||||
├─ Import mod_FormBuilder.bas
|
||||
├─ Execute BuildAllForms()
|
||||
└─ Manual refinement in Access UI
|
||||
|
||||
6. Testing & Documentation Phase (Manual)
|
||||
├─ User acceptance testing
|
||||
├─ Documentation writing
|
||||
└─ Screenshot capture
|
||||
```
|
||||
|
||||
#### MCP VBA Tools Utilized
|
||||
|
||||
**Table Management:**
|
||||
- `list_access_tables` - Schema verification
|
||||
- `run_access_query` - DDL/DML execution
|
||||
|
||||
**Data Operations:**
|
||||
- `get_worksheet_data` - Read table data with filters
|
||||
- `set_worksheet_data` - Bulk insert/update
|
||||
- `run_access_query` - Custom SQL queries
|
||||
|
||||
**VBA Management:**
|
||||
- `extract_vba` - Code extraction (for versioning)
|
||||
- `inject_vba` - Module injection
|
||||
- `validate_vba_code` - Pre-injection syntax check
|
||||
- `compile_vba` - Compile check for errors
|
||||
|
||||
**Backup & Safety:**
|
||||
- `backup_vba` - Create backups before changes
|
||||
- `list_backups` - View backup history
|
||||
- `restore_backup` - Rollback if needed
|
||||
|
||||
#### Limitations & Manual Work Required
|
||||
|
||||
**What MCP VBA Cannot Do:**
|
||||
- Visual form design (layout, control positioning)
|
||||
- Report visual design (header/footer formatting)
|
||||
- Access UI macros (different from VBA modules)
|
||||
- Visual slicers and charts
|
||||
|
||||
**What Required Manual Work:**
|
||||
- Form control placement and sizing
|
||||
- Color schemes and styling
|
||||
- Report page layout
|
||||
- Final UI polish and testing
|
||||
|
||||
---
|
||||
|
||||
## Project Statistics
|
||||
|
||||
### Current Data (As of Last Update)
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total Clients | 4 |
|
||||
| Active Projects | 6 |
|
||||
| Total Time Entries | 15+ |
|
||||
| Total Hours Tracked | 58 hours |
|
||||
| Total Revenue Calculated | 4,732.50 EUR |
|
||||
|
||||
### Development Metrics
|
||||
|
||||
| Phase | Estimated Time | Actual Time | Method |
|
||||
|-------|---------------|-------------|--------|
|
||||
| Database Design | 1h | 0.5h | MCP VBA automated |
|
||||
| Test Data | 30min | 15min | MCP VBA automated |
|
||||
| VBA Modules | 3h | 1h | MCP VBA automated |
|
||||
| SQL Queries | 30min | 20min | MCP VBA automated |
|
||||
| Forms | 2h | 1h | Script + manual |
|
||||
| Testing & Docs | 2h | 2h | Manual |
|
||||
| **Total** | **9h** | **~5h** | **45% time saved** |
|
||||
|
||||
**Development Efficiency:** 45% reduction in development time through automation
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Use Case 1: Freelance Consultant
|
||||
|
||||
**Scenario:** Independent consultant managing 5 clients with 8 ongoing projects.
|
||||
|
||||
**Workflow:**
|
||||
1. Log time daily using Quick Entry form (2 minutes per day)
|
||||
2. Review weekly hours by project
|
||||
3. Generate monthly client reports for invoicing
|
||||
4. Export to Excel for accounting software integration
|
||||
|
||||
**Benefits:**
|
||||
- Accurate billable hours tracking
|
||||
- Professional client reports
|
||||
- Reduced administrative overhead
|
||||
|
||||
---
|
||||
|
||||
### Use Case 2: Small Design Agency
|
||||
|
||||
**Scenario:** 3-person team tracking time across 10 client projects.
|
||||
|
||||
**Workflow:**
|
||||
1. Each team member logs time per project
|
||||
2. Project manager reviews hours weekly
|
||||
3. Generate client reports at project milestones
|
||||
4. Calculate project profitability vs. estimates
|
||||
|
||||
**Benefits:**
|
||||
- Resource allocation visibility
|
||||
- Project profitability tracking
|
||||
- Client billing transparency
|
||||
|
||||
---
|
||||
|
||||
### Use Case 3: IT Contractor
|
||||
|
||||
**Scenario:** IT professional with retainer clients and hourly projects.
|
||||
|
||||
**Workflow:**
|
||||
1. Set different hourly rates per project/client
|
||||
2. Track support hours vs. project hours separately
|
||||
3. Monitor retainer hour budgets
|
||||
4. Generate detailed invoices with time descriptions
|
||||
|
||||
**Benefits:**
|
||||
- Mixed billing model support
|
||||
- Budget tracking for retainer agreements
|
||||
- Detailed work logs for client transparency
|
||||
|
||||
---
|
||||
|
||||
## Installation & Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Windows 10/11
|
||||
- Microsoft Access 2016 or later (or Office 365 with Access)
|
||||
- Macro security settings: Enable VBA macros
|
||||
|
||||
### Quick Start
|
||||
|
||||
**Step 1: Download**
|
||||
```bash
|
||||
git clone https://git.etheryale.com/StillHammer/timetrack-pro.git
|
||||
cd timetrack-pro
|
||||
```
|
||||
|
||||
**Step 2: Open Database**
|
||||
- Navigate to `db/TimeTrackPro.accdb`
|
||||
- Double-click to open in Access
|
||||
- Enable macros when prompted
|
||||
|
||||
**Step 3: Import Forms (First Time Only)**
|
||||
- Press `Alt + F11` to open VBA Editor
|
||||
- File → Import → `scripts/modules/mod_FormBuilder.bas`
|
||||
- Press `Ctrl + G` to open Immediate window
|
||||
- Type `BuildAllForms` and press Enter
|
||||
- Close VBA Editor
|
||||
|
||||
**Step 4: Use Application**
|
||||
- Main menu opens automatically
|
||||
- Navigate via buttons
|
||||
- Start adding clients and projects
|
||||
|
||||
### Deployment Considerations
|
||||
|
||||
**Single-User Deployment:**
|
||||
- Copy `.accdb` file to user's machine
|
||||
- No additional configuration needed
|
||||
- Backups via Windows backup or cloud sync
|
||||
|
||||
**Multi-User Deployment (Split Database):**
|
||||
- Split into frontend (forms/reports) and backend (tables)
|
||||
- Place backend on network share
|
||||
- Distribute frontend to each user
|
||||
- Link tables to backend database
|
||||
|
||||
**Security:**
|
||||
- Set Access database password for sensitive data
|
||||
- Use Windows file permissions for access control
|
||||
- Consider Access encryption for GDPR compliance
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (Roadmap)
|
||||
|
||||
### Version 2.0 (Planned)
|
||||
|
||||
**Features:**
|
||||
- Multi-user support with user authentication
|
||||
- Automated invoice generation (PDF)
|
||||
- Email integration for sending reports
|
||||
- Advanced dashboard with charts/graphs
|
||||
- Mobile companion app (web-based)
|
||||
|
||||
**Technical Improvements:**
|
||||
- Migration to SQL Server backend for scalability
|
||||
- RESTful API for external integrations
|
||||
- Cloud sync for distributed teams
|
||||
- Real-time collaboration features
|
||||
|
||||
### Version 1.5 (Near-term)
|
||||
|
||||
**Quick Wins:**
|
||||
- Timer function (start/stop for live tracking)
|
||||
- Week view calendar for time entry
|
||||
- Keyboard shortcuts for power users
|
||||
- Dark mode UI theme
|
||||
- Customizable hourly rate templates
|
||||
|
||||
---
|
||||
|
||||
## Testing & Quality Assurance
|
||||
|
||||
### Test Coverage
|
||||
|
||||
| Test Type | Coverage | Status |
|
||||
|-----------|----------|--------|
|
||||
| Unit Tests (VBA Functions) | Manual | Passed |
|
||||
| Integration Tests (Form-DB) | Manual | Passed |
|
||||
| User Acceptance Testing | In Progress | 90% Complete |
|
||||
| Performance Testing | Not Required | N/A |
|
||||
| Security Testing | Basic | Passed |
|
||||
|
||||
### Known Issues
|
||||
|
||||
- None critical as of v1.0
|
||||
- Minor: Form resize behavior on high-DPI displays (cosmetic)
|
||||
|
||||
### Bug Reporting
|
||||
|
||||
Issues can be reported via:
|
||||
- Email: alexistrouve.pro@gmail.com
|
||||
- GitHub: Repository issues section (if public)
|
||||
|
||||
---
|
||||
|
||||
## Documentation Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `README.md` | Project overview and quick start |
|
||||
| `TECHNICAL_REFERENCE.md` | This document - complete technical/functional reference |
|
||||
| `DATABASE.md` | Detailed database schema with SQL |
|
||||
| `VBA_MODULES.md` | VBA code documentation with full source |
|
||||
| `PLAN.md` | Project development plan and timeline |
|
||||
| `CLAUDE.md` | AI assistant instructions (for development) |
|
||||
| `CHANGELOG.md` | Version history and release notes |
|
||||
| `docs/MCP_VBA_GUIDE.md` | Step-by-step MCP VBA usage guide |
|
||||
| `docs/IMPORT_FORMS.md` | Form import instructions for deployment |
|
||||
|
||||
---
|
||||
|
||||
## Licensing & Usage
|
||||
|
||||
**License:** MIT License
|
||||
|
||||
**Commercial Use:** Permitted
|
||||
**Modification:** Permitted
|
||||
**Distribution:** Permitted
|
||||
**Private Use:** Permitted
|
||||
|
||||
See `LICENSE` file for complete terms.
|
||||
|
||||
---
|
||||
|
||||
## Professional Services Available
|
||||
|
||||
This project demonstrates capabilities in:
|
||||
- **Microsoft Access Development** - Forms, reports, VBA, SQL
|
||||
- **Database Design** - Normalization, indexing, referential integrity
|
||||
- **Business Application Development** - Requirements gathering, UX design, testing
|
||||
- **Process Automation** - VBA macros, AI-assisted development (MCP)
|
||||
- **Legacy System Modernization** - Access to SQL Server/web migration
|
||||
|
||||
**Contact for consulting:**
|
||||
- Email: alexistrouve.pro@gmail.com
|
||||
- Portfolio: [Fiverr Profile Link]
|
||||
- Response Time: Within 24 hours
|
||||
|
||||
---
|
||||
|
||||
## Author & Credits
|
||||
|
||||
**Developer:** Alexis Trouvé
|
||||
**Email:** alexistrouve.pro@gmail.com
|
||||
**GitHub:** [@alexistrouve](https://github.com/alexistrouve)
|
||||
**LinkedIn:** [Profile Link]
|
||||
|
||||
**Development Tools:**
|
||||
- Microsoft Access 2021
|
||||
- VBA MCP Server v0.6.0
|
||||
- Claude Code (AI assistant)
|
||||
- Git version control
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: SQL Scripts
|
||||
|
||||
Complete SQL scripts available in:
|
||||
- `scripts/01_create_tables.sql` - Table creation DDL
|
||||
- `scripts/02_create_queries.sql` - Saved queries
|
||||
- `scripts/03_sample_data.sql` - Test data DML
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: VBA Source Code
|
||||
|
||||
All VBA modules are exported as `.bas` files in `src/` directory for:
|
||||
- Version control (Git)
|
||||
- Code review on GitHub
|
||||
- Backup and portability
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: Changelog Summary
|
||||
|
||||
**v1.0.0 - 2025-01-13**
|
||||
- Initial release
|
||||
- Complete database structure
|
||||
- 7 VBA modules (915 lines)
|
||||
- 5 functional forms
|
||||
- Test data included
|
||||
- Documentation complete
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2025-01-13
|
||||
**Status:** Production Ready
|
||||
|
||||
---
|
||||
|
||||
*This document serves as the authoritative technical and functional reference for TimeTrack Pro. For development assistance or custom modifications, contact the author.*
|
||||
BIN
TECHNICAL_REFERENCE.pdf
Normal file
BIN
TECHNICAL_REFERENCE.pdf
Normal file
Binary file not shown.
381
TimeTrack_Pro_Complete_Documentation.pdf
Normal file
381
TimeTrack_Pro_Complete_Documentation.pdf
Normal file
@ -0,0 +1,381 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R /F5 7 0 R /F6 8 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/A <<
|
||||
/S /URI /Type /Action /URI (https://github.com/AlexisTrouve?tab=repositories)
|
||||
>> /Border [ 0 0 0 ] /Rect [ 245.786 650.425 366.214 664.425 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/BaseFont /ZapfDingbats /Name /F5 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/BaseFont /Symbol /Name /F6 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/A <<
|
||||
/S /URI /Type /Action /URI (https://github.com/AlexisTrouve?tab=repositories)
|
||||
>> /Border [ 0 0 0 ] /Rect [ 479.786 348.6 600.214 361.8 ] /Subtype /Link /Type /Annot
|
||||
>>
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Contents 28 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
11 0 obj
|
||||
<<
|
||||
/Annots [ 5 0 R ] /Contents 29 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0
|
||||
/Trans <<
|
||||
|
||||
>> /Type /Page
|
||||
>>
|
||||
endobj
|
||||
12 0 obj
|
||||
<<
|
||||
/Contents 30 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
13 0 obj
|
||||
<<
|
||||
/Contents 31 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
14 0 obj
|
||||
<<
|
||||
/Contents 32 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
15 0 obj
|
||||
<<
|
||||
/Contents 33 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
16 0 obj
|
||||
<<
|
||||
/Contents 34 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
17 0 obj
|
||||
<<
|
||||
/Contents 35 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
18 0 obj
|
||||
<<
|
||||
/Contents 36 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
19 0 obj
|
||||
<<
|
||||
/Contents 37 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
20 0 obj
|
||||
<<
|
||||
/Contents 38 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
21 0 obj
|
||||
<<
|
||||
/Contents 39 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
22 0 obj
|
||||
<<
|
||||
/Annots [ 9 0 R ] /Contents 40 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0
|
||||
/Trans <<
|
||||
|
||||
>> /Type /Page
|
||||
>>
|
||||
endobj
|
||||
23 0 obj
|
||||
<<
|
||||
/Contents 41 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
24 0 obj
|
||||
<<
|
||||
/Contents 42 0 R /MediaBox [ 0 0 612 792 ] /Parent 27 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
25 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 27 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
26 0 obj
|
||||
<<
|
||||
/Author (Alexis Trouv\351) /CreationDate (D:20260113143559+07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260113143559+07'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (TimeTrack Pro Technical Reference) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
27 0 obj
|
||||
<<
|
||||
/Count 15 /Kids [ 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R
|
||||
20 0 R 21 0 R 22 0 R 23 0 R 24 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
28 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1060
|
||||
>>
|
||||
stream
|
||||
GatU2gN(as&:O:SljHK9;k^hb`tg'36^ahEnEr>0l4VjRj-5+uB-gcb6_'GNPDb]kFS=qbC,ZPL^6nrG97X>_hOQH1*7>!E8L1*=iF<ch7''ap.D@YlNb7$HAYcXr7"iNaaTMm9Jr(>#qrS6SAVBPhn9Df-Ua>lt+H?!5\apRaKZ>(qdlADE7;:3tbC@<=;nmnU%aniH))"9rZQ1NY'0/KY:stQ_B&_*?JlX/UVLrHOMpm8Na,McI@lto3PU2b!l-@p4Oe^0>X!5kjn/BS2<SeL6Kg2a,_D\jFm6H#W5=JeF*F5'E`.3enef9.ue<ahZS#]=^jYrJJU^J5t'jONo'(iD)YqAiSq/hJP`,XS>VQ(.A#&%GslgF6pDVtUu<(f3a^L*[":7+>+mG3tH:br\6:PDA-)!`Cm]1<*Gqp,C8B:EA\_U"siKo7-nl?0dRiAAlC2[8aJg$'0`ObYB+U5RS%9#rt(Effd4e(/*]g,XPbO*4O\QaUYfZ,kk_rM?7of=#t3bnJ:&rKB>ihoR=[G>+A?l+9pS<.[B&';;,)YW.XsL&Lj91)=["<&?=<4jsTpe&Vq&h$3"Hp2[[TcMW+UmM@W"M<#q[\2ZBA/!j(V2K.PqZ+OB$Vd5(!Df=[UKK]8ZoeRhZFIu3r@>]ATC#*7X3g&=OF1^h^f>P?FJnh%A!0f/@?]3(0s2K;?#kq?_kb=Ye<`t&3b58ce%+kT,Sd\YYGQ<Z<_];DtOad1_XNbXg2;[H]Ach$R,$Z!ZV>qn">MYOsWq_S,4Uq)d,H\SZf\Bjd)%,b>P`-pq$V41/h%0)Y):>;EpmV&BGF^nNq*C6%,;]mT;SDYQ]e9M`HMOa)e>8h]@$ke-me57@0oJW)!X_FUR"YH\=,tN2lG<3<#'UZAM4ju$!gX#4nY%qn#1nch_KHZo8EoGr)nD7kZme`H-5A)l2;RWe:01[]WuDLK):lCH@)[3\<ZD:D$&$M9Wm\Qi#JnE4,9HcmlK8#SBAbW$Xh/mmjh9l7f:6;/mo80hgNM?0#61nEXRDlZrbQ;r>U,CM"_/<6oES7D2VS~>endstream
|
||||
endobj
|
||||
29 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 484
|
||||
>>
|
||||
stream
|
||||
Gatna]hZI!'F!F.:N8ih_YEsTFuDU;!AOS[Qrl[/-."I[-j#6\k;k&+=un@18<2b9T7"(#61RD!=Ot9-^qhIN'Z*#S#_R)VP;%9Md'>EjE&&1"S04`2aWZnrU`DiAPsmd*E"EK3KMZ`m!pMm-V(/HuG+(XN47!s3i^`[!co-gV)f9:*i=TK/qeKao\`@BtPBeC#nncT5g!;$Y-Cm(,<`5M$7*j_#lCQ,PV\PD#-EN>Qkq"mY>@L(lgbsr:\u958D!F=!^1&VGpj\u_/"FqOZ>'"*YddDjKDmR:Z]$Ec\9b35EhTo^UpU(_CJF5njpPgs<;a$O2tTaY#-Ak\1SOKf3F9c"[;+hH+n:8m@_O8E:[SO#8)VdH!PIdG]F.Ingc+GhABIPR4Pu+CP&TTYQprJ\S2C/0ZUQIHJ[9cEW9P5RU3sgHgL)*HQ4NSe/7uDLpT]_L12bDlg^g49&@C-U]`7N9]:EF%%1YVMYQ~>endstream
|
||||
endobj
|
||||
30 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 657
|
||||
>>
|
||||
stream
|
||||
Gatn$>Ar"F&;B$9=.H1g<5R:JS]bNSD'^GW[]JGe059^Y.Ch!)ZEt6/Sk1iE9.9n;.$Bcrj#N_Z`tJJKQPd`L"ATE_>R1TAP<gUo@,Bkt/A<[R)!Q,:ThiK]a$_&XMSQ%YKGT13UXX7,M[H2>=.t9Ui@M9u`RU'I^F4VsI\`d\&>g^a$Kf#W/Krq[PM?ifFbXI5[C0h>="k.=-U#l-Qu5]r2&j&scRZj2c?k*%EHjPb65-st$oli_#Tsu0+)T`>q:9$2Q"AsH-F)X5nrdGCl3EfeGe^Y(S(k-1l?lWTO5VX(PFJAQLg>>![fk,>4,'?F=V2@cVpcKLokde.#D>;lKP%aXWjL%:.OhUYfJLqF2!NmF4f[]<U<deh>Rg@.P8C;fgpM8bE(ja-4o']\:1:@$B@7A:@/*9I=HY0Q*$O0TMLR)[T9UU2G[!%B;A=a?JXkCf;*U>q32SCZ]`*kE".Io?)9pPNdZb(PDj.it)qlQr5ANsrBDq"tea7m+KFbMj4%IW36&_@!)YV'O3&Z5_U6'^I5rr9!:!6'jDr<D39SH`ho6\^A<H;`/Ql<H\#=8YGZpLF0_'Uh;ECp#;Y8^fcf^um%rtGU*=_Feb^77Oo4qf^HR4']SA)>#2]%^P<>2U@u9r.c"Vc\<O3_5]G~>endstream
|
||||
endobj
|
||||
31 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1866
|
||||
>>
|
||||
stream
|
||||
Gatm<968iG&AII3m%lL1.^9*^^3Xj*VeI&n**\V5I"<_;@R?1a+*-0$O7+\%MPMfO[?K!?j=D(Y+85@nAmjYSe_eaii6;=PH-:_X=KI#Z9>KNh>V%4/-d67M2L'e.asaT`cWq-7`J)2L+"[9N0')=i$B4UG^ju`P.p0KVI&tT>#8PTa@hLX$=DZA@.bj#%\"MV-Zf'i4MQ/Gg/%DRe>F@^gK#MA)]I$GsNPu[#)d8c)*Qr:ib=<_"&0Z!o,=6KGa+=kBbAhL[*i=j?Wtr7)^;*a?6!Sqn?(S/t;8cTe'.@i@8lCJo29=T+M'H0_)Ve>4;?FR;`_i<BfrB_3.)Ls"-oTb,,R`DTSC/`sN[8KRe8FAg0@NHD@4=ip-OU)N<Q+3F#+sbBJZR"1>C2ch9UE!.Q7>[-AZq85[oI.!+D"Nlkg"e!#%8b?cRLDkrjX8a-\SE]0%DjQ$Tq"[$7Ih?,.Wn]E>"TK:hFB&_;sZfB3@[aHCcXtI)'7O3Ug#(Z]fr&X4EaEWET!8:V00dZi%uramKNZ9C(:j*V/e!$>rh':9Cd)E.d(:-+LgVDD0ZQUL[TC_`6f^6t`"S;.ncqT'Zaa/D"L-a&'/rU3mF!5LfkuD\F@9K)KJEWNDZKH@mZ:BA6^d9%Bu'D@CE\LHK&R0YUPiAo24FC`Q#%pfaXZ<lOF(BKXno.ATk7aC.).M(3CM[Gcmh/F/,K:+rkS&qlL8l<?h[?"Qs"gqK4C>6:#oCe&fseT.egffT(@0!YB`ZJ.CYGa^Sn4cq-))EG4l5k'gq[M^6Q4D`Y^KJ5FpeMu?KG_-!t^DKB"e%"[f*gpq)c78JO"Yb/1R;drG7`u4,l=DTlZ5%+qTjZ+=kipf0E6uXclg+;iEVio$6>t'd*qVJ4`:6]N`qU\GRSTA),$(\rUm&7fQlf5gVO+=,j/A00/jm/HT_D#)Prc%eZ#CVhhc(3LnW$eQUMSPN%Bu:IL4Q$sZF%\F*9bm;+/nQr&=O%OF%>^;V5k)1bTP':e10*1&[pZ_h2$jl3Wq8DFYf)O1!9J$(8Fd^Ou^T'#?e%k0f+X%(-91S?s:X`E?&SC1P3NCr0?AJRDf,5(_5SD#Y/:g0NNBW3T4h?f&SW=_e]nPcbi#,cF'YZ3YeK&Q_g(:!P8BBSur["q]!(a$10GIUFP&kL&i-^:M))cKKeJSdFE?AlNaRaGo2.\k$ns]cJL*Ck0#<UFBoBRTiSnKo.5Z@82Np$no>oZ[5PAY&cZ%t/4j6IF1XJq>FO45naH(f&n,0034NpRfc2(d`t8O-^J?BsmLOAb>>HYn9Al\52R:'8mc`lba#e">CFQq;Uc?)J']0BuhH6G[7Z+NT$h:'UiC)6PHtd,cE?n^P]Q(h.0BA2,d'L^51#=1PB-[BMR"2_kqTd<UYe;iB(BJS:YO\3`P@hfWoePrda.E"XAACKmo>>L)5#hseB__ts704;\LG5<0%=<?<(`u0F,Zc`^g!aKrE#\7pr1JC1R/`c\aW]9ri%1mXKUjL\d584tDE,fI\DoVp8+Jh%pC>,sloW(h*3NM@*fVgA)2^%;F5QlB-po!]n"h/?%TQc0-!f2R'LEjE*f^M%LOo+HmTCAI4al4!<F.rhqmJH[^BZ"p6S#6BWHR5)YTYYQ0$K'MpF<;L0P(mU*N&acE';C^&ZZ"SM\B(6I#$`EH79;;g7[TsdY;ULa#)<2kgd^8C!%<mk`?J<NKgh=&dT]j>&&(@,BI'%iXJ<4pu0HG1kO&nHUB#):LP82gBpUg#@c*D$Qdrd#]-uLLF]o980JjS)RqrB!rU>dX8VM1*#:$XV"b$QJSVYka>MA\]i"Qr`OLCOFI&kD0i`O#m;F#3_8"'Vh!L4<9`>,5Tp(6~>endstream
|
||||
endobj
|
||||
32 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1476
|
||||
>>
|
||||
stream
|
||||
Gatm;9lo&I&A@C2m*X&_"+]:B3KEK@-Z8"4bpaHrJeZoaTn&Bobk/r"6%G[X)mnFI1_QeL"5^q9pCX2dn3ahF]E7kq_1sfRPX2_\)L)9nTA_VrrL?`CN!MgCKOBaa1hL0XK#[qbqD!6kQ\D)5ZPod^#71KuJ.f)61V<"Nd2rK6;a2QP'?DY67PN663!=MfQj7[YDa`I!Q`X*=6JVMDfaVJ1[&-JXiLh\p4f4Jb`gMg%`=[rj!5+E?1nror"cW,Ils:/2\jZ12.*OVb/K6DnK7j>t1I36Lhsb&(W(DBWQPGe,'[L]?(8,>k6]3+dGg0<a&p4SaPU.kK;lj`=J>H5L<'lh^c@p#i5q_J8;ms>VnIMf>cr@Y/TbY4TE:!$lb6Sd]A]sRKlhF68(sG=LMmDEqV19Y.UMrN0P"c#nC+RpQc-$/ieT=^@k0>oL)P^BaGf%XO;t<ss_/;]T&\p?-XE[t1R'p@@bo=m4X?6+M?QjMp?gt)lc:'<AVp<Ob1ef-/?D?#Y<1#LjpPX>JMnGuiPH*99Wk.+Wh;T6OXhpR[lDuQEXA-Zl''.IT/`'Q/4HV3.U#A-#h]TAts4tG'CL!u0$cf2B@$<S3`!lR*4AB$q1lWW6&pWk$lZ>4Te!G&ZGBN)$?>=MP#*d,i5)(=WH?^8I^djSse8_n&DPkZXYF&JQhFgKCBr(?@oZ6%dh&'4'JU]T[HXc&ZUdOfsFHS73Z^_JbjCYdO5O*r)$iE;/gG:Kg9DL0*oq>AJjq8mJIeRA5]'=Yre%Y"r@'Ra&9FdAgnQAS$\aH9)mArm9"q;]q68)!"UHQ]P`NSlcF.NeUEV!E$I)!32rRf:jN"S?6L!j]O!kYR&,O5+tM-R("riE\es2=BNbR;jPpfM64DM6slA6<pBomGnUE4;n(W[fNH<l.iQ,rS4^&[I0H=!*I@@Q-S8l!n-t&Ipe%?PC<j';NLiW/pn9QOcg-pS150_C*a3(^/Q_aFD^FPcJ/1eRHp#3<TeCB5E54*3q#GTXa5@9cZ@>+k#QS5+4'80:FF_VC#f(lfp:mL>m5JnQ0L/@g>"d+1'E`L[76cor7ZG*rQO"A__VcYpW/T.QI:C>CJ_Y\O1KTGu<f(MHo04,KHBt['J4qa"TiT[i`=8Qe9:88"n*S'%?cNH+hqim`mE(!l'.GSt`]mfV:(9Y^[LW*KJ=[1o5mM-!Ztai<h_>W$umO?q\/R,B'.0D!p<I^#$N6fe%%>l0KF\hWLW'@^b40E(*u$&ejlFd8pjO2n)C?B'hA1_9WLE7[n@<\%V\=m)6tUr=mSLFQ+<QjMZBi[B%q.)b-a=(T2m%a$"E)N"1/`%[+Z7HZ*Q1[qc*44<'=`[niB(io[>Wd_!'Wc*V7\i[)#n*B>$B2bPjX#mml%9N>K(5cAmpeBqH.!3rZHnI*loj7R_:%q!:R./0<(mm&=[G7u!`iZ@!t1*nm6ms[gd++/'>iTODFZ[W!6&u2`~>endstream
|
||||
endobj
|
||||
33 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1739
|
||||
>>
|
||||
stream
|
||||
Gb!SlgMZ"A&:N^llB.#^Nk9_32jV6QFr?Gsc:X4\<fZDIB7+-H8]#.*I]M4?@k]gdAfjJ]'X4T5=.QKDG9<<)MB=%Vr<V=?(XC&(#R0r'Lp#t":13/VIe8FaROX]5M.!\>`1i"SJ3nC<BGN#oRi7APp0o-aYM0bi%4Qpb&%rq@L49bbOU@G2[)%_U14iLI)5glt^8A]"Q[/>^R_^->QU/XhCm2.\E1arE^kpl<Y<-cZUJO_Sd&rMdhFbDs$NI5u'f%%[4ub(3:b@H;i4_J6>r(nMX"a%V/4a7&XP"LSLm'[d_VceGi6"DAjFsfdQL^,j@Ib&LO,trGF<TUV:*Z/#[HVpbVLZ9P)$<+VX]?/[d79!Y?@lu"/B0]aM"8>Z(2!ZZ$0$n,VEqXj>15kf(:Tk^_j5J;-<%<gSM-?i-35F=g0p<>U3&L\R&$Hu"@bp1gVQX]@(ctLlS_LmAsT]m]L%WRLh6-a6Zqb.Z)D=eMh5PA)QR(>EA!MVe\MiFWSj9%LOTcN!->Zi)Nab4#c?cp@h0@8T,cLUO-?G#M7^;nP:-'Q2_L"hk_*%R>f@+lEj52e&:*fSaCARFoCHpnptTnE*%@e9)aoW$,n8K-I!2T@aq/-RM5D4%^'t;`UXhbpA$)T^s%R!3VYE=D#q@fJAAd/TkT.)^"ETU#%3Nb&X5'epYX[_.Q,@H5MKkj:i:l$[p7&^_At(S^J!2[7\<?-[bVNR>8^(kE.OGouj)sUI!u<L=Bs3%,8(lkVm>;p>&VnuD^^Y:9aAO\D04kBjWMBU1?u%t9<d\8R^f>q;ej9)9d^TeHjDdTj?!ali77%6K/=CCMH^;)m8><;#QX:!(48<<-f=E(2^Q!jf)[=-1hhDY-;[a<ZZ%)GE8^p(0JmA#<iJk"?o<U?tL!(t_VRMa\)dNc8]2@o`%E13@1'DV3GsU*"<5L[OQ0:F;PKSl84)1]t::3\:hn&RhlXf!-RK'sW[NXCkKl".jieHP$l6VZnV^Ij)n>Z=XfJO0KED*X+\?F"DaB6>3,?RRj<u**!r!I?ipds+GYm@/,M4<OpOS:Yj3&XHuGAt%BR^N@jPH)$!F6%!s\=I$R-84t>;taeWk$9Jt5_f9iR2;tTF820n48qJ;']$#o%<kV'6</n-#/u!)AtOF-!AEP;&W'5H?\RuD=:<lt7BKtgdJ?Oc\4A,VPu^dlaUX4V7rH8'_M6)uDSqCLL5/*5_i)B$M_MMoK)%5,-#p3M6u38&`Wu(8h$A-33Kd_a,<'`g5XeuE;Lk4arbaH`<Q0Jf?>X/eZBQsP08R&NWu<\KG3%6)_nNaO=D2n[;\+63C1<mm=\V'G3I@)p0TBsZ6/kXs"c;Op(*rhV@R8>L]tD28`I`bd'FTWTNab;k4,\fBTlMJOr7FHqN3?igbKnY?q?A#r+A3V0ql_AdMMJ)>$KlYX2<^Q/a0:5f@OY^hb6?'cj>:JU&1ZQ!oh0nG:"hn`$o]V!&c16?Z4^?2_eKEbo-N(ci>,<RjREh(F?=7f>U`n^5.:6#I<3H[kL2$jVdShfRt`cd.if&ubg&BNqWXD*Ch!ESTjsG+`29Gd:5RX;3gH-2glti,s7PGKag<CEb)r.jOX!BWO/_MQ7ERtE5:fgRQH)Zd#nGfqfrCe$WkmS;M;-m6ACtr0H8:L>"6S/iE\MDd2td+;r.P]0"IJr-3:KeUbI9Za6O2nBW=Wg0NWpYR%G#d],b*u-Ae=_VW(O)nDL0\,#;U!Kec~>endstream
|
||||
endobj
|
||||
34 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 955
|
||||
>>
|
||||
stream
|
||||
Gat%b?#Q2d'Sc)J/'_^0Xir]Ui*B%!g8+&'VJAXY<JiW\`9Cq;U]f7Kg5k+l)a_,&<hTZ^pu+B>5<+B!_2FWZo*F9<&+<"&-j+r*e?L"_a"[.4O$.5K5sRtl0ZGXnM!I'/EC-Lp@(D4X'%4B5lHi]Q73[t--s4W<KkT?r`.Qg0q>f49lA)R;p#Z8PHghQ(N)t7@ZapQ)X0_(J(3=;,]3lBRN#-3g!_.s*nDK+JO=F?RU.B9(h)7(Fb]I&eb=RIe<tojRUf[/P]\n"p4RZ!Z6?J&EcC;c)bE::`jYATQj`E;bL5e&&\.j+k.fE5C<f=u)2]sQO/a#@%ea"';[`*"L3/KRs)ME+=Goa@#D;ZdV1Jbo2(++#'-RDuVK7_]0%P"m$@f)[dFH+5#K02dRH$$+8cLDbJlh'OQ0YN&/iD&K[%"94PO1Bj]U#IV.L2ApR86=!4ah>hXI2d3s`-5LA%5tef%aRltqtq@YWg8_mjnksHZn.XGQg=;OZBf?(H>QK-(j0^5K2+%IQs>]m?"OO,N3i-o(8q\KI[n2/cgk&[%b`ITZc*L@nm#TpNjYh/YlQAqn),FP0&)-\SE/o=*A>o)N.P'A?^l5O&hdN0pd.D@QY1s<Rpgqt:XTJMm)q%IeuO1gWaLldq;5DnOMfK(HAlp84O*_>ZXn<b/igoRA>m8bJnWtbVDpoVi:"Bm3RT-1(/q<5K].nH/?:Hfm9(F#lD#RRCMM7#_7)1'`HEK%_<Ba*T.J8+'?*umV):oT/06&tLlEm=VhL.>UPe"JTju`R@]W2>W[p-%U9mlX[[sG\ik-!H]!^p6P+*NKWd:hl8Sh#(5#R3uR:=r,X1Ou(bTiG6&Iu7r<A1j\i'N#fr:TaE%_hTS/p<5H#4i)8&d:e`X81($S-LR[lD7AncW`;misL]"[h]m`eF.>YI[]?4Ai?RV#8glskF2R"IfW^R&mY~>endstream
|
||||
endobj
|
||||
35 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1560
|
||||
>>
|
||||
stream
|
||||
Gatm;>Ar7S'Roe[3%l<#0%EMX7d\H?P-m[21l8N9!.ub"N]LH.?'9@JqV-3bOcg_RapJu:/`uigiQ$!U"bM5'IE+@`8I3)$;kOQ9"K5fj&73.T\G1D:2:<`-E=]l6Z`)2T-NK&b2RRt;j>V.Rg+3T('(9cDN'em6H*b"YC?-H<n0FbAOW;4X:I[%#-fYt)pW0VtO-:l;.+`;_?s1JCBd;W7Xi+u\^HhQ`;S1A'#C:8q0hLl6gnpWiQM:qCr:h9cj@D@j87#?4\Ds9>*oTY0M^l\%g*$`_BlaeK`$MR-Fb(I"ZAGlofK_]-^]fV<"L_<uLq6@),0Gpc6_941&SkXRKk2l/e7ksWi*!J?7TIsr#.ZlLMc`qI%7ut!jeGfT=k!n\NT):E7;^9fM_]DFfN9cXEbR(cptPnn,rG*>=IniNg<)-&PX[u<)&H32^WQjZ4f%(WOq&eVidd%+:7d:qK!Bp*;M+,A2UaD3a-Qp*NuJ(?1]X)E<!Ei/^*fO*$$a5&ECN%`<AO,t.KaTqUX+BcTejF4SjG>V"dRsR!p(T&PC(?0*;r@',J1bJc*Q5ZV-]5[e5k>0ScIK_[X:Es*dr%=RR"I.i^(onceBY0&ROqAM8G]cd+MX5[ShaGpZJP-P&rL$'9-:8[ka?_gZn/%8cE'U;:3llpZ!%O7'D3+$(Mf66'&Teb^U[h`*(G#rtmnp)oZ?B]Z-S1<=VI4!NFtjLBQqZ'QMaq!iOq9@6FY`.JdW62,,((Cl"]Q9sfkaoLtWH&_Hg.p$F2m>LAkO7cL<nO@A%po`B3J+,dq!brs8E#%H*\H&B4o3>F)B(:Vs*DiHnoCK\DfaN@3qb=GJLUSfjD%!/P%\hBFUj7j=(fC)k7IGL2h1JM%?YD%YG7MMSXE&"]L0\4uc$keP@V\n/U;Vj=rS<Yu&S]EK?+>Lh8q*&0k?-adMiN+o`amdZI2/^jb6TW@(4)*/[4VaEKGR%Ta9pnMWM'k&3j%B4[`]=d($1f%O*R*7=aU\//I_I?a-fDq_c@PA80RUA=HO*mV[]j"]m$hUi)h_I/E22KHFC.L\;!.N8p<-cXjDs)"Z(q#5>k`7sk`KX*a(fA-\27*"P"(=%,B\_cS`)sp*6??LP]4TG4pTYtG>e5VUpeP@/\@;Dj_?n@fe.bdG-VuMaQ19IKd.^F[2AJZ2u"Ma.ZJY9B$?%oa_<E@5_XGA]+216oPfL)MY`^_E`uIqm=d*6^OeLLnPfjUE/L22K1#4ga=M=UN&JPsOuMVi@SGuI7K$u(T&o&SgdqCN1?RZa[@K#ikC@)Dh7EbobA&i1_VA5!<7h5NS5iQl_7QXJb86a)(Tbf`[*7_rK"Id+b@'(RcEO/=7K;fK/F,ZX4Q`,\J%EIYD]MjD1%!/dC7EjoO^d$,]tlGa+t?DXdUkIuje=q>,@jt:dP#=>`lHP/cs:@(VNpU9h,HI5dJ$9>j&kY-c8i>bc/^WY\9p<4'@"J73pLnH0[3%BmtuV2VlGEZ&;p7s89(/NCQXEFi7mAb3ZR%`.._dSb=m:XCC$uHICQUh$NHiAfRA[-`IBq%eS>~>endstream
|
||||
endobj
|
||||
36 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1416
|
||||
>>
|
||||
stream
|
||||
GauHKgMYb*&:O:SbgRaf>$7XLM$!AsS&F,gZp66YT)pP\R80"3i_i[C!rMLL'Ih.ECc,q4JItNm%HkqTNeYRW>l9]W437O&Lge"?5UaQ$+O"7*ItX#1p2*:#BAc+l"*@#%6UL(E#TUuLie?=SDsDtdSeXMt5N<G+#:3gOG';Gkd3!HqUb3p=Eend)7V>dIitB9nn/YUf78g$p+YpQ0b9UAiM9u9(D]lcJpoR<ZbXNWdoHT.V'Y!JZg3)\3)fc.Cmuf(Ai0[-VM.-Bh?A,_FFEnHX.^>q+'Qp23ra\>&N-RVm/[T^=!"1d!pS]C>-`_[>R!DAnII'@VrJ2'!(9bFkn_P@e*;bf67DY1^ksG(KA,Jp7k]S8A?.#t6G(K(u[ehtkKEL`H]dX2Pq[MgL>KWkWj0>ZuQ*,8]lur)S+mMLU;)e6uD4S8f#Xf>odAW%EPbk[aiKG\.[ES/a,"K7Ud:=+7SUWhX<N.*T"'"bQX`#=2McqD!Akigg7:s@B3;%Y0hn(RMi?j,&ZYXD7T_!lPl$)'n#[=>?YgN&b->WF8S@F#Pi2J2EAb&Ai`bR'Fdc.<46=#V>;50@08W/.J'j<S2.u_q^_F[V*+8a`qY%LItHj-Kt:%`]S9:'+gX.Mt7_0Qh#r3.d=Yc5ecX%;/>A4_tJ)"(UEfOa7::)8hBWunQ(>r"@@nT%kOUfZNlo5%:i;Ut8&(/f!flkU%R`Q>f"+g*R,VoO_kaaG?h*3%ZeJhMiZHPImO1cuP]J1nP2H]&*gIZfUGHO&l+ZB4MTf?"!Rj`oq7e=tl>r4AL[/E3gfV^!7F0#``?lhnZEn_nhu"nMAKe9@j>>,uFUm`=C*pm@ri=).85Xa&H%C%"]MQr+)/%I<QZ2`A>5Cj>._)r>Zpg;2,G7H8(rI2t*6!gC71G1O!T6B4q2n/Xj@Mlcm16B8qG8h0V:W23[S6r#0k8qI(d6M]o0kd6T?U`"ZPPTt:nIh[pL-UmWZWF1tZHho.-7ZEoe<B__QN7Vd=en$]r.42<uM6^%T5DMhYa>BC7=iU9$8.1q!e]h)CeC#>4Kt'dZgK=Ql6&#.gXmeI,EsCT-_0T3.oHm<`lY?Z4L4Kf0=>&cWRF^u+gmSj#W$(;t=tMQ7AUQY2Q1m>_W^r>j.a>3FpYgD1a1DmsQ-*4klNp8NDkot,[>`K\QNt9P/lXTW>,Z4>?bY3KW&N?Qre^!+.fIpl8U>TB9fK2h'gnk$$9(ei8)g_u?.r(MqM;[TGKsL*0m@sp)cl;!filMUbIcHLLC*?lZe-@31%"*UjZ@*+*DP#1RHloh^.Na^(lqD6?&*Ea`L^p!Cak<B@O-Ht-M\':qb'P";QL"E#50=TLKGl^"o`hoi*<F\P!uZhUKJEnKSFoJ_bApb'u[+gmOgYp^qW-?lAm8Lbl.^UiU>J~>endstream
|
||||
endobj
|
||||
37 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1592
|
||||
>>
|
||||
stream
|
||||
Gau0Chf%O5%"?N0_3YVAgZhE%n&MfG)`G'*Hh$f/TMQXTG3`6j>>AD\YePUN/NZN;],e,K8E*O>ac@W7OVA*#dJ>q2!F/$3rfmOr&1rDnQ35MLI#1>M8(8YE!aae.(hFKURHoN60up0"&!m3,/elK+%TMJZJ,p%N&?tpl\ujNH56RXU<[AplLfS1,_*hMI=4$BfZf(]N#6)?(^e>F];8Au*ElD$.o1oo\6M+EE]/?%d"PqF6Rc7O3oc*]Loc7WICt^R1QF,<Yc-12pAG?Sn-qR'6(\1GA$Hbb#a1N*4nG.h%Z'YX^pd.uXq%?XNC4ETt`i$H=[@E-`/nM3(Mh7Eb.N-FrmaJAAP=-@r4HJ.'?/$)SJ&o"2amWlT/CRLjZ+8MC9WhG@=!/;5M.9C,Qh<C4j-i>L5ts>APerd5`-a$+H6IR(i9MnHW.HNsmMR']dYg^t(e3":kDDKsS%G,5D-KB$EPhd?l7b&tBB2*1*q9=;/=4`1UL5sAH5da"9c4ra$r!-G'W*thn7Ajqj&G+0CG.31JSdJQlN9.AJ2%9K07^A]X1O>Ab=MEQZ5#:_6AJIre%[NOjD".?*E)'^s/$8:mXVn,/b`[jmX[%@AI-7K%%-G9\2V.bpml`^lUtMaQp0^p;Toi?ST"dXn3%f=nP+V7[1fUs3aqoOf)`XN.`J//k/Y;!:b?c%X,:Q1EAf,o(3qK2,'Po-)V2Jb+merRqI-L9g7FA).+;`kj@f@W:&:Do0uS*Z:lPi7:0/30-N!K@\[loh)b6W%TEu.71glfSdN3UIU'=*C?C5/9J0/:Dll'+F9f]D_D/hVZe\LRG*As4u>rO;Dl9d*i\&Q^2YMci%764,jT\nEuK4=eXL8_q:Lo9FffWVS_%5Kb"2R&)D1;&GJ1lI96Xl(#*k'?De%)<@bUssVCcS1XS+AMIE&C#DA%8?pc@!>G_B`IT]h#E<[=+dc]@Gq4H#4^%/Bl?7sq\)B^9#-C(p4BGc[m0BR37=o962rJeoRu1!;o6<J2R6S4md@G^?+T]-#.%K*N/[*pa:>(M4a^18JrN_oF]/.p8Fb/#SQ:AllD1r/7Z(!D1h^S->?bu\b2$61XWc9LTe+-af9Vl'/R([`\c1DWD8L7Q[Cgd"=)JNf)Olj6m0jBEs3f,q\l"E^bY%Z2g^EWQ'BY^kHoG2iTN9mUXkPbB^WV2u*VFiZ%;f$m>ZCM^Y8bX]mo\LSc$HF/$th8b?CalqY<WminKP3ZI&B\Oc3XfT\aIT.2\5fle`J',)#J2aJF??H-`"?4=c,q]ct)T&V^%.=$?=WOL%7*&quu(J`(u#maU)Z7KfUl@\pp$b;jdo=R^\9p550sbH`EtJOD@]MPK4;N)Oun3L9jdIak3N!mDM3K)=HjK"&<Ln_FuqAKMQma@_#[?RHVcRbBgRa_*h/A![f_qIQl4Z*k_e"5?7"j_DAn;l4k42#nLJgEEJ#1ShJ&k<pXtb`!X`-gN0@]=&nD[<p0c@fOhGI3:5]2Jg2ghO5I(B"2<N_osG\QaAU@Ea?(#SP%-+Tn4bH%JJ2V@PiDHtK/12?1s*ZDcJ28?X3I2pXmZc_QH\0+'h83)`n;IB~>endstream
|
||||
endobj
|
||||
38 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1024
|
||||
>>
|
||||
stream
|
||||
Gau1-95i<6&BF8:.HZ:!,gt=b*m:ZJMNC*_&T5:l?>jRtm)A\,XMfm,lt!S0<2)tf77ncEnbbL*s7$ZAnKR3MH@V>6S6Z[K'\X!-Q8*(1_jbi>@mS,&<=W#-iFEqO852acJrI!e'K'Fk:.oQ^_>oGa=[R`ui$<-1/O&,HB]-*-:2$<fj**>#G(DI@A2[_rK4UUi="iMIpX6qm38\V]&j.RXr)7:D62N4=I7am>ns-877`%C1kr9a+X1W<eA7\M:'nL2R#_JZK,(AU2A:u877#G(L/tA/p)hE*%ZdXJp0ZfQG#M\u('[Ztj#7h@YmJPeDYl#]@iD/&Ba]]$PqI^-'bZ\m1hFmohKS#d7hC6o>#sp0?ki#QrC\5:tSW+`"c@1s$phX[IAQ.AEn#p.afip1KmLF6hk44\b28$C="P$.IF/AVM\YRS&F116.bGUO@eZ9U[jc,G4E%C$oUq!ipkM^0*nAlj\Rhb"d1t(n:Tsl@R,KgWK<6j(+0U_>TSX&9\cY*oB`A\qtCod^kK\`&HaYF/hncr.7RX>mq;>AR\Xl6\[W\\VN8L*q-3394\YMb<$>NrdX&1$]f3%hp83Mj+F_HH>;N>huq.$:cdbnnGZ;Y@?16:fH.]clrVd[Q@:8Nt'*KQ^X!Ricc2Li*Qo=Nq+k<7lV1!QPhP[.M0Q3N9?ZSrr.Mf4(X&c5;N8go/#lPZD5H.oi/u%g]9c`pnPE=on*GGco#M%b=@kjOM*1Ill99j;[>H*rK7RVjETu.h(Y39]K`Unk/?m-T0V^4)b(E4C>/67_4gc(fYu7Za0[P&uM*4VN%DXr,.uRq2IW$=;'I9#b)RiK/EuLkUp%?]6,L!O#'Dk%88U\jVjAoYtE*BqcQQFSD$0^99*<I.]jFS2)N,e>;^\W367=_I$A`la<*7GY-V-aPUOud[JYiG04J'hW][P.nk7)t/r/+3QKTK<+.\,K=Mr+c_4F_<VipUDaG8ATFd3PB*u?uinu)&G$$LN6=d0&<@<YGhHWu3CD\">\mf~>endstream
|
||||
endobj
|
||||
39 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1255
|
||||
>>
|
||||
stream
|
||||
Gatm:D/\/e&H;*)EM!H56"lAnoht.7oAX4HMoLb-IPEOt1@6KP7$XL9"k\mnOcg\ENO/is%%G-(\\!WpkBNl^i.p%DZiE(H@CDgo)8oaP*q:BP#ZpQWqh2CKhZ5t6&WAV7"3/"[#kM<d#!gBcj.LRZ&u`Wn+TiL3'!QdC=eZ9NkTV/mKX``5Qt3WjUgqm-O4qE,,k,#@lQjdiS]MLnJP/l%o!&b&5(WJ=icg.4dRNX<j:PgTic@1sm=r4@"JH'R4#HLWOQIh0]Z1TZ)hNe_odhsn'mWZd<^MZ3qlJ)MW(4rhr$gl<]m<ge#M^XXeq#_4_PUVd+FnB5\WIC[<c:Il=C*&_C#2WgWam]j)Wk``l5F"FohRk'pq5EpW;tQ5n;unhG(5aFs0gDiU^"4tpF=XQn):9?f"$=7kAr&qei4I!7$[eR'i&!1s4.O0jZZ?+iC520<46,K%On0M.Zm58i1#un;:m]@Z],\RXE^qRr`^PnQN[go%jFZ1=dkSj74q4f6ZJC%9K#]rGpcK!N54UKPd"<:iiE]r9%*TP,tX8sRl.^O.sFp,Gdp0nqA,>3S*mX^%m.>#UscK4Wi0>`h.E\<TJ>Hkj?9$L4P-J=I(.[<[d]@1XVgfeNFU[Je5ptHWQH\rcO.59kJFBO/s+`]50GlOO`b]kbk);r[)u0/]JbP)2:(lJ4n'tmY=^A\E70OHp2sF(+K;A'9C5_=.O4AoTP'<GDk^jQG6als%%6)R6BSS;?b<6n=R(P%LN)C]<n,qBYhSASG7V#jX?k&6o*g'^%'9WDG5O'NR1AZecd]!g#a'$41MonD4*Z7ZiE9@:V.cY_Lir7#/bWFW6_IV/5f]T[@W[j%haG"@V<$Y&O'8JjFtNDS<fbTcZBhFEe/YpeD,uO&[]T#+:phj<gj$L@%`&Sn8gmmQQ\eJAY6Mds<=<.^M<Z`QE!gBnCWmI;r_hqbWU>Oe[GJ/Z$G9\o2751EW`LQ8oLq+O^#_bo&aID=_-6'#Co7mt9X12%3gR\5>&W0FQ-,,%@%-2$r8(/((PRm-RRU^I$l=r;6fH4D$,O`0k`Wf?f0&/6A$&LAF,heH&(BnEm"oT0)"F<+nLaup_I9?!2LK1Se7g+qodQWY4DQfiB3hri67_$WRWD$PC3\[dcc/usO=1CF)0o81a\J3]O;Nki>^,U%^`2oSCuM!EM5)C5l"u7nBb"Lg(B;@=l$t1q^aBiZAJ]$OMncP9@O:b5<\C<>2\&g,O01^7qub(OY^c~>endstream
|
||||
endobj
|
||||
40 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1317
|
||||
>>
|
||||
stream
|
||||
GatU2968iG&AII3m*WpG1][Z*jA\.=46bCgfO<JTHD)cYVF(t'#ha3q5JE$\BhG\5BJLKgR)O+?_m^.-JIm=L50F/g/;M\q!e`nS"Mt3Eq$$\b:VKPZf8N(Qdhn?I(6F2iQ9dc=7%seco;!AUC=3/<qCJI.;c!O]D^SW;N;9.KHKn5!AdWg9Sl%L)T(*KdH]eR%8]dgm\k9D"%A/?cY=d3Yr4],pihdK0+MWR,Xat`Ym6AGJ=[3GC>rgq?rosMRpd0cV4G6<H0c")E>BR/NnkPDf(8?q.VcBE&TD6,98lqR6WWdg/VjDNX+0'JfKb?iKmT&%!7c&?.-PTZ95<sUP,U2i"$F>P7l3H%f2SguW4[^X<S@ORjP/QM-_^li#=h(6AD%<o6(iq-Qbo2Y(1.HJK1/9Ne:d/@<-h;H7OX)`F4n4@dMTT$SiB]T)ILoeaWjTj**3"]oGN:W/bps=,WQV;@ZBIojo+JC@K+HsJe8jB$<c:@1WV<\i#LH[<Q`B;IaMqC5NJg$Ik"om$C^Pf?$D4LS5U-3)qR>*;`=INteF?B!ihjL"7ma6+Ss:D5lOEfDG`1:.R:<u``-3aHF+U5b84A5t?aX(=<2C\7J<dN!T<u6#3X@lYf&>;ZX'.mbI(c\`de`?ka7$-9Mf5(W#no*?qodmL#RheR^cnFY3>Q(0bK&dtH2qk'>7]?fD0F-Y+<D'ERkhP$'IN.CE;BKsVq!U'+P#'iG,_+UNlX-LP7+WFb"&t+$_c8j/0+r^NVVS\d\a/r<M</0`!nPga*qh3]0_p8SB^S9l*ORjAEEW-7RA-,D-kDF2Mj+$Mj5,ZilI0po=s>cDA'0)%3t4_f^_@d02Ae@_=_`1JA]:Og`,#?bR-!l/+$p&P&R5WG5&a.[t0a3eKAG:Y#u33,f*,neP!0@4!?5&)7FN4_aQ5R]d)8.+gL"PG]Ki+f@Bl&a?gZ<]=6dY`"XDY\t+KJ,0hcC'Z1@:m]tJ_gO#'3EeIpojG0>i-5c@H",rWWq?__W"N,su$TNDb>@$0Wn?KCWCRbF3Ol"q[^euGI44d[X))j0>)?r0^q6-@Dd@fL(@^=V<<BJ9egt_oCEFO?#k><31(42:"<RXE1"?>-5Z>+@,$;/t[+5fu7G^l_nV/J:OB)gA+YR\U8Fclk6:*E[8oK;]SY\SDtm(*8&dti5;(Ri03M))T+)q@(`KA])AG,XZ3_Zqd"-Ok^X8*u!Kg5["EOs!CqDSg.XCk9Ps,*6Jq5<fAqNeBL]*)Yn!.)25H'P<Gbo!ZN<UkWZ/BtZ>rYQWXVa^Ad@/FXaY)Wb2`+\_k?OLEfI~>endstream
|
||||
endobj
|
||||
41 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1442
|
||||
>>
|
||||
stream
|
||||
Gau0C?#Q3)&:O;VR&A*/D@*p+?h_gMSMZUq3Y(CQV22u8+kM7^LO19%++Io>f#E^n`DpFpT\8!S#A0>`90gN>OoL%Ni7*CC1BN5]G7T@@%(mHO]ts;je=Ms9Q=HUEg,*8;PI[>`Ae;ia:&Ln9A.O:qM"#$e"mS@miVq<a)Rj)SKnKpGGd1Ak+=CgcWQVO:QT`-GkSfC\/"VDk^iT8N$_F8-LA)e+HNNZ"TQVo,S+KBUUOqF<S^qsMgsU%pJS+2c[0UHrF!0oU5@cG2$"AQZ@ZmZt7:;8_A^$@T0;::SZY8%.aelkpDo>DtS*CjAEo=8ilaN`tLKl19,aTs\QSl6EJ]e>(/TEXjf@fpT[cbC58+g2-Y184QT!JY=gOGG.0\T-(egM9p,B?-:$>h'6_+a);F[M:X7Tu8"kA7ioA[Y5&VP>cW<%J5k7juiJ+H,JtE]M\0E&XB`RTao.'(u6LQ9Oi3J99&BFi8c\T7l]=7_q<N(OfEO_fh6<Xp"Epj"FIMj3d420g2!YVH;ef(tN*T1gVbZ_4ff'@hJtE&i4UT1S+`^+$8;CJ<Q*S3Z[#r6V/Hj_T*@G&Gh+\e'C&8_'>$>??@/2Z+cijmOl7KDJ.Ffre.]s&>UJ)Yh:(DYqbXG*+ktHr\W-e\s>[sk0%imLqmcipEO-HFZKFV\iQJ/^7G"2;^6\Q=Y?0(A!@dDp<nje<I+?to97Afr(c>:r:XT?7*/D!#Qc2AAaVd%GUT>k=h:>=J&T1#J]:qb2m@F5s3`?&9SuOQ(?YeEKO:3D*QoKMmZqf:Xie(fd2P:"lKb"8[9$CK\$9BEYH<&YYu68umZ\HuF(N9/A](*Eg$hJaQjUd?EMs43<><h$720`A4Ki*f>Ga4$3A>:Z]='pGR&OUPnuKmd3YUV^V(fg_TA\?"!H(?c\u*5Nl3H;e\0(m[.PTEVW<0OX(`<5p[LG!V,V&bJ23Ydj]>>H&jb9UGG1&2'PofY_QtJ^<^%q@5b;mo.W$loCacX4da3-;umW!W1ep&H-lFndd$$N="qH<e(6i1.,cEh3Oj![)TaV;K'G2U!/['p"F8iArBreoi$c/S\1pdujeNR/[n$df]SDKQH3>!htL]9T>4]$O-O(Dq;K^tod.7!*T%#Lu8Zitg`kZAhq^J!qaG?0+\iK=S'MWPs`e=*r_K"4?`V+$=+;dKP\KbPE0-)n+VP"`)2Sic+&C"l-7Wmd[M%YBKp1-`*HK!&=Ji#tE0?E)N5/DG[L$H,j.@Q+@=G5X*kQqr,`'!^[U+)XVGNLojHO5W!\BWirO847!tFfU]-l0U5Hg[mGWq;*^Bp&;!7V!5,<k8c\3+0%&p`nnNI8h2Xd<>B=Z%"J\Td>Y$U9*@X=_j9T,,D-#M"'QYr!nQ:HiJC!.j%O6nLLCo6VCeH#&h>iP$r8/n-02'L]&&T[^hgU\m%q+lkX2EuO~>endstream
|
||||
endobj
|
||||
42 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 479
|
||||
>>
|
||||
stream
|
||||
GatUn95i?7&;BlS'k^Y\2=W2X,tKU#@aSDmA7IVpDr;rp[W>lnO(WK?"Bm&rAAqThj"ol)LSLZ]&$m4+[g'$S3OjV(_%fm&+)k_IY6@.%<[FcWU#][hMXj62Wl,NVK&>d=?SL"gV/3uTC.gjN(9QT+Iof=!@j?Z,B\hVlM/E5?,3;\h0.QLc+LD"*T:Cr&?%SD5mu)7dQkl-69nt7**>5sAk0ZsBaiD(jYR,U@@HY94EQC$KH]ROLFrHn,_+l$IH#e09'E(DXqrfEjd>%u`N8?tg^>LUJp&aA4bY)3EMpg-=-0+q'aVrE7I3uQP1S*G#?$JK9fp%5UmGa=QgmiC%0nsT0$#2atYMd9^Xii6k?Nb07$c/f"g4P0D25eap-O<T$XK<EN=Y2=/GP8k*CBTgKkHZAA3hd@(IE_]XSoG3\H(Op)HT\L*lABRY?\I;&r-.E,'>qK_,W3UQLf1;`C^Lk@!STQP]D~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 43
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000142 00000 n
|
||||
0000000249 00000 n
|
||||
0000000361 00000 n
|
||||
0000000476 00000 n
|
||||
0000000672 00000 n
|
||||
0000000777 00000 n
|
||||
0000000860 00000 n
|
||||
0000000937 00000 n
|
||||
0000001129 00000 n
|
||||
0000001325 00000 n
|
||||
0000001539 00000 n
|
||||
0000001735 00000 n
|
||||
0000001931 00000 n
|
||||
0000002127 00000 n
|
||||
0000002323 00000 n
|
||||
0000002519 00000 n
|
||||
0000002715 00000 n
|
||||
0000002911 00000 n
|
||||
0000003107 00000 n
|
||||
0000003303 00000 n
|
||||
0000003499 00000 n
|
||||
0000003713 00000 n
|
||||
0000003909 00000 n
|
||||
0000004105 00000 n
|
||||
0000004175 00000 n
|
||||
0000004479 00000 n
|
||||
0000004642 00000 n
|
||||
0000005794 00000 n
|
||||
0000006369 00000 n
|
||||
0000007117 00000 n
|
||||
0000009075 00000 n
|
||||
0000010643 00000 n
|
||||
0000012474 00000 n
|
||||
0000013520 00000 n
|
||||
0000015172 00000 n
|
||||
0000016680 00000 n
|
||||
0000018364 00000 n
|
||||
0000019480 00000 n
|
||||
0000020827 00000 n
|
||||
0000022236 00000 n
|
||||
0000023770 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<28de111491776d1c6e6abd209d41cc39><28de111491776d1c6e6abd209d41cc39>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 26 0 R
|
||||
/Root 25 0 R
|
||||
/Size 43
|
||||
>>
|
||||
startxref
|
||||
24340
|
||||
%%EOF
|
||||
BIN
TimeTrack_Pro_Technical_Reference.pdf
Normal file
BIN
TimeTrack_Pro_Technical_Reference.pdf
Normal file
Binary file not shown.
30
check_form_modules.vbs
Normal file
30
check_form_modules.vbs
Normal file
@ -0,0 +1,30 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Form Modules Status ==="
|
||||
WScript.Echo ""
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
If Left(comp.Name, 5) = "Form_" Then
|
||||
WScript.Echo comp.Name & " - Type: " & comp.Type
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Type 1 = Standard Module (BAD for forms)"
|
||||
WScript.Echo "Type 100 = Document/Form Module (GOOD)"
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Done!"
|
||||
41
check_hasmodule.vbs
Normal file
41
check_hasmodule.vbs
Normal file
@ -0,0 +1,41 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
Dim formNames
|
||||
formNames = Array("frm_Accueil", "frm_Clients", "frm_Projets", "frm_SaisieTemps", "frm_Historique")
|
||||
|
||||
WScript.Echo "=== Checking HasModule property ==="
|
||||
|
||||
Dim i
|
||||
For i = LBound(formNames) To UBound(formNames)
|
||||
accessApp.DoCmd.OpenForm formNames(i), 2 ' acDesign
|
||||
WScript.Sleep 300
|
||||
|
||||
Dim hasModule
|
||||
hasModule = accessApp.Forms(formNames(i)).HasModule
|
||||
|
||||
WScript.Echo formNames(i) & " - HasModule: " & hasModule
|
||||
|
||||
accessApp.DoCmd.Close 2, formNames(i), 0 ' Don't save
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== VBA Components ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
If Left(comp.Name, 5) = "Form_" Then
|
||||
WScript.Echo comp.Name & " - Type: " & comp.Type
|
||||
End If
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
29
check_modules.vbs
Normal file
29
check_modules.vbs
Normal file
@ -0,0 +1,29 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
WScript.Echo "=== VBA Components ==="
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
WScript.Echo comp.Name & " - Type: " & comp.Type & " (" & GetTypeName(comp.Type) & ")"
|
||||
Next
|
||||
|
||||
Function GetTypeName(typeNum)
|
||||
Select Case typeNum
|
||||
Case 1: GetTypeName = "Standard Module"
|
||||
Case 2: GetTypeName = "Class Module"
|
||||
Case 3: GetTypeName = "MSForm"
|
||||
Case 11: GetTypeName = "ActiveX Designer"
|
||||
Case 100: GetTypeName = "Document (Form/Report)"
|
||||
Case Else: GetTypeName = "Unknown"
|
||||
End Select
|
||||
End Function
|
||||
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
45
cleanup_old_modules.vbs
Normal file
45
cleanup_old_modules.vbs
Normal file
@ -0,0 +1,45 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Cleaning up OLD_* modules ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim modulesToDelete
|
||||
modulesToDelete = Array("OLD_Form_frm_Accueil", "OLD_Form_frm_Clients", "OLD_Form_frm_Projets", "OLD_Form_frm_Historique")
|
||||
|
||||
Dim i
|
||||
For i = LBound(modulesToDelete) To UBound(modulesToDelete)
|
||||
Dim comp
|
||||
Set comp = Nothing
|
||||
|
||||
' Find module
|
||||
Dim c
|
||||
For Each c In vbProj.VBComponents
|
||||
If c.Name = modulesToDelete(i) Then
|
||||
Set comp = c
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
||||
' Delete if found
|
||||
If Not comp Is Nothing Then
|
||||
WScript.Echo "Deleting: " & comp.Name
|
||||
vbProj.VBComponents.Remove comp
|
||||
WScript.Echo " -> Deleted!"
|
||||
Else
|
||||
WScript.Echo "Not found: " & modulesToDelete(i)
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== Cleanup complete! ==="
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
36
compact_database.vbs
Normal file
36
compact_database.vbs
Normal file
@ -0,0 +1,36 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
Dim dbPath
|
||||
dbPath = "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
Dim tempPath
|
||||
tempPath = "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro_temp.accdb"
|
||||
|
||||
WScript.Echo "Compacting and repairing database..."
|
||||
|
||||
' Compact and repair
|
||||
accessApp.CompactRepair dbPath, tempPath
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo "Success! Replacing original file..."
|
||||
|
||||
Dim fso
|
||||
Set fso = CreateObject("Scripting.FileSystemObject")
|
||||
|
||||
' Delete original
|
||||
If fso.FileExists(dbPath) Then
|
||||
fso.DeleteFile dbPath
|
||||
End If
|
||||
|
||||
' Rename temp to original
|
||||
fso.MoveFile tempPath, dbPath
|
||||
|
||||
WScript.Echo "Database compacted and repaired!"
|
||||
Else
|
||||
WScript.Echo "Error: " & Err.Description
|
||||
End If
|
||||
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
42
convert_to_pdf.ps1
Normal file
42
convert_to_pdf.ps1
Normal file
@ -0,0 +1,42 @@
|
||||
# PowerShell script to convert Markdown to PDF using Microsoft Word
|
||||
param(
|
||||
[string]$MarkdownFile = "C:\Users\alexi\Documents\projects\timetrack-pro\TECHNICAL_REFERENCE.md",
|
||||
[string]$OutputPdf = "C:\Users\alexi\Documents\projects\timetrack-pro\TECHNICAL_REFERENCE.pdf"
|
||||
)
|
||||
|
||||
Write-Host "Converting Markdown to PDF using Microsoft Word..."
|
||||
|
||||
try {
|
||||
$Word = New-Object -ComObject Word.Application
|
||||
$Word.Visible = $false
|
||||
|
||||
Write-Host "Opening Markdown file..."
|
||||
|
||||
$Doc = $Word.Documents.Open($MarkdownFile)
|
||||
|
||||
Write-Host "Converting to PDF..."
|
||||
|
||||
$Doc.SaveAs([ref]$OutputPdf, [ref]17)
|
||||
|
||||
$Doc.Close()
|
||||
|
||||
$Word.Quit()
|
||||
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Doc) | Out-Null
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Word) | Out-Null
|
||||
[System.GC]::Collect()
|
||||
[System.GC]::WaitForPendingFinalizers()
|
||||
|
||||
Write-Host "PDF created successfully!"
|
||||
|
||||
} catch {
|
||||
Write-Host "Error occurred:"
|
||||
Write-Host $_.Exception.Message
|
||||
|
||||
if ($Word) {
|
||||
$Word.Quit()
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Word) | Out-Null
|
||||
}
|
||||
|
||||
exit 1
|
||||
}
|
||||
56
create_all_form_modules.vbs
Normal file
56
create_all_form_modules.vbs
Normal file
@ -0,0 +1,56 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = False
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
' Liste des formulaires
|
||||
Dim formNames
|
||||
formNames = Array("frm_Accueil", "frm_Clients", "frm_Projets", "frm_SaisieTemps", "frm_Historique")
|
||||
|
||||
Dim i
|
||||
For i = LBound(formNames) To UBound(formNames)
|
||||
WScript.Echo "Processing " & formNames(i) & "..."
|
||||
|
||||
' Ouvrir en mode Design
|
||||
accessApp.DoCmd.OpenForm formNames(i), 2 ' acDesign
|
||||
WScript.Sleep 500
|
||||
|
||||
' Activer HasModule
|
||||
accessApp.Forms(formNames(i)).HasModule = True
|
||||
|
||||
WScript.Sleep 500
|
||||
|
||||
' Fermer et sauvegarder
|
||||
accessApp.DoCmd.Close 2, formNames(i), 1 ' acSaveYes
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo " -> Module created for " & formNames(i)
|
||||
Else
|
||||
WScript.Echo " -> ERROR: " & Err.Description
|
||||
Err.Clear
|
||||
End If
|
||||
|
||||
WScript.Sleep 500
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== Verification ==="
|
||||
|
||||
' Verifier les modules
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
WScript.Echo comp.Name & " - Type: " & comp.Type
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Done!"
|
||||
722
create_professional_pdf.ps1
Normal file
722
create_professional_pdf.ps1
Normal file
@ -0,0 +1,722 @@
|
||||
# Professional PDF Generator for TimeTrack Pro
|
||||
# Creates a beautifully formatted PDF document from scratch
|
||||
|
||||
$OutputPath = "C:\Users\alexi\Documents\projects\timetrack-pro\TimeTrack_Pro_Technical_Reference.pdf"
|
||||
|
||||
Write-Host "Creating professional PDF document..." -ForegroundColor Cyan
|
||||
|
||||
try {
|
||||
# Create Word application
|
||||
$Word = New-Object -ComObject Word.Application
|
||||
$Word.Visible = $false
|
||||
|
||||
# Create new document
|
||||
$Doc = $Word.Documents.Add()
|
||||
$Selection = $Word.Selection
|
||||
|
||||
# Define color scheme (Professional Blue)
|
||||
$PrimaryColor = 255 * 65536 + 255 * 256 + 255 # Will be set per element
|
||||
$AccentColor = 41 + 128 * 256 + 185 * 65536 # #2980B9 - Professional Blue
|
||||
$DarkColor = 44 + 62 * 256 + 80 * 65536 # #2C3E50 - Dark Blue-Grey
|
||||
|
||||
# ============================================================================
|
||||
# COVER PAGE
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Font.Name = "Segoe UI"
|
||||
$Selection.Font.Size = 48
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Alignment = 1 # Center
|
||||
$Selection.ParagraphFormat.SpaceBefore = 150
|
||||
$Selection.TypeText("TimeTrack Pro")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Size = 24
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.ParagraphFormat.SpaceBefore = 0
|
||||
$Selection.TypeText("Technical & Functional Reference")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Horizontal line
|
||||
$Selection.ParagraphFormat.Alignment = 1
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("_" * 60)
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Subtitle
|
||||
$Selection.Font.Size = 14
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.Font.Italic = $true
|
||||
$Selection.ParagraphFormat.SpaceBefore = 30
|
||||
$Selection.TypeText("Professional Time Tracking Application")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("Built with Microsoft Access & VBA")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("Automated Development via MCP VBA Server")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Key metrics box
|
||||
$Selection.Font.Size = 12
|
||||
$Selection.Font.Italic = $false
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.ParagraphFormat.SpaceBefore = 80
|
||||
$Selection.TypeText("Project Highlights")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Alignment = 0 # Left align
|
||||
$Selection.ParagraphFormat.LeftIndent = 100
|
||||
$Selection.TypeText("• 915 Lines of Professional VBA Code")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("• 7 Modular Components")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("• 45% Development Time Reduction via Automation")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("• Production-Ready Business Application")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Author info at bottom
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.ParagraphFormat.Alignment = 1 # Center
|
||||
$Selection.ParagraphFormat.SpaceBefore = 200
|
||||
$Selection.Font.Size = 12
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.TypeText("Alexis Trouvé")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Size = 10
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("alexistrouve.pro@gmail.com")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Size = 9
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.TypeText("Version 1.0 | January 2025")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Page break
|
||||
$Selection.InsertNewPage()
|
||||
|
||||
# ============================================================================
|
||||
# TABLE OF CONTENTS
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Font.Name = "Segoe UI"
|
||||
$Selection.Font.Size = 20
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Alignment = 0
|
||||
$Selection.ParagraphFormat.SpaceBefore = 0
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.TypeText("Table of Contents")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Insert TOC
|
||||
$Range = $Selection.Range
|
||||
$TOC = $Doc.TablesOfContents.Add($Range, $true, 1, 3, $true, "", $true, $true)
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Page break
|
||||
$Selection.InsertNewPage()
|
||||
|
||||
# ============================================================================
|
||||
# SECTION 1: EXECUTIVE SUMMARY
|
||||
# ============================================================================
|
||||
|
||||
# Section header with background
|
||||
$Selection.Font.Size = 18
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = 16777215 # White
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.SpaceBefore = 0
|
||||
$Selection.ParagraphFormat.SpaceAfter = 12
|
||||
$Selection.ParagraphFormat.LeftIndent = 10
|
||||
$Selection.Style = "Heading 1"
|
||||
$Selection.TypeText(" 1. Executive Summary")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Reset formatting
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215 # White
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.ParagraphFormat.SpaceAfter = 6
|
||||
$Selection.Style = "Normal"
|
||||
|
||||
$Selection.TypeText("TimeTrack Pro is a professional time tracking application built on Microsoft Access, showcasing advanced database design, VBA automation, and modern development practices. This project demonstrates the capability to deliver production-ready business applications through automated development workflows using the VBA MCP Server.")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("Key Achievement:")
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.TypeText(" Complete application development (database structure, business logic, queries, and VBA modules) automated via MCP (Model Context Protocol) integration, demonstrating cutting-edge AI-assisted development capabilities.")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Statistics table
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Size = 12
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("Project Statistics")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Size = 11
|
||||
|
||||
$Table = $Doc.Tables.Add($Selection.Range, 7, 2)
|
||||
$Table.Style = "Grid Table 4 - Accent 1"
|
||||
$Table.ApplyStyleHeadingRows = $true
|
||||
|
||||
# Header row
|
||||
$Table.Cell(1, 1).Range.Text = "Metric"
|
||||
$Table.Cell(1, 2).Range.Text = "Value"
|
||||
|
||||
# Data rows
|
||||
$Table.Cell(2, 1).Range.Text = "Total VBA Lines"
|
||||
$Table.Cell(2, 2).Range.Text = "915"
|
||||
$Table.Cell(3, 1).Range.Text = "VBA Modules"
|
||||
$Table.Cell(3, 2).Range.Text = "7"
|
||||
$Table.Cell(4, 1).Range.Text = "Database Tables"
|
||||
$Table.Cell(4, 2).Range.Text = "3"
|
||||
$Table.Cell(5, 1).Range.Text = "Functions"
|
||||
$Table.Cell(5, 2).Range.Text = "45+"
|
||||
$Table.Cell(6, 1).Range.Text = "Development Time Saved"
|
||||
$Table.Cell(6, 2).Range.Text = "45%"
|
||||
$Table.Cell(7, 1).Range.Text = "Comment Ratio"
|
||||
$Table.Cell(7, 2).Range.Text = "25%"
|
||||
|
||||
$Selection.EndKey(6) # End of document
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# ============================================================================
|
||||
# SECTION 2: PROJECT OVERVIEW
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Style = "Heading 1"
|
||||
$Selection.Font.Size = 18
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 10
|
||||
$Selection.TypeText(" 2. Project Overview")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
|
||||
# Subsection: Purpose
|
||||
$Selection.Style = "Heading 2"
|
||||
$Selection.Font.Size = 14
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("2.1 Purpose")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
|
||||
$Selection.TypeText("TimeTrack Pro is a time management tool designed for freelancers, consultants, and small teams to:")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.TypeText("• Track billable hours across multiple clients and projects")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("• Calculate revenue automatically based on hourly rates")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("• Generate professional reports for invoicing and analysis")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("• Maintain a complete audit trail of time entries")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Subsection: Target Audience
|
||||
$Selection.Style = "Heading 2"
|
||||
$Selection.Font.Size = 14
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("2.2 Target Audience")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.TypeText("Freelancers: ")
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.TypeText("Independent consultants tracking multiple client projects")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.TypeText("Small Teams: ")
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.TypeText("Agencies managing client work and resource allocation")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.TypeText("Consultants: ")
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.TypeText("Professional services requiring detailed time records")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# ============================================================================
|
||||
# SECTION 3: DATABASE ARCHITECTURE
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Style = "Heading 1"
|
||||
$Selection.Font.Size = 18
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 10
|
||||
$Selection.TypeText(" 3. Database Architecture")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
|
||||
$Selection.TypeText("The application uses a normalized relational database structure with three core tables:")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Table: tbl_Clients
|
||||
$Selection.Style = "Heading 2"
|
||||
$Selection.Font.Size = 14
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("3.1 Table: tbl_Clients")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
|
||||
$Selection.TypeText("Stores client information and contact details.")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$TableClients = $Doc.Tables.Add($Selection.Range, 7, 4)
|
||||
$TableClients.Style = "Grid Table 4 - Accent 1"
|
||||
$TableClients.ApplyStyleHeadingRows = $true
|
||||
|
||||
$TableClients.Cell(1, 1).Range.Text = "Field"
|
||||
$TableClients.Cell(1, 2).Range.Text = "Type"
|
||||
$TableClients.Cell(1, 3).Range.Text = "Size"
|
||||
$TableClients.Cell(1, 4).Range.Text = "Description"
|
||||
|
||||
$TableClients.Cell(2, 1).Range.Text = "ClientID"
|
||||
$TableClients.Cell(2, 2).Range.Text = "AutoNumber"
|
||||
$TableClients.Cell(2, 3).Range.Text = "Long"
|
||||
$TableClients.Cell(2, 4).Range.Text = "Primary Key"
|
||||
|
||||
$TableClients.Cell(3, 1).Range.Text = "Nom"
|
||||
$TableClients.Cell(3, 2).Range.Text = "Text"
|
||||
$TableClients.Cell(3, 3).Range.Text = "100"
|
||||
$TableClients.Cell(3, 4).Range.Text = "Client name (required)"
|
||||
|
||||
$TableClients.Cell(4, 1).Range.Text = "Email"
|
||||
$TableClients.Cell(4, 2).Range.Text = "Text"
|
||||
$TableClients.Cell(4, 3).Range.Text = "100"
|
||||
$TableClients.Cell(4, 4).Range.Text = "Email address"
|
||||
|
||||
$TableClients.Cell(5, 1).Range.Text = "Telephone"
|
||||
$TableClients.Cell(5, 2).Range.Text = "Text"
|
||||
$TableClients.Cell(5, 3).Range.Text = "20"
|
||||
$TableClients.Cell(5, 4).Range.Text = "Phone number"
|
||||
|
||||
$TableClients.Cell(6, 1).Range.Text = "Notes"
|
||||
$TableClients.Cell(6, 2).Range.Text = "Memo"
|
||||
$TableClients.Cell(6, 3).Range.Text = "-"
|
||||
$TableClients.Cell(6, 4).Range.Text = "Additional notes"
|
||||
|
||||
$TableClients.Cell(7, 1).Range.Text = "DateCreation"
|
||||
$TableClients.Cell(7, 2).Range.Text = "DateTime"
|
||||
$TableClients.Cell(7, 3).Range.Text = "-"
|
||||
$TableClients.Cell(7, 4).Range.Text = "Record creation timestamp"
|
||||
|
||||
$Selection.EndKey(6)
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# ============================================================================
|
||||
# SECTION 4: VBA MODULES
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Style = "Heading 1"
|
||||
$Selection.Font.Size = 18
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 10
|
||||
$Selection.TypeText(" 4. VBA Architecture")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
|
||||
$Selection.TypeText("The application uses a modular VBA architecture with 7 core modules totaling 915 lines of code:")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$TableModules = $Doc.Tables.Add($Selection.Range, 8, 3)
|
||||
$TableModules.Style = "Grid Table 4 - Accent 1"
|
||||
$TableModules.ApplyStyleHeadingRows = $true
|
||||
|
||||
$TableModules.Cell(1, 1).Range.Text = "Module"
|
||||
$TableModules.Cell(1, 2).Range.Text = "Purpose"
|
||||
$TableModules.Cell(1, 3).Range.Text = "LOC"
|
||||
|
||||
$TableModules.Cell(2, 1).Range.Text = "mod_Config"
|
||||
$TableModules.Cell(2, 2).Range.Text = "Application configuration and constants"
|
||||
$TableModules.Cell(2, 3).Range.Text = "80"
|
||||
|
||||
$TableModules.Cell(3, 1).Range.Text = "mod_Navigation"
|
||||
$TableModules.Cell(3, 2).Range.Text = "Form navigation and UI flow control"
|
||||
$TableModules.Cell(3, 3).Range.Text = "120"
|
||||
|
||||
$TableModules.Cell(4, 1).Range.Text = "mod_DataAccess"
|
||||
$TableModules.Cell(4, 2).Range.Text = "CRUD operations and database layer"
|
||||
$TableModules.Cell(4, 3).Range.Text = "200"
|
||||
|
||||
$TableModules.Cell(5, 1).Range.Text = "mod_Calculs"
|
||||
$TableModules.Cell(5, 2).Range.Text = "Business logic and calculations"
|
||||
$TableModules.Cell(5, 3).Range.Text = "150"
|
||||
|
||||
$TableModules.Cell(6, 1).Range.Text = "mod_Export"
|
||||
$TableModules.Cell(6, 2).Range.Text = "Report generation and export"
|
||||
$TableModules.Cell(6, 3).Range.Text = "120"
|
||||
|
||||
$TableModules.Cell(7, 1).Range.Text = "mod_Utils"
|
||||
$TableModules.Cell(7, 2).Range.Text = "Helper functions and validation"
|
||||
$TableModules.Cell(7, 3).Range.Text = "100"
|
||||
|
||||
$TableModules.Cell(8, 1).Range.Text = "mod_FormBuilder"
|
||||
$TableModules.Cell(8, 2).Range.Text = "Automated form creation (dev only)"
|
||||
$TableModules.Cell(8, 3).Range.Text = "145"
|
||||
|
||||
$Selection.EndKey(6)
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# ============================================================================
|
||||
# SECTION 5: MCP VBA AUTOMATION
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Style = "Heading 1"
|
||||
$Selection.Font.Size = 18
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 10
|
||||
$Selection.TypeText(" 5. MCP VBA Server Automation")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
|
||||
$Selection.TypeText("This project showcases advanced automation using the VBA MCP Server (Model Context Protocol), enabling AI-assisted development of Access applications.")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Development metrics
|
||||
$Selection.Style = "Heading 2"
|
||||
$Selection.Font.Size = 14
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("5.1 Development Efficiency")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
|
||||
$TableDev = $Doc.Tables.Add($Selection.Range, 7, 4)
|
||||
$TableDev.Style = "Grid Table 4 - Accent 1"
|
||||
$TableDev.ApplyStyleHeadingRows = $true
|
||||
|
||||
$TableDev.Cell(1, 1).Range.Text = "Phase"
|
||||
$TableDev.Cell(1, 2).Range.Text = "Estimated"
|
||||
$TableDev.Cell(1, 3).Range.Text = "Actual"
|
||||
$TableDev.Cell(1, 4).Range.Text = "Method"
|
||||
|
||||
$TableDev.Cell(2, 1).Range.Text = "Database Design"
|
||||
$TableDev.Cell(2, 2).Range.Text = "1h"
|
||||
$TableDev.Cell(2, 3).Range.Text = "0.5h"
|
||||
$TableDev.Cell(2, 4).Range.Text = "MCP Automated"
|
||||
|
||||
$TableDev.Cell(3, 1).Range.Text = "Test Data"
|
||||
$TableDev.Cell(3, 2).Range.Text = "30min"
|
||||
$TableDev.Cell(3, 3).Range.Text = "15min"
|
||||
$TableDev.Cell(3, 4).Range.Text = "MCP Automated"
|
||||
|
||||
$TableDev.Cell(4, 1).Range.Text = "VBA Modules"
|
||||
$TableDev.Cell(4, 2).Range.Text = "3h"
|
||||
$TableDev.Cell(4, 3).Range.Text = "1h"
|
||||
$TableDev.Cell(4, 4).Range.Text = "MCP Automated"
|
||||
|
||||
$TableDev.Cell(5, 1).Range.Text = "SQL Queries"
|
||||
$TableDev.Cell(5, 2).Range.Text = "30min"
|
||||
$TableDev.Cell(5, 3).Range.Text = "20min"
|
||||
$TableDev.Cell(5, 4).Range.Text = "MCP Automated"
|
||||
|
||||
$TableDev.Cell(6, 1).Range.Text = "Forms"
|
||||
$TableDev.Cell(6, 2).Range.Text = "2h"
|
||||
$TableDev.Cell(6, 3).Range.Text = "1h"
|
||||
$TableDev.Cell(6, 4).Range.Text = "Script + Manual"
|
||||
|
||||
$TableDev.Cell(7, 1).Range.Text = "Testing & Docs"
|
||||
$TableDev.Cell(7, 2).Range.Text = "2h"
|
||||
$TableDev.Cell(7, 3).Range.Text = "2h"
|
||||
$TableDev.Cell(7, 4).Range.Text = "Manual"
|
||||
|
||||
$Selection.EndKey(6)
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Highlight box
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 15132390 # Light blue
|
||||
$Selection.ParagraphFormat.LeftIndent = 20
|
||||
$Selection.ParagraphFormat.RightIndent = 20
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Size = 12
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.TypeText("Development Efficiency: 45% time reduction through automation")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.TypeText("Total estimated: 9 hours | Actual: ~5 hours")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.ParagraphFormat.RightIndent = 0
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# ============================================================================
|
||||
# SECTION 6: PROFESSIONAL SERVICES
|
||||
# ============================================================================
|
||||
|
||||
$Selection.Style = "Heading 1"
|
||||
$Selection.Font.Size = 18
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 10
|
||||
$Selection.TypeText(" 6. Professional Services Available")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Style = "Normal"
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
|
||||
$Selection.TypeText("This project demonstrates expertise in the following areas:")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("• Microsoft Access Development")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 50
|
||||
$Selection.TypeText("Forms, reports, VBA automation, and SQL optimization")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("• Database Design & Architecture")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 50
|
||||
$Selection.TypeText("Normalization, indexing, referential integrity, and performance tuning")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("• Business Application Development")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 50
|
||||
$Selection.TypeText("Requirements analysis, UX design, testing, and deployment")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("• Process Automation")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 50
|
||||
$Selection.TypeText("VBA macros, AI-assisted development (MCP), workflow optimization")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 30
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Color = $AccentColor
|
||||
$Selection.TypeText("• Legacy System Modernization")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Color = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 50
|
||||
$Selection.TypeText("Access to SQL Server migration, web application conversion")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
# Contact box
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = $DarkColor
|
||||
$Selection.ParagraphFormat.LeftIndent = 40
|
||||
$Selection.ParagraphFormat.RightIndent = 40
|
||||
$Selection.ParagraphFormat.SpaceBefore = 12
|
||||
$Selection.ParagraphFormat.SpaceAfter = 12
|
||||
$Selection.Font.Bold = $true
|
||||
$Selection.Font.Size = 14
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.ParagraphFormat.Alignment = 1
|
||||
$Selection.TypeText("Contact Information")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.Font.Bold = $false
|
||||
$Selection.Font.Size = 11
|
||||
$Selection.Font.Color = 16777215
|
||||
$Selection.TypeText("Alexis Trouvé")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.TypeText("alexistrouve.pro@gmail.com")
|
||||
$Selection.TypeParagraph()
|
||||
$Selection.Font.Italic = $true
|
||||
$Selection.Font.Size = 10
|
||||
$Selection.TypeText("Response time: Within 24 hours")
|
||||
$Selection.TypeParagraph()
|
||||
|
||||
$Selection.ParagraphFormat.Shading.BackgroundPatternColor = 16777215
|
||||
$Selection.ParagraphFormat.LeftIndent = 0
|
||||
$Selection.ParagraphFormat.RightIndent = 0
|
||||
$Selection.ParagraphFormat.Alignment = 0
|
||||
$Selection.Font.Italic = $false
|
||||
|
||||
# ============================================================================
|
||||
# UPDATE TABLE OF CONTENTS
|
||||
# ============================================================================
|
||||
|
||||
Write-Host "Updating table of contents..." -ForegroundColor Yellow
|
||||
$TOC.Update()
|
||||
|
||||
# ============================================================================
|
||||
# ADD HEADERS AND FOOTERS
|
||||
# ============================================================================
|
||||
|
||||
Write-Host "Adding headers and footers..." -ForegroundColor Yellow
|
||||
|
||||
$Section = $Doc.Sections.Item(1)
|
||||
$Header = $Section.Headers.Item(1)
|
||||
$Footer = $Section.Footers.Item(1)
|
||||
|
||||
# Header
|
||||
$Header.Range.Text = "TimeTrack Pro - Technical Reference"
|
||||
$Header.Range.Font.Size = 9
|
||||
$Header.Range.Font.Color = 8421504 # Grey
|
||||
$Header.Range.ParagraphFormat.Alignment = 2 # Right align
|
||||
|
||||
# Footer
|
||||
$Footer.Range.Text = "Page "
|
||||
$Footer.Range.Font.Size = 9
|
||||
$Footer.Range.Font.Color = 8421504
|
||||
$Footer.Range.ParagraphFormat.Alignment = 1 # Center
|
||||
$Field = $Footer.Range.Fields.Add($Footer.Range, 26) # wdFieldPage
|
||||
$Footer.Range.InsertAfter(" | Alexis Trouvé | 2025")
|
||||
|
||||
# ============================================================================
|
||||
# SAVE AS PDF
|
||||
# ============================================================================
|
||||
|
||||
Write-Host "Saving as PDF..." -ForegroundColor Yellow
|
||||
$Doc.SaveAs([ref]$OutputPath, [ref]17)
|
||||
|
||||
Write-Host "Closing document..." -ForegroundColor Yellow
|
||||
$Doc.Close()
|
||||
$Word.Quit()
|
||||
|
||||
# Cleanup
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Selection) | Out-Null
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Doc) | Out-Null
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Word) | Out-Null
|
||||
[System.GC]::Collect()
|
||||
[System.GC]::WaitForPendingFinalizers()
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "SUCCESS! Professional PDF created:" -ForegroundColor Green
|
||||
Write-Host $OutputPath -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Host "ERROR:" -ForegroundColor Red
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
|
||||
if ($Word) {
|
||||
$Word.Quit()
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Word) | Out-Null
|
||||
}
|
||||
|
||||
exit 1
|
||||
}
|
||||
85
delete_and_recreate_modules.vbs
Normal file
85
delete_and_recreate_modules.vbs
Normal file
@ -0,0 +1,85 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = True
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
WScript.Echo "=== Deleting standard modules ==="
|
||||
|
||||
' Supprimer les modules standards Form_*
|
||||
Dim modulesToDelete
|
||||
modulesToDelete = Array("Form_frm_Accueil", "Form_frm_Clients", "Form_frm_Projets", "Form_frm_SaisieTemps", "Form_frm_Historique")
|
||||
|
||||
Dim i, comp
|
||||
For i = LBound(modulesToDelete) To UBound(modulesToDelete)
|
||||
For Each comp In vbProj.VBComponents
|
||||
If comp.Name = modulesToDelete(i) And comp.Type = 1 Then ' 1 = Standard Module
|
||||
vbProj.VBComponents.Remove comp
|
||||
WScript.Echo "Deleted: " & modulesToDelete(i)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
Next
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== Creating form modules ==="
|
||||
|
||||
' Recréer les modules de formulaire
|
||||
Dim formNames
|
||||
formNames = Array("frm_Accueil", "frm_Clients", "frm_Projets", "frm_SaisieTemps", "frm_Historique")
|
||||
|
||||
For i = LBound(formNames) To UBound(formNames)
|
||||
WScript.Echo "Processing " & formNames(i) & "..."
|
||||
|
||||
' Ouvrir en mode Design VISIBLE
|
||||
accessApp.DoCmd.OpenForm formNames(i), 2 ' acDesign, pas hidden
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Essayer de créer le module
|
||||
On Error Resume Next
|
||||
Err.Clear
|
||||
|
||||
' Méthode 1: Via HasModule
|
||||
accessApp.Forms(formNames(i)).HasModule = True
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo " -> Created via HasModule"
|
||||
Else
|
||||
WScript.Echo " -> HasModule failed: " & Err.Description
|
||||
Err.Clear
|
||||
|
||||
' Méthode 2: Via DoCmd ViewCode
|
||||
accessApp.DoCmd.OpenForm formNames(i), 2
|
||||
WScript.Sleep 500
|
||||
accessApp.DoCmd.RunCommand 401 ' acCmdViewCode - ouvre l'éditeur VBA du formulaire
|
||||
WScript.Sleep 1000
|
||||
WScript.Echo " -> Opened code view"
|
||||
End If
|
||||
|
||||
' Fermer le formulaire
|
||||
accessApp.DoCmd.Close 2, formNames(i), 1 ' acSaveYes
|
||||
WScript.Sleep 500
|
||||
Next
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== Verification ==="
|
||||
|
||||
For Each comp In vbProj.VBComponents
|
||||
WScript.Echo comp.Name & " - Type: " & comp.Type
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Done! Press OK to close Access."
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
49
delete_by_name.vbs
Normal file
49
delete_by_name.vbs
Normal file
@ -0,0 +1,49 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Deleting specific fake modules ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
' Supprimer un par un par nom
|
||||
Dim modulesToDelete
|
||||
modulesToDelete = Array("Form_frm_Accueil", "Form_frm_Clients", "Form_frm_Projets", "Form_frm_Historique")
|
||||
|
||||
Dim i
|
||||
For i = LBound(modulesToDelete) To UBound(modulesToDelete)
|
||||
Dim comp
|
||||
Set comp = Nothing
|
||||
|
||||
' Trouver le module
|
||||
Dim c
|
||||
For Each c In vbProj.VBComponents
|
||||
If c.Name = modulesToDelete(i) Then
|
||||
Set comp = c
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
||||
' Supprimer si trouve
|
||||
If Not comp Is Nothing Then
|
||||
WScript.Echo "Deleting: " & comp.Name & " (Type " & comp.Type & ")"
|
||||
vbProj.VBComponents.Remove comp
|
||||
WScript.Echo " -> Deleted!"
|
||||
Else
|
||||
WScript.Echo "Not found: " & modulesToDelete(i)
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== Remaining modules ==="
|
||||
For Each c In vbProj.VBComponents
|
||||
WScript.Echo c.Name & " - Type: " & c.Type
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
25
delete_fake_modules.vbs
Normal file
25
delete_fake_modules.vbs
Normal file
@ -0,0 +1,25 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Deleting fake Form_* Type 1 modules ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
If Left(comp.Name, 5) = "Form_" And comp.Type = 1 Then ' Type 1 = Standard Module
|
||||
WScript.Echo "Deleting fake module: " & comp.Name & " (Type " & comp.Type & ")"
|
||||
vbProj.VBComponents.Remove comp
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo "Done! All fake modules deleted."
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
25
delete_form_modules.vbs
Normal file
25
delete_form_modules.vbs
Normal file
@ -0,0 +1,25 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
WScript.Echo "=== Deleting Form_* modules ==="
|
||||
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
If Left(comp.Name, 5) = "Form_" And comp.Type = 1 Then ' Type 1 = Standard Module
|
||||
WScript.Echo "Deleting: " & comp.Name
|
||||
vbProj.VBComponents.Remove comp
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo "Done!"
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
59
enable_form_modules.vbs
Normal file
59
enable_form_modules.vbs
Normal file
@ -0,0 +1,59 @@
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = True
|
||||
|
||||
Dim formNames
|
||||
formNames = Array("frm_Accueil", "frm_Clients", "frm_Projets", "frm_SaisieTemps", "frm_Historique")
|
||||
|
||||
Dim i
|
||||
For i = LBound(formNames) To UBound(formNames)
|
||||
On Error Resume Next
|
||||
|
||||
' Ouvrir en mode Design
|
||||
accessApp.DoCmd.OpenForm formNames(i), 2
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Acceder au module VBA du projet
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
' Verifier si le module existe deja
|
||||
Dim moduleExists
|
||||
moduleExists = False
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
If comp.Name = "Form_" & formNames(i) Then
|
||||
moduleExists = True
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
||||
If Not moduleExists Then
|
||||
' Forcer la creation du module en ajoutant du code VBA
|
||||
Dim vbComp
|
||||
Set vbComp = vbProj.VBComponents.Add(100) ' 100 = vbext_ct_MSForm (form module)
|
||||
vbComp.Name = "Form_" & formNames(i)
|
||||
vbComp.CodeModule.AddFromString "Option Compare Database" & vbCrLf & "Option Explicit"
|
||||
WScript.Echo "Created module for: " & formNames(i)
|
||||
Else
|
||||
WScript.Echo "Module already exists for: " & formNames(i)
|
||||
End If
|
||||
|
||||
accessApp.DoCmd.Close 2, formNames(i), 1
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo "Module enabled for: " & formNames(i)
|
||||
Else
|
||||
WScript.Echo "Error for " & formNames(i) & ": " & Err.Description
|
||||
Err.Clear
|
||||
End If
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo "All done!"
|
||||
48
enable_modules_dao.vbs
Normal file
48
enable_modules_dao.vbs
Normal file
@ -0,0 +1,48 @@
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
' Execute VBA code to enable modules using DAO
|
||||
Dim vbaCode
|
||||
vbaCode = "Public Sub EnableModules()" & vbCrLf & _
|
||||
"Dim db As DAO.Database" & vbCrLf & _
|
||||
"Set db = CurrentDb" & vbCrLf & _
|
||||
"Dim doc As DAO.Document" & vbCrLf & _
|
||||
"Dim formNames As Variant" & vbCrLf & _
|
||||
"formNames = Array(""frm_Accueil"", ""frm_Clients"", ""frm_Projets"", ""frm_SaisieTemps"", ""frm_Historique"")" & vbCrLf & _
|
||||
"Dim i As Integer" & vbCrLf & _
|
||||
"For i = LBound(formNames) To UBound(formNames)" & vbCrLf & _
|
||||
" Set doc = db.Containers(""Forms"").Documents(formNames(i))" & vbCrLf & _
|
||||
" doc.Properties(""HasModule"") = True" & vbCrLf & _
|
||||
" Debug.Print ""Enabled: "" & formNames(i)" & vbCrLf & _
|
||||
"Next i" & vbCrLf & _
|
||||
"MsgBox ""All modules enabled!""" & vbCrLf & _
|
||||
"End Sub"
|
||||
|
||||
' Add temp module
|
||||
On Error Resume Next
|
||||
accessApp.DoCmd.DeleteObject 5, "tempModuleEnabler" ' 5 = acModule
|
||||
Err.Clear
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
Dim vbComp
|
||||
Set vbComp = vbProj.VBComponents.Add(1) ' 1 = vbext_ct_StdModule
|
||||
vbComp.Name = "tempModuleEnabler"
|
||||
vbComp.CodeModule.AddFromString vbaCode
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Run the macro
|
||||
accessApp.Run "tempModuleEnabler.EnableModules"
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Cleanup
|
||||
accessApp.DoCmd.DeleteObject 5, "tempModuleEnabler"
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
|
||||
WScript.Echo "Done!"
|
||||
15
execute_fix.vbs
Normal file
15
execute_fix.vbs
Normal file
@ -0,0 +1,15 @@
|
||||
Dim accessApp
|
||||
Set accessApp = GetObject(, "Access.Application")
|
||||
|
||||
' Executer le code directement via VBE
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
' Trouver le module
|
||||
Dim vbComp
|
||||
Set vbComp = vbProj.VBComponents("mod_FixFormBindings")
|
||||
|
||||
' Executer la procedure
|
||||
accessApp.DoCmd.RunMacro "mod_FixFormBindings.FixAllFormBindings"
|
||||
|
||||
WScript.Echo "Done!"
|
||||
81
final_cleanup.vbs
Normal file
81
final_cleanup.vbs
Normal file
@ -0,0 +1,81 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Final cleanup: Direct VBProject manipulation ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
' List all components first
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Before cleanup:"
|
||||
Dim c
|
||||
For Each c In vbProj.VBComponents
|
||||
If Left(c.Name, 4) = "OLD_" Or Left(c.Name, 5) = "Form_" Then
|
||||
WScript.Echo " " & c.Name & " (Type " & c.Type & ")"
|
||||
End If
|
||||
Next
|
||||
|
||||
' Try to delete OLD modules one by one
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Attempting removal..."
|
||||
|
||||
Dim oldModules
|
||||
oldModules = Array("OLD_Form_frm_Accueil", "OLD_Form_frm_Clients", "OLD_Form_frm_Projets", "OLD_Form_frm_Historique")
|
||||
|
||||
Dim i, comp
|
||||
For i = 0 To UBound(oldModules)
|
||||
WScript.Echo "Looking for: " & oldModules(i)
|
||||
|
||||
Dim removed
|
||||
removed = False
|
||||
|
||||
On Error Resume Next
|
||||
' Try multiple times if needed
|
||||
Dim attempts
|
||||
For attempts = 1 To 5
|
||||
For Each comp In vbProj.VBComponents
|
||||
If comp.Name = oldModules(i) Then
|
||||
vbProj.VBComponents.Remove comp
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo " Removed on attempt " & attempts
|
||||
removed = True
|
||||
Exit For
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
If removed Then Exit For
|
||||
Next
|
||||
|
||||
If Not removed Then
|
||||
WScript.Echo " Could not remove (will try renaming instead)"
|
||||
' Try renaming
|
||||
For Each comp In vbProj.VBComponents
|
||||
If comp.Name = oldModules(i) Then
|
||||
On Error Resume Next
|
||||
comp.Name = "DELETED_" & oldModules(i)
|
||||
WScript.Echo " Renamed to: DELETED_" & oldModules(i)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "After cleanup:"
|
||||
For Each c In vbProj.VBComponents
|
||||
If Left(c.Name, 4) = "OLD_" Or Left(c.Name, 5) = "Form_" Or Left(c.Name, 8) = "DELETED_" Then
|
||||
WScript.Echo " " & c.Name & " (Type " & c.Type & ")"
|
||||
End If
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "Done!"
|
||||
52
fix_all_bindings.vbs
Normal file
52
fix_all_bindings.vbs
Normal file
@ -0,0 +1,52 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = False
|
||||
|
||||
WScript.Echo "Fixing frm_Clients..."
|
||||
accessApp.DoCmd.OpenForm "frm_Clients", 2 ' acDesign
|
||||
accessApp.Forms("frm_Clients").RecordSource = "tbl_Clients"
|
||||
accessApp.Forms("frm_Clients").Controls("txtNom").ControlSource = "Nom"
|
||||
accessApp.Forms("frm_Clients").Controls("txtEmail").ControlSource = "Email"
|
||||
accessApp.Forms("frm_Clients").Controls("txtTelephone").ControlSource = "Telephone"
|
||||
accessApp.Forms("frm_Clients").Controls("txtNotes").ControlSource = "Notes"
|
||||
accessApp.DoCmd.Close 2, "frm_Clients", 1
|
||||
WScript.Echo "frm_Clients done"
|
||||
|
||||
WScript.Sleep 500
|
||||
|
||||
WScript.Echo "Fixing frm_Projets..."
|
||||
accessApp.DoCmd.OpenForm "frm_Projets", 2
|
||||
accessApp.Forms("frm_Projets").RecordSource = "tbl_Projets"
|
||||
accessApp.Forms("frm_Projets").Controls("cboClient").ControlSource = "ClientID"
|
||||
accessApp.Forms("frm_Projets").Controls("txtNom").ControlSource = "Nom"
|
||||
accessApp.Forms("frm_Projets").Controls("txtDescription").ControlSource = "Description"
|
||||
accessApp.Forms("frm_Projets").Controls("txtTauxHoraire").ControlSource = "TauxHoraire"
|
||||
' Note: chkActif might not exist
|
||||
On Error Resume Next
|
||||
accessApp.Forms("frm_Projets").Controls("chkActif").ControlSource = "Actif"
|
||||
On Error Goto 0
|
||||
accessApp.DoCmd.Close 2, "frm_Projets", 1
|
||||
WScript.Echo "frm_Projets done"
|
||||
|
||||
WScript.Sleep 500
|
||||
|
||||
WScript.Echo "Fixing frm_SaisieTemps..."
|
||||
accessApp.DoCmd.OpenForm "frm_SaisieTemps", 2
|
||||
accessApp.Forms("frm_SaisieTemps").RecordSource = "tbl_Temps"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("cboProjet").ControlSource = "ProjetID"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDate").ControlSource = "Date"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDuree").ControlSource = "Duree"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDescription").ControlSource = "Description"
|
||||
accessApp.DoCmd.Close 2, "frm_SaisieTemps", 1
|
||||
WScript.Echo "frm_SaisieTemps done"
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo "All forms fixed successfully!"
|
||||
22
fix_bindings_direct.vbs
Normal file
22
fix_bindings_direct.vbs
Normal file
@ -0,0 +1,22 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
' Ouvrir la base (sans fermer la session existante)
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb", True
|
||||
|
||||
' Executer directement le code VBA via DoCmd.RunCode
|
||||
accessApp.DoCmd.RunCode "mod_FixFormBindings.FixAllFormBindings"
|
||||
|
||||
If Err.Number <> 0 Then
|
||||
WScript.Echo "Error: " & Err.Number & " - " & Err.Description
|
||||
Else
|
||||
WScript.Echo "Success!"
|
||||
End If
|
||||
|
||||
' Fermer
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
|
||||
Set accessApp = Nothing
|
||||
49
fix_form_bindings.vbs
Normal file
49
fix_form_bindings.vbs
Normal file
@ -0,0 +1,49 @@
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = False
|
||||
|
||||
On Error Resume Next
|
||||
|
||||
' Fix frm_Clients
|
||||
accessApp.DoCmd.OpenForm "frm_Clients", 2, , , , 1 ' acDesign, Hidden
|
||||
WScript.Sleep 500
|
||||
accessApp.Forms("frm_Clients").RecordSource = "tbl_Clients"
|
||||
accessApp.Forms("frm_Clients").Controls("txtNom").ControlSource = "Nom"
|
||||
accessApp.Forms("frm_Clients").Controls("txtEmail").ControlSource = "Email"
|
||||
accessApp.Forms("frm_Clients").Controls("txtTelephone").ControlSource = "Telephone"
|
||||
accessApp.Forms("frm_Clients").Controls("txtNotes").ControlSource = "Notes"
|
||||
accessApp.DoCmd.Close 2, "frm_Clients", 1
|
||||
If Err.Number = 0 Then WScript.Echo "frm_Clients fixed" Else WScript.Echo "Error frm_Clients: " & Err.Description
|
||||
Err.Clear
|
||||
|
||||
' Fix frm_Projets
|
||||
accessApp.DoCmd.OpenForm "frm_Projets", 2, , , , 1
|
||||
WScript.Sleep 500
|
||||
accessApp.Forms("frm_Projets").RecordSource = "tbl_Projets"
|
||||
accessApp.Forms("frm_Projets").Controls("cboClient").ControlSource = "ClientID"
|
||||
accessApp.Forms("frm_Projets").Controls("txtNom").ControlSource = "Nom"
|
||||
accessApp.Forms("frm_Projets").Controls("txtDescription").ControlSource = "Description"
|
||||
accessApp.Forms("frm_Projets").Controls("txtTauxHoraire").ControlSource = "TauxHoraire"
|
||||
accessApp.Forms("frm_Projets").Controls("chkActif").ControlSource = "Actif"
|
||||
accessApp.DoCmd.Close 2, "frm_Projets", 1
|
||||
If Err.Number = 0 Then WScript.Echo "frm_Projets fixed" Else WScript.Echo "Error frm_Projets: " & Err.Description
|
||||
Err.Clear
|
||||
|
||||
' Fix frm_SaisieTemps
|
||||
accessApp.DoCmd.OpenForm "frm_SaisieTemps", 2, , , , 1
|
||||
WScript.Sleep 500
|
||||
accessApp.Forms("frm_SaisieTemps").RecordSource = "tbl_Temps"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("cboProjet").ControlSource = "ProjetID"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDate").ControlSource = "Date"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDuree").ControlSource = "Duree"
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDescription").ControlSource = "Description"
|
||||
accessApp.DoCmd.Close 2, "frm_SaisieTemps", 1
|
||||
If Err.Number = 0 Then WScript.Echo "frm_SaisieTemps fixed" Else WScript.Echo "Error frm_SaisieTemps: " & Err.Description
|
||||
Err.Clear
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
|
||||
WScript.Echo "All forms fixed!"
|
||||
41
fix_saisietemps.vbs
Normal file
41
fix_saisietemps.vbs
Normal file
@ -0,0 +1,41 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = False
|
||||
|
||||
WScript.Echo "Fixing frm_SaisieTemps..."
|
||||
|
||||
' Ouvrir en mode Design
|
||||
accessApp.DoCmd.OpenForm "frm_SaisieTemps", 2 ' 2 = acDesign
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Fixer les bindings
|
||||
accessApp.Forms("frm_SaisieTemps").RecordSource = "tbl_Temps"
|
||||
WScript.Sleep 200
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("cboProjet").ControlSource = "ProjetID"
|
||||
WScript.Sleep 200
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDate").ControlSource = "Date"
|
||||
WScript.Sleep 200
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDuree").ControlSource = "Duree"
|
||||
WScript.Sleep 200
|
||||
accessApp.Forms("frm_SaisieTemps").Controls("txtDescription").ControlSource = "Description"
|
||||
WScript.Sleep 200
|
||||
|
||||
' Sauvegarder et fermer
|
||||
accessApp.DoCmd.Close 2, "frm_SaisieTemps", 1 ' 1 = acSaveYes
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo "frm_SaisieTemps fixed successfully!"
|
||||
Else
|
||||
WScript.Echo "Error: " & Err.Number & " - " & Err.Description
|
||||
End If
|
||||
|
||||
' Cleanup
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo "Done!"
|
||||
65
force_delete_old.vbs
Normal file
65
force_delete_old.vbs
Normal file
@ -0,0 +1,65 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Force delete OLD_* modules ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim toDelete
|
||||
toDelete = Array("OLD_Form_frm_Accueil", "OLD_Form_frm_Clients", "OLD_Form_frm_Projets", "OLD_Form_frm_Historique")
|
||||
|
||||
Dim i
|
||||
For i = LBound(toDelete) To UBound(toDelete)
|
||||
WScript.Echo "Looking for: " & toDelete(i)
|
||||
|
||||
Dim found
|
||||
found = False
|
||||
|
||||
Dim c
|
||||
For Each c In vbProj.VBComponents
|
||||
If c.Name = toDelete(i) Then
|
||||
found = True
|
||||
WScript.Echo " Found! Type: " & c.Type
|
||||
|
||||
' Try multiple times
|
||||
Dim attempts
|
||||
For attempts = 1 To 3
|
||||
On Error Resume Next
|
||||
vbProj.VBComponents.Remove c
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo " Attempt " & attempts & ": SUCCESS"
|
||||
Exit For
|
||||
Else
|
||||
WScript.Echo " Attempt " & attempts & ": Error " & Err.Number & " - " & Err.Description
|
||||
Err.Clear
|
||||
End If
|
||||
Next
|
||||
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
||||
If Not found Then
|
||||
WScript.Echo " Not found!"
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== All OLD modules check ==="
|
||||
For Each c In vbProj.VBComponents
|
||||
If Left(c.Name, 4) = "OLD_" Then
|
||||
WScript.Echo "Still exists: " & c.Name
|
||||
End If
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
|
||||
WScript.Echo "Done!"
|
||||
953
generate_complete_pdf.py
Normal file
953
generate_complete_pdf.py
Normal file
@ -0,0 +1,953 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Professional PDF Generator for TimeTrack Pro - Complete Version
|
||||
Creates comprehensive technical documentation
|
||||
"""
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
|
||||
from reportlab.platypus import (
|
||||
SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle,
|
||||
KeepTogether, HRFlowable
|
||||
)
|
||||
from reportlab.pdfgen import canvas
|
||||
|
||||
# Professional colors
|
||||
PRIMARY = colors.HexColor('#2C3E50')
|
||||
ACCENT = colors.HexColor('#2980B9')
|
||||
LIGHT_ACCENT = colors.HexColor('#3498DB')
|
||||
SUCCESS = colors.HexColor('#27AE60')
|
||||
GREY = colors.HexColor('#7F8C8D')
|
||||
LIGHT_GREY = colors.HexColor('#ECF0F1')
|
||||
|
||||
OUTPUT = r"C:\Users\alexi\Documents\projects\timetrack-pro\TimeTrack_Pro_Complete_Documentation.pdf"
|
||||
|
||||
|
||||
class NumberedCanvas(canvas.Canvas):
|
||||
def __init__(self, *args, **kwargs):
|
||||
canvas.Canvas.__init__(self, *args, **kwargs)
|
||||
self._saved_page_states = []
|
||||
|
||||
def showPage(self):
|
||||
self._saved_page_states.append(dict(self.__dict__))
|
||||
self._startPage()
|
||||
|
||||
def save(self):
|
||||
num_pages = len(self._saved_page_states)
|
||||
for state in self._saved_page_states:
|
||||
self.__dict__.update(state)
|
||||
self.draw_decorations(num_pages)
|
||||
canvas.Canvas.showPage(self)
|
||||
canvas.Canvas.save(self)
|
||||
|
||||
def draw_decorations(self, count):
|
||||
self.saveState()
|
||||
self.setFont('Helvetica', 9)
|
||||
self.setFillColor(GREY)
|
||||
|
||||
# Page number
|
||||
self.drawCentredString(letter[0]/2, 0.5*inch, f"Page {self._pageNumber} of {count}")
|
||||
|
||||
# Header (skip page 1)
|
||||
if self._pageNumber > 1:
|
||||
self.drawString(0.75*inch, letter[1]-0.5*inch, "TimeTrack Pro - Technical Reference")
|
||||
self.drawRightString(letter[0]-0.75*inch, letter[1]-0.5*inch, "Alexis Trouvé | 2025")
|
||||
|
||||
self.restoreState()
|
||||
|
||||
|
||||
def get_styles():
|
||||
"""Get custom styles"""
|
||||
s = getSampleStyleSheet()
|
||||
|
||||
# Add custom styles with unique names
|
||||
s.add(ParagraphStyle(
|
||||
name='CTitle', parent=s['Heading1'], fontSize=48, textColor=PRIMARY,
|
||||
spaceAfter=20, alignment=TA_CENTER, fontName='Helvetica-Bold'))
|
||||
|
||||
s.add(ParagraphStyle(
|
||||
name='CSubtitle', parent=s['Normal'], fontSize=24, textColor=ACCENT,
|
||||
spaceAfter=30, alignment=TA_CENTER))
|
||||
|
||||
s.add(ParagraphStyle(
|
||||
name='SHead', parent=s['Heading1'], fontSize=20, textColor=colors.white,
|
||||
backColor=PRIMARY, spaceAfter=16, spaceBefore=12, leftIndent=10,
|
||||
fontName='Helvetica-Bold', leading=28))
|
||||
|
||||
s.add(ParagraphStyle(
|
||||
name='SubHead', parent=s['Heading2'], fontSize=14, textColor=ACCENT,
|
||||
spaceAfter=10, spaceBefore=12, fontName='Helvetica-Bold'))
|
||||
|
||||
s.add(ParagraphStyle(
|
||||
name='Body', parent=s['Normal'], fontSize=11, textColor=PRIMARY,
|
||||
alignment=TA_JUSTIFY, spaceAfter=8, leading=14))
|
||||
|
||||
s.add(ParagraphStyle(
|
||||
name='Bull', parent=s['Normal'], fontSize=11, textColor=PRIMARY,
|
||||
leftIndent=30, spaceAfter=6, leading=14))
|
||||
|
||||
s.add(ParagraphStyle(
|
||||
name='CodeBlock', parent=s['Normal'], fontSize=9, textColor=PRIMARY,
|
||||
fontName='Courier', leftIndent=20, backColor=LIGHT_GREY, spaceAfter=8))
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def build_pdf():
|
||||
doc = SimpleDocTemplate(OUTPUT, pagesize=letter, rightMargin=0.75*inch,
|
||||
leftMargin=0.75*inch, topMargin=1*inch,
|
||||
bottomMargin=0.75*inch,
|
||||
title="TimeTrack Pro Technical Reference",
|
||||
author="Alexis Trouvé")
|
||||
|
||||
story = []
|
||||
s = get_styles()
|
||||
|
||||
print("Building PDF sections...")
|
||||
|
||||
# ========== COVER PAGE ==========
|
||||
print(" - Cover page")
|
||||
story.append(Spacer(1, 1.5*inch))
|
||||
story.append(Paragraph("TimeTrack Pro", s['CTitle']))
|
||||
story.append(Spacer(1, 0.3*inch))
|
||||
story.append(Paragraph("Technical & Functional Reference", s['CSubtitle']))
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
story.append(HRFlowable(width="70%", thickness=2, color=ACCENT,
|
||||
spaceAfter=30, spaceBefore=10, hAlign='CENTER'))
|
||||
|
||||
desc = ParagraphStyle('CD', parent=s['Body'], fontSize=13, alignment=TA_CENTER,
|
||||
fontName='Helvetica-Oblique')
|
||||
story.append(Paragraph("Professional Time Tracking Application", desc))
|
||||
story.append(Paragraph("Built with Microsoft Access & VBA", desc))
|
||||
story.append(Paragraph("Automated Development via MCP VBA Server", desc))
|
||||
story.append(Spacer(1, 0.5*inch))
|
||||
|
||||
# Highlights table
|
||||
hl_data = [
|
||||
['Project Highlights', ''],
|
||||
['Total VBA Code', '915 Lines'],
|
||||
['Modular Components', '7 Modules'],
|
||||
['Database Tables', '3 Normalized Tables'],
|
||||
['Public Functions', '45+ Functions'],
|
||||
['Development Time Saved', '45% via Automation'],
|
||||
['Code Quality', '25% Comment Ratio'],
|
||||
]
|
||||
|
||||
hl_table = Table(hl_data, colWidths=[3*inch, 2*inch])
|
||||
hl_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('ALIGN', (0,0), (-1,0), 'CENTER'),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 14),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 12),
|
||||
('TOPPADDING', (0,0), (-1,0), 12),
|
||||
('BACKGROUND', (0,1), (0,-1), LIGHT_GREY),
|
||||
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
|
||||
('ALIGN', (1,1), (1,-1), 'RIGHT'),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 10),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(hl_table)
|
||||
story.append(Spacer(1, 1*inch))
|
||||
|
||||
auth = ParagraphStyle('Auth', parent=s['Body'], fontSize=13, alignment=TA_CENTER,
|
||||
fontName='Helvetica-Bold')
|
||||
cont = ParagraphStyle('Cont', parent=s['Body'], fontSize=11, alignment=TA_CENTER,
|
||||
textColor=ACCENT)
|
||||
vers = ParagraphStyle('Vers', parent=s['Body'], fontSize=10, alignment=TA_CENTER,
|
||||
textColor=GREY)
|
||||
|
||||
story.append(Paragraph("Alexis Trouvé", auth))
|
||||
story.append(Spacer(1, 0.1*inch))
|
||||
story.append(Paragraph("alexistrouve.pro@gmail.com", cont))
|
||||
story.append(Paragraph('<link href="https://github.com/AlexisTrouve?tab=repositories">github.com/AlexisTrouve</link>', cont))
|
||||
story.append(Spacer(1, 0.3*inch))
|
||||
story.append(Paragraph("Version 1.0 | January 2025", vers))
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== TABLE OF CONTENTS ==========
|
||||
print(" - Table of contents")
|
||||
story.append(Paragraph("Table of Contents", s['SHead']))
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
toc_items = [
|
||||
"1. Executive Summary",
|
||||
"2. Project Overview",
|
||||
"3. Database Architecture",
|
||||
"4. VBA Module Architecture",
|
||||
"5. Functional Specifications",
|
||||
"6. MCP VBA Server Automation",
|
||||
"7. Use Cases & Applications",
|
||||
"8. Installation & Deployment",
|
||||
"9. Professional Services Available",
|
||||
"10. Appendices & Resources"
|
||||
]
|
||||
|
||||
for item in toc_items:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 1 ==========
|
||||
print(" - Section 1: Executive Summary")
|
||||
story.append(Paragraph("1. Executive Summary", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"""TimeTrack Pro is a professional time tracking application built on Microsoft Access,
|
||||
showcasing advanced database design, VBA automation, and modern development practices.
|
||||
This project demonstrates the capability to deliver production-ready business applications
|
||||
through automated development workflows using the VBA MCP Server.""", s['Body']))
|
||||
story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"""<b><font color="#2980B9">Key Achievement:</font></b> Complete application development
|
||||
(database structure, business logic, queries, and VBA modules) automated via MCP
|
||||
(Model Context Protocol), demonstrating cutting-edge AI-assisted development.""", s['Body']))
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("1.1 Project Statistics", s['SubHead']))
|
||||
|
||||
stats = [
|
||||
['Metric', 'Value', 'Description'],
|
||||
['Total VBA Lines', '915', 'Professional, commented code'],
|
||||
['VBA Modules', '7', 'Modular architecture'],
|
||||
['Database Tables', '3', 'Normalized structure'],
|
||||
['Public Functions', '45+', 'Reusable components'],
|
||||
['Time Saved', '45%', 'Via MCP automation'],
|
||||
['Comment Ratio', '25%', 'Well-documented'],
|
||||
['Error Handling', '100%', 'All public functions'],
|
||||
]
|
||||
|
||||
st = Table(stats, colWidths=[2*inch, 1.5*inch, 3*inch])
|
||||
st.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 11),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,1), (-1,-1), 6),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 6),
|
||||
]))
|
||||
|
||||
story.append(st)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("1.2 Current Data", s['SubHead']))
|
||||
|
||||
usage = [
|
||||
['Metric', 'Value'],
|
||||
['Active Clients', '4'],
|
||||
['Active Projects', '6'],
|
||||
['Time Entries', '15+'],
|
||||
['Hours Tracked', '58 hours'],
|
||||
['Revenue Calculated', '€4,732.50'],
|
||||
]
|
||||
|
||||
ut = Table(usage, colWidths=[3*inch, 2*inch])
|
||||
ut.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), ACCENT),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 11),
|
||||
('FONTNAME', (1,1), (1,-1), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (1,1), (1,-1), 12),
|
||||
('TEXTCOLOR', (1,1), (1,-1), SUCCESS),
|
||||
('ALIGN', (1,0), (1,-1), 'RIGHT'),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 10),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 10),
|
||||
('TOPPADDING', (0,0), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,0), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(ut)
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 2 ==========
|
||||
print(" - Section 2: Project Overview")
|
||||
story.append(Paragraph("2. Project Overview", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("2.1 Purpose", s['SubHead']))
|
||||
story.append(Paragraph("TimeTrack Pro is a time management tool designed for freelancers, consultants, and small teams to:", s['Body']))
|
||||
|
||||
for item in ["Track billable hours across multiple clients and projects",
|
||||
"Calculate revenue automatically based on hourly rates",
|
||||
"Generate professional reports for invoicing",
|
||||
"Maintain complete audit trail of time entries"]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
story.append(Paragraph("2.2 Target Audience", s['SubHead']))
|
||||
|
||||
aud = [
|
||||
['Audience', 'Use Case'],
|
||||
['Freelancers', 'Independent consultants tracking multiple client projects'],
|
||||
['Small Teams', 'Agencies managing client work and resource allocation'],
|
||||
['Consultants', 'Professional services requiring detailed time records'],
|
||||
]
|
||||
|
||||
at = Table(aud, colWidths=[1.5*inch, 5*inch])
|
||||
at.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), ACCENT),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'TOP'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(at)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("2.3 Technology Stack", s['SubHead']))
|
||||
|
||||
tech = [
|
||||
['Component', 'Technology', 'Version'],
|
||||
['Database', 'Microsoft Access', '2016+/Office 365'],
|
||||
['Language', 'VBA', '7.1'],
|
||||
['Automation', 'VBA MCP Server', 'v0.6.0+'],
|
||||
['Export', 'PDF, Excel', 'Native'],
|
||||
['Version Control', 'Git', '2.x'],
|
||||
]
|
||||
|
||||
tt = Table(tech, colWidths=[2*inch, 2.2*inch, 2.3*inch])
|
||||
tt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 6),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 6),
|
||||
]))
|
||||
|
||||
story.append(tt)
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 3 ==========
|
||||
print(" - Section 3: Database Architecture")
|
||||
story.append(Paragraph("3. Database Architecture", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"The application uses a normalized relational database following third normal form (3NF). Three core tables with enforced referential integrity.", s['Body']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("3.1 Entity Relationships", s['SubHead']))
|
||||
|
||||
erd = """
|
||||
tbl_Clients (1) ──── (N) tbl_Projets (1) ──── (N) tbl_Temps
|
||||
|
||||
• One Client has many Projects (1:N)
|
||||
• One Project has many Time Entries (1:N)
|
||||
• Referential integrity enforced with CASCADE
|
||||
"""
|
||||
story.append(Paragraph(erd, s['CodeBlock']))
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("3.2 Table: tbl_Clients", s['SubHead']))
|
||||
|
||||
cl = [
|
||||
['Field', 'Type', 'Constraints', 'Description'],
|
||||
['ClientID', 'AutoNumber', 'PRIMARY KEY', 'Unique identifier'],
|
||||
['Nom', 'Text(100)', 'NOT NULL', 'Client name'],
|
||||
['Email', 'Text(100)', 'VALIDATED', 'Email with format check'],
|
||||
['Telephone', 'Text(20)', 'NULL', 'Phone number'],
|
||||
['Notes', 'Memo', 'NULL', 'Additional notes'],
|
||||
['DateCreation', 'DateTime', 'DEFAULT Now()', 'Creation timestamp'],
|
||||
]
|
||||
|
||||
clt = Table(cl, colWidths=[1.3*inch, 1.3*inch, 1.5*inch, 2.4*inch])
|
||||
clt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 9),
|
||||
('FONTSIZE', (0,1), (-1,-1), 8),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 6),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 6),
|
||||
('TOPPADDING', (0,0), (-1,0), 8),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 8),
|
||||
('TOPPADDING', (0,1), (-1,-1), 5),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 5),
|
||||
]))
|
||||
|
||||
story.append(clt)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("3.3 Table: tbl_Projets", s['SubHead']))
|
||||
|
||||
pr = [
|
||||
['Field', 'Type', 'Constraints', 'Description'],
|
||||
['ProjetID', 'AutoNumber', 'PRIMARY KEY', 'Unique identifier'],
|
||||
['ClientID', 'Long', 'FOREIGN KEY', 'Ref to tbl_Clients'],
|
||||
['Nom', 'Text(100)', 'NOT NULL', 'Project name'],
|
||||
['Description', 'Memo', 'NULL', 'Project details'],
|
||||
['TauxHoraire', 'Currency', 'DEFAULT 0', 'Hourly rate (EUR)'],
|
||||
['Actif', 'Yes/No', 'DEFAULT True', 'Active/archived'],
|
||||
['DateCreation', 'DateTime', 'DEFAULT Now()', 'Creation timestamp'],
|
||||
]
|
||||
|
||||
prt = Table(pr, colWidths=[1.3*inch, 1.3*inch, 1.5*inch, 2.4*inch])
|
||||
prt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 9),
|
||||
('FONTSIZE', (0,1), (-1,-1), 8),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 6),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 6),
|
||||
('TOPPADDING', (0,0), (-1,0), 8),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 8),
|
||||
('TOPPADDING', (0,1), (-1,-1), 5),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 5),
|
||||
]))
|
||||
|
||||
story.append(prt)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("3.4 Table: tbl_Temps", s['SubHead']))
|
||||
|
||||
tm = [
|
||||
['Field', 'Type', 'Constraints', 'Description'],
|
||||
['TempsID', 'AutoNumber', 'PRIMARY KEY', 'Unique identifier'],
|
||||
['ProjetID', 'Long', 'FOREIGN KEY', 'Ref to tbl_Projets'],
|
||||
['Date', 'DateTime', 'NOT NULL', 'Entry date (no future)'],
|
||||
['Duree', 'Double', 'NOT NULL, > 0', 'Hours (decimal)'],
|
||||
['Description', 'Memo', 'NULL', 'Work description'],
|
||||
['DateCreation', 'DateTime', 'DEFAULT Now()', 'Creation timestamp'],
|
||||
]
|
||||
|
||||
tmt = Table(tm, colWidths=[1.3*inch, 1.3*inch, 1.5*inch, 2.4*inch])
|
||||
tmt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 9),
|
||||
('FONTSIZE', (0,1), (-1,-1), 8),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 6),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 6),
|
||||
('TOPPADDING', (0,0), (-1,0), 8),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 8),
|
||||
('TOPPADDING', (0,1), (-1,-1), 5),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 5),
|
||||
]))
|
||||
|
||||
story.append(tmt)
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 4 ==========
|
||||
print(" - Section 4: VBA Architecture")
|
||||
story.append(Paragraph("4. VBA Module Architecture", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"Modular VBA architecture with 7 core modules totaling 915 lines of professional, commented code. Clear separation of concerns following best practices.", s['Body']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("4.1 Module Overview", s['SubHead']))
|
||||
|
||||
mod = [
|
||||
['Module', 'Purpose', 'LOC', 'Functions'],
|
||||
['mod_Config', 'App configuration & constants', '80', '—'],
|
||||
['mod_Navigation', 'Form navigation & UI flow', '120', '7'],
|
||||
['mod_DataAccess', 'CRUD operations & DB layer', '200', '15+'],
|
||||
['mod_Calculs', 'Business logic & calculations', '150', '8'],
|
||||
['mod_Export', 'Report generation & export', '120', '5'],
|
||||
['mod_Utils', 'Helper functions & validation', '100', '8'],
|
||||
['mod_FormBuilder', 'Automated form creation', '145', '6'],
|
||||
]
|
||||
|
||||
modt = Table(mod, colWidths=[1.5*inch, 2.5*inch, 0.7*inch, 1*inch])
|
||||
modt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 9),
|
||||
('FONTSIZE', (0,1), (-1,-1), 9),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('ALIGN', (2,0), (3,-1), 'CENTER'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 6),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 6),
|
||||
]))
|
||||
|
||||
story.append(modt)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("4.2 Code Quality Metrics", s['SubHead']))
|
||||
|
||||
for item in [
|
||||
"Total VBA Lines: <b>915</b>",
|
||||
"Average Module Size: <b>~130 lines</b>",
|
||||
"Total Functions: <b>45+</b>",
|
||||
"Comment Ratio: <b>25%</b>",
|
||||
"Error Handling: <b>100%</b> (all public functions)",
|
||||
"Coding Standard: <b>Option Explicit</b>, meaningful names, consistent indentation"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 5 ==========
|
||||
print(" - Section 5: Functional Specifications")
|
||||
story.append(Paragraph("5. Functional Specifications", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("5.1 Core Features", s['SubHead']))
|
||||
|
||||
feat = [
|
||||
['Feature', 'Description', 'Status'],
|
||||
['Client Management', 'Full CRUD for clients with contacts', '✓ Complete'],
|
||||
['Project Management', 'Projects linked to clients with rates', '✓ Complete'],
|
||||
['Time Entry', 'Quick entry with validation', '✓ Complete'],
|
||||
['Automatic Calculations', 'Revenue by project/client', '✓ Complete'],
|
||||
['Report Generation', 'Exportable PDF/Excel reports', '✓ Complete'],
|
||||
['Data Validation', 'Email, phone, date validation', '✓ Complete'],
|
||||
]
|
||||
|
||||
ft = Table(feat, colWidths=[2*inch, 3*inch, 1.5*inch])
|
||||
ft.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), ACCENT),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 10),
|
||||
('FONTSIZE', (0,1), (-1,-1), 9),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 6),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 6),
|
||||
('TEXTCOLOR', (2,1), (2,-1), SUCCESS),
|
||||
('FONTNAME', (2,1), (2,-1), 'Helvetica-Bold'),
|
||||
]))
|
||||
|
||||
story.append(ft)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("5.2 User Interface", s['SubHead']))
|
||||
|
||||
ui = [
|
||||
['Form', 'Purpose'],
|
||||
['frm_Accueil', 'Main dashboard with navigation & statistics'],
|
||||
['frm_Clients', 'Client list with add/edit/delete'],
|
||||
['frm_Projets', 'Project management with client filter'],
|
||||
['frm_SaisieTemps', 'Quick time entry (<30 sec target)'],
|
||||
['frm_Historique', 'Time history with filters & export'],
|
||||
]
|
||||
|
||||
uit = Table(ui, colWidths=[2*inch, 4.5*inch])
|
||||
uit.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(uit)
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 6 ==========
|
||||
print(" - Section 6: MCP VBA Automation")
|
||||
story.append(Paragraph("6. MCP VBA Server Automation", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"""This project showcases advanced automation using the VBA MCP Server (Model Context Protocol),
|
||||
enabling AI-assisted development of Access applications. This cutting-edge approach demonstrates
|
||||
the future of database application development.""", s['Body']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("6.1 Development Efficiency", s['SubHead']))
|
||||
|
||||
dev = [
|
||||
['Phase', 'Estimated', 'Actual', 'Method'],
|
||||
['Database Design', '1h', '0.5h', 'MCP Automated'],
|
||||
['Test Data', '30min', '15min', 'MCP Automated'],
|
||||
['VBA Modules', '3h', '1h', 'MCP Automated'],
|
||||
['SQL Queries', '30min', '20min', 'MCP Automated'],
|
||||
['Forms', '2h', '1h', 'Script + Manual'],
|
||||
['Testing & Docs', '2h', '2h', 'Manual'],
|
||||
['TOTAL', '9h', '~5h', '45% Time Saved'],
|
||||
]
|
||||
|
||||
devt = Table(dev, colWidths=[1.8*inch, 1.2*inch, 1.2*inch, 2.3*inch])
|
||||
devt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 10),
|
||||
('FONTSIZE', (0,1), (-1,-1), 9),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-2), [colors.white, LIGHT_GREY]),
|
||||
('BACKGROUND', (0,-1), (-1,-1), SUCCESS),
|
||||
('TEXTCOLOR', (0,-1), (-1,-1), colors.white),
|
||||
('FONTNAME', (0,-1), (-1,-1), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,-1), (-1,-1), 11),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,0), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(devt)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("6.2 MCP Tools Used", s['SubHead']))
|
||||
|
||||
for item in [
|
||||
"<b>run_access_query</b> - DDL/DML SQL execution",
|
||||
"<b>inject_vba</b> - Module code injection",
|
||||
"<b>validate_vba_code</b> - Pre-injection syntax check",
|
||||
"<b>get/set_worksheet_data</b> - Bulk data operations",
|
||||
"<b>list_access_tables</b> - Schema verification",
|
||||
"<b>compile_vba</b> - Compilation error detection",
|
||||
"<b>backup_vba</b> - Safety backups before changes"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 7 ==========
|
||||
print(" - Section 7: Use Cases")
|
||||
story.append(Paragraph("7. Use Cases & Applications", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("7.1 Freelance Consultant", s['SubHead']))
|
||||
story.append(Paragraph(
|
||||
"""<b>Scenario:</b> Independent consultant managing 5 clients with 8 ongoing projects.""",
|
||||
s['Body']))
|
||||
|
||||
for item in [
|
||||
"Log time daily (2 min/day)",
|
||||
"Review weekly hours by project",
|
||||
"Generate monthly client reports",
|
||||
"Export to Excel for accounting"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
story.append(Paragraph("7.2 Small Design Agency", s['SubHead']))
|
||||
story.append(Paragraph(
|
||||
"""<b>Scenario:</b> 3-person team tracking time across 10 client projects.""",
|
||||
s['Body']))
|
||||
|
||||
for item in [
|
||||
"Team members log time per project",
|
||||
"Manager reviews hours weekly",
|
||||
"Generate client reports at milestones",
|
||||
"Calculate project profitability"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(Spacer(1, 0.1*inch))
|
||||
|
||||
story.append(Paragraph("7.3 IT Contractor", s['SubHead']))
|
||||
story.append(Paragraph(
|
||||
"""<b>Scenario:</b> IT professional with retainer clients and hourly projects.""",
|
||||
s['Body']))
|
||||
|
||||
for item in [
|
||||
"Different rates per project/client",
|
||||
"Track support vs project hours",
|
||||
"Monitor retainer budgets",
|
||||
"Detailed invoices with descriptions"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 8 ==========
|
||||
print(" - Section 8: Installation")
|
||||
story.append(Paragraph("8. Installation & Deployment", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("8.1 Prerequisites", s['SubHead']))
|
||||
|
||||
for item in [
|
||||
"Windows 10/11",
|
||||
"Microsoft Access 2016+ or Office 365 with Access",
|
||||
"Macro security: Enable VBA macros"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("8.2 Quick Start", s['SubHead']))
|
||||
|
||||
inst = """
|
||||
1. Clone/download project
|
||||
2. Open db/TimeTrackPro.accdb
|
||||
3. Enable macros when prompted
|
||||
4. Import forms (first time):
|
||||
- Alt+F11 → Import → scripts/modules/mod_FormBuilder.bas
|
||||
- Ctrl+G → BuildAllForms → Enter
|
||||
5. Start using application
|
||||
"""
|
||||
story.append(Paragraph(inst, s['CodeBlock']))
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("8.3 Deployment Options", s['SubHead']))
|
||||
|
||||
dep = [
|
||||
['Option', 'Description', 'Best For'],
|
||||
['Single-User', 'Copy .accdb to user machine', 'Personal use'],
|
||||
['Split Database', 'Frontend + backend on network', 'Team (< 10 users)'],
|
||||
['SQL Server', 'Migrate to SQL Server backend', 'Enterprise'],
|
||||
]
|
||||
|
||||
dept = Table(dep, colWidths=[1.5*inch, 3*inch, 2*inch])
|
||||
dept.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), ACCENT),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(dept)
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 9 ==========
|
||||
print(" - Section 9: Professional Services")
|
||||
story.append(Paragraph("9. Professional Services Available", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"This project demonstrates expertise in the following areas:", s['Body']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
serv = [
|
||||
['Service', 'Description'],
|
||||
['Microsoft Access Development', 'Forms, reports, VBA automation, SQL optimization'],
|
||||
['Database Design', 'Normalization, indexing, referential integrity, performance'],
|
||||
['Business Applications', 'Requirements, UX design, testing, deployment'],
|
||||
['Process Automation', 'VBA macros, MCP integration, workflow optimization'],
|
||||
['Legacy Modernization', 'Access to SQL Server/web migration'],
|
||||
['AI-Assisted Development', 'MCP Server integration, cutting-edge automation'],
|
||||
]
|
||||
|
||||
servt = Table(serv, colWidths=[2.3*inch, 4.2*inch])
|
||||
servt.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 11),
|
||||
('FONTSIZE', (0,1), (-1,-1), 10),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'TOP'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 10),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 10),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(servt)
|
||||
story.append(Spacer(1, 0.3*inch))
|
||||
|
||||
# Contact box
|
||||
contact_data = [[
|
||||
Paragraph("<b>Contact Information</b>", ParagraphStyle('CB', parent=s['Body'],
|
||||
fontSize=14, alignment=TA_CENTER, textColor=colors.white,
|
||||
fontName='Helvetica-Bold')),
|
||||
Paragraph('Alexis Trouvé<br/>alexistrouve.pro@gmail.com<br/><link href="https://github.com/AlexisTrouve?tab=repositories" color="white">github.com/AlexisTrouve</link><br/><i>Response time: Within 24 hours</i>',
|
||||
ParagraphStyle('CC', parent=s['Body'], fontSize=11,
|
||||
alignment=TA_CENTER, textColor=colors.white))
|
||||
]]
|
||||
|
||||
ct = Table(contact_data, colWidths=[6.5*inch])
|
||||
ct.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,-1), PRIMARY),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 20),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 20),
|
||||
('TOPPADDING', (0,0), (-1,-1), 15),
|
||||
('BOTTOMPADDING', (0,0), (-1,-1), 15),
|
||||
]))
|
||||
|
||||
story.append(ct)
|
||||
story.append(PageBreak())
|
||||
|
||||
# ========== SECTION 10 ==========
|
||||
print(" - Section 10: Appendices")
|
||||
story.append(Paragraph("10. Appendices & Resources", s['SHead']))
|
||||
story.append(Spacer(1, 0.15*inch))
|
||||
|
||||
story.append(Paragraph("10.1 Documentation Files", s['SubHead']))
|
||||
|
||||
docs = [
|
||||
['File', 'Purpose'],
|
||||
['README.md', 'Project overview and quick start'],
|
||||
['TECHNICAL_REFERENCE.md', 'Complete technical reference'],
|
||||
['DATABASE.md', 'Detailed database schema'],
|
||||
['VBA_MODULES.md', 'VBA code documentation'],
|
||||
['PLAN.md', 'Project development plan'],
|
||||
['CHANGELOG.md', 'Version history'],
|
||||
['docs/MCP_VBA_GUIDE.md', 'MCP VBA usage guide'],
|
||||
]
|
||||
|
||||
docst = Table(docs, colWidths=[2.5*inch, 4*inch])
|
||||
docst.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), ACCENT),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTNAME', (0,1), (0,-1), 'Courier'),
|
||||
('FONTSIZE', (0,0), (-1,0), 10),
|
||||
('FONTSIZE', (0,1), (-1,-1), 9),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 8),
|
||||
('TOPPADDING', (0,0), (-1,0), 10),
|
||||
('BOTTOMPADDING', (0,0), (-1,0), 10),
|
||||
('TOPPADDING', (0,1), (-1,-1), 6),
|
||||
('BOTTOMPADDING', (0,1), (-1,-1), 6),
|
||||
]))
|
||||
|
||||
story.append(docst)
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("10.2 License & Usage", s['SubHead']))
|
||||
|
||||
for item in [
|
||||
"<b>License:</b> MIT License",
|
||||
"<b>Commercial Use:</b> Permitted",
|
||||
"<b>Modification:</b> Permitted",
|
||||
"<b>Distribution:</b> Permitted",
|
||||
"<b>Private Use:</b> Permitted"
|
||||
]:
|
||||
story.append(Paragraph(f"• {item}", s['Bull']))
|
||||
|
||||
story.append(Spacer(1, 0.2*inch))
|
||||
|
||||
story.append(Paragraph("10.3 Version Information", s['SubHead']))
|
||||
|
||||
ver = [
|
||||
['Item', 'Details'],
|
||||
['Document Version', '1.0'],
|
||||
['Application Version', '1.0'],
|
||||
['Last Updated', 'January 13, 2025'],
|
||||
['Status', 'Production Ready'],
|
||||
]
|
||||
|
||||
vert = Table(ver, colWidths=[2*inch, 4.5*inch])
|
||||
vert.setStyle(TableStyle([
|
||||
('BACKGROUND', (0,0), (-1,0), PRIMARY),
|
||||
('TEXTCOLOR', (0,0), (-1,0), colors.white),
|
||||
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0,0), (-1,0), 10),
|
||||
('GRID', (0,0), (-1,-1), 0.5, GREY),
|
||||
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 10),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 10),
|
||||
('TOPPADDING', (0,0), (-1,-1), 8),
|
||||
('BOTTOMPADDING', (0,0), (-1,-1), 8),
|
||||
]))
|
||||
|
||||
story.append(vert)
|
||||
story.append(Spacer(1, 0.5*inch))
|
||||
|
||||
# Final note
|
||||
final = ParagraphStyle('Final', parent=s['Body'], fontSize=10,
|
||||
alignment=TA_CENTER, textColor=GREY,
|
||||
fontName='Helvetica-Oblique')
|
||||
|
||||
story.append(HRFlowable(width="50%", thickness=1, color=GREY,
|
||||
spaceAfter=15, spaceBefore=15, hAlign='CENTER'))
|
||||
story.append(Paragraph(
|
||||
"This document serves as the authoritative technical and functional reference for TimeTrack Pro.",
|
||||
final))
|
||||
story.append(Paragraph(
|
||||
"For development assistance or custom modifications, contact the author.",
|
||||
final))
|
||||
|
||||
# Build PDF
|
||||
print("\nBuilding PDF with page numbers...")
|
||||
doc.build(story, canvasmaker=NumberedCanvas)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"SUCCESS - Professional PDF generated!")
|
||||
print(f"{'='*60}")
|
||||
print(f"File: {OUTPUT}")
|
||||
import os
|
||||
size = os.path.getsize(OUTPUT) / 1024
|
||||
print(f"Size: {size:.1f} KB")
|
||||
print(f"Sections: 10 complete sections")
|
||||
print(f"Tables: 15+ professional tables")
|
||||
print(f"Pages: ~15-20 pages with headers/footers")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build_pdf()
|
||||
703
generate_professional_pdf.py
Normal file
703
generate_professional_pdf.py
Normal file
@ -0,0 +1,703 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Professional PDF Generator for TimeTrack Pro Technical Reference
|
||||
Creates a comprehensive, beautifully formatted technical document
|
||||
"""
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter, A4
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
|
||||
from reportlab.platypus import (
|
||||
SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle,
|
||||
Image, KeepTogether, ListFlowable, ListItem, HRFlowable
|
||||
)
|
||||
from reportlab.pdfgen import canvas
|
||||
from datetime import datetime
|
||||
|
||||
# Professional color scheme
|
||||
PRIMARY_COLOR = colors.HexColor('#2C3E50') # Dark blue-grey
|
||||
ACCENT_COLOR = colors.HexColor('#2980B9') # Professional blue
|
||||
LIGHT_ACCENT = colors.HexColor('#3498DB') # Light blue
|
||||
SUCCESS_COLOR = colors.HexColor('#27AE60') # Green
|
||||
GREY_COLOR = colors.HexColor('#7F8C8D') # Grey
|
||||
LIGHT_GREY = colors.HexColor('#ECF0F1') # Light grey
|
||||
|
||||
OUTPUT_FILE = r"C:\Users\alexi\Documents\projects\timetrack-pro\TimeTrack_Pro_Professional_Documentation.pdf"
|
||||
|
||||
|
||||
class NumberedCanvas(canvas.Canvas):
|
||||
"""Custom canvas for page numbers and headers/footers"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
canvas.Canvas.__init__(self, *args, **kwargs)
|
||||
self._saved_page_states = []
|
||||
|
||||
def showPage(self):
|
||||
self._saved_page_states.append(dict(self.__dict__))
|
||||
self._startPage()
|
||||
|
||||
def save(self):
|
||||
num_pages = len(self._saved_page_states)
|
||||
for state in self._saved_page_states:
|
||||
self.__dict__.update(state)
|
||||
self.draw_page_decorations(num_pages)
|
||||
canvas.Canvas.showPage(self)
|
||||
canvas.Canvas.save(self)
|
||||
|
||||
def draw_page_decorations(self, page_count):
|
||||
self.saveState()
|
||||
self.setFont('Helvetica', 9)
|
||||
|
||||
# Page number in footer
|
||||
page_num = f"Page {self._pageNumber} of {page_count}"
|
||||
self.setFillColor(GREY_COLOR)
|
||||
self.drawCentredString(letter[0] / 2, 0.5 * inch, page_num)
|
||||
|
||||
# Document title in header (skip first page)
|
||||
if self._pageNumber > 1:
|
||||
self.setFillColor(GREY_COLOR)
|
||||
self.drawString(0.75 * inch, letter[1] - 0.5 * inch,
|
||||
"TimeTrack Pro - Technical Reference")
|
||||
self.drawRightString(letter[0] - 0.75 * inch, letter[1] - 0.5 * inch,
|
||||
"Alexis Trouvé | 2025")
|
||||
|
||||
self.restoreState()
|
||||
|
||||
|
||||
def create_styles():
|
||||
"""Create custom paragraph styles"""
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
# Cover title
|
||||
styles.add(ParagraphStyle(
|
||||
name='CoverTitle',
|
||||
parent=styles['Heading1'],
|
||||
fontSize=48,
|
||||
textColor=PRIMARY_COLOR,
|
||||
spaceAfter=20,
|
||||
alignment=TA_CENTER,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
# Cover subtitle
|
||||
styles.add(ParagraphStyle(
|
||||
name='CoverSubtitle',
|
||||
parent=styles['Normal'],
|
||||
fontSize=24,
|
||||
textColor=ACCENT_COLOR,
|
||||
spaceAfter=30,
|
||||
alignment=TA_CENTER,
|
||||
fontName='Helvetica'
|
||||
))
|
||||
|
||||
# Section header
|
||||
styles.add(ParagraphStyle(
|
||||
name='SectionHeader',
|
||||
parent=styles['Heading1'],
|
||||
fontSize=20,
|
||||
textColor=colors.white,
|
||||
backColor=PRIMARY_COLOR,
|
||||
spaceAfter=16,
|
||||
spaceBefore=12,
|
||||
leftIndent=10,
|
||||
fontName='Helvetica-Bold',
|
||||
leading=28
|
||||
))
|
||||
|
||||
# Subsection header
|
||||
styles.add(ParagraphStyle(
|
||||
name='SubsectionHeader',
|
||||
parent=styles['Heading2'],
|
||||
fontSize=14,
|
||||
textColor=ACCENT_COLOR,
|
||||
spaceAfter=10,
|
||||
spaceBefore=12,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
# Body text
|
||||
styles.add(ParagraphStyle(
|
||||
name='BodyText',
|
||||
parent=styles['Normal'],
|
||||
fontSize=11,
|
||||
textColor=PRIMARY_COLOR,
|
||||
alignment=TA_JUSTIFY,
|
||||
spaceAfter=8,
|
||||
leading=14
|
||||
))
|
||||
|
||||
# Bullet points
|
||||
styles.add(ParagraphStyle(
|
||||
name='Bullet',
|
||||
parent=styles['Normal'],
|
||||
fontSize=11,
|
||||
textColor=PRIMARY_COLOR,
|
||||
leftIndent=30,
|
||||
spaceAfter=6,
|
||||
leading=14
|
||||
))
|
||||
|
||||
# Highlight box
|
||||
styles.add(ParagraphStyle(
|
||||
name='HighlightBox',
|
||||
parent=styles['Normal'],
|
||||
fontSize=12,
|
||||
textColor=colors.white,
|
||||
backColor=ACCENT_COLOR,
|
||||
alignment=TA_CENTER,
|
||||
spaceAfter=12,
|
||||
spaceBefore=12,
|
||||
leftIndent=20,
|
||||
rightIndent=20,
|
||||
borderPadding=10,
|
||||
fontName='Helvetica-Bold'
|
||||
))
|
||||
|
||||
# Code/Technical
|
||||
styles.add(ParagraphStyle(
|
||||
name='Code',
|
||||
parent=styles['Normal'],
|
||||
fontSize=9,
|
||||
textColor=PRIMARY_COLOR,
|
||||
fontName='Courier',
|
||||
leftIndent=20,
|
||||
backColor=LIGHT_GREY,
|
||||
spaceAfter=8
|
||||
))
|
||||
|
||||
return styles
|
||||
|
||||
|
||||
def create_cover_page(story, styles):
|
||||
"""Create professional cover page"""
|
||||
|
||||
# Title
|
||||
story.append(Spacer(1, 1.5 * inch))
|
||||
story.append(Paragraph("TimeTrack Pro", styles['CoverTitle']))
|
||||
story.append(Spacer(1, 0.3 * inch))
|
||||
|
||||
# Subtitle
|
||||
story.append(Paragraph("Technical & Functional Reference", styles['CoverSubtitle']))
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Horizontal line
|
||||
story.append(HRFlowable(width="70%", thickness=2, color=ACCENT_COLOR,
|
||||
spaceAfter=30, spaceBefore=10, hAlign='CENTER'))
|
||||
|
||||
# Description
|
||||
desc_style = ParagraphStyle(
|
||||
'CoverDesc',
|
||||
parent=styles['BodyText'],
|
||||
fontSize=13,
|
||||
alignment=TA_CENTER,
|
||||
textColor=PRIMARY_COLOR,
|
||||
fontName='Helvetica-Oblique'
|
||||
)
|
||||
|
||||
story.append(Paragraph("Professional Time Tracking Application", desc_style))
|
||||
story.append(Paragraph("Built with Microsoft Access & VBA", desc_style))
|
||||
story.append(Paragraph("Automated Development via MCP VBA Server", desc_style))
|
||||
story.append(Spacer(1, 0.5 * inch))
|
||||
|
||||
# Key highlights box
|
||||
highlights_data = [
|
||||
['Project Highlights', ''],
|
||||
['Total VBA Code', '915 Lines'],
|
||||
['Modular Components', '7 Modules'],
|
||||
['Database Tables', '3 Normalized Tables'],
|
||||
['Public Functions', '45+ Functions'],
|
||||
['Development Time Saved', '45% via Automation'],
|
||||
['Code Quality', '25% Comment Ratio'],
|
||||
]
|
||||
|
||||
highlights_table = Table(highlights_data, colWidths=[3*inch, 2*inch])
|
||||
highlights_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 14),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 12),
|
||||
('BACKGROUND', (0, 1), (0, -1), LIGHT_GREY),
|
||||
('BACKGROUND', (1, 1), (1, -1), colors.white),
|
||||
('TEXTCOLOR', (0, 1), (-1, -1), PRIMARY_COLOR),
|
||||
('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
|
||||
('FONTNAME', (1, 1), (1, -1), 'Helvetica'),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 11),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('ALIGN', (1, 1), (1, -1), 'RIGHT'),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 10),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 10),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 8),
|
||||
]))
|
||||
|
||||
story.append(highlights_table)
|
||||
story.append(Spacer(1, 1 * inch))
|
||||
|
||||
# Author info
|
||||
author_style = ParagraphStyle(
|
||||
'Author',
|
||||
parent=styles['BodyText'],
|
||||
fontSize=13,
|
||||
alignment=TA_CENTER,
|
||||
textColor=PRIMARY_COLOR,
|
||||
fontName='Helvetica-Bold'
|
||||
)
|
||||
|
||||
contact_style = ParagraphStyle(
|
||||
'Contact',
|
||||
parent=styles['BodyText'],
|
||||
fontSize=11,
|
||||
alignment=TA_CENTER,
|
||||
textColor=ACCENT_COLOR
|
||||
)
|
||||
|
||||
story.append(Paragraph("Alexis Trouvé", author_style))
|
||||
story.append(Spacer(1, 0.1 * inch))
|
||||
story.append(Paragraph("alexistrouve.pro@gmail.com", contact_style))
|
||||
story.append(Spacer(1, 0.3 * inch))
|
||||
|
||||
version_style = ParagraphStyle(
|
||||
'Version',
|
||||
parent=styles['BodyText'],
|
||||
fontSize=10,
|
||||
alignment=TA_CENTER,
|
||||
textColor=GREY_COLOR
|
||||
)
|
||||
story.append(Paragraph("Version 1.0 | January 2025", version_style))
|
||||
|
||||
story.append(PageBreak())
|
||||
|
||||
|
||||
def create_section_1_executive_summary(story, styles):
|
||||
"""Section 1: Executive Summary"""
|
||||
|
||||
story.append(Paragraph("1. Executive Summary", styles['SectionHeader']))
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"""TimeTrack Pro is a professional time tracking application built on Microsoft Access,
|
||||
showcasing advanced database design, VBA automation, and modern development practices.
|
||||
This project demonstrates the capability to deliver production-ready business applications
|
||||
through automated development workflows using the VBA MCP Server.""",
|
||||
styles['BodyText']
|
||||
))
|
||||
story.append(Spacer(1, 0.1 * inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"""<b><font color="#2980B9">Key Achievement:</font></b> Complete application development
|
||||
(database structure, business logic, queries, and VBA modules) automated via MCP
|
||||
(Model Context Protocol) integration, demonstrating cutting-edge AI-assisted development
|
||||
capabilities.""",
|
||||
styles['BodyText']
|
||||
))
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Statistics table
|
||||
story.append(Paragraph("1.1 Project Statistics", styles['SubsectionHeader']))
|
||||
|
||||
stats_data = [
|
||||
['Metric', 'Value', 'Description'],
|
||||
['Total VBA Lines', '915', 'Professional, commented code'],
|
||||
['VBA Modules', '7', 'Modular architecture with separation of concerns'],
|
||||
['Database Tables', '3', 'Normalized relational structure'],
|
||||
['Public Functions', '45+', 'Reusable business logic components'],
|
||||
['Development Time Saved', '45%', 'Via MCP VBA automation'],
|
||||
['Comment Ratio', '25%', 'Well-documented codebase'],
|
||||
['Error Handling', '100%', 'All public functions include error handling'],
|
||||
]
|
||||
|
||||
stats_table = Table(stats_data, colWidths=[2*inch, 1.5*inch, 3*inch])
|
||||
stats_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 11),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 10),
|
||||
('BACKGROUND', (0, 1), (-1, -1), colors.white),
|
||||
('TEXTCOLOR', (0, 1), (-1, -1), PRIMARY_COLOR),
|
||||
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 10),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 8),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 8),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 6),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 6),
|
||||
]))
|
||||
|
||||
story.append(stats_table)
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Current usage data
|
||||
story.append(Paragraph("1.2 Current Application Data", styles['SubsectionHeader']))
|
||||
|
||||
usage_data = [
|
||||
['Metric', 'Value'],
|
||||
['Active Clients', '4'],
|
||||
['Active Projects', '6'],
|
||||
['Total Time Entries', '15+'],
|
||||
['Total Hours Tracked', '58 hours'],
|
||||
['Total Revenue Calculated', '€4,732.50'],
|
||||
]
|
||||
|
||||
usage_table = Table(usage_data, colWidths=[3*inch, 2*inch])
|
||||
usage_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), ACCENT_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (0, -1), 'LEFT'),
|
||||
('ALIGN', (1, 0), (1, -1), 'RIGHT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 11),
|
||||
('FONTNAME', (1, 1), (1, -1), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (1, 1), (1, -1), 12),
|
||||
('TEXTCOLOR', (1, 1), (1, -1), SUCCESS_COLOR),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 10),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 10),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 10),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 8),
|
||||
]))
|
||||
|
||||
story.append(usage_table)
|
||||
story.append(PageBreak())
|
||||
|
||||
|
||||
def create_section_2_project_overview(story, styles):
|
||||
"""Section 2: Project Overview"""
|
||||
|
||||
story.append(Paragraph("2. Project Overview", styles['SectionHeader']))
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
# Purpose
|
||||
story.append(Paragraph("2.1 Purpose", styles['SubsectionHeader']))
|
||||
story.append(Paragraph(
|
||||
"""TimeTrack Pro is a time management tool designed for freelancers, consultants,
|
||||
and small teams to:""",
|
||||
styles['BodyText']
|
||||
))
|
||||
|
||||
bullets = [
|
||||
"Track billable hours across multiple clients and projects",
|
||||
"Calculate revenue automatically based on hourly rates",
|
||||
"Generate professional reports for invoicing and analysis",
|
||||
"Maintain a complete audit trail of time entries"
|
||||
]
|
||||
|
||||
for bullet in bullets:
|
||||
story.append(Paragraph(f"• {bullet}", styles['Bullet']))
|
||||
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
# Target Audience
|
||||
story.append(Paragraph("2.2 Target Audience", styles['SubsectionHeader']))
|
||||
|
||||
audience_data = [
|
||||
['Audience', 'Use Case'],
|
||||
['Freelancers', 'Independent consultants tracking multiple client projects and generating invoices'],
|
||||
['Small Teams', 'Agencies managing client work, resource allocation, and team productivity'],
|
||||
['Consultants', 'Professional services requiring detailed time records and client reporting'],
|
||||
]
|
||||
|
||||
audience_table = Table(audience_data, colWidths=[1.5*inch, 5*inch])
|
||||
audience_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), ACCENT_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 11),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 10),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 8),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 8),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 8),
|
||||
]))
|
||||
|
||||
story.append(audience_table)
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Differentiators
|
||||
story.append(Paragraph("2.3 Key Differentiators", styles['SubsectionHeader']))
|
||||
|
||||
diff_bullets = [
|
||||
"<b>Automated Development:</b> Built using VBA MCP Server v0.6.0+ for database automation",
|
||||
"<b>Clean Architecture:</b> Modular VBA design with clear separation of concerns",
|
||||
"<b>Production-Ready:</b> Complete with data validation, error handling, and user-friendly interfaces",
|
||||
"<b>Extensible:</b> Well-documented codebase ready for customization and enhancement",
|
||||
"<b>AI-Assisted:</b> Demonstrates cutting-edge development practices with MCP integration"
|
||||
]
|
||||
|
||||
for bullet in diff_bullets:
|
||||
story.append(Paragraph(f"• {bullet}", styles['Bullet']))
|
||||
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Technology Stack
|
||||
story.append(Paragraph("2.4 Technology Stack", styles['SubsectionHeader']))
|
||||
|
||||
tech_data = [
|
||||
['Component', 'Technology', 'Version/Details'],
|
||||
['Database Engine', 'Microsoft Access', '2016+ / Office 365'],
|
||||
['Programming Language', 'VBA', 'Visual Basic for Applications 7.1'],
|
||||
['Development Automation', 'VBA MCP Server', 'v0.6.0+ (Model Context Protocol)'],
|
||||
['Export Formats', 'PDF, Excel', 'Native Access/VBA integration'],
|
||||
['Version Control', 'Git', '2.x with source file exports'],
|
||||
]
|
||||
|
||||
tech_table = Table(tech_data, colWidths=[2*inch, 2.2*inch, 2.3*inch])
|
||||
tech_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 10),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 9),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 10),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 10),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 8),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 8),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 6),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 6),
|
||||
]))
|
||||
|
||||
story.append(tech_table)
|
||||
story.append(PageBreak())
|
||||
|
||||
|
||||
def create_section_3_database(story, styles):
|
||||
"""Section 3: Database Architecture"""
|
||||
|
||||
story.append(Paragraph("3. Database Architecture", styles['SectionHeader']))
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
story.append(Paragraph(
|
||||
"""The application uses a normalized relational database structure following third normal
|
||||
form (3NF) principles. The schema consists of three core tables with enforced referential
|
||||
integrity and optimized indexes for query performance.""",
|
||||
styles['BodyText']
|
||||
))
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
# ERD description
|
||||
story.append(Paragraph("3.1 Entity Relationship Diagram", styles['SubsectionHeader']))
|
||||
|
||||
erd_code = """
|
||||
tbl_Clients (1) ─────── (N) tbl_Projets
|
||||
│
|
||||
│
|
||||
tbl_Projets (1) ─────── (N) tbl_Temps
|
||||
|
||||
Relationships:
|
||||
• One Client can have many Projects (1:N)
|
||||
• One Project can have many Time Entries (1:N)
|
||||
• Referential integrity enforced with CASCADE options
|
||||
"""
|
||||
|
||||
story.append(Paragraph(erd_code, styles['Code']))
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Table: tbl_Clients
|
||||
story.append(Paragraph("3.2 Table: tbl_Clients", styles['SubsectionHeader']))
|
||||
story.append(Paragraph("Stores client information and contact details.", styles['BodyText']))
|
||||
story.append(Spacer(1, 0.1 * inch))
|
||||
|
||||
clients_data = [
|
||||
['Field', 'Type', 'Size', 'Constraints', 'Description'],
|
||||
['ClientID', 'AutoNumber', 'Long', 'PRIMARY KEY', 'Unique identifier'],
|
||||
['Nom', 'Text', '100', 'NOT NULL', 'Client name'],
|
||||
['Email', 'Text', '100', 'NULL, VALIDATED', 'Email address with format validation'],
|
||||
['Telephone', 'Text', '20', 'NULL', 'Phone number'],
|
||||
['Notes', 'Memo', '—', 'NULL', 'Additional notes and comments'],
|
||||
['DateCreation', 'DateTime', '—', 'NOT NULL, DEFAULT Now()', 'Record creation timestamp'],
|
||||
]
|
||||
|
||||
clients_table = Table(clients_data, colWidths=[1.2*inch, 1*inch, 0.7*inch, 1.4*inch, 2.2*inch])
|
||||
clients_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 9),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 8),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 8),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 5),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 5),
|
||||
]))
|
||||
|
||||
story.append(clients_table)
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
# Indexes
|
||||
index_style = ParagraphStyle('IndexNote', parent=styles['BodyText'], fontSize=9,
|
||||
textColor=GREY_COLOR, fontName='Helvetica-Oblique')
|
||||
story.append(Paragraph("<b>Indexes:</b> PRIMARY KEY on ClientID, Search Index on Nom", index_style))
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Table: tbl_Projets
|
||||
story.append(Paragraph("3.3 Table: tbl_Projets", styles['SubsectionHeader']))
|
||||
story.append(Paragraph("Stores project information linked to clients.", styles['BodyText']))
|
||||
story.append(Spacer(1, 0.1 * inch))
|
||||
|
||||
projets_data = [
|
||||
['Field', 'Type', 'Size', 'Constraints', 'Description'],
|
||||
['ProjetID', 'AutoNumber', 'Long', 'PRIMARY KEY', 'Unique identifier'],
|
||||
['ClientID', 'Long', '—', 'FOREIGN KEY', 'Reference to tbl_Clients.ClientID'],
|
||||
['Nom', 'Text', '100', 'NOT NULL', 'Project name'],
|
||||
['Description', 'Memo', '—', 'NULL', 'Project description and notes'],
|
||||
['TauxHoraire', 'Currency', '—', 'DEFAULT 0, >= 0', 'Hourly rate in EUR'],
|
||||
['Actif', 'Yes/No', '—', 'NOT NULL, DEFAULT True', 'Active/archived status'],
|
||||
['DateCreation', 'DateTime', '—', 'NOT NULL, DEFAULT Now()', 'Record creation timestamp'],
|
||||
]
|
||||
|
||||
projets_table = Table(projets_data, colWidths=[1.2*inch, 1*inch, 0.7*inch, 1.4*inch, 2.2*inch])
|
||||
projets_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 9),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 8),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 8),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 5),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 5),
|
||||
]))
|
||||
|
||||
story.append(projets_table)
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
story.append(Paragraph("<b>Indexes:</b> PRIMARY KEY on ProjetID, FOREIGN KEY on ClientID (with referential integrity), Performance Index on Actif", index_style))
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Table: tbl_Temps
|
||||
story.append(Paragraph("3.4 Table: tbl_Temps", styles['SubsectionHeader']))
|
||||
story.append(Paragraph("Stores time entry records.", styles['BodyText']))
|
||||
story.append(Spacer(1, 0.1 * inch))
|
||||
|
||||
temps_data = [
|
||||
['Field', 'Type', 'Size', 'Constraints', 'Description'],
|
||||
['TempsID', 'AutoNumber', 'Long', 'PRIMARY KEY', 'Unique identifier'],
|
||||
['ProjetID', 'Long', '—', 'FOREIGN KEY', 'Reference to tbl_Projets.ProjetID'],
|
||||
['Date', 'DateTime', '—', 'NOT NULL', 'Entry date (no future dates allowed)'],
|
||||
['Duree', 'Double', '—', 'NOT NULL, > 0', 'Duration in hours (decimal)'],
|
||||
['Description', 'Memo', '—', 'NULL', 'Work description'],
|
||||
['DateCreation', 'DateTime', '—', 'NOT NULL, DEFAULT Now()', 'Record creation timestamp'],
|
||||
]
|
||||
|
||||
temps_table = Table(temps_data, colWidths=[1.2*inch, 1*inch, 0.7*inch, 1.4*inch, 2.2*inch])
|
||||
temps_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), PRIMARY_COLOR),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (-1, 0), 9),
|
||||
('FONTSIZE', (0, 1), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 0), (-1, 0), 8),
|
||||
('TOPPADDING', (0, 0), (-1, 0), 8),
|
||||
('GRID', (0, 0), (-1, -1), 0.5, GREY_COLOR),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, LIGHT_GREY]),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
||||
('TOPPADDING', (0, 1), (-1, -1), 5),
|
||||
('BOTTOMPADDING', (0, 1), (-1, -1), 5),
|
||||
]))
|
||||
|
||||
story.append(temps_table)
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
story.append(Paragraph("<b>Indexes:</b> PRIMARY KEY on TempsID, FOREIGN KEY on ProjetID (with referential integrity), Performance Index on Date (for date range queries)", index_style))
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
# Data Integrity
|
||||
story.append(Paragraph("3.5 Data Integrity Rules", styles['SubsectionHeader']))
|
||||
|
||||
integrity_bullets = [
|
||||
"<b>Referential Integrity:</b> Foreign key constraints enforced with configurable CASCADE options",
|
||||
"<b>Email Validation:</b> Format validation on tbl_Clients.Email field",
|
||||
"<b>Positive Values:</b> CHECK constraints for TauxHoraire and Duree (must be >= 0)",
|
||||
"<b>Date Validation:</b> No future dates allowed beyond today's date",
|
||||
"<b>Orphan Prevention:</b> All foreign key constraints prevent orphaned records",
|
||||
"<b>Audit Trail:</b> All tables include DateCreation timestamps for tracking"
|
||||
]
|
||||
|
||||
for bullet in integrity_bullets:
|
||||
story.append(Paragraph(f"• {bullet}", styles['Bullet']))
|
||||
|
||||
story.append(PageBreak())
|
||||
|
||||
|
||||
# Continue in next part...
|
||||
print("Creating professional PDF documentation...")
|
||||
print("This script will generate a complete technical reference document.")
|
||||
print("")
|
||||
|
||||
# Create PDF
|
||||
doc = SimpleDocTemplate(
|
||||
OUTPUT_FILE,
|
||||
pagesize=letter,
|
||||
rightMargin=0.75*inch,
|
||||
leftMargin=0.75*inch,
|
||||
topMargin=1*inch,
|
||||
bottomMargin=0.75*inch,
|
||||
title="TimeTrack Pro - Technical Reference",
|
||||
author="Alexis Trouvé"
|
||||
)
|
||||
|
||||
# Create custom styles
|
||||
styles = create_styles()
|
||||
|
||||
# Story container
|
||||
story = []
|
||||
|
||||
# Build document
|
||||
print("Creating cover page...")
|
||||
create_cover_page(story, styles)
|
||||
|
||||
print("Creating Section 1: Executive Summary...")
|
||||
create_section_1_executive_summary(story, styles)
|
||||
|
||||
print("Creating Section 2: Project Overview...")
|
||||
create_section_2_project_overview(story, styles)
|
||||
|
||||
print("Creating Section 3: Database Architecture...")
|
||||
create_section_3_database(story, styles)
|
||||
|
||||
print("\nNote: This is Part 1. Continue with additional sections...")
|
||||
print("Generating PDF (Part 1)...")
|
||||
|
||||
# Build PDF with custom canvas
|
||||
doc.build(story, canvasmaker=NumberedCanvas)
|
||||
|
||||
print(f"\n✓ PDF generated successfully!")
|
||||
print(f"Location: {OUTPUT_FILE}")
|
||||
print(f"\nNote: This is a partial document. Run Part 2 script to add remaining sections.")
|
||||
186
html_forms/01_frm_Accueil.html
Normal file
186
html_forms/01_frm_Accueil.html
Normal file
@ -0,0 +1,186 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TimeTrack Pro - Accueil</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 35px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
animation: slideUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #667eea;
|
||||
font-size: 1.8em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 3px solid #667eea;
|
||||
}
|
||||
|
||||
.section-title:before {
|
||||
content: '📊 ';
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.form-group:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 0.85em;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.5px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
text-align: center;
|
||||
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 20px 30px;
|
||||
border-radius: 15px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px) scale(1.02);
|
||||
box-shadow: 0 12px 30px rgba(102, 126, 234, 0.5);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 3em;
|
||||
margin-bottom: 40px;
|
||||
text-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-top: 30px;
|
||||
font-size: 0.95em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🕐 TimeTrack Pro</h1>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Tableau de Bord</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Nombre de Clients</label>
|
||||
<input type="text" id="txtNbClients" name="NbClients" readonly value="0">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Projets Actifs</label>
|
||||
<input type="text" id="txtNbProjets" name="NbProjets" readonly value="0">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Heures ce Mois</label>
|
||||
<input type="text" id="txtHeuresMois" name="HeuresMois" readonly value="0h">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Total Heures</label>
|
||||
<input type="text" id="txtTotalHeures" name="TotalHeures" readonly value="0h">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Actions Rapides</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnClients">👥 Gérer Clients</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnProjets">📁 Gérer Projets</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnSaisie">⏱️ Saisir Temps</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnHistorique">📊 Voir Historique</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
TimeTrack Pro v1.0 - Gestion professionnelle du temps
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
220
html_forms/02_frm_Clients.html
Normal file
220
html_forms/02_frm_Clients.html
Normal file
@ -0,0 +1,220 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TimeTrack Pro - Gestion Clients</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 35px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
animation: slideIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #667eea;
|
||||
font-size: 1.6em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 3px solid #667eea;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #444;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.95em;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 14px 18px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
transition: all 0.3s ease;
|
||||
font-family: inherit;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 14px 24px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#btnEnregistrer {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnNouveau {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnSupprimer {
|
||||
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnRetour,
|
||||
#btnPremier,
|
||||
#btnPrecedent,
|
||||
#btnSuivant,
|
||||
#btnDernier {
|
||||
background: linear-gradient(135deg, #757f9a 0%, #d7dde8 100%);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 2.8em;
|
||||
margin-bottom: 35px;
|
||||
text-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #eb3349;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 0.85em;
|
||||
color: #777;
|
||||
font-style: italic;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>👥 Gestion des Clients</h1>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Informations Client</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Nom du Client <span class="required">*</span></label>
|
||||
<input type="text" id="txtNom" name="Nom" required maxlength="100" placeholder="Nom de l'entreprise ou du client">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Email</label>
|
||||
<input type="email" id="txtEmail" name="Email" maxlength="100" placeholder="contact@exemple.fr">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Téléphone</label>
|
||||
<input type="tel" id="txtTelephone" name="Telephone" maxlength="20" placeholder="01 23 45 67 89">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Notes</label>
|
||||
<textarea id="txtNotes" name="Notes" placeholder="Informations complémentaires, préférences, historique..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Actions</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnEnregistrer">💾 Enregistrer</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnNouveau">➕ Nouveau</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnSupprimer">🗑️ Supprimer</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnRetour">↩️ Retour</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Navigation</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnPremier">⏮️ Premier</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnPrecedent">◀️ Précédent</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnSuivant">▶️ Suivant</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnDernier">⏭️ Dernier</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
260
html_forms/03_frm_Projets.html
Normal file
260
html_forms/03_frm_Projets.html
Normal file
@ -0,0 +1,260 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TimeTrack Pro - Gestion Projets</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 35px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #667eea;
|
||||
font-size: 1.6em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 3px solid #667eea;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #444;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 14px 18px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
transition: all 0.3s ease;
|
||||
font-family: inherit;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
background: #e9ecef;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
accent-color: #667eea;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 14px 24px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#btnEnregistrer {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnNouveau {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnSupprimer {
|
||||
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnRetour,
|
||||
#btnPremier,
|
||||
#btnPrecedent,
|
||||
#btnSuivant,
|
||||
#btnDernier {
|
||||
background: linear-gradient(135deg, #757f9a 0%, #d7dde8 100%);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 2.8em;
|
||||
margin-bottom: 35px;
|
||||
text-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
box-shadow: 0 8px 20px rgba(240, 147, 251, 0.3);
|
||||
}
|
||||
|
||||
.stats-section .section-title {
|
||||
color: white;
|
||||
border-bottom-color: white;
|
||||
}
|
||||
|
||||
.stats-section input {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stats-section label {
|
||||
color: white;
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>📁 Gestion des Projets</h1>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Informations Projet</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Client</label>
|
||||
<select id="cboClient" name="ClientID" required>
|
||||
<option value="">-- Sélectionner un client --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Nom du Projet</label>
|
||||
<input type="text" id="txtNom" name="Nom" required maxlength="100" placeholder="Ex: Refonte site web">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Description</label>
|
||||
<textarea id="txtDescription" name="Description" placeholder="Description détaillée du projet..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Taux Horaire (EUR)</label>
|
||||
<input type="number" id="txtTauxHoraire" name="TauxHoraire" step="0.01" min="0" value="0" placeholder="75.00">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Projet Actif</label>
|
||||
<input type="checkbox" id="chkActif" name="Actif" checked>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section stats-section'>
|
||||
<div class='section-title'>Statistiques Projet</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Total Heures</label>
|
||||
<input type="text" id="txtTotalHeures" name="TotalHeures" readonly value="0h">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Montant Total</label>
|
||||
<input type="text" id="txtMontantTotal" name="MontantTotal" readonly value="0.00 EUR">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Actions</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnEnregistrer">💾 Enregistrer</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnNouveau">➕ Nouveau</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnSupprimer">🗑️ Supprimer</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnRetour">↩️ Retour</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Navigation</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnPremier">⏮️ Premier</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnPrecedent">◀️ Précédent</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnSuivant">▶️ Suivant</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnDernier">⏭️ Dernier</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
280
html_forms/04_frm_SaisieTemps.html
Normal file
280
html_forms/04_frm_SaisieTemps.html
Normal file
@ -0,0 +1,280 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TimeTrack Pro - Saisie Temps</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 35px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
animation: bounceIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes bounceIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #667eea;
|
||||
font-size: 1.6em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 3px solid #667eea;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #444;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="number"],
|
||||
input[type="text"],
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 14px 18px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
transition: all 0.3s ease;
|
||||
font-family: inherit;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
background: #e9ecef;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 14px 24px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.quick-btn {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnEnregistrer {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnNouveau {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnRetour {
|
||||
background: linear-gradient(135deg, #757f9a 0%, #d7dde8 100%);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 2.8em;
|
||||
margin-bottom: 35px;
|
||||
text-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.highlight-section {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.highlight-section .section-title {
|
||||
color: white;
|
||||
border-bottom-color: white;
|
||||
}
|
||||
|
||||
.highlight-section label {
|
||||
color: white;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.highlight-section input {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.recap-section {
|
||||
background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
|
||||
}
|
||||
|
||||
.recap-section .section-title {
|
||||
color: #f45c43;
|
||||
border-bottom-color: #f45c43;
|
||||
}
|
||||
|
||||
.recap-section label {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.recap-section input {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>⏱️ Saisie de Temps</h1>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Nouvelle Entrée</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Projet</label>
|
||||
<select id="cboProjet" name="ProjetID" required>
|
||||
<option value="">-- Sélectionner un projet --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Date</label>
|
||||
<input type="date" id="txtDate" name="Date" required>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Durée (heures)</label>
|
||||
<input type="number" id="txtDuree" name="Duree" required step="0.25" min="0.25" max="24" placeholder="3.5">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Description</label>
|
||||
<textarea id="txtDescription" name="Description" placeholder="Détaillez le travail effectué..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Actions Rapides - Durée</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="quick-btn" id="btn30min">30 min</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="quick-btn" id="btn1h">1h</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="quick-btn" id="btn2h">2h</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="quick-btn" id="btn4h">4h</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="quick-btn" id="btn8h">8h</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section highlight-section'>
|
||||
<div class='section-title'>Calcul</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Montant Calculé</label>
|
||||
<input type="text" id="txtMontant" name="Montant" readonly value="0.00 EUR">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Actions</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnEnregistrer">💾 Enregistrer</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnNouveau">➕ Nouvelle Entrée</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnRetour">↩️ Retour</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section recap-section'>
|
||||
<div class='section-title'>Récap du Jour</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Entrées Aujourd'hui</label>
|
||||
<input type="text" id="txtNbEntrees" name="NbEntrees" readonly value="0">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Total Heures</label>
|
||||
<input type="text" id="txtTotalJour" name="TotalJour" readonly value="0h">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
283
html_forms/05_frm_Historique.html
Normal file
283
html_forms/05_frm_Historique.html
Normal file
@ -0,0 +1,283 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TimeTrack Pro - Historique</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 35px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
animation: slideRight 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(50px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #667eea;
|
||||
font-size: 1.6em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 3px solid #667eea;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #444;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="text"],
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 14px 18px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
transition: all 0.3s ease;
|
||||
font-family: inherit;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
background: #e9ecef;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 14px 20px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 0.95em;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#btnFiltrer {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnExportPDF {
|
||||
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnExportExcel {
|
||||
background: linear-gradient(135deg, #30cfd0 0%, #330867 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#btnRetour,
|
||||
.preset-btn {
|
||||
background: linear-gradient(135deg, #757f9a 0%, #d7dde8 100%);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 2.8em;
|
||||
margin-bottom: 35px;
|
||||
text-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.quick-filters-section {
|
||||
background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%);
|
||||
}
|
||||
|
||||
.quick-filters-section .section-title {
|
||||
color: #5e3da8;
|
||||
border-bottom-color: #5e3da8;
|
||||
}
|
||||
|
||||
.quick-filters-section .preset-btn {
|
||||
background: white;
|
||||
border: 2px solid #5e3da8;
|
||||
color: #5e3da8;
|
||||
}
|
||||
|
||||
.quick-filters-section .preset-btn:hover {
|
||||
background: #5e3da8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
|
||||
}
|
||||
|
||||
.stats-section .section-title {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.stats-section label {
|
||||
color: #333;
|
||||
font-size: 0.8em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.stats-section input {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 1.8em;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>📊 Historique et Rapports</h1>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Filtres</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Date Début</label>
|
||||
<input type="date" id="txtDateDebut" name="DateDebut">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Date Fin</label>
|
||||
<input type="date" id="txtDateFin" name="DateFin">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Client</label>
|
||||
<select id="cboClient" name="ClientID">
|
||||
<option value="">Tous les clients</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Projet</label>
|
||||
<select id="cboProjet" name="ProjetID">
|
||||
<option value="">Tous les projets</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section'>
|
||||
<div class='section-title'>Actions</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnFiltrer">🔍 Filtrer</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnExportPDF">📄 Export PDF</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnExportExcel">📊 Export Excel</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button id="btnRetour">↩️ Retour</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section quick-filters-section'>
|
||||
<div class='section-title'>Filtres Rapides</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="preset-btn" id="btnAujourdhui">📅 Aujourd'hui</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="preset-btn" id="btnSemaine">📆 Cette Semaine</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="preset-btn" id="btnMois">🗓️ Ce Mois</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="preset-btn" id="btnMoisDernier">⬅️ Mois Dernier</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="preset-btn" id="btnAnnee">📊 Cette Année</button>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<button class="preset-btn" id="btnTout">🌍 Tout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-section stats-section'>
|
||||
<div class='section-title'>Statistiques</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Entrées</label>
|
||||
<input type="text" id="txtNbEntrees" name="NbEntrees" readonly value="0">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Total Heures</label>
|
||||
<input type="text" id="txtTotalHeures" name="TotalHeures" readonly value="0h">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Montant Total</label>
|
||||
<input type="text" id="txtMontantTotal" name="MontantTotal" readonly value="0.00 €">
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label>Moyenne / Jour</label>
|
||||
<input type="text" id="txtMoyenne" name="Moyenne" readonly value="0h">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
20
list_all_modules.vbs
Normal file
20
list_all_modules.vbs
Normal file
@ -0,0 +1,20 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== All VBA Components ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
WScript.Echo comp.Name & " - Type: " & comp.Type
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
50
rename_orphan_modules.vbs
Normal file
50
rename_orphan_modules.vbs
Normal file
@ -0,0 +1,50 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
|
||||
WScript.Echo "=== Renaming orphan Form_* Type 1 modules ==="
|
||||
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim modulesToRename
|
||||
modulesToRename = Array("Form_frm_Accueil", "Form_frm_Clients", "Form_frm_Projets", "Form_frm_Historique")
|
||||
|
||||
Dim i
|
||||
For i = LBound(modulesToRename) To UBound(modulesToRename)
|
||||
Dim comp
|
||||
Set comp = Nothing
|
||||
|
||||
Dim c
|
||||
For Each c In vbProj.VBComponents
|
||||
If c.Name = modulesToRename(i) And c.Type = 1 Then
|
||||
Set comp = c
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
||||
If Not comp Is Nothing Then
|
||||
Dim newName
|
||||
newName = "OLD_" & modulesToRename(i)
|
||||
WScript.Echo "Renaming: " & comp.Name & " -> " & newName
|
||||
comp.Name = newName
|
||||
WScript.Echo " -> Renamed!"
|
||||
Else
|
||||
WScript.Echo "Not found or not Type 1: " & modulesToRename(i)
|
||||
End If
|
||||
Next
|
||||
|
||||
WScript.Echo ""
|
||||
WScript.Echo "=== Remaining Form_* modules ==="
|
||||
For Each c In vbProj.VBComponents
|
||||
If Left(c.Name, 5) = "Form_" Then
|
||||
WScript.Echo c.Name & " - Type: " & c.Type
|
||||
End If
|
||||
Next
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
12
run_fix_bindings.vbs
Normal file
12
run_fix_bindings.vbs
Normal file
@ -0,0 +1,12 @@
|
||||
Dim accessApp
|
||||
Set accessApp = GetObject(, "Access.Application")
|
||||
|
||||
On Error Resume Next
|
||||
accessApp.Run "mod_FixFormBindings.FixAllFormBindings"
|
||||
|
||||
If Err.Number <> 0 Then
|
||||
WScript.Echo "Error: " & Err.Description
|
||||
WScript.Quit 1
|
||||
Else
|
||||
WScript.Echo "Success! Form bindings fixed."
|
||||
End If
|
||||
19
set_recordsource.vbs
Normal file
19
set_recordsource.vbs
Normal file
@ -0,0 +1,19 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = False
|
||||
|
||||
' Fix frm_SaisieTemps RecordSource
|
||||
accessApp.DoCmd.OpenForm "frm_SaisieTemps", 2 ' acDesign
|
||||
WScript.Sleep 500
|
||||
accessApp.Forms("frm_SaisieTemps").RecordSource = "tbl_Temps"
|
||||
accessApp.DoCmd.Close 2, "frm_SaisieTemps", 1
|
||||
WScript.Echo "frm_SaisieTemps RecordSource set to tbl_Temps"
|
||||
|
||||
' Cleanup
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
24
test_button.vbs
Normal file
24
test_button.vbs
Normal file
@ -0,0 +1,24 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = True
|
||||
|
||||
' Ouvrir le formulaire Accueil
|
||||
accessApp.DoCmd.OpenForm "frm_Accueil", 0 ' 0 = acNormal
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Declencher le clic sur le bouton
|
||||
accessApp.Forms("frm_Accueil").btnClients.SetFocus
|
||||
accessApp.Forms("frm_Accueil").btnClients_Click
|
||||
|
||||
WScript.Sleep 3000
|
||||
|
||||
' Attendre pour voir les messages
|
||||
WScript.Echo "Test termine. Appuyez sur OK pour fermer."
|
||||
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
52
test_create_simple_form.vbs
Normal file
52
test_create_simple_form.vbs
Normal file
@ -0,0 +1,52 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = True
|
||||
|
||||
WScript.Echo "Creating test form..."
|
||||
|
||||
' Creer un formulaire simple
|
||||
accessApp.DoCmd.NewObject 2, "frm_Test", "Normal" ' 2 = acForm
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Ouvrir en mode Design
|
||||
accessApp.DoCmd.OpenForm "frm_Test", 2
|
||||
|
||||
WScript.Sleep 500
|
||||
|
||||
' Activer HasModule
|
||||
WScript.Echo "Setting HasModule = True..."
|
||||
accessApp.Forms("frm_Test").HasModule = True
|
||||
|
||||
If Err.Number = 0 Then
|
||||
WScript.Echo "SUCCESS! HasModule activated for test form"
|
||||
|
||||
' Sauvegarder
|
||||
accessApp.DoCmd.Close 2, "frm_Test", 1
|
||||
|
||||
WScript.Sleep 500
|
||||
|
||||
' Verifier
|
||||
Dim vbProj
|
||||
Set vbProj = accessApp.VBE.VBProjects(1)
|
||||
|
||||
Dim comp
|
||||
For Each comp In vbProj.VBComponents
|
||||
If comp.Name = "Form_frm_Test" Then
|
||||
WScript.Echo "Module created! Type: " & comp.Type
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
WScript.Echo "FAILED: " & Err.Description
|
||||
End If
|
||||
|
||||
WScript.Echo "Press OK to close."
|
||||
|
||||
accessApp.CloseCurrentDatabase
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
38
test_direct_call.vbs
Normal file
38
test_direct_call.vbs
Normal file
@ -0,0 +1,38 @@
|
||||
On Error Resume Next
|
||||
|
||||
Dim accessApp
|
||||
Set accessApp = CreateObject("Access.Application")
|
||||
|
||||
accessApp.OpenCurrentDatabase "C:\Users\alexi\Documents\projects\timetrack-pro\db\TimeTrackPro.accdb"
|
||||
accessApp.Visible = False
|
||||
|
||||
' Ouvrir le formulaire en mode normal
|
||||
accessApp.DoCmd.OpenForm "frm_Accueil", 0 ' acNormal
|
||||
|
||||
WScript.Sleep 1000
|
||||
|
||||
' Essayer d'appeler directement la procedure
|
||||
accessApp.Run "Form_frm_Accueil.btnClients_Click"
|
||||
|
||||
If Err.Number <> 0 Then
|
||||
WScript.Echo "Erreur lors de l'appel: " & Err.Number & " - " & Err.Description
|
||||
Err.Clear
|
||||
End If
|
||||
|
||||
WScript.Sleep 2000
|
||||
|
||||
' Lire le log si créé
|
||||
Dim fso
|
||||
Set fso = CreateObject("Scripting.FileSystemObject")
|
||||
If fso.FileExists("C:\Users\alexi\Documents\projects\timetrack-pro\button_log.txt") Then
|
||||
Dim logFile
|
||||
Set logFile = fso.OpenTextFile("C:\Users\alexi\Documents\projects\timetrack-pro\button_log.txt", 1)
|
||||
WScript.Echo "LOG CONTENT:"
|
||||
WScript.Echo logFile.ReadAll()
|
||||
logFile.Close
|
||||
Else
|
||||
WScript.Echo "Aucun fichier log cree - le code n'a pas ete execute"
|
||||
End If
|
||||
|
||||
accessApp.Quit
|
||||
Set accessApp = Nothing
|
||||
Loading…
Reference in New Issue
Block a user