> 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/expense-management/interface.md).

# Interface Documentation

## Overview

This document outlines the interfaces, value objects, repositories, and domain contracts for the Expense Management module in the Bee O'clock panel service. The module follows Domain-Driven Design principles with clear separation between domain logic and infrastructure concerns.

## Domain Interfaces

### IExpense

**File:** `applications/panel.beeoclock/src/modules/expense/domain/interfaces/i.expense.ts`

The core expense aggregate root interface that defines the structure and behavior of expense entities within the domain.

```typescript
import {IBaseEntityBeeoClockTenantId} from '@beeoclock/common/core/entities/i.base.entity.beeoclock.tenant.id';
import {ExpenseValueRootValueObject} from '../value-objects/expense-value.value-object';
import {ExpenseItemValueObject} from '../value-objects/expense-item.value-object';

export interface IExpense extends IBaseEntityBeeoClockTenantId {
  totalValue: ExpenseValueRootValueObject;    // Total monetary value of the expense
  expensedAt: Date;                          // Date when the expense occurred
  description?: string;                      // Optional description of the expense
  items?: ExpenseItemValueObject[];          // Array of individual expense line items
}
```

**Key Characteristics:**

* Extends base entity with tenant ID for multi-tenancy support
* Uses value objects for monetary amounts and complex structures
* Supports itemized expenses with detailed breakdown
* Immutable expense timestamp for audit purposes

### IExpenseCategory

**File:** `applications/panel.beeoclock/src/modules/expense/domain/interfaces/i.expense-category.ts`

Interface defining expense categorization structure for organizing and filtering expenses.

```typescript
import {IBaseEntityBeeoClockTenantId} from '@beeoclock/common/core/entities/i.base.entity.beeoclock.tenant.id';

export interface IExpenseCategory extends IBaseEntityBeeoClockTenantId {
  name: string;                              // Unique category name within tenant
  description?: string;                      // Optional description of the category
}
```

**Key Characteristics:**

* Extends base entity with tenant ID for multi-tenancy support
* Simple name-based categorization system
* Optional description for detailed category information
* Designed for reusability across multiple expenses

### IExpenseRepository

**File:** `applications/panel.beeoclock/src/modules/expense/domain/interfaces/i.expense.repository.ts`

Repository interface defining data access patterns for expense entities with advanced querying capabilities.

```typescript
import {IBaseRepository} from '@beeoclock/common/core/persistance/interfaces/i.base.repository';
import {IExpense} from './i.expense';
import {PaginationRequestDto} from '@beeoclock/common/application/dto/pagination.request.dto';
import {PaginationResponseDto} from '@beeoclock/common/application/dto/pagination.response.dto';
import {ExpensePaginationDto} from '../../application/dtos/expense.pagination.dto';

export interface IExpenseRepository extends IBaseRepository<IExpense> {
  /**
   * Find expenses with advanced pagination and filtering capabilities
   * @param expensePaginationDto - Pagination parameters with expense-specific filters
   * @returns Promise resolving to paginated expense results
   */
  findManyByPagination(
    expensePaginationDto: ExpensePaginationDto
  ): Promise<PaginationResponseDto<IExpense>>;

  /**
   * Find an expense by ID with tenant and permission validation
   * @param id - Expense identifier
   * @returns Promise resolving to expense entity or null if not found
   */
  findOneById(id: string): Promise<IExpense | null>;

  /**
   * Create a new expense entity with validation
   * @param expense - Expense data to persist
   * @returns Promise resolving to created expense entity
   */
  create(expense: IExpense): Promise<IExpense>;

  /**
   * Update an existing expense entity
   * @param id - Expense identifier
   * @param expense - Updated expense data
   * @returns Promise resolving to updated expense entity
   */
  update(id: string, expense: Partial<IExpense>): Promise<IExpense>;

  /**
   * Delete an expense entity (soft delete)
   * @param id - Expense identifier
   * @returns Promise resolving to deletion result
   */
  deleteOneById(id: string): Promise<void>;
}
```

