# Phase 3 Execution Plan: Modular Finance Extraction


# Islamic Sunday School Alignment Update

Phase 3 finance now treats the extension as **Islamic Sunday School**.

`SchoolCore\Finance` owns financial correctness: invoices, payments, refunds, allocation, files, transactions, audit, idempotency, and balance calculation.

`IslamicSundaySchool\Finance` may customize labels and policies for:

```text
tuition
registration fees
supply/book fees
Qur'an/Arabic/Islamic studies program fees
sibling discounts
need-based scholarships
sadaqah/zakat-sponsored scholarships, if the organization supports them
masjid-family reporting labels
```

Do not put religious finance labels into core contracts. Core should use neutral concepts like charge, payment, discount, scholarship, donation classification, and allocation policy. The Islamic Sunday School layer maps those to local terminology.

---
## 1. Purpose

Phase 3 extracts finance into a reusable `SchoolCore` module that can support any school system while allowing Islamic Sunday School-specific finance behavior through policies and extension services.

The finance module must own the invariant parts of school finance:

- invoices
- invoice items
- charges
- discounts
- payments
- refunds
- balances
- payment files
- financial audit logs
- payment allocation
- transaction safety
- authorization boundaries

Islamic Sunday School may customize labels, tuition rules, donation classification, family/group billing behavior, scholarships, and reporting views, but it must not alter core payment correctness.

The goal is not to make finance merely “cleaner.” The goal is to make it hard to corrupt money, leak files, or fork business logic across controllers like a spreadsheet that learned HTTP and got overconfident.

---

## 2. Required Dependency Direction

Finance must follow the modular platform boundary:

```text
App\Domain\IslamicIslamicSundaySchool\Finance
        ↓ depends on
App\Domain\SchoolCore\Finance
        ↓ depends on
App\Domain\SchoolCore\Shared
```

Forbidden dependencies:

```text
SchoolCore\Finance -> IslamicSundaySchool\*
SchoolCore\Finance -> Http\Request
SchoolCore\Finance -> auth()
SchoolCore\Finance -> controller classes
SchoolCore\Finance -> Islamic Sunday School vocabulary
SchoolCore\Finance -> direct unscoped school queries
```

Allowed dependencies:

```text
IslamicSundaySchool\Finance -> SchoolCore\Finance contracts
Http\Controllers -> finance contracts
Jobs -> finance contracts
Finance services -> SchoolContext
Finance services -> shared audit/transaction/file contracts
```

---

## 3. Target Namespace Structure

Create the finance module under `SchoolCore`:

```text
app/
  Domain/
    SchoolCore/
      Finance/
        Contracts/
          InvoiceServiceContract.php
          PaymentServiceContract.php
          RefundServiceContract.php
          ChargeServiceContract.php
          PaymentAllocationPolicyContract.php
          TuitionPolicyContract.php
          FinanceAuditLoggerContract.php
          FinanceFileServiceContract.php
        DTO/
          CreateInvoiceData.php
          InvoiceItemData.php
          RecordPaymentData.php
          EditPaymentData.php
          RefundPaymentData.php
          PaymentFileData.php
          FinanceActorData.php
          MoneyData.php
        Enums/
          InvoiceStatus.php
          PaymentStatus.php
          PaymentMethod.php
          LedgerDirection.php
          FinanceAuditAction.php
        Exceptions/
          FinanceException.php
          InvoiceNotFoundException.php
          PaymentNotFoundException.php
          UnauthorizedFinanceAccessException.php
          InvalidPaymentAmountException.php
          InvoiceOverpaymentException.php
          FinanceConcurrencyException.php
        Services/
          InvoiceService.php
          PaymentService.php
          RefundService.php
          ChargeService.php
          BalanceCalculator.php
          PaymentFileService.php
          FinanceAuditLogger.php
        Policies/
          DefaultPaymentAllocationPolicy.php
          DefaultTuitionPolicy.php
        Repositories/
          InvoiceRepository.php
          PaymentRepository.php
          FinanceReadRepository.php
        Support/
          Money.php
          FinanceTransactionRunner.php
          FinanceIdempotencyKey.php
          PaymentFileLocator.php
```

