> For the complete documentation index, see [llms.txt](https://docs.beeoclock.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.beeoclock.com/services/absence-management/usecase.md).

# Use Case Documentation

## Overview

This document describes the business use cases and application layer logic for the Absence Management module. Each use case encapsulates business rules, permissions, and orchestrates the interaction between domain entities, repositories, and external services.

## Architecture Pattern

The module follows the CQRS (Command Query Responsibility Segregation) pattern with the following structure:

* **Use Cases**: Application layer orchestrating business logic
* **Commands**: Write operations (Create, Update, Delete)
* **Queries**: Read operations (Get, List)
* **Domain Entities**: Business logic and validation
* **Events**: Asynchronous notifications

## Use Cases

### 1. CreateAbsenceUseCase

**Purpose:** Creates a new absence record with proper validation and permission checking.

**Input:** `CreateAbsenceUseCaseParams`

```typescript
{
  ctx: TenantActorContext,
  absence: AbsenceDto
}
```

**Output:** `void`

**Business Rules:**

1. User must have `CREATE_ABSENCE` permission
2. Users with `OWN` scope can only create absences for themselves
3. Absence must have valid date range (end > start)
4. Must specify either `entireBusiness: true` or at least one member
5. Timezone must be provided

**Process Flow:**

1. Validate user permissions using `PermissionValidatorUseCase`
2. Check scope restrictions for member assignments
3. Execute `CreateAbsenceCommand` via command bus
4. Command handler validates domain rules
5. Persist absence to database
6. Emit `AbsenceCreatedMessage` for notifications
7. Clear relevant cache entries

**Permission Matrix:**

* **OWN scope**: Can only create absences where the actor is in the members list
* **ANY scope**: Can create any absence within the tenant

**Error Scenarios:**

* Insufficient permissions → `AccessPermissionException`
* Invalid domain data → `AbsenceDomainError`
* Database errors → Logged and re-thrown

***

### 2. GetAbsenceUseCase

**Purpose:** Retrieves a specific absence by ID with proper access control.

**Input:** `GetAbsenceUseCaseParams`

```typescript
{
  ctx: TenantActorContext,
  absenceId: string
}
```

**Output:** `AbsenceDto | null`

**Business Rules:**

1. User must have `READ_ABSENCE` permission
2. Users with `OWN` scope can only view their own absences or business-wide absences
3. Returns `null` if absence not found or access denied

**Process Flow:**

1. Validate user permissions
2. Execute `GetAbsenceByIdQuery` via query bus
3. Check access rights based on permission scope
4. Return absence data or null if unauthorized

**Access Control Logic:**

```typescript
// Business-wide absences are visible to all authorized users
if (absence.entireBusiness) return absence;

// Check if user has access to non-business-wide absence
const hasAccess = scope === PermissionScopeEnum.ANY || 
                  absence.members.some(member => member._id === ctx.actor.issuerId);

return hasAccess ? absence : null;
```

**Caching:** Results are cached using `AbsenceCacheKeys.GET_ABSENCE`

***

### 3. GetPagedAbsencesUseCase

**Purpose:** Retrieves a paginated list of absences with filtering and access control.

**Input:** `GetPagedAbsencesUseCaseParams`

```typescript
{
  ctx: TenantActorContext,
  pagination: AbsencePaginationDto
}
```

**Output:** `PaginationResponseDto<AbsenceDto>`

**Business Rules:**

1. User must have `READ_ABSENCE` permission
2. Users with `OWN` scope only see their own absences and business-wide absences
3. Users with `ANY` scope see all absences within the tenant
4. Returns empty result if no permissions

**Process Flow:**

1. Validate permissions with fallback to empty result
2. Apply scope-based filtering
3. Execute paginated query
4. Return formatted pagination response

**Scope-based Filtering:**

* **OWN scope**: Filter to show only absences where user is a member or business-wide absences
* **ANY scope**: Show all absences within the tenant

**Caching:** Results can be cached using `AbsenceCacheKeys.GET_PAGED_ABSENCES`

***

### 4. UpdateAbsenceUseCase

**Purpose:** Updates an existing absence with validation and access control.

**Input:** `UpdateAbsenceUseCaseParams`

```typescript
{
  ctx: TenantActorContext,
  absenceId: string,
  update: AbsenceDto
}
```

**Output:** `void`

**Business Rules:**

1. User must have `EDIT_ABSENCE` permission
2. Absence must exist and be accessible
3. Users can only update absences they have access to
4. Domain validation rules apply (dates, assignments, etc.)

**Process Flow:**

1. Validate user permissions
2. Fetch existing absence
3. Check access rights using `assertActorHasAccessToAbsence`
4. Execute `UpdateAbsenceCommand`
5. Domain validation and persistence
6. Emit `AbsenceUpdatedMessage`
7. Clear cache entries

**Access Validation:**

```typescript
function assertActorHasAccessToAbsence(options: {
  actorMemberId: string;
  absence: AbsenceDto;
  scope: PermissionScopeEnum;
  action: string;
  logger: Logger;
}): void {
  const hasAccess = absence.entireBusiness || 
                    scope === PermissionScopeEnum.ANY ||
                    absence.members.some(m => m._id.toString() === actorMemberId);
  
  if (!hasAccess) {
    throw new AccessPermissionException(`You can only ${action} your own absences`);
  }
}
```

***

### 5. DeleteAbsenceUseCase

**Purpose:** Soft deletes an absence record by updating its state history.

**Input:** `DeleteAbsenceUseCaseParams`

```typescript
{
  ctx: TenantActorContext,
  absenceId: string
}
```

**Output:** `void`

**Business Rules:**

1. User must have `DELETE_ABSENCE` permission
2. Absence must exist and be accessible
3. Performs soft delete (state history update)
4. Cannot delete already deleted absences

**Process Flow:**

1. Validate user permissions
2. Fetch absence to verify existence and access
3. Check access rights
4. Execute `DeleteAbsenceCommand`
5. Update state history to 'deleted'
6. Emit `AbsenceDeletedMessage`
7. Clear cache entries

**Soft Delete Implementation:**

* Absences are not physically removed from the database
* State history is updated with `EntityStateEnum.deleted`
* Deleted absences are filtered out in queries
* Maintains audit trail and referential integrity

***

## Command Handlers

### CreateAbsenceHandler

**Responsibilities:**

1. Domain entity creation and validation
2. Database persistence
3. Event emission
4. Cache invalidation

**Key Operations:**

```typescript
// Create domain entity with validation
const absence: IAbsence = AbsenceMap.toPersistenceFromDto(command.absenceDto);
const absenceDomain = Absence.create(absence);

// Persist to database
const createdAbsence = await this.sharedUnitOfWork.absences.createItem(
  AbsenceMap.toPersistence(absenceDomain.getValue())
);

// Emit event
await this.messageQueueService.emitEvent(new AbsenceCreatedMessage({
  tenantId: command.tenantId,
  absence: AbsenceMap.toDto(createdAbsence)
}));
```

### UpdateAbsenceHandler

**Responsibilities:**

1. Domain validation of updates
2. Database update operation
3. Event emission
4. Cache invalidation

### DeleteAbsenceHandler

**Responsibilities:**

1. Fetch existing absence
2. Create domain object
3. Update state history
4. Persist changes
5. Event emission

***

## Query Handlers

### GetAbsenceByIdHandler

**Responsibilities:**

1. Database query execution
2. Result transformation
3. Cache management

**Caching Strategy:**

* Uses `@Cached` decorator with `AbsenceCacheKeys.GET_ABSENCE`
* Cache key includes tenant ID for multi-tenancy
* Automatically invalidated on updates/deletes

### GetPagedAbsenceHandler

**Responsibilities:**

1. Build query filters
2. Execute paginated database query
3. Transform results
4. Return pagination metadata

**Query Building:**

```typescript
// Build aggregation pipeline
const pipeline = [
  { $match: matchConditions },
  { $lookup: memberLookup },
  { $skip: (page - 1) * pageSize },
  { $limit: pageSize }
];
```

***

## Domain Validation Rules

### Absence Entity Validation

1. **ID Validation**: Must be valid ObjectId
2. **Date Validation**: Start date must be before end date
3. **Type Validation**: Must be valid AbsenceTypeEnum value
4. **Timezone Validation**: Must be provided
5. **Assignment Validation**: Must have either entireBusiness=true or at least one member

```typescript
// Core validation in Absence.create()
if (new Date(props.start) >= new Date(props.end)) {
  validationErrors.push('End date must be greater than start date');
}

if (!props.entireBusiness && props.members.length === 0) {
  validationErrors.push('Absence must have at least one member assigned or be for the entire business');
}
```

***

## Event-Driven Architecture

### Message Types

1. **AbsenceCreatedMessage**: Published when a new absence is created
2. **AbsenceUpdatedMessage**: Published when an absence is modified
3. **AbsenceDeletedMessage**: Published when an absence is deleted

### Event Consumers

**AbsenceConsumer (Panel Service):**

* Receives AMQP messages
* Transforms to WebSocket messages
* Broadcasts to connected clients

**AbsenceCacheConsumer (Cache Service):**

* Receives cache invalidation events
* Clears relevant cache entries
* Maintains cache consistency

### WebSocket Integration

Real-time notifications are sent to connected clients:

```typescript
// Example WebSocket message
const wsMessage: AbsenceCreatedWsMessage = {
  type: 'absence-created',
  payload: { absence: createdAbsence }
};

this.webSocket.sendMessage(tenantId, wsMessage);
```

***

## Permission Integration

### Permission Validation

All use cases integrate with the `PermissionValidatorUseCase`:

```typescript
const { scope } = await this.permissionValidatorUseCase.execute({
  actor: ctx.actor,
  requiredPermissions: [ModulePermissionEnum.CREATE_ABSENCE],
});
```

### Permission Scopes

* **PermissionScopeEnum.OWN**: Limited to user's own data
* **PermissionScopeEnum.ANY**: Access to all tenant data

### Access Patterns

1. **Create**: Users can only create absences they're assigned to (OWN scope)
2. **Read**: Users can view their absences + business-wide absences
3. **Update/Delete**: Users can only modify absences they have access to

***

## Cache Management

### Cache Keys

* `GET_ABSENCE`: Individual absence cache
* `GET_PAGED_ABSENCES`: Paginated list cache

### Invalidation Strategy

* **Create**: Invalidates `GET_PAGED_ABSENCES`
* **Update**: Invalidates both `GET_ABSENCE` and `GET_PAGED_ABSENCES`
* **Delete**: Invalidates both cache keys

### Cache Decorators

```typescript
@DeleteCache([
  AbsenceCacheKeys.GET_ABSENCE,
  AbsenceCacheKeys.GET_PAGED_ABSENCES
])
```

***

## Error Handling

### Exception Types

1. **AccessPermissionException**: Permission-related errors
2. **AbsenceDomainError**: Domain validation errors
3. **AbsenceCreationException**: Creation-specific errors
4. **AbsenceUpdationException**: Update-specific errors

### Error Logging

All use cases implement comprehensive error logging:

```typescript
catch (error) {
  this.logger.error('Error creating absence', error);
  throw error;
}
```

***

## Testing Considerations

### Unit Testing

* Mock dependencies (CommandBus, QueryBus, PermissionValidator)
* Test permission scenarios
* Validate business rules
* Test error conditions

### Integration Testing

* Test with real database
* Verify event emission
* Test cache behavior
* Validate multi-tenancy

### Example Test Scenarios

1. **Permission Tests**: Verify OWN vs ANY scope behavior
2. **Validation Tests**: Test domain validation rules
3. **Access Control Tests**: Verify users can only access appropriate data
4. **Event Tests**: Verify correct events are emitted
5. **Cache Tests**: Verify cache invalidation works correctly
