# Module Structure

## Created: NGauge_modules/surf-invoicing

Complete module implementation for invoice generation, management, and credit notes.

```
NGauge_modules/surf-invoicing/
├── src/                           # Module source code (PSR-4)
│   ├── Module.php                 # Module bootstrap class
│   ├── behaviors/
│   │   └── CartUpdateBehavior.php # Prevent updates on invoiced carts
│   ├── commands/
│   │   └── GenerateController.php # Console: yii surf-invoicing/generate
│   ├── controllers/
│   │   └── InvoiceController.php  # Web: invoice views & credit notes
│   ├── helpers/
│   │   └── CartInvoiceHelper.php  # View helpers for cart UI
│   ├── jobs/
│   │   └── InvoiceGenerationJob.php # Async queue job
│   ├── models/
│   │   ├── Invoicesbe.php         # Invoice snapshot model
│   │   └── CreditNote.php         # Credit note model
│   ├── services/
│   │   ├── InvoiceEligibilityService.php    # Cart eligibility check
│   │   ├── InvoiceFactory.php               # Cart -> Invoice mapping
│   │   └── InvoiceGenerationService.php     # Orchestration service
│   └── traits/
│       ├── InvoiceRulesTrait.php   # Reusable rule composition
│       └── CreditNoteTrait.php     # Credit note relations & helpers
├── migrations/
│   └── m260311_000001_create_invoicing_tables.php
├── docs/
│   ├── Invoicing_plan.MD          # Architecture & design
│   ├── Invoicing_todo.MD          # Implementation checklist
│   └── INTEGRATION.md             # Step-by-step integration
├── composer.json                  # Package meta
└── README.md                       # Module overview
```

## Database Schema

### Tables Created

**invoices** - Immutable invoice snapshots
- `id` (PK)
- `source_cart_id` (FK) - Link to original cart
- `source_centrearrddate` - Center arrival date from cart
- `generated_at` - Generation timestamp
- `generation_key` (UNIQUE) - Idempotency key
- `invoice_number` (UNIQUE) - Invoice reference
- Cart snapshot fields (email, name, address, etc.)
- Financial snapshot (subtotal, total, tax, shipping, etc.)
- `source_snapshot_json` - Complete audit payload

**credit_notes** - Credit note tracking
- `id` (PK)
- `invoice_id` (FK) - Parent invoice
- `credit_note_number` (UNIQUE)
- `amount` - Credit amount
- `reason` - Reason for credit
- `status` - Draft|Issued|Cancelled
- `created_at`, `created_by`
- `notes` - Additional notes

**basket** (modified) - Cart linkage columns
- `invoiceid` (FK) - Link to generated invoice
- `invoiced_by` - User/system that generated invoice
- `invoicedate` - Generation timestamp

## Indexes

- `invoices.generation_key` (UNIQUE)
- `invoices.source_cart_id`
- `invoices.invoice_number` (UNIQUE)
- `invoices.generated_at`
- `credit_notes.invoice_id`
- `credit_notes.status`
- `credit_notes.created_at`

## Core Components

### Services

| Service | Purpose |
|---------|---------|
| `InvoiceEligibilityService` | Determine which carts are eligible for invoicing |
| `InvoiceFactory` | Map cart data to invoice snapshot payload |
| `InvoiceGenerationService` | Orchestrate generation with transactions & idempotency |

### Models

| Model | Purpose |
|-------|---------|
| `Invoicesbe` | Invoice snapshot (immutable after generation) |
| `CreditNote` | Credit note with draft/issued/cancelled states |

### Command

```bash
php yii surf-invoicing/generate [--date=YYYY-MM-DD] [--dry-run=1]
```

- Generate invoices for orders by `centrearrddate`
- Idempotent: skips already-invoiced carts
- Dry-run mode for testing
- Formatted output with created/skipped/failed counts

### Job

`InvoiceGenerationJob` for queue-based async processing.

### Behavior

`CartUpdateBehavior` - Attaches to CartController to:
- Block update/save/delete actions on invoiced carts
- Throw ForbiddenHttpException with message
- Log attempts

### Helper

`CartInvoiceHelper` - View helper methods:
- `isInvoiced($cart)` - Check if cart has invoice
- `canEdit($cart)` - Check if cart is editable
- `renderInvoiceStatusBadge($cart)` - Badge HTML
- `renderInvoiceInfoPanel($cart)` - Full info panel
- `getInvoiceBalance($cart)` - Balance after credits
- `getTotalCredit($cart)` - Total credit applied

### Traits

| Trait | Purpose |
|-------|---------|
| `InvoiceRulesTrait` | Reusable validation from cart + invoice-specific |
| `CreditNoteTrait` | Credit note relations and balance helpers |

## Eligibility Criteria

Carts are invoiceable if:
- Status: `Completed`, `Confirmed`, or `Paid`
- Billing Status: `Pending`, `PartiallyPaid`, or `Unpaid`
- Center Arrival Date: set and <= run date
- Not already invoiced (generation_key unique check)

## Idempotency

- Unique key: `CART_{cart_id}_ARRDATE_{arrival_date}`
- Prevents duplicate invoices for same cart+date
- Safe for repeated runs

## Immutability

- Invoice snapshots are immutable after generation
- All cart fields frozen at generation time
- Complete JSON snapshot stored for audit
- Credit notes allow corrections without modifying invoice

## Integration Points (See INTEGRATION.md)

1. Register module in backend config
2. Attach `CartUpdateBehavior` to `CartController`
3. Update cart views to use `CartInvoiceHelper`
4. Run migrations
5. Test generation command
6. Set up cron/scheduler for daily runs
7. (Optional) Add queue job support
8. (Optional) Customize eligibility criteria

## Customization Points

### Change invoice eligibility

Edit `InvoiceEligibilityService::getEligibleStatuses()` or `getEligibleBillingStatuses()`.

### Custom invoice numbering

Override `InvoiceFactory::generateInvoiceNumber()`.

### Custom snapshot fields

Extend `InvoiceFactory::createInvoiceDataFromCart()`.

### Custom validation

Add rules to `Invoicesbe::rules()` or extend in behavior.

## Version

**v1.0.0** (2026-03-11)

- Initial release
- Core services and models
- Console command
- Credit note support
- Queue job support
- Cart update restriction
- View helpers
