# Service Contract Migration Wizard - Implementation Summary ## ✅ What Was Built A step-by-step wizard that migrates Vtiger service contracts to Hub systems: - **Cases** → Archived to `tticket_archived_tickets` - **Timelogs** → Transferred as klippekort top-ups (prepaid card hours) Features: - ✅ Dry-run toggle (preview mode without database writes) - ✅ Step-by-step review of each case/timelog - ✅ Manual klippekort selection per timelog - ✅ Progress tracking and summary report - ✅ Read-only from Vtiger (no writes back to Vtiger) ## 🎯 Files Created/Modified ### New Files: 1. **[app/timetracking/backend/service_contract_wizard.py](app/timetracking/backend/service_contract_wizard.py)** (275 lines) - Core wizard service with all business logic - Methods: `load_contract_detailed_data()`, `archive_case()`, `transfer_timelog_to_klippekort()`, `get_wizard_summary()` - Dry-run support built into each method 2. **[app/timetracking/frontend/service_contract_wizard.html](app/timetracking/frontend/service_contract_wizard.html)** (650 lines) - Complete wizard UI with Nordic design - Contract dropdown selector - Progress bar with live counters - Current item display with conditional klippekort dropdown - Summary report on completion ### Modified Files: 1. **[app/services/vtiger_service.py](app/services/vtiger_service.py)** (+65 lines) - Added `get_service_contracts(account_id=None)` - Fetch active service contracts - Added `get_service_contract_cases(contract_id)` - Fetch cases linked to contract - Added `get_service_contract_timelogs(contract_id)` - Fetch timelogs linked to contract 2. **[app/timetracking/backend/models.py](app/timetracking/backend/models.py)** (+70 lines) - `ServiceContractBase` - Base contract model - `ServiceContractItem` - Single case/timelog item - `ServiceContractWizardData` - Complete contract data for wizard - `ServiceContractWizardAction` - Action result (archive/transfer) - `ServiceContractWizardSummary` - Final summary - `TimologTransferRequest` - Request model for timelog transfer - `TimologTransferResult` - Transfer result 3. **[app/timetracking/backend/router.py](app/timetracking/backend/router.py)** (+180 lines) - `GET /api/v1/timetracking/service-contracts` - List contracts dropdown - `POST /api/v1/timetracking/service-contracts/wizard/load` - Load contract data - `POST /api/v1/timetracking/service-contracts/wizard/archive-case` - Archive case - `POST /api/v1/timetracking/service-contracts/wizard/transfer-timelog` - Transfer timelog - `GET /api/v1/timetracking/service-contracts/wizard/customer-cards/{customer_id}` - Get klippekort 4. **[app/timetracking/frontend/views.py](app/timetracking/frontend/views.py)** (+5 lines) - Added frontend route: `/timetracking/service-contract-wizard` ## 🚀 How to Test ### 1. Start the API ```bash docker-compose up -d api docker-compose logs -f api ``` ### 2. Access the Wizard ``` http://localhost:8000/timetracking/service-contract-wizard ``` ### 3. Dry-Run Mode (Recommended First) 1. Check the "Preview Mode" checkbox at top (enabled by default) 2. Select a service contract from dropdown 3. Review each case/timelog and click "Gem & Næste" 4. No data is written to database in dry-run mode 5. Review summary report to see what WOULD be changed ### 4. Live Mode 1. **Uncheck** "Preview Mode" checkbox 2. Select same or different contract 3. Process items - changes ARE committed to database 4. Cases are exported to `tticket_archived_tickets` 5. Timelogs are added to klippekort via top-up transaction ## 🔍 Database Changes ### Dryrun Mode: - All operations are **logged** but **NOT committed** - Queries are constructed but rolled back - UI shows what WOULD happen ### Live Mode: - Cases are inserted into `tticket_archived_tickets` with: - `source_system = 'vtiger_service_contract'` - `external_id = vtiger case ID` - Full case data in `raw_data` JSONB field - Timelogs create transactions in `tticket_prepaid_transactions` with: - `transaction_type = 'top_up'` - Hours added to klippekort `purchased_hours` - Description references vTiger timelog ## 📊 Data Flow ``` Vtiger Service Contract ↓ SelectContract (dropdown) ↓ LoadContractData ├─ Cases → Archive to tticket_archived_tickets └─ Timelogs → Transfer to klippekort (top-up) ↓ WizardProgress (step-by-step review) ├─ [DRY RUN] Preview mode (no DB writes) └─ [LIVE] Commit to database ↓ Summary Report ├─ Cases archived: N ├─ Hours transferred: N └─ Failed items: N ``` ## 🔐 Safety Features 1. **Dry-run mode enabled by default** - Users see what WOULD happen first 2. **Customer linking** - Looks up Hub customer ID from vTiger account 3. **Klippekort validation** - Verifies card belongs to customer before transfer 4. **Read-only from Vtiger** - No writes back to Vtiger (only reads) 5. **Transaction handling** - Each operation is atomic 6. **Audit logging** - All actions logged with DRY RUN/COMMITTED markers ## 🛠️ Technical Details ### Wizard Service (`ServiceContractWizardService`) - Stateless service class - All methods are static - Database operations via `execute_query()` helpers - Klippekort transfers via `KlippekortService.top_up_card()` ### Frontend UI - Vanilla JavaScript (no frameworks) - Nordic Top design system (matches existing Hub UI) - Responsive Bootstrap 5 grid - Real-time progress updates - Conditional klippekort dropdown (only for timelogs) ### API Endpoints - RESTful architecture - All endpoints support `dry_run` query parameter - Request/response models use Pydantic validation - Comprehensive error handling with HTTPException ## 📝 Logging Output ### Dry-Run Mode: ``` 🔍 DRY RUN: Would archive case 1x123: 'Case Title' 🔍 DRY RUN: Would transfer 5h to card 42 from timelog 2x456 ``` ### Live Mode: ``` ✅ Archived case 1x123 to tticket_archived_tickets (ID: 1234) ✅ Transferred 5h from timelog 2x456 to card 42 ``` ## 🐛 Troubleshooting ### Contracts dropdown is empty: - Verify Vtiger integration is configured (VTIGER_URL, VTIGER_USERNAME, VTIGER_API_KEY in .env) - Check vTiger has active ServiceContracts - Check API user has access to ServiceContracts module ### Klippekort dropdown empty for customer: - Customer may not have any active prepaid cards - Or customer is not linked between Vtiger account and Hub customer - Create a prepaid card for the customer first ### Dry-run mode not working: - Ensure checkbox is checked - Check browser console for JavaScript errors - Verify `dry_run` parameter is passed to API endpoints ## 📋 Next Steps 1. **Test with sample data** - Create test service contract in Vtiger 2. **Verify database changes** - Query `tticket_archived_tickets` post-migration 3. **Monitor klippekort** - Check `tticket_prepaid_transactions` for top-up entries 4. **Adjust as needed** - Tweak timelog filtering or case mapping based on results ## 🔗 Related Components - **Klippekort System**: [app/ticket/backend/klippekort_service.py](app/ticket/backend/klippekort_service.py) - **Archive System**: Database table `tticket_archived_tickets` - **Timetracking Module**: [app/timetracking/](app/timetracking/) - **Vtiger Integration**: [app/services/vtiger_service.py](app/services/vtiger_service.py) --- **Status**: ✅ Ready for testing and deployment **Estimated Time to Test**: 15-20 minutes **Database Dependency**: PostgreSQL (no migrations needed - uses existing tables)