Islamic Sunday School-specific finance goes here:

```text
app/
  Domain/
    IslamicSundaySchool/
      Finance/
        Policies/
          IslamicSundaySchoolTuitionPolicy.php
          IslamicSundaySchoolPaymentAllocationPolicy.php
        Reports/
          IslamicSundaySchoolFinanceReportBuilder.php
        Providers/
          IslamicSundaySchoolFinanceServiceProvider.php
```

---

## 4. Core Design Rules

### 4.1 Finance services must require `SchoolContext`

Every public finance service method must receive or resolve `SchoolContext`.

Example:

```php
public function recordPayment(
    SchoolContext $context,
    RecordPaymentData $data
): PaymentResult;
```

A finance operation without context is invalid.

### 4.2 Money must use fixed precision

No float math in finance service logic.

Allowed:

- integer cents
- fixed decimal string
- database decimal with explicit rounding strategy
- dedicated `Money` value object

Forbidden:

```php
$amount = (float) $request->amount;
$total += $amount;
```

That is not financial software. That is numerology with database writes.

### 4.3 Every mutation must be transactional

Required transactional operations:

- invoice creation
- invoice item update
- charge application
- discount application
- payment recording
- payment editing
- payment deletion/reversal
- refund creation
- invoice recalculation
- payment file association
- financial audit log creation

### 4.4 Row locking is required for invoice/payment mutation

Any mutation that changes invoice balance or payment amount must lock the relevant records.

Required pattern:

```php
DB::transaction(function () use ($context, $invoiceId, $data) {
    $invoice = Invoice::query()
        ->where('school_id', $context->schoolId())
        ->whereKey($invoiceId)
        ->lockForUpdate()
        ->firstOrFail();

    // mutate payment/invoice safely here
});
```

### 4.5 Controllers must not calculate balances

Controllers may:

- validate request
- authorize route
- build DTO
- call finance contract
- return resource

Controllers may not:

- calculate invoice totals
- calculate remaining balance
- determine payment allocation
- decide finance status
- serve files by arbitrary filename
- update invoice after payment manually

---

## 5. Core Contracts

### 5.1 `InvoiceServiceContract`

```php
namespace App\Domain\SchoolCore\Finance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Finance\DTO\CreateInvoiceData;

interface InvoiceServiceContract
{
    public function createInvoice(SchoolContext $context, CreateInvoiceData $data): InvoiceResult;

    public function recalculateInvoice(SchoolContext $context, int $invoiceId): InvoiceBalanceResult;

    public function voidInvoice(SchoolContext $context, int $invoiceId, string $reason): InvoiceResult;

    public function getInvoice(SchoolContext $context, int $invoiceId): InvoiceView;
}
```

### 5.2 `PaymentServiceContract`

```php
namespace App\Domain\SchoolCore\Finance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Finance\DTO\RecordPaymentData;
use App\Domain\SchoolCore\Finance\DTO\EditPaymentData;

interface PaymentServiceContract
{
    public function recordPayment(SchoolContext $context, RecordPaymentData $data): PaymentResult;

    public function editPayment(SchoolContext $context, int $paymentId, EditPaymentData $data): PaymentResult;

    public function reversePayment(SchoolContext $context, int $paymentId, string $reason): PaymentResult;

    public function getPayment(SchoolContext $context, int $paymentId): PaymentView;
}
```

### 5.3 `RefundServiceContract`

```php
namespace App\Domain\SchoolCore\Finance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use App\Domain\SchoolCore\Finance\DTO\RefundPaymentData;

interface RefundServiceContract
{
    public function refundPayment(SchoolContext $context, RefundPaymentData $data): RefundResult;
}
```

