kamau1 commited on
Commit
51e0775
Β·
1 Parent(s): 4756596

Fix Tende Pay export notification: handle AMOUNT key and improve user guidance

Browse files
docs/devlogs/server/runtimeerror.txt CHANGED
@@ -1,66 +1,56 @@
1
- ===== Application Startup at 2025-12-10 13:22:15 =====
2
 
3
- INFO: Started server process [7]
4
  INFO: Waiting for application startup.
5
- INFO: 2025-12-10T13:22:32 - app.main: ============================================================
6
- INFO: 2025-12-10T13:22:32 - app.main: πŸš€ SwiftOps API v1.0.0 | PRODUCTION
7
- INFO: 2025-12-10T13:22:32 - app.main: πŸ“Š Dashboard: Enabled
8
- INFO: 2025-12-10T13:22:32 - app.main: ============================================================
9
- INFO: 2025-12-10T13:22:32 - app.main: πŸ“¦ Database:
10
- INFO: 2025-12-10T13:22:32 - app.main: βœ“ Connected | 47 tables | 6 users
11
- INFO: 2025-12-10T13:22:32 - app.main: πŸ’Ύ Cache & Sessions:
12
- INFO: 2025-12-10T13:22:33 - app.services.otp_service: βœ… OTP Service initialized with Redis storage
13
- INFO: 2025-12-10T13:22:33 - app.main: βœ“ Redis: Connected
14
- INFO: 2025-12-10T13:22:33 - app.main: πŸ”Œ External Services:
15
- INFO: 2025-12-10T13:22:34 - app.main: βœ“ Cloudinary: Connected
16
- INFO: 2025-12-10T13:22:34 - app.main: βœ“ Resend: Configured
17
- INFO: 2025-12-10T13:22:34 - app.main: β—‹ WASender: Disconnected
18
- INFO: 2025-12-10T13:22:34 - app.main: βœ“ Supabase: Connected | 6 buckets
19
- INFO: 2025-12-10T13:22:34 - app.main: ⏰ Scheduler:
20
- INFO: 2025-12-10T13:22:34 - apscheduler.scheduler: Adding job tentatively -- it will be properly scheduled when the scheduler starts
21
- INFO: 2025-12-10T13:22:34 - apscheduler.scheduler: Added job "Daily Field Agent Reconciliation" to job store "default"
22
- INFO: 2025-12-10T13:22:34 - apscheduler.scheduler: Scheduler started
23
- INFO: 2025-12-10T13:22:34 - app.tasks.scheduler: Reconciliation scheduler started (runs at 10 PM Africa/Nairobi)
24
- INFO: 2025-12-10T13:22:34 - app.main: βœ“ Daily reconciliation scheduler started (runs at midnight)
25
- INFO: 2025-12-10T13:22:34 - app.main: ============================================================
26
- INFO: 2025-12-10T13:22:34 - app.main: βœ… Startup complete | Ready to serve requests
27
- INFO: 2025-12-10T13:22:34 - 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: 10.16.13.79:2541 - "GET /health HTTP/1.1" 200 OK
31
- INFO: 10.16.37.13:14743 - "GET /api/v1/ticket-expenses/stats/summary?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&page=1&page_size=20&status=pending HTTP/1.1" 200 OK
32
- INFO: 10.16.13.79:19111 - "GET /api/v1/ticket-expenses?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&page=1&page_size=20&is_approved=false HTTP/1.1" 200 OK
33
- INFO: 10.16.13.79:1467 - "GET /api/v1/ticket-expenses?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&is_paid=false&page=1&page_size=100&is_approved=true HTTP/1.1" 200 OK
34
- INFO: 2025-12-10T13:23:41 - app.api.v1.ticket_expenses: Bulk export requested by user c5cf92be-4172-4fe2-af5c-f05d83b3a938 for 2 expenses
35
- WARNING: 2025-12-10T13:23:41 - app.services.tende_pay_formatter: Invalid phone number format: +2547994597823 -> 2547994597823
36
- INFO: 2025-12-10T13:23:41 - app.api.v1.ticket_expenses: Bulk exported 2 expenses by user c5cf92be-4172-4fe2-af5c-f05d83b3a938. Generated 0 payment rows. Reference: CSV_EXPORT_20251210_132341_c5cf92be-4172-4fe2-af5c-f05d83b3a938
37
- INFO: 10.16.37.13:38179 - "POST /api/v1/ticket-expenses/bulk-export?expense_ids=35db9201-3c05-4853-be91-5eb6f30782d1&expense_ids=d73cb843-e4fb-4200-aa3e-243887714422 HTTP/1.1" 200 OK
38
- INFO: 10.16.13.79:62181 - "GET /health HTTP/1.1" 200 OK
39
- INFO: 10.16.37.13:6569 - "GET /health HTTP/1.1" 200 OK
40
- INFO: 10.16.37.13:21572 - "GET /health HTTP/1.1" 200 OK
41
- INFO: 10.16.13.79:18784 - "GET /api/v1/notifications/stats HTTP/1.1" 200 OK
42
- INFO: 10.16.13.79:29931 - "GET /api/v1/notifications/stats HTTP/1.1" 200 OK
43
- INFO: 10.16.37.13:8632 - "GET /api/v1/notifications?page=1&page_size=50 HTTP/1.1" 200 OK
44
- INFO: 2025-12-10T13:26:00 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
45
- INFO: 2025-12-10T13:26:00 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
46
- INFO: 10.16.13.79:20577 - "GET /api/v1/profile/me HTTP/1.1" 200 OK
47
- INFO: 2025-12-10T13:26:01 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
48
- INFO: 2025-12-10T13:26:01 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
49
- INFO: 10.16.13.79:20577 - "GET /api/v1/financial-accounts/me HTTP/1.1" 200 OK
50
- INFO: 2025-12-10T13:26:01 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
51
- INFO: 2025-12-10T13:26:01 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
52
- INFO: 10.16.37.13:65405 - "GET /api/v1/asset-assignments/me HTTP/1.1" 200 OK
53
- INFO: 2025-12-10T13:26:01 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
54
- INFO: 2025-12-10T13:26:01 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
55
- INFO: 10.16.37.13:4263 - "GET /api/v1/documents/users/me HTTP/1.1" 200 OK
56
- INFO: 2025-12-10T13:26:26 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
57
- INFO: 2025-12-10T13:26:26 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
58
- INFO: 2025-12-10T13:26:26 - app.services.audit_service: Audit log created: update on financial_account by viyisa8151@feralrex.com
59
- INFO: 10.16.13.79:29992 - "PUT /api/v1/financial-accounts/me/c6d350c7-1b90-4e48-b7e5-ce80d1bac22a HTTP/1.1" 200 OK
60
- INFO: 2025-12-10T13:26:26 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
61
- INFO: 2025-12-10T13:26:26 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
62
- INFO: 10.16.13.79:29992 - "GET /api/v1/financial-accounts/me HTTP/1.1" 200 OK
63
- INFO: 10.16.13.79:13377 - "GET /health HTTP/1.1" 200 OK
64
- INFO: 2025-12-10T13:26:37 - app.api.v1.ticket_expenses: Bulk export requested by user c5cf92be-4172-4fe2-af5c-f05d83b3a938 for 2 expenses
65
- INFO: 10.16.13.79:31385 - "POST /api/v1/ticket-expenses/bulk-export?expense_ids=35db9201-3c05-4853-be91-5eb6f30782d1&expense_ids=d73cb843-e4fb-4200-aa3e-243887714422 HTTP/1.1" 400 Bad Request
66
- INFO: 10.16.13.79:28716 - "GET /health HTTP/1.1" 200 OK
 
