> For the complete documentation index, see [llms.txt](https://docs.flexbase.in/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flexbase.in/sample-references-domain/collection-app.md).

# Collection App

## 🎯 Application Overview

This document demonstrates how to implement a comprehensive collection management system using the Flexbase framework, featuring domain models, workflow states, debt tracking, payment processing, installment management, and enterprise-grade role-based access control. This system is designed to be **universally applicable** across different collection scenarios including loans, subscriptions, invoices, utilities, memberships, and rent.

***

## 📋 Business Requirements

### **Core Entities:**

* **Collection** - A debt obligation that needs to be collected (Loans, Invoices, Subscriptions, Dues)
* **Debtor** - Entity responsible for paying the debt (Customer, Borrower, Tenant, Subscriber)
* **Creditor** - Entity owed the debt (Lender, Company, Landlord, Service Provider)
* **Installment** - Scheduled payment due dates and amounts
* **Payment** - Actual payment received against installments
* **CollectionAgent** - Staff assigned to collect debts
* **CollectionCampaign** - Outbound campaigns (calls, emails, letters)
* **PaymentPlan** - Negotiated payment arrangements
* **WriteOff** - Debt cancellation or settlement
* **Reminder** - Automated payment reminders

### **Workflow Requirements:**

* **Debt Origination** - Creating collection records from various sources
* **Installment Generation** - Automatic or manual schedule creation
* **Payment Application** - Recording and matching payments to installments
* **Collection Management** - Agent assignment, campaign tracking
* **Payment Negotiation** - Creating flexible payment plans
* **Delinquency Handling** - Escalation workflows for overdue debts
* **Write-Off Management** - Bad debt cancellation process
* **Reporting & Analytics** - Collection performance metrics

### **Role-Based Access Control:**

* **Debtor** - View own debts and payment history
* **CollectionAgent** - Manage assigned collection accounts
* **CollectionManager** - Oversee all collections and campaigns
* **Finance** - Process payments, manage write-offs
* **Accounting** - Review financial reports
* **Admin** - Full system access
* **Legal** - Manage disputes and settlements

***

## 🎯 Business Scenario Adaptations

This collection system is designed to be universally applicable. Here are example adaptations:

| Business Scenario | Collection Type  | Debtor         | Creditor        | Example                    |
| ----------------- | ---------------- | -------------- | --------------- | -------------------------- |
| **Banking**       | Personal Loan    | Borrower       | Bank            | Auto loans, personal loans |
| **Banking**       | Mortgage         | Homeowner      | Bank            | Home mortgages             |
| **Retail**        | Layaway          | Customer       | Store           | Purchase plans             |
| **Healthcare**    | Medical Bill     | Patient        | Clinic/Hospital | Treatment bills            |
| **Education**     | Tuition          | Student/Parent | School          | Semester fees              |
| **Utilities**     | Utility Bill     | Customer       | Utility Company | Electricity, water         |
| **Property**      | Rent             | Tenant         | Landlord        | Monthly rent               |
| **Subscription**  | Membership       | Member         | Business        | Gym, streaming             |
| **Business**      | Invoice          | Client         | Vendor          | B2B invoices               |
| **Credit Cards**  | Credit Card Bill | Cardholder     | Bank            | Credit card debt           |

***

## 🧱 Core Collection Modules (In Depth)

### 1) Collections Module

#### CollectionsController Features

* CreateCollection (POST) → `Collection.CreateCollection()`
* UpdateCollection (PUT)
* AssignAgent (POST) → `Collection.AssignAgent()`
* PauseCollection (POST) → `Collection.Pause()`
* ResumeCollection (POST) → `Collection.Resume()`
* WriteOffCollection (POST) → `Collection.WriteOff()`
* SettleCollection (POST) → `Collection.Settle()`
* GetCollection(s) (GET)

#### Domain Object: Collection

* Workflow: `CollectionWorkflowState`
* States: Originated → Active → Delinquent → In Collections → Settled/WrittenOff
* Types: Loan, Invoice, Subscription, Rent, Utility
* RBAC:
  * Admin/Agent: Create, Update, Assign, Pause, WriteOff
  * Debtor: View own collections
  * Manager: Oversight and reporting

```csharp
[Table("Collections")]
[Index(nameof(DebtorId), Name="IX_Collections_Debtor")]
[Index(nameof(CreditorId), Name="IX_Collections_Creditor")]
[Index(nameof(CollectionType), Name="IX_Collections_Type")]
public partial class Collection : DomainModelBridge
{
    public CollectionWorkflowState CollectionState { get; protected set; } = new CollectionOriginated();
    
    public string CollectionType { get; protected set; } // Loan | Invoice | Subscription | Rent | Utility
    public string CollectionCode { get; protected set; } // Unique identifier
    
    public string DebtorId { get; protected set; } // FK → Debtor.Id
    public string CreditorId { get; protected set; } // FK → Creditor.Id
    
    public string? CollectionAgentId { get; protected set; } // Assigned collection agent
    public string? OriginalCreditorId { get; protected set; } // For sold/purchased debt
    
    public decimal PrincipalAmount { get; protected set; }
    public decimal InterestRate { get; protected set; }
    public decimal PenaltyRate { get; protected set; }
    public decimal TotalDue { get; protected set; }
    public decimal AmountPaid { get; protected set; }
    public decimal AmountOutstanding { get; protected set; }
    
    public DateTime OriginationDate { get; protected set; }
    public DateTime FirstDueDate { get; protected set; }
    public DateTime? FinalDueDate { get; protected set; }
    public DateTime? LastPaymentDate { get; protected set; }
    
    public int? InstallmentCount { get; protected set; } // Number of installments
    public int InstallmentFrequency { get; protected set; } // Days between installments
    
    public string Currency { get; protected set; } = "USD";
    public string TimeZoneId { get; protected set; } = "UTC";
    
    public string? PaymentPlanId { get; protected set; } // If on payment plan
    public PaymentPlan? PaymentPlan { get; protected set; }
    
    public string? Notes { get; protected set; }
    
    // Navigation properties
    public Debtor Debtor { get; protected set; }
    public ICollection<Installment> Installments { get; protected set; } = new List<Installment>();
    public ICollection<Payment> Payments { get; protected set; } = new List<Payment>();
    
    [BusinessMethod]
    public virtual Collection CreateCollection(CreateCollectionCommand cmd)
    {
        var role = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        if (!CollectionState.CanTransition(role, "Create")) throw new UnauthorizedAccessException();
        this.Convert(cmd.Dto); 
        this.SetAdded(cmd.Dto.GetGeneratedId()); 
        return this;
    }
    
    [BusinessMethod]
    public virtual Collection AssignAgent(AssignAgentCommand cmd)
    {
        CollectionAgentId = cmd.AgentId;
        CollectionState = CollectionState.AssignToCollections();
        this.SetModified();
        return this;
    }
}
```

***

### 2) Debtors Module

#### DebtorsController Features

* RegisterDebtor (POST) → `Debtor.RegisterDebtor()`
* UpdateProfile (PUT)
* Blacklist (POST) → `Debtor.Blacklist()`
* Whitelist (POST) → `Debtor.Whitelist()`

#### Domain Object: Debtor

* Workflow: `DebtorWorkflowState`
* States: Registered → Active → Blacklisted
* Profiles: Individual, Business

```csharp
[Table("Debtors")]
[Index(nameof(Email), Name="IX_Debtors_Email")]
[Index(nameof(Phone), Name="IX_Debtors_Phone")]
public partial class Debtor : DomainModelBridge
{
    public DebtorWorkflowState DebtorState { get; protected set; } = new DebtorRegistered();
    
    [Required] public string FirstName { get; protected set; }
    [Required] public string LastName { get; protected set; }
    [Required] public string Email { get; protected set; }
    public string Phone { get; protected set; }
    
    public string Address { get; protected set; }
    public string City { get; protected set; }
    public string State { get; protected set; }
    public string PostalCode { get; protected set; }
    public string Country { get; protected set; }
    
    public string? EmployerName { get; protected set; }
    public string? EmployerPhone { get; protected set; }
    
    public bool IsBlacklisted { get; protected set; }
    public string? BlacklistReason { get; protected set; }
    
    public string TimeZoneId { get; protected set; } = "UTC";
    
    // Navigation properties
    public ICollection<Collection> Collections { get; protected set; } = new List<Collection>();
}

public class DebtorWorkflowState : FlexState
{
    public virtual DebtorWorkflowState Activate() => this;
    public virtual DebtorWorkflowState Blacklist() => this;
    public virtual DebtorWorkflowState Whitelist() => this;
    public virtual bool CanTransition(string role, string action) => GetAllowedRoles(action).Contains(role);
    protected virtual string[] GetAllowedRoles(string action) => Array.Empty<string>();
}

public class DebtorRegistered : DebtorWorkflowState
{
    protected override string[] GetAllowedRoles(string action) => action switch
    {
        "Activate" => new[] { "CollectionAgent", "Admin" },
        _ => Array.Empty<string>()
    };
    public override DebtorWorkflowState Activate() => new DebtorActive();
}

public class DebtorActive : DebtorWorkflowState
{
    protected override string[] GetAllowedRoles(string action) => action switch
    {
        "Blacklist" => new[] { "CollectionAgent", "CollectionManager", "Admin" },
        _ => Array.Empty<string>()
    };
    public override DebtorWorkflowState Blacklist() => new DebtorBlacklisted();
}

public class DebtorBlacklisted : DebtorWorkflowState
{
    protected override string[] GetAllowedRoles(string action) => action switch
    {
        "Whitelist" => new[] { "CollectionManager", "Admin" },
        _ => Array.Empty<string>()
    };
    public override DebtorWorkflowState Whitelist() => new DebtorActive();
}
```

***

### 3) Installments Module

#### InstallmentsController Features

* GenerateInstallments (POST) → `Collection.GenerateInstallments()`
* UpdateInstallment (PUT)
* MarkDue (POST) → `Installment.MarkDue()`
* MarkOverdue (POST) → `Installment.MarkOverdue()`

#### Domain Object: Installment

* Workflow: `InstallmentWorkflowState` (Scheduled → Due → Paid → Overdue/Partial)
* Represents one payment due in a collection schedule

```csharp
[Table("Installments")]
[Index(nameof(CollectionId), Name="IX_Installments_Collection")]
[Index(nameof(DueDate), Name="IX_Installments_DueDate")]
public partial class Installment : DomainModelBridge
{
    public InstallmentWorkflowState InstallmentState { get; protected set; } = new InstallmentScheduled();
    
    public string CollectionId { get; protected set; }
    public Collection Collection { get; protected set; }
    
    public int InstallmentNumber { get; protected set; } // 1, 2, 3...
    
    public decimal PrincipalAmount { get; protected set; }
    public decimal InterestAmount { get; protected set; }
    public decimal PenaltyAmount { get; protected set; }
    public decimal TotalDue { get; protected set; }
    
    public DateTime DueDate { get; protected set; }
    public DateTime? PaidDate { get; protected set; }
    
    public decimal AmountPaid { get; protected set; }
    public decimal AmountOutstanding { get; protected set; }
    
    public string? PaymentMethod { get; protected set; }
    public string? TransactionReference { get; protected set; }
    
    public string TimeZoneId { get; protected set; }
    
    // Navigation properties
    public ICollection<Payment> Payments { get; protected set; } = new List<Payment>();
}

public class InstallmentWorkflowState : FlexState
{
    public virtual InstallmentWorkflowState MarkDue() => this;
    public virtual InstallmentWorkflowState MarkPaid() => this;
    public virtual InstallmentWorkflowState MarkOverdue() => this;
    public virtual InstallmentWorkflowState MarkPartial() => this;
}

public class InstallmentScheduled : InstallmentWorkflowState
{
    public override InstallmentWorkflowState MarkDue() => new InstallmentDue();
}

public class InstallmentDue : InstallmentWorkflowState
{
    public override InstallmentWorkflowState MarkPaid() => new InstallmentPaid();
    public override InstallmentWorkflowState MarkOverdue() => new InstallmentOverdue();
    public override InstallmentWorkflowState MarkPartial() => new InstallmentPartial();
}

public class InstallmentPaid : InstallmentWorkflowState { }
public class InstallmentPartial : InstallmentWorkflowState { }
public class InstallmentOverdue : InstallmentWorkflowState 
{
    public override InstallmentWorkflowState MarkPaid() => new InstallmentPaid();
    public override InstallmentWorkflowState MarkPartial() => new InstallmentPartial();
}
```

***

### 4) Payments Module

#### PaymentsController Features

* RecordPayment (POST) → `Payment.RecordPayment()`
* AllocatePayment (POST) → `Payment.AllocateToInstallment()`
* RefundPayment (POST) → `Payment.Refund()`
* GetPaymentHistory (GET)

#### Domain Object: Payment

* Workflow: `PaymentWorkflowState` (Recorded → Allocated → Confirmed/Refunded)
* Links payments to specific installments

```csharp
[Table("Payments")]
[Index(nameof(CollectionId), Name="IX_Payments_Collection")]
[Index(nameof(DebtorId), Name="IX_Payments_Debtor")]
public partial class Payment : DomainModelBridge
{
    public PaymentWorkflowState PaymentState { get; protected set; } = new PaymentRecorded();
    
    public string CollectionId { get; protected set; }
    public string DebtorId { get; protected set; }
    public string InstallmentId { get; protected set; } // Which installment this pays
    
    public decimal Amount { get; protected set; }
    public string PaymentMethod { get; protected set; } // Cash | Check | CreditCard | ACH | Wire
    public DateTime PaymentDate { get; protected set; }
    
    public string TransactionReference { get; protected set; }
    public string? CheckNumber { get; protected set; }
    public string? BankAccountLast4 { get; protected set; }
    
    public bool IsConfirmed { get; protected set; }
    public DateTime? ConfirmedDate { get; protected set; }
    
    public string? RefundReason { get; protected set; }
    public decimal? RefundAmount { get; protected set; }
    
    public string Notes { get; protected set; }
    
    // Navigation properties
    public Collection Collection { get; protected set; }
    public Debtor Debtor { get; protected set; }
    public Installment Installment { get; protected set; }
    
    [BusinessMethod]
    public virtual Payment RecordPayment(RecordPaymentCommand cmd)
    {
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        
        // Update installment
        var installment = _repo.FindAll<Installment>().FirstOrDefault(i => i.Id == cmd.Dto.InstallmentId);
        if (installment != null)
        {
            installment.AmountPaid += cmd.Dto.Amount;
            installment.AmountOutstanding = installment.TotalDue - installment.AmountPaid;
            
            if (installment.AmountOutstanding <= 0)
            {
                installment.PaymentState = new InstallmentPaid();
            }
            else
            {
                installment.PaymentState = new InstallmentPartial();
            }
        }
        
        return this;
    }
}

public class PaymentWorkflowState : FlexState
{
    public virtual PaymentWorkflowState Allocate() => this;
    public virtual PaymentWorkflowState Confirm() => this;
    public virtual PaymentWorkflowState Refund() => this;
}

public class PaymentRecorded : PaymentWorkflowState
{
    public override PaymentWorkflowState Allocate() => new PaymentAllocated();
    public override PaymentWorkflowState Refund() => new PaymentRefunded();
}

public class PaymentAllocated : PaymentWorkflowState
{
    public override PaymentWorkflowState Confirm() => new PaymentConfirmed();
    public override PaymentWorkflowState Refund() => new PaymentRefunded();
}

public class PaymentConfirmed : PaymentWorkflowState { }
public class PaymentRefunded : PaymentWorkflowState { }
```

***

### 5) Collection Campaigns Module

#### CampaignsController Features

* CreateCampaign (POST) → `CollectionCampaign.Create()`
* AssignContact (POST) → `CollectionCampaign.AssignContact()`
* RecordContact (POST) → `ContactRecord.Create()`
* CloseCampaign (POST) → `CollectionCampaign.Close()`

#### Domain Object: CollectionCampaign

* Tracks collection efforts and agent activities
* Maintains history of all contacts with debtor

```csharp
[Table("CollectionCampaigns")]
[Index(nameof(CollectionId), Name="IX_Campaigns_Collection")]
public partial class CollectionCampaign : DomainModelBridge
{
    public CampaignWorkflowState CampaignState { get; protected set; } = new CampaignActive();
    
    public string CollectionId { get; protected set; }
    public string? AgentId { get; protected set; } // Assigned agent
    
    public string CampaignType { get; protected set; } // Call | Email | Letter | Visit
    
    public DateTime StartDate { get; protected set; }
    public DateTime? EndDate { get; protected set; }
    
    public int ContactAttempts { get; protected set; }
    public int SuccessfulContacts { get; protected set; }
    public DateTime? LastContactDate { get; protected set; }
    
    public string? Notes { get; protected set; }
    
    // Navigation properties
    public Collection Collection { get; protected set; }
    public ICollection<ContactRecord> Contacts { get; protected set; } = new List<ContactRecord>();
}

[Table("ContactRecords")]
public partial class ContactRecord : DomainModelBridge
{
    public string CampaignId { get; protected set; }
    public string? AgentId { get; protected set; }
    
    public string ContactMethod { get; protected set; } // Call | Email | Letter | Visit
    public DateTime ContactDate { get; protected set; }
    
    public string ContactOutcome { get; protected set; } // NoAnswer | LeftMessage | Spoke | PromisedToPay | Refused
    public string ContactNotes { get; protected set; }
    
    public DateTime? NextFollowUpDate { get; protected set; }
    
    // Navigation properties
    public CollectionCampaign Campaign { get; protected set; }
}
```

***

### 6) Payment Plans Module

#### PaymentPlansController Features

* CreatePlan (POST) → `PaymentPlan.Create()`
* ProposePlan (POST) → `PaymentPlan.Propose()`
* AcceptPlan (POST) → `PaymentPlan.Accept()`
* RejectPlan (POST) → `PaymentPlan.Reject()`

#### Domain Object: PaymentPlan

* Negotiated arrangements to pay debt over time
* Different from original payment schedule

```csharp
[Table("PaymentPlans")]
[Index(nameof(CollectionId), Name="IX_PaymentPlans_Collection")]
public partial class PaymentPlan : DomainModelBridge
{
    public PaymentPlanWorkflowState PlanState { get; protected set; } = new PlanProposed();
    
    public string CollectionId { get; protected set; }
    public string? ProposedByAgentId { get; protected set; }
    
    public decimal RevisedTotalAmount { get; protected set; }
    public decimal? RevisedInterestRate { get; protected set; }
    public decimal? RevisedPenaltyRate { get; protected set; }
    
    public int RevisedInstallmentCount { get; protected set; }
    public int RevisedFrequency { get; protected set; }
    
    public DateTime? ProposedDate { get; protected set; }
    public DateTime? AcceptedDate { get; protected set; }
    public DateTime? RejectedDate { get; protected set; }
    
    public string? RejectionReason { get; protected set; }
    public string? PlanNotes { get; protected set; }
    
    // Navigation properties
    public Collection Collection { get; protected set; }
    public ICollection<PaymentPlanInstallment> Installments { get; protected set; } = new List<PaymentPlanInstallment>();
}

public class PaymentPlanWorkflowState : FlexState
{
    public virtual PaymentPlanWorkflowState Propose() => this;
    public virtual PaymentPlanWorkflowState Accept() => this;
    public virtual PaymentPlanWorkflowState Reject() => this;
}

public class PlanProposed : PaymentPlanWorkflowState
{
    public override PaymentPlanWorkflowState Accept() => new PlanAccepted();
    public override PaymentPlanWorkflowState Reject() => new PlanRejected();
}

public class PlanAccepted : PaymentPlanWorkflowState { }
public class PlanRejected : PaymentPlanWorkflowState 
{
    public override PaymentPlanWorkflowState Propose() => new PlanProposed();
}
```

***

## 📊 Collection Tracking (Audit Trail)

Purpose: Track all changes to collection status, payments, and agent activities with full audit history.

### Tracking Events

```csharp
[Table("CollectionTracking")]
[Index(nameof(CollectionId), nameof(EventDate), Name="IX_Tracking_Collection_Date")]
public class CollectionTracking : DomainModelBridge
{
    public string CollectionId { get; set; }
    public string EventType { get; set; } // Created | Paid | Contacted | Overdue | WrittenOff | Settled
    public DateTime EventDate { get; set; }
    public string? AgentId { get; set; }
    public string? EventDescription { get; set; }
    public string? PreviousState { get; set; }
    public string? NewState { get; set; }
    public decimal? AmountBefore { get; set; }
    public decimal? AmountAfter { get; set; }
}
```

***

## 📚 Collection Calendar (Due Date Tracking)

Purpose: Track all payment due dates and overdue accounts with fast queries.

Data sources:

* `Installment` (Scheduled → Due → Paid/Overdue)
* Payment deadlines by collection/agent
* Holiday/cutoff date tracking

Recommended schema:

```csharp
[Table("CollectionCalendar")]
[Index(nameof(CollectionId), nameof(DueDate), Name="IX_Calendar_Collection_Due")]
[Index(nameof(DebtorId), Name="IX_Calendar_Debtor")]
public class CollectionCalendar : DomainModelBridge
{
    public string CollectionId { get; set; }
    public string DebtorId { get; set; }
    public string InstallmentId { get; set; }
    
    public DateTime DueDate { get; set; }
    public decimal AmountDue { get; set; }
    
    public string CalendarType { get; set; } // Installment | PaymentPlan | Reminder
    public string CalendarStatus { get; set; } // Scheduled | Due | Overdue | Paid
    
    public string? AgentId { get; set; }
    public string? CampaignId { get; set; }
    
    // Navigation
    public Collection Collection { get; set; }
    public Installment Installment { get; set; }
}
```

***

## 📈 Collection Analytics (Performance Metrics)

### Summary Dashboard

Purpose: Executive/operations view with daily/weekly metrics across agents, collection types, and payment performance.

New projection table:

```csharp
[Table("CollectionSummary")]
[Index(nameof(AgentId), nameof(Date), Name="IX_Summary_Agent_Date")]
public class CollectionSummary : DomainModelBridge
{
    public string? AgentId { get; set; }
    public string CollectionType { get; set; }
    public DateTime Date { get; set; }
    
    public int TotalCollections { get; set; }
    public decimal TotalAmountDue { get; set; }
    public decimal TotalAmountCollected { get; set; }
    public decimal CollectionRate { get; set; } // Percentage
    
    public int OverdueCount { get; set; }
    public decimal OverdueAmount { get; set; }
    public int WriteOffCount { get; set; }
    public decimal WriteOffAmount { get; set; }
    
    public int ContactsMade { get; set; }
    public int SuccessfulContacts { get; set; }
}
```

***

## 💰 Loan Collection Example (Detailed)

### Scenario: Personal Auto Loan

**Initial Setup:**

```csharp
var loanCollection = new Collection()
{
    CollectionType = "Loan",
    CollectionCode = "AUTO-2024-001234",
    DebtorId = "John.Doe",
    CreditorId = "ABC_BANK",
    PrincipalAmount = 25000.00m,
    InterestRate = 5.50m, // Annual rate
    PenaltyRate = 2.00m, // Monthly penalty for late payment
    OriginationDate = new DateTime(2024, 1, 15),
    FirstDueDate = new DateTime(2024, 2, 15),
    FinalDueDate = new DateTime(2027, 1, 15),
    InstallmentCount = 60, // 5 years monthly
    InstallmentFrequency = 30, // Monthly
    Currency = "USD",
    TimeZoneId = "America/New_York",
    CollectionState = new CollectionActive()
};
```

**Installment Generation:**

```csharp
public List<Installment> GenerateLoanInstallments(Collection loan, ILogger logger)
{
    var installments = new List<Installment>();
    var principalPerMonth = loan.PrincipalAmount / loan.InstallmentCount;
    var monthlyRate = loan.InterestRate / 100 / 12; // Convert annual to monthly
    
    for (int i = 1; i <= loan.InstallmentCount; i++)
    {
        var dueDate = loan.FirstDueDate.AddMonths(i - 1);
        var remainingPrincipal = loan.PrincipalAmount - (principalPerMonth * (i - 1));
        var interestAmount = remainingPrincipal * monthlyRate;
        
        installments.Add(new Installment()
        {
            CollectionId = loan.Id,
            InstallmentNumber = i,
            PrincipalAmount = principalPerMonth,
            InterestAmount = interestAmount,
            TotalDue = principalPerMonth + interestAmount,
            DueDate = dueDate,
            InstallmentState = new InstallmentScheduled()
        });
    }
    
    return installments;
}
```

**Payment Application:**

```csharp
public void RecordPaymentForLoan(string collectionId, string installmentId, decimal amount, string paymentMethod)
{
    var payment = new Payment()
    {
        CollectionId = collectionId,
        InstallmentId = installmentId,
        Amount = amount,
        PaymentMethod = paymentMethod,
        PaymentDate = DateTime.UtcNow,
        PaymentState = new PaymentRecorded()
    };
    
    // Update installment
    var installment = FindInstallment(installmentId);
    installment.AmountPaid += amount;
    
    if (installment.AmountOutstanding <= 0)
    {
        installment.InstallmentState = new InstallmentPaid();
        installment.PaidDate = DateTime.UtcNow;
    }
    else
    {
        installment.InstallmentState = new InstallmentPartial();
    }
    
    // Update collection
    var collection = FindCollection(collectionId);
    collection.AmountPaid += amount;
    collection.AmountOutstanding = collection.TotalDue - collection.AmountPaid;
}
```

**Overdue Handling:**

```csharp
public void MarkOverdueInstallments()
{
    var overdueInstallments = _repo.FindAll<Installment>()
        .Where(i => i.DueDate < DateTime.UtcNow && i.InstallmentState is InstallmentDue)
        .ToList();
    
    foreach (var installment in overdueInstallments)
    {
        // Calculate penalty
        var daysOverdue = (DateTime.UtcNow - installment.DueDate).Days;
        var penalty = installment.TotalDue * (installment.Collection.PenaltyRate / 100 / 30) * daysOverdue;
        
        installment.PenaltyAmount = penalty;
        installment.TotalDue += penalty;
        installment.InstallmentState = new InstallmentOverdue();
        
        // Update collection state
        if (installment.Collection.CollectionState is CollectionActive)
        {
            installment.Collection.CollectionState = new CollectionDelinquent();
        }
    }
}
```

**Collection Campaign:**

```csharp
public void CreateCollectionCampaign(string collectionId, string agentId)
{
    var campaign = new CollectionCampaign()
    {
        CollectionId = collectionId,
        AgentId = agentId,
        CampaignType = "Call",
        StartDate = DateTime.UtcNow,
        CampaignState = new CampaignActive()
    };
    
    _repo.Insert(campaign);
}

public void RecordContactAttempt(string campaignId, string agentId, string outcome, string notes)
{
    var contact = new ContactRecord()
    {
        CampaignId = campaignId,
        AgentId = agentId,
        ContactMethod = "PhoneCall",
        ContactDate = DateTime.UtcNow,
        ContactOutcome = outcome,
        ContactNotes = notes
    };
    
    _repo.Insert(contact);
    
    // Update campaign
    var campaign = FindCampaign(campaignId);
    campaign.ContactAttempts++;
    if (outcome == "Spoke")
    {
        campaign.SuccessfulContacts++;
    }
    campaign.LastContactDate = DateTime.UtcNow;
}
```

**Payment Plan Negotiation:**

```csharp
public PaymentPlan ProposePaymentPlan(string collectionId, decimal newMonthlyPayment, int newInstallmentCount)
{
    var collection = FindCollection(collectionId);
    var revisedTotal = newMonthlyPayment * newInstallmentCount;
    
    var plan = new PaymentPlan()
    {
        CollectionId = collectionId,
        ProposedByAgentId = _currentAgentId,
        RevisedTotalAmount = revisedTotal,
        RevisedInstallmentCount = newInstallmentCount,
        RevisedFrequency = 30,
        ProposedDate = DateTime.UtcNow,
        PlanState = new PlanProposed()
    };
    
    _repo.Insert(plan);
    return plan;
}

public void AcceptPaymentPlan(string planId)
{
    var plan = FindPaymentPlan(planId);
    plan.PlanState = new PlanAccepted();
    plan.AcceptedDate = DateTime.UtcNow;
    
    // Update collection with new terms
    var collection = FindCollection(plan.CollectionId);
    collection.TotalDue = plan.RevisedTotalAmount;
    collection.InstallmentCount = plan.RevisedInstallmentCount;
    collection.PaymentPlanId = planId;
    
    // Regenerate installments with new terms
    RegenerateInstallments(collection);
}
```

***

## 🎯 Key Implementation Notes

### Timezone Handling

* All due dates stored in UTC ticks for fast queries
* Local dates maintained via `TimeZoneId` for display
* Holiday calendars configured per creditor timezone

### RBAC Examples

```csharp
// Check if user can manage collections
bool CanManageCollections(string userId, string collectionId)
{
    var collection = FindCollection(collectionId);
    var isOwnCollection = collection.AgentId == userId;
    var userRole = GetUserRole(userId);
    
    return userRole == "CollectionManager" || userRole == "Admin" || 
           (userRole == "CollectionAgent" && isOwnCollection);
}

// Check if debtor can view collection
bool CanDebtorViewCollection(string userId, string collectionId)
{
    var collection = FindCollection(collectionId);
    return collection.DebtorId == userId;
}
```

### Reporting Endpoints

```csharp
[HttpGet("/collections/agent/{agentId}/performance")]
public CollectionPerformanceDto GetAgentPerformance(string agentId, DateTime startDate, DateTime endDate)
{
    // Aggregate payments, contacts, settlement rates
}

[HttpGet("/collections/delinquent")]
public List<CollectionDto> GetDelinquentCollections(int daysOverdue)
{
    // Find collections with overdue installments
}

[HttpGet("/collections/forecast")]
public CollectionForecastDto GetForecastByDateRange(DateTime startDate, DateTime endDate)
{
    // Projected collections based on installments and historical rates
}
```

***

This design provides a flexible, enterprise-grade collection management system applicable to any debt collection scenario while maintaining clear workflows, role-based access, and comprehensive tracking.