### 5.4 `PaymentAllocationPolicyContract`

```php
namespace App\Domain\SchoolCore\Finance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;

interface PaymentAllocationPolicyContract
{
    public function allocate(
        SchoolContext $context,
        InvoiceView $invoice,
        Money $paymentAmount
    ): PaymentAllocationResult;
}
```

Islamic Sunday School may override this contract to support family-level rules, scholarships, donations, or program-specific payment behavior.

### 5.5 `TuitionPolicyContract`

```php
namespace App\Domain\SchoolCore\Finance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;

interface TuitionPolicyContract
{
    public function calculateExpectedCharges(
        SchoolContext $context,
        StudentFinanceProfile $student
    ): ChargeCalculationResult;
}
```

### 5.6 `FinanceFileServiceContract`

```php
namespace App\Domain\SchoolCore\Finance\Contracts;

use App\Domain\SchoolCore\Shared\Context\SchoolContext;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

interface FinanceFileServiceContract
{
    public function attachPaymentFile(
        SchoolContext $context,
        int $paymentId,
        PaymentFileData $file
    ): PaymentFileResult;

    public function downloadPaymentFile(
        SchoolContext $context,
        int $paymentId,
        string $mode
    ): BinaryFileResponse;
}
```

Critical rule: payment files are accessed by payment ID or file record ID, never by arbitrary filename.

---

## 6. DTO Requirements

Create DTOs for finance inputs. Do not pass raw request arrays into finance services.

### 6.1 `RecordPaymentData`

Fields:

```text
invoice_id
student_id, nullable
guardian_id, nullable
amount
currency
payment_method
payment_date
reference_number, nullable
notes, nullable
idempotency_key, nullable
uploaded_file, nullable
metadata, nullable
```

Validation belongs in FormRequest, but DTO construction must still guard required invariants.

### 6.2 `EditPaymentData`

Fields:

```text
amount, nullable
payment_method, nullable
payment_date, nullable
reference_number, nullable
notes, nullable
replacement_file, nullable
edit_reason
metadata, nullable
```

`edit_reason` is required for auditability.

### 6.3 `MoneyData`

Fields:

```text
amount_minor
currency
scale
```

Example:

```text
amount_minor = 12500
currency = USD
scale = 2
```

This represents `$125.00`.

---

## 7. Payment File Access Hardening

Current risk: file access based on filename can allow unauthorized users to guess or retrieve files if route authorization is incomplete.

New rule:

```text
No finance file download may accept only a filename.
```

Target route:

```text
GET /api/finance/payments/{payment}/file
```

Required flow:

```text
1. Resolve SchoolContext.
2. Load payment scoped to school_id.
3. Authorize actor against payment/invoice/student/guardian.
4. Resolve file from payment record.
5. Confirm file path belongs to configured finance storage disk/path.
6. Log access.
7. Stream/download file.
```

Forbidden flow:

```text
1. Accept filename.
2. basename(filename).
3. Search upload folders.
4. Download first matching file.
```

Basename prevents traversal. It does not prove authorization. This is apparently a distinction computers need humans to explain.

---

## 8. Transaction and Locking Strategy

### 8.1 Payment recording

Required transaction:

```text
BEGIN
  lock invoice
  validate invoice belongs to school
  validate actor authorization
  validate payment amount
  create payment
  attach payment file, if provided
  allocate payment
  recalculate invoice
  write audit log
COMMIT
```

### 8.2 Payment editing

Required transaction:

```text
BEGIN
  lock payment
  lock invoice
  capture before state
  validate actor authorization
  validate new amount/method/date
  update payment
  reallocate payment if needed
  recalculate invoice
  write audit log with before/after
COMMIT
```

### 8.3 Payment reversal

Required transaction:

```text
BEGIN
  lock payment
  lock invoice
  mark payment reversed
  recalculate invoice
  write audit log
COMMIT
```

### 8.4 Refund

Required transaction:

