bmc_hub/app/workflows/customer_activation.py

62 lines
3.9 KiB
Python
Raw Normal View History

"""Customer-card opt-in; global configuration remains the master switch."""
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field, field_validator
from app.core.auth_dependencies import get_current_user, require_permission
from app.core.auth_service import AuthService
from app.core.config import settings
from .service import transaction
router=APIRouter(prefix='/workflows/customers')
async def read_access(user=Depends(get_current_user)):
if not user.get('is_superadmin') and not any(AuthService.user_has_permission(user['id'],permission) for permission in ('customers.view','workflows.view')):
raise HTTPException(403,'Du mangler rettighed til at se kunden eller workflows')
return user
def can_manage(user):
return bool(user.get('is_superadmin') or all(AuthService.user_has_permission(user['id'],permission) for permission in ('customers.edit','workflows.activate')))
def activation_status(customer_id,cursor,user,lock=False):
cursor.execute('SELECT id FROM customers WHERE id=%s'+(' FOR UPDATE' if lock else ''),(customer_id,))
if not cursor.fetchone(): raise HTTPException(404,'Kunden findes ikke')
cursor.execute("SELECT to_regclass('public.bmc_workflow_customer_activation') AS name")
present=cursor.fetchone()
row=None
if present and present.get('name'):
cursor.execute('SELECT * FROM bmc_workflow_customer_activation WHERE customer_id=%s'+(' FOR UPDATE' if lock else ''),(customer_id,))
row=cursor.fetchone()
inherited_enabled=customer_id in settings.WORKFLOWS_CUSTOMER_IDS
enabled=bool(row['enabled']) if row else inherited_enabled
return {'customer_id':customer_id,'enabled':enabled,'explicit_override':bool(row['enabled']) if row else None,
'inherited':row is None,'effective_enabled':bool(settings.WORKFLOWS_ENABLED and settings.WORKFLOWS_MODE=='active' and enabled),
'global_enabled':settings.WORKFLOWS_ENABLED,'mode':settings.WORKFLOWS_MODE,
'can_manage':can_manage(user),'can_view':True,
'updated_at':row.get('updated_at') if row else None,'updated_by':row.get('updated_by') if row else None,
'schema_ready':bool(present and present.get('name'))}
class ActivationUpdate(BaseModel):
enabled:bool
reason:str=Field(min_length=3,max_length=1000)
@field_validator('reason')
@classmethod
def reason_is_meaningful(cls,value):
value=value.strip()
if len(value)<3: raise ValueError('Begrundelsen skal være mindst tre tegn')
return value
@router.get('/{customer_id}/activation')
def get_activation(customer_id:int,user=Depends(read_access)):
with transaction() as cursor: return activation_status(customer_id,cursor,user)
@router.put('/{customer_id}/activation')
def set_activation(customer_id:int,payload:ActivationUpdate,user=Depends(require_permission('customers.edit')),workflow_access=Depends(require_permission('workflows.activate'))):
with transaction() as cursor:
# Parent-row locking serializes first-ever overrides and their audit records.
previous=activation_status(customer_id,cursor,user,lock=True)
if not previous['schema_ready']: raise HTTPException(503,'Kundeaktivering kræver workflowmigration 1067')
cursor.execute('INSERT INTO bmc_workflow_customer_activation(customer_id,enabled,updated_by) VALUES(%s,%s,%s) ON CONFLICT(customer_id) DO UPDATE SET enabled=EXCLUDED.enabled,updated_by=EXCLUDED.updated_by,updated_at=now()',
(customer_id,payload.enabled,user['id']))
cursor.execute('INSERT INTO bmc_workflow_customer_activation_history(customer_id,before_enabled,after_enabled,inherited_enabled,actor_id,reason) VALUES(%s,%s,%s,%s,%s,%s)',
(customer_id,previous['explicit_override'],payload.enabled,customer_id in settings.WORKFLOWS_CUSTOMER_IDS,user['id'],payload.reason))
return activation_status(customer_id,cursor,user)