# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

### Build

```bash
# Install the local ZATCA SDK (required once before first build)
mvn install:install-file -Dfile=libs/zatca-einvoicing-sdk-238-R3.3.3.jar \
  -DgroupId=com.gazt -DartifactId=einvoicing-sdk -Dversion=238-R3.3.3 -Dpackaging=jar

# Build (skip tests)
./mvnw clean package -DskipTests

# Build with tests
./mvnw clean package
```

### Run

```bash
./mvnw spring-boot:run
```

The server starts on HTTPS port 8000 using the embedded `keystore.jks`. Active profile defaults to `production` (set in `application.yml`).

### Tests

```bash
# Run all tests (requires local PostgreSQL at localhost:5432/rest_erp)
./mvnw test

# Run a single test class
./mvnw test -Dtest=SuperAdminAuthControllerTest

# Tests use the `test` profile (application-test.yml)
```

Tests are integration tests that hit a real PostgreSQL database — do not use mocks for database access. RabbitMQ, Redis, Kafka, and mail are `@MockBean`-ed in `SuperAdminBaseTest`.

## Architecture

### Multi-Tenancy: Schema-Per-Tenant

This is the most critical concept in the codebase. Each company gets its own PostgreSQL schema (`company_{id}`). Data isolation is handled automatically via Hibernate's `MultiTenancyStrategy.SCHEMA`.

**How it works at runtime:**
1. `TenantFilter` (`@Order(1)`) intercepts every request, reads the JWT, and stores `tenantSchema` + `companyId` in `TenantContext` (a `ThreadLocal`).
2. `AuthFilter` (Spring Security) validates the JWT and loads `UserDetails` — this already queries the correct schema because `TenantContext` is set.
3. `MultiTenantConnectionProviderImpl` executes `SET search_path TO <schema>` on every Hibernate connection and resets to `public` on release.
4. `CurrentTenantIdentifierResolverImpl` reads `TenantContext.getCurrentTenant()` to tell Hibernate which schema to use.
5. `TenantFilter`'s `finally` block calls `TenantContext.clear()` to prevent ThreadLocal leaks.

**Controllers and services must NOT pass `companyId` as a parameter.** The schema isolation handles filtering automatically.

### Database Schemas

| Schema | Purpose |
|--------|---------|
| `public` | Shared reference data (`roles`, `country`), Hibernate default |
| `superadmin` | SaaS platform management: `company`, `superadmin_users`, `trail_model` |
| `company_{id}` | Per-tenant isolated data: all business entities |

### Package Structure

```
src/main/java/com/camelsoft/restforyou/
├── Client/          # Tenant-scoped code: Controllers, Services, Models, Repositories, DTOs
├── Public/          # Shared data in public schema: Role, RoleEnum, country
├── SuperAdmin/      # Platform management in superadmin schema: company CRUD, billing, trials
├── tools/           # Infrastructure: multi-tenant config, security, filters, exceptions, utils
└── webRTCvideoChat/ # Video conferencing via LiveKit
```

The `Client/` package is organized into sub-domains: `Auth/`, `Finances/`, `HR/`, `HRTools/`, `ChartOfAccounting/`, `ProjectTools/`, `Schedule/`, etc.

### Key Infrastructure Files

| File | Role |
|------|------|
| `tools/configuration/MultiTenant/TenantContext.java` | ThreadLocal holding current schema and companyId |
| `tools/configuration/MultiTenant/TenantContextService.java` | Helper for cross-schema access (e.g., switching to `superadmin` to read company data) |
| `tools/configuration/MultiTenant/TenantFilter.java` | Extracts JWT claims into TenantContext before security filter |
| `tools/scurity/AuthFilter.java` | JWT validation + Spring Security context |
| `tools/scurity/SecurityConfig.java` | Defines public vs protected endpoints |
| `tools/util/TokenUtil.java` | JWT generation and parsing |
| `tools/annotation/RequiresPackage.java` + `tools/aspect/PackageCheckAspect.java` | AOP-based subscription package enforcement |