```text
BEGIN
  lock payment
  lock invoice
  validate refundable amount
  create refund record
  update payment refunded amount/status
  recalculate invoice
  write audit log
COMMIT
```

---

## 9. Audit Logging Requirements

Every finance mutation must create a finance audit event.

Required audit fields:

```text
school_id
academic_year_id
term_id, nullable
actor_user_id
actor_role_snapshot
action
entity_type
entity_id
before_json, nullable
after_json, nullable
reason, nullable
request_id
ip_address, nullable
user_agent, nullable
idempotency_key, nullable
created_at
```

Required actions:

```text
invoice.created
invoice.updated
invoice.voided
invoice.recalculated
payment.recorded
payment.edited
payment.reversed
payment.file_attached
payment.file_downloaded
refund.created
charge.applied
discount.applied
```

Audit logs are append-only. Do not update old audit rows unless the project enjoys inventing evidence after the fact.

---

## 10. Idempotency Requirements

Payment mutations must support idempotency.

Minimum implementation:

```text
idempotency_key
actor_user_id
school_id
operation
payload_hash
result_entity_type
result_entity_id
created_at
expires_at
```

Behavior:

```text
- Same key + same operation + same payload returns original result.
- Same key + same operation + different payload fails.
- Expired keys may be purged.
- Missing idempotency key is allowed for legacy endpoints during transition, but new clients should send one.
```

Apply first to:

```text
- record payment
- edit payment
- refund payment
```

---

## 11. Extension Model for Islamic Sunday School

Islamic Sunday School finance must extend the core through policies and adapters, not by modifying core services.

### 11.1 Islamic Sunday School allowed customizations

```text
- family-based billing labels
- donation classification
- ministry scholarship logic
- Islamic Sunday School-specific tuition plans
- custom discount labels
- custom finance dashboards
- masjid/family reports
```

### 11.2 Islamic Sunday School forbidden customizations

```text
- bypassing invoice row locks
- bypassing payment audit logs
- downloading files by filename
- calculating balances in controllers
- writing payments without SchoolContext
- storing Islamic Sunday School terms inside core contracts
```

### 11.3 Binding example

```php
$this->app->bind(
    TuitionPolicyContract::class,
    IslamicSundaySchoolTuitionPolicy::class
);

$this->app->bind(
    PaymentAllocationPolicyContract::class,
    IslamicSundaySchoolPaymentAllocationPolicy::class
);
```

---

## 12. Migration Strategy

### 12.1 Do not rewrite all finance at once

Use vertical slices.

Recommended order:

```text
1. Payment file access
2. Payment recording
3. Payment editing
4. Invoice recalculation
5. Refunds/reversals
6. Discounts/charges
7. Reports
```

Why this order:

- File access is a security boundary.
- Payment recording/editing affects money.
- Invoice recalculation must become single-source-of-truth.
- Refunds and discounts build on stable payment/invoice primitives.
- Reports should consume stable finance read models last.

### 12.2 Compatibility layer

Existing controllers may remain temporarily, but must delegate to new contracts.

Example:

```text
PaymentManualController
  -> RecordPaymentRequest
  -> SchoolContext
  -> RecordPaymentData
  -> PaymentServiceContract::recordPayment()
  -> PaymentResource
```

Legacy service methods should become wrappers or be deprecated.

### 12.3 Deprecation rule

Every legacy finance method must be labeled as one of:

```text
canonical
adapter
deprecated
remove
```

No unlabeled duplicate finance logic.

---

## 13. First Implementation Slice: Payment File Access

### Objective

Replace filename-based payment file download with payment-scoped, authorized access.

### Tasks

```text
FIN-001. Create FinanceFileServiceContract.
FIN-002. Create PaymentFileService implementation.
FIN-003. Create FileAccessPolicyContract if not already present from Phase 2.
FIN-004. Add payment-file route using payment ID.
FIN-005. Add FormRequest for file mode validation.
FIN-006. Update controller to call FinanceFileServiceContract.
FIN-007. Add audit log action payment.file_downloaded.
FIN-008. Mark old filename route deprecated or disable it behind compatibility flag.
```

