Spaces:
Sleeping
Sleeping
Fix runtime error: remove await from sync function, update AuditService calls to new API
Browse files- docs/devlogs/server/runtimeerror.txt +54 -395
- src/app/api/v1/customers.py +26 -23
- src/app/api/v1/payroll.py +41 -35
- src/app/api/v1/tasks.py +58 -52
- src/app/api/v1/timesheets.py +65 -56
docs/devlogs/server/runtimeerror.txt
CHANGED
|
@@ -1,398 +1,57 @@
|
|
| 1 |
-
===== Application Startup at 2025-12-
|
| 2 |
|
| 3 |
-
INFO: Started server process [7]
|
| 4 |
-
INFO: Waiting for application startup.
|
| 5 |
-
INFO: 2025-12-10T21:54:05 - app.main: ============================================================
|
| 6 |
-
INFO: 2025-12-10T21:54:05 - app.main: π SwiftOps API v1.0.0 | PRODUCTION
|
| 7 |
-
INFO: 2025-12-10T21:54:05 - app.main: π Dashboard: Enabled
|
| 8 |
-
INFO: 2025-12-10T21:54:05 - app.main: ============================================================
|
| 9 |
-
INFO: 2025-12-10T21:54:05 - app.main: π¦ Database:
|
| 10 |
-
INFO: 2025-12-10T21:54:05 - app.main: β Connected | 47 tables | 6 users
|
| 11 |
-
INFO: 2025-12-10T21:54:05 - app.main: πΎ Cache & Sessions:
|
| 12 |
-
INFO: 2025-12-10T21:54:06 - app.services.otp_service: β
OTP Service initialized with Redis storage
|
| 13 |
-
INFO: 2025-12-10T21:54:07 - app.main: β Redis: Connected
|
| 14 |
-
INFO: 2025-12-10T21:54:07 - app.main: π External Services:
|
| 15 |
-
INFO: 2025-12-10T21:54:07 - app.main: β Cloudinary: Connected
|
| 16 |
-
INFO: 2025-12-10T21:54:07 - app.main: β Resend: Configured
|
| 17 |
-
INFO: 2025-12-10T21:54:07 - app.main: β WASender: Disconnected
|
| 18 |
-
INFO: 2025-12-10T21:54:07 - app.main: β Supabase: Connected | 6 buckets
|
| 19 |
-
INFO: 2025-12-10T21:54:07 - app.main: β° Scheduler:
|
| 20 |
-
INFO: 2025-12-10T21:54:07 - apscheduler.scheduler: Adding job tentatively -- it will be properly scheduled when the scheduler starts
|
| 21 |
-
INFO: 2025-12-10T21:54:07 - apscheduler.scheduler: Added job "Daily Field Agent Reconciliation" to job store "default"
|
| 22 |
-
INFO: 2025-12-10T21:54:07 - apscheduler.scheduler: Scheduler started
|
| 23 |
-
INFO: 2025-12-10T21:54:07 - app.tasks.scheduler: Reconciliation scheduler started (runs at 10 PM Africa/Nairobi)
|
| 24 |
-
INFO: 2025-12-10T21:54:07 - app.main: β Daily reconciliation scheduler started (runs at midnight)
|
| 25 |
-
INFO: 2025-12-10T21:54:07 - app.main: ============================================================
|
| 26 |
-
INFO: 2025-12-10T21:54:07 - app.main: β
Startup complete | Ready to serve requests
|
| 27 |
-
INFO: 2025-12-10T21:54:07 - app.main: ============================================================
|
| 28 |
-
INFO: Application startup complete.
|
| 29 |
-
INFO: Uvicorn running on http://0.0.0.0:7860 (Press CTRL+C to quit)
|
| 30 |
-
INFO: 2025-12-10T22:00:00 - apscheduler.executors.default: Running job "Daily Field Agent Reconciliation (trigger: cron[hour='22', minute='0'], next run at: 2025-12-10 22:00:00 UTC)" (scheduled at 2025-12-10 22:00:00+00:00)
|
| 31 |
-
INFO: 2025-12-10T22:00:00 - app.tasks.scheduler: Starting scheduled validation for 2025-12-10
|
| 32 |
-
INFO: 2025-12-10T22:00:00 - app.tasks.scheduler: Validating 1 projects for 2025-12-10
|
| 33 |
-
INFO: 2025-12-10T22:00:00 - app.services.reconciliation.reconciliation_service: Starting reconciliation: project=0ade6bd1-e492-4e25-b681-59f42058d29a, date=2025-12-10, type=scheduled
|
| 34 |
-
INFO: 2025-12-10T22:00:00 - app.services.reconciliation.reconciliation_service: Aggregated 1 agents in 71ms
|
| 35 |
-
INFO: 2025-12-10T22:00:00 - app.services.reconciliation.anomaly_detector: Detected 0 anomalies across 1 agents
|
| 36 |
-
ERROR: 2025-12-10T22:00:00 - app.services.reconciliation.reconciliation_service: Reconciliation failed: (psycopg2.ProgrammingError) can't adapt type 'dict'
|
| 37 |
-
[SQL:
|
| 38 |
-
UPDATE reconciliation_runs
|
| 39 |
-
SET
|
| 40 |
-
status = 'completed',
|
| 41 |
-
completed_at = NOW(),
|
| 42 |
-
agents_processed = %(agents_processed)s,
|
| 43 |
-
timesheets_created = %(timesheets_created)s,
|
| 44 |
-
timesheets_updated = %(timesheets_updated)s,
|
| 45 |
-
assignments_processed = %(assignments_processed)s,
|
| 46 |
-
expenses_processed = %(expenses_processed)s,
|
| 47 |
-
summary_stats = %(summary_stats)s,
|
| 48 |
-
anomalies_detected = %(anomalies)s,
|
| 49 |
-
execution_time_ms = %(execution_time_ms)s,
|
| 50 |
-
query_time_ms = %(query_time_ms)s,
|
| 51 |
-
updated_at = NOW()
|
| 52 |
-
WHERE id = %(run_id)s
|
| 53 |
-
]
|
| 54 |
-
[parameters: {'agents_processed': 1, 'timesheets_created': 0, 'timesheets_updated': 1, 'assignments_processed': 1, 'expenses_processed': 3, 'summary_stats': {'total_agents': 1, 'total_tickets_assigned': 1, 'total_tickets_completed': 2, 'total_tickets_rejected': 0, 'total_tickets_cancelled': 0, 'total_expen ... (37 characters truncated) ... es': 2370.0, 'total_pending_expenses': 7650.0, 'total_expense_claims': 3, 'avg_tickets_per_agent': 2.0, 'avg_expenses_per_agent': Decimal('10020.00')}, 'anomalies': [], 'execution_time_ms': 288, 'query_time_ms': 71, 'run_id': 'ade18d69-39da-40da-92bf-5ff92beaf9ea'}]
|
| 55 |
-
(Background on this error at: https://sqlalche.me/e/20/f405)
|
| 56 |
Traceback (most recent call last):
|
| 57 |
-
File "/usr/local/
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
The above exception was the direct cause of the following exception:
|
| 64 |
-
|
| 65 |
-
Traceback (most recent call last):
|
| 66 |
-
File "/app/src/app/services/reconciliation/reconciliation_service.py", line 117, in reconcile_project_day
|
| 67 |
-
self._complete_run(
|
| 68 |
-
File "/app/src/app/services/reconciliation/reconciliation_service.py", line 482, in _complete_run
|
| 69 |
-
self.db.execute(query, {
|
| 70 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2308, in execute
|
| 71 |
-
return self._execute_internal(
|
| 72 |
-
^^^^^^^^^^^^^^^^^^^^^^^
|
| 73 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2199, in _execute_internal
|
| 74 |
-
result = conn.execute(
|
| 75 |
-
^^^^^^^^^^^^^
|
| 76 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1416, in execute
|
| 77 |
-
return meth(
|
| 78 |
-
^^^^^
|
| 79 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 516, in _execute_on_connection
|
| 80 |
-
return connection._execute_clauseelement(
|
| 81 |
-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 82 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_clauseelement
|
| 83 |
-
ret = self._execute_context(
|
| 84 |
-
^^^^^^^^^^^^^^^^^^^^^^
|
| 85 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
|
| 86 |
-
return self._exec_single_context(
|
| 87 |
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 88 |
-
File "/usr/local/lib/python3.11/site-packages/
|
| 89 |
-
self.
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
File "/usr/local/lib/python3.11/site-packages/
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
Traceback (most recent call last):
|
| 137 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
|
| 138 |
-
self.dialect.do_execute(
|
| 139 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 922, in do_execute
|
| 140 |
-
cursor.execute(statement, parameters)
|
| 141 |
-
psycopg2.ProgrammingError: can't adapt type 'dict'
|
| 142 |
-
|
| 143 |
-
The above exception was the direct cause of the following exception:
|
| 144 |
-
|
| 145 |
-
Traceback (most recent call last):
|
| 146 |
-
File "/app/src/app/services/reconciliation/reconciliation_service.py", line 117, in reconcile_project_day
|
| 147 |
-
self._complete_run(
|
| 148 |
-
File "/app/src/app/services/reconciliation/reconciliation_service.py", line 482, in _complete_run
|
| 149 |
-
self.db.execute(query, {
|
| 150 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2308, in execute
|
| 151 |
-
return self._execute_internal(
|
| 152 |
-
^^^^^^^^^^^^^^^^^^^^^^^
|
| 153 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2199, in _execute_internal
|
| 154 |
-
result = conn.execute(
|
| 155 |
-
^^^^^^^^^^^^^
|
| 156 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1416, in execute
|
| 157 |
-
return meth(
|
| 158 |
-
^^^^^
|
| 159 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 516, in _execute_on_connection
|
| 160 |
-
return connection._execute_clauseelement(
|
| 161 |
-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 162 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_clauseelement
|
| 163 |
-
ret = self._execute_context(
|
| 164 |
-
^^^^^^^^^^^^^^^^^^^^^^
|
| 165 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
|
| 166 |
-
return self._exec_single_context(
|
| 167 |
-
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 168 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
|
| 169 |
-
self._handle_dbapi_exception(
|
| 170 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2343, in _handle_dbapi_exception
|
| 171 |
-
raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
|
| 172 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
|
| 173 |
-
self.dialect.do_execute(
|
| 174 |
-
File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 922, in do_execute
|
| 175 |
-
cursor.execute(statement, parameters)
|
| 176 |
-
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'dict'
|
| 177 |
-
[SQL:
|
| 178 |
-
UPDATE reconciliation_runs
|
| 179 |
-
SET
|
| 180 |
-
status = 'completed',
|
| 181 |
-
completed_at = NOW(),
|
| 182 |
-
agents_processed = %(agents_processed)s,
|
| 183 |
-
timesheets_created = %(timesheets_created)s,
|
| 184 |
-
timesheets_updated = %(timesheets_updated)s,
|
| 185 |
-
assignments_processed = %(assignments_processed)s,
|
| 186 |
-
expenses_processed = %(expenses_processed)s,
|
| 187 |
-
summary_stats = %(summary_stats)s,
|
| 188 |
-
anomalies_detected = %(anomalies)s,
|
| 189 |
-
execution_time_ms = %(execution_time_ms)s,
|
| 190 |
-
query_time_ms = %(query_time_ms)s,
|
| 191 |
-
updated_at = NOW()
|
| 192 |
-
WHERE id = %(run_id)s
|
| 193 |
-
]
|
| 194 |
-
[parameters: {'agents_processed': 1, 'timesheets_created': 0, 'timesheets_updated': 1, 'assignments_processed': 1, 'expenses_processed': 3, 'summary_stats': {'total_agents': 1, 'total_tickets_assigned': 1, 'total_tickets_completed': 2, 'total_tickets_rejected': 0, 'total_tickets_cancelled': 0, 'total_expen ... (37 characters truncated) ... es': 2370.0, 'total_pending_expenses': 7650.0, 'total_expense_claims': 3, 'avg_tickets_per_agent': 2.0, 'avg_expenses_per_agent': Decimal('10020.00')}, 'anomalies': [], 'execution_time_ms': 288, 'query_time_ms': 71, 'run_id': 'ade18d69-39da-40da-92bf-5ff92beaf9ea'}]
|
| 195 |
-
(Background on this error at: https://sqlalche.me/e/20/f405)
|
| 196 |
-
|
| 197 |
-
The above exception was the direct cause of the following exception:
|
| 198 |
-
|
| 199 |
-
Traceback (most recent call last):
|
| 200 |
-
File "/app/src/app/tasks/scheduler.py", line 132, in validate_all_projects
|
| 201 |
-
run_id = service.reconcile_project_day(
|
| 202 |
-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 203 |
-
File "/app/src/app/services/reconciliation/reconciliation_service.py", line 149, in reconcile_project_day
|
| 204 |
-
raise ReconciliationError(f"Reconciliation failed: {str(e)}") from e
|
| 205 |
-
app.services.reconciliation.reconciliation_service.ReconciliationError: Reconciliation failed: (psycopg2.ProgrammingError) can't adapt type 'dict'
|
| 206 |
-
[SQL:
|
| 207 |
-
UPDATE reconciliation_runs
|
| 208 |
-
SET
|
| 209 |
-
status = 'completed',
|
| 210 |
-
completed_at = NOW(),
|
| 211 |
-
agents_processed = %(agents_processed)s,
|
| 212 |
-
timesheets_created = %(timesheets_created)s,
|
| 213 |
-
timesheets_updated = %(timesheets_updated)s,
|
| 214 |
-
assignments_processed = %(assignments_processed)s,
|
| 215 |
-
expenses_processed = %(expenses_processed)s,
|
| 216 |
-
summary_stats = %(summary_stats)s,
|
| 217 |
-
anomalies_detected = %(anomalies)s,
|
| 218 |
-
execution_time_ms = %(execution_time_ms)s,
|
| 219 |
-
query_time_ms = %(query_time_ms)s,
|
| 220 |
-
updated_at = NOW()
|
| 221 |
-
WHERE id = %(run_id)s
|
| 222 |
-
]
|
| 223 |
-
[parameters: {'agents_processed': 1, 'timesheets_created': 0, 'timesheets_updated': 1, 'assignments_processed': 1, 'expenses_processed': 3, 'summary_stats': {'total_agents': 1, 'total_tickets_assigned': 1, 'total_tickets_completed': 2, 'total_tickets_rejected': 0, 'total_tickets_cancelled': 0, 'total_expen ... (37 characters truncated) ... es': 2370.0, 'total_pending_expenses': 7650.0, 'total_expense_claims': 3, 'avg_tickets_per_agent': 2.0, 'avg_expenses_per_agent': Decimal('10020.00')}, 'anomalies': [], 'execution_time_ms': 288, 'query_time_ms': 71, 'run_id': 'ade18d69-39da-40da-92bf-5ff92beaf9ea'}]
|
| 224 |
-
(Background on this error at: https://sqlalche.me/e/20/f405)
|
| 225 |
-
INFO: 2025-12-10T22:00:00 - app.tasks.scheduler: Validation summary: 0/1 projects succeeded
|
| 226 |
-
WARNING: 2025-12-10T22:00:00 - app.tasks.scheduler: Failed projects: ['Atomio Fttx']
|
| 227 |
-
INFO: 2025-12-10T22:00:00 - app.tasks.scheduler: Scheduled validation completed for 2025-12-10
|
| 228 |
-
INFO: 2025-12-10T22:00:00 - apscheduler.executors.default: Job "Daily Field Agent Reconciliation (trigger: cron[hour='22', minute='0'], next run at: 2025-12-11 22:00:00 UTC)" executed successfully
|
| 229 |
-
INFO: 2025-12-10T22:00:00 - app.tasks.scheduler: Job daily_validation completed successfully
|
| 230 |
-
INFO: 10.16.13.79:30150 - "GET / HTTP/1.1" 200 OK
|
| 231 |
-
INFO: 10.16.13.79:44122 - "GET / HTTP/1.1" 200 OK
|
| 232 |
-
INFO: 10.16.13.79:55137 - "GET /health HTTP/1.1" 200 OK
|
| 233 |
-
INFO: 2025-12-11T06:07:25 - app.core.supabase_auth: Session refreshed successfully
|
| 234 |
-
INFO: 2025-12-11T06:07:29 - app.api.v1.auth: β
Token refreshed successfully for: nadina73@nembors.com
|
| 235 |
-
INFO: 10.16.37.13:60071 - "POST /api/v1/auth/refresh-token HTTP/1.1" 200 OK
|
| 236 |
-
INFO: 2025-12-11T06:07:30 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 237 |
-
INFO: 2025-12-11T06:07:30 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 238 |
-
INFO: 10.16.13.79:32694 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 239 |
-
INFO: 2025-12-11T06:07:30 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 240 |
-
INFO: 2025-12-11T06:07:30 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 241 |
-
INFO: 10.16.37.13:60071 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 242 |
-
INFO: 2025-12-11T06:07:30 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 243 |
-
INFO: 2025-12-11T06:07:30 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 244 |
-
INFO: 10.16.13.79:32694 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 245 |
-
INFO: 2025-12-11T06:07:31 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 246 |
-
INFO: 2025-12-11T06:07:31 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 247 |
-
INFO: 10.16.37.13:60071 - "GET /api/v1/analytics/user/overview HTTP/1.1" 200 OK
|
| 248 |
-
INFO: 2025-12-11T06:07:32 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 249 |
-
INFO: 2025-12-11T06:07:32 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 250 |
-
INFO: 2025-12-11T06:07:32 - app.services.project_service: Listed 1 projects (total: 1) for user c5cf92be-4172-4fe2-af5c-f05d83b3a938
|
| 251 |
-
INFO: 10.16.13.79:32694 - "GET /api/v1/projects?page=1&per_page=100 HTTP/1.1" 200 OK
|
| 252 |
-
INFO: 10.16.13.79:50368 - "GET /health HTTP/1.1" 200 OK
|
| 253 |
-
INFO: 10.16.13.79:63963 - "GET /health HTTP/1.1" 200 OK
|
| 254 |
-
INFO: 10.16.13.79:14550 - "GET /health HTTP/1.1" 200 OK
|
| 255 |
-
INFO: 10.16.37.13:47705 - "GET /health HTTP/1.1" 200 OK
|
| 256 |
-
INFO: 2025-12-11T06:19:31 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 257 |
-
INFO: 2025-12-11T06:19:31 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 258 |
-
INFO: 10.16.13.79:29766 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 259 |
-
INFO: 2025-12-11T06:19:32 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 260 |
-
INFO: 2025-12-11T06:19:32 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 261 |
-
INFO: 10.16.13.79:29766 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 262 |
-
INFO: 2025-12-11T06:19:32 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 263 |
-
INFO: 2025-12-11T06:19:32 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 264 |
-
INFO: 10.16.37.13:47705 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 265 |
-
INFO: 2025-12-11T06:19:32 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 266 |
-
INFO: 2025-12-11T06:19:32 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 267 |
-
INFO: 10.16.37.13:47705 - "GET /api/v1/analytics/user/overview HTTP/1.1" 200 OK
|
| 268 |
-
INFO: 2025-12-11T06:19:33 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 269 |
-
INFO: 2025-12-11T06:19:33 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 270 |
-
INFO: 2025-12-11T06:19:33 - app.services.project_service: Listed 1 projects (total: 1) for user c5cf92be-4172-4fe2-af5c-f05d83b3a938
|
| 271 |
-
INFO: 10.16.37.13:47705 - "GET /api/v1/projects?page=1&per_page=100 HTTP/1.1" 200 OK
|
| 272 |
-
INFO: 10.16.37.13:10471 - "GET /health HTTP/1.1" 200 OK
|
| 273 |
-
INFO: 2025-12-11T06:19:46 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 274 |
-
INFO: 2025-12-11T06:19:46 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 275 |
-
INFO: 2025-12-11T06:19:46 - app.services.audit_service: Audit log created: update on user_preferences by nadina73@nembors.com
|
| 276 |
-
INFO: 2025-12-11T06:19:46 - app.api.v1.auth: Preferences updated for user: nadina73@nembors.com
|
| 277 |
-
INFO: 10.16.13.79:25924 - "PUT /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 278 |
-
INFO: 2025-12-11T06:19:47 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 279 |
-
INFO: 2025-12-11T06:19:47 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 280 |
-
INFO: 10.16.37.13:10471 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 281 |
-
INFO: 2025-12-11T06:19:47 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 282 |
-
INFO: 2025-12-11T06:19:47 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 283 |
-
INFO: 2025-12-11T06:19:47 - app.services.dashboard_service: Dashboard cache MISS for project 0ade6bd1-e492-4e25-b681-59f42058d29a, user c5cf92be-4172-4fe2-af5c-f05d83b3a938 - building fresh data
|
| 284 |
-
INFO: 2025-12-11T06:19:47 - app.services.dashboard_service: Built and cached dashboard for project 0ade6bd1-e492-4e25-b681-59f42058d29a
|
| 285 |
-
INFO: 10.16.13.79:25924 - "GET /api/v1/projects/0ade6bd1-e492-4e25-b681-59f42058d29a/dashboard HTTP/1.1" 200 OK
|
| 286 |
-
INFO: 10.16.13.79:25924 - "GET /api/v1/notifications?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&page_size=50&is_read=false HTTP/1.1" 200 OK
|
| 287 |
-
INFO: 10.16.37.13:10471 - "GET /api/v1/tickets/stats?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a HTTP/1.1" 200 OK
|
| 288 |
-
INFO: 10.16.13.79:39294 - "GET /api/v1/contractor-invoices?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a HTTP/1.1" 200 OK
|
| 289 |
-
INFO: 10.16.37.13:56789 - "GET /api/v1/contractor-invoices/stats?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a HTTP/1.1" 200 OK
|
| 290 |
-
INFO: 10.16.13.79:54124 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 291 |
-
INFO: 10.16.13.79:54124 - "GET /api/v1/tickets/2de41ce7-dff1-4151-9710-87958d18b5c4/detail HTTP/1.1" 200 OK
|
| 292 |
-
INFO: 10.16.37.13:5569 - "GET /health HTTP/1.1" 200 OK
|
| 293 |
-
INFO: 10.16.37.13:15098 - "GET /health HTTP/1.1" 200 OK
|
| 294 |
-
INFO: 10.16.13.79:36865 - "GET /health HTTP/1.1" 200 OK
|
| 295 |
-
INFO: 10.16.13.79:32070 - "GET /health HTTP/1.1" 200 OK
|
| 296 |
-
INFO: 10.16.37.13:16706 - "GET /health HTTP/1.1" 200 OK
|
| 297 |
-
INFO: 2025-12-11T06:23:08 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 298 |
-
INFO: 2025-12-11T06:23:08 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 299 |
-
INFO: 10.16.13.79:5103 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 300 |
-
INFO: 2025-12-11T06:23:09 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 301 |
-
INFO: 2025-12-11T06:23:09 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 302 |
-
INFO: 10.16.13.79:5103 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 303 |
-
INFO: 2025-12-11T06:23:09 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 304 |
-
INFO: 2025-12-11T06:23:09 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 305 |
-
INFO: 10.16.37.13:16706 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 306 |
-
INFO: 10.16.37.13:18227 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 307 |
-
INFO: 10.16.13.79:5103 - "GET /health HTTP/1.1" 200 OK
|
| 308 |
-
INFO: 2025-12-11T06:23:16 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 309 |
-
INFO: 2025-12-11T06:23:16 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 310 |
-
INFO: 10.16.37.13:17230 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 311 |
-
INFO: 10.16.13.79:33301 - "GET /health HTTP/1.1" 200 OK
|
| 312 |
-
INFO: 2025-12-11T06:23:16 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 313 |
-
INFO: 2025-12-11T06:23:16 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 314 |
-
INFO: 10.16.37.13:17230 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 315 |
-
INFO: 2025-12-11T06:23:16 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 316 |
-
INFO: 2025-12-11T06:23:16 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 317 |
-
INFO: 10.16.13.79:33301 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 318 |
-
INFO: 10.16.37.13:17230 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 319 |
-
INFO: 10.16.13.79:33301 - "GET /api/v1/tickets/2de41ce7-dff1-4151-9710-87958d18b5c4/detail HTTP/1.1" 200 OK
|
| 320 |
-
INFO: 10.16.37.13:30569 - "GET /health HTTP/1.1" 200 OK
|
| 321 |
-
INFO: 2025-12-11T06:25:50 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 322 |
-
INFO: 2025-12-11T06:25:50 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 323 |
-
INFO: 10.16.13.79:29711 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 324 |
-
INFO: 10.16.37.13:39487 - "GET /health HTTP/1.1" 200 OK
|
| 325 |
-
INFO: 2025-12-11T06:25:51 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 326 |
-
INFO: 2025-12-11T06:25:51 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 327 |
-
INFO: 10.16.37.13:39487 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 328 |
-
INFO: 2025-12-11T06:25:51 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 329 |
-
INFO: 2025-12-11T06:25:51 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 330 |
-
INFO: 10.16.13.79:29711 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 331 |
-
INFO: 10.16.37.13:53350 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 332 |
-
INFO: 2025-12-11T06:25:59 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 333 |
-
INFO: 2025-12-11T06:25:59 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 334 |
-
INFO: 10.16.37.13:52474 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 335 |
-
INFO: 10.16.13.79:62628 - "GET /health HTTP/1.1" 200 OK
|
| 336 |
-
INFO: 2025-12-11T06:25:59 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 337 |
-
INFO: 2025-12-11T06:25:59 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 338 |
-
INFO: 10.16.37.13:52474 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 339 |
-
INFO: 2025-12-11T06:25:59 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 340 |
-
INFO: 2025-12-11T06:25:59 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 341 |
-
INFO: 10.16.13.79:62628 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 342 |
-
INFO: 10.16.37.13:23782 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 343 |
-
INFO: 10.16.37.13:16935 - "GET /health HTTP/1.1" 200 OK
|
| 344 |
-
INFO: 2025-12-11T06:26:23 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 345 |
-
INFO: 2025-12-11T06:26:23 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 346 |
-
INFO: 10.16.13.79:7127 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 347 |
-
INFO: 2025-12-11T06:26:23 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 348 |
-
INFO: 2025-12-11T06:26:23 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 349 |
-
INFO: 10.16.13.79:7127 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 350 |
-
INFO: 2025-12-11T06:26:23 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 351 |
-
INFO: 2025-12-11T06:26:23 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 352 |
-
INFO: 10.16.37.13:16935 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 353 |
-
INFO: 10.16.13.79:63911 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 354 |
-
INFO: 10.16.37.13:62825 - "GET /health HTTP/1.1" 200 OK
|
| 355 |
-
INFO: 10.16.37.13:62825 - "GET /api/v1/tickets/2de41ce7-dff1-4151-9710-87958d18b5c4/detail HTTP/1.1" 200 OK
|
| 356 |
-
INFO: 10.16.13.79:59155 - "GET /health HTTP/1.1" 200 OK
|
| 357 |
-
INFO: 10.16.37.13:27183 - "GET /health HTTP/1.1" 200 OK
|
| 358 |
-
INFO: 2025-12-11T06:27:46 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 359 |
-
INFO: 2025-12-11T06:27:46 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 360 |
-
INFO: 10.16.13.79:51476 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 361 |
-
INFO: 2025-12-11T06:27:46 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 362 |
-
INFO: 2025-12-11T06:27:46 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 363 |
-
INFO: 10.16.13.79:51476 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 364 |
-
INFO: 2025-12-11T06:27:46 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 365 |
-
INFO: 2025-12-11T06:27:46 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 366 |
-
INFO: 10.16.37.13:27183 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 367 |
-
INFO: 10.16.37.13:64873 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 368 |
-
INFO: 10.16.13.79:33275 - "GET /health HTTP/1.1" 200 OK
|
| 369 |
-
INFO: 10.16.13.79:33275 - "GET /api/v1/tickets/2de41ce7-dff1-4151-9710-87958d18b5c4/detail HTTP/1.1" 200 OK
|
| 370 |
-
INFO: 10.16.37.13:23023 - "GET /health HTTP/1.1" 200 OK
|
| 371 |
-
INFO: 2025-12-11T06:28:31 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 372 |
-
INFO: 2025-12-11T06:28:31 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 373 |
-
INFO: 10.16.13.79:31206 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 374 |
-
INFO: 2025-12-11T06:28:31 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 375 |
-
INFO: 2025-12-11T06:28:31 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 376 |
-
INFO: 10.16.13.79:31206 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 377 |
-
INFO: 2025-12-11T06:28:31 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 378 |
-
INFO: 2025-12-11T06:28:31 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 379 |
-
INFO: 10.16.37.13:23023 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 380 |
-
INFO: 10.16.13.79:49321 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 381 |
-
INFO: 10.16.37.13:23023 - "GET /health HTTP/1.1" 200 OK
|
| 382 |
-
INFO: 10.16.37.13:22536 - "GET /api/v1/tickets/2de41ce7-dff1-4151-9710-87958d18b5c4/detail HTTP/1.1" 200 OK
|
| 383 |
-
INFO: 10.16.37.13:22536 - "GET /health HTTP/1.1" 200 OK
|
| 384 |
-
INFO: 2025-12-11T06:28:37 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 385 |
-
INFO: 2025-12-11T06:28:37 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 386 |
-
INFO: 10.16.13.79:20306 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
|
| 387 |
-
INFO: 2025-12-11T06:28:37 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 388 |
-
INFO: 2025-12-11T06:28:37 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 389 |
-
INFO: 10.16.13.79:20306 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
|
| 390 |
-
INFO: 2025-12-11T06:28:38 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
|
| 391 |
-
INFO: 2025-12-11T06:28:38 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
|
| 392 |
-
INFO: 10.16.37.13:22536 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
|
| 393 |
-
INFO: 10.16.13.79:35880 - "GET /api/v1/contractor-invoices/81c96213-485f-4170-92a0-23c08332923b HTTP/1.1" 200 OK
|
| 394 |
-
INFO: 10.16.13.79:35880 - "GET /api/v1/tickets/2de41ce7-dff1-4151-9710-87958d18b5c4/detail HTTP/1.1" 200 OK
|
| 395 |
-
INFO: 10.16.37.13:4657 - "GET /api/v1/projects/0ade6bd1-e492-4e25-b681-59f42058d29a/regions/4cd27765-5720-4cc0-872e-bf0da3cd1898 HTTP/1.1" 405 Method Not Allowed
|
| 396 |
-
INFO: 10.16.13.79:11727 - "GET /health HTTP/1.1" 200 OK
|
| 397 |
-
INFO: 10.16.37.13:10176 - "GET /api/v1/projects/0ade6bd1-e492-4e25-b681-59f42058d29a/regions/4cd27765-5720-4cc0-872e-bf0da3cd1898 HTTP/1.1" 405 Method Not Allowed
|
| 398 |
-
INFO: 10.16.37.13:19693 - "GET /health HTTP/1.1" 200 OK
|
|
|
|
| 1 |
+
===== Application Startup at 2025-12-12 11:59:51 =====
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
Traceback (most recent call last):
|
| 4 |
+
File "/usr/local/bin/uvicorn", line 8, in <module>
|
| 5 |
+
sys.exit(main())
|
| 6 |
+
^^^^^^
|
| 7 |
+
File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1485, in __call__
|
| 8 |
+
return self.main(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 10 |
+
File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1406, in main
|
| 11 |
+
rv = self.invoke(ctx)
|
| 12 |
+
^^^^^^^^^^^^^^^^
|
| 13 |
+
File "/usr/local/lib/python3.11/site-packages/click/core.py", line 1269, in invoke
|
| 14 |
+
return ctx.invoke(self.callback, **ctx.params)
|
| 15 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 16 |
+
File "/usr/local/lib/python3.11/site-packages/click/core.py", line 824, in invoke
|
| 17 |
+
return callback(*args, **kwargs)
|
| 18 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 19 |
+
File "/usr/local/lib/python3.11/site-packages/uvicorn/main.py", line 416, in main
|
| 20 |
+
run(
|
| 21 |
+
File "/usr/local/lib/python3.11/site-packages/uvicorn/main.py", line 587, in run
|
| 22 |
+
server.run()
|
| 23 |
+
File "/usr/local/lib/python3.11/site-packages/uvicorn/server.py", line 61, in run
|
| 24 |
+
return asyncio.run(self.serve(sockets=sockets))
|
| 25 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 26 |
+
File "/usr/local/lib/python3.11/asyncio/runners.py", line 190, in run
|
| 27 |
+
return runner.run(main)
|
| 28 |
+
^^^^^^^^^^^^^^^^
|
| 29 |
+
File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run
|
| 30 |
+
return self._loop.run_until_complete(task)
|
| 31 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 32 |
+
File "uvloop/loop.pyx", line 1518, in uvloop.loop.Loop.run_until_complete
|
| 33 |
+
File "/usr/local/lib/python3.11/site-packages/uvicorn/server.py", line 68, in serve
|
| 34 |
+
config.load()
|
| 35 |
+
File "/usr/local/lib/python3.11/site-packages/uvicorn/config.py", line 467, in load
|
| 36 |
+
self.loaded_app = import_from_string(self.app)
|
| 37 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 38 |
+
File "/usr/local/lib/python3.11/site-packages/uvicorn/importer.py", line 21, in import_from_string
|
| 39 |
+
module = importlib.import_module(module_str)
|
| 40 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 41 |
+
File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
|
| 42 |
+
return _bootstrap._gcd_import(name[level:], package, level)
|
| 43 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 44 |
+
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
|
| 45 |
+
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
|
| 46 |
+
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
|
| 47 |
+
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
|
| 48 |
+
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
|
| 49 |
+
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
|
| 50 |
+
File "/app/src/app/main.py", line 151, in <module>
|
| 51 |
+
from app.api.v1.router import api_router
|
| 52 |
+
File "/app/src/app/api/v1/router.py", line 5, in <module>
|
| 53 |
+
from app.api.v1 import (
|
| 54 |
+
File "/app/src/app/api/v1/payroll.py", line 599
|
| 55 |
+
await AuditService.log_action(
|
| 56 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 57 |
+
SyntaxError: 'await' outside async function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/app/api/v1/customers.py
CHANGED
|
@@ -97,18 +97,19 @@ async def create_customer(
|
|
| 97 |
customer = CustomerService.create_customer(db, data, current_user)
|
| 98 |
|
| 99 |
# Log audit trail
|
| 100 |
-
|
| 101 |
db=db,
|
| 102 |
-
user_id=current_user.id,
|
| 103 |
action="create_customer",
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
| 107 |
"customer_name": customer.customer_name,
|
| 108 |
"phone": customer.phone_primary,
|
| 109 |
"client_id": str(customer.client_id)
|
| 110 |
-
}
|
| 111 |
-
request=request
|
| 112 |
)
|
| 113 |
|
| 114 |
# Build response with nested data
|
|
@@ -319,17 +320,18 @@ async def update_customer(
|
|
| 319 |
customer = CustomerService.update_customer(db, customer_id, data, current_user)
|
| 320 |
|
| 321 |
# Log audit trail
|
| 322 |
-
|
| 323 |
db=db,
|
| 324 |
-
user_id=current_user.id,
|
| 325 |
action="update_customer",
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
|
|
|
|
|
|
| 333 |
)
|
| 334 |
|
| 335 |
# Build response with nested data
|
|
@@ -381,17 +383,18 @@ async def delete_customer(
|
|
| 381 |
CustomerService.delete_customer(db, customer_id, current_user)
|
| 382 |
|
| 383 |
# Log audit trail
|
| 384 |
-
|
| 385 |
db=db,
|
| 386 |
-
user_id=current_user.id,
|
| 387 |
action="delete_customer",
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
|
|
|
|
|
|
|
|
|
| 391 |
"customer_name": customer.customer_name,
|
| 392 |
"phone": customer.phone_primary
|
| 393 |
-
}
|
| 394 |
-
request=request
|
| 395 |
)
|
| 396 |
|
| 397 |
except HTTPException:
|
|
|
|
| 97 |
customer = CustomerService.create_customer(db, data, current_user)
|
| 98 |
|
| 99 |
# Log audit trail
|
| 100 |
+
AuditService.log_action(
|
| 101 |
db=db,
|
|
|
|
| 102 |
action="create_customer",
|
| 103 |
+
entity_type="customer",
|
| 104 |
+
description=f"Created customer: {customer.customer_name}",
|
| 105 |
+
user=current_user,
|
| 106 |
+
entity_id=str(customer.id),
|
| 107 |
+
request=request,
|
| 108 |
+
additional_metadata={
|
| 109 |
"customer_name": customer.customer_name,
|
| 110 |
"phone": customer.phone_primary,
|
| 111 |
"client_id": str(customer.client_id)
|
| 112 |
+
}
|
|
|
|
| 113 |
)
|
| 114 |
|
| 115 |
# Build response with nested data
|
|
|
|
| 320 |
customer = CustomerService.update_customer(db, customer_id, data, current_user)
|
| 321 |
|
| 322 |
# Log audit trail
|
| 323 |
+
AuditService.log_action(
|
| 324 |
db=db,
|
|
|
|
| 325 |
action="update_customer",
|
| 326 |
+
entity_type="customer",
|
| 327 |
+
description=f"Updated customer {customer.id}",
|
| 328 |
+
user=current_user,
|
| 329 |
+
entity_id=str(customer.id),
|
| 330 |
+
request=request,
|
| 331 |
+
changes={
|
| 332 |
+
"old": old_state,
|
| 333 |
+
"new": data.model_dump(exclude_unset=True)
|
| 334 |
+
}
|
| 335 |
)
|
| 336 |
|
| 337 |
# Build response with nested data
|
|
|
|
| 383 |
CustomerService.delete_customer(db, customer_id, current_user)
|
| 384 |
|
| 385 |
# Log audit trail
|
| 386 |
+
AuditService.log_action(
|
| 387 |
db=db,
|
|
|
|
| 388 |
action="delete_customer",
|
| 389 |
+
entity_type="customer",
|
| 390 |
+
description=f"Deleted customer: {customer.customer_name}",
|
| 391 |
+
user=current_user,
|
| 392 |
+
entity_id=str(customer_id),
|
| 393 |
+
request=request,
|
| 394 |
+
additional_metadata={
|
| 395 |
"customer_name": customer.customer_name,
|
| 396 |
"phone": customer.phone_primary
|
| 397 |
+
}
|
|
|
|
| 398 |
)
|
| 399 |
|
| 400 |
except HTTPException:
|
src/app/api/v1/payroll.py
CHANGED
|
@@ -93,20 +93,21 @@ async def generate_payroll(
|
|
| 93 |
|
| 94 |
if result.payroll:
|
| 95 |
# Log audit trail
|
| 96 |
-
|
| 97 |
db=db,
|
| 98 |
-
user_id=current_user.id,
|
| 99 |
action="generate_payroll",
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
| 103 |
"user_id": str(data.user_id),
|
| 104 |
"project_id": str(data.project_id),
|
| 105 |
"period": f"{data.period_start_date} to {data.period_end_date}",
|
| 106 |
"success": result.success,
|
| 107 |
"skipped_reason": result.skipped_reason
|
| 108 |
-
}
|
| 109 |
-
ip_address=request.client.host if request.client else None
|
| 110 |
)
|
| 111 |
|
| 112 |
return result
|
|
@@ -169,13 +170,15 @@ async def generate_payroll_batch(
|
|
| 169 |
)
|
| 170 |
|
| 171 |
# Log audit trail
|
| 172 |
-
|
| 173 |
db=db,
|
| 174 |
-
user_id=current_user.id,
|
| 175 |
action="generate_batch_payroll",
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
| 179 |
"target_date": str(data.target_date),
|
| 180 |
"project_id": str(data.project_id) if data.project_id else None,
|
| 181 |
"total_processed": result.total_processed,
|
|
@@ -375,17 +378,18 @@ async def recalculate_payroll(
|
|
| 375 |
result = PayrollService.recalculate_payroll(db, payroll_id, current_user)
|
| 376 |
|
| 377 |
# Log audit trail
|
| 378 |
-
|
| 379 |
db=db,
|
| 380 |
-
user_id=current_user.id,
|
| 381 |
action="recalculate_payroll",
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
|
|
|
|
|
|
|
|
|
| 385 |
"payroll_id": str(payroll_id),
|
| 386 |
"success": result.success
|
| 387 |
-
}
|
| 388 |
-
ip_address=request.client.host if request.client else None
|
| 389 |
)
|
| 390 |
|
| 391 |
return result
|
|
@@ -447,19 +451,20 @@ async def mark_payroll_as_paid(
|
|
| 447 |
)
|
| 448 |
|
| 449 |
# Log audit trail
|
| 450 |
-
|
| 451 |
db=db,
|
| 452 |
-
user_id=current_user.id,
|
| 453 |
action="mark_payroll_paid",
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
|
|
|
|
|
|
|
|
|
| 457 |
"payroll_id": str(payroll_id),
|
| 458 |
"payment_method": data.payment_method,
|
| 459 |
"payment_reference": data.payment_reference,
|
| 460 |
"total_amount": str(payroll.total_amount)
|
| 461 |
-
}
|
| 462 |
-
ip_address=request.client.host if request.client else None
|
| 463 |
)
|
| 464 |
|
| 465 |
return payroll
|
|
@@ -480,7 +485,7 @@ async def mark_payroll_as_paid(
|
|
| 480 |
|
| 481 |
@router.post("/export", status_code=status.HTTP_200_OK)
|
| 482 |
@require_permission("manage_payroll")
|
| 483 |
-
def export_payroll_for_payment(
|
| 484 |
background_tasks: BackgroundTasks,
|
| 485 |
from_date: date = Query(..., description="Period start date (inclusive)"),
|
| 486 |
to_date: date = Query(..., description="Period end date (inclusive)"),
|
|
@@ -595,22 +600,23 @@ def export_payroll_for_payment(
|
|
| 595 |
output.seek(0)
|
| 596 |
filename = f"tende_pay_payroll_{from_date.isoformat()}_{to_date.isoformat()}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}.csv"
|
| 597 |
|
| 598 |
-
# Log audit trail
|
| 599 |
-
|
| 600 |
db=db,
|
| 601 |
-
user_id=current_user.id,
|
| 602 |
action="export_payroll_for_payment",
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
|
|
|
|
|
|
|
|
|
| 606 |
"from_date": from_date.isoformat(),
|
| 607 |
"to_date": to_date.isoformat(),
|
| 608 |
"project_id": str(project_id) if project_id else None,
|
| 609 |
"user_id": str(user_id) if user_id else None,
|
| 610 |
"exported_count": len(csv_rows),
|
| 611 |
"warning_count": len(warnings)
|
| 612 |
-
}
|
| 613 |
-
ip_address=request.client.host if request and request.client else None
|
| 614 |
)
|
| 615 |
|
| 616 |
logger.info(
|
|
|
|
| 93 |
|
| 94 |
if result.payroll:
|
| 95 |
# Log audit trail
|
| 96 |
+
AuditService.log_action(
|
| 97 |
db=db,
|
|
|
|
| 98 |
action="generate_payroll",
|
| 99 |
+
entity_type="payroll",
|
| 100 |
+
description=f"Generated payroll for user {data.user_id} project {data.project_id} period {data.period_start_date} to {data.period_end_date}",
|
| 101 |
+
user=current_user,
|
| 102 |
+
entity_id=str(result.payroll.id),
|
| 103 |
+
request=request,
|
| 104 |
+
additional_metadata={
|
| 105 |
"user_id": str(data.user_id),
|
| 106 |
"project_id": str(data.project_id),
|
| 107 |
"period": f"{data.period_start_date} to {data.period_end_date}",
|
| 108 |
"success": result.success,
|
| 109 |
"skipped_reason": result.skipped_reason
|
| 110 |
+
}
|
|
|
|
| 111 |
)
|
| 112 |
|
| 113 |
return result
|
|
|
|
| 170 |
)
|
| 171 |
|
| 172 |
# Log audit trail
|
| 173 |
+
AuditService.log_action(
|
| 174 |
db=db,
|
|
|
|
| 175 |
action="generate_batch_payroll",
|
| 176 |
+
entity_type="payroll",
|
| 177 |
+
description=f"Generated batch payroll for target date {data.target_date} project {data.project_id if data.project_id else 'all'}",
|
| 178 |
+
user=current_user,
|
| 179 |
+
entity_id=None,
|
| 180 |
+
request=request,
|
| 181 |
+
additional_metadata={
|
| 182 |
"target_date": str(data.target_date),
|
| 183 |
"project_id": str(data.project_id) if data.project_id else None,
|
| 184 |
"total_processed": result.total_processed,
|
|
|
|
| 378 |
result = PayrollService.recalculate_payroll(db, payroll_id, current_user)
|
| 379 |
|
| 380 |
# Log audit trail
|
| 381 |
+
AuditService.log_action(
|
| 382 |
db=db,
|
|
|
|
| 383 |
action="recalculate_payroll",
|
| 384 |
+
entity_type="payroll",
|
| 385 |
+
description=f"Recalculated payroll {payroll_id}",
|
| 386 |
+
user=current_user,
|
| 387 |
+
entity_id=str(payroll_id),
|
| 388 |
+
request=request,
|
| 389 |
+
additional_metadata={
|
| 390 |
"payroll_id": str(payroll_id),
|
| 391 |
"success": result.success
|
| 392 |
+
}
|
|
|
|
| 393 |
)
|
| 394 |
|
| 395 |
return result
|
|
|
|
| 451 |
)
|
| 452 |
|
| 453 |
# Log audit trail
|
| 454 |
+
AuditService.log_action(
|
| 455 |
db=db,
|
|
|
|
| 456 |
action="mark_payroll_paid",
|
| 457 |
+
entity_type="payroll",
|
| 458 |
+
description=f"Marked payroll {payroll_id} as paid via {data.payment_method}",
|
| 459 |
+
user=current_user,
|
| 460 |
+
entity_id=str(payroll_id),
|
| 461 |
+
request=request,
|
| 462 |
+
additional_metadata={
|
| 463 |
"payroll_id": str(payroll_id),
|
| 464 |
"payment_method": data.payment_method,
|
| 465 |
"payment_reference": data.payment_reference,
|
| 466 |
"total_amount": str(payroll.total_amount)
|
| 467 |
+
}
|
|
|
|
| 468 |
)
|
| 469 |
|
| 470 |
return payroll
|
|
|
|
| 485 |
|
| 486 |
@router.post("/export", status_code=status.HTTP_200_OK)
|
| 487 |
@require_permission("manage_payroll")
|
| 488 |
+
async def export_payroll_for_payment(
|
| 489 |
background_tasks: BackgroundTasks,
|
| 490 |
from_date: date = Query(..., description="Period start date (inclusive)"),
|
| 491 |
to_date: date = Query(..., description="Period end date (inclusive)"),
|
|
|
|
| 600 |
output.seek(0)
|
| 601 |
filename = f"tende_pay_payroll_{from_date.isoformat()}_{to_date.isoformat()}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}.csv"
|
| 602 |
|
| 603 |
+
# Log audit trail
|
| 604 |
+
AuditService.log_action(
|
| 605 |
db=db,
|
|
|
|
| 606 |
action="export_payroll_for_payment",
|
| 607 |
+
entity_type="payroll",
|
| 608 |
+
description=f"Exported {len(csv_rows)} payroll records for payment (period: {from_date} to {to_date})",
|
| 609 |
+
user=current_user,
|
| 610 |
+
entity_id=None,
|
| 611 |
+
request=request,
|
| 612 |
+
additional_metadata={
|
| 613 |
"from_date": from_date.isoformat(),
|
| 614 |
"to_date": to_date.isoformat(),
|
| 615 |
"project_id": str(project_id) if project_id else None,
|
| 616 |
"user_id": str(user_id) if user_id else None,
|
| 617 |
"exported_count": len(csv_rows),
|
| 618 |
"warning_count": len(warnings)
|
| 619 |
+
}
|
|
|
|
| 620 |
)
|
| 621 |
|
| 622 |
logger.info(
|
src/app/api/v1/tasks.py
CHANGED
|
@@ -131,19 +131,20 @@ async def create_task(
|
|
| 131 |
task = TaskService.create_task(db, data, current_user, background_tasks)
|
| 132 |
|
| 133 |
# Log audit trail
|
| 134 |
-
|
| 135 |
db=db,
|
| 136 |
-
user_id=current_user.id,
|
| 137 |
action="create_task",
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
| 141 |
"task_title": task.task_title,
|
| 142 |
"project_id": str(task.project_id),
|
| 143 |
"task_type": task.task_type,
|
| 144 |
"status": task.status.value
|
| 145 |
-
}
|
| 146 |
-
request=request
|
| 147 |
)
|
| 148 |
|
| 149 |
# Build response with nested data
|
|
@@ -495,17 +496,18 @@ async def update_task(
|
|
| 495 |
task = TaskService.update_task(db, task_id, data, current_user)
|
| 496 |
|
| 497 |
# Log audit trail
|
| 498 |
-
|
| 499 |
db=db,
|
| 500 |
-
user_id=current_user.id,
|
| 501 |
action="update_task",
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
|
|
|
|
|
|
| 509 |
)
|
| 510 |
|
| 511 |
# Build response with nested data
|
|
@@ -562,18 +564,18 @@ async def update_task_status(
|
|
| 562 |
task = TaskService.update_task_status(db, task_id, data, current_user)
|
| 563 |
|
| 564 |
# Log audit trail
|
| 565 |
-
|
| 566 |
db=db,
|
| 567 |
-
user_id=current_user.id,
|
| 568 |
action="update_task_status",
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
|
|
|
| 577 |
)
|
| 578 |
|
| 579 |
# Build response
|
|
@@ -617,14 +619,15 @@ async def start_task(
|
|
| 617 |
task = TaskService.start_task(db, task_id, data, current_user)
|
| 618 |
|
| 619 |
# Log audit trail
|
| 620 |
-
|
| 621 |
db=db,
|
| 622 |
-
user_id=current_user.id,
|
| 623 |
action="start_task",
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
|
|
|
|
|
|
| 628 |
)
|
| 629 |
|
| 630 |
# Build response
|
|
@@ -670,17 +673,18 @@ async def complete_task(
|
|
| 670 |
task = TaskService.complete_task(db, task_id, data, current_user, background_tasks)
|
| 671 |
|
| 672 |
# Log audit trail
|
| 673 |
-
|
| 674 |
db=db,
|
| 675 |
-
user_id=current_user.id,
|
| 676 |
action="complete_task",
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
|
|
|
|
|
|
|
|
|
| 680 |
"completed_at": str(task.completed_at),
|
| 681 |
"notes": data.completion_notes
|
| 682 |
-
}
|
| 683 |
-
request=request
|
| 684 |
)
|
| 685 |
|
| 686 |
# Build response
|
|
@@ -726,14 +730,15 @@ async def cancel_task(
|
|
| 726 |
task = TaskService.cancel_task(db, task_id, data, current_user, background_tasks)
|
| 727 |
|
| 728 |
# Log audit trail
|
| 729 |
-
|
| 730 |
db=db,
|
| 731 |
-
user_id=current_user.id,
|
| 732 |
action="cancel_task",
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
|
|
|
|
|
|
| 737 |
)
|
| 738 |
|
| 739 |
# Build response
|
|
@@ -786,17 +791,18 @@ async def delete_task(
|
|
| 786 |
TaskService.delete_task(db, task_id, current_user)
|
| 787 |
|
| 788 |
# Log audit trail
|
| 789 |
-
|
| 790 |
db=db,
|
| 791 |
-
user_id=current_user.id,
|
| 792 |
action="delete_task",
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
|
|
|
|
|
|
|
|
|
| 796 |
"task_title": task.task_title,
|
| 797 |
"project_id": str(task.project_id)
|
| 798 |
-
}
|
| 799 |
-
request=request
|
| 800 |
)
|
| 801 |
|
| 802 |
except HTTPException:
|
|
|
|
| 131 |
task = TaskService.create_task(db, data, current_user, background_tasks)
|
| 132 |
|
| 133 |
# Log audit trail
|
| 134 |
+
AuditService.log_action(
|
| 135 |
db=db,
|
|
|
|
| 136 |
action="create_task",
|
| 137 |
+
entity_type="task",
|
| 138 |
+
description=f"Created task: {task.task_title}",
|
| 139 |
+
user=current_user,
|
| 140 |
+
entity_id=str(task.id),
|
| 141 |
+
request=request,
|
| 142 |
+
additional_metadata={
|
| 143 |
"task_title": task.task_title,
|
| 144 |
"project_id": str(task.project_id),
|
| 145 |
"task_type": task.task_type,
|
| 146 |
"status": task.status.value
|
| 147 |
+
}
|
|
|
|
| 148 |
)
|
| 149 |
|
| 150 |
# Build response with nested data
|
|
|
|
| 496 |
task = TaskService.update_task(db, task_id, data, current_user)
|
| 497 |
|
| 498 |
# Log audit trail
|
| 499 |
+
AuditService.log_action(
|
| 500 |
db=db,
|
|
|
|
| 501 |
action="update_task",
|
| 502 |
+
entity_type="task",
|
| 503 |
+
description=f"Updated task {task.id}",
|
| 504 |
+
user=current_user,
|
| 505 |
+
entity_id=str(task.id),
|
| 506 |
+
request=request,
|
| 507 |
+
changes={
|
| 508 |
+
"old": old_state,
|
| 509 |
+
"new": data.model_dump(exclude_unset=True)
|
| 510 |
+
}
|
| 511 |
)
|
| 512 |
|
| 513 |
# Build response with nested data
|
|
|
|
| 564 |
task = TaskService.update_task_status(db, task_id, data, current_user)
|
| 565 |
|
| 566 |
# Log audit trail
|
| 567 |
+
AuditService.log_action(
|
| 568 |
db=db,
|
|
|
|
| 569 |
action="update_task_status",
|
| 570 |
+
entity_type="task",
|
| 571 |
+
description=f"Updated task status from {old_status.value} to {task.status.value}",
|
| 572 |
+
user=current_user,
|
| 573 |
+
entity_id=str(task.id),
|
| 574 |
+
request=request,
|
| 575 |
+
changes={
|
| 576 |
+
"old": {"status": old_status.value},
|
| 577 |
+
"new": {"status": task.status.value, "reason": data.reason}
|
| 578 |
+
}
|
| 579 |
)
|
| 580 |
|
| 581 |
# Build response
|
|
|
|
| 619 |
task = TaskService.start_task(db, task_id, data, current_user)
|
| 620 |
|
| 621 |
# Log audit trail
|
| 622 |
+
AuditService.log_action(
|
| 623 |
db=db,
|
|
|
|
| 624 |
action="start_task",
|
| 625 |
+
entity_type="task",
|
| 626 |
+
description=f"Started task {task.id}",
|
| 627 |
+
user=current_user,
|
| 628 |
+
entity_id=str(task.id),
|
| 629 |
+
request=request,
|
| 630 |
+
additional_metadata={"started_at": str(task.started_at)}
|
| 631 |
)
|
| 632 |
|
| 633 |
# Build response
|
|
|
|
| 673 |
task = TaskService.complete_task(db, task_id, data, current_user, background_tasks)
|
| 674 |
|
| 675 |
# Log audit trail
|
| 676 |
+
AuditService.log_action(
|
| 677 |
db=db,
|
|
|
|
| 678 |
action="complete_task",
|
| 679 |
+
entity_type="task",
|
| 680 |
+
description=f"Completed task {task.id}",
|
| 681 |
+
user=current_user,
|
| 682 |
+
entity_id=str(task.id),
|
| 683 |
+
request=request,
|
| 684 |
+
additional_metadata={
|
| 685 |
"completed_at": str(task.completed_at),
|
| 686 |
"notes": data.completion_notes
|
| 687 |
+
}
|
|
|
|
| 688 |
)
|
| 689 |
|
| 690 |
# Build response
|
|
|
|
| 730 |
task = TaskService.cancel_task(db, task_id, data, current_user, background_tasks)
|
| 731 |
|
| 732 |
# Log audit trail
|
| 733 |
+
AuditService.log_action(
|
| 734 |
db=db,
|
|
|
|
| 735 |
action="cancel_task",
|
| 736 |
+
entity_type="task",
|
| 737 |
+
description=f"Cancelled task {task.id}",
|
| 738 |
+
user=current_user,
|
| 739 |
+
entity_id=str(task.id),
|
| 740 |
+
request=request,
|
| 741 |
+
additional_metadata={"reason": data.cancellation_reason}
|
| 742 |
)
|
| 743 |
|
| 744 |
# Build response
|
|
|
|
| 791 |
TaskService.delete_task(db, task_id, current_user)
|
| 792 |
|
| 793 |
# Log audit trail
|
| 794 |
+
AuditService.log_action(
|
| 795 |
db=db,
|
|
|
|
| 796 |
action="delete_task",
|
| 797 |
+
entity_type="task",
|
| 798 |
+
description=f"Deleted task: {task.task_title}",
|
| 799 |
+
user=current_user,
|
| 800 |
+
entity_id=str(task_id),
|
| 801 |
+
request=request,
|
| 802 |
+
additional_metadata={
|
| 803 |
"task_title": task.task_title,
|
| 804 |
"project_id": str(task.project_id)
|
| 805 |
+
}
|
|
|
|
| 806 |
)
|
| 807 |
|
| 808 |
except HTTPException:
|
src/app/api/v1/timesheets.py
CHANGED
|
@@ -69,18 +69,19 @@ async def create_timesheet(
|
|
| 69 |
timesheet = TimesheetService.create_timesheet(db, data, current_user)
|
| 70 |
|
| 71 |
# Log audit trail
|
| 72 |
-
|
| 73 |
db=db,
|
| 74 |
-
user_id=current_user.id,
|
| 75 |
action="create_timesheet",
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
| 79 |
"user_id": str(timesheet.user_id),
|
| 80 |
"work_date": str(timesheet.work_date),
|
| 81 |
"status": timesheet.status.value
|
| 82 |
-
}
|
| 83 |
-
request=request
|
| 84 |
)
|
| 85 |
|
| 86 |
# Build response with nested data
|
|
@@ -186,18 +187,19 @@ async def apply_for_leave(
|
|
| 186 |
timesheet = TimesheetService.create_timesheet(db, timesheet_data, current_user)
|
| 187 |
|
| 188 |
# Log audit trail
|
| 189 |
-
|
| 190 |
db=db,
|
| 191 |
-
user_id=current_user.id,
|
| 192 |
action="apply_for_leave",
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
| 196 |
"work_date": str(data.work_date),
|
| 197 |
-
"status": data.status,
|
| 198 |
"leave_reason": data.leave_reason
|
| 199 |
-
}
|
| 200 |
-
request=request
|
| 201 |
)
|
| 202 |
|
| 203 |
# Build response
|
|
@@ -253,19 +255,20 @@ async def bulk_create_timesheets(
|
|
| 253 |
timesheets = TimesheetService.bulk_create_timesheets(db, data, current_user)
|
| 254 |
|
| 255 |
# Log audit trail
|
| 256 |
-
|
| 257 |
db=db,
|
| 258 |
-
user_id=current_user.id,
|
| 259 |
action="bulk_create_timesheets",
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
|
|
|
|
|
|
|
|
|
| 263 |
"project_id": str(data.project_id),
|
| 264 |
"work_date": str(data.work_date),
|
| 265 |
"user_count": len(timesheets),
|
| 266 |
"status": data.status.value
|
| 267 |
-
}
|
| 268 |
-
request=request
|
| 269 |
)
|
| 270 |
|
| 271 |
# Build response array
|
|
@@ -750,17 +753,18 @@ async def update_timesheet(
|
|
| 750 |
timesheet = TimesheetService.update_timesheet(db, timesheet_id, data, current_user)
|
| 751 |
|
| 752 |
# Log audit trail
|
| 753 |
-
|
| 754 |
db=db,
|
| 755 |
-
user_id=current_user.id,
|
| 756 |
action="update_timesheet",
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
|
|
|
|
|
|
| 764 |
)
|
| 765 |
|
| 766 |
# Build response with nested data
|
|
@@ -809,18 +813,19 @@ async def approve_leave(
|
|
| 809 |
timesheet = TimesheetService.approve_leave(db, timesheet_id, data, current_user)
|
| 810 |
|
| 811 |
# Log audit trail
|
| 812 |
-
|
| 813 |
db=db,
|
| 814 |
-
user_id=current_user.id,
|
| 815 |
action="approve_leave",
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
|
|
|
|
|
|
|
|
|
| 819 |
"user_id": str(timesheet.user_id),
|
| 820 |
"work_date": str(timesheet.work_date),
|
| 821 |
"status": timesheet.status.value
|
| 822 |
-
}
|
| 823 |
-
request=request
|
| 824 |
)
|
| 825 |
|
| 826 |
# Build response with nested data
|
|
@@ -871,18 +876,19 @@ async def delete_timesheet(
|
|
| 871 |
TimesheetService.delete_timesheet(db, timesheet_id, current_user)
|
| 872 |
|
| 873 |
# Log audit trail
|
| 874 |
-
|
| 875 |
db=db,
|
| 876 |
-
user_id=current_user.id,
|
| 877 |
action="delete_timesheet",
|
| 878 |
-
|
| 879 |
-
|
| 880 |
-
|
|
|
|
|
|
|
|
|
|
| 881 |
"user_id": str(timesheet.user_id),
|
| 882 |
"work_date": str(timesheet.work_date),
|
| 883 |
"status": timesheet.status.value
|
| 884 |
-
}
|
| 885 |
-
request=request
|
| 886 |
)
|
| 887 |
|
| 888 |
except HTTPException:
|
|
@@ -967,14 +973,15 @@ async def generate_timesheets(
|
|
| 967 |
'method': 'single_user'
|
| 968 |
}
|
| 969 |
|
| 970 |
-
|
| 971 |
db=db,
|
| 972 |
-
user_id=current_user.id,
|
| 973 |
action="generate_timesheet_single",
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
| 977 |
-
|
|
|
|
|
|
|
| 978 |
)
|
| 979 |
|
| 980 |
return stats
|
|
@@ -988,13 +995,15 @@ async def generate_timesheets(
|
|
| 988 |
regenerate=regenerate
|
| 989 |
)
|
| 990 |
|
| 991 |
-
|
| 992 |
db=db,
|
| 993 |
-
user_id=current_user.id,
|
| 994 |
action="generate_timesheets_bulk",
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
|
|
|
|
|
|
|
|
|
| 998 |
request=request
|
| 999 |
)
|
| 1000 |
|
|
|
|
| 69 |
timesheet = TimesheetService.create_timesheet(db, data, current_user)
|
| 70 |
|
| 71 |
# Log audit trail
|
| 72 |
+
AuditService.log_action(
|
| 73 |
db=db,
|
|
|
|
| 74 |
action="create_timesheet",
|
| 75 |
+
entity_type="timesheet",
|
| 76 |
+
description=f"Created timesheet for user {timesheet.user_id} on {timesheet.work_date}",
|
| 77 |
+
user=current_user,
|
| 78 |
+
entity_id=str(timesheet.id),
|
| 79 |
+
request=request,
|
| 80 |
+
additional_metadata={
|
| 81 |
"user_id": str(timesheet.user_id),
|
| 82 |
"work_date": str(timesheet.work_date),
|
| 83 |
"status": timesheet.status.value
|
| 84 |
+
}
|
|
|
|
| 85 |
)
|
| 86 |
|
| 87 |
# Build response with nested data
|
|
|
|
| 187 |
timesheet = TimesheetService.create_timesheet(db, timesheet_data, current_user)
|
| 188 |
|
| 189 |
# Log audit trail
|
| 190 |
+
AuditService.log_action(
|
| 191 |
db=db,
|
|
|
|
| 192 |
action="apply_for_leave",
|
| 193 |
+
entity_type="timesheet",
|
| 194 |
+
description=f"Applied for leave on {data.work_date}: {data.status}",
|
| 195 |
+
user=current_user,
|
| 196 |
+
entity_id=str(timesheet.id),
|
| 197 |
+
request=request,
|
| 198 |
+
additional_metadata={
|
| 199 |
"work_date": str(data.work_date),
|
| 200 |
+
"status": data.status,
|
| 201 |
"leave_reason": data.leave_reason
|
| 202 |
+
}
|
|
|
|
| 203 |
)
|
| 204 |
|
| 205 |
# Build response
|
|
|
|
| 255 |
timesheets = TimesheetService.bulk_create_timesheets(db, data, current_user)
|
| 256 |
|
| 257 |
# Log audit trail
|
| 258 |
+
AuditService.log_action(
|
| 259 |
db=db,
|
|
|
|
| 260 |
action="bulk_create_timesheets",
|
| 261 |
+
entity_type="timesheet",
|
| 262 |
+
description=f"Bulk created {len(timesheets)} timesheets for project {data.project_id} on {data.work_date}",
|
| 263 |
+
user=current_user,
|
| 264 |
+
entity_id=None,
|
| 265 |
+
request=request,
|
| 266 |
+
additional_metadata={
|
| 267 |
"project_id": str(data.project_id),
|
| 268 |
"work_date": str(data.work_date),
|
| 269 |
"user_count": len(timesheets),
|
| 270 |
"status": data.status.value
|
| 271 |
+
}
|
|
|
|
| 272 |
)
|
| 273 |
|
| 274 |
# Build response array
|
|
|
|
| 753 |
timesheet = TimesheetService.update_timesheet(db, timesheet_id, data, current_user)
|
| 754 |
|
| 755 |
# Log audit trail
|
| 756 |
+
AuditService.log_action(
|
| 757 |
db=db,
|
|
|
|
| 758 |
action="update_timesheet",
|
| 759 |
+
entity_type="timesheet",
|
| 760 |
+
description=f"Updated timesheet {timesheet.id}",
|
| 761 |
+
user=current_user,
|
| 762 |
+
entity_id=str(timesheet.id),
|
| 763 |
+
request=request,
|
| 764 |
+
changes={
|
| 765 |
+
"old": old_state,
|
| 766 |
+
"new": data.model_dump(exclude_unset=True)
|
| 767 |
+
}
|
| 768 |
)
|
| 769 |
|
| 770 |
# Build response with nested data
|
|
|
|
| 813 |
timesheet = TimesheetService.approve_leave(db, timesheet_id, data, current_user)
|
| 814 |
|
| 815 |
# Log audit trail
|
| 816 |
+
AuditService.log_action(
|
| 817 |
db=db,
|
|
|
|
| 818 |
action="approve_leave",
|
| 819 |
+
entity_type="timesheet",
|
| 820 |
+
description=f"Approved leave for user {timesheet.user_id} on {timesheet.work_date}",
|
| 821 |
+
user=current_user,
|
| 822 |
+
entity_id=str(timesheet.id),
|
| 823 |
+
request=request,
|
| 824 |
+
additional_metadata={
|
| 825 |
"user_id": str(timesheet.user_id),
|
| 826 |
"work_date": str(timesheet.work_date),
|
| 827 |
"status": timesheet.status.value
|
| 828 |
+
}
|
|
|
|
| 829 |
)
|
| 830 |
|
| 831 |
# Build response with nested data
|
|
|
|
| 876 |
TimesheetService.delete_timesheet(db, timesheet_id, current_user)
|
| 877 |
|
| 878 |
# Log audit trail
|
| 879 |
+
AuditService.log_action(
|
| 880 |
db=db,
|
|
|
|
| 881 |
action="delete_timesheet",
|
| 882 |
+
entity_type="timesheet",
|
| 883 |
+
description=f"Deleted timesheet for user {timesheet.user_id} on {timesheet.work_date}",
|
| 884 |
+
user=current_user,
|
| 885 |
+
entity_id=str(timesheet_id),
|
| 886 |
+
request=request,
|
| 887 |
+
additional_metadata={
|
| 888 |
"user_id": str(timesheet.user_id),
|
| 889 |
"work_date": str(timesheet.work_date),
|
| 890 |
"status": timesheet.status.value
|
| 891 |
+
}
|
|
|
|
| 892 |
)
|
| 893 |
|
| 894 |
except HTTPException:
|
|
|
|
| 973 |
'method': 'single_user'
|
| 974 |
}
|
| 975 |
|
| 976 |
+
AuditService.log_action(
|
| 977 |
db=db,
|
|
|
|
| 978 |
action="generate_timesheet_single",
|
| 979 |
+
entity_type="timesheet",
|
| 980 |
+
description=f"Generated timesheet for user {user_id} on {target_date}",
|
| 981 |
+
user=current_user,
|
| 982 |
+
entity_id=str(timesheet.id) if timesheet else None,
|
| 983 |
+
request=request,
|
| 984 |
+
additional_metadata=stats
|
| 985 |
)
|
| 986 |
|
| 987 |
return stats
|
|
|
|
| 995 |
regenerate=regenerate
|
| 996 |
)
|
| 997 |
|
| 998 |
+
AuditService.log_action(
|
| 999 |
db=db,
|
|
|
|
| 1000 |
action="generate_timesheets_bulk",
|
| 1001 |
+
entity_type="timesheet",
|
| 1002 |
+
description=f"Generated timesheets in bulk for {target_date}",
|
| 1003 |
+
user=current_user,
|
| 1004 |
+
entity_id=None,
|
| 1005 |
+
request=request,
|
| 1006 |
+
additional_metadata=stats
|
| 1007 |
request=request
|
| 1008 |
)
|
| 1009 |
|