### Getting Company/Tenant Info in Services

```java
// Company ID — no DB call, reads ThreadLocal
Long id = tenantContextService.getCurrentCompanyId();

// Full company entity — auto-switches to superadmin schema and back
company comp = tenantContextService.getCurrentCompany();
```

### Cross-Schema Access Pattern

```java
String previous = TenantContext.getCurrentTenant();
try {
    TenantContext.setCurrentTenant("superadmin");
    // query superadmin data
} finally {
    TenantContext.setCurrentTenant(previous);
}
```

### Scheduled Jobs (Critical)

Cron jobs run on background threads with no HTTP request — `TenantContext` is empty. Every scheduled task in `Client/Services/Schedule/` must loop over all companies and manually set/clear the context per iteration:

```java
TenantContext.setCurrentTenant("superadmin");
List<company> companies = companyService.findAll();
TenantContext.clear();
for (company comp : companies) {
    try {
        TenantContext.setCurrentTenant(comp.getTenantSchema());
        TenantContext.setCurrentCompanyId(comp.getId());
        // tenant-scoped work
    } finally {
        TenantContext.clear();
    }
}
```

### Async and WebSocket

`@Async` methods and WebSocket handlers also lack a request-scoped `TenantContext`. For WebSocket, a `TenantChannelInterceptor` must be used to propagate tenant info from the CONNECT frame's JWT into each message handler.

### Caching Layer

Redis-backed cache services live in `Client/Services/Cache/`: `CompanyCacheService`, `UserDetailsCacheService`, `DepartmentCacheService`, etc. These are `@MockBean`-ed in tests.

### Messaging

RabbitMQ (via `MailConsumerService`) handles async email dispatch. Kafka (`KafkaConfig`) is available for event streaming. Both are `@MockBean`-ed in tests.

### External Integrations

- **ZATCA**: Saudi e-invoicing SDK (local JAR in `libs/`). Services in `Client/Services/Finance/Zatca/`. XML templates in `src/main/resources/templates/InvoiceXmlTemplate/`.
- **Firebase**: Push notifications via `FireBaseFCMService`. Credentials in `src/main/resources/firebase-service-account.json`.
- **Stripe**: Payment processing (`stripe.key.secret` in `application.yml`).
- **MyFatoorah**: Saudi payment gateway.
- **Muqeem / Salamah / Dakhli**: Saudi government ELM services for HR compliance (visa, insurance, income verification).
- **LiveKit**: WebRTC video conferencing (`webRTCvideoChat/` package).
- **AWS S3 / Hetzner Object Storage**: File storage via `FileCdnController`.

### Profiles

| Profile | Database | When used |
|---------|----------|-----------|
| `production` | `46.224.48.190:5432/rest_erp` | Live server (default) |
| `development` | `49.13.75.18:5432/rest_erp` | Dev server |
| `test` | `localhost:5432/rest_erp` | Local integration tests |

Switch profile with `-Dspring.profiles.active=test`.

### ZATCA SDK Installation

The ZATCA e-invoicing SDK is not in Maven Central. Before building from scratch, install the local JAR:

```bash
mvn install:install-file -Dfile=libs/zatca-einvoicing-sdk-238-R3.3.3.jar \
  -DgroupId=com.gazt -DartifactId=einvoicing-sdk -Dversion=238-R3.3.3 -Dpackaging=jar
```

The Jenkinsfile already does this as a dedicated pipeline stage.

### Imports to Use

- `Role` entity → `com.camelsoft.restforyou.Public.Models.Role`
- `RoleEnum` → `com.camelsoft.restforyou.Public.Enum.RoleEnum`
- `company` entity → `com.camelsoft.restforyou.SuperAdmin.models.company`
- Never import from the wrong schema package — role and company entities have counterparts in other packages that will cause subtle bugs.

### `hbm2ddl.auto` Setting

`spring.jpa.hibernate.ddl-auto` is set to `none` in `application.yml`. Schema creation for new tenants is handled programmatically by `TenantSchemaService` in `Client/Services/MultiTenant/`.