### Required tests

```text
- admin can download payment file in same school
- finance user can download payment file in same school
- parent can download payment file only for own student/family, if policy allows
- unrelated parent cannot download
- teacher cannot download unless explicitly allowed
- user from another school cannot download
- guessed filename cannot download
- missing file returns 404
- payment without file returns 404
- invalid mode fails validation
- file download creates audit log
```

### Done when

```text
- No active endpoint downloads payment files by filename alone.
- Payment file authorization is policy-based.
- Cross-school file access is blocked by test.
- Access is audited.
```

---

## 14. Second Implementation Slice: Payment Recording

### Objective

Move manual payment recording into modular finance service.

### Tasks

```text
FIN-009. Create RecordPaymentData.
FIN-010. Create PaymentServiceContract.
FIN-011. Implement PaymentService::recordPayment().
FIN-012. Create Money value object.
FIN-013. Add FinanceTransactionRunner.
FIN-014. Lock invoice during payment recording.
FIN-015. Move invoice recalculation into BalanceCalculator.
FIN-016. Add payment.recorded audit event.
FIN-017. Add idempotency support.
FIN-018. Update PaymentManualController to delegate.
```

### Required tests

```text
- records valid payment
- rejects zero amount
- rejects negative amount
- rejects overpayment unless policy permits
- rejects invoice from another school
- recalculates invoice balance
- creates audit log
- creates same result for repeated idempotency key
- rejects same idempotency key with different payload
- concurrent payment attempts do not corrupt invoice balance
```

### Done when

```text
- Controller no longer calculates payment balance.
- Payment creation is transactional.
- Invoice recalculation is single-source.
- Duplicate payment submission is idempotent.
```

---

## 15. Third Implementation Slice: Payment Editing

### Objective

Make payment editing transaction-safe and auditable.

### Tasks

```text
FIN-019. Create EditPaymentData.
FIN-020. Implement PaymentService::editPayment().
FIN-021. Require edit reason.
FIN-022. Lock payment and invoice rows.
FIN-023. Capture before/after state.
FIN-024. Recalculate invoice inside transaction.
FIN-025. Add payment.edited audit event.
FIN-026. Update controller to delegate.
```

### Required tests

```text
- edits amount safely
- edits method safely
- rejects unauthorized actor
- rejects cross-school payment edit
- rejects edit without reason
- recalculates invoice balance
- creates before/after audit log
- concurrent edits do not corrupt balance
```

### Done when

```text
- Payment edit is fully transactional.
- Audit record includes before and after values.
- Invoice balance cannot drift during concurrent edits.
```

---

## 16. Fourth Implementation Slice: Invoice Recalculation

### Objective

Centralize all invoice balance calculations.

### Tasks

```text
FIN-027. Create BalanceCalculator.
FIN-028. Define invoice total calculation.
FIN-029. Define paid amount calculation.
FIN-030. Define refund/reversal handling.
FIN-031. Define status transition rules.
FIN-032. Replace scattered recalculation calls.
FIN-033. Add invoice.recalculated audit event where mutation-triggered.
```

### Invoice status rules

Example baseline:

```text
draft: invoice created but not issued
open: invoice issued with outstanding balance
partial: invoice has payment but balance remains
paid: balance is zero
overpaid: paid amount exceeds expected amount, only if policy permits
void: invoice intentionally canceled
```

### Required tests

```text
- invoice total includes invoice items
- discount reduces balance correctly
- payment reduces balance correctly
- reversed payment restores balance
- refund restores balance correctly
- paid status is applied correctly
- partial status is applied correctly
- void invoice cannot receive payment unless policy permits
```

### Done when

```text
- Invoice balance is calculated by one service.
- Controllers and reports do not duplicate balance logic.
- Finance reports use read models derived from the calculator.
```

