# AuditPolicy

## Description

AuditPolicy defines which entities, operations, and fields are tracked by the audit system. Each policy targets a specific entity and operation type, with an optional company scope for multi-company deployments. Policies follow a lifecycle state machine (DRAFT → ACTIVE ↔ INACTIVE) that enables safe rollout and rollback. Only ACTIVE policies trigger audit capture. A uniqueness constraint ensures at most one ACTIVE policy exists for a given (entityName, companyId, operationType) combination. Policy scope must match the target entity's scope — company-bound entities require company-scoped policies; global entities require global policies.

Examples: "ACTIVE policy auditing UPDATE on SalesOrder for company-123", "DRAFT policy under review for DELETE on JournalEntry".

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Draft: createAuditPolicy
    Draft --> Active: activateAuditPolicy
    Draft --> Active: replaceAuditPolicy (atomic swap)
    Active --> Inactive: deactivateAuditPolicy
    Inactive --> Active: reactivateAuditPolicy
    Draft --> [*]: deleteAuditPolicy
    note right of Draft: Only DRAFT policies can be deleted
    note right of Active: At most one ACTIVE per (entityName, companyId, operationType)
```

### Command Definitions

- [createAuditPolicy](../commands/CreateAuditPolicy.md) - Create a new audit policy in DRAFT status
- [updateAuditPolicy](../commands/UpdateAuditPolicy.md) - Modify a DRAFT audit policy's configuration
- [activateAuditPolicy](../commands/ActivateAuditPolicy.md) - Transition a DRAFT policy to ACTIVE
- [deactivateAuditPolicy](../commands/DeactivateAuditPolicy.md) - Transition an ACTIVE policy to INACTIVE
- [reactivateAuditPolicy](../commands/ReactivateAuditPolicy.md) - Transition an INACTIVE policy back to ACTIVE
- [replaceAuditPolicy](../commands/ReplaceAuditPolicy.md) - Atomically deactivate current ACTIVE and activate a DRAFT replacement
- [deleteAuditPolicy](../commands/DeleteAuditPolicy.md) - Permanently remove a DRAFT policy

### Query Definitions

- [getAuditPolicy](../queries/GetAuditPolicy.md) - Retrieve a single audit policy by ID
- [listAuditPolicies](../queries/ListAuditPolicies.md) - List audit policies with optional filters

### Models

- AuditPolicy
- PolicyFieldRule

### Invariants

- Policies are always created in DRAFT status
- At most one ACTIVE policy can exist for a given (entityName, companyId, operationType) combination — activating a conflicting policy is rejected with an error instructing the administrator to deactivate the existing policy first
- Only DRAFT policies can be deleted; ACTIVE and INACTIVE policies cannot — deleting an ACTIVE or INACTIVE policy returns an error
- Target entity name is required and must be non-empty
- Target entity name must reference a registered AuditableEntity
- Exactly one operation type (CREATE, UPDATE, or DELETE) must be specified per policy — policies without an operation type cannot be activated
- Operation types are limited to the set CREATE, UPDATE, DELETE — invalid values are rejected
- Activating a policy without a target entity is rejected
- Policy scope must match the target entity's scope: company-bound entities require company-scoped policies (companyId set); global entities require global policies (companyId null) — mismatches are rejected at creation time
- entityName and companyId are immutable after creation — scope is validated only at creation time; updates cannot change the policy's scope
- Only ACTIVE policies trigger audit capture; DRAFT and INACTIVE policies do not
- Deactivating a policy stops new audit capture but does not delete previously recorded audit entries
- Reactivating an INACTIVE policy resumes audit capture
- A policy without field-level rules audits all eligible fields on the target entity (as defined by the entity's auditableFields registration)
- A policy with field-level rules audits only the specified fields (which must be a subset of the entity's auditableFields)
- `replaceAuditPolicy` atomically deactivates the current ACTIVE policy and activates the replacement DRAFT policy in a single transaction
- `replaceAuditPolicy` requires the replacement policy to be in DRAFT status — non-DRAFT replacements are rejected
- `replaceAuditPolicy` requires an existing ACTIVE policy for the same (entityName, companyId, operationType) — if none exists, the operation is rejected (use `activateAuditPolicy` instead)
- After `replaceAuditPolicy`, the previously active policy is in INACTIVE status and the replacement is in ACTIVE status
- During manual (non-atomic) replacement, events arriving between deactivation and activation are silently discarded
- Creating, updating, activating, deactivating, reactivating, replacing, and deleting audit policies requires the `manageAuditPolicies` permission at the appropriate scope
- A caller with company-scoped `manageAuditPolicies` can only manage policies for their assigned companies
- A caller with global-scoped `manageAuditPolicies` can only manage global policies

### Relationships

- **Has Many PolicyFieldRule**: Each AuditPolicy has zero or more PolicyFieldRule records defining field-level audit configuration
- **References AuditableEntity**: entityName must match a registered AuditableEntity
- **References organization (cross-module)**: companyId references a Company from the organization module for company-scoped policies
