# CLAUDE.md

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

## What this is

Monorepo for **yima / YimaShop**, a multi-shop POS SaaS for small West-African retail (Senegal pilot). The domain is in French: UI labels, docs, commit messages and flash messages are French. Currency is FCFA/XOF, the time zone is `Africa/Dakar`, and legal IDs are NINEA/RCCM.

| Folder | Role | Stack |
|---|---|---|
| `portail-manager/` | Backend: JSON API (`/api`, `/api/v1`) **and** the Twig admin/manager web portal | Symfony 6.4, PHP 8.3, Doctrine ORM 3, lexik JWT, Messenger, Tailwind/Flowbite/Turbo via Webpack Encore |
| `yima-shop/` | PWA for cashiers and managers (mobile-first, offline-first). **Reference client** | Angular 20 standalone, Ionic 8, Capacitor 8, `@angular/service-worker` |
| `yima_shop/` | Flutter port of the Ionic client. It follows Ionic and must not diverge from it | Flutter 3.44 / Dart 3.12, Riverpod, go_router, dio, freezed |
| `yima-wc-connector/` | WordPress plugin: syncs catalogue and stock from yima to WooCommerce, sends orders from WooCommerce back to yima, HMAC license | PHP 7.4+, WooCommerce |
| `yima-wc-design/` | WordPress plugin: design tokens, Elementor widgets, sector kits | PHP 7.4+, Elementor |
| `yima-pos/` | Design docs: vision, architecture, data model, security, API, roadmap, progress tracking, module specs, ADRs | Markdown |
| `yima-shop-visual/` | Brand assets (logos, favicons, manifest icons) | — |

`portail-manager/CLAUDE.md` holds the detailed backend guide (security layers, voters, soft-delete pattern, controller checklist, Twig layout). Read it before changing backend code.

## Commands

### Backend (`portail-manager/`)
```bash
composer install && npm install
symfony serve --port=8010 --no-tls          # API at http://localhost:8010/api
npm run watch                                # Encore assets for the Twig portal
php bin/console doctrine:schema:update --force   # dev schema sync (migrations for prod)
php bin/console doctrine:fixtures:load -n    # DESTRUCTIVE: purges and reseeds the AmansaShop demo
php bin/console app:rbac:seed                # idempotent seed of RBAC permissions and roles
php bin/console lint:container && php bin/console lint:twig templates/

# Tests (APP_ENV=test, database yima-shop_test)
php bin/console doctrine:database:create --env=test --if-not-exists
php bin/console doctrine:schema:update --force --env=test
php bin/phpunit                               # full suite
php bin/phpunit tests/Functional/Api/         # one directory
php bin/phpunit --filter testSaleAnonymous    # one test
```
PHPUnit is set to `failOnDeprecation`, `failOnNotice` and `failOnWarning`, so a deprecation fails the suite. PHPUnit 12 needs data providers written as `#[DataProvider]` attributes. `.env.local` is **not** loaded in the test env, so any secret the tests need must be declared in `.env.test`. Tests use dedicated JWT keys (`config/jwt/*-test.pem`, passphrase `test_passphrase`).

### Ionic PWA (`yima-shop/`)
```bash
npm install
npm start -- --port 8110                      # http://localhost:8110, expects backend on :8010
npm run build                                 # prod build + service worker
npm run lint
npm test                                      # Karma/Jasmine
npm test -- --include src/app/path/to/file.spec.ts   # single spec
```
The API URL is set in `src/environments/environment.ts` (`apiBaseUrl`).

### Flutter (`yima_shop/`)
```bash
flutter pub get
dart run build_runner build                   # freezed / json_serializable codegen
flutter run --dart-define=API_BASE_URL=http://10.0.2.2:8010/api   # Android emulator
flutter analyze && flutter test
```
Without `--dart-define`, the app targets **production**.

Production domains: API + Twig portal on `https://api-shop.yimalabs.com` (`BASE_URL` in `portail-manager/.env.prod`), PWA on `https://shop.yimalabs.com` (`CORS_ALLOW_ORIGIN` in `.env.prod`, `environment.prod.ts`). The PWA link on the landing and registration pages is the `pwa_url` setting stored in the database, editable in the admin settings page.