---

## 17. Fifth Implementation Slice: Refunds and Reversals

### Objective

Add safe, auditable reversal/refund behavior.

### Tasks

```text
FIN-034. Create RefundServiceContract.
FIN-035. Create RefundPaymentData.
FIN-036. Implement refundPayment().
FIN-037. Implement reversePayment().
FIN-038. Add reason requirement.
FIN-039. Add refund.created and payment.reversed audit events.
FIN-040. Add refund/reversal status handling to BalanceCalculator.
```

### Required tests

```text
- refund cannot exceed paid amount
- refund requires reason
- reversal requires reason
- reversed payment is excluded from paid total
- refunded amount affects balance
- refund creates audit log
- reversal creates audit log
- cross-school refund fails
- unauthorized refund fails
```

### Done when

```text
- Refund and reversal behavior is explicit.
- Existing payment rows are not silently deleted.
- Financial history remains auditable.
```

---

## 18. Finance Reports

Reports must not define finance truth. They display finance truth.

### Tasks

```text
FIN-041. Create FinanceReadRepository.
FIN-042. Create invoice balance read model.
FIN-043. Create payment summary read model.
FIN-044. Create outstanding balance read model.
FIN-045. Update finance reports to consume read models.
FIN-046. Add school context to all report queries.
```

### Required tests

```text
- finance report excludes other schools
- outstanding balance matches BalanceCalculator
- payment report handles refunds/reversals
- report authorization is enforced
- export output matches approved snapshot
```

---

## 19. Database and Schema Requirements

### 19.1 Required constraints

Add or verify:

```text
payments.school_id indexed
payments.invoice_id indexed
payments.student_id indexed, nullable
payments.guardian_id indexed, nullable
payments.status indexed
payments.idempotency_key indexed, nullable
payments.amount uses decimal or integer minor amount

invoices.school_id indexed
invoices.student_id indexed, nullable
invoices.status indexed
invoices.total_amount fixed precision
invoices.balance_amount fixed precision

finance_audit_logs.school_id indexed
finance_audit_logs.actor_user_id indexed
finance_audit_logs.entity_type/entity_id indexed
finance_audit_logs.created_at indexed

payment_files.school_id indexed
payment_files.payment_id indexed
payment_files.storage_disk
payment_files.storage_path
```

### 19.2 Unique constraints

Recommended:

```text
unique(school_id, idempotency_key, operation) where idempotency_key is not null
unique(payment_id, file_type) if only one file per type is allowed
```

### 19.3 Migration caution

Do not rename columns casually in the first slice. Wrap legacy schema with repositories/adapters first. Database heroics during finance extraction are how downtime earns a dramatic soundtrack.

---

## 20. Authorization Requirements

Finance authorization must be policy-based.

Minimum actions:

```text
invoice.view
invoice.create
invoice.update
invoice.void
payment.view
payment.record
payment.edit
payment.reverse
payment.refund
payment.file.download
finance.report.view
```

Every finance endpoint must have tests for:

```text
unauthenticated
wrong school
wrong role
wrong owner/family
authorized admin
authorized finance staff
```

Authorization must check both:

```text
actor role/capability
resource ownership/school boundary
```

---

## 21. FormRequest Requirements

Create or update:

```text
RecordPaymentRequest
EditPaymentRequest
RefundPaymentRequest
ReversePaymentRequest
DownloadPaymentFileRequest
CreateInvoiceRequest
VoidInvoiceRequest
ApplyChargeRequest
ApplyDiscountRequest
```

Each FormRequest must define:

```text
authorize()
rules()
messages(), if needed
toDTO(), optional but recommended
```

Controllers should use:

```php
$data = $request->toDTO();
```

Not:

```php
$data = array_merge($request->all(), $request->query->all(), $request->json()->all());
```

That merge pattern should be retired, preferably with a small ceremony and no witnesses.

---

## 22. Service Provider Bindings

