bmc_hub/docs/TIMETRACKING_BILLED_VIA_HUB.md

5.3 KiB

Time Tracking - Billed Via Hub Order ID

Oversigt

Implementeret tracking af hvilken Hub ordre (og dermed e-conomic ordre) hver tidsregistrering er blevet faktureret gennem.

Database Ændringer

Migration: 060_add_billed_via_thehub_id.sql

Tilføjet nyt felt til tmodule_times tabellen:

ALTER TABLE tmodule_times ADD COLUMN billed_via_thehub_id INTEGER;
ALTER TABLE tmodule_times 
    ADD CONSTRAINT tmodule_times_billed_via_thehub_id_fkey 
    FOREIGN KEY (billed_via_thehub_id) 
    REFERENCES tmodule_orders(id) 
    ON DELETE SET NULL;
CREATE INDEX idx_tmodule_times_billed_via_thehub_id ON tmodule_times(billed_via_thehub_id);

Felt beskrivelse:

  • billed_via_thehub_id: Hub ordre ID som tidsregistreringen er faktureret gennem
  • Foreign key til tmodule_orders.id
  • Via Hub ordren kan man finde economic_order_number som er ordrenummeret i e-conomic

Kode Ændringer

1. app/timetracking/backend/models.py

Tilføjet felt til TModuleTime Pydantic model:

billed_via_thehub_id: Optional[int] = Field(None, description="Hub order ID this time was billed through")
is_travel: bool = False  # Også tilføjet manglende felt

2. app/timetracking/backend/economic_export.py

Opdateret export_order_to_economic() til at sætte billed_via_thehub_id når ordren eksporteres:

# Marker time entries som billed og opdater billed_via_thehub_id
execute_update(
    """UPDATE tmodule_times
       SET status = 'billed',
           billed_via_thehub_id = %s
       WHERE id IN (
           SELECT UNNEST(time_entry_ids) 
           FROM tmodule_order_lines 
           WHERE order_id = %s
       )""",
    (request.order_id, request.order_id)
)

Vigtig note: vTiger opdateres også via update_timelog_billed() som sætter billed_via_thehub_id i vTiger's Timelog records.

3. app/timetracking/backend/order_service.py

FJERNET for tidlig status opdatering:

# BEFORE: Time entries blev sat til 'billed' når Hub ordre blev oprettet
# AFTER: Time entries forbliver 'approved' indtil e-conomic eksporten er succesfuld

Nu opdateres status='billed' og billed_via_thehub_id KUN i economic_export.py efter succesfuld eksport.

4. app/timetracking/backend/vtiger_sync.py

BESKYTTELSE MOD OVERSKRIVNING: Tidsregistreringer med billed_via_thehub_id opdateres IKKE ved sync:

# Update only if NOT yet approved AND NOT yet billed
result = execute_update(
    """UPDATE tmodule_times 
       SET description = %s, original_hours = %s, worked_date = %s,
           user_name = %s, billable = %s, vtiger_data = %s::jsonb, 
           sync_hash = %s, last_synced_at = CURRENT_TIMESTAMP
       WHERE vtiger_id = %s 
       AND status = 'pending' 
       AND billed_via_thehub_id IS NULL""",
    ...
)

Dette sikrer at allerede fakturerede tidsregistreringer forbliver låst og ikke overskrevet af vTiger sync.

Workflow

Før ændringerne:

  1. Godkendte tider → status='approved'
  2. Opret ordre i Hub → status='billed' ⚠️ (for tidligt!)
  3. Eksporter til e-conomic → economic_order_number sættes på Hub ordre

Efter ændringerne:

  1. Godkendte tider → status='approved'
  2. Opret ordre i Hub → tider forbliver status='approved'
  3. Eksporter til e-conomicstatus='billed' + billed_via_thehub_id sættes
  4. vTiger opdateres også med billed_via_thehub_id

Data Relations

tmodule_times.billed_via_thehub_id 
  ↓ (foreign key)
tmodule_orders.id
  → tmodule_orders.economic_order_number (e-conomic ordre nummer)
  → tmodule_orders.economic_draft_id (e-conomic kladde ID)

Queries

Find alle tidsregistreringer for en e-conomic ordre:

SELECT t.*, o.economic_order_number
FROM tmodule_times t
JOIN tmodule_orders o ON t.billed_via_thehub_id = o.id
WHERE o.economic_order_number = '12345';

Find tider der er faktureret via en specifik Hub ordre:

SELECT * FROM tmodule_times 
WHERE billed_via_thehub_id = 123;

Find tider der IKKE er faktureret endnu:

SELECT * FROM tmodule_times 
WHERE status = 'approved' 
AND billed_via_thehub_id IS NULL;

vTiger Integration

vTiger's Timelog records opdateres også via app/timetracking/backend/vtiger_sync.py:

async def update_timelog_billed(self, vtiger_ids: List[str], hub_order_id: int):
    payload = {
        "elementType": "Timelog",
        "element": {
            "id": vtiger_id,
            "billed_via_thehub_id": str(hub_order_id),
            "cf_timelog_invoiced": "1"
        }
    }

Note: cf_timelog_invoiced (IS BILLED) feltet i vTiger kan vi IKKE ændre fra Hub (vTiger begrænsning), men vi opdaterer vores eget billed_via_thehub_id felt.

Testing

Migrationen er kørt og verificeret:

docker exec bmc-hub-postgres psql -U bmc_hub -d bmc_hub -c "\d tmodule_times"
# Viser: billed_via_thehub_id | integer med foreign key

Deployment

  1. Migration er kørt på dev (060_add_billed_via_thehub_id.sql)
  2. Kode ændringer deployed i samme commit
  3. Eksisterende tidsregistreringer har billed_via_thehub_id = NULL
  4. Fremtidige eksporter vil populere feltet korrekt

Relaterede Filer

  • migrations/060_add_billed_via_thehub_id.sql
  • app/timetracking/backend/models.py
  • app/timetracking/backend/economic_export.py
  • app/timetracking/backend/order_service.py
  • app/timetracking/backend/vtiger_sync.py