**Key Features:**

* Extends base repository with common CRUD operations
* Advanced pagination with expense-specific filtering
* Supports date range queries and category filtering
* Built-in tenant isolation for multi-tenant architecture
* Soft delete pattern for data integrity

### IExpenseCategoryRepository

**File:** `applications/panel.beeoclock/src/modules/expense/domain/interfaces/i.expense-category.repository.ts`

Repository interface for managing expense categories with pagination and bulk operations.

```typescript
import {IBaseRepository} from '@beeoclock/common/core/persistance/interfaces/i.base.repository';
import {IExpenseCategory} from './i.expense-category';
import {PaginationRequestDto} from '@beeoclock/common/application/dto/pagination.request.dto';
import {PaginationResponseDto} from '@beeoclock/common/application/dto/pagination.response.dto';

export interface IExpenseCategoryRepository extends IBaseRepository<IExpenseCategory> {
  /**
   * Find expense categories with pagination support
   * @param paginationRequestDto - Standard pagination parameters
   * @returns Promise resolving to paginated category results
   */
  findManyByPagination(
    paginationRequestDto: PaginationRequestDto
  ): Promise<PaginationResponseDto<IExpenseCategory>>;

  /**
   * Create multiple expense categories in bulk
   * @param expenseCategories - Array of expense categories to create
   * @returns Promise resolving to array of created categories
   */
  createMany(expenseCategories: IExpenseCategory[]): Promise<IExpenseCategory[]>;

  /**
   * Find an expense category by name within tenant scope
   * @param name - Category name to search for
   * @returns Promise resolving to category entity or null if not found
   */
  findOneByName(name: string): Promise<IExpenseCategory | null>;

  /**
   * Create a single expense category with validation
   * @param expenseCategory - Category data to persist
   * @returns Promise resolving to created category entity
   */
  create(expenseCategory: IExpenseCategory): Promise<IExpenseCategory>;
}
```

**Key Features:**

* Bulk operations for efficient category management
* Name-based lookup with tenant scoping
* Standard pagination for category listing
* Designed for high-frequency read operations

## Value Objects

### ExpenseValueRootValueObject

**File:** `applications/panel.beeoclock/src/modules/expense/domain/value-objects/expense-value.value-object.ts`

Value object representing monetary amounts with currency support.

```typescript
import {CurrencyCodeEnum} from '@beeoclock/common/core/enum/currency.code.enum';

export class ExpenseValueRootValueObject {
  /**
   * Monetary amount for the expense
   */
  public readonly amount: number;

  /**
   * Currency code for the expense amount
   */
  public readonly currency: CurrencyCodeEnum;

  constructor(amount: number, currency: CurrencyCodeEnum) {
    if (amount < 0) {
      throw new Error('Expense amount cannot be negative');
    }
    this.amount = amount;
    this.currency = currency;
  }

  /**
   * Calculate the sum of multiple expense values
   * @param values - Array of expense values to sum
   * @returns New ExpenseValueRootValueObject with total amount
   */
  static sum(values: ExpenseValueRootValueObject[]): ExpenseValueRootValueObject {
    if (values.length === 0) {
      throw new Error('Cannot sum empty array of expense values');
    }

    const currency = values[0].currency;
    if (!values.every(value => value.currency === currency)) {
      throw new Error('All expense values must have the same currency');
    }

    const totalAmount = values.reduce((sum, value) => sum + value.amount, 0);
    return new ExpenseValueRootValueObject(totalAmount, currency);
  }

  /**
   * Check if this value equals another expense value
   * @param other - Other expense value to compare
   * @returns Boolean indicating equality
   */
  equals(other: ExpenseValueRootValueObject): boolean {
    return this.amount === other.amount && this.currency === other.currency;
  }

  /**
   * Convert to plain object for serialization
   * @returns Plain object representation
   */
  toObject(): { amount: number; currency: CurrencyCodeEnum } {
    return {
      amount: this.amount,
      currency: this.currency
    };
  }
}
```