Create or update:

```php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Domain\SchoolCore\Finance\Contracts\PaymentServiceContract;
use App\Domain\SchoolCore\Finance\Services\PaymentService;

final class SchoolCoreFinanceServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(PaymentServiceContract::class, PaymentService::class);
        $this->app->bind(InvoiceServiceContract::class, InvoiceService::class);
        $this->app->bind(RefundServiceContract::class, RefundService::class);
        $this->app->bind(FinanceFileServiceContract::class, PaymentFileService::class);
        $this->app->bind(FinanceAuditLoggerContract::class, FinanceAuditLogger::class);

        $this->app->bind(
            PaymentAllocationPolicyContract::class,
            DefaultPaymentAllocationPolicy::class
        );

        $this->app->bind(
            TuitionPolicyContract::class,
            DefaultTuitionPolicy::class
        );
    }
}
```

Islamic Sunday School may override policy bindings in its own provider.

---

## 23. Architecture Tests

Add tests that fail if finance boundaries are violated.

Required assertions:

```text
SchoolCore\Finance must not depend on Domain\IslamicIslamicSundaySchool
SchoolCore\Finance must not depend on Illuminate\Http\Request
SchoolCore\Finance services must not call auth()
SchoolCore\Finance services must not call request()
Controllers must not instantiate finance concrete services directly
Finance mutations must use FinanceTransactionRunner or DB transaction
Payment file downloads must go through FinanceFileServiceContract
```

Optional static checks:

```text
No float casts in SchoolCore\Finance
No direct response()->download in finance controllers
No filename-only payment file route
No raw DB update for invoice balance outside BalanceCalculator
```

---

## 24. Rollout Plan

### Step 1: Add finance module scaffolding

```text
- create namespaces
- create contracts
- create DTOs
- create service provider
- bind default implementations
```

### Step 2: Add tests around existing behavior

```text
- payment file download tests
- payment record tests
- payment edit tests
- invoice recalculation tests
```

Some tests may expose current failures. Good. That is the point. A failing test is cheaper than explaining to a parent why their invoice time-traveled.

### Step 3: Implement payment file slice

```text
- safest isolated security win
- minimal balance/invoice interaction
```

### Step 4: Implement payment record slice

```text
- introduce transaction runner
- introduce money value object
- introduce audit log
```

### Step 5: Implement payment edit slice

```text
- fix transaction safety
- before/after audit
```

### Step 6: Centralize invoice recalculation

```text
- remove duplicate balance logic
- point reports toward read models
```

### Step 7: Add refunds/reversals

```text
- build on stable payment/invoice model
```

### Step 8: Deprecate legacy methods

```text
- mark adapters
- remove unreachable duplicate logic
- update docs
```

---

## 25. Implementation Tickets

## FIN-001: Create finance module scaffold

### Description

Create the `SchoolCore\Finance` namespace and initial empty contracts, DTO folders, service folders, and provider.

### Acceptance Criteria

- Namespace exists.
- Service provider is registered.
- Empty contracts compile.
- No controller behavior changes yet.

---

## FIN-002: Add Money value object

### Description

Create a `Money` value object using integer minor units or fixed decimal strategy.

### Acceptance Criteria

- No float input accepted without explicit parsing.
- Supports add, subtract, compare, equals, isZero, isPositive.
- Currency mismatch throws exception.
- Unit tests cover rounding and comparison.

---

## FIN-003: Add finance audit logger

### Description

Create append-only finance audit log service.

### Acceptance Criteria

- Logs actor, context, action, entity, before/after, reason.
- Audit rows are school-scoped.
- Unit tests verify required fields.
- Mutation services can call logger inside transaction.

---

## FIN-004: Replace payment file download flow

### Description

Implement payment-scoped file download.

### Acceptance Criteria

- Route uses payment ID.
- File is resolved from payment/file record.
- Authorization uses policy.
- Cross-school access fails.
- Filename guessing fails.
- Audit event is written.

