# CLAUDE.md

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

## Project Overview

C4rgo Cloud is a Symfony 6.4 LTS web application for cargo/logistics management. It handles container tracking, barcode scanning, document management, reporting, and IoT device integration (MQTT). The backend is PHP 8.2+ with Doctrine ORM (2.x) against MariaDB, and the frontend uses Webpack Encore with Twig templates. Mapping and routing use PHP 8 attributes (no annotations).

## Common Commands

### PHP Dependencies
```bash
composer install
composer validate --strict
```

### Frontend Assets
```bash
npm install
npm run dev          # Development build
npm run watch        # Watch mode
npm run build        # Production build
npm run dev-server   # Webpack dev server
```

### Testing
```bash
./vendor/bin/simple-phpunit                        # Run all tests
./vendor/bin/simple-phpunit tests/Controller/      # Run controller tests only
./vendor/bin/simple-phpunit --filter=TestClassName  # Run a single test class
```
Tests use SQLite in-memory database (configured in `phpunit.xml.dist`).

### Linting & Code Style
```bash
./vendor/bin/php-cs-fixer fix --diff --dry-run -v   # Check code style (Symfony rules)
./vendor/bin/php-cs-fixer fix                        # Auto-fix code style
php bin/console lint:yaml config                     # Validate YAML config
php bin/console lint:twig templates                  # Validate Twig templates
php bin/console lint:xliff translations              # Validate translation files
php bin/console doctrine:schema:validate             # Validate DB schema vs entities
```

### Docker (Development)
```bash
docker-compose up -d    # Start: nginx (127.0.0.1:8080), MariaDB (127.0.0.1:3307)
docker-compose down     # Stop
```
Docker DB defaults: database=symfony, user=symfony, password=symfony.

### Database Migrations
```bash
php bin/console doctrine:migrations:migrate
php bin/console doctrine:fixtures:load              # Load test fixtures
```

## Architecture

### Routing Convention
All routes are locale-prefixed: `/{_locale}/...` where `_locale` matches `en|fr|de|es|cs|nl|ru|uk|ro|pt_BR|pl|it|ja|id|ca|sl|hr|zh_CN`. API routes under `src/Controller/Api/` use the prefix `/api/v1` without locale.

### Security & Roles
- Authentication: form login with bcrypt + CSRF protection, Okta OAuth2 (optional), JWT for APIs
- Two-factor auth via email (Scheb 2FA bundle)
- Roles: `ROLE_USER`, `ROLE_ADMIN`, `ROLE_SUPERADMIN`, `ROLE_SCANADMIN`
- Access control defined in `config/packages/security.yaml`
- Routes under `/{locale}/admin` require `ROLE_ADMIN`, `/{locale}/sa` requires `ROLE_SUPERADMIN`

### Service Layer
Business logic lives in `src/Services/`:
- `C4rgoServiceClient` — external C4rgo API integration
- `SIM7600Service` — MQTT communication with IoT tracking devices
- `OktaApiService` — Okta OAuth2 authentication
- `EncryptionService` — data encryption
- `ExportService` / `ReportService` — report generation and export
- `Acquisition\*` (`AcquisitionUploadService` + `AcquisitionStorage`/`AcquisitionImporter`/`AcquisitionLogger`/`AcquisitionConfig`) — ricezione acquisizioni via zip (endpoint `POST /api/v1/acquisition/upload`, in `UploadController`). Sostituisce il legacy `public/upload.php`. Salva SEMPRE su filesystem; aggiorna il DB solo se il flag è attivo; logga le operazioni in `acquisition_upload_log`. Tutti i parametri vivono nella tabella `decode` (classe `CONFIG`): `base.data.folder`, `base.data.dbimport.enabled`, `base.data.upload.maxbytes`, `base.data.upload.field`, `base.data.log.retention.days`.

Services use autowiring with explicit bindings in `config/services.yaml`. The `src/{Entity,Migrations,Tests}` directories are excluded from autowiring.

### Mailer Configuration
Dual SMTP transports configured in `config/packages/mailer.yaml`: a main transport and a separate `report` transport for report emails. The report mailer is registered as `mailer.report` service in `services.yaml`.

