fix: Timetracking wizard approval endpoint (v1.3.76)
- Fix parameter handling in approve_time_entry endpoint - Change from query params to body Dict[str, Any] - Send all required fields to wizard.approve_time_entry() - Calculate rounded_to if auto-rounding enabled - Add approval_note, billable, is_travel fields - Add Dict, Any imports
This commit is contained in:
parent
6c4042b9b6
commit
7cb38663bc
73
RELEASE_NOTES_v1.3.76.md
Normal file
73
RELEASE_NOTES_v1.3.76.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# Release Notes - v1.3.76
|
||||||
|
|
||||||
|
**Release Date:** 2. januar 2026
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
|
||||||
|
### Timetracking Wizard Approval
|
||||||
|
- **Fixed approval endpoint** - Wizard approval nu virker korrekt
|
||||||
|
- **Fixed parameter handling** - Router modtager nu body params korrekt som Dict
|
||||||
|
- **Fixed missing fields** - Sender nu alle nødvendige felter til wizard.approve_time_entry():
|
||||||
|
- `rounded_to` beregnes hvis auto-rounding er enabled
|
||||||
|
- `approval_note` sendes med fra frontend
|
||||||
|
- `billable` sættes til true som default
|
||||||
|
- `is_travel` sendes med fra checkbox
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
- Ændret `/api/v1/timetracking/wizard/approve/{time_id}` endpoint
|
||||||
|
- Modtager nu `request: Dict[str, Any]` i stedet for individuelle query params
|
||||||
|
- Tilføjet `Dict, Any` imports i router
|
||||||
|
- Beregner `rounded_to` baseret på TIMETRACKING_AUTO_ROUND setting
|
||||||
|
|
||||||
|
## 📝 Files Changed
|
||||||
|
|
||||||
|
- `app/timetracking/backend/router.py` - Fixed approve_time_entry endpoint
|
||||||
|
|
||||||
|
## 🚀 Deployment Instructions
|
||||||
|
|
||||||
|
### Production Server Update
|
||||||
|
|
||||||
|
1. **SSH til serveren:**
|
||||||
|
```bash
|
||||||
|
ssh bmcadmin@172.16.31.183
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Naviger til projekt directory:**
|
||||||
|
```bash
|
||||||
|
cd /opt/bmc_hub
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Pull ny version:**
|
||||||
|
```bash
|
||||||
|
git fetch --tags
|
||||||
|
git checkout v1.3.76
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Genstart containers:**
|
||||||
|
```bash
|
||||||
|
docker-compose restart api
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Verificer:**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8001/health
|
||||||
|
# Test approval:
|
||||||
|
# Gå til http://172.16.31.183:8000/timetracking/wizard
|
||||||
|
# Godkend en tidsregistrering
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚠️ Breaking Changes
|
||||||
|
|
||||||
|
None - this is a bug fix
|
||||||
|
|
||||||
|
## 📊 Impact
|
||||||
|
|
||||||
|
- Timetracking wizard approval virker nu igen
|
||||||
|
- Ingen database ændringer nødvendige
|
||||||
|
- Ingen configuration ændringer nødvendige
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Git Tag:** v1.3.76
|
||||||
|
**Previous Version:** v1.3.75
|
||||||
|
**Commit:** TBD
|
||||||
@ -7,7 +7,7 @@ Isoleret routing uden påvirkning af existing Hub endpoints.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List
|
from typing import Optional, List, Dict, Any
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Depends
|
from fastapi import APIRouter, HTTPException, Depends
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@ -218,9 +218,7 @@ async def get_next_pending_entry(
|
|||||||
@router.post("/wizard/approve/{time_id}", response_model=TModuleTimeWithContext, tags=["Wizard"])
|
@router.post("/wizard/approve/{time_id}", response_model=TModuleTimeWithContext, tags=["Wizard"])
|
||||||
async def approve_time_entry(
|
async def approve_time_entry(
|
||||||
time_id: int,
|
time_id: int,
|
||||||
billable_hours: Optional[float] = None,
|
request: Dict[str, Any],
|
||||||
hourly_rate: Optional[float] = None,
|
|
||||||
rounding_method: Optional[str] = None,
|
|
||||||
user_id: Optional[int] = None
|
user_id: Optional[int] = None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -229,10 +227,12 @@ async def approve_time_entry(
|
|||||||
Path params:
|
Path params:
|
||||||
- time_id: ID på tidsregistreringen
|
- time_id: ID på tidsregistreringen
|
||||||
|
|
||||||
Body (optional):
|
Body:
|
||||||
- billable_hours: Timer efter godkendelse (hvis ikke angivet, bruges original_hours med auto-rounding)
|
- billable_hours: Timer efter godkendelse (optional)
|
||||||
- hourly_rate: Timepris i DKK (override customer rate)
|
- hourly_rate: Timepris i DKK (optional)
|
||||||
- rounding_method: "up", "down", "nearest" (override default)
|
- is_travel: True hvis kørsel (optional)
|
||||||
|
- approval_note: Godkendelsesnote (optional)
|
||||||
|
- rounding_method: "up", "down", "nearest" (optional)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
@ -253,6 +253,9 @@ async def approve_time_entry(
|
|||||||
raise HTTPException(status_code=404, detail="Time entry not found")
|
raise HTTPException(status_code=404, detail="Time entry not found")
|
||||||
|
|
||||||
# Beregn approved_hours
|
# Beregn approved_hours
|
||||||
|
billable_hours = request.get('billable_hours')
|
||||||
|
rounding_method = request.get('rounding_method')
|
||||||
|
|
||||||
if billable_hours is None:
|
if billable_hours is None:
|
||||||
approved_hours = Decimal(str(entry['original_hours']))
|
approved_hours = Decimal(str(entry['original_hours']))
|
||||||
|
|
||||||
@ -267,20 +270,30 @@ async def approve_time_entry(
|
|||||||
approved_hours = (approved_hours / increment).quantize(Decimal('1'), rounding='ROUND_DOWN') * increment
|
approved_hours = (approved_hours / increment).quantize(Decimal('1'), rounding='ROUND_DOWN') * increment
|
||||||
else:
|
else:
|
||||||
approved_hours = (approved_hours / increment).quantize(Decimal('1'), rounding='ROUND_HALF_UP') * increment
|
approved_hours = (approved_hours / increment).quantize(Decimal('1'), rounding='ROUND_HALF_UP') * increment
|
||||||
|
|
||||||
|
rounded_to = increment
|
||||||
|
else:
|
||||||
|
rounded_to = None
|
||||||
else:
|
else:
|
||||||
approved_hours = Decimal(str(billable_hours))
|
approved_hours = Decimal(str(billable_hours))
|
||||||
|
rounded_to = None
|
||||||
|
|
||||||
# Opdater med hourly_rate hvis angivet
|
# Opdater med hourly_rate hvis angivet
|
||||||
|
hourly_rate = request.get('hourly_rate')
|
||||||
if hourly_rate is not None:
|
if hourly_rate is not None:
|
||||||
execute_update(
|
execute_update(
|
||||||
"UPDATE tmodule_times SET hourly_rate = %s WHERE id = %s",
|
"UPDATE tmodule_times SET hourly_rate = %s WHERE id = %s",
|
||||||
(Decimal(str(hourly_rate)), time_id)
|
(Decimal(str(hourly_rate)), time_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Godkend
|
# Godkend med alle felter
|
||||||
approval = TModuleTimeApproval(
|
approval = TModuleTimeApproval(
|
||||||
time_id=time_id,
|
time_id=time_id,
|
||||||
approved_hours=float(approved_hours)
|
approved_hours=float(approved_hours),
|
||||||
|
rounded_to=float(rounded_to) if rounded_to else None,
|
||||||
|
approval_note=request.get('approval_note'),
|
||||||
|
billable=True, # Default til fakturerbar
|
||||||
|
is_travel=request.get('is_travel', False)
|
||||||
)
|
)
|
||||||
|
|
||||||
return wizard.approve_time_entry(approval, user_id=user_id)
|
return wizard.approve_time_entry(approval, user_id=user_id)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user