---

## FIN-005: Extract payment recording

### Description

Move payment recording into `PaymentService`.

### Acceptance Criteria

- Uses `SchoolContext`.
- Uses transaction.
- Locks invoice.
- Uses `Money`.
- Writes audit log.
- Supports idempotency.
- Controller delegates only.

---

## FIN-006: Extract payment editing

### Description

Move payment editing into `PaymentService`.

### Acceptance Criteria

- Uses transaction.
- Locks payment and invoice.
- Requires reason.
- Writes before/after audit log.
- Recalculates invoice inside lock.
- Concurrent edit test passes.

---

## FIN-007: Centralize invoice recalculation

### Description

Create `BalanceCalculator` and remove duplicate invoice balance logic.

### Acceptance Criteria

- All payment/refund/discount operations use same calculator.
- Tests cover partial, paid, overpaid, void, refund, reversed.
- No controller calculates invoice balance.

---

## FIN-008: Add refunds and reversals

### Description

Create explicit refund and reversal flows.

### Acceptance Criteria

- Refunds cannot exceed refundable amount.
- Reversals require reason.
- No silent deletion of financial history.
- Audit logs exist.
- Balance recalculation is correct.

---

## FIN-009: Add finance architecture tests

### Description

Enforce modular finance boundaries in CI.

### Acceptance Criteria

- `SchoolCore\Finance` cannot import `IslamicSundaySchool`.
- Services cannot call `auth()` or `request()`.
- No filename-only payment file download route remains active.
- No float casts in finance module.
- CI fails on boundary violation.

---

## 26. Definition of Done for Phase 3

Phase 3 is done when:

```text
- Finance core contracts exist.
- Payment file access is payment-scoped and authorized.
- Payment recording uses modular service.
- Payment editing uses modular service.
- Invoice recalculation is centralized.
- Finance audit logs are append-only and used by mutations.
- Money math avoids floats.
- Finance services require SchoolContext.
- Islamic Sunday School finance behavior is implemented through policies.
- Cross-school finance access is blocked by tests.
- Architecture tests prevent SchoolCore from depending on IslamicSundaySchool.
- Legacy finance methods are labeled as canonical, adapter, deprecated, or remove.
```

---

## 27. Non-Goals

Do not include in Phase 3:

```text
- complete finance UI redesign
- total database schema rewrite
- full accounting ledger replacement
- payroll
- external payment gateway integration
- donation tax receipt system
- masjid/community management CRM
```

Those may come later. Phase 3 is about building the reliable reusable finance core, not adopting every financial ambition a committee can imagine after coffee.

---

## 28. Main Risks

### Risk: accidental behavior break

Mitigation:

```text
- snapshot current API responses
- migrate one vertical slice at a time
- keep legacy adapters temporarily
```

### Risk: cross-school leakage

Mitigation:

```text
- mandatory SchoolContext
- school-scoped repositories
- authorization tests
```

### Risk: balance drift

Mitigation:

```text
- one BalanceCalculator
- transactions
- row locks
- concurrency tests
```

### Risk: Islamic Sunday School leakage into core

Mitigation:

```text
- contract vocabulary review
- architecture tests
- Islamic Sunday School policies only
```

### Risk: duplicated old/new finance logic

Mitigation:

```text
- legacy method labels
- controller delegation
- deprecation tickets
```

---

## 29. Immediate Next Work Queue

Start here:

```text
1. FIN-001 Create finance module scaffold
2. FIN-002 Create Money value object
3. FIN-003 Create finance audit logger
4. FIN-004 Replace payment file download flow
5. FIN-005 Extract payment recording
6. FIN-006 Extract payment editing
7. FIN-007 Centralize invoice recalculation
8. FIN-009 Add finance architecture tests
```

Do not start with reports. Reports consume truth. They do not create it. Starting with reports while payment mutation is unsafe is like painting a house while the foundation is trying to leave.