### Webpack Entries
Three entry points in `webpack.config.js`: `app` (main), `stats` (charts/analytics), `search` (search functionality). Output goes to `public/build/`.

### Key Directories
- `src/Controller/Admin/` — admin panel (user management, settings)
- `src/Controller/Api/` — REST API endpoints (no locale prefix)
- `src/Controller/Configuratore/` — configurator module
- `src/Entity/` — Doctrine entities (60+ models)
- `src/Repository/` — Doctrine repositories (data access)
- `src/EventListener/` — security and authentication event listeners
- `templates/` — Twig templates organized by feature
- `public/upload/` — user-uploaded files (avatars, logos, containers)

### Code Style
PHP CS Fixer with `@Symfony` ruleset. Configuration in `.php-cs-fixer.dist.php`.

## Known Issues & Technical Debt

See `README.md` section "Analisi Qualita del Codice" for the original prioritized list, and `UPGRADE_PROGRESS.md` for the live status of the Phase-0 work. Current state of the key critical items:

Resolved:
- ✅ SQL injection in `SearchController`, `ConfiguratoreController`, `CheckController`, `Counters.php` — parameterized / int-cast. `Converter.php` & `C4rgoUtils.php` were already safe.
- ✅ Encryption key no longer logged (`EncryptionService`).
- ✅ SSL verification in `OktaApiService` is now env-configurable (`OKTA_SSL_VERIFY`, default `true`).
- ✅ Deprecated `Controller` base class — all controllers now extend `AbstractController`.
- ✅ `$this->getDoctrine()` fully removed (EntityManagerInterface injected everywhere).

Still open:
- Missing JWT signature validation in `OktaApiService` (deferred: needs `firebase/php-jwt` + JWKS).
- AES-256-CBC without HMAC, padding-oracle vulnerable (deferred: changes ciphertext format → data migration).
- User tokens stored unencrypted in database (deferred: data migration).
- Hardcoded credentials committed in `.env` (must move to `.env.local`; rotate exposed secrets).
- Direct `$_GET`/`$_ENV` access instead of Symfony DI — `OktaApiService` done; ~15 files remain.
- Legacy `public/upload.php` (SQL injection + credenziali hardcoded) sostituito dal servizio Symfony `Acquisition\*` ma ancora presente nel repo: va dismesso quando tutte le macchine puntano a `POST /api/v1/acquisition/upload`.
- Test suite was Symfony Demo skeleton (Post/Tag/blog); removed and replaced with a DB-independent smoke test. Real route-level functional tests need a MariaDB test DB (the legacy mappings can't build the schema on SQLite).

## Migration Plan

See `UPGRADE.md` for the phased migration strategy: Symfony 4 → 5.4 → 6.4 → 7.x.
- ✅ Fase 1 (Symfony 5.4) — completata (vedi `PHASE1_PLAN.md`).
- ✅ Fase 2 (Symfony 6.4 LTS) — completata (vedi `PHASE2_PLAN.md`): authenticator manager,
  annotations→attributi, PHP 8.2, doctrine/annotations rimosso, 0 deprecation self/direct.
- ⏭️ Fase 3 (Symfony 7.x): include ORM 2→3 e migrazione SessionInterface→RequestStack.
  - ✅ I mapping `inversedBy`/`mappedBy` invalidi pre-esistenti sono stati allineati al DB:
    `doctrine:schema:validate` è ora pulito e `doctrine:schema:update` non aborta più (vedi
    CHANGELOG 5.8.0 → *Corretto*).
  - ✅ `report_fields_where_declared: true` abilitato (5.8.3): la deprecation ORM 3 resta emessa
    solo dal bundle vendor `dukecity/command-scheduler-bundle`, non dalle nostre entità.
  - ⚠️ Blocco noto: la naming strategy resta `underscore`, non `underscore_number_aware`
    (deprecata). Passare a number-aware rinominerebbe le colonne legacy con cifre prive di
    `name:` esplicito (`iata3digit` → `iata_3_digit`, `ibx1` → `ibx_1`, …) e rompe
    `schema:update`. Prerequisito di Fase 3: fissare i `name:` espliciti in quelle
    `#[ORM\Column]`.