**Key Features:**

* Immutable value object with validation
* Currency consistency enforcement
* Arithmetic operations with currency validation
* Serialization support for persistence

### ExpenseItemValueObject

**File:** `applications/panel.beeoclock/src/modules/expense/domain/value-objects/expense-item.value-object.ts`

Value object representing individual line items within an expense.

```typescript
import {ExpenseValueRootValueObject} from './expense-value.value-object';
import {ExpenseSourceValueObject} from './expense-source.value-object';
import {IExpenseCategory} from '../interfaces/i.expense-category';

export class ExpenseItemValueObject {
  /**
   * Categories associated with this expense item
   */
  public readonly categories: IExpenseCategory[];

  /**
   * Monetary value of this specific line item
   */
  public readonly itemValue: ExpenseValueRootValueObject;

  /**
   * Description of this expense line item
   */
  public readonly description: string;

  /**
   * Source information for this expense item
   */
  public readonly source: ExpenseSourceValueObject;

  constructor(
    categories: IExpenseCategory[],
    itemValue: ExpenseValueRootValueObject,
    description: string,
    source: ExpenseSourceValueObject
  ) {
    if (!categories || categories.length === 0) {
      throw new Error('Expense item must have at least one category');
    }
    if (!description || description.trim().length === 0) {
      throw new Error('Expense item description cannot be empty');
    }

    this.categories = categories;
    this.itemValue = itemValue;
    this.description = description.trim();
    this.source = source;
  }

  /**
   * Check if this item belongs to a specific category
   * @param categoryName - Name of the category to check
   * @returns Boolean indicating category membership
   */
  hasCategory(categoryName: string): boolean {
    return this.categories.some(category => category.name === categoryName);
  }

  /**
   * Get all category names for this item
   * @returns Array of category names
   */
  getCategoryNames(): string[] {
    return this.categories.map(category => category.name);
  }

  /**
   * Convert to plain object for serialization
   * @returns Plain object representation
   */
  toObject(): any {
    return {
      categories: this.categories,
      itemValue: this.itemValue.toObject(),
      description: this.description,
      source: this.source.toObject()
    };
  }
}
```

**Key Features:**

* Encapsulates line item business logic
* Category validation and querying
* Integration with expense value and source objects
* Immutable design with validation

### ExpenseSourceValueObject

**File:** `applications/panel.beeoclock/src/modules/expense/domain/value-objects/expense-source.value-object.ts`

Value object representing the source or origin of an expense item.

```typescript
import {ExpenseSourceTypeEnum} from '../enums/expense-source-type.enum';

export class ExpenseSourceValueObject {
  /**
   * Identifier of the source entity
   */
  public readonly sourceId: string;

  /**
   * Type of the source entity
   */
  public readonly sourceType: ExpenseSourceTypeEnum;

  /**
   * Display name of the source entity
   */
  public readonly name?: string;

  /**
   * Optional description of the source entity
   */
  public readonly description?: string;

  constructor(
    sourceId: string,
    sourceType: ExpenseSourceTypeEnum,
    name?: string,
    description?: string
  ) {
    if (!sourceId || sourceId.trim().length === 0) {
      throw new Error('Source ID cannot be empty');
    }
    if (!Object.values(ExpenseSourceTypeEnum).includes(sourceType)) {
      throw new Error('Invalid expense source type');
    }

    this.sourceId = sourceId.trim();
    this.sourceType = sourceType;
    this.name = name?.trim();
    this.description = description?.trim();
  }

  /**
   * Check if this source is of a specific type
   * @param type - Source type to check
   * @returns Boolean indicating type match
   */
  isOfType(type: ExpenseSourceTypeEnum): boolean {
    return this.sourceType === type;
  }

  /**
   * Get display name or fallback to source ID
   * @returns Display name for the source
   */
  getDisplayName(): string {
    return this.name || this.sourceId;
  }

  /**
   * Convert to plain object for serialization
   * @returns Plain object representation
   */
  toObject(): any {
    return {
      sourceId: this.sourceId,
      sourceType: this.sourceType,
      name: this.name,
      description: this.description
    };
  }
}
```

