- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
- Added hub_customer_id to TModuleApprovalStats for better tracking.
- Introduced TModuleWizardEditRequest for editing time entries, allowing updates to description, hours, and billing method.
- Implemented approval and rejection logic for Hub Worklogs, including handling negative IDs.
- Created a new endpoint for updating entry details, supporting both Hub Worklogs and Module Times.
- Updated frontend to include an edit modal for time entries, with specific fields for Hub Worklogs and Module Times.
- Enhanced customer statistics retrieval to include pending counts from Hub Worklogs.
- Added migrations for ticket enhancements, including new fields and constraints for worklogs and prepaid cards.
- Implemented a new simplified time tracking wizard (wizard2) for approval processes.
- Added a registrations view to list all time tracking entries.
- Enhanced the existing wizard.html to include a billable checkbox for entries.
- Updated JavaScript logic to handle billable state and travel status for time entries.
- Introduced a cleanup step in the deployment script to remove old images.
- Created a new HTML template for registrations with filtering and pagination capabilities.
- 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
- 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
- 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()
- 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.
- 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
BUG FIX:
- Hardcoded 'TIME001' eksisterer ikke i e-conomic
- Tilføjet TIMETRACKING_ECONOMIC_PRODUCT setting (default: '1000')
- Produkt nummer kan nu ændres via .env
- Fejl: Product 'TIME001' not found
LØSNING:
Tilføj til .env: TIMETRACKING_ECONOMIC_PRODUCT=XXXX
hvor XXXX er dit produkt nummer for konsulentimer i e-conomic
BUG FIX:
- Hardcoded layout 21 fejler med 'Layout 21 is historic'
- Tilføjet TIMETRACKING_ECONOMIC_LAYOUT setting (default: 19)
- Layout 19 er standard dansk faktura layout
- Kan nu ændres via .env uden kode-ændringer
ERROR: e-conomic API error - layout: Layout '21' is historic
LØSNING: Brug layout 19 eller andet aktivt layout nummer
BUG FIX:
- execute_query returnerer list, men TModuleOrder(**updated) forventede dict
- TypeError: argument after ** must be a mapping, not list
- Changed til updated[0] for at få første row
- Dette er den SIDSTE execute_query/execute_query_single bug i order_service.py
ERROR: TModuleOrder() argument after ** must be a mapping, not list
→ return TModuleOrder(**updated)
→ updated er list, skal være dict