diff --git a/app/timetracking/backend/router.py b/app/timetracking/backend/router.py index 3297b54..2ce01b6 100644 --- a/app/timetracking/backend/router.py +++ b/app/timetracking/backend/router.py @@ -688,6 +688,67 @@ async def update_customer_hourly_rate(customer_id: int, hourly_rate: float, user raise HTTPException(status_code=500, detail=str(e)) +@router.post("/customers/bulk-update-rate", tags=["Customers"]) +async def bulk_update_customer_hourly_rates( + customer_ids: List[int], + hourly_rate: float, + user_id: Optional[int] = None +): + """ + Opdater timepris for flere kunder på én gang. + + Args: + customer_ids: Liste af kunde-ID'er + hourly_rate: Ny timepris i DKK (f.eks. 850.00) + + Returns: + Antal opdaterede kunder + """ + try: + from decimal import Decimal + + # Validate inputs + if not customer_ids: + raise HTTPException(status_code=400, detail="No customers selected") + + if hourly_rate < 0: + raise HTTPException(status_code=400, detail="Hourly rate must be positive") + + rate_decimal = Decimal(str(hourly_rate)) + + # Update all selected customers + execute_update( + "UPDATE tmodule_customers SET hourly_rate = %s, updated_at = CURRENT_TIMESTAMP WHERE id = ANY(%s)", + (rate_decimal, customer_ids) + ) + + # Count affected rows + updated_count = len(customer_ids) + + # Audit log for each customer + for customer_id in customer_ids: + audit.log_event( + entity_type="customer", + entity_id=str(customer_id), + event_type="hourly_rate_updated", + details={"hourly_rate": float(hourly_rate), "bulk_update": True}, + user_id=user_id + ) + + logger.info(f"✅ Bulk updated hourly rate for {updated_count} customers to {hourly_rate} DKK") + + return { + "updated": updated_count, + "hourly_rate": float(hourly_rate) + } + + except HTTPException: + raise + except Exception as e: + logger.error(f"❌ Error in bulk hourly rate update: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + @router.patch("/customers/{customer_id}/time-card", tags=["Customers"]) async def toggle_customer_time_card(customer_id: int, enabled: bool, user_id: Optional[int] = None): """ diff --git a/app/timetracking/frontend/customers.html b/app/timetracking/frontend/customers.html index 2c99727..a7a42e6 100644 --- a/app/timetracking/frontend/customers.html +++ b/app/timetracking/frontend/customers.html @@ -90,16 +90,21 @@
-
+
-
+
+
+ +
@@ -109,6 +114,9 @@ + @@ -224,9 +232,45 @@ + + + {% endblock %} diff --git a/test_bulk_customer_update.py b/test_bulk_customer_update.py new file mode 100644 index 0000000..0feae88 --- /dev/null +++ b/test_bulk_customer_update.py @@ -0,0 +1,25 @@ +""" +Quick test of bulk customer hourly rate update endpoint +""" +import json + +# Test payload structure +test_payload = { + "customer_ids": [1, 2, 3], + "hourly_rate": 1200.00 +} + +print("Test payload for POST /api/v1/timetracking/customers/bulk-update-rate:") +print(json.dumps(test_payload, indent=2)) + +print("\nExpected response:") +expected_response = { + "updated": 3, + "hourly_rate": 1200.00 +} +print(json.dumps(expected_response, indent=2)) + +print("\nEndpoint ready for testing!") +print("curl -X POST http://172.16.31.183:8000/api/v1/timetracking/customers/bulk-update-rate \\") +print(' -H "Content-Type: application/json" \\') +print(f' -d \'{json.dumps(test_payload)}\'')
+ + Kunde vTiger ID Timepris (DKK)