triflix commited on
Commit
0a515cd
·
verified ·
1 Parent(s): 5768d5b

Upload 23 files

Browse files
Frontendplanner.md ADDED
@@ -0,0 +1,920 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Frontend Developer API Documentation
2
+ # Architecture PM System - KillaDesign
3
+
4
+ **Base URL**: `http://localhost:8000` (Development)
5
+ **Framework**: FastAPI
6
+ **Database**: PostgreSQL
7
+
8
+ ---
9
+
10
+ ## Table of Contents
11
+ 1. [Dashboard](#dashboard)
12
+ 2. [Projects](#projects)
13
+ 3. [Employees](#employees)
14
+ 4. [Timesheets](#timesheets)
15
+ 5. [Milestones](#milestones)
16
+ 6. [Invoices](#invoices)
17
+ 7. [Subcontractors](#subcontractors)
18
+ 8. [Staff Allocation](#staff-allocation)
19
+ 9. [Data Models](#data-models)
20
+
21
+ ---
22
+
23
+ ## Dashboard
24
+
25
+ ### Get Dashboard Statistics
26
+ Returns statistics for display on the home page.
27
+
28
+ **Endpoint**: `GET /`
29
+ **Response Type**: HTML Page
30
+ **Description**: Returns rendered HTML with dashboard statistics
31
+
32
+ **Response Context**:
33
+ ```json
34
+ {
35
+ "projects_count": 0,
36
+ "employees_count": 0,
37
+ "timesheets_count": 0,
38
+ "invoices_count": 0
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Projects
45
+
46
+ ### 1. List All Projects (HTML)
47
+ **Endpoint**: `GET /projects`
48
+ **Response Type**: HTML
49
+ **Description**: Returns rendered HTML page with projects list
50
+
51
+ ---
52
+
53
+ ### 2. List All Projects (API)
54
+ **Endpoint**: `GET /api/projects`
55
+ **Response Type**: JSON
56
+ **Description**: Returns array of all projects
57
+
58
+ **Response Example**:
59
+ ```json
60
+ [
61
+ {
62
+ "project_id": "PROJ001",
63
+ "project_name": "Villa Design",
64
+ "client_name": "ABC Corporation",
65
+ "contract_value_aed": 500000.00,
66
+ "planned_cost_aed": 400000.00,
67
+ "project_start_date": "2024-01-01",
68
+ "project_end_date": "2024-12-31",
69
+ "project_type": "Residential",
70
+ "project_status": "Active",
71
+ "current_phase": "Design Development",
72
+ "created_at": "2024-01-01T10:00:00Z",
73
+ "updated_at": "2024-01-01T10:00:00Z"
74
+ }
75
+ ]
76
+ ```
77
+
78
+ ---
79
+
80
+ ### 3. Create Project
81
+ **Endpoint**: `POST /projects/create`
82
+ **Content-Type**: `application/x-www-form-urlencoded`
83
+ **Description**: Creates a new project
84
+
85
+ **Request Parameters**:
86
+ | Parameter | Type | Required | Description |
87
+ |-----------|------|----------|-------------|
88
+ | project_id | string | Yes | Unique project identifier |
89
+ | project_name | string | Yes | Name of the project |
90
+ | client_name | string | Yes | Name of the client |
91
+ | contract_value_aed | float | Yes | Contract value in AED |
92
+ | planned_cost_aed | float | Yes | Planned cost in AED |
93
+ | project_start_date | date | Yes | Start date (YYYY-MM-DD) |
94
+ | project_end_date | date | Yes | End date (YYYY-MM-DD) |
95
+ | project_type | string | Yes | Type of project |
96
+ | project_status | string | Yes | Current status |
97
+ | current_phase | string | No | Current project phase |
98
+
99
+ **Example Request (Form Data)**:
100
+ ```
101
+ project_id=PROJ002
102
+ project_name=Modern Office Building
103
+ client_name=XYZ Ltd
104
+ contract_value_aed=1500000.00
105
+ planned_cost_aed=1200000.00
106
+ project_start_date=2024-02-01
107
+ project_end_date=2025-02-01
108
+ project_type=Commercial
109
+ project_status=Planning
110
+ current_phase=Initial Design
111
+ ```
112
+
113
+ **Response**: Redirects to `/projects` (Status 303)
114
+
115
+ ---
116
+
117
+ ### 4. Delete Project
118
+ **Endpoint**: `POST /projects/delete/{project_id}`
119
+ **Description**: Deletes a project by ID
120
+
121
+ **Path Parameters**:
122
+ - `project_id`: string - The project ID to delete
123
+
124
+ **Response**: Redirects to `/projects` (Status 303)
125
+
126
+ ---
127
+
128
+ ## Employees
129
+
130
+ ### 1. List All Employees (HTML)
131
+ **Endpoint**: `GET /employees`
132
+ **Response Type**: HTML
133
+ **Description**: Returns rendered HTML page with employees list
134
+
135
+ ---
136
+
137
+ ### 2. List All Employees (API)
138
+ **Endpoint**: `GET /api/employees`
139
+ **Response Type**: JSON
140
+ **Description**: Returns array of all employees
141
+
142
+ **Response Example**:
143
+ ```json
144
+ [
145
+ {
146
+ "employee_id": "EMP001",
147
+ "employee_name": "John Doe",
148
+ "department": "Architecture",
149
+ "role": "Senior Architect",
150
+ "hourly_rate_aed": 250.00,
151
+ "employment_type": "Full-time",
152
+ "start_date": "2023-01-15",
153
+ "end_date": null,
154
+ "cost_category": "Professional",
155
+ "is_active": true,
156
+ "email": "john.doe@example.com",
157
+ "phone": "+971501234567",
158
+ "created_at": "2023-01-15T09:00:00Z",
159
+ "updated_at": "2023-01-15T09:00:00Z"
160
+ }
161
+ ]
162
+ ```
163
+
164
+ ---
165
+
166
+ ### 3. Create Employee
167
+ **Endpoint**: `POST /employees/create`
168
+ **Content-Type**: `application/x-www-form-urlencoded`
169
+ **Description**: Creates a new employee
170
+
171
+ **Request Parameters**:
172
+ | Parameter | Type | Required | Description |
173
+ |-----------|------|----------|-------------|
174
+ | employee_id | string | Yes | Unique employee identifier |
175
+ | employee_name | string | Yes | Employee's full name |
176
+ | department | string | Yes | Department name |
177
+ | role | string | Yes | Job role/title |
178
+ | hourly_rate_aed | float | Yes | Hourly rate in AED |
179
+ | employment_type | string | Yes | Full-time/Part-time/Contract |
180
+ | start_date | date | Yes | Employment start date |
181
+ | cost_category | string | Yes | Cost category classification |
182
+ | email | string | No | Email address |
183
+ | phone | string | No | Phone number |
184
+
185
+ **Example Request (Form Data)**:
186
+ ```
187
+ employee_id=EMP002
188
+ employee_name=Jane Smith
189
+ department=Interior Design
190
+ role=Interior Designer
191
+ hourly_rate_aed=180.00
192
+ employment_type=Full-time
193
+ start_date=2023-06-01
194
+ cost_category=Professional
195
+ email=jane.smith@example.com
196
+ phone=+971507654321
197
+ ```
198
+
199
+ **Response**: Redirects to `/employees` (Status 303)
200
+
201
+ ---
202
+
203
+ ### 4. Delete Employee
204
+ **Endpoint**: `POST /employees/delete/{employee_id}`
205
+ **Description**: Deletes an employee by ID
206
+
207
+ **Path Parameters**:
208
+ - `employee_id`: string - The employee ID to delete
209
+
210
+ **Response**: Redirects to `/employees` (Status 303)
211
+
212
+ ---
213
+
214
+ ## Timesheets
215
+
216
+ ### 1. List All Timesheets (HTML)
217
+ **Endpoint**: `GET /timesheets`
218
+ **Response Type**: HTML
219
+ **Description**: Returns rendered HTML page with timesheets list, employees, and projects
220
+
221
+ ---
222
+
223
+ ### 2. List All Timesheets (API)
224
+ **Endpoint**: `GET /api/timesheets`
225
+ **Response Type**: JSON
226
+ **Description**: Returns array of timesheets (limited to 200 records)
227
+
228
+ **Response Example**:
229
+ ```json
230
+ [
231
+ {
232
+ "record_id": "TS001",
233
+ "date": "2024-01-15",
234
+ "employee_id": "EMP001",
235
+ "project_id": "PROJ001",
236
+ "hours_worked": 8.00,
237
+ "billable_hours": 7.50,
238
+ "work_category": "Design",
239
+ "task_description": "Conceptual drawings and sketches",
240
+ "is_approved": false,
241
+ "created_at": "2024-01-15T18:00:00Z",
242
+ "updated_at": "2024-01-15T18:00:00Z"
243
+ }
244
+ ]
245
+ ```
246
+
247
+ ---
248
+
249
+ ### 3. Create Timesheet
250
+ **Endpoint**: `POST /timesheets/create`
251
+ **Content-Type**: `application/x-www-form-urlencoded`
252
+ **Description**: Creates a new timesheet entry
253
+
254
+ **Request Parameters**:
255
+ | Parameter | Type | Required | Description |
256
+ |-----------|------|----------|-------------|
257
+ | record_id | string | Yes | Unique record identifier |
258
+ | date | date | Yes | Date of work (YYYY-MM-DD) |
259
+ | employee_id | string | Yes | Employee ID (FK) |
260
+ | project_id | string | Yes | Project ID (FK) |
261
+ | hours_worked | float | Yes | Total hours worked |
262
+ | billable_hours | float | Yes | Billable hours |
263
+ | work_category | string | Yes | Category of work |
264
+ | task_description | string | No | Description of tasks |
265
+
266
+ **Example Request (Form Data)**:
267
+ ```
268
+ record_id=TS002
269
+ date=2024-01-16
270
+ employee_id=EMP001
271
+ project_id=PROJ001
272
+ hours_worked=8.00
273
+ billable_hours=8.00
274
+ work_category=Design Development
275
+ task_description=Detailed floor plans and elevations
276
+ ```
277
+
278
+ **Response**: Redirects to `/timesheets` (Status 303)
279
+
280
+ ---
281
+
282
+ ### 4. Delete Timesheet
283
+ **Endpoint**: `POST /timesheets/delete/{record_id}`
284
+ **Description**: Deletes a timesheet entry by ID
285
+
286
+ **Path Parameters**:
287
+ - `record_id`: string - The timesheet record ID to delete
288
+
289
+ **Response**: Redirects to `/timesheets` (Status 303)
290
+
291
+ ---
292
+
293
+ ## Milestones
294
+
295
+ ### 1. List All Milestones (HTML)
296
+ **Endpoint**: `GET /milestones`
297
+ **Response Type**: HTML
298
+ **Description**: Returns rendered HTML page with milestones list and projects
299
+
300
+ ---
301
+
302
+ ### 2. List All Milestones (API)
303
+ **Endpoint**: `GET /api/milestones`
304
+ **Response Type**: JSON
305
+ **Description**: Returns array of all milestones
306
+
307
+ **Response Example**:
308
+ ```json
309
+ [
310
+ {
311
+ "milestone_id": 1,
312
+ "project_id": "PROJ001",
313
+ "milestone_name": "Concept Design Approval",
314
+ "milestone_order": 1,
315
+ "planned_date": "2024-02-15",
316
+ "actual_date": "2024-02-14",
317
+ "status": "Completed",
318
+ "completion_percentage": 100,
319
+ "created_at": "2024-01-01T10:00:00Z",
320
+ "updated_at": "2024-02-14T16:00:00Z"
321
+ }
322
+ ]
323
+ ```
324
+
325
+ ---
326
+
327
+ ### 3. Create Milestone
328
+ **Endpoint**: `POST /milestones/create`
329
+ **Content-Type**: `application/x-www-form-urlencoded`
330
+ **Description**: Creates a new milestone
331
+
332
+ **Request Parameters**:
333
+ | Parameter | Type | Required | Description |
334
+ |-----------|------|----------|-------------|
335
+ | project_id | string | Yes | Project ID (FK) |
336
+ | milestone_name | string | Yes | Name of milestone |
337
+ | milestone_order | integer | Yes | Order/sequence number |
338
+ | planned_date | date | Yes | Planned completion date |
339
+ | status | string | Yes | Current status |
340
+ | completion_percentage | integer | No | Completion % (0-100) |
341
+ | actual_date | date | No | Actual completion date |
342
+
343
+ **Example Request (Form Data)**:
344
+ ```
345
+ project_id=PROJ001
346
+ milestone_name=Design Development Complete
347
+ milestone_order=2
348
+ planned_date=2024-04-30
349
+ status=In Progress
350
+ completion_percentage=45
351
+ ```
352
+
353
+ **Response**: Redirects to `/milestones` (Status 303)
354
+
355
+ ---
356
+
357
+ ### 4. Delete Milestone
358
+ **Endpoint**: `POST /milestones/delete/{milestone_id}`
359
+ **Description**: Deletes a milestone by ID
360
+
361
+ **Path Parameters**:
362
+ - `milestone_id`: integer - The milestone ID to delete
363
+
364
+ **Response**: Redirects to `/milestones` (Status 303)
365
+
366
+ ---
367
+
368
+ ## Invoices
369
+
370
+ ### 1. List All Invoices (HTML)
371
+ **Endpoint**: `GET /invoices`
372
+ **Response Type**: HTML
373
+ **Description**: Returns rendered HTML page with invoices list and projects
374
+
375
+ ---
376
+
377
+ ### 2. List All Invoices (API)
378
+ **Endpoint**: `GET /api/invoices`
379
+ **Response Type**: JSON
380
+ **Description**: Returns array of all invoices
381
+
382
+ **Response Example**:
383
+ ```json
384
+ [
385
+ {
386
+ "invoice_id": "INV001",
387
+ "project_id": "PROJ001",
388
+ "invoice_date": "2024-02-01",
389
+ "invoice_amount_aed": 50000.00,
390
+ "due_date": "2024-03-01",
391
+ "payment_date": "2024-02-28",
392
+ "payment_status": "Paid",
393
+ "days_outstanding": null,
394
+ "milestone_reference": "Concept Design Approval",
395
+ "paid_amount_aed": 50000.00,
396
+ "created_at": "2024-02-01T10:00:00Z",
397
+ "updated_at": "2024-02-28T14:30:00Z"
398
+ }
399
+ ]
400
+ ```
401
+
402
+ ---
403
+
404
+ ### 3. Create Invoice
405
+ **Endpoint**: `POST /invoices/create`
406
+ **Content-Type**: `application/x-www-form-urlencoded`
407
+ **Description**: Creates a new invoice
408
+
409
+ **Request Parameters**:
410
+ | Parameter | Type | Required | Description |
411
+ |-----------|------|----------|-------------|
412
+ | invoice_id | string | Yes | Unique invoice identifier |
413
+ | project_id | string | Yes | Project ID (FK) |
414
+ | invoice_date | date | Yes | Invoice issue date |
415
+ | invoice_amount_aed | float | Yes | Invoice amount in AED |
416
+ | due_date | date | Yes | Payment due date |
417
+ | payment_status | string | Yes | Payment status |
418
+ | payment_date | date | No | Actual payment date |
419
+ | milestone_reference | string | No | Related milestone |
420
+
421
+ **Example Request (Form Data)**:
422
+ ```
423
+ invoice_id=INV002
424
+ project_id=PROJ001
425
+ invoice_date=2024-05-01
426
+ invoice_amount_aed=75000.00
427
+ due_date=2024-05-31
428
+ payment_status=Pending
429
+ milestone_reference=Design Development Complete
430
+ ```
431
+
432
+ **Response**: Redirects to `/invoices` (Status 303)
433
+
434
+ ---
435
+
436
+ ### 4. Delete Invoice
437
+ **Endpoint**: `POST /invoices/delete/{invoice_id}`
438
+ **Description**: Deletes an invoice by ID
439
+
440
+ **Path Parameters**:
441
+ - `invoice_id`: string - The invoice ID to delete
442
+
443
+ **Response**: Redirects to `/invoices` (Status 303)
444
+
445
+ ---
446
+
447
+ ## Subcontractors
448
+
449
+ ### 1. List All Subcontractors (HTML)
450
+ **Endpoint**: `GET /subcontractors`
451
+ **Response Type**: HTML
452
+ **Description**: Returns rendered HTML page with subcontractors list and projects
453
+
454
+ ---
455
+
456
+ ### 2. List All Subcontractors (API)
457
+ **Endpoint**: `GET /api/subcontractors`
458
+ **Response Type**: JSON
459
+ **Description**: Returns array of all subcontractors
460
+
461
+ **Response Example**:
462
+ ```json
463
+ [
464
+ {
465
+ "subcontractor_id": 1,
466
+ "project_id": "PROJ001",
467
+ "subcontractor_name": "Elite MEP Services",
468
+ "service_type": "MEP Consultation",
469
+ "contract_amount_aed": 150000.00,
470
+ "amount_invoiced_aed": 50000.00,
471
+ "payment_status": "Partially Paid",
472
+ "work_category": "MEP Design",
473
+ "contract_start_date": "2024-02-01",
474
+ "contract_end_date": "2024-12-31",
475
+ "contact_person": "Ahmed Ali",
476
+ "contact_email": "ahmed@elitemep.com",
477
+ "contact_phone": "+971504567890",
478
+ "created_at": "2024-02-01T09:00:00Z",
479
+ "updated_at": "2024-02-01T09:00:00Z"
480
+ }
481
+ ]
482
+ ```
483
+
484
+ ---
485
+
486
+ ### 3. Create Subcontractor
487
+ **Endpoint**: `POST /subcontractors/create`
488
+ **Content-Type**: `application/x-www-form-urlencoded`
489
+ **Description**: Creates a new subcontractor
490
+
491
+ **Request Parameters**:
492
+ | Parameter | Type | Required | Description |
493
+ |-----------|------|----------|-------------|
494
+ | project_id | string | Yes | Project ID (FK) |
495
+ | subcontractor_name | string | Yes | Subcontractor company name |
496
+ | service_type | string | Yes | Type of service provided |
497
+ | contract_amount_aed | float | Yes | Contract amount in AED |
498
+ | amount_invoiced_aed | float | No | Amount invoiced (default: 0) |
499
+ | payment_status | string | Yes | Payment status |
500
+ | work_category | string | Yes | Work category |
501
+ | contact_person | string | No | Contact person name |
502
+ | contact_email | string | No | Contact email |
503
+
504
+ **Example Request (Form Data)**:
505
+ ```
506
+ project_id=PROJ001
507
+ subcontractor_name=Structural Engineering Co
508
+ service_type=Structural Design
509
+ contract_amount_aed=80000.00
510
+ amount_invoiced_aed=0
511
+ payment_status=Not Started
512
+ work_category=Structural
513
+ contact_person=Mohammed Hassan
514
+ contact_email=mohammed@structural.com
515
+ ```
516
+
517
+ **Response**: Redirects to `/subcontractors` (Status 303)
518
+
519
+ ---
520
+
521
+ ### 4. Delete Subcontractor
522
+ **Endpoint**: `POST /subcontractors/delete/{subcontractor_id}`
523
+ **Description**: Deletes a subcontractor by ID
524
+
525
+ **Path Parameters**:
526
+ - `subcontractor_id`: integer - The subcontractor ID to delete
527
+
528
+ **Response**: Redirects to `/subcontractors` (Status 303)
529
+
530
+ ---
531
+
532
+ ## Staff Allocation
533
+
534
+ ### 1. List All Staff Allocations (HTML)
535
+ **Endpoint**: `GET /staff-allocation`
536
+ **Response Type**: HTML
537
+ **Description**: Returns rendered HTML page with staff allocations, projects, employees, and milestones
538
+
539
+ ---
540
+
541
+ ### 2. List All Staff Allocations (API)
542
+ **Endpoint**: `GET /api/staff-allocation`
543
+ **Response Type**: JSON
544
+ **Description**: Returns array of all staff allocations
545
+
546
+ **Response Example**:
547
+ ```json
548
+ [
549
+ {
550
+ "allocation_id": 1,
551
+ "project_id": "PROJ001",
552
+ "milestone_id": 1,
553
+ "milestone_name": "Concept Design Approval",
554
+ "employee_id": "EMP001",
555
+ "employee_name": "John Doe",
556
+ "role": "Senior Architect",
557
+ "category": "Professional",
558
+ "hours_allocated": 160.00,
559
+ "hours_worked": 142.50,
560
+ "hourly_rate_aed": 250.00,
561
+ "skill_match_score": 95,
562
+ "availability_status": "Available",
563
+ "performance_rating": "Excellent",
564
+ "start_date": "2024-01-01",
565
+ "end_date": "2024-02-15",
566
+ "notes": "Lead designer for concept phase",
567
+ "created_at": "2024-01-01T10:00:00Z",
568
+ "updated_at": "2024-02-15T16:00:00Z"
569
+ }
570
+ ]
571
+ ```
572
+
573
+ ---
574
+
575
+ ### 3. Create Staff Allocation
576
+ **Endpoint**: `POST /staff-allocation/create`
577
+ **Content-Type**: `application/x-www-form-urlencoded`
578
+ **Description**: Creates a new staff allocation
579
+
580
+ **Request Parameters**:
581
+ | Parameter | Type | Required | Description |
582
+ |-----------|------|----------|-------------|
583
+ | project_id | string | Yes | Project ID (FK) |
584
+ | employee_id | string | Yes | Employee ID (FK) |
585
+ | employee_name | string | Yes | Employee's full name |
586
+ | role | string | Yes | Role/position |
587
+ | hours_allocated | float | Yes | Hours allocated |
588
+ | hours_worked | float | No | Hours worked (default: 0) |
589
+ | hourly_rate_aed | float | Yes | Hourly rate in AED |
590
+ | start_date | date | Yes | Allocation start date |
591
+ | milestone_id | integer | No | Milestone ID (FK) |
592
+ | milestone_name | string | No | Milestone name |
593
+ | category | string | No | Category classification |
594
+ | skill_match_score | integer | No | Skill match score (0-100) |
595
+ | availability_status | string | No | Availability status |
596
+ | performance_rating | string | No | Performance rating |
597
+ | end_date | date | No | Allocation end date |
598
+ | notes | string | No | Additional notes |
599
+
600
+ **Example Request (Form Data)**:
601
+ ```
602
+ project_id=PROJ001
603
+ employee_id=EMP001
604
+ employee_name=John Doe
605
+ role=Senior Architect
606
+ hours_allocated=160.00
607
+ hours_worked=0
608
+ hourly_rate_aed=250.00
609
+ start_date=2024-03-01
610
+ milestone_id=2
611
+ milestone_name=Design Development Complete
612
+ category=Professional
613
+ skill_match_score=95
614
+ availability_status=Available
615
+ performance_rating=Excellent
616
+ end_date=2024-04-30
617
+ notes=Lead architect for design development phase
618
+ ```
619
+
620
+ **Response**: Redirects to `/staff-allocation` (Status 303)
621
+
622
+ ---
623
+
624
+ ### 4. Delete Staff Allocation
625
+ **Endpoint**: `POST /staff-allocation/delete/{allocation_id}`
626
+ **Description**: Deletes a staff allocation by ID
627
+
628
+ **Path Parameters**:
629
+ - `allocation_id`: integer - The allocation ID to delete
630
+
631
+ **Response**: Redirects to `/staff-allocation` (Status 303)
632
+
633
+ ---
634
+
635
+ ## Data Models
636
+
637
+ ### Project Model
638
+ ```typescript
639
+ interface Project {
640
+ project_id: string; // Primary Key
641
+ project_name: string;
642
+ client_name: string;
643
+ contract_value_aed: number; // Decimal(15,2)
644
+ planned_cost_aed: number; // Decimal(15,2)
645
+ project_start_date: string; // Date (YYYY-MM-DD)
646
+ project_end_date: string; // Date (YYYY-MM-DD)
647
+ project_type: string;
648
+ project_status: string;
649
+ current_phase?: string;
650
+ created_at?: string; // DateTime
651
+ updated_at?: string; // DateTime
652
+ }
653
+ ```
654
+
655
+ ### Employee Model
656
+ ```typescript
657
+ interface Employee {
658
+ employee_id: string; // Primary Key
659
+ employee_name: string;
660
+ department: string;
661
+ role: string;
662
+ hourly_rate_aed: number; // Decimal(10,2)
663
+ employment_type: string;
664
+ start_date: string; // Date
665
+ end_date?: string; // Date
666
+ cost_category: string;
667
+ is_active: boolean;
668
+ email?: string;
669
+ phone?: string;
670
+ created_at?: string; // DateTime
671
+ updated_at?: string; // DateTime
672
+ }
673
+ ```
674
+
675
+ ### Timesheet Model
676
+ ```typescript
677
+ interface Timesheet {
678
+ record_id: string; // Primary Key
679
+ date: string; // Date
680
+ employee_id: string; // Foreign Key
681
+ project_id: string; // Foreign Key
682
+ hours_worked: number; // Decimal(5,2)
683
+ billable_hours: number; // Decimal(5,2)
684
+ work_category: string;
685
+ task_description?: string;
686
+ is_approved: boolean;
687
+ approved_by?: string;
688
+ approved_at?: string; // DateTime
689
+ created_at?: string; // DateTime
690
+ updated_at?: string; // DateTime
691
+ }
692
+ ```
693
+
694
+ ### Milestone Model
695
+ ```typescript
696
+ interface Milestone {
697
+ milestone_id: number; // Primary Key (Auto)
698
+ project_id: string; // Foreign Key
699
+ milestone_name: string;
700
+ milestone_order: number; // Integer
701
+ planned_date: string; // Date
702
+ actual_date?: string; // Date
703
+ status: string;
704
+ completion_percentage: number; // Integer (0-100)
705
+ created_at?: string; // DateTime
706
+ updated_at?: string; // DateTime
707
+ }
708
+ ```
709
+
710
+ ### Invoice Model
711
+ ```typescript
712
+ interface Invoice {
713
+ invoice_id: string; // Primary Key
714
+ project_id: string; // Foreign Key
715
+ invoice_date: string; // Date
716
+ invoice_amount_aed: number; // Decimal(15,2)
717
+ due_date: string; // Date
718
+ payment_date?: string; // Date
719
+ payment_status: string;
720
+ days_outstanding?: number; // Integer
721
+ milestone_reference?: string;
722
+ paid_amount_aed: number; // Decimal(15,2)
723
+ created_at?: string; // DateTime
724
+ updated_at?: string; // DateTime
725
+ }
726
+ ```
727
+
728
+ ### Subcontractor Model
729
+ ```typescript
730
+ interface Subcontractor {
731
+ subcontractor_id: number; // Primary Key (Auto)
732
+ project_id: string; // Foreign Key
733
+ subcontractor_name: string;
734
+ service_type: string;
735
+ contract_amount_aed: number; // Decimal(15,2)
736
+ amount_invoiced_aed: number; // Decimal(15,2)
737
+ payment_status: string;
738
+ work_category: string;
739
+ contract_start_date?: string; // Date
740
+ contract_end_date?: string; // Date
741
+ contact_person?: string;
742
+ contact_email?: string;
743
+ contact_phone?: string;
744
+ created_at?: string; // DateTime
745
+ updated_at?: string; // DateTime
746
+ }
747
+ ```
748
+
749
+ ### Staff Allocation Model
750
+ ```typescript
751
+ interface StaffAllocation {
752
+ allocation_id: number; // Primary Key (Auto)
753
+ project_id: string; // Foreign Key
754
+ milestone_id?: number; // Foreign Key
755
+ milestone_name?: string;
756
+ employee_id: string; // Foreign Key
757
+ employee_name: string;
758
+ role: string;
759
+ category?: string;
760
+ hours_allocated: number; // Decimal(10,2)
761
+ hours_worked: number; // Decimal(10,2)
762
+ hourly_rate_aed: number; // Decimal(10,2)
763
+ skill_match_score?: number; // Integer
764
+ availability_status?: string;
765
+ performance_rating?: string;
766
+ start_date: string; // Date
767
+ end_date?: string; // Date
768
+ notes?: string; // Text
769
+ created_at?: string; // DateTime
770
+ updated_at?: string; // DateTime
771
+ }
772
+ ```
773
+
774
+ ---
775
+
776
+ ## Common Response Formats
777
+
778
+ ### Success Response (JSON Endpoints)
779
+ ```json
780
+ {
781
+ "data": [ /* array of objects */ ]
782
+ }
783
+ ```
784
+
785
+ ### Error Response
786
+ ```json
787
+ {
788
+ "detail": "Error message description"
789
+ }
790
+ ```
791
+
792
+ ### HTTP Status Codes
793
+ - `200 OK` - Successful GET request
794
+ - `303 See Other` - Successful POST request (redirects)
795
+ - `404 Not Found` - Resource not found
796
+ - `422 Unprocessable Entity` - Validation error
797
+ - `500 Internal Server Error` - Server error
798
+
799
+ ---
800
+
801
+ ## Frontend Implementation Notes
802
+
803
+ ### 1. **API Calls**
804
+ - All HTML endpoints return rendered pages
805
+ - All `/api/*` endpoints return JSON data
806
+ - Use `/api/*` endpoints for AJAX/Fetch requests
807
+ - POST endpoints use form-urlencoded data and return redirects
808
+
809
+ ### 2. **Date Format**
810
+ - Input: `YYYY-MM-DD` (e.g., "2024-01-15")
811
+ - Output: ISO format with timezone
812
+
813
+ ### 3. **Decimal Values**
814
+ - All monetary values are in AED
815
+ - Use 2 decimal places for currency
816
+ - Hours can use up to 2 decimal places
817
+
818
+ ### 4. **Foreign Key Relationships**
819
+ - **Timesheets**: Requires valid `employee_id` and `project_id`
820
+ - **Milestones**: Requires valid `project_id`
821
+ - **Invoices**: Requires valid `project_id`
822
+ - **Subcontractors**: Requires valid `project_id`
823
+ - **Staff Allocation**: Requires valid `project_id`, `employee_id`, optional `milestone_id`
824
+
825
+ ### 5. **Cascade Deletes**
826
+ - Deleting a **Project** will also delete:
827
+ - All Milestones
828
+ - All Subcontractors
829
+ - All Staff Allocations
830
+ - Deleting a **Milestone** will set `milestone_id` to NULL in Staff Allocations
831
+
832
+ ### 6. **Form Submission**
833
+ All POST endpoints expect `application/x-www-form-urlencoded` data:
834
+
835
+ ```javascript
836
+ // Example using Fetch API
837
+ const formData = new FormData();
838
+ formData.append('project_id', 'PROJ001');
839
+ formData.append('project_name', 'New Project');
840
+ // ... add more fields
841
+
842
+ fetch('/projects/create', {
843
+ method: 'POST',
844
+ body: new URLSearchParams(formData)
845
+ });
846
+ ```
847
+
848
+ ### 7. **AJAX Data Fetching**
849
+ For dynamic data loading, use the `/api/*` endpoints:
850
+
851
+ ```javascript
852
+ // Example: Fetch all projects
853
+ fetch('/api/projects')
854
+ .then(response => response.json())
855
+ .then(data => {
856
+ // data is an array of project objects
857
+ console.log(data);
858
+ });
859
+ ```
860
+
861
+ ---
862
+
863
+ ## Database Connection
864
+ - **Type**: PostgreSQL
865
+ - **Hosted on**: Render.com
866
+ - **Connection**: Managed by SQLAlchemy ORM
867
+ - **Session Management**: Automatic via dependency injection
868
+
869
+ ---
870
+
871
+ ## CRUD Operations Summary
872
+
873
+ | Resource | List (JSON) | Create | Update | Delete | Get Single |
874
+ |----------|------------|--------|--------|--------|------------|
875
+ | Projects | ✅ `/api/projects` | ✅ | ✅ (in CRUD) | ✅ | ✅ (in CRUD) |
876
+ | Employees | ✅ `/api/employees` | ✅ | ✅ (in CRUD) | ✅ | ✅ (in CRUD) |
877
+ | Timesheets | ✅ `/api/timesheets` | ✅ | ✅ (in CRUD) | ✅ | ✅ (in CRUD) |
878
+ | Milestones | ✅ `/api/milestones` | ✅ | ✅ (in CRUD) | ✅ | ✅ (in CRUD) |
879
+ | Invoices | ✅ `/api/invoices` | ✅ | ✅ (in CRUD) | ✅ | ✅ (in CRUD) |
880
+ | Subcontractors | ✅ `/api/subcontractors` | ✅ | ✅ (in CRUD) | ✅ | ✅ (in CRUD) |
881
+ | Staff Allocation | ✅ `/api/staff-allocation` | ⚠️ (in CRUD only) | ⚠️ (in CRUD only) | ⚠️ (in CRUD only) | ⚠️ (in CRUD only) |
882
+
883
+ **Note**: Operations marked with ⚠️ are implemented in `crud.py` but not exposed as API endpoints in `main.py`. They can be added if needed.
884
+
885
+ ---
886
+
887
+ ## Additional Features Available in CRUD Layer
888
+
889
+ The following functions are available in `crud.py` but not exposed as API endpoints:
890
+
891
+ ### Get Single Record
892
+ - `get_project(db, project_id: str)`
893
+ - `get_employee(db, employee_id: str)`
894
+ - `get_timesheet(db, record_id: str)`
895
+ - `get_milestone(db, milestone_id: int)`
896
+ - `get_invoice(db, invoice_id: str)`
897
+ - `get_subcontractor(db, subcontractor_id: int)`
898
+ - `get_staff_allocation(db, allocation_id: int)`
899
+
900
+ ### Update Record
901
+ - `update_project(db, project_id: str, project: ProjectCreate)`
902
+ - `update_employee(db, employee_id: str, employee: EmployeeCreate)`
903
+ - `update_timesheet(db, record_id: str, timesheet: TimesheetCreate)`
904
+ - `update_milestone(db, milestone_id: int, milestone: MilestoneCreate)`
905
+ - `update_invoice(db, invoice_id: str, invoice: InvoiceCreate)`
906
+ - `update_subcontractor(db, subcontractor_id: int, subcontractor: SubcontractorCreate)`
907
+ - `update_staff_allocation(db, allocation_id: int, allocation: StaffAllocationCreate)`
908
+
909
+ **If you need these operations exposed as API endpoints, they can be added to `main.py`.**
910
+
911
+ ---
912
+
913
+ ## Contact & Support
914
+ For backend modifications or additional endpoints, contact the backend development team.
915
+
916
+ ---
917
+
918
+ **Document Version**: 1.0
919
+ **Last Updated**: 2025-10-08
920
+ **Generated for**: Frontend Development Team
__pycache__/crud.cpython-313.pyc ADDED
Binary file (14.8 kB). View file
 
__pycache__/database.cpython-313.pyc ADDED
Binary file (913 Bytes). View file
 
__pycache__/models.cpython-313.pyc ADDED
Binary file (9.37 kB). View file
 
__pycache__/schemas.cpython-313.pyc ADDED
Binary file (8.12 kB). View file
 
crud.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy.orm import Session
2
+ from typing import List, Optional
3
+ import models
4
+ import schemas
5
+
6
+ # Projects CRUD
7
+ def get_projects(db: Session, skip: int = 0, limit: int = 100):
8
+ return db.query(models.Project).offset(skip).limit(limit).all()
9
+
10
+ def get_project(db: Session, project_id: str):
11
+ return db.query(models.Project).filter(models.Project.project_id == project_id).first()
12
+
13
+ def create_project(db: Session, project: schemas.ProjectCreate):
14
+ db_project = models.Project(**project.model_dump())
15
+ db.add(db_project)
16
+ db.commit()
17
+ db.refresh(db_project)
18
+ return db_project
19
+
20
+ def update_project(db: Session, project_id: str, project: schemas.ProjectCreate):
21
+ db_project = get_project(db, project_id)
22
+ if db_project:
23
+ for key, value in project.model_dump().items():
24
+ setattr(db_project, key, value)
25
+ db.commit()
26
+ db.refresh(db_project)
27
+ return db_project
28
+
29
+ def delete_project(db: Session, project_id: str):
30
+ db_project = get_project(db, project_id)
31
+ if db_project:
32
+ db.delete(db_project)
33
+ db.commit()
34
+ return db_project
35
+
36
+
37
+ # Employees CRUD
38
+ def get_employees(db: Session, skip: int = 0, limit: int = 100):
39
+ return db.query(models.Employee).offset(skip).limit(limit).all()
40
+
41
+ def get_employee(db: Session, employee_id: str):
42
+ return db.query(models.Employee).filter(models.Employee.employee_id == employee_id).first()
43
+
44
+ def create_employee(db: Session, employee: schemas.EmployeeCreate):
45
+ db_employee = models.Employee(**employee.model_dump())
46
+ db.add(db_employee)
47
+ db.commit()
48
+ db.refresh(db_employee)
49
+ return db_employee
50
+
51
+ def update_employee(db: Session, employee_id: str, employee: schemas.EmployeeCreate):
52
+ db_employee = get_employee(db, employee_id)
53
+ if db_employee:
54
+ for key, value in employee.model_dump().items():
55
+ setattr(db_employee, key, value)
56
+ db.commit()
57
+ db.refresh(db_employee)
58
+ return db_employee
59
+
60
+ def delete_employee(db: Session, employee_id: str):
61
+ db_employee = get_employee(db, employee_id)
62
+ if db_employee:
63
+ db.delete(db_employee)
64
+ db.commit()
65
+ return db_employee
66
+
67
+
68
+ # Timesheets CRUD
69
+ def get_timesheets(db: Session, skip: int = 0, limit: int = 100):
70
+ return db.query(models.Timesheet).offset(skip).limit(limit).all()
71
+
72
+ def get_timesheet(db: Session, record_id: str):
73
+ return db.query(models.Timesheet).filter(models.Timesheet.record_id == record_id).first()
74
+
75
+ def create_timesheet(db: Session, timesheet: schemas.TimesheetCreate):
76
+ db_timesheet = models.Timesheet(**timesheet.model_dump())
77
+ db.add(db_timesheet)
78
+ db.commit()
79
+ db.refresh(db_timesheet)
80
+ return db_timesheet
81
+
82
+ def update_timesheet(db: Session, record_id: str, timesheet: schemas.TimesheetCreate):
83
+ db_timesheet = get_timesheet(db, record_id)
84
+ if db_timesheet:
85
+ for key, value in timesheet.model_dump().items():
86
+ setattr(db_timesheet, key, value)
87
+ db.commit()
88
+ db.refresh(db_timesheet)
89
+ return db_timesheet
90
+
91
+ def delete_timesheet(db: Session, record_id: str):
92
+ db_timesheet = get_timesheet(db, record_id)
93
+ if db_timesheet:
94
+ db.delete(db_timesheet)
95
+ db.commit()
96
+ return db_timesheet
97
+
98
+
99
+ # Milestones CRUD
100
+ def get_milestones(db: Session, skip: int = 0, limit: int = 100):
101
+ return db.query(models.Milestone).offset(skip).limit(limit).all()
102
+
103
+ def get_milestone(db: Session, milestone_id: int):
104
+ return db.query(models.Milestone).filter(models.Milestone.milestone_id == milestone_id).first()
105
+
106
+ def create_milestone(db: Session, milestone: schemas.MilestoneCreate):
107
+ db_milestone = models.Milestone(**milestone.model_dump())
108
+ db.add(db_milestone)
109
+ db.commit()
110
+ db.refresh(db_milestone)
111
+ return db_milestone
112
+
113
+ def update_milestone(db: Session, milestone_id: int, milestone: schemas.MilestoneCreate):
114
+ db_milestone = get_milestone(db, milestone_id)
115
+ if db_milestone:
116
+ for key, value in milestone.model_dump().items():
117
+ setattr(db_milestone, key, value)
118
+ db.commit()
119
+ db.refresh(db_milestone)
120
+ return db_milestone
121
+
122
+ def delete_milestone(db: Session, milestone_id: int):
123
+ db_milestone = get_milestone(db, milestone_id)
124
+ if db_milestone:
125
+ db.delete(db_milestone)
126
+ db.commit()
127
+ return db_milestone
128
+
129
+
130
+ # Invoices CRUD
131
+ def get_invoices(db: Session, skip: int = 0, limit: int = 100):
132
+ return db.query(models.Invoice).offset(skip).limit(limit).all()
133
+
134
+ def get_invoice(db: Session, invoice_id: str):
135
+ return db.query(models.Invoice).filter(models.Invoice.invoice_id == invoice_id).first()
136
+
137
+ def create_invoice(db: Session, invoice: schemas.InvoiceCreate):
138
+ db_invoice = models.Invoice(**invoice.model_dump())
139
+ db.add(db_invoice)
140
+ db.commit()
141
+ db.refresh(db_invoice)
142
+ return db_invoice
143
+
144
+ def update_invoice(db: Session, invoice_id: str, invoice: schemas.InvoiceCreate):
145
+ db_invoice = get_invoice(db, invoice_id)
146
+ if db_invoice:
147
+ for key, value in invoice.model_dump().items():
148
+ setattr(db_invoice, key, value)
149
+ db.commit()
150
+ db.refresh(db_invoice)
151
+ return db_invoice
152
+
153
+ def delete_invoice(db: Session, invoice_id: str):
154
+ db_invoice = get_invoice(db, invoice_id)
155
+ if db_invoice:
156
+ db.delete(db_invoice)
157
+ db.commit()
158
+ return db_invoice
159
+
160
+
161
+ # Subcontractors CRUD
162
+ def get_subcontractors(db: Session, skip: int = 0, limit: int = 100):
163
+ return db.query(models.Subcontractor).offset(skip).limit(limit).all()
164
+
165
+ def get_subcontractor(db: Session, subcontractor_id: int):
166
+ return db.query(models.Subcontractor).filter(models.Subcontractor.subcontractor_id == subcontractor_id).first()
167
+
168
+ def create_subcontractor(db: Session, subcontractor: schemas.SubcontractorCreate):
169
+ db_subcontractor = models.Subcontractor(**subcontractor.model_dump())
170
+ db.add(db_subcontractor)
171
+ db.commit()
172
+ db.refresh(db_subcontractor)
173
+ return db_subcontractor
174
+
175
+ def update_subcontractor(db: Session, subcontractor_id: int, subcontractor: schemas.SubcontractorCreate):
176
+ db_subcontractor = get_subcontractor(db, subcontractor_id)
177
+ if db_subcontractor:
178
+ for key, value in subcontractor.model_dump().items():
179
+ setattr(db_subcontractor, key, value)
180
+ db.commit()
181
+ db.refresh(db_subcontractor)
182
+ return db_subcontractor
183
+
184
+ def delete_subcontractor(db: Session, subcontractor_id: int):
185
+ db_subcontractor = get_subcontractor(db, subcontractor_id)
186
+ if db_subcontractor:
187
+ db.delete(db_subcontractor)
188
+ db.commit()
189
+ return db_subcontractor
190
+
191
+
192
+ # Staff Allocations CRUD
193
+ def get_staff_allocations(db: Session, skip: int = 0, limit: int = 100):
194
+ return db.query(models.StaffAllocation).offset(skip).limit(limit).all()
195
+
196
+ def get_staff_allocation(db: Session, allocation_id: int):
197
+ return db.query(models.StaffAllocation).filter(models.StaffAllocation.allocation_id == allocation_id).first()
198
+
199
+ def create_staff_allocation(db: Session, allocation: schemas.StaffAllocationCreate):
200
+ db_allocation = models.StaffAllocation(**allocation.model_dump())
201
+ db.add(db_allocation)
202
+ db.commit()
203
+ db.refresh(db_allocation)
204
+ return db_allocation
205
+
206
+ def update_staff_allocation(db: Session, allocation_id: int, allocation: schemas.StaffAllocationCreate):
207
+ db_allocation = get_staff_allocation(db, allocation_id)
208
+ if db_allocation:
209
+ for key, value in allocation.model_dump().items():
210
+ setattr(db_allocation, key, value)
211
+ db.commit()
212
+ db.refresh(db_allocation)
213
+ return db_allocation
214
+
215
+ def delete_staff_allocation(db: Session, allocation_id: int):
216
+ db_allocation = get_staff_allocation(db, allocation_id)
217
+ if db_allocation:
218
+ db.delete(db_allocation)
219
+ db.commit()
220
+ return db_allocation
database.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.orm import sessionmaker
4
+
5
+ DATABASE_URL = "postgresql://killadesigndatabase_user:vqBjgAloNiEVZJLRCV0PS0tM8pqJMAvP@dpg-d3j9117fte5s73f70kjg-a.oregon-postgres.render.com/killadesigndatabase"
6
+
7
+ engine = create_engine(DATABASE_URL)
8
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
9
+ Base = declarative_base()
10
+
11
+ def get_db():
12
+ db = SessionLocal()
13
+ try:
14
+ yield db
15
+ finally:
16
+ db.close()
db/schema.sql ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- PostgreSQL schema for KIllaDesign data model
2
+ -- Safe re-runnable DDL
3
+
4
+ -- Recommended: run inside a dedicated schema
5
+ CREATE SCHEMA IF NOT EXISTS kd;
6
+ SET search_path TO kd, public;
7
+
8
+ -- Domain definitions (optionally used for clarity)
9
+ CREATE DOMAIN IF NOT EXISTS money_aed AS NUMERIC(16,2)
10
+ CHECK (VALUE >= 0);
11
+
12
+ -- Lookup tables (optional, minimal to keep example concise)
13
+ CREATE TABLE IF NOT EXISTS project_types (
14
+ type TEXT PRIMARY KEY
15
+ );
16
+ INSERT INTO project_types(type) VALUES
17
+ ('Mixed-Use'), ('Hospitality'), ('Office'), ('Residential'), ('Cultural')
18
+ ON CONFLICT DO NOTHING;
19
+
20
+ CREATE TABLE IF NOT EXISTS work_categories (
21
+ category TEXT PRIMARY KEY
22
+ );
23
+ INSERT INTO work_categories(category) VALUES
24
+ ('Architecture'), ('Project Management'), ('Subcontractor')
25
+ ON CONFLICT DO NOTHING;
26
+
27
+ -- Core tables
28
+ CREATE TABLE IF NOT EXISTS projects (
29
+ project_id TEXT PRIMARY KEY,
30
+ project_name TEXT NOT NULL,
31
+ client_name TEXT NOT NULL,
32
+ contract_value_aed money_aed NOT NULL,
33
+ planned_cost_aed money_aed NOT NULL,
34
+ project_start_date DATE NOT NULL,
35
+ project_end_date DATE,
36
+ project_type TEXT REFERENCES project_types(type),
37
+ project_status TEXT NOT NULL,
38
+ current_phase TEXT
39
+ );
40
+
41
+ CREATE TABLE IF NOT EXISTS employees (
42
+ employee_id TEXT PRIMARY KEY,
43
+ employee_name TEXT NOT NULL,
44
+ department TEXT,
45
+ role TEXT,
46
+ hourly_rate_aed money_aed NOT NULL,
47
+ employment_type TEXT,
48
+ start_date DATE,
49
+ cost_category TEXT REFERENCES work_categories(category)
50
+ );
51
+
52
+ CREATE TABLE IF NOT EXISTS milestones (
53
+ project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
54
+ milestone_name TEXT NOT NULL,
55
+ milestone_order INT NOT NULL,
56
+ planned_date DATE,
57
+ actual_date DATE,
58
+ status TEXT,
59
+ completion_percentage INT CHECK (completion_percentage BETWEEN 0 AND 100),
60
+ PRIMARY KEY (project_id, milestone_order)
61
+ );
62
+
63
+ CREATE TABLE IF NOT EXISTS invoices (
64
+ invoice_id TEXT PRIMARY KEY,
65
+ project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
66
+ invoice_date DATE NOT NULL,
67
+ invoice_amount_aed money_aed NOT NULL,
68
+ due_date DATE,
69
+ payment_date DATE,
70
+ payment_status TEXT,
71
+ days_outstanding INT,
72
+ milestone_reference TEXT
73
+ );
74
+
75
+ CREATE TABLE IF NOT EXISTS subcontractors (
76
+ project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
77
+ subcontractor_name TEXT NOT NULL,
78
+ service_type TEXT NOT NULL,
79
+ contract_amount_aed money_aed NOT NULL,
80
+ amount_invoiced_aed money_aed NOT NULL,
81
+ payment_status TEXT,
82
+ work_category TEXT REFERENCES work_categories(category),
83
+ PRIMARY KEY (project_id, subcontractor_name, service_type)
84
+ );
85
+
86
+ CREATE TABLE IF NOT EXISTS timesheets (
87
+ record_id TEXT PRIMARY KEY,
88
+ date DATE NOT NULL,
89
+ employee_id TEXT NOT NULL REFERENCES employees(employee_id) ON DELETE RESTRICT,
90
+ project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
91
+ hours_worked NUMERIC(6,2) NOT NULL CHECK (hours_worked >= 0),
92
+ work_category TEXT REFERENCES work_categories(category),
93
+ task_description TEXT,
94
+ billable_hours NUMERIC(6,2) CHECK (billable_hours >= 0)
95
+ );
96
+
97
+ CREATE TABLE IF NOT EXISTS milestone_budgets (
98
+ project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
99
+ milestone_name TEXT NOT NULL,
100
+ milestone_order INT NOT NULL,
101
+ planned_date DATE,
102
+ actual_date DATE,
103
+ status TEXT,
104
+ budgeted_hours NUMERIC(10,2),
105
+ budgeted_cost_aed money_aed,
106
+ actual_hours NUMERIC(10,2),
107
+ actual_cost_aed money_aed,
108
+ cumulative_budgeted_hours NUMERIC(10,2),
109
+ cumulative_budgeted_cost_aed money_aed,
110
+ cumulative_actual_hours NUMERIC(10,2),
111
+ cumulative_actual_cost_aed money_aed,
112
+ hours_variance NUMERIC(10,2),
113
+ cost_variance_aed money_aed,
114
+ schedule_variance_days INT,
115
+ budget_utilization_percent NUMERIC(5,2),
116
+ architecture_hours NUMERIC(10,2),
117
+ architecture_cost_aed money_aed,
118
+ pm_hours NUMERIC(10,2),
119
+ pm_cost_aed money_aed,
120
+ subcontractor_hours NUMERIC(10,2),
121
+ subcontractor_cost_aed money_aed,
122
+ notes TEXT,
123
+ PRIMARY KEY (project_id, milestone_order)
124
+ );
125
+
126
+ CREATE TABLE IF NOT EXISTS staff_allocation (
127
+ project_id TEXT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
128
+ milestone_id TEXT,
129
+ milestone_name TEXT,
130
+ employee_id TEXT NOT NULL REFERENCES employees(employee_id) ON DELETE RESTRICT,
131
+ employee_name TEXT,
132
+ role TEXT,
133
+ category TEXT,
134
+ hours_allocated NUMERIC(10,2),
135
+ hours_worked NUMERIC(10,2),
136
+ hourly_rate_aed money_aed,
137
+ cost_aed money_aed,
138
+ utilization_percent NUMERIC(6,2),
139
+ skill_match_score NUMERIC(6,2),
140
+ availability_status TEXT,
141
+ start_date DATE,
142
+ end_date DATE,
143
+ performance_rating TEXT,
144
+ notes TEXT,
145
+ PRIMARY KEY (project_id, milestone_name, employee_id)
146
+ );
main.py ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, Depends, HTTPException, Form
2
+ from fastapi.responses import HTMLResponse, RedirectResponse
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.templating import Jinja2Templates
5
+ from sqlalchemy.orm import Session
6
+ from typing import Optional
7
+ from datetime import date
8
+ from decimal import Decimal
9
+
10
+ import models
11
+ import schemas
12
+ import crud
13
+ from database import engine, get_db
14
+
15
+ # Create tables
16
+ models.Base.metadata.create_all(bind=engine)
17
+
18
+ app = FastAPI(title="Architecture PM System")
19
+
20
+ # Mount static files
21
+ app.mount("/static", StaticFiles(directory="static"), name="static")
22
+
23
+ # Templates
24
+ templates = Jinja2Templates(directory="templates")
25
+
26
+ # ============================================================================
27
+ # HOME PAGE
28
+ # ============================================================================
29
+ @app.get("/", response_class=HTMLResponse)
30
+ async def home(request: Request, db: Session = Depends(get_db)):
31
+ projects_count = db.query(models.Project).count()
32
+ employees_count = db.query(models.Employee).count()
33
+ timesheets_count = db.query(models.Timesheet).count()
34
+ invoices_count = db.query(models.Invoice).count()
35
+
36
+ return templates.TemplateResponse("index.html", {
37
+ "request": request,
38
+ "projects_count": projects_count,
39
+ "employees_count": employees_count,
40
+ "timesheets_count": timesheets_count,
41
+ "invoices_count": invoices_count
42
+ })
43
+
44
+ # ============================================================================
45
+ # PROJECTS
46
+ # ============================================================================
47
+ @app.get("/projects", response_class=HTMLResponse)
48
+ async def list_projects(request: Request, db: Session = Depends(get_db)):
49
+ projects = crud.get_projects(db)
50
+ return templates.TemplateResponse("projects.html", {
51
+ "request": request,
52
+ "projects": projects
53
+ })
54
+
55
+ @app.get("/api/projects")
56
+ async def api_list_projects(db: Session = Depends(get_db)):
57
+ return crud.get_projects(db)
58
+
59
+ @app.post("/projects/create")
60
+ async def create_project(
61
+ project_id: str = Form(...),
62
+ project_name: str = Form(...),
63
+ client_name: str = Form(...),
64
+ contract_value_aed: float = Form(...),
65
+ planned_cost_aed: float = Form(...),
66
+ project_start_date: date = Form(...),
67
+ project_end_date: date = Form(...),
68
+ project_type: str = Form(...),
69
+ project_status: str = Form(...),
70
+ current_phase: Optional[str] = Form(None),
71
+ db: Session = Depends(get_db)
72
+ ):
73
+ project = schemas.ProjectCreate(
74
+ project_id=project_id,
75
+ project_name=project_name,
76
+ client_name=client_name,
77
+ contract_value_aed=Decimal(str(contract_value_aed)),
78
+ planned_cost_aed=Decimal(str(planned_cost_aed)),
79
+ project_start_date=project_start_date,
80
+ project_end_date=project_end_date,
81
+ project_type=project_type,
82
+ project_status=project_status,
83
+ current_phase=current_phase
84
+ )
85
+ crud.create_project(db, project)
86
+ return RedirectResponse(url="/projects", status_code=303)
87
+
88
+ @app.post("/projects/delete/{project_id}")
89
+ async def delete_project(project_id: str, db: Session = Depends(get_db)):
90
+ crud.delete_project(db, project_id)
91
+ return RedirectResponse(url="/projects", status_code=303)
92
+
93
+ # ============================================================================
94
+ # EMPLOYEES
95
+ # ============================================================================
96
+ @app.get("/employees", response_class=HTMLResponse)
97
+ async def list_employees(request: Request, db: Session = Depends(get_db)):
98
+ employees = crud.get_employees(db)
99
+ return templates.TemplateResponse("employees.html", {
100
+ "request": request,
101
+ "employees": employees
102
+ })
103
+
104
+ @app.get("/api/employees")
105
+ async def api_list_employees(db: Session = Depends(get_db)):
106
+ return crud.get_employees(db)
107
+
108
+ @app.post("/employees/create")
109
+ async def create_employee(
110
+ employee_id: str = Form(...),
111
+ employee_name: str = Form(...),
112
+ department: str = Form(...),
113
+ role: str = Form(...),
114
+ hourly_rate_aed: float = Form(...),
115
+ employment_type: str = Form(...),
116
+ start_date: date = Form(...),
117
+ cost_category: str = Form(...),
118
+ email: Optional[str] = Form(None),
119
+ phone: Optional[str] = Form(None),
120
+ db: Session = Depends(get_db)
121
+ ):
122
+ employee = schemas.EmployeeCreate(
123
+ employee_id=employee_id,
124
+ employee_name=employee_name,
125
+ department=department,
126
+ role=role,
127
+ hourly_rate_aed=Decimal(str(hourly_rate_aed)),
128
+ employment_type=employment_type,
129
+ start_date=start_date,
130
+ cost_category=cost_category,
131
+ email=email,
132
+ phone=phone
133
+ )
134
+ crud.create_employee(db, employee)
135
+ return RedirectResponse(url="/employees", status_code=303)
136
+
137
+ @app.post("/employees/delete/{employee_id}")
138
+ async def delete_employee(employee_id: str, db: Session = Depends(get_db)):
139
+ crud.delete_employee(db, employee_id)
140
+ return RedirectResponse(url="/employees", status_code=303)
141
+
142
+ # ============================================================================
143
+ # TIMESHEETS
144
+ # ============================================================================
145
+ @app.get("/timesheets", response_class=HTMLResponse)
146
+ async def list_timesheets(request: Request, db: Session = Depends(get_db)):
147
+ timesheets = crud.get_timesheets(db, limit=200)
148
+ employees = crud.get_employees(db)
149
+ projects = crud.get_projects(db)
150
+ return templates.TemplateResponse("timesheets.html", {
151
+ "request": request,
152
+ "timesheets": timesheets,
153
+ "employees": employees,
154
+ "projects": projects
155
+ })
156
+
157
+ @app.get("/api/timesheets")
158
+ async def api_list_timesheets(db: Session = Depends(get_db)):
159
+ return crud.get_timesheets(db, limit=200)
160
+
161
+ @app.post("/timesheets/create")
162
+ async def create_timesheet(
163
+ record_id: str = Form(...),
164
+ date: date = Form(...),
165
+ employee_id: str = Form(...),
166
+ project_id: str = Form(...),
167
+ hours_worked: float = Form(...),
168
+ billable_hours: float = Form(...),
169
+ work_category: str = Form(...),
170
+ task_description: Optional[str] = Form(None),
171
+ db: Session = Depends(get_db)
172
+ ):
173
+ timesheet = schemas.TimesheetCreate(
174
+ record_id=record_id,
175
+ date=date,
176
+ employee_id=employee_id,
177
+ project_id=project_id,
178
+ hours_worked=Decimal(str(hours_worked)),
179
+ billable_hours=Decimal(str(billable_hours)),
180
+ work_category=work_category,
181
+ task_description=task_description
182
+ )
183
+ crud.create_timesheet(db, timesheet)
184
+ return RedirectResponse(url="/timesheets", status_code=303)
185
+
186
+ @app.post("/timesheets/delete/{record_id}")
187
+ async def delete_timesheet(record_id: str, db: Session = Depends(get_db)):
188
+ crud.delete_timesheet(db, record_id)
189
+ return RedirectResponse(url="/timesheets", status_code=303)
190
+
191
+ # ============================================================================
192
+ # MILESTONES
193
+ # ============================================================================
194
+ @app.get("/milestones", response_class=HTMLResponse)
195
+ async def list_milestones(request: Request, db: Session = Depends(get_db)):
196
+ milestones = crud.get_milestones(db)
197
+ projects = crud.get_projects(db)
198
+ return templates.TemplateResponse("milestones.html", {
199
+ "request": request,
200
+ "milestones": milestones,
201
+ "projects": projects
202
+ })
203
+
204
+ @app.get("/api/milestones")
205
+ async def api_list_milestones(db: Session = Depends(get_db)):
206
+ return crud.get_milestones(db)
207
+
208
+ @app.post("/milestones/create")
209
+ async def create_milestone(
210
+ project_id: str = Form(...),
211
+ milestone_name: str = Form(...),
212
+ milestone_order: int = Form(...),
213
+ planned_date: date = Form(...),
214
+ status: str = Form(...),
215
+ completion_percentage: int = Form(0),
216
+ actual_date: Optional[date] = Form(None),
217
+ db: Session = Depends(get_db)
218
+ ):
219
+ milestone = schemas.MilestoneCreate(
220
+ project_id=project_id,
221
+ milestone_name=milestone_name,
222
+ milestone_order=milestone_order,
223
+ planned_date=planned_date,
224
+ actual_date=actual_date,
225
+ status=status,
226
+ completion_percentage=completion_percentage
227
+ )
228
+ crud.create_milestone(db, milestone)
229
+ return RedirectResponse(url="/milestones", status_code=303)
230
+
231
+ @app.post("/milestones/delete/{milestone_id}")
232
+ async def delete_milestone(milestone_id: int, db: Session = Depends(get_db)):
233
+ crud.delete_milestone(db, milestone_id)
234
+ return RedirectResponse(url="/milestones", status_code=303)
235
+
236
+ # ============================================================================
237
+ # INVOICES
238
+ # ============================================================================
239
+ @app.get("/invoices", response_class=HTMLResponse)
240
+ async def list_invoices(request: Request, db: Session = Depends(get_db)):
241
+ invoices = crud.get_invoices(db)
242
+ projects = crud.get_projects(db)
243
+ return templates.TemplateResponse("invoices.html", {
244
+ "request": request,
245
+ "invoices": invoices,
246
+ "projects": projects
247
+ })
248
+
249
+ @app.get("/api/invoices")
250
+ async def api_list_invoices(db: Session = Depends(get_db)):
251
+ return crud.get_invoices(db)
252
+
253
+ @app.post("/invoices/create")
254
+ async def create_invoice(
255
+ invoice_id: str = Form(...),
256
+ project_id: str = Form(...),
257
+ invoice_date: date = Form(...),
258
+ invoice_amount_aed: float = Form(...),
259
+ due_date: date = Form(...),
260
+ payment_status: str = Form(...),
261
+ payment_date: Optional[date] = Form(None),
262
+ milestone_reference: Optional[str] = Form(None),
263
+ db: Session = Depends(get_db)
264
+ ):
265
+ invoice = schemas.InvoiceCreate(
266
+ invoice_id=invoice_id,
267
+ project_id=project_id,
268
+ invoice_date=invoice_date,
269
+ invoice_amount_aed=Decimal(str(invoice_amount_aed)),
270
+ due_date=due_date,
271
+ payment_date=payment_date,
272
+ payment_status=payment_status,
273
+ milestone_reference=milestone_reference
274
+ )
275
+ crud.create_invoice(db, invoice)
276
+ return RedirectResponse(url="/invoices", status_code=303)
277
+
278
+ @app.post("/invoices/delete/{invoice_id}")
279
+ async def delete_invoice(invoice_id: str, db: Session = Depends(get_db)):
280
+ crud.delete_invoice(db, invoice_id)
281
+ return RedirectResponse(url="/invoices", status_code=303)
282
+
283
+ # ============================================================================
284
+ # SUBCONTRACTORS
285
+ # ============================================================================
286
+ @app.get("/subcontractors", response_class=HTMLResponse)
287
+ async def list_subcontractors(request: Request, db: Session = Depends(get_db)):
288
+ subcontractors = crud.get_subcontractors(db)
289
+ projects = crud.get_projects(db)
290
+ return templates.TemplateResponse("subcontractors.html", {
291
+ "request": request,
292
+ "subcontractors": subcontractors,
293
+ "projects": projects
294
+ })
295
+
296
+ @app.get("/api/subcontractors")
297
+ async def api_list_subcontractors(db: Session = Depends(get_db)):
298
+ return crud.get_subcontractors(db)
299
+
300
+ @app.post("/subcontractors/create")
301
+ async def create_subcontractor(
302
+ project_id: str = Form(...),
303
+ subcontractor_name: str = Form(...),
304
+ service_type: str = Form(...),
305
+ contract_amount_aed: float = Form(...),
306
+ amount_invoiced_aed: float = Form(0),
307
+ payment_status: str = Form(...),
308
+ work_category: str = Form(...),
309
+ contact_person: Optional[str] = Form(None),
310
+ contact_email: Optional[str] = Form(None),
311
+ db: Session = Depends(get_db)
312
+ ):
313
+ subcontractor = schemas.SubcontractorCreate(
314
+ project_id=project_id,
315
+ subcontractor_name=subcontractor_name,
316
+ service_type=service_type,
317
+ contract_amount_aed=Decimal(str(contract_amount_aed)),
318
+ amount_invoiced_aed=Decimal(str(amount_invoiced_aed)),
319
+ payment_status=payment_status,
320
+ work_category=work_category,
321
+ contact_person=contact_person,
322
+ contact_email=contact_email
323
+ )
324
+ crud.create_subcontractor(db, subcontractor)
325
+ return RedirectResponse(url="/subcontractors", status_code=303)
326
+
327
+ @app.post("/subcontractors/delete/{subcontractor_id}")
328
+ async def delete_subcontractor(subcontractor_id: int, db: Session = Depends(get_db)):
329
+ crud.delete_subcontractor(db, subcontractor_id)
330
+ return RedirectResponse(url="/subcontractors", status_code=303)
331
+
332
+ # ============================================================================
333
+ # STAFF ALLOCATION
334
+ # ============================================================================
335
+ @app.get("/staff-allocation", response_class=HTMLResponse)
336
+ async def list_staff_allocations(request: Request, db: Session = Depends(get_db)):
337
+ allocations = crud.get_staff_allocations(db)
338
+ projects = crud.get_projects(db)
339
+ employees = crud.get_employees(db)
340
+ milestones = crud.get_milestones(db)
341
+ return templates.TemplateResponse("staff_allocation.html", {
342
+ "request": request,
343
+ "allocations": allocations,
344
+ "projects": projects,
345
+ "employees": employees,
346
+ "milestones": milestones
347
+ })
348
+
349
+ @app.get("/api/staff-allocation")
350
+ async def api_list_staff_allocations(db: Session = Depends(get_db)):
351
+ return crud.get_staff_allocations(db)
352
+
353
+ @app.post("/staff-allocation/create")
354
+ async def create_staff_allocation(
355
+ project_id: str = Form(...),
356
+ employee_id: str = Form(...),
357
+ employee_name: str = Form(...),
358
+ role: str = Form(...),
359
+ hours_allocated: float = Form(...),
360
+ hours_worked: float = Form(0),
361
+ hourly_rate_aed: float = Form(...),
362
+ start_date: date = Form(...),
363
+ milestone_id: Optional[int] = Form(None),
364
+ milestone_name: Optional[str] = Form(None),
365
+ category: Optional[str] = Form(None),
366
+ skill_match_score: Optional[int] = Form(None),
367
+ availability_status: Optional[str] = Form(None),
368
+ performance_rating: Optional[str] = Form(None),
369
+ end_date: Optional[date] = Form(None),
370
+ notes: Optional[str] = Form(None),
371
+ db: Session = Depends(get_db)
372
+ ):
373
+ allocation = schemas.StaffAllocationCreate(
374
+ project_id=project_id,
375
+ milestone_id=milestone_id,
376
+ milestone_name=milestone_name,
377
+ employee_id=employee_id,
378
+ employee_name=employee_name,
379
+ role=role,
380
+ category=category,
381
+ hours_allocated=Decimal(str(hours_allocated)),
382
+ hours_worked=Decimal(str(hours_worked)),
383
+ hourly_rate_aed=Decimal(str(hourly_rate_aed)),
384
+ skill_match_score=skill_match_score,
385
+ availability_status=availability_status,
386
+ performance_rating=performance_rating,
387
+ start_date=start_date,
388
+ end_date=end_date,
389
+ notes=notes
390
+ )
391
+ crud.create_staff_allocation(db, allocation)
392
+ return RedirectResponse(url="/staff-allocation", status_code=303)
393
+
394
+ @app.post("/staff-allocation/delete/{allocation_id}")
395
+ async def delete_staff_allocation(allocation_id: int, db: Session = Depends(get_db)):
396
+ crud.delete_staff_allocation(db, allocation_id)
397
+ return RedirectResponse(url="/staff-allocation", status_code=303)
398
+
399
+
400
+ if __name__ == "__main__":
401
+ import uvicorn
402
+ uvicorn.run(app, host="0.0.0.0", port=8000)
models.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, String, Numeric, Date, Integer, Boolean, Text, DateTime, ForeignKey, CheckConstraint
2
+ from sqlalchemy.sql import func
3
+ from sqlalchemy.orm import relationship
4
+ from database import Base
5
+
6
+ class Project(Base):
7
+ __tablename__ = "projects"
8
+
9
+ project_id = Column(String(20), primary_key=True, index=True)
10
+ project_name = Column(String(255), nullable=False)
11
+ client_name = Column(String(255), nullable=False, index=True)
12
+ contract_value_aed = Column(Numeric(15, 2), nullable=False)
13
+ planned_cost_aed = Column(Numeric(15, 2), nullable=False)
14
+ project_start_date = Column(Date, nullable=False)
15
+ project_end_date = Column(Date, nullable=False)
16
+ project_type = Column(String(50), nullable=False, index=True)
17
+ project_status = Column(String(50), nullable=False, index=True)
18
+ current_phase = Column(String(100))
19
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
20
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
21
+
22
+ # Relationships
23
+ timesheets = relationship("Timesheet", back_populates="project")
24
+ milestones = relationship("Milestone", back_populates="project", cascade="all, delete-orphan")
25
+ invoices = relationship("Invoice", back_populates="project")
26
+ subcontractors = relationship("Subcontractor", back_populates="project", cascade="all, delete-orphan")
27
+ staff_allocations = relationship("StaffAllocation", back_populates="project", cascade="all, delete-orphan")
28
+
29
+
30
+ class Employee(Base):
31
+ __tablename__ = "employees"
32
+
33
+ employee_id = Column(String(20), primary_key=True, index=True)
34
+ employee_name = Column(String(255), nullable=False)
35
+ department = Column(String(100), nullable=False, index=True)
36
+ role = Column(String(100), nullable=False, index=True)
37
+ hourly_rate_aed = Column(Numeric(10, 2), nullable=False)
38
+ employment_type = Column(String(50), nullable=False)
39
+ start_date = Column(Date, nullable=False)
40
+ end_date = Column(Date)
41
+ cost_category = Column(String(100), nullable=False, index=True)
42
+ is_active = Column(Boolean, default=True, index=True)
43
+ email = Column(String(255))
44
+ phone = Column(String(50))
45
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
46
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
47
+
48
+ # Relationships
49
+ timesheets = relationship("Timesheet", back_populates="employee")
50
+ staff_allocations = relationship("StaffAllocation", back_populates="employee")
51
+
52
+
53
+ class Timesheet(Base):
54
+ __tablename__ = "timesheets"
55
+
56
+ record_id = Column(String(20), primary_key=True, index=True)
57
+ date = Column(Date, nullable=False, index=True)
58
+ employee_id = Column(String(20), ForeignKey("employees.employee_id"), nullable=False, index=True)
59
+ project_id = Column(String(20), ForeignKey("projects.project_id"), nullable=False, index=True)
60
+ hours_worked = Column(Numeric(5, 2), nullable=False)
61
+ billable_hours = Column(Numeric(5, 2), nullable=False)
62
+ work_category = Column(String(100), nullable=False)
63
+ task_description = Column(String(500))
64
+ is_approved = Column(Boolean, default=False, index=True)
65
+ approved_by = Column(String(20))
66
+ approved_at = Column(DateTime)
67
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
68
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
69
+
70
+ # Relationships
71
+ employee = relationship("Employee", back_populates="timesheets")
72
+ project = relationship("Project", back_populates="timesheets")
73
+
74
+
75
+ class Milestone(Base):
76
+ __tablename__ = "milestones"
77
+
78
+ milestone_id = Column(Integer, primary_key=True, autoincrement=True)
79
+ project_id = Column(String(20), ForeignKey("projects.project_id", ondelete="CASCADE"), nullable=False, index=True)
80
+ milestone_name = Column(String(255), nullable=False)
81
+ milestone_order = Column(Integer, nullable=False)
82
+ planned_date = Column(Date, nullable=False, index=True)
83
+ actual_date = Column(Date)
84
+ status = Column(String(50), nullable=False, index=True)
85
+ completion_percentage = Column(Integer, default=0)
86
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
87
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
88
+
89
+ # Relationships
90
+ project = relationship("Project", back_populates="milestones")
91
+ staff_allocations = relationship("StaffAllocation", back_populates="milestone")
92
+
93
+
94
+ class Invoice(Base):
95
+ __tablename__ = "invoices"
96
+
97
+ invoice_id = Column(String(20), primary_key=True, index=True)
98
+ project_id = Column(String(20), ForeignKey("projects.project_id"), nullable=False, index=True)
99
+ invoice_date = Column(Date, nullable=False)
100
+ invoice_amount_aed = Column(Numeric(15, 2), nullable=False)
101
+ due_date = Column(Date, nullable=False, index=True)
102
+ payment_date = Column(Date, index=True)
103
+ payment_status = Column(String(50), nullable=False, index=True)
104
+ days_outstanding = Column(Integer)
105
+ milestone_reference = Column(String(255))
106
+ paid_amount_aed = Column(Numeric(15, 2), default=0)
107
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
108
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
109
+
110
+ # Relationships
111
+ project = relationship("Project", back_populates="invoices")
112
+
113
+
114
+ class Subcontractor(Base):
115
+ __tablename__ = "subcontractors"
116
+
117
+ subcontractor_id = Column(Integer, primary_key=True, autoincrement=True)
118
+ project_id = Column(String(20), ForeignKey("projects.project_id", ondelete="CASCADE"), nullable=False, index=True)
119
+ subcontractor_name = Column(String(255), nullable=False, index=True)
120
+ service_type = Column(String(100), nullable=False, index=True)
121
+ contract_amount_aed = Column(Numeric(15, 2), nullable=False)
122
+ amount_invoiced_aed = Column(Numeric(15, 2), default=0)
123
+ payment_status = Column(String(50), nullable=False, index=True)
124
+ work_category = Column(String(100), nullable=False)
125
+ contract_start_date = Column(Date)
126
+ contract_end_date = Column(Date)
127
+ contact_person = Column(String(255))
128
+ contact_email = Column(String(255))
129
+ contact_phone = Column(String(50))
130
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
131
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
132
+
133
+ # Relationships
134
+ project = relationship("Project", back_populates="subcontractors")
135
+
136
+
137
+ class StaffAllocation(Base):
138
+ __tablename__ = "staff_allocation"
139
+
140
+ allocation_id = Column(Integer, primary_key=True, autoincrement=True)
141
+ project_id = Column(String(20), ForeignKey("projects.project_id", ondelete="CASCADE"), nullable=False, index=True)
142
+ milestone_id = Column(Integer, ForeignKey("milestones.milestone_id", ondelete="SET NULL"), index=True)
143
+ milestone_name = Column(String(255))
144
+ employee_id = Column(String(20), ForeignKey("employees.employee_id"), nullable=False, index=True)
145
+ employee_name = Column(String(255), nullable=False)
146
+ role = Column(String(100), nullable=False)
147
+ category = Column(String(100))
148
+ hours_allocated = Column(Numeric(10, 2), nullable=False)
149
+ hours_worked = Column(Numeric(10, 2), default=0)
150
+ hourly_rate_aed = Column(Numeric(10, 2), nullable=False)
151
+ skill_match_score = Column(Integer)
152
+ availability_status = Column(String(50))
153
+ performance_rating = Column(String(50))
154
+ start_date = Column(Date, nullable=False, index=True)
155
+ end_date = Column(Date)
156
+ notes = Column(Text)
157
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
158
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
159
+
160
+ # Relationships
161
+ project = relationship("Project", back_populates="staff_allocations")
162
+ milestone = relationship("Milestone", back_populates="staff_allocations")
163
+ employee = relationship("Employee", back_populates="staff_allocations")
requirements.txt CHANGED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ sqlalchemy
4
+ psycopg2-binary
5
+ pandas
6
+ openpyxl
7
+ python-multipart
8
+ jinja2
9
+ python-dotenv
10
+ aiofiles
sampledataofsheet.md ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- Sheet: Projects ---
2
+
3
+ Project_ID Project_Name Client_Name Contract_Value_AED Planned_Cost_AED Project_Start_Date Project_End_Date Project_Type Project_Status Current_Phase
4
+ KD-2024-001 Dubai Marina Mixed-Use Tower Emaar Properties 45000000 36000000 2024-01-15 2025-12-15 Mixed-Use In Progress Design Development
5
+ KD-2024-002 Jumeirah Hospitality Resort Jumeirah Group 28000000 22400000 2024-03-01 2025-08-01 Hospitality In Progress Construction Documentation
6
+ KD-2024-003 DIFC Office Complex Phase 2 DIFC Authority 35000000 28000000 2024-02-01 2025-10-01 Office Planning Concept Design
7
+ KD-2024-004 Abu Dhabi Residential Master Plan Aldar Properties 52000000 41600000 2024-04-01 2026-03-01 Residential In Progress Schematic Design
8
+ KD-2024-005 Sharjah Cultural Museum Sharjah Investment Authority 18000000 14400000 2024-05-15 2025-11-15 Cultural Design Phase Design Development
9
+
10
+ --- Sheet: Employees ---
11
+
12
+ Employee_ID Employee_Name Department Role Hourly_Rate_AED Employment_Type Start_Date Cost_Category
13
+ EMP-001 Employee 1 Architecture Project Manager 350 Full-Time 2023-04-24 Architecture
14
+ EMP-002 Employee 2 Architecture Senior Architect 850 Full-Time 2023-09-18 Architecture
15
+ EMP-003 Employee 3 Architecture BIM Coordinator 400 Full-Time 2023-05-24 Architecture
16
+ EMP-004 Employee 4 Architecture Design Principal 850 Full-Time 2023-10-14 Architecture
17
+ EMP-005 Employee 5 Architecture Design Principal 300 Full-Time 2023-10-13 Architecture
18
+ EMP-006 Employee 6 Architecture Junior Architect 200 Full-Time 2023-06-08 Architecture
19
+ EMP-007 Employee 7 Architecture Project Manager 150 Full-Time 2023-03-17 Project Management
20
+ EMP-008 Employee 8 Architecture Interior Designer 300 Full-Time 2023-08-03 Project Management
21
+ EMP-009 Employee 9 Architecture Project Manager 350 Full-Time 2023-01-28 Architecture
22
+ EMP-010 Employee 10 Architecture Structural Engineer 200 Full-Time 2023-02-05 Architecture
23
+ EMP-011 Employee 11 Architecture Project Coordinator 300 Full-Time 2023-11-06 Project Management
24
+ EMP-012 Employee 12 Architecture Senior Architect 200 Full-Time 2023-11-14 Project Management
25
+ EMP-013 Employee 13 Architecture Senior Architect 500 Full-Time 2023-10-03 Architecture
26
+ EMP-014 Employee 14 Architecture Project Manager 350 Full-Time 2023-07-13 Project Management
27
+ EMP-015 Employee 15 Architecture Design Principal 600 Full-Time 2023-10-15 Architecture
28
+ EMP-016 Employee 16 Architecture Design Principal 400 Full-Time 2023-09-09 Architecture
29
+
30
+ EMP-041 Employee 41 Project Management Senior Associate 300 Full-Time 2023-10-03 Architecture
31
+ EMP-042 Employee 42 Project Management Structural Engineer 850 Full-Time 2023-02-24 Architecture
32
+ EMP-043 Employee 43 Project Management BIM Coordinator 400 Full-Time 2023-08-27 Architecture
33
+ EMP-044 Employee 44 Project Management Senior Architect 300 Full-Time 2023-02-25 Architecture
34
+ EMP-045 Employee 45 Project Management Urban Designer 600 Full-Time 2023-09-25 Architecture
35
+ EMP-046 Employee 46 Project Management Interior Designer 500 Contract 2023-03-05 Architecture
36
+ EMP-047 Employee 47 Project Management Project Manager 600 Contract 2023-11-16 Project Management
37
+ EMP-048 Employee 48 Project Management Project Coordinator 250 Contract 2023-09-06 Architecture
38
+ EMP-049 Employee 49 Project Management Project Manager 350 Contract 2023-05-17 Project Management
39
+ EMP-050 Employee 50 Project Management Interior Designer 250 Contract 2023-10-14 Project Management
40
+
41
+ --- Sheet: Timesheets ---
42
+
43
+ Record_ID Date Employee_ID Project_ID Hours_Worked Work_Category Task_Description Billable_Hours
44
+ TS-000001 2024-04-07 EMP-007 KD-2024-001 9.27 Project Management Documentation 8.18
45
+ TS-000002 2024-01-28 EMP-044 KD-2024-001 4.48 Subcontractor Site Visits 4.30
46
+ TS-000003 2024-02-25 EMP-016 KD-2024-002 5.52 Project Management Client Presentations 4.88
47
+ TS-000004 2024-05-22 EMP-030 KD-2024-002 11.00 Architecture BIM Modeling 10.58
48
+ TS-000005 2024-02-20 EMP-004 KD-2024-005 10.69 Architecture Design Reviews 8.91
49
+ TS-000006 2024-09-05 EMP-031 KD-2024-002 10.92 Architecture Client Presentations 9.56
50
+ TS-000007 2024-07-18 EMP-017 KD-2024-004 6.28 Subcontractor Permit Applications 5.86
51
+ TS-000008 2024-09-06 EMP-010 KD-2024-002 6.37 Architecture Project Planning 6.03
52
+ TS-000009 2024-02-01 EMP-048 KD-2024-003 4.46 Subcontractor BIM Modeling 4.02
53
+ TS-000010 2024-09-28 EMP-011 KD-2024-001 11.69 Architecture Client Presentations 9.51
54
+ TS-000011 2024-02-04 EMP-044 KD-2024-002 7.23 Subcontractor Design Reviews 6.62
55
+ TS-000012 2024-01-21 EMP-040 KD-2024-001 7.35 Subcontractor Project Planning 6.65
56
+ TS-000013 2024-05-13 EMP-014 KD-2024-003 5.91 Project Management Client Presentations 5.52
57
+ TS-000014 2024-06-02 EMP-030 KD-2024-003 11.43 Architecture Concept Design Development 10.19
58
+ TS-000015 2024-02-21 EMP-005 KD-2024-005 5.71 Project Management Client Presentations 5.63
59
+ TS-000016 2024-02-05 EMP-016 KD-2024-003 6.28 Project Management Permit Applications 5.91
60
+ TS-000017 2024-09-27 EMP-001 KD-2024-005 6.40 Subcontractor Technical Drawings 6.32
61
+ TS-000018 2024-03-09 EMP-017 KD-2024-001 11.12 Subcontractor Permit Applications 9.24
62
+ TS-000019 2024-05-24 EMP-039 KD-2024-002 9.74 Architecture Coordination Meetings 8.78
63
+ TS-000020 2024-05-08 EMP-004 KD-2024-001 9.07 Project Management Concept Design Development 7.26
64
+ TS-000021 2024-03-07 EMP-041 KD-2024-003 5.29 Project Management Permit Applications 4.98
65
+ TS-000022 2024-01-05 EMP-008 KD-2024-001 11.56 Subcontractor Client Presentations 10.51
66
+ TS-000023 2024-07-08 EMP-038 KD-2024-005 5.18 Architecture Concept Design Development 4.46
67
+ TS-000024 2024-01-21 EMP-023 KD-2024-002 9.46 Subcontractor Technical Drawings 8.24
68
+ TS-000025 2024-07-27 EMP-040 KD-2024-002 11.41 Architecture Client Presentations 11.36
69
+ TS-000026 2024-03-31 EMP-027 KD-2024-001 5.43 Project Management Documentation 5.22
70
+ TS-000027 2024-05-07 EMP-018 KD-2024-002 10.30 Architecture Documentation 10.04
71
+ TS-000028 2024-08-28 EMP-015 KD-2024-002 10.53 Project Management Site Visits 9.07
72
+ TS-000029 2024-04-26 EMP-015 KD-2024-001 9.28 Project Management Site Visits 7.94
73
+ TS-000030 2024-02-05 EMP-050 KD-2024-003 6.81 Subcontractor Documentation 6.37
74
+ TS-000031 2024-06-18 EMP-002 KD-2024-001 11.02 Project Management Client Presentations 10.10
75
+ TS-000032 2024-05-15 EMP-003 KD-2024-001 8.77 Project Management Site Visits 7.78
76
+ TS-000033 2024-09-18 EMP-008 KD-2024-004 11.20 Architecture Coordination Meetings 9.06
77
+ TS-000034 2024-08-11 EMP-001 KD-2024-005 11.40 Subcontractor Design Reviews 9.95
78
+ TS-000035 2024-02-05 EMP-043 KD-2024-003 8.99 Subcontractor Technical Drawings 8.49
79
+ TS-000036 2024-06-02 EMP-033 KD-2024-003 9.34 Project Management Documentation 8.77
80
+ TS-000037 2024-03-06 EMP-013 KD-2024-004 9.32 Project Management Client Presentations 8.60
81
+ TS-000038 2024-06-03 EMP-026 KD-2024-005 10.67 Project Management Coordination Meetings 8.98
82
+ TS-000039 2024-06-13 EMP-030 KD-2024-004 7.54 Architecture Permit Applications 6.75
83
+ TS-000040 2024-03-27 EMP-043 KD-2024-001 6.27 Subcontractor Project Planning 5.44
84
+ TS-000041 2024-04-30 EMP-044 KD-2024-003 5.80 Architecture Client Presentations 4.67
85
+ TS-000042 2024-05-05 EMP-031 KD-2024-005 10.80 Architecture BIM Modeling 9.54
86
+ TS-000043 2024-04-09 EMP-046 KD-2024-004 7.96 Architecture Client Presentations 7.41
87
+ TS-000044 2024-01-03 EMP-049 KD-2024-001 10.23 Architecture Client Presentations 9.83
88
+ TS-000045 2024-09-22 EMP-030 KD-2024-001 8.46 Architecture BIM Modeling 6.99
89
+ TS-000046 2024-08-25 EMP-043 KD-2024-005 11.90 Subcontractor Site Visits 11.78
90
+ TS-000047 2024-08-14 EMP-040 KD-2024-005 7.41 Subcontractor BIM Modeling 7.26
91
+ TS-000048 2024-08-31 EMP-029 KD-2024-003 10.01 Subcontractor Coordination Meetings 9.54
92
+ TS-000049 2024-09-23 EMP-032 KD-2024-002 6.20 Architecture Coordination Meetings 5.25
93
+ TS-000050 2024-06-20 EMP-021 KD-2024-005 4.64 Architecture Design Reviews 4.07
94
+ TS-000051 2024-03-19 EMP-046 KD-2024-002 4.51 Project Management Site Visits 4.10
95
+ TS-000052 2024-07-31 EMP-004 KD-2024-002 10.66 Project Management Project Planning 10.55
96
+ TS-000053 2024-01-11 EMP-049 KD-2024-005 7.04 Architecture Site Visits 6.05
97
+ TS-000054 2024-07-18 EMP-027 KD-2024-005 9.98 Subcontractor Project Planning 9.78
98
+ TS-000055 2024-09-06 EMP-015 KD-2024-003 7.49 Architecture Documentation 6.50
99
+ TS-000056 2024-07-26 EMP-047 KD-2024-002 10.72 Architecture Project Planning 9.72
100
+ TS-000057 2024-07-20 EMP-038 KD-2024-005 9.30 Architecture Documentation 7.69
101
+ TS-000058 2024-08-24 EMP-012 KD-2024-001 6.08 Project Management Design Reviews 5.42
102
+ TS-000059 2024-06-21 EMP-049 KD-2024-004 6.23 Project Management Coordination Meetings 6.02
103
+ TS-000060 2024-08-28 EMP-002 KD-2024-005 4.42 Project Management Design Reviews 4.11
104
+ TS-000061 2024-01-21 EMP-049 KD-2024-001 11.60 Architecture Concept Design Development 10.72
105
+ TS-000062 2024-05-02 EMP-009 KD-2024-004 9.36 Subcontractor Design Reviews 8.36
106
+ TS-000063 2024-05-11 EMP-050 KD-2024-003 5.34 Subcontractor Technical Drawings 5.10
107
+ TS-000064 2024-03-24 EMP-020 KD-2024-001 8.63 Project Management Project Planning 8.07
108
+ TS-000065 2024-07-11 EMP-026 KD-2024-002 4.61 Subcontractor Design Reviews 3.78
109
+ TS-000066 2024-06-03 EMP-044 KD-2024-005 10.44 Subcontractor Concept Design Development 9.08
110
+ TS-000067 2024-08-07 EMP-043 KD-2024-003 4.55 Subcontractor Site Visits 3.65
111
+ TS-000068 2024-08-03 EMP-032 KD-2024-001 7.47 Project Management BIM Modeling 7.03
112
+ TS-000069 2024-08-10 EMP-012 KD-2024-005 11.72 Project Management Project Planning 11.27
113
+ TS-000070 2024-09-04 EMP-030 KD-2024-004 10.61 Subcontractor Coordination Meetings 9.17
114
+ TS-000071 2024-05-05 EMP-006 KD-2024-003 11.05 Architecture BIM Modeling 10.10
115
+ TS-000072 2024-07-13 EMP-022 KD-2024-001 7.95 Project Management Client Presentations 7.14
116
+ TS-000073 2024-06-30 EMP-017 KD-2024-003 6.24 Subcontractor Coordination Meetings 5.69
117
+ TS-000074 2024-09-21 EMP-013 KD-2024-001 5.93 Project Management BIM Modeling 5.40
118
+ TS-000075 2024-05-03 EMP-045 KD-2024-004 9.17 Project Management BIM Modeling 8.79
119
+
120
+
121
+ --- Sheet: Milestones ---
122
+
123
+ Project_ID Milestone_Name Milestone_Order Planned_Date Actual_Date Status Completion_Percentage
124
+ KD-2024-001 Concept Design Approval 1 2024-01-12 NaN Completed 76
125
+ KD-2024-001 Schematic Design Completion 2 2024-02-17 2024-02-17 Completed 0
126
+ KD-2024-001 Design Development Approval 3 2024-03-27 NaN In Progress 0
127
+ KD-2024-001 Construction Documentation 4 2024-04-09 NaN Completed 0
128
+ KD-2024-001 Permit Submission 5 2024-05-13 2024-05-07 Completed 100
129
+ KD-2024-001 Permit Approval 6 2024-06-17 NaN In Progress 0
130
+ KD-2024-001 Construction Start 7 2024-07-20 NaN Pending 0
131
+ KD-2024-001 Structural Completion 8 2024-08-08 NaN In Progress 79
132
+ KD-2024-001 MEP Installation 9 2024-09-25 NaN Completed 0
133
+ KD-2024-001 Final Completion 10 2024-10-16 2024-10-28 Completed 0
134
+ KD-2024-002 Concept Design Approval 1 2024-01-18 NaN Completed 0
135
+ KD-2024-002 Schematic Design Completion 2 2024-02-18 NaN In Progress 29
136
+ KD-2024-002 Design Development Approval 3 2024-03-14 NaN In Progress 0
137
+ KD-2024-002 Construction Documentation 4 2024-04-22 2024-05-01 Completed 0
138
+ KD-2024-002 Permit Submission 5 2024-05-15 NaN Pending 0
139
+ KD-2024-002 Permit Approval 6 2024-06-24 NaN Completed 8
140
+ KD-2024-002 Construction Start 7 2024-07-17 NaN Completed 22
141
+ KD-2024-002 Structural Completion 8 2024-08-13 2024-08-31 Completed 4
142
+ KD-2024-002 MEP Installation 9 2024-09-20 NaN Pending 35
143
+ KD-2024-002 Final Completion 10 2024-10-17 NaN Pending 0
144
+ KD-2024-003 Concept Design Approval 1 2024-01-30 2024-02-18 Pending 30
145
+ KD-2024-003 Schematic Design Completion 2 2024-02-20 NaN Completed 0
146
+ KD-2024-003 Design Development Approval 3 2024-03-28 NaN In Progress 33
147
+ KD-2024-003 Construction Documentation 4 2024-04-11 2024-04-10 In Progress 20
148
+ KD-2024-003 Permit Submission 5 2024-05-27 2024-06-10 Completed 86
149
+ KD-2024-003 Permit Approval 6 2024-06-14 NaN Pending 99
150
+ KD-2024-003 Construction Start 7 2024-07-08 2024-07-13 Pending 76
151
+ KD-2024-003 Structural Completion 8 2024-08-07 NaN Pending 0
152
+ KD-2024-003 MEP Installation 9 2024-09-26 NaN In Progress 0
153
+ KD-2024-003 Final Completion 10 2024-10-20 2024-10-18 In Progress 0
154
+
155
+
156
+
157
+ --- Sheet: Invoices ---
158
+
159
+ Invoice_ID Project_ID Invoice_Date Invoice_Amount_AED Due_Date Payment_Date Payment_Status Days_Outstanding Milestone_Reference
160
+ INV-001-001 KD-2024-001 2024-01-25 669473 2024-02-24 NaN Outstanding 40 Design Development
161
+ INV-001-002 KD-2024-001 2024-02-25 1830640 2024-03-26 2024-03-21 Outstanding 17 Design Development
162
+ INV-001-003 KD-2024-001 2024-04-24 2122235 2024-05-24 2024-06-24 Paid 31 Concept Design
163
+ INV-002-001 KD-2024-002 2024-01-20 1409558 2024-02-19 NaN Outstanding 27 Final Completion
164
+ INV-002-002 KD-2024-002 2024-02-27 509791 2024-03-28 2024-04-26 Paid 46 Construction Documentation
165
+ INV-002-003 KD-2024-002 2024-04-30 529521 2024-05-30 NaN Overdue 12 Concept Design
166
+ INV-003-001 KD-2024-003 2024-01-21 2038477 2024-02-20 2024-02-29 Overdue 56 Construction Documentation
167
+ INV-003-002 KD-2024-003 2024-02-24 770387 2024-03-25 2024-04-13 Overdue 37 Construction Documentation
168
+ INV-004-001 KD-2024-004 2024-01-03 2227725 2024-02-02 2024-02-16 Outstanding 29 Construction Documentation
169
+ INV-004-002 KD-2024-004 2024-02-28 2362216 2024-03-29 2024-04-14 Outstanding 22 Design Development
170
+ INV-004-003 KD-2024-004 2024-04-05 2488276 2024-05-05 NaN Paid 14 Design Development
171
+ INV-005-001 KD-2024-005 2024-01-07 2039470 2024-02-06 2024-03-09 Overdue 10 Concept Design
172
+ INV-005-002 KD-2024-005 2024-02-26 1991305 2024-03-27 2024-04-15 Overdue 16 Concept Design
173
+ INV-005-003 KD-2024-005 2024-04-19 600580 2024-05-19 2024-06-24 Paid 3 Concept Design
174
+
175
+ --- Sheet: Subcontractors ---
176
+
177
+ Project_ID Subcontractor_Name Service_Type Contract_Amount_AED Amount_Invoiced_AED Payment_Status Work_Category
178
+ KD-2024-001 UAE Fire Safety Systems Fire Safety 179598 573867 Paid Subcontractor
179
+ KD-2024-001 UAE Fire Safety Systems Acoustics 210565 215477 Outstanding Subcontractor
180
+ KD-2024-001 Acoustic Engineering Partners Acoustics 59756 315743 Paid Subcontractor
181
+ KD-2024-001 Acoustic Engineering Partners Structural Engineering 744663 545583 Paid Subcontractor
182
+ KD-2024-001 Sustainability Consultants MENA Sustainability 784764 143826 Paid Subcontractor
183
+ KD-2024-001 Transportation Planning Group Structural Engineering 586435 123453 Paid Subcontractor
184
+ KD-2024-002 Transportation Planning Group Structural Engineering 571917 208296 Outstanding Subcontractor
185
+ KD-2024-002 Gulf MEP Consultants Landscape 598558 211469 Paid Subcontractor
186
+ KD-2024-002 Acoustic Engineering Partners Fire Safety 359698 168163 Pending Subcontractor
187
+ KD-2024-003 UAE Fire Safety Systems MEP Design 771634 504012 Paid Subcontractor
188
+ KD-2024-003 Gulf MEP Consultants Structural Engineering 63368 93326 Pending Subcontractor
189
+ KD-2024-003 Transportation Planning Group Sustainability 751520 518105 Pending Subcontractor
190
+ KD-2024-003 Dubai Structural Engineering LLC Sustainability 128693 106210 Paid Subcontractor
191
+ KD-2024-003 Acoustic Engineering Partners MEP Design 528938 310401 Paid Subcontractor
192
+ KD-2024-003 UAE Fire Safety Systems Structural Engineering 154657 183188 Pending Subcontractor
193
+ KD-2024-003 Transportation Planning Group Landscape 427712 327897 Paid Subcontractor
194
+ KD-2024-004 Dubai Structural Engineering LLC Structural Engineering 98118 10142 Pending Subcontractor
195
+ KD-2024-004 Acoustic Engineering Partners Acoustics 228795 344625 Paid Subcontractor
196
+ KD-2024-004 UAE Fire Safety Systems Sustainability 331342 97466 Paid Subcontractor
197
+ KD-2024-004 Transportation Planning Group Landscape 241752 119361 Paid Subcontractor
198
+ KD-2024-004 UAE Fire Safety Systems Fire Safety 335037 404452 Outstanding Subcontractor
199
+ KD-2024-004 Transportation Planning Group Fire Safety 492226 174880 Pending Subcontractor
200
+ KD-2024-004 Gulf MEP Consultants Acoustics 111495 167138 Pending Subcontractor
201
+ KD-2024-004 UAE Fire Safety Systems Sustainability 556121 596580 Outstanding Subcontractor
202
+ KD-2024-005 Gulf MEP Consultants Sustainability 448197 376018 Outstanding Subcontractor
203
+ KD-2024-005 Dubai Structural Engineering LLC Acoustics 187002 506545 Paid Subcontractor
204
+ KD-2024-005 Dubai Structural Engineering LLC Fire Safety 512094 48205 Pending Subcontractor
205
+ KD-2024-005 Emirates Landscape Design Acoustics 379502 12960 Outstanding Subcontractor
206
+ KD-2024-005 Transportation Planning Group Acoustics 600311 592458 Pending Subcontractor
207
+ KD-2024-005 Acoustic Engineering Partners Landscape 594315 548513 Outstanding Subcontractor
208
+
209
+ --- Sheet: Data_Instructions ---
210
+
211
+ Sheet_Name Description Key_Fields Update_Frequency
212
+ Projects Master list of all projects with contract values, planned costs, dates and project details Project_ID (unique), Contract_Value_AED, Planned_Cost_AED, Project_Type Monthly or when new projects are added
213
+ Employees Complete employee database with roles, rates, and cost categories for accurate costing Employee_ID (unique), Hourly_Rate_AED, Cost_Category, Role When new employees join or rates change
214
+ Timesheets Daily time tracking entries by employees on projects - core data for actual cost calculations Employee_ID, Project_ID, Hours_Worked, Work_Category, Date Daily - core operational data
215
+ Milestones Project milestone tracking with planned vs actual dates for timeline analysis Project_ID, Milestone_Name, Planned_Date, Actual_Date, Status Weekly as milestones are achieved
216
+ Invoices Invoice register for tracking billing, payments, and outstanding amounts per project Project_ID, Invoice_Amount_AED, Payment_Status, Days_Outstanding Monthly or when invoices are sent/paid
217
+ Subcontractors External consultant and subcontractor costs by project for complete cost tracking Project_ID, Service_Type, Contract_Amount_AED, Work_Category When subcontractor agreements are signed
218
+
219
+ --- Sheet: milestone_budgets ---
220
+
221
+ Project_ID Milestone_Name Milestone_Order Planned_Date Actual_Date Status Budgeted_Hours Budgeted_Cost_AED Actual_Hours Actual_Cost_AED Cumulative_Budgeted_Hours Cumulative_Budgeted_Cost_AED Cumulative_Actual_Hours Cumulative_Actual_Cost_AED Hours_Variance Cost_Variance_AED Schedule_Variance_Days Budget_Utilization_Percent Architecture_Hours Architecture_Cost_AED PM_Hours PM_Cost_AED Subcontractor_Hours Subcontractor_Cost_AED Notes
222
+ KD-2024-001 Concept Design Approval 1 2024-01-12 2024-01-12 Completed 120 72000 125 75000 120 72000 125 75000 5 3000 0 104.17 80 45000 25 18000 15 9000 Initial design phase completed on schedule
223
+ KD-2024-001 Schematic Design Completion 2 2024-02-17 2024-02-17 Completed 200 135000 215 147500 320 207000 340 222500 15 12500 0 107.49 140 105000 40 22500 35 20000 Additional revisions required
224
+ KD-2024-001 Design Development Approval 3 2024-03-27 NaT In Progress 180 108000 95 67500 500 315000 435 290000 -85 -40500 0 92.06 120 50000 30 12500 25 5000 Ahead of schedule under budget
225
+ KD-2024-002 Concept Design Approval 1 2024-01-18 2024-01-18 Completed 80 48000 78 46800 80 48000 78 46800 -2 -1200 0 97.50 60 36000 15 9000 5 1800 Efficient design phase
226
+ KD-2024-002 Schematic Design Completion 2 2024-02-18 NaT In Progress 150 90000 85 51000 230 138000 163 97800 -65 -40200 0 70.87 100 60000 30 18000 20 12000 Good progress on schematics
227
+
228
+ --- Sheet: staff_allocation ---
229
+
230
+ Project_ID Milestone_ID Milestone_Name Employee_ID Employee_Name Role Category Hours_Allocated Hours_Worked Hourly_Rate_AED Cost_AED Utilization_Percent Skill_Match_Score Availability_Status Start_Date End_Date Performance_Rating Notes
231
+ KD-2024-001 ms_1 Concept Design Approval EMP-001 Employee 1 Project Manager Architecture 80 85 350 29750 106.25 95 AVAILABLE 2024-01-05 2024-01-15 EXCELLENT Led design team effectively
232
+ KD-2024-001 ms_1 Concept Design Approval EMP-002 Employee 2 Senior Architect Architecture 40 45 850 38250 112.50 98 BUSY 2024-01-05 2024-01-15 EXCELLENT Quality architectural work
233
+ KD-2024-001 ms_2 Schematic Design Completion EMP-002 Employee 2 Senior Architect Architecture 140 150 850 127500 107.14 98 AVAILABLE 2024-02-01 2024-02-20 EXCELLENT Strong schematic development
234
+ KD-2024-001 ms_2 Schematic Design Completion EMP-003 Employee 3 BIM Coordinator Architecture 60 65 400 26000 108.33 92 AVAILABLE 2024-02-01 2024-02-20 GOOD BIM coordination support
235
+ KD-2024-002 ms_1 Concept Design Approval EMP-004 Employee 4 Design Principal Architecture 60 58 850 49300 96.67 95 AVAILABLE 2024-01-10 2024-01-20 GOOD Design leadership
236
+
schemas.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, ConfigDict
2
+ from typing import Optional
3
+ from datetime import date, datetime
4
+ from decimal import Decimal
5
+
6
+ # Project Schemas
7
+ class ProjectBase(BaseModel):
8
+ project_name: str
9
+ client_name: str
10
+ contract_value_aed: Decimal
11
+ planned_cost_aed: Decimal
12
+ project_start_date: date
13
+ project_end_date: date
14
+ project_type: str
15
+ project_status: str
16
+ current_phase: Optional[str] = None
17
+
18
+ class ProjectCreate(ProjectBase):
19
+ project_id: str
20
+
21
+ class Project(ProjectBase):
22
+ project_id: str
23
+ created_at: Optional[datetime] = None
24
+ updated_at: Optional[datetime] = None
25
+
26
+ model_config = ConfigDict(from_attributes=True)
27
+
28
+
29
+ # Employee Schemas
30
+ class EmployeeBase(BaseModel):
31
+ employee_name: str
32
+ department: str
33
+ role: str
34
+ hourly_rate_aed: Decimal
35
+ employment_type: str
36
+ start_date: date
37
+ cost_category: str
38
+ end_date: Optional[date] = None
39
+ is_active: bool = True
40
+ email: Optional[str] = None
41
+ phone: Optional[str] = None
42
+
43
+ class EmployeeCreate(EmployeeBase):
44
+ employee_id: str
45
+
46
+ class Employee(EmployeeBase):
47
+ employee_id: str
48
+ created_at: Optional[datetime] = None
49
+ updated_at: Optional[datetime] = None
50
+
51
+ model_config = ConfigDict(from_attributes=True)
52
+
53
+
54
+ # Timesheet Schemas
55
+ class TimesheetBase(BaseModel):
56
+ date: date
57
+ employee_id: str
58
+ project_id: str
59
+ hours_worked: Decimal
60
+ billable_hours: Decimal
61
+ work_category: str
62
+ task_description: Optional[str] = None
63
+ is_approved: bool = False
64
+
65
+ class TimesheetCreate(TimesheetBase):
66
+ record_id: str
67
+
68
+ class Timesheet(TimesheetBase):
69
+ record_id: str
70
+ created_at: Optional[datetime] = None
71
+ updated_at: Optional[datetime] = None
72
+
73
+ model_config = ConfigDict(from_attributes=True)
74
+
75
+
76
+ # Milestone Schemas
77
+ class MilestoneBase(BaseModel):
78
+ project_id: str
79
+ milestone_name: str
80
+ milestone_order: int
81
+ planned_date: date
82
+ actual_date: Optional[date] = None
83
+ status: str
84
+ completion_percentage: int = 0
85
+
86
+ class MilestoneCreate(MilestoneBase):
87
+ pass
88
+
89
+ class Milestone(MilestoneBase):
90
+ milestone_id: int
91
+ created_at: Optional[datetime] = None
92
+ updated_at: Optional[datetime] = None
93
+
94
+ model_config = ConfigDict(from_attributes=True)
95
+
96
+
97
+ # Invoice Schemas
98
+ class InvoiceBase(BaseModel):
99
+ project_id: str
100
+ invoice_date: date
101
+ invoice_amount_aed: Decimal
102
+ due_date: date
103
+ payment_date: Optional[date] = None
104
+ payment_status: str
105
+ days_outstanding: Optional[int] = None
106
+ milestone_reference: Optional[str] = None
107
+ paid_amount_aed: Decimal = 0
108
+
109
+ class InvoiceCreate(InvoiceBase):
110
+ invoice_id: str
111
+
112
+ class Invoice(InvoiceBase):
113
+ invoice_id: str
114
+ created_at: Optional[datetime] = None
115
+ updated_at: Optional[datetime] = None
116
+
117
+ model_config = ConfigDict(from_attributes=True)
118
+
119
+
120
+ # Subcontractor Schemas
121
+ class SubcontractorBase(BaseModel):
122
+ project_id: str
123
+ subcontractor_name: str
124
+ service_type: str
125
+ contract_amount_aed: Decimal
126
+ amount_invoiced_aed: Decimal = 0
127
+ payment_status: str
128
+ work_category: str
129
+ contract_start_date: Optional[date] = None
130
+ contract_end_date: Optional[date] = None
131
+ contact_person: Optional[str] = None
132
+ contact_email: Optional[str] = None
133
+ contact_phone: Optional[str] = None
134
+
135
+ class SubcontractorCreate(SubcontractorBase):
136
+ pass
137
+
138
+ class Subcontractor(SubcontractorBase):
139
+ subcontractor_id: int
140
+ created_at: Optional[datetime] = None
141
+ updated_at: Optional[datetime] = None
142
+
143
+ model_config = ConfigDict(from_attributes=True)
144
+
145
+
146
+ # Staff Allocation Schemas
147
+ class StaffAllocationBase(BaseModel):
148
+ project_id: str
149
+ milestone_id: Optional[int] = None
150
+ milestone_name: Optional[str] = None
151
+ employee_id: str
152
+ employee_name: str
153
+ role: str
154
+ category: Optional[str] = None
155
+ hours_allocated: Decimal
156
+ hours_worked: Decimal = 0
157
+ hourly_rate_aed: Decimal
158
+ skill_match_score: Optional[int] = None
159
+ availability_status: Optional[str] = None
160
+ performance_rating: Optional[str] = None
161
+ start_date: date
162
+ end_date: Optional[date] = None
163
+ notes: Optional[str] = None
164
+
165
+ class StaffAllocationCreate(StaffAllocationBase):
166
+ pass
167
+
168
+ class StaffAllocation(StaffAllocationBase):
169
+ allocation_id: int
170
+ created_at: Optional[datetime] = None
171
+ updated_at: Optional[datetime] = None
172
+
173
+ model_config = ConfigDict(from_attributes=True)
static/style.css ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Custom Styles */
2
+ body {
3
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
4
+ background-color: #f8f9fa;
5
+ }
6
+
7
+ .navbar-brand {
8
+ font-weight: bold;
9
+ font-size: 1.5rem;
10
+ }
11
+
12
+ .card {
13
+ box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
14
+ border: none;
15
+ }
16
+
17
+ .table {
18
+ font-size: 0.9rem;
19
+ }
20
+
21
+ .modal-header {
22
+ background-color: #0d6efd;
23
+ color: white;
24
+ }
25
+
26
+ .btn {
27
+ border-radius: 0.25rem;
28
+ }
29
+
30
+ footer {
31
+ margin-top: auto;
32
+ }
33
+
34
+ /* Responsive tables */
35
+ @media (max-width: 768px) {
36
+ .table {
37
+ font-size: 0.75rem;
38
+ }
39
+ }
40
+
41
+ .display-4 {
42
+ font-weight: bold;
43
+ }
templates/base.html ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}Architecture PM System{% endblock %}</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css">
9
+ <link rel="stylesheet" href="/static/style.css">
10
+ </head>
11
+ <body>
12
+ <!-- Navigation -->
13
+ <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
14
+ <div class="container-fluid">
15
+ <a class="navbar-brand" href="/">
16
+ <i class="bi bi-building"></i> Architecture PM
17
+ </a>
18
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
19
+ <span class="navbar-toggler-icon"></span>
20
+ </button>
21
+ <div class="collapse navbar-collapse" id="navbarNav">
22
+ <ul class="navbar-nav">
23
+ <li class="nav-item">
24
+ <a class="nav-link" href="/"><i class="bi bi-house-door"></i> Home</a>
25
+ </li>
26
+ <li class="nav-item">
27
+ <a class="nav-link" href="/projects"><i class="bi bi-folder"></i> Projects</a>
28
+ </li>
29
+ <li class="nav-item">
30
+ <a class="nav-link" href="/employees"><i class="bi bi-people"></i> Employees</a>
31
+ </li>
32
+ <li class="nav-item">
33
+ <a class="nav-link" href="/timesheets"><i class="bi bi-clock-history"></i> Timesheets</a>
34
+ </li>
35
+ <li class="nav-item">
36
+ <a class="nav-link" href="/milestones"><i class="bi bi-flag"></i> Milestones</a>
37
+ </li>
38
+ <li class="nav-item">
39
+ <a class="nav-link" href="/invoices"><i class="bi bi-receipt"></i> Invoices</a>
40
+ </li>
41
+ <li class="nav-item">
42
+ <a class="nav-link" href="/subcontractors"><i class="bi bi-tools"></i> Subcontractors</a>
43
+ </li>
44
+ <li class="nav-item">
45
+ <a class="nav-link" href="/staff-allocation"><i class="bi bi-person-workspace"></i> Staff Allocation</a>
46
+ </li>
47
+ </ul>
48
+ </div>
49
+ </div>
50
+ </nav>
51
+
52
+ <!-- Main Content -->
53
+ <main class="container-fluid py-4">
54
+ {% block content %}{% endblock %}
55
+ </main>
56
+
57
+ <!-- Footer -->
58
+ <footer class="bg-light text-center text-muted py-3 mt-5">
59
+ <div class="container">
60
+ <p class="mb-0">© 2024 Architecture PM System. All rights reserved.</p>
61
+ </div>
62
+ </footer>
63
+
64
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
65
+ {% block scripts %}{% endblock %}
66
+ </body>
67
+ </html>
templates/employees.html ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block title %}Employees{% endblock %}
3
+ {% block content %}
4
+ <div class="container-fluid">
5
+ <div class="d-flex justify-content-between mb-4">
6
+ <h1><i class="bi bi-people"></i> Employees</h1>
7
+ <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addModal">
8
+ <i class="bi bi-plus"></i> Add Employee
9
+ </button>
10
+ </div>
11
+ <div class="card">
12
+ <div class="card-body">
13
+ <table class="table table-striped">
14
+ <thead class="table-dark">
15
+ <tr>
16
+ <th>ID</th><th>Name</th><th>Department</th><th>Role</th>
17
+ <th>Rate (AED/hr)</th><th>Type</th><th>Category</th><th>Actions</th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ {% for emp in employees %}
22
+ <tr>
23
+ <td>{{ emp.employee_id }}</td>
24
+ <td>{{ emp.employee_name }}</td>
25
+ <td>{{ emp.department }}</td>
26
+ <td>{{ emp.role }}</td>
27
+ <td>{{ emp.hourly_rate_aed }}</td>
28
+ <td><span class="badge bg-info">{{ emp.employment_type }}</span></td>
29
+ <td>{{ emp.cost_category }}</td>
30
+ <td>
31
+ <form method="post" action="/employees/delete/{{ emp.employee_id }}" style="display:inline;">
32
+ <button class="btn btn-danger btn-sm" onclick="return confirm('Delete?')">
33
+ <i class="bi bi-trash"></i>
34
+ </button>
35
+ </form>
36
+ </td>
37
+ </tr>
38
+ {% endfor %}
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="modal fade" id="addModal">
46
+ <div class="modal-dialog modal-lg">
47
+ <div class="modal-content">
48
+ <div class="modal-header">
49
+ <h5>Add Employee</h5>
50
+ <button class="btn-close" data-bs-dismiss="modal"></button>
51
+ </div>
52
+ <form method="post" action="/employees/create">
53
+ <div class="modal-body">
54
+ <div class="row g-3">
55
+ <div class="col-md-6">
56
+ <label>Employee ID</label>
57
+ <input type="text" class="form-control" name="employee_id" required>
58
+ </div>
59
+ <div class="col-md-6">
60
+ <label>Name</label>
61
+ <input type="text" class="form-control" name="employee_name" required>
62
+ </div>
63
+ <div class="col-md-6">
64
+ <label>Department</label>
65
+ <input type="text" class="form-control" name="department" required>
66
+ </div>
67
+ <div class="col-md-6">
68
+ <label>Role</label>
69
+ <input type="text" class="form-control" name="role" required>
70
+ </div>
71
+ <div class="col-md-6">
72
+ <label>Hourly Rate (AED)</label>
73
+ <input type="number" step="0.01" class="form-control" name="hourly_rate_aed" required>
74
+ </div>
75
+ <div class="col-md-6">
76
+ <label>Employment Type</label>
77
+ <select class="form-select" name="employment_type" required>
78
+ <option value="Full-Time">Full-Time</option>
79
+ <option value="Contract">Contract</option>
80
+ <option value="Part-Time">Part-Time</option>
81
+ </select>
82
+ </div>
83
+ <div class="col-md-6">
84
+ <label>Start Date</label>
85
+ <input type="date" class="form-control" name="start_date" required>
86
+ </div>
87
+ <div class="col-md-6">
88
+ <label>Cost Category</label>
89
+ <select class="form-select" name="cost_category" required>
90
+ <option value="Architecture">Architecture</option>
91
+ <option value="Project Management">Project Management</option>
92
+ <option value="Engineering">Engineering</option>
93
+ </select>
94
+ </div>
95
+ <div class="col-md-6">
96
+ <label>Email</label>
97
+ <input type="email" class="form-control" name="email">
98
+ </div>
99
+ <div class="col-md-6">
100
+ <label>Phone</label>
101
+ <input type="text" class="form-control" name="phone">
102
+ </div>
103
+ </div>
104
+ </div>
105
+ <div class="modal-footer">
106
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
107
+ <button type="submit" class="btn btn-success">Add</button>
108
+ </div>
109
+ </form>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ {% endblock %}
templates/index.html ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Dashboard - Architecture PM System{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container">
7
+ <h1 class="mb-4"><i class="bi bi-speedometer2"></i> Dashboard</h1>
8
+
9
+ <div class="row g-4 mb-5">
10
+ <div class="col-md-3">
11
+ <div class="card text-white bg-primary">
12
+ <div class="card-body">
13
+ <h5 class="card-title"><i class="bi bi-folder"></i> Projects</h5>
14
+ <h2 class="display-4">{{ projects_count }}</h2>
15
+ <a href="/projects" class="btn btn-light btn-sm mt-2">View All</a>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <div class="col-md-3">
20
+ <div class="card text-white bg-success">
21
+ <div class="card-body">
22
+ <h5 class="card-title"><i class="bi bi-people"></i> Employees</h5>
23
+ <h2 class="display-4">{{ employees_count }}</h2>
24
+ <a href="/employees" class="btn btn-light btn-sm mt-2">View All</a>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ <div class="col-md-3">
29
+ <div class="card text-white bg-info">
30
+ <div class="card-body">
31
+ <h5 class="card-title"><i class="bi bi-clock-history"></i> Timesheets</h5>
32
+ <h2 class="display-4">{{ timesheets_count }}</h2>
33
+ <a href="/timesheets" class="btn btn-light btn-sm mt-2">View All</a>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <div class="col-md-3">
38
+ <div class="card text-white bg-warning">
39
+ <div class="card-body">
40
+ <h5 class="card-title"><i class="bi bi-receipt"></i> Invoices</h5>
41
+ <h2 class="display-4">{{ invoices_count }}</h2>
42
+ <a href="/invoices" class="btn btn-light btn-sm mt-2">View All</a>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="row">
49
+ <div class="col-md-12">
50
+ <div class="card">
51
+ <div class="card-header bg-primary text-white">
52
+ <h5 class="mb-0"><i class="bi bi-graph-up"></i> Quick Links</h5>
53
+ </div>
54
+ <div class="card-body">
55
+ <div class="list-group">
56
+ <a href="/projects" class="list-group-item list-group-item-action">
57
+ <i class="bi bi-folder"></i> Manage Projects
58
+ </a>
59
+ <a href="/employees" class="list-group-item list-group-item-action">
60
+ <i class="bi bi-people"></i> Manage Employees
61
+ </a>
62
+ <a href="/timesheets" class="list-group-item list-group-item-action">
63
+ <i class="bi bi-clock-history"></i> Track Time
64
+ </a>
65
+ <a href="/milestones" class="list-group-item list-group-item-action">
66
+ <i class="bi bi-flag"></i> Monitor Milestones
67
+ </a>
68
+ <a href="/invoices" class="list-group-item list-group-item-action">
69
+ <i class="bi bi-receipt"></i> Process Invoices
70
+ </a>
71
+ <a href="/subcontractors" class="list-group-item list-group-item-action">
72
+ <i class="bi bi-tools"></i> Manage Subcontractors
73
+ </a>
74
+ <a href="/staff-allocation" class="list-group-item list-group-item-action">
75
+ <i class="bi bi-person-workspace"></i> Allocate Staff
76
+ </a>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ {% endblock %}
templates/invoices.html ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Invoices - Architecture PM System{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1><i class="bi bi-receipt"></i> Project Invoices</h1>
9
+ <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addInvoiceModal">
10
+ <i class="bi bi-plus-circle"></i> Add Invoice
11
+ </button>
12
+ </div>
13
+
14
+ <!-- Summary Cards -->
15
+ <div class="row g-3 mb-4">
16
+ <div class="col-md-3">
17
+ <div class="card bg-success text-white">
18
+ <div class="card-body">
19
+ <h6 class="card-title">Total Invoiced</h6>
20
+ <h3>AED {{ "{:,.2f}".format(invoices|sum(attribute='invoice_amount_aed')|default(0)) }}</h3>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <div class="col-md-3">
25
+ <div class="card bg-warning text-dark">
26
+ <div class="card-body">
27
+ <h6 class="card-title">Outstanding</h6>
28
+ <h3>{{ invoices|selectattr('payment_status', 'equalto', 'Outstanding')|list|length }}</h3>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <div class="col-md-3">
33
+ <div class="card bg-danger text-white">
34
+ <div class="card-body">
35
+ <h6 class="card-title">Overdue</h6>
36
+ <h3>{{ invoices|selectattr('payment_status', 'equalto', 'Overdue')|list|length }}</h3>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ <div class="col-md-3">
41
+ <div class="card bg-info text-white">
42
+ <div class="card-body">
43
+ <h6 class="card-title">Paid</h6>
44
+ <h3>{{ invoices|selectattr('payment_status', 'equalto', 'Paid')|list|length }}</h3>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="card">
51
+ <div class="card-body">
52
+ <div class="table-responsive">
53
+ <table class="table table-striped table-hover">
54
+ <thead class="table-dark">
55
+ <tr>
56
+ <th>Invoice ID</th>
57
+ <th>Project ID</th>
58
+ <th>Invoice Date</th>
59
+ <th>Amount (AED)</th>
60
+ <th>Due Date</th>
61
+ <th>Payment Date</th>
62
+ <th>Status</th>
63
+ <th>Days Outstanding</th>
64
+ <th>Milestone</th>
65
+ <th>Actions</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ {% for invoice in invoices %}
70
+ <tr>
71
+ <td><strong>{{ invoice.invoice_id }}</strong></td>
72
+ <td><span class="badge bg-secondary">{{ invoice.project_id }}</span></td>
73
+ <td>{{ invoice.invoice_date }}</td>
74
+ <td><strong>{{ "{:,.2f}".format(invoice.invoice_amount_aed) }}</strong></td>
75
+ <td>{{ invoice.due_date }}</td>
76
+ <td>
77
+ {% if invoice.payment_date %}
78
+ <span class="text-success">{{ invoice.payment_date }}</span>
79
+ {% else %}
80
+ <span class="text-muted">Not paid</span>
81
+ {% endif %}
82
+ </td>
83
+ <td>
84
+ {% if invoice.payment_status == 'Paid' %}
85
+ <span class="badge bg-success">Paid</span>
86
+ {% elif invoice.payment_status == 'Outstanding' %}
87
+ <span class="badge bg-warning text-dark">Outstanding</span>
88
+ {% elif invoice.payment_status == 'Overdue' %}
89
+ <span class="badge bg-danger">Overdue</span>
90
+ {% else %}
91
+ <span class="badge bg-secondary">{{ invoice.payment_status }}</span>
92
+ {% endif %}
93
+ </td>
94
+ <td>
95
+ {% if invoice.days_outstanding %}
96
+ <span class="badge bg-info">{{ invoice.days_outstanding }} days</span>
97
+ {% else %}
98
+ -
99
+ {% endif %}
100
+ </td>
101
+ <td><small>{{ invoice.milestone_reference or '-' }}</small></td>
102
+ <td>
103
+ <form method="post" action="/invoices/delete/{{ invoice.invoice_id }}" style="display:inline;">
104
+ <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this invoice?')">
105
+ <i class="bi bi-trash"></i>
106
+ </button>
107
+ </form>
108
+ </td>
109
+ </tr>
110
+ {% endfor %}
111
+ </tbody>
112
+ </table>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ <!-- Add Invoice Modal -->
119
+ <div class="modal fade" id="addInvoiceModal" tabindex="-1">
120
+ <div class="modal-dialog modal-lg">
121
+ <div class="modal-content">
122
+ <div class="modal-header bg-success text-white">
123
+ <h5 class="modal-title">Add New Invoice</h5>
124
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
125
+ </div>
126
+ <form method="post" action="/invoices/create">
127
+ <div class="modal-body">
128
+ <div class="row g-3">
129
+ <div class="col-md-6">
130
+ <label class="form-label">Invoice ID</label>
131
+ <input type="text" class="form-control" name="invoice_id" required placeholder="INV-001-001">
132
+ </div>
133
+ <div class="col-md-6">
134
+ <label class="form-label">Project</label>
135
+ <select class="form-select" name="project_id" required>
136
+ <option value="">Select Project</option>
137
+ {% for project in projects %}
138
+ <option value="{{ project.project_id }}">{{ project.project_id }} - {{ project.project_name }}</option>
139
+ {% endfor %}
140
+ </select>
141
+ </div>
142
+ <div class="col-md-6">
143
+ <label class="form-label">Invoice Date</label>
144
+ <input type="date" class="form-control" name="invoice_date" required>
145
+ </div>
146
+ <div class="col-md-6">
147
+ <label class="form-label">Invoice Amount (AED)</label>
148
+ <input type="number" step="0.01" class="form-control" name="invoice_amount_aed" required placeholder="10000.00">
149
+ </div>
150
+ <div class="col-md-6">
151
+ <label class="form-label">Due Date</label>
152
+ <input type="date" class="form-control" name="due_date" required>
153
+ </div>
154
+ <div class="col-md-6">
155
+ <label class="form-label">Payment Date (Optional)</label>
156
+ <input type="date" class="form-control" name="payment_date">
157
+ </div>
158
+ <div class="col-md-6">
159
+ <label class="form-label">Payment Status</label>
160
+ <select class="form-select" name="payment_status" required>
161
+ <option value="Outstanding">Outstanding</option>
162
+ <option value="Paid">Paid</option>
163
+ <option value="Overdue">Overdue</option>
164
+ <option value="Partial">Partial</option>
165
+ <option value="Cancelled">Cancelled</option>
166
+ </select>
167
+ </div>
168
+ <div class="col-md-6">
169
+ <label class="form-label">Milestone Reference (Optional)</label>
170
+ <input type="text" class="form-control" name="milestone_reference" placeholder="Concept Design">
171
+ </div>
172
+ </div>
173
+ </div>
174
+ <div class="modal-footer">
175
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
176
+ <button type="submit" class="btn btn-success">Add Invoice</button>
177
+ </div>
178
+ </form>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ {% endblock %}
templates/milestones.html ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Milestones - Architecture PM System{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1><i class="bi bi-flag"></i> Project Milestones</h1>
9
+ <button class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#addMilestoneModal">
10
+ <i class="bi bi-plus-circle"></i> Add Milestone
11
+ </button>
12
+ </div>
13
+
14
+ <div class="card">
15
+ <div class="card-body">
16
+ <div class="table-responsive">
17
+ <table class="table table-striped table-hover">
18
+ <thead class="table-dark">
19
+ <tr>
20
+ <th>ID</th>
21
+ <th>Project ID</th>
22
+ <th>Milestone Name</th>
23
+ <th>Order</th>
24
+ <th>Planned Date</th>
25
+ <th>Actual Date</th>
26
+ <th>Status</th>
27
+ <th>Completion %</th>
28
+ <th>Actions</th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ {% for milestone in milestones %}
33
+ <tr>
34
+ <td>{{ milestone.milestone_id }}</td>
35
+ <td><span class="badge bg-secondary">{{ milestone.project_id }}</span></td>
36
+ <td>{{ milestone.milestone_name }}</td>
37
+ <td><span class="badge bg-primary">{{ milestone.milestone_order }}</span></td>
38
+ <td>{{ milestone.planned_date }}</td>
39
+ <td>
40
+ {% if milestone.actual_date %}
41
+ {{ milestone.actual_date }}
42
+ {% else %}
43
+ <span class="text-muted">Not completed</span>
44
+ {% endif %}
45
+ </td>
46
+ <td>
47
+ {% if milestone.status == 'Completed' %}
48
+ <span class="badge bg-success">{{ milestone.status }}</span>
49
+ {% elif milestone.status == 'In Progress' %}
50
+ <span class="badge bg-info">{{ milestone.status }}</span>
51
+ {% elif milestone.status == 'Pending' %}
52
+ <span class="badge bg-warning text-dark">{{ milestone.status }}</span>
53
+ {% elif milestone.status == 'Delayed' %}
54
+ <span class="badge bg-danger">{{ milestone.status }}</span>
55
+ {% else %}
56
+ <span class="badge bg-secondary">{{ milestone.status }}</span>
57
+ {% endif %}
58
+ </td>
59
+ <td>
60
+ <div class="progress" style="height: 20px;">
61
+ <div class="progress-bar
62
+ {% if milestone.completion_percentage >= 75 %}bg-success
63
+ {% elif milestone.completion_percentage >= 50 %}bg-info
64
+ {% elif milestone.completion_percentage >= 25 %}bg-warning
65
+ {% else %}bg-danger{% endif %}"
66
+ role="progressbar"
67
+ style="width: {{ milestone.completion_percentage }}%"
68
+ aria-valuenow="{{ milestone.completion_percentage }}"
69
+ aria-valuemin="0"
70
+ aria-valuemax="100">
71
+ {{ milestone.completion_percentage }}%
72
+ </div>
73
+ </div>
74
+ </td>
75
+ <td>
76
+ <form method="post" action="/milestones/delete/{{ milestone.milestone_id }}" style="display:inline;">
77
+ <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this milestone?')">
78
+ <i class="bi bi-trash"></i>
79
+ </button>
80
+ </form>
81
+ </td>
82
+ </tr>
83
+ {% endfor %}
84
+ </tbody>
85
+ </table>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+
91
+ <!-- Add Milestone Modal -->
92
+ <div class="modal fade" id="addMilestoneModal" tabindex="-1">
93
+ <div class="modal-dialog modal-lg">
94
+ <div class="modal-content">
95
+ <div class="modal-header bg-warning">
96
+ <h5 class="modal-title text-dark">Add New Milestone</h5>
97
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
98
+ </div>
99
+ <form method="post" action="/milestones/create">
100
+ <div class="modal-body">
101
+ <div class="row g-3">
102
+ <div class="col-md-6">
103
+ <label class="form-label">Project</label>
104
+ <select class="form-select" name="project_id" required>
105
+ <option value="">Select Project</option>
106
+ {% for project in projects %}
107
+ <option value="{{ project.project_id }}">{{ project.project_id }} - {{ project.project_name }}</option>
108
+ {% endfor %}
109
+ </select>
110
+ </div>
111
+ <div class="col-md-6">
112
+ <label class="form-label">Milestone Name</label>
113
+ <input type="text" class="form-control" name="milestone_name" required placeholder="e.g., Concept Design Approval">
114
+ </div>
115
+ <div class="col-md-6">
116
+ <label class="form-label">Milestone Order</label>
117
+ <input type="number" class="form-control" name="milestone_order" required min="1" placeholder="1">
118
+ </div>
119
+ <div class="col-md-6">
120
+ <label class="form-label">Planned Date</label>
121
+ <input type="date" class="form-control" name="planned_date" required>
122
+ </div>
123
+ <div class="col-md-6">
124
+ <label class="form-label">Actual Date (Optional)</label>
125
+ <input type="date" class="form-control" name="actual_date">
126
+ </div>
127
+ <div class="col-md-6">
128
+ <label class="form-label">Status</label>
129
+ <select class="form-select" name="status" required>
130
+ <option value="Pending">Pending</option>
131
+ <option value="In Progress">In Progress</option>
132
+ <option value="Completed">Completed</option>
133
+ <option value="Delayed">Delayed</option>
134
+ <option value="Cancelled">Cancelled</option>
135
+ </select>
136
+ </div>
137
+ <div class="col-md-12">
138
+ <label class="form-label">Completion Percentage</label>
139
+ <input type="range" class="form-range" name="completion_percentage" min="0" max="100" value="0" id="completionRange">
140
+ <div class="text-center">
141
+ <strong id="completionValue">0%</strong>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ <div class="modal-footer">
147
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
148
+ <button type="submit" class="btn btn-warning">Add Milestone</button>
149
+ </div>
150
+ </form>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <script>
156
+ // Update completion percentage display
157
+ const rangeInput = document.getElementById('completionRange');
158
+ const valueDisplay = document.getElementById('completionValue');
159
+
160
+ if (rangeInput && valueDisplay) {
161
+ rangeInput.addEventListener('input', function() {
162
+ valueDisplay.textContent = this.value + '%';
163
+ });
164
+ }
165
+ </script>
166
+ {% endblock %}
templates/projects.html ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Projects - Architecture PM System{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1><i class="bi bi-folder"></i> Projects</h1>
9
+ <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addProjectModal">
10
+ <i class="bi bi-plus-circle"></i> Add Project
11
+ </button>
12
+ </div>
13
+
14
+ <div class="card">
15
+ <div class="card-body">
16
+ <div class="table-responsive">
17
+ <table class="table table-striped table-hover">
18
+ <thead class="table-dark">
19
+ <tr>
20
+ <th>Project ID</th>
21
+ <th>Project Name</th>
22
+ <th>Client</th>
23
+ <th>Type</th>
24
+ <th>Status</th>
25
+ <th>Contract Value (AED)</th>
26
+ <th>Planned Cost (AED)</th>
27
+ <th>Start Date</th>
28
+ <th>End Date</th>
29
+ <th>Actions</th>
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ {% for project in projects %}
34
+ <tr>
35
+ <td>{{ project.project_id }}</td>
36
+ <td>{{ project.project_name }}</td>
37
+ <td>{{ project.client_name }}</td>
38
+ <td><span class="badge bg-info">{{ project.project_type }}</span></td>
39
+ <td>
40
+ {% if project.project_status == 'In Progress' %}
41
+ <span class="badge bg-success">{{ project.project_status }}</span>
42
+ {% elif project.project_status == 'Planning' %}
43
+ <span class="badge bg-warning">{{ project.project_status }}</span>
44
+ {% else %}
45
+ <span class="badge bg-secondary">{{ project.project_status }}</span>
46
+ {% endif %}
47
+ </td>
48
+ <td>{{ "{:,.2f}".format(project.contract_value_aed) }}</td>
49
+ <td>{{ "{:,.2f}".format(project.planned_cost_aed) }}</td>
50
+ <td>{{ project.project_start_date }}</td>
51
+ <td>{{ project.project_end_date }}</td>
52
+ <td>
53
+ <form method="post" action="/projects/delete/{{ project.project_id }}" style="display:inline;">
54
+ <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?')">
55
+ <i class="bi bi-trash"></i>
56
+ </button>
57
+ </form>
58
+ </td>
59
+ </tr>
60
+ {% endfor %}
61
+ </tbody>
62
+ </table>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <!-- Add Project Modal -->
69
+ <div class="modal fade" id="addProjectModal" tabindex="-1">
70
+ <div class="modal-dialog modal-lg">
71
+ <div class="modal-content">
72
+ <div class="modal-header">
73
+ <h5 class="modal-title">Add New Project</h5>
74
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
75
+ </div>
76
+ <form method="post" action="/projects/create">
77
+ <div class="modal-body">
78
+ <div class="row g-3">
79
+ <div class="col-md-6">
80
+ <label class="form-label">Project ID</label>
81
+ <input type="text" class="form-control" name="project_id" required>
82
+ </div>
83
+ <div class="col-md-6">
84
+ <label class="form-label">Project Name</label>
85
+ <input type="text" class="form-control" name="project_name" required>
86
+ </div>
87
+ <div class="col-md-6">
88
+ <label class="form-label">Client Name</label>
89
+ <input type="text" class="form-control" name="client_name" required>
90
+ </div>
91
+ <div class="col-md-6">
92
+ <label class="form-label">Project Type</label>
93
+ <select class="form-select" name="project_type" required>
94
+ <option value="Mixed-Use">Mixed-Use</option>
95
+ <option value="Hospitality">Hospitality</option>
96
+ <option value="Office">Office</option>
97
+ <option value="Residential">Residential</option>
98
+ <option value="Cultural">Cultural</option>
99
+ </select>
100
+ </div>
101
+ <div class="col-md-6">
102
+ <label class="form-label">Contract Value (AED)</label>
103
+ <input type="number" step="0.01" class="form-control" name="contract_value_aed" required>
104
+ </div>
105
+ <div class="col-md-6">
106
+ <label class="form-label">Planned Cost (AED)</label>
107
+ <input type="number" step="0.01" class="form-control" name="planned_cost_aed" required>
108
+ </div>
109
+ <div class="col-md-6">
110
+ <label class="form-label">Start Date</label>
111
+ <input type="date" class="form-control" name="project_start_date" required>
112
+ </div>
113
+ <div class="col-md-6">
114
+ <label class="form-label">End Date</label>
115
+ <input type="date" class="form-control" name="project_end_date" required>
116
+ </div>
117
+ <div class="col-md-6">
118
+ <label class="form-label">Project Status</label>
119
+ <select class="form-select" name="project_status" required>
120
+ <option value="Planning">Planning</option>
121
+ <option value="In Progress">In Progress</option>
122
+ <option value="Design Phase">Design Phase</option>
123
+ <option value="On Hold">On Hold</option>
124
+ <option value="Completed">Completed</option>
125
+ </select>
126
+ </div>
127
+ <div class="col-md-6">
128
+ <label class="form-label">Current Phase</label>
129
+ <input type="text" class="form-control" name="current_phase">
130
+ </div>
131
+ </div>
132
+ </div>
133
+ <div class="modal-footer">
134
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
135
+ <button type="submit" class="btn btn-primary">Add Project</button>
136
+ </div>
137
+ </form>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ {% endblock %}
templates/staff_allocation.html ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Staff Allocation - Architecture PM System{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1><i class="bi bi-person-workspace"></i> Staff Allocation</h1>
9
+ <button class="btn btn-info" data-bs-toggle="modal" data-bs-target="#addAllocationModal">
10
+ <i class="bi bi-plus-circle"></i> Add Allocation
11
+ </button>
12
+ </div>
13
+
14
+ <!-- Summary Cards -->
15
+ <div class="row g-3 mb-4">
16
+ <div class="col-md-3">
17
+ <div class="card bg-info text-white">
18
+ <div class="card-body">
19
+ <h6 class="card-title">Total Allocations</h6>
20
+ <h3>{{ allocations|length }}</h3>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <div class="col-md-3">
25
+ <div class="card bg-primary text-white">
26
+ <div class="card-body">
27
+ <h6 class="card-title">Total Hours Allocated</h6>
28
+ <h3>{{ "{:,.0f}".format(allocations|sum(attribute='hours_allocated')|default(0)) }}</h3>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <div class="col-md-3">
33
+ <div class="card bg-success text-white">
34
+ <div class="card-body">
35
+ <h6 class="card-title">Total Hours Worked</h6>
36
+ <h3>{{ "{:,.0f}".format(allocations|sum(attribute='hours_worked')|default(0)) }}</h3>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ <div class="col-md-3">
41
+ <div class="card bg-warning text-dark">
42
+ <div class="card-body">
43
+ <h6 class="card-title">Avg Utilization</h6>
44
+ {% set total_allocated = allocations|sum(attribute='hours_allocated')|default(1) %}
45
+ {% set total_worked = allocations|sum(attribute='hours_worked')|default(0) %}
46
+ <h3>{{ "{:.1f}".format((total_worked / total_allocated * 100) if total_allocated > 0 else 0) }}%</h3>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="card">
53
+ <div class="card-body">
54
+ <div class="table-responsive">
55
+ <table class="table table-striped table-hover table-sm">
56
+ <thead class="table-dark">
57
+ <tr>
58
+ <th>ID</th>
59
+ <th>Project</th>
60
+ <th>Milestone</th>
61
+ <th>Employee</th>
62
+ <th>Role</th>
63
+ <th>Category</th>
64
+ <th>Hours Allocated</th>
65
+ <th>Hours Worked</th>
66
+ <th>Utilization %</th>
67
+ <th>Rate (AED/hr)</th>
68
+ <th>Cost (AED)</th>
69
+ <th>Skill Match</th>
70
+ <th>Availability</th>
71
+ <th>Performance</th>
72
+ <th>Start Date</th>
73
+ <th>End Date</th>
74
+ <th>Actions</th>
75
+ </tr>
76
+ </thead>
77
+ <tbody>
78
+ {% for alloc in allocations %}
79
+ <tr>
80
+ <td>{{ alloc.allocation_id }}</td>
81
+ <td><span class="badge bg-secondary">{{ alloc.project_id }}</span></td>
82
+ <td><small>{{ alloc.milestone_name or '-' }}</small></td>
83
+ <td>
84
+ <strong>{{ alloc.employee_name }}</strong>
85
+ <br><small class="text-muted">{{ alloc.employee_id }}</small>
86
+ </td>
87
+ <td><small>{{ alloc.role }}</small></td>
88
+ <td><span class="badge bg-info">{{ alloc.category or '-' }}</span></td>
89
+ <td>{{ alloc.hours_allocated }}</td>
90
+ <td><strong>{{ alloc.hours_worked }}</strong></td>
91
+ <td>
92
+ {% set utilization = (alloc.hours_worked / alloc.hours_allocated * 100) if alloc.hours_allocated > 0 else 0 %}
93
+ <div class="progress" style="height: 20px; min-width: 60px;">
94
+ <div class="progress-bar
95
+ {% if utilization >= 100 %}bg-success
96
+ {% elif utilization >= 75 %}bg-info
97
+ {% elif utilization >= 50 %}bg-warning
98
+ {% else %}bg-danger{% endif %}"
99
+ role="progressbar"
100
+ style="width: {{ [utilization, 100]|min }}%">
101
+ {{ "{:.0f}".format(utilization) }}%
102
+ </div>
103
+ </div>
104
+ </td>
105
+ <td>{{ alloc.hourly_rate_aed }}</td>
106
+ <td><strong>{{ "{:,.2f}".format(alloc.hours_worked * alloc.hourly_rate_aed) }}</strong></td>
107
+ <td>
108
+ {% if alloc.skill_match_score %}
109
+ <span class="badge
110
+ {% if alloc.skill_match_score >= 90 %}bg-success
111
+ {% elif alloc.skill_match_score >= 70 %}bg-info
112
+ {% else %}bg-warning{% endif %}">
113
+ {{ alloc.skill_match_score }}
114
+ </span>
115
+ {% else %}
116
+ -
117
+ {% endif %}
118
+ </td>
119
+ <td>
120
+ {% if alloc.availability_status == 'AVAILABLE' %}
121
+ <span class="badge bg-success">Available</span>
122
+ {% elif alloc.availability_status == 'BUSY' %}
123
+ <span class="badge bg-danger">Busy</span>
124
+ {% elif alloc.availability_status == 'PARTIALLY_AVAILABLE' %}
125
+ <span class="badge bg-warning text-dark">Partial</span>
126
+ {% elif alloc.availability_status == 'ON_LEAVE' %}
127
+ <span class="badge bg-secondary">On Leave</span>
128
+ {% else %}
129
+ <span class="badge bg-light text-dark">{{ alloc.availability_status or '-' }}</span>
130
+ {% endif %}
131
+ </td>
132
+ <td>
133
+ {% if alloc.performance_rating == 'EXCELLENT' %}
134
+ <span class="badge bg-success">⭐⭐⭐⭐⭐</span>
135
+ {% elif alloc.performance_rating == 'GOOD' %}
136
+ <span class="badge bg-info">⭐⭐⭐⭐</span>
137
+ {% elif alloc.performance_rating == 'AVERAGE' %}
138
+ <span class="badge bg-warning text-dark">⭐⭐⭐</span>
139
+ {% elif alloc.performance_rating == 'NEEDS_IMPROVEMENT' %}
140
+ <span class="badge bg-warning">⭐⭐</span>
141
+ {% elif alloc.performance_rating == 'POOR' %}
142
+ <span class="badge bg-danger">⭐</span>
143
+ {% else %}
144
+ -
145
+ {% endif %}
146
+ </td>
147
+ <td>{{ alloc.start_date }}</td>
148
+ <td>{{ alloc.end_date or '-' }}</td>
149
+ <td>
150
+ <form method="post" action="/staff-allocation/delete/{{ alloc.allocation_id }}" style="display:inline;">
151
+ <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?')">
152
+ <i class="bi bi-trash"></i>
153
+ </button>
154
+ </form>
155
+ </td>
156
+ </tr>
157
+ {% endfor %}
158
+ </tbody>
159
+ </table>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+
165
+ <!-- Add Allocation Modal -->
166
+ <div class="modal fade" id="addAllocationModal" tabindex="-1">
167
+ <div class="modal-dialog modal-xl">
168
+ <div class="modal-content">
169
+ <div class="modal-header bg-info text-white">
170
+ <h5 class="modal-title">Add New Staff Allocation</h5>
171
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
172
+ </div>
173
+ <form method="post" action="/staff-allocation/create">
174
+ <div class="modal-body">
175
+ <div class="row g-3">
176
+ <div class="col-md-6">
177
+ <label class="form-label">Project</label>
178
+ <select class="form-select" name="project_id" required>
179
+ <option value="">Select Project</option>
180
+ {% for project in projects %}
181
+ <option value="{{ project.project_id }}">{{ project.project_id }} - {{ project.project_name }}</option>
182
+ {% endfor %}
183
+ </select>
184
+ </div>
185
+ <div class="col-md-6">
186
+ <label class="form-label">Milestone (Optional)</label>
187
+ <select class="form-select" name="milestone_id">
188
+ <option value="">Select Milestone (Optional)</option>
189
+ {% for milestone in milestones %}
190
+ <option value="{{ milestone.milestone_id }}">{{ milestone.project_id }} - {{ milestone.milestone_name }}</option>
191
+ {% endfor %}
192
+ </select>
193
+ </div>
194
+ <div class="col-md-6">
195
+ <label class="form-label">Employee</label>
196
+ <select class="form-select" name="employee_id" id="employeeSelect" required>
197
+ <option value="">Select Employee</option>
198
+ {% for emp in employees %}
199
+ <option value="{{ emp.employee_id }}"
200
+ data-name="{{ emp.employee_name }}"
201
+ data-role="{{ emp.role }}"
202
+ data-rate="{{ emp.hourly_rate_aed }}">
203
+ {{ emp.employee_id }} - {{ emp.employee_name }} ({{ emp.role }})
204
+ </option>
205
+ {% endfor %}
206
+ </select>
207
+ </div>
208
+ <div class="col-md-6">
209
+ <label class="form-label">Employee Name (Auto-filled)</label>
210
+ <input type="text" class="form-control" name="employee_name" id="employeeName" required readonly>
211
+ </div>
212
+ <div class="col-md-6">
213
+ <label class="form-label">Role (Auto-filled)</label>
214
+ <input type="text" class="form-control" name="role" id="employeeRole" required readonly>
215
+ </div>
216
+ <div class="col-md-6">
217
+ <label class="form-label">Category</label>
218
+ <select class="form-select" name="category">
219
+ <option value="Architecture">Architecture</option>
220
+ <option value="Project Management">Project Management</option>
221
+ <option value="Engineering">Engineering</option>
222
+ </select>
223
+ </div>
224
+ <div class="col-md-6">
225
+ <label class="form-label">Hours Allocated</label>
226
+ <input type="number" step="0.01" class="form-control" name="hours_allocated" required placeholder="80.00">
227
+ </div>
228
+ <div class="col-md-6">
229
+ <label class="form-label">Hours Worked</label>
230
+ <input type="number" step="0.01" class="form-control" name="hours_worked" value="0" placeholder="0.00">
231
+ </div>
232
+ <div class="col-md-6">
233
+ <label class="form-label">Hourly Rate (AED) (Auto-filled)</label>
234
+ <input type="number" step="0.01" class="form-control" name="hourly_rate_aed" id="hourlyRate" required readonly>
235
+ </div>
236
+ <div class="col-md-6">
237
+ <label class="form-label">Skill Match Score (0-100)</label>
238
+ <input type="number" class="form-control" name="skill_match_score" min="0" max="100" placeholder="85">
239
+ </div>
240
+ <div class="col-md-6">
241
+ <label class="form-label">Availability Status</label>
242
+ <select class="form-select" name="availability_status">
243
+ <option value="AVAILABLE">Available</option>
244
+ <option value="BUSY">Busy</option>
245
+ <option value="PARTIALLY_AVAILABLE">Partially Available</option>
246
+ <option value="ON_LEAVE">On Leave</option>
247
+ </select>
248
+ </div>
249
+ <div class="col-md-6">
250
+ <label class="form-label">Performance Rating</label>
251
+ <select class="form-select" name="performance_rating">
252
+ <option value="">Not Rated</option>
253
+ <option value="EXCELLENT">Excellent</option>
254
+ <option value="GOOD">Good</option>
255
+ <option value="AVERAGE">Average</option>
256
+ <option value="NEEDS_IMPROVEMENT">Needs Improvement</option>
257
+ <option value="POOR">Poor</option>
258
+ </select>
259
+ </div>
260
+ <div class="col-md-6">
261
+ <label class="form-label">Start Date</label>
262
+ <input type="date" class="form-control" name="start_date" required>
263
+ </div>
264
+ <div class="col-md-6">
265
+ <label class="form-label">End Date (Optional)</label>
266
+ <input type="date" class="form-control" name="end_date">
267
+ </div>
268
+ <div class="col-md-12">
269
+ <label class="form-label">Notes (Optional)</label>
270
+ <textarea class="form-control" name="notes" rows="2" placeholder="Additional notes about this allocation..."></textarea>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ <div class="modal-footer">
275
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
276
+ <button type="submit" class="btn btn-info">Add Allocation</button>
277
+ </div>
278
+ </form>
279
+ </div>
280
+ </div>
281
+ </div>
282
+
283
+ <script>
284
+ // Auto-fill employee details
285
+ document.getElementById('employeeSelect').addEventListener('change', function() {
286
+ const selectedOption = this.options[this.selectedIndex];
287
+ document.getElementById('employeeName').value = selectedOption.dataset.name || '';
288
+ document.getElementById('employeeRole').value = selectedOption.dataset.role || '';
289
+ document.getElementById('hourlyRate').value = selectedOption.dataset.rate || '';
290
+ });
291
+ </script>
292
+ {% endblock %}
templates/subcontractors.html ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Subcontractors - Architecture PM System{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1><i class="bi bi-tools"></i> Subcontractors & Consultants</h1>
9
+ <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addSubcontractorModal">
10
+ <i class="bi bi-plus-circle"></i> Add Subcontractor
11
+ </button>
12
+ </div>
13
+
14
+ <!-- Summary Cards -->
15
+ <div class="row g-3 mb-4">
16
+ <div class="col-md-4">
17
+ <div class="card bg-primary text-white">
18
+ <div class="card-body">
19
+ <h6 class="card-title">Total Subcontractors</h6>
20
+ <h3>{{ subcontractors|length }}</h3>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <div class="col-md-4">
25
+ <div class="card bg-info text-white">
26
+ <div class="card-body">
27
+ <h6 class="card-title">Total Contract Value</h6>
28
+ <h3>AED {{ "{:,.0f}".format(subcontractors|sum(attribute='contract_amount_aed')|default(0)) }}</h3>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <div class="col-md-4">
33
+ <div class="card bg-success text-white">
34
+ <div class="card-body">
35
+ <h6 class="card-title">Total Invoiced</h6>
36
+ <h3>AED {{ "{:,.0f}".format(subcontractors|sum(attribute='amount_invoiced_aed')|default(0)) }}</h3>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+
42
+ <div class="card">
43
+ <div class="card-body">
44
+ <div class="table-responsive">
45
+ <table class="table table-striped table-hover">
46
+ <thead class="table-dark">
47
+ <tr>
48
+ <th>ID</th>
49
+ <th>Project ID</th>
50
+ <th>Subcontractor Name</th>
51
+ <th>Service Type</th>
52
+ <th>Contract Amount (AED)</th>
53
+ <th>Amount Invoiced (AED)</th>
54
+ <th>Remaining (AED)</th>
55
+ <th>Payment Status</th>
56
+ <th>Work Category</th>
57
+ <th>Actions</th>
58
+ </tr>
59
+ </thead>
60
+ <tbody>
61
+ {% for sub in subcontractors %}
62
+ <tr>
63
+ <td>{{ sub.subcontractor_id }}</td>
64
+ <td><span class="badge bg-secondary">{{ sub.project_id }}</span></td>
65
+ <td><strong>{{ sub.subcontractor_name }}</strong></td>
66
+ <td><span class="badge bg-info">{{ sub.service_type }}</span></td>
67
+ <td>{{ "{:,.2f}".format(sub.contract_amount_aed) }}</td>
68
+ <td>{{ "{:,.2f}".format(sub.amount_invoiced_aed) }}</td>
69
+ <td>
70
+ {% set remaining = sub.contract_amount_aed - sub.amount_invoiced_aed %}
71
+ <strong class="{% if remaining > 0 %}text-warning{% else %}text-success{% endif %}">
72
+ {{ "{:,.2f}".format(remaining) }}
73
+ </strong>
74
+ </td>
75
+ <td>
76
+ {% if sub.payment_status == 'Paid' %}
77
+ <span class="badge bg-success">Paid</span>
78
+ {% elif sub.payment_status == 'Pending' %}
79
+ <span class="badge bg-warning text-dark">Pending</span>
80
+ {% elif sub.payment_status == 'Outstanding' %}
81
+ <span class="badge bg-danger">Outstanding</span>
82
+ {% else %}
83
+ <span class="badge bg-secondary">{{ sub.payment_status }}</span>
84
+ {% endif %}
85
+ </td>
86
+ <td><small>{{ sub.work_category }}</small></td>
87
+ <td>
88
+ <form method="post" action="/subcontractors/delete/{{ sub.subcontractor_id }}" style="display:inline;">
89
+ <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this subcontractor?')">
90
+ <i class="bi bi-trash"></i>
91
+ </button>
92
+ </form>
93
+ </td>
94
+ </tr>
95
+ {% endfor %}
96
+ </tbody>
97
+ </table>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <!-- Add Subcontractor Modal -->
104
+ <div class="modal fade" id="addSubcontractorModal" tabindex="-1">
105
+ <div class="modal-dialog modal-lg">
106
+ <div class="modal-content">
107
+ <div class="modal-header bg-primary text-white">
108
+ <h5 class="modal-title">Add New Subcontractor</h5>
109
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
110
+ </div>
111
+ <form method="post" action="/subcontractors/create">
112
+ <div class="modal-body">
113
+ <div class="row g-3">
114
+ <div class="col-md-6">
115
+ <label class="form-label">Project</label>
116
+ <select class="form-select" name="project_id" required>
117
+ <option value="">Select Project</option>
118
+ {% for project in projects %}
119
+ <option value="{{ project.project_id }}">{{ project.project_id }} - {{ project.project_name }}</option>
120
+ {% endfor %}
121
+ </select>
122
+ </div>
123
+ <div class="col-md-6">
124
+ <label class="form-label">Subcontractor Name</label>
125
+ <input type="text" class="form-control" name="subcontractor_name" required placeholder="Company Name">
126
+ </div>
127
+ <div class="col-md-6">
128
+ <label class="form-label">Service Type</label>
129
+ <select class="form-select" name="service_type" required>
130
+ <option value="">Select Service</option>
131
+ <option value="Structural Engineering">Structural Engineering</option>
132
+ <option value="MEP Design">MEP Design</option>
133
+ <option value="Landscape">Landscape</option>
134
+ <option value="Fire Safety">Fire Safety</option>
135
+ <option value="Acoustics">Acoustics</option>
136
+ <option value="Sustainability">Sustainability</option>
137
+ <option value="Transportation Planning">Transportation Planning</option>
138
+ </select>
139
+ </div>
140
+ <div class="col-md-6">
141
+ <label class="form-label">Work Category</label>
142
+ <select class="form-select" name="work_category" required>
143
+ <option value="Subcontractor">Subcontractor</option>
144
+ <option value="Consultant">Consultant</option>
145
+ <option value="Specialist">Specialist</option>
146
+ </select>
147
+ </div>
148
+ <div class="col-md-6">
149
+ <label class="form-label">Contract Amount (AED)</label>
150
+ <input type="number" step="0.01" class="form-control" name="contract_amount_aed" required placeholder="50000.00">
151
+ </div>
152
+ <div class="col-md-6">
153
+ <label class="form-label">Amount Invoiced (AED)</label>
154
+ <input type="number" step="0.01" class="form-control" name="amount_invoiced_aed" value="0" placeholder="0.00">
155
+ </div>
156
+ <div class="col-md-6">
157
+ <label class="form-label">Payment Status</label>
158
+ <select class="form-select" name="payment_status" required>
159
+ <option value="Pending">Pending</option>
160
+ <option value="Partial">Partial</option>
161
+ <option value="Paid">Paid</option>
162
+ <option value="Outstanding">Outstanding</option>
163
+ <option value="Overdue">Overdue</option>
164
+ </select>
165
+ </div>
166
+ <div class="col-md-6">
167
+ <label class="form-label">Contact Person (Optional)</label>
168
+ <input type="text" class="form-control" name="contact_person" placeholder="John Doe">
169
+ </div>
170
+ <div class="col-md-12">
171
+ <label class="form-label">Contact Email (Optional)</label>
172
+ <input type="email" class="form-control" name="contact_email" placeholder="contact@company.com">
173
+ </div>
174
+ </div>
175
+ </div>
176
+ <div class="modal-footer">
177
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
178
+ <button type="submit" class="btn btn-primary">Add Subcontractor</button>
179
+ </div>
180
+ </form>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ {% endblock %}
templates/timesheets.html ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block title %}Timesheets{% endblock %}
3
+ {% block content %}
4
+ <div class="container-fluid">
5
+ <div class="d-flex justify-content-between mb-4">
6
+ <h1><i class="bi bi-clock-history"></i> Timesheets</h1>
7
+ <button class="btn btn-info" data-bs-toggle="modal" data-bs-target="#addModal">
8
+ <i class="bi bi-plus"></i> Add Timesheet
9
+ </button>
10
+ </div>
11
+ <div class="card">
12
+ <div class="card-body">
13
+ <table class="table table-sm table-striped">
14
+ <thead class="table-dark">
15
+ <tr>
16
+ <th>Record ID</th><th>Date</th><th>Employee</th><th>Project</th>
17
+ <th>Hours</th><th>Billable</th><th>Category</th><th>Actions</th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ {% for ts in timesheets %}
22
+ <tr>
23
+ <td>{{ ts.record_id }}</td>
24
+ <td>{{ ts.date }}</td>
25
+ <td>{{ ts.employee_id }}</td>
26
+ <td>{{ ts.project_id }}</td>
27
+ <td>{{ ts.hours_worked }}</td>
28
+ <td>{{ ts.billable_hours }}</td>
29
+ <td>{{ ts.work_category }}</td>
30
+ <td>
31
+ <form method="post" action="/timesheets/delete/{{ ts.record_id }}" style="display:inline;">
32
+ <button class="btn btn-danger btn-sm" onclick="return confirm('Delete?')">
33
+ <i class="bi bi-trash"></i>
34
+ </button>
35
+ </form>
36
+ </td>
37
+ </tr>
38
+ {% endfor %}
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="modal fade" id="addModal">
46
+ <div class="modal-dialog">
47
+ <div class="modal-content">
48
+ <div class="modal-header">
49
+ <h5>Add Timesheet</h5>
50
+ <button class="btn-close" data-bs-dismiss="modal"></button>
51
+ </div>
52
+ <form method="post" action="/timesheets/create">
53
+ <div class="modal-body">
54
+ <div class="mb-3">
55
+ <label>Record ID</label>
56
+ <input type="text" class="form-control" name="record_id" required>
57
+ </div>
58
+ <div class="mb-3">
59
+ <label>Date</label>
60
+ <input type="date" class="form-control" name="date" required>
61
+ </div>
62
+ <div class="mb-3">
63
+ <label>Employee</label>
64
+ <select class="form-select" name="employee_id" required>
65
+ {% for emp in employees %}
66
+ <option value="{{ emp.employee_id }}">{{ emp.employee_name }}</option>
67
+ {% endfor %}
68
+ </select>
69
+ </div>
70
+ <div class="mb-3">
71
+ <label>Project</label>
72
+ <select class="form-select" name="project_id" required>
73
+ {% for proj in projects %}
74
+ <option value="{{ proj.project_id }}">{{ proj.project_name }}</option>
75
+ {% endfor %}
76
+ </select>
77
+ </div>
78
+ <div class="mb-3">
79
+ <label>Hours Worked</label>
80
+ <input type="number" step="0.01" class="form-control" name="hours_worked" required>
81
+ </div>
82
+ <div class="mb-3">
83
+ <label>Billable Hours</label>
84
+ <input type="number" step="0.01" class="form-control" name="billable_hours" required>
85
+ </div>
86
+ <div class="mb-3">
87
+ <label>Work Category</label>
88
+ <select class="form-select" name="work_category" required>
89
+ <option value="Architecture">Architecture</option>
90
+ <option value="Project Management">Project Management</option>
91
+ <option value="Subcontractor">Subcontractor</option>
92
+ </select>
93
+ </div>
94
+ <div class="mb-3">
95
+ <label>Task Description</label>
96
+ <textarea class="form-control" name="task_description"></textarea>
97
+ </div>
98
+ </div>
99
+ <div class="modal-footer">
100
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
101
+ <button type="submit" class="btn btn-info">Add</button>
102
+ </div>
103
+ </form>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ {% endblock %}