Demo login (fixtures): `yalya96@gmail.com` / `Welcome123@`.

## Cross-cutting architecture

- **Central database as the single source of truth.** All business writes go through the Symfony API to the central MySQL/MariaDB database (`yima-shop`). Data reaches customer storefronts through two downstream channels:
  - **Channel A:** a read-only projection to a per-client database (`TenantConnectionService`, encrypted credentials).
  - **Channel B:** a WooCommerce connector. Its server side is in `src/Controller/Api/Connector` and `Service/Pos/ConnectorOrderService`; its WordPress side is `yima-wc-connector/`.

  The normative reference is `portail-manager/docs/ARCHITECTURE_MULTITENANT.md`.
- **Tenant = `Company`.** The UI calls it "Boutique", but the code keeps `Company`/`companies`. Every query is scoped to the user's primary company.
- **Authorization:**
  - Global roles: `ROLE_ADMIN > ROLE_MANAGER > ROLE_EMPLOYER`.
  - On top of those, the RBAC permission codes `<domain>.<subject?>.<action>` (e.g. `pos.sell`, `catalogue.product.edit`) are resolved by `PermissionResolver`/`PermissionVoter` and gated with `denyAccessUnlessGranted('<perm>', $company)`.
  - New permissions are declared in `RbacSeedCommand`.
  - Client-side permission checks only hide or show UI. The server always re-checks.
- **API contract:**
  - Every response uses the envelope `{ code, success, message, data, meta }` built by `ApiResponseFormatter`.
  - New business endpoints go under `/api/v1/` (controllers in `src/Controller/Api/V1`), with plural resource names.
  - Query conventions: `?page=&limit=`, `?sort=-field`, `?filter[x]=`. Validation errors return 422.
  - Sensitive creates accept an `Idempotency-Key` header.
  - Firewalls: `api` (stateless JWT, RS256 + rotating refresh tokens) and `main` (form login for the Twig portal).
- **Money:** amounts are integer **XOF cents**, never floats. On the front end use `shared/utils/money.util`.
- **Append-only ledger data:** sales, cash and accounting entries never get soft-delete. Gedmo soft-delete with an `idx_<table>_deleted` index applies only to deletable entities (catalogue, customers…). Entities exposed through the API use a public UUID.
- **Offline sales:** `yima-shop/src/app/core/services/sales-sync.service.ts` queues a sale in Capacitor Preferences when the network fails and replays it later with the same `Idempotency-Key`. The backend must stay idempotent on sale creation.
- **Ionic client layout:**
  - `core/services/` has one service per API domain and `core/models/` holds the TS interfaces.
  - `features/<domain>/` holds the pages. State uses Angular signals.
  - `auth.interceptor` adds the Bearer token and transparently refreshes it on 401.
  - Login can return **202** with a challenge flow: device → country → IP → 2FA (`features/auth/challenge`).
- **Ionic → Flutter parity rule:** a functional change ships on Ionic first and is then logged in the sync register of `yima-pos/08-portage-flutter.md` §11 so it can be ported to `yima_shop/`. Flutter never re-implements business rules.

## Backend coding rules (summary; details in `portail-manager/CLAUDE.md` and `yima-pos/notes/conventions.md`)

- Controllers that do I/O wrap it in try/catch and log through `FileLogger` (action, user_id, error, file, line).
- Writes go through `EntityManagerService` safe methods (`safePersist`, `safeFlush`…), never the raw EntityManager.
- Entity and class names are English; user-facing strings are French.
- Web routes are prefixed `admin_*` or `manager_*`; API routes are prefixed `api_*`.
- Schema changes must be additive. Check them with `doctrine:schema:update --dump-sql` before applying.

## Delivery

A feature counts as done when:
- tests are green;
- the module spec in `yima-pos/modules/` is updated;
- progress is logged in `yima-pos/07-suivi.md`;
- if the client was touched, the Flutter sync register is updated.

Work happens on the `dev` branch; `main` is the PR target. `.env.local` and `config/jwt/*.pem` are gitignored.