**Key Features:**

* Encapsulates source entity information
* Type validation and checking
* Flexible naming with fallbacks
* Immutable design with validation

## Enumerations

### ExpenseSourceTypeEnum

**File:** `applications/panel.beeoclock/src/modules/expense/domain/enums/expense-source-type.enum.ts`

Enumeration defining the types of entities that can be associated with expense items.

```typescript
export enum ExpenseSourceTypeEnum {
  /**
   * Business member or employee
   */
  member = 'member',

  /**
   * Business resource or asset
   */
  resource = 'resource',

  /**
   * Product or inventory item
   */
  product = 'product',

  /**
   * External supplier or vendor
   */
  supplier = 'supplier'
}
```

**Usage Context:**

* **member**: Employee salaries, commissions, benefits
* **resource**: Equipment maintenance, facility costs, resource allocation
* **product**: Inventory costs, product-related expenses, cost of goods sold
* **supplier**: Vendor payments, external services, outsourced operations

## Data Transfer Objects (DTOs)

### ExpenseDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/dtos/expense.dto.ts`

Data transfer object for expense entities with validation decorators.

```typescript
import {IsOptional, IsString, IsArray, ValidateNested, IsISO8601, IsNotEmpty} from 'class-validator';
import {Type} from 'class-transformer';
import {BaseBeeoClockEntityDto} from '@beeoclock/common/application/dto/base.beeoclock.entity.dto';
import {ExpenseValueDto} from './expense-value.dto';
import {ExpenseItemDto} from './expense-item.dto';

export class ExpenseDto extends BaseBeeoClockEntityDto {
  @IsOptional()
  @IsString()
  public readonly object?: 'ExpenseDto' = 'ExpenseDto';

  @ValidateNested()
  @Type(() => ExpenseValueDto)
  public totalValue!: ExpenseValueDto;

  @IsISO8601()
  @IsNotEmpty()
  public expensedAt!: string;

  @IsOptional()
  @IsString()
  public description?: string;

  @IsOptional()
  @IsArray()
  @ValidateNested({each: true})
  @Type(() => ExpenseItemDto)
  public items?: ExpenseItemDto[];
}
```

### ExpenseValueDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/dtos/expense-value.dto.ts`

Data transfer object for monetary values with currency validation.

```typescript
import {IsEnum, IsNumber, IsOptional, IsPositive, IsString} from 'class-validator';
import {CurrencyCodeEnum} from '@beeoclock/common/core/enum/currency.code.enum';

export class ExpenseValueDto {
  @IsOptional()
  @IsString()
  public readonly object?: 'ExpenseValueDto' = 'ExpenseValueDto';

  @IsNumber({maxDecimalPlaces: 2})
  @IsPositive()
  public amount!: number;

  @IsEnum(CurrencyCodeEnum)
  public currency!: CurrencyCodeEnum;
}
```

### ExpenseItemDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/dtos/expense-item.dto.ts`

Data transfer object for expense line items with category and source validation.

```typescript
import {IsArray, IsString, ValidateNested, IsNotEmpty, IsOptional} from 'class-validator';
import {Type} from 'class-transformer';
import {ExpenseCategoryDto} from './expense-category.dto';
import {ExpenseValueDto} from './expense-value.dto';
import {ExpenseSourceDto} from './expense-source.dto';

export class ExpenseItemDto {
  @IsOptional()
  @IsString()
  public readonly object?: 'ExpenseLineItemDto' = 'ExpenseLineItemDto';

  @IsArray()
  @ValidateNested({each: true})
  @Type(() => ExpenseCategoryDto)
  public categories!: ExpenseCategoryDto[];

  @ValidateNested()
  @Type(() => ExpenseValueDto)
  public itemValue!: ExpenseValueDto;

  @IsString()
  @IsNotEmpty()
  public description!: string;

  @ValidateNested()
  @Type(() => ExpenseSourceDto)
  public source!: ExpenseSourceDto;
}
```

### ExpenseSourceDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/dtos/expense-source.dto.ts`

Data transfer object for expense source information with type validation.

```typescript
import {IsEnum, IsOptional, IsString, IsNotEmpty} from 'class-validator';
import {ExpenseSourceTypeEnum} from '../domain/enums/expense-source-type.enum';

export class ExpenseSourceDto {
  @IsOptional()
  @IsString()
  public readonly object?: 'ExpenseSourceDto' = 'ExpenseSourceDto';

  @IsString()
  @IsNotEmpty()
  public sourceId!: string;

  @IsEnum(ExpenseSourceTypeEnum)
  public sourceType!: ExpenseSourceTypeEnum;

  @IsOptional()
  @IsString()
  public name?: string;

  @IsOptional()
  @IsString()
  public description?: string;
}
```

### ExpenseCategoryDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/dtos/expense-category.dto.ts`

Data transfer object for expense categories with validation.

```typescript
import {IsOptional, IsString, IsNotEmpty} from 'class-validator';
import {BaseBeeoClockEntityDto} from '@beeoclock/common/application/dto/base.beeoclock.entity.dto';

export class ExpenseCategoryDto extends BaseBeeoClockEntityDto {
  @IsOptional()
  @IsString()
  public readonly object?: 'ExpenseCategoryDto' = 'ExpenseCategoryDto';

  @IsString()
  @IsNotEmpty()
  public name!: string;

  @IsOptional()
  @IsString()
  public description?: string;
}
```

### ExpensePaginationDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/dtos/expense.pagination.dto.ts`

Data transfer object for expense pagination with advanced filtering.

```typescript
import {IsOptional, IsString, IsArray, IsISO8601} from 'class-validator';
import {Transform} from 'class-transformer';
import {PaginationRequestDto} from '@beeoclock/common/application/dto/pagination.request.dto';

export class ExpensePaginationDto extends PaginationRequestDto {
  @IsOptional()
  @IsString()
  public phrase?: string;

  @IsOptional()
  @IsISO8601()
  public start?: string;

  @IsOptional()
  @IsISO8601()
  public end?: string;

  @IsOptional()
  @IsArray()
  @Transform(({value}) => Array.isArray(value) ? value : [value])
  public expenseCategories?: string[];
}
```

## Mappers

### ExpenseMapperToDto

**File:** `applications/panel.beeoclock/src/modules/expense/application/mappers/expense.mapper-to-dto.ts`

Mapper for converting domain entities to DTOs with proper type transformation.

```typescript
import {IExpense} from '../../domain/interfaces/i.expense';
import {ExpenseDto} from '../dtos/expense.dto';
import {ExpenseValueMapperToDto} from './expense-value.mapper-to-dto';
import {ExpenseItemMapperToDto} from './expense-item.mapper-to-dto';

export class ExpenseMapperToDto {
  /**
   * Convert expense domain entity to DTO
   * @param expense - Domain expense entity
   * @returns Expense DTO
   */
  static map(expense: IExpense): ExpenseDto {
    const dto = new ExpenseDto();
    
    dto._id = expense._id;
    dto.totalValue = ExpenseValueMapperToDto.map(expense.totalValue);
    dto.expensedAt = expense.expensedAt.toISOString();
    dto.description = expense.description;
    dto.items = expense.items?.map(item => ExpenseItemMapperToDto.map(item));
    dto.createdAt = expense.createdAt?.toISOString();
    dto.updatedAt = expense.updatedAt?.toISOString();

    return dto;
  }

  /**
   * Convert array of expense entities to DTOs
   * @param expenses - Array of domain expense entities
   * @returns Array of expense DTOs
   */
  static mapArray(expenses: IExpense[]): ExpenseDto[] {
    return expenses.map(expense => this.map(expense));
  }
}
```