1
+ ===== Application Startup at 2025-12-10 13:47:47 =====
2
 
3
+ INFO: Started server process [6]
4
  INFO: Waiting for application startup.
5
+ INFO: 2025-12-10T13:48:14 - app.main: ============================================================
6
+ INFO: 2025-12-10T13:48:14 - app.main: πŸš€ SwiftOps API v1.0.0 | PRODUCTION
7
+ INFO: 2025-12-10T13:48:14 - app.main: πŸ“Š Dashboard: Enabled
8
+ INFO: 2025-12-10T13:48:14 - app.main: ============================================================
9
+ INFO: 2025-12-10T13:48:14 - app.main: πŸ“¦ Database:
10
+ INFO: 2025-12-10T13:48:14 - app.main: βœ“ Connected | 47 tables | 6 users
11
+ INFO: 2025-12-10T13:48:14 - app.main: πŸ’Ύ Cache & Sessions:
12
+ INFO: 2025-12-10T13:48:15 - app.services.otp_service: βœ… OTP Service initialized with Redis storage
13
+ INFO: 2025-12-10T13:48:16 - app.main: βœ“ Redis: Connected
14
+ INFO: 2025-12-10T13:48:16 - app.main: πŸ”Œ External Services:
15
+ INFO: 2025-12-10T13:48:16 - app.main: βœ“ Cloudinary: Connected
16
+ INFO: 2025-12-10T13:48:16 - app.main: βœ“ Resend: Configured
17
+ INFO: 2025-12-10T13:48:16 - app.main: β—‹ WASender: Disconnected
18
+ INFO: 2025-12-10T13:48:16 - app.main: βœ“ Supabase: Connected | 6 buckets
19
+ INFO: 2025-12-10T13:48:16 - app.main: ⏰ Scheduler:
20
+ INFO: 2025-12-10T13:48:16 - apscheduler.scheduler: Adding job tentatively -- it will be properly scheduled when the scheduler starts
21
+ INFO: 2025-12-10T13:48:16 - apscheduler.scheduler: Added job "Daily Field Agent Reconciliation" to job store "default"
22
+ INFO: 2025-12-10T13:48:16 - apscheduler.scheduler: Scheduler started
23
+ INFO: 2025-12-10T13:48:16 - app.tasks.scheduler: Reconciliation scheduler started (runs at 10 PM Africa/Nairobi)
24
+ INFO: 2025-12-10T13:48:16 - app.main: βœ“ Daily reconciliation scheduler started (runs at midnight)
25
+ INFO: 2025-12-10T13:48:16 - app.main: ============================================================
26
+ INFO: 2025-12-10T13:48:16 - app.main: βœ… Startup complete | Ready to serve requests
27
+ INFO: 2025-12-10T13:48:16 - 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: 10.16.37.13:48475 - "GET /?logs=container HTTP/1.1" 200 OK
31
+ INFO: 10.16.37.13:48781 - "GET /health HTTP/1.1" 200 OK
32
+ INFO: 10.16.37.13:63708 - "GET /health HTTP/1.1" 200 OK
33
+ INFO: 2025-12-10T13:48:36 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
34
+ INFO: 2025-12-10T13:48:36 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
35
+ INFO: 10.16.13.79:2766 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
36
+ INFO: 2025-12-10T13:48:37 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
37
+ INFO: 2025-12-10T13:48:37 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
38
+ INFO: 10.16.13.79:2766 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
39
+ INFO: 2025-12-10T13:48:37 - app.api.deps: Checking active user: c5cf92be-4172-4fe2-af5c-f05d83b3a938, is_active: True, type: <class 'bool'>
40
+ INFO: 2025-12-10T13:48:37 - app.api.deps: User c5cf92be-4172-4fe2-af5c-f05d83b3a938 is active - proceeding
41
+ INFO: 10.16.37.13:63708 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
42
+ INFO: 10.16.13.79:2766 - "GET /api/v1/ticket-expenses?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&is_paid=false&page=1&page_size=100&is_approved=true HTTP/1.1" 200 OK
43
+ INFO: 10.16.37.13:38981 - "GET /health HTTP/1.1" 200 OK
44
+ INFO: 2025-12-10T13:48:43 - app.api.v1.ticket_expenses: Bulk export requested by user c5cf92be-4172-4fe2-af5c-f05d83b3a938 for 2 expenses
45
+ INFO: 2025-12-10T13:48:43 - app.api.v1.ticket_expenses: Bulk exported 2 of 2 expenses by user c5cf92be-4172-4fe2-af5c-f05d83b3a938. Generated 1 payment rows. Skipped: 0. Reference: CSV_EXPORT_20251210_134843_c5cf92be-4172-4fe2-af5c-f05d83b3a938
46
+ ERROR: 2025-12-10T13:48:43 - app.api.v1.ticket_expenses: Failed to queue export completion notification: 'total_amount'
47
+ INFO: 10.16.37.13:38981 - "POST /api/v1/ticket-expenses/bulk-export?expense_ids=b8a0bac2-69c0-4218-a565-e1e2b6ce0d4d&expense_ids=7b697e88-c4c1-4366-b05e-c0689a90451c HTTP/1.1" 200 OK
48
+ INFO: 10.16.37.13:36720 - "GET /api/v1/ticket-expenses/stats/summary?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&page=1&page_size=20&status=pending HTTP/1.1" 200 OK
49
+ INFO: 10.16.13.79:45984 - "GET /api/v1/ticket-expenses?project_id=0ade6bd1-e492-4e25-b681-59f42058d29a&page=1&page_size=20&is_approved=false HTTP/1.1" 200 OK
50
+ INFO: 10.16.13.79:17081 - "GET /health HTTP/1.1" 200 OK
51
+ INFO: 10.16.13.79:5119 - "GET /api/v1/notifications/stats HTTP/1.1" 200 OK
52
+ INFO: 10.16.37.13:13915 - "GET /api/v1/notifications?page=1&page_size=50&project_id=0ade6bd1-e492-4e25-b681-59f42058d29a HTTP/1.1" 200 OK
53
+ INFO: 2025-12-10T13:49:23 - app.services.notification_service: Marked notification c7196978-05d9-42ba-b07d-130048cec958 as read for user c5cf92be-4172-4fe2-af5c-f05d83b3a938
54
+ INFO: 10.16.13.79:5119 - "PUT /api/v1/notifications/c7196978-05d9-42ba-b07d-130048cec958/read HTTP/1.1" 200 OK
55
+ INFO: 10.16.13.79:5119 - "GET /api/v1/notifications/stats HTTP/1.1" 200 OK
56
+ INFO: 10.16.37.13:13915 - "GET /api/v1/notifications?page=1&page_size=50&project_id=0ade6bd1-e492-4e25-b681-59f42058d29a HTTP/1.1" 200 OK
 
 
 
 
 
 
 
 
 
 
src/app/api/v1/ticket_expenses.py CHANGED
@@ -1328,17 +1328,22 @@ def bulk_export_expenses(
1328
  ticket = db.query(Ticket).filter(Ticket.id == first_expense.ticket_id).first()
1329
  project_id = ticket.project_id if ticket else None
1330
 
1331
- total_amount = sum(row["total_amount"] for row in csv_rows)
 
 
 
 
 
1332
 
1333
  background_tasks.add_task(
1334
  NotificationHelper.notify_expense_export_complete,
1335
  db=db,
1336
  user_id=current_user.id,
1337
- total_expenses=len(expenses),
1338
- total_amount=total_amount,
1339
  payment_groups=len(csv_rows),
1340
  project_id=project_id,
1341
- expense_ids=[e.id for e in expenses]
1342
  )
1343
  except Exception as e:
1344
  logger.error(f"Failed to queue export completion notification: {str(e)}")
 
1328
  ticket = db.query(Ticket).filter(Ticket.id == first_expense.ticket_id).first()
1329
  project_id = ticket.project_id if ticket else None
1330
 
1331
+ # Calculate total from Tende Pay CSV format (key is "AMOUNT" not "total_amount")
1332
+ total_amount = sum(row["AMOUNT"] for row in csv_rows)
1333
+
1334
+ # Calculate total from actually exported expenses (more accurate)
1335
+ exported_expenses = [e for e in expenses if e.id in exported_expense_ids]
1336
+ total_amount_from_expenses = sum(e.total_cost for e in exported_expenses)
1337
 
1338
  background_tasks.add_task(
1339
  NotificationHelper.notify_expense_export_complete,
1340
  db=db,
1341
  user_id=current_user.id,
1342
+ total_expenses=len(exported_expenses), # Only count exported ones
1343
+ total_amount=float(total_amount_from_expenses),
1344
  payment_groups=len(csv_rows),
1345
  project_id=project_id,
1346
+ expense_ids=[e.id for e in exported_expenses]
1347
  )
1348
  except Exception as e:
1349
  logger.error(f"Failed to queue export completion notification: {str(e)}")
src/app/services/notification_helper.py CHANGED
@@ -628,8 +628,8 @@ class NotificationHelper:
628
  """
629
  service = NotificationService()
630
 
631
- title = f"Expense Payment Export Complete"
632
- message = f"Exported {total_expenses} expenses (${total_amount:.2f}) in {payment_groups} payment groups. All marked as paid."
633
 
634
  metadata = {
635
  'project_id': str(project_id) if project_id else None,
 
628
  """
629
  service = NotificationService()
630
 
631
+ title = f"Tende Pay Export Complete"
632
+ message = f"Exported {total_expenses} expenses (KES {total_amount:,.2f}) in {payment_groups} payment groups. CSV ready for Tende Pay upload. All expenses marked as paid."
633
 
634
  metadata = {
635
  'project_id': str(project_id) if project_id else None,
tests/fixtures/payment_export_2025-12-10.csv ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ NAME,ID NUMBER,PHONE NUMBER,AMOUNT,PAYMENT MODE,BANK (Optional),BANK ACCOUNT NO (Optional),PAYBILL BUSINESS NO (Optional),PAYBILL ACCOUNT NO (Optional),BUY GOODS TILL NO (Optional),BILL PAYMENT BILLER CODE (Optional),BILL PAYMENT ACCOUNT NO (Optional),NARRATION (OPTIONAL)
2
+ Viyisa Sasa,12437583,254799459782,310.0,MPESA,,,,,,,,"Expenses 2025-12-10: Ticket #Nnacy Wanjiru (Transport 210.0) | Ticket #Elizabeth Muthoni (Transport 100.0) - 2 items, 310.0 KES"