triflix commited on
Commit
fc8aa7f
·
verified ·
1 Parent(s): 7ab175e

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +642 -401
main.py CHANGED
@@ -1,402 +1,643 @@
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)
 
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
+
401
+ # ============================================================================
402
+ # PROJECTS - UPDATE
403
+ # ============================================================================
404
+ @app.get("/projects/edit/{project_id}", response_class=HTMLResponse)
405
+ async def edit_project_form(request: Request, project_id: str, db: Session = Depends(get_db)):
406
+ project = crud.get_project(db, project_id)
407
+ if not project:
408
+ raise HTTPException(status_code=404, detail="Project not found")
409
+ return templates.TemplateResponse("edit_project.html", {
410
+ "request": request,
411
+ "project": project
412
+ })
413
+
414
+ @app.post("/projects/update/{project_id}")
415
+ async def update_project(
416
+ project_id: str,
417
+ project_name: str = Form(...),
418
+ client_name: str = Form(...),
419
+ contract_value_aed: float = Form(...),
420
+ planned_cost_aed: float = Form(...),
421
+ project_start_date: date = Form(...),
422
+ project_end_date: date = Form(...),
423
+ project_type: str = Form(...),
424
+ project_status: str = Form(...),
425
+ current_phase: Optional[str] = Form(None),
426
+ db: Session = Depends(get_db)
427
+ ):
428
+ project = schemas.ProjectCreate(
429
+ project_id=project_id,
430
+ project_name=project_name,
431
+ client_name=client_name,
432
+ contract_value_aed=Decimal(str(contract_value_aed)),
433
+ planned_cost_aed=Decimal(str(planned_cost_aed)),
434
+ project_start_date=project_start_date,
435
+ project_end_date=project_end_date,
436
+ project_type=project_type,
437
+ project_status=project_status,
438
+ current_phase=current_phase
439
+ )
440
+ crud.update_project(db, project_id, project)
441
+ return RedirectResponse(url="/projects", status_code=303)
442
+
443
+
444
+ # ============================================================================
445
+ # EMPLOYEES - UPDATE
446
+ # ============================================================================
447
+ @app.post("/employees/update/{employee_id}")
448
+ async def update_employee(
449
+ employee_id: str,
450
+ employee_name: str = Form(...),
451
+ department: str = Form(...),
452
+ role: str = Form(...),
453
+ hourly_rate_aed: float = Form(...),
454
+ employment_type: str = Form(...),
455
+ start_date: date = Form(...),
456
+ cost_category: str = Form(...),
457
+ email: Optional[str] = Form(None),
458
+ phone: Optional[str] = Form(None),
459
+ db: Session = Depends(get_db)
460
+ ):
461
+ employee = schemas.EmployeeCreate(
462
+ employee_id=employee_id,
463
+ employee_name=employee_name,
464
+ department=department,
465
+ role=role,
466
+ hourly_rate_aed=Decimal(str(hourly_rate_aed)),
467
+ employment_type=employment_type,
468
+ start_date=start_date,
469
+ cost_category=cost_category,
470
+ email=email,
471
+ phone=phone
472
+ )
473
+ crud.update_employee(db, employee_id, employee)
474
+ return RedirectResponse(url="/employees", status_code=303)
475
+
476
+
477
+ # ============================================================================
478
+ # TIMESHEETS - UPDATE
479
+ # ============================================================================
480
+ @app.post("/timesheets/update/{record_id}")
481
+ async def update_timesheet(
482
+ record_id: str,
483
+ date: date = Form(...),
484
+ employee_id: str = Form(...),
485
+ project_id: str = Form(...),
486
+ hours_worked: float = Form(...),
487
+ billable_hours: float = Form(...),
488
+ work_category: str = Form(...),
489
+ task_description: Optional[str] = Form(None),
490
+ db: Session = Depends(get_db)
491
+ ):
492
+ timesheet = schemas.TimesheetCreate(
493
+ record_id=record_id,
494
+ date=date,
495
+ employee_id=employee_id,
496
+ project_id=project_id,
497
+ hours_worked=Decimal(str(hours_worked)),
498
+ billable_hours=Decimal(str(billable_hours)),
499
+ work_category=work_category,
500
+ task_description=task_description
501
+ )
502
+ crud.update_timesheet(db, record_id, timesheet)
503
+ return RedirectResponse(url="/timesheets", status_code=303)
504
+
505
+
506
+ # ============================================================================
507
+ # MILESTONES - UPDATE
508
+ # ============================================================================
509
+ @app.post("/milestones/update/{milestone_id}")
510
+ async def update_milestone(
511
+ milestone_id: int,
512
+ project_id: str = Form(...),
513
+ milestone_name: str = Form(...),
514
+ milestone_order: int = Form(...),
515
+ planned_date: date = Form(...),
516
+ status: str = Form(...),
517
+ completion_percentage: int = Form(0),
518
+ actual_date: Optional[date] = Form(None),
519
+ db: Session = Depends(get_db)
520
+ ):
521
+ milestone = schemas.MilestoneCreate(
522
+ project_id=project_id,
523
+ milestone_name=milestone_name,
524
+ milestone_order=milestone_order,
525
+ planned_date=planned_date,
526
+ actual_date=actual_date,
527
+ status=status,
528
+ completion_percentage=completion_percentage
529
+ )
530
+ crud.update_milestone(db, milestone_id, milestone)
531
+ return RedirectResponse(url="/milestones", status_code=303)
532
+
533
+
534
+ # ============================================================================
535
+ # INVOICES - UPDATE
536
+ # ============================================================================
537
+ @app.post("/invoices/update/{invoice_id}")
538
+ async def update_invoice(
539
+ invoice_id: str,
540
+ project_id: str = Form(...),
541
+ invoice_date: date = Form(...),
542
+ invoice_amount_aed: float = Form(...),
543
+ due_date: date = Form(...),
544
+ payment_status: str = Form(...),
545
+ payment_date: Optional[date] = Form(None),
546
+ milestone_reference: Optional[str] = Form(None),
547
+ db: Session = Depends(get_db)
548
+ ):
549
+ invoice = schemas.InvoiceCreate(
550
+ invoice_id=invoice_id,
551
+ project_id=project_id,
552
+ invoice_date=invoice_date,
553
+ invoice_amount_aed=Decimal(str(invoice_amount_aed)),
554
+ due_date=due_date,
555
+ payment_date=payment_date,
556
+ payment_status=payment_status,
557
+ milestone_reference=milestone_reference
558
+ )
559
+ crud.update_invoice(db, invoice_id, invoice)
560
+ return RedirectResponse(url="/invoices", status_code=303)
561
+
562
+
563
+ # ============================================================================
564
+ # SUBCONTRACTORS - UPDATE
565
+ # ============================================================================
566
+ @app.post("/subcontractors/update/{subcontractor_id}")
567
+ async def update_subcontractor(
568
+ subcontractor_id: int,
569
+ project_id: str = Form(...),
570
+ subcontractor_name: str = Form(...),
571
+ service_type: str = Form(...),
572
+ contract_amount_aed: float = Form(...),
573
+ amount_invoiced_aed: float = Form(0),
574
+ payment_status: str = Form(...),
575
+ work_category: str = Form(...),
576
+ contact_person: Optional[str] = Form(None),
577
+ contact_email: Optional[str] = Form(None),
578
+ db: Session = Depends(get_db)
579
+ ):
580
+ subcontractor = schemas.SubcontractorCreate(
581
+ project_id=project_id,
582
+ subcontractor_name=subcontractor_name,
583
+ service_type=service_type,
584
+ contract_amount_aed=Decimal(str(contract_amount_aed)),
585
+ amount_invoiced_aed=Decimal(str(amount_invoiced_aed)),
586
+ payment_status=payment_status,
587
+ work_category=work_category,
588
+ contact_person=contact_person,
589
+ contact_email=contact_email
590
+ )
591
+ crud.update_subcontractor(db, subcontractor_id, subcontractor)
592
+ return RedirectResponse(url="/subcontractors", status_code=303)
593
+
594
+
595
+ # ============================================================================
596
+ # STAFF ALLOCATION - UPDATE
597
+ # ============================================================================
598
+ @app.post("/staff-allocation/update/{allocation_id}")
599
+ async def update_staff_allocation(
600
+ allocation_id: int,
601
+ project_id: str = Form(...),
602
+ employee_id: str = Form(...),
603
+ employee_name: str = Form(...),
604
+ role: str = Form(...),
605
+ hours_allocated: float = Form(...),
606
+ hours_worked: float = Form(0),
607
+ hourly_rate_aed: float = Form(...),
608
+ start_date: date = Form(...),
609
+ milestone_id: Optional[int] = Form(None),
610
+ milestone_name: Optional[str] = Form(None),
611
+ category: Optional[str] = Form(None),
612
+ skill_match_score: Optional[int] = Form(None),
613
+ availability_status: Optional[str] = Form(None),
614
+ performance_rating: Optional[str] = Form(None),
615
+ end_date: Optional[date] = Form(None),
616
+ notes: Optional[str] = Form(None),
617
+ db: Session = Depends(get_db)
618
+ ):
619
+ allocation = schemas.StaffAllocationCreate(
620
+ project_id=project_id,
621
+ milestone_id=milestone_id,
622
+ milestone_name=milestone_name,
623
+ employee_id=employee_id,
624
+ employee_name=employee_name,
625
+ role=role,
626
+ category=category,
627
+ hours_allocated=Decimal(str(hours_allocated)),
628
+ hours_worked=Decimal(str(hours_worked)),
629
+ hourly_rate_aed=Decimal(str(hourly_rate_aed)),
630
+ skill_match_score=skill_match_score,
631
+ availability_status=availability_status,
632
+ performance_rating=performance_rating,
633
+ start_date=start_date,
634
+ end_date=end_date,
635
+ notes=notes
636
+ )
637
+ crud.update_staff_allocation(db, allocation_id, allocation)
638
+ return RedirectResponse(url="/staff-allocation", status_code=303)
639
+
640
+
641
+ if __name__ == "__main__":
642
+ import uvicorn
643
  uvicorn.run(app, host="0.0.0.0", port=8000)