### ExpenseMapperToDomain

**File:** `applications/panel.beeoclock/src/modules/expense/application/mappers/expense.mapper-to-domain.ts`

Mapper for converting DTOs to domain entities with validation.

```typescript
import {IExpense} from '../../domain/interfaces/i.expense';
import {ExpenseDto} from '../dtos/expense.dto';
import {ExpenseValueMapperToDomain} from './expense-value.mapper-to-domain';
import {ExpenseItemMapperToDomain} from './expense-item.mapper-to-domain';

export class ExpenseMapperToDomain {
  /**
   * Convert expense DTO to domain entity
   * @param dto - Expense DTO
   * @returns Domain expense entity
   */
  static map(dto: ExpenseDto): IExpense {
    return {
      _id: dto._id,
      totalValue: ExpenseValueMapperToDomain.map(dto.totalValue),
      expensedAt: new Date(dto.expensedAt),
      description: dto.description,
      items: dto.items?.map(item => ExpenseItemMapperToDomain.map(item)),
      createdAt: dto.createdAt ? new Date(dto.createdAt) : undefined,
      updatedAt: dto.updatedAt ? new Date(dto.updatedAt) : undefined,
      businessEntityId: dto.businessEntityId,
      tenantId: dto.tenantId
    };
  }

  /**
   * Convert array of expense DTOs to domain entities
   * @param dtos - Array of expense DTOs
   * @returns Array of domain expense entities
   */
  static mapArray(dtos: ExpenseDto[]): IExpense[] {
    return dtos.map(dto => this.map(dto));
  }
}
```

## Use Case Interfaces

### ICreateExpenseUseCase

Interface defining the contract for expense creation use cases.

```typescript
import {ExpenseDto} from '../dtos/expense.dto';

export interface ICreateExpenseUseCase {
  /**
   * Execute expense creation with validation and business rules
   * @param expenseDto - Expense data to create
   * @returns Promise resolving to created expense DTO
   */
  execute(expenseDto: ExpenseDto): Promise<ExpenseDto>;
}
```

### IGetPagedExpensesUseCase

Interface defining the contract for paginated expense retrieval.

```typescript
import {ExpensePaginationDto} from '../dtos/expense.pagination.dto';
import {PaginationResponseDto} from '@beeoclock/common/application/dto/pagination.response.dto';
import {ExpenseDto} from '../dtos/expense.dto';

export interface IGetPagedExpensesUseCase {
  /**
   * Execute paginated expense retrieval with filtering
   * @param pagination - Pagination and filter parameters
   * @returns Promise resolving to paginated expense results
   */
  execute(pagination: ExpensePaginationDto): Promise<PaginationResponseDto<ExpenseDto>>;
}
```

## Architecture Integration

### Dependency Injection

The expense management module integrates with the NestJS dependency injection system:

```typescript
// Repository tokens
export const EXPENSE_REPOSITORY_TOKEN = Symbol('IExpenseRepository');
export const EXPENSE_CATEGORY_REPOSITORY_TOKEN = Symbol('IExpenseCategoryRepository');

// Use case tokens
export const CREATE_EXPENSE_USE_CASE_TOKEN = Symbol('ICreateExpenseUseCase');
export const GET_PAGED_EXPENSES_USE_CASE_TOKEN = Symbol('IGetPagedExpensesUseCase');
```

### Module Configuration

Integration with the panel module's dependency injection container:

