- Add migration 053 for approval columns
- Checks if columns exist before adding (safe)
- Adds: approved_hours, rounded_to, approval_note, billable, is_travel, approved_at, approved_by
- Adds indexes for status and approved_at
- Required for production deployment
- Add logging before approval object creation
- Add logging after approval object creation
- Add logging before database update
- Add logging after database update
- Add time_id to error messages
- This will help diagnose 500 errors on production
- Implemented a Python script using Paramiko to connect to an SFTP server.
- Added functionality to list files in the current directory and check for the existence of a '/backups' directory.
- Included error handling for directory listing and creation.
- Implemented a test upload feature to verify file upload capabilities.
- Added cleanup for uploaded test files and ensured proper connection closure.
- Let Pydantic handle Decimal conversion
- Send approved_hours as Decimal, not float
- Send rounded_to as Decimal, not float
- Add exc_info=True to error logging
- 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
BREAKING CHANGES:
- vTiger sync: Never overwrites existing vtiger_id
- Contact sync: REPLACES links instead of appending (idempotent)
- E-conomic sync: Only updates fields it owns (address, city, postal, email_domain, website)
- E-conomic sync: Does NOT overwrite name or cvr_number anymore
ARCHITECTURE:
- Each data source owns specific fields
- Sync operations are now idempotent (can run multiple times)
- Clear documentation of field ownership in sync_router.py
- Contact links deleted and recreated on sync to match vTiger state
FIXED:
- Contact relationships now correct after re-sync
- No more mixed customer data from different sources
- Sorting contacts by company_count DESC (companies first)
- Added address field to UPDATE query in economic sync
- Added address field to INSERT query for new customers
- Fixes issue where address from e-conomic was not synced
- Prevents mixed data (København address with Lundby city/postal)
- Address is now synced along with city, postal_code, country
- Added more specific error message when customer not found
- Added debug logging to check customer object type
- Changed error from 'Customer not found' to include customer_id
- Helps diagnose 'string indices must be integers' error
- Fixed customer_rate being returned as string from DB (NUMERIC type)
- Added parseFloat() when using customer_rate in calculations
- Fixes customer stats showing '-' instead of actual hourly rate
- Applied to loadCustomerContext(), displayCaseEntries(), and approveEntry()
- Fixed loadSyncStats() to correctly parse API response
- Customers API returns {customers: [...]} not array directly
- Added const data = await response.json() and data.customers
- Fixes sync stats counters showing NaN or incorrect values
- Added EconomicService.search_customer_by_name() method
- Added GET /api/v1/customers/{id}/search-economic endpoint
- Returns matching e-conomic customers by name (partial match)
- Helps find economic customer number for customers without CVR
- Shows customerNumber, name, CVR, email, city in results
- Added POST /api/v1/customers/{id}/link-economic endpoint
- Allows manually setting economic_customer_number for customers without CVR
- Useful for vTiger customers that can't auto-match via CVR or name
- Updates last_synced_at timestamp when linking
- Created VERSION file with current version
- Health endpoints now read version from VERSION file instead of hardcoded
- Fixes issue where health check showed wrong version
- main.py /health and /api/v1/system/health now show correct version
- Fixed UPDATE query to include economic_customer_number field
- Previously only set on INSERT, not UPDATE when matching by CVR
- Now e-conomic customer number is always synced as source of truth
- Fixes issue where customers synced from e-conomic were missing customer numbers
- Changed wizard.html to use customer_rate instead of customer_hourly_rate
- Fixes issue where wizard displayed default 1200 DKK instead of customer-specific hourly rate
- Backend API returns customer_rate field (from TModuleTimeWithContext model)
- Frontend now correctly reads customer_rate from API response
- Affected lines: 571, 807, 1084 in wizard.html
Fixed API endpoint to accept JSON request body instead of query params:
- Added TModuleBulkRateUpdate Pydantic model
- Changed endpoint to accept request body
- Fixed parameter references to use request.customer_ids and request.hourly_rate
- Added migration for hourly_rate_updated and time_card_toggled event types
Resolves: 'Not Found' error and audit log constraint violations
Added bulk selection and update functionality for customer hourly rates:
Frontend (customers.html):
- Added checkbox column with select-all functionality
- Created bulk price update modal with customer list
- Implemented JavaScript for selection state management
- Shows selected count in UI badge
- Supports indeterminate state for partial selection
Backend (router.py):
- New POST /api/v1/timetracking/customers/bulk-update-rate endpoint
- Accepts {customer_ids: List[int], hourly_rate: float}
- Updates multiple customers in single SQL query
- Creates audit log entries for each updated customer
- Returns updated count
Use case: Select multiple customers and update hourly rate simultaneously
Når ordre eksporteres til e-conomic opdateres vTiger Timelog med:
- billed_via_thehub_id: Hub ordre ID (f.eks. 5)
- cf_timelog_invoiced: '1' (markér som faktureret)
Dette sikrer at timelogs i vTiger bliver markeret som fakturerede
og kan filtreres/rapporteres korrekt i vTiger.
KRITISK FIX: Serveren stoppede fordi updateto.sh brugte forkert compose fil
Problem:
- updateto.sh brugte 'docker-compose.yml' (dev version)
- Dev versionen har 'restart: unless-stopped'
- Production skal bruge 'docker-compose.prod.yml' med 'restart: always'
- Når server rebooter eller opdateres stopper containerne og starter ikke automatisk
Løsning:
- updateto.sh bruger nu '-f docker-compose.prod.yml' eksplicit
- Sikrer korrekt restart politik (always) anvendes
- Containerne vil nu starte automatisk efter reboot
- Tilføjet Hub ordre ID til success/info beskeder
- Viser at vTiger Timelog er opdateret med Hub ordre ID
- Gør det tydeligt at koblinger mellem Hub og vTiger er oprettet
- Tilføjet update_timelog_billed() metode til vtiger_sync.py
- Opdaterer billed_via_thehub_id felt i vTiger Timelog records
- Kaldes automatisk efter succesfuld e-conomic eksport
- Respekterer READ_ONLY og DRY_RUN safety flags
- Fejler ikke eksporten hvis vTiger update fejler (bare logger warning)
- Før: 'CC5784. 8.0 timer 1,200,- 9,600 / Arbejde konstant / 09.12.2025 - Marley'
- Nu: 'Arbejde konstant / 09.12.2025 - Marley'
- e-conomic viser timer og priser i egne kolonner, så det er overflødigt i teksten