```typescript
@Module({
  providers: [
    {
      provide: EXPENSE_REPOSITORY_TOKEN,
      useClass: ExpenseRepository
    },
    {
      provide: EXPENSE_CATEGORY_REPOSITORY_TOKEN,
      useClass: ExpenseCategoryRepository
    },
    {
      provide: CREATE_EXPENSE_USE_CASE_TOKEN,
      useClass: CreateExpenseUseCase
    },
    {
      provide: GET_PAGED_EXPENSES_USE_CASE_TOKEN,
      useClass: GetPagedExpensesUseCase
    }
  ]
})
export class ExpenseModule {}
```

## Design Patterns

### Repository Pattern

* Abstract data access through repository interfaces
* Consistent querying and persistence operations
* Support for complex queries with filtering and pagination

### Value Object Pattern

* Immutable objects for monetary values and complex data
* Encapsulation of business rules and validation
* Type safety for financial calculations

### Domain-Driven Design

* Clear separation between domain logic and infrastructure
* Rich domain models with business behavior
* Aggregate roots for consistency boundaries

### Command Query Responsibility Segregation (CQRS)

* Separate interfaces for command and query operations
* Optimized data access patterns for different use cases
* Clear separation of concerns for read and write operations

## Validation Rules

### Domain Validation

* Expense amounts must be positive
* Currency consistency across expense items
* Required fields validation (amount, date, source)
* Category existence validation

### DTO Validation

* JSON schema validation using class-validator decorators
* Type conversion and transformation using class-transformer
* Custom validation rules for business-specific requirements

### Repository Validation

* Data integrity checks during persistence
* Constraint validation at the database level
* Foreign key integrity for related entities

## Error Handling

### Domain Exceptions

```typescript
// Value object validation errors
export class InvalidExpenseAmountError extends Error {
  constructor(amount: number) {
    super(`Invalid expense amount: ${amount}. Amount must be positive.`);
  }
}

// Business rule violations
export class CurrencyMismatchError extends Error {
  constructor(expected: string, actual: string) {
    super(`Currency mismatch: expected ${expected}, got ${actual}`);
  }
}
```

### Repository Exceptions

```typescript
// Data access errors
export class ExpenseNotFoundError extends Error {
  constructor(id: string) {
    super(`Expense with ID ${id} not found`);
  }
}

// Constraint violations
export class DuplicateExpenseCategoryError extends Error {
  constructor(name: string) {
    super(`Expense category with name '${name}' already exists`);
  }
}
```

## Testing Interfaces

### Mock Repositories

```typescript
export const createMockExpenseRepository = (): jest.Mocked<IExpenseRepository> => ({
  findManyByPagination: jest.fn(),
  findOneById: jest.fn(),
  create: jest.fn(),
  update: jest.fn(),
  deleteOneById: jest.fn(),
  // ... other base repository methods
});

export const createMockExpenseCategoryRepository = (): jest.Mocked<IExpenseCategoryRepository> => ({
  findManyByPagination: jest.fn(),
  createMany: jest.fn(),
  findOneByName: jest.fn(),
  create: jest.fn(),
  // ... other base repository methods
});
```

### Test Builders

```typescript
export class ExpenseTestBuilder {
  private expense: Partial<IExpense> = {};

  withTotalValue(amount: number, currency: CurrencyCodeEnum): this {
    this.expense.totalValue = new ExpenseValueRootValueObject(amount, currency);
    return this;
  }

  withExpenseDate(date: Date): this {
    this.expense.expensedAt = date;
    return this;
  }

  withDescription(description: string): this {
    this.expense.description = description;
    return this;
  }

  build(): IExpense {
    return {
      totalValue: this.expense.totalValue || new ExpenseValueRootValueObject(100, CurrencyCodeEnum.USD),
      expensedAt: this.expense.expensedAt || new Date(),
      description: this.expense.description,
      items: this.expense.items,
      ...this.expense
    } as IExpense;
  }
}
```

This interface documentation provides a comprehensive view of the expense management module's contracts, value objects, and domain structure, enabling developers to understand and work with the expense system effectively while maintaining consistency with domain-driven design principles.
