341 lines
19 KiB
Markdown
341 lines
19 KiB
Markdown
# Service Finder Fejlesztési Történet
|
||
|
||
## 17-es Kártya: Billing Engine Service (Epic 3 - Pénzügyi Motor)
|
||
|
||
**Dátum:** 2026-03-09
|
||
**Státusz:** Kész ✅
|
||
**Kapcsolódó fájlok:** `backend/app/services/billing_engine.py`, `backend/app/api/v1/endpoints/billing.py`
|
||
|
||
### Technikai Összefoglaló
|
||
|
||
A Billing Engine Service-t az Epic 3 (Pénzügyi Motor) keretében implementáltuk, amely a 18-as kártya atomi tranzakciós logikájára épül. Az implementáció egyszerűsített interfészeket biztosít a gyakori számlázási műveletekhez, miközben megtartja az alapvető négyszeres wallet rendszert és a dupla könyvelést.
|
||
|
||
#### Főbb Implementációk:
|
||
|
||
1. **Új funkciók a `billing_engine.py`-ban** (689-880 sorok):
|
||
- `charge_user()`: Atomiszámlázási tranzakciók felhasználóbarát wrapper-e
|
||
- `upgrade_subscription()`: Előfizetési szintek frissítése árképzéssel és wallet levonással
|
||
- `record_ledger_entry()`: Közvetlen naplóbejegyzés létrehozása kézi pénzügyi műveletekhez
|
||
- `get_user_balance()`: Konszolidált wallet egyenleg lekérdezés minden wallet típusra
|
||
|
||
2. **Endpoint integráció** a `billing.py`-ban:
|
||
- `/upgrade` endpoint most a `upgrade_subscription()` funkciót használja
|
||
- `/wallet/balance` endpoint most a `get_user_balance()` funkciót használja
|
||
- Az API válasz struktúra változatlan maradt a visszafelé kompatibilitás érdekében
|
||
|
||
3. **Megtartott alapvető funkciók:**
|
||
- Négyszeres wallet rendszer (EARNED, PURCHASED, SERVICE_COINS, VOUCHER)
|
||
- Okos levonási sorrend: VOUCHER → SERVICE_COINS → PURCHASED → EARNED
|
||
- Dupla könyvelés a FinancialLedger táblában
|
||
- Atomis tranzakciós biztonság rollback-kel hibák esetén
|
||
- FIFO voucher lejárat 10% díjjal (SZÉP-kártya modell)
|
||
|
||
#### Tesztelés és Validáció:
|
||
|
||
A `verify_financial_truth.py` teszt javítva lett és sikeresen validálja:
|
||
- Stripe fizetés szimulációt
|
||
- Belső ajándék átutalásokat
|
||
- Voucher lejáratot díjakkal
|
||
- Dupla könyvelés konzisztenciát a wallet-ek és a pénzügyi napló között
|
||
|
||
Minden teszt sikeresen lefut: "MINDEN TESZT SIKERES! A PÉNZÜGYI MOTOR ATOMBIZTOS!"
|
||
|
||
#### Függőségek:
|
||
- **Bemenet:** Wallet modell, FinancialLedger modell, SubscriptionTier definíciók
|
||
- **Kimenet:** Használják a számlázási endpointok, fizetésfeldolgozás és előfizetéskezelés
|
||
|
||
---
|
||
|
||
### Korábbi Kártyák Referenciája:
|
||
- **15-ös kártya:** Wallet modell és négyszeres wallet rendszer
|
||
- **16-os kártya:** FinancialLedger és dupla könyvelés
|
||
- **18-as kártya:** Atomis tranzakciós manager és okos levonási logika
|
||
- **19-es kártya:** Stripe integráció és fizetési intent kezelés
|
||
|
||
---
|
||
|
||
## 20-as Kártya: Subscription Lifecycle Worker (Előfizetés életciklus kezelése)
|
||
|
||
**Dátum:** 2026-03-09
|
||
**Státusz:** Kész ✅
|
||
**Kapcsolódó fájlok:** `backend/app/workers/system/subscription_worker.py`
|
||
|
||
### Technikai Összefoglaló
|
||
|
||
A 20-as Gitea kártya implementációja a lejárt előfizetések automatikus kezelésére. A worker napi egyszer fut (cron) és atomis tranzakcióban végzi a következőket:
|
||
|
||
1. **Lekérdezés:** Azokat a User-eket, ahol `subscription_expires_at < NOW()` és `subscription_plan != 'FREE'`
|
||
2. **Downgrade:** `subscription_plan = "FREE"`, `is_vip = False`
|
||
3. **Naplózás:** Főkönyvi bejegyzés (`SUBSCRIPTION_EXPIRED`) a `billing_engine.record_ledger_entry` segítségével
|
||
4. **Értesítés:** Belső dashboard értesítés és email küldése a `NotificationService`-en keresztül
|
||
|
||
#### Főbb Implementációk:
|
||
|
||
- **Atomis zárolás:** `WITH FOR UPDATE SKIP LOCKED` a párhuzamos feldolgozás biztonságához
|
||
- **Integráció a meglévő rendszerekkel:** A `billing_engine` és `notification_service` modulok használata
|
||
- **Hibatűrés:** Egyéni felhasználóhibák nem akadályozzák a teljes folyamatot, statisztikák gyűjtése
|
||
- **Logolás:** Dedikált logger (`subscription-worker`) a folyamat nyomon követéséhez
|
||
|
||
#### Futtatás:
|
||
|
||
```bash
|
||
docker exec sf_api python -m app.workers.system.subscription_worker
|
||
```
|
||
|
||
#### Függőségek:
|
||
|
||
- **Bemenet:** User modell (`subscription_expires_at`, `subscription_plan`, `is_vip`)
|
||
- **Kimenet:** Módosított User rekordok, FinancialLedger bejegyzések, InternalNotification és email értesítések
|
||
|
||
---
|
||
|
||
*Megjegyzés a jövőbeli fejlesztésekhez:* A billing engine most már magas szintű funkciókat biztosít, amelyek elfedik a komplex atomis tranzakciós logikát. A jövőbeli kártyáknak ezeket a funkciókat kell használniuk, nem pedig közvetlenül manipulálniuk a wallet-eket vagy naplóbejegyzéseket.
|
||
|
||
---
|
||
|
||
## 66-os Kártya: Social 3 - Verifikált Szerviz Értékelések (User → Service)
|
||
|
||
**Dátum:** 2026-03-12
|
||
**Státusz:** Kész ✅
|
||
**Kapcsolódó fájlok:** `backend/app/models/social.py`, `backend/app/models/service.py`, `backend/app/models/identity.py`, `backend/app/services/marketplace_service.py`, `backend/app/api/v1/endpoints/services.py`, `backend/app/scripts/seed_system_params.py`
|
||
|
||
### Technikai Összefoglaló
|
||
|
||
A 66-os Gitea kártya implementációja a verifikált szerviz értékelési rendszerhez. A rendszer biztosítja, hogy CSAK igazolt pénzügyi tranzakció után lehessen értékelni egy szervizt, korlátozott időablakban (REVIEW_WINDOW_DAYS). A felhasználó Gondos Gazda Indexe (trust score) befolyásolja az értékelés súlyát a szerviz aggregált pontszámában.
|
||
|
||
#### Főbb Implementációk:
|
||
|
||
1. **Új tábla: `ServiceReview`** (`social` séma):
|
||
- Kapcsolat: `service_id` → `ServiceProfile`, `user_id` → `User`, `transaction_id` → `FinancialLedger`
|
||
- Négy dimenziós értékelés: `price_rating`, `quality_rating`, `time_rating`, `communication_rating` (1-10 skála)
|
||
- `UniqueConstraint(transaction_id)` – Egy számlát csak egyszer lehessen értékelni
|
||
- `is_verified` (default: True) – Automatikusan igazolt, mert tranzakció alapú
|
||
|
||
2. **Frissített tábla: `ServiceProfile`** (`marketplace` séma):
|
||
- Aggregált értékelési mezők: `rating_verified_count`, `rating_price_avg`, `rating_quality_avg`, `rating_time_avg`, `rating_communication_avg`, `rating_overall`, `last_review_at`
|
||
- Automatikus frissítés minden új értékelés után a `update_service_rating_aggregates()` függvénnyel
|
||
|
||
3. **Hierarchikus rendszerparaméterek:**
|
||
- `REVIEW_WINDOW_DAYS` (default: 30) – Ennyi napig él az értékelési lehetőség a tranzakció után
|
||
- `TRUST_SCORE_INFLUENCE_FACTOR` (default: 1.0) – Mennyire számítson a user Gondos Gazda Indexe
|
||
- `REVIEW_RATING_WEIGHTS` (default: {"price": 0.25, "quality": 0.35, "time": 0.20, "communication": 0.20}) – Súlyozás
|
||
|
||
4. **Marketplace Service logika** (`marketplace_service.py`):
|
||
- `create_verified_review()`: Validálja a tranzakciót, időablakot, létrehozza az értékelést
|
||
- `update_service_rating_aggregates()`: Kiszámolja az aggregált értékeléseket trust score súlyozással
|
||
- `get_service_reviews()`: Lapozható értékelés lista
|
||
- `can_user_review_service()`: Ellenőrzi, hogy a user értékelheti-e a szervizt
|
||
|
||
5. **API végpontok** (`services.py`):
|
||
- `POST /services/{service_id}/reviews`: Értékelés beküldése (transaction_id kötelező!)
|
||
- `GET /services/{service_id}/reviews`: Értékelések listázása (pagination, sorting)
|
||
- `GET /services/{service_id}/reviews/check`: Ellenőrzi az értékelési jogosultságot
|
||
|
||
#### Tesztelés és Validáció:
|
||
|
||
- **Tranzakció validáció:** Csak a felhasználóhoz tartozó, sikeres tranzakciók elfogadva
|
||
- **Időablak validáció:** `REVIEW_WINDOW_DAYS`-nál régebbi tranzakciók elutasítva
|
||
- **Duplikáció védelem:** `UniqueConstraint` megakadályozza az ismétlődő értékeléseket
|
||
- **Trust score súlyozás:** A `TRUST_SCORE_INFLUENCE_FACTOR` befolyásolja az aggregált pontszámot
|
||
- **Weighted overall score:** A négy dimenzió súlyozott átlaga a `REVIEW_RATING_WEIGHTS` alapján
|
||
|
||
#### Függőségek:
|
||
|
||
- **Bemenet:** `FinancialLedger` tranzakciók (sikeres fizetések), `User` trust score, `ServiceProfile` adatok
|
||
- **Kimenet:** `ServiceReview` rekordok, frissített `ServiceProfile` aggregált értékelések, keresési rangsorolás
|
||
- **Adatbázis:** PostgreSQL, SQLAlchemy async session, Alembic migráció
|
||
|
||
#### Kapcsolódó Módosítások:
|
||
|
||
- **Modellek:** `social.py` (ServiceReview), `service.py` (ServiceProfile aggregált mezők), `identity.py` (User kapcsolat)
|
||
- **Service:** `marketplace_service.py` (verifikált értékelés logika)
|
||
- **API:** `services.py` (új végpontok)
|
||
- **Seed script:** `seed_system_params.py` (új rendszerparaméterek)
|
||
- **Logic Spec:** `plans/logic_spec_66_verified_service_reviews.md` (tervezési dokumentáció)
|
||
|
||
---
|
||
|
||
## Epic 5 Kártyák: #27, #28, #29 - Master Data Management & Robot Ecosystem
|
||
|
||
**Dátum:** 2026-03-12
|
||
**Státusz:** Kész ✅
|
||
**Kapcsolódó fájlok:**
|
||
- `backend/app/workers/vehicle/vehicle_robot_2_researcher.py`
|
||
- `backend/app/workers/vehicle/vehicle_robot_3_alchemist_pro.py`
|
||
- `backend/app/services/deduplication_service.py`
|
||
- `backend/app/models/vehicle_definitions.py`
|
||
- `backend/migrations/versions/715a999712ce_add_is_manual_column_to_vehicle_model_.py`
|
||
|
||
### Technikai Összefoglaló
|
||
|
||
Az Epic 5 (Master Data Management & Robot Ecosystem) három kártyáját implementáltuk, amelyek a robotok védelmét és adatminőségét javítják.
|
||
|
||
#### 1. #27 Kártya: Manuális felülírás elleni védelem (`is_manual` check)
|
||
|
||
**Cél:** Megakadályozni, hogy a manuálisan létrehozott és ellenőrzött rekordokat a robotok felülírják AI generált adatokkal.
|
||
|
||
**Implementáció:**
|
||
- A `vehicle_model_definitions` táblában már létezik az `is_manual` mező (Boolean, default False).
|
||
- Mindkét robot (Researcher és Alchemist Pro) SELECT lekérdezéseihez hozzáadtuk a `AND is_manual = FALSE` feltételt.
|
||
- Így a manuálisan létrehozott rekordok (`is_manual = TRUE`) kimaradnak a robot feldolgozásból.
|
||
|
||
**Módosított fájlok:**
|
||
- `vehicle_robot_2_researcher.py`: sor 164 (WHERE záradék)
|
||
- `vehicle_robot_3_alchemist_pro.py`: sor 182 (WHERE záradék)
|
||
|
||
#### 2. #28 Kártya: Regex modul a Researcher robotba
|
||
|
||
**Cél:** A nyers szövegből strukturált adatok (ccm, kW, motoradatok) kinyerése és JSON kontextusba ágyazása.
|
||
|
||
**Implementáció:**
|
||
- Új metódus `extract_specs_from_text` a `VehicleResearcher` osztályban, amely regex mintákkal kinyeri a köbcentimétert, kilowattot és motor kódot.
|
||
- A kinyert specifikációk a `research_metadata` JSON mezőbe kerülnek mentéskor.
|
||
- A regex támogatja a különböző formátumokat (cc, cm³, L, kW, HP, LE) és átváltásokat.
|
||
|
||
**Módosított fájlok:**
|
||
- `vehicle_robot_2_researcher.py`: új metódus és a `research_vehicle` frissítése.
|
||
|
||
#### 3. #29 Kártya: DeduplicationService létrehozása
|
||
|
||
**Cél:** Explicit deduplikáció a márka, technikai kód és jármű típus alapján, integrálva a mapping_rules.py és mapping_dictionary.py fájlokat.
|
||
|
||
**Implementáció:**
|
||
- Új service fájl: `backend/app/services/deduplication_service.py`
|
||
- Normalizációs függvények a márka, technikai kód és jármű osztály számára (szinonimák kezelése).
|
||
- Duplikátum keresés a `vehicle_model_definitions` táblában normalizált értékek alapján.
|
||
- Integráció a mapping_rules.py `unify_data` funkciójával.
|
||
- A service használható a robotokban és a manuális adatbeviteli felületeken.
|
||
|
||
**Függőségek:**
|
||
- **Bemenet:** `mapping_rules.py` (SOURCE_MAPPINGS, unify_data), opcionális `mapping_dictionary.py` (jelenleg beépített szótár)
|
||
- **Kimenet:** Duplikátum detektálás, normalizált adatok visszaadása.
|
||
|
||
### Tesztelés
|
||
|
||
A módosítások nem befolyásolják a meglévő funkcionalitást, mivel csak védelmi réteget adnak hozzá. A robotok továbbra is működnek, de kihagyják a manuális rekordokat. A regex modul csak akkor fut, ha van elég szöveg.
|
||
|
||
### Következő lépések
|
||
|
||
- A DeduplicationService integrálása a TechEnricher robotba (vehicle_robot_3_alchemist_pro.py) a duplikátum ellenőrzéshez a beszúrás előtt.
|
||
- A mapping_dictionary.py fájl kibővítése a valós szinonimákkal.
|
||
|
||
---
|
||
|
||
## Universal Schema Synchronizer Script
|
||
|
||
**Dátum:** 2026-03-12
|
||
**Státusz:** Kész ✅
|
||
**Kapcsolódó fájlok:** `backend/app/scripts/sync_engine.py`, `backend/app/models/__init__.py`, `backend/app/tests_internal/diagnostics/compare_schema.py`
|
||
|
||
### Technikai Összefoglaló
|
||
|
||
Létrehoztunk egy "Universal Schema Synchronizer" scriptet, amely dinamikusan importálja az összes SQLAlchemy modellt az `app/models` könyvtárból, összehasonlítja a live adatbázis sémával, és létrehozza a hiányzó táblákat és oszlopokat anélkül, hogy bármit törölne. A script célja, hogy ne kelljen Alembic-re támaszkodni a séma szinkronizáláshoz.
|
||
|
||
#### Főbb Implementációk:
|
||
|
||
1. **Dinamikus import (`sync_engine.py`):**
|
||
- Az `os.walk` segítségével bejárja az `app/models/` könyvtárat.
|
||
- Minden `.py` fájlt importál `importlib` használatával, hogy a `Base.metadata.tables` automatikusan feltöltődjön.
|
||
- A manuális importok mellett biztosítja, hogy minden modell betöltődik.
|
||
|
||
2. **Séma javítási logika:**
|
||
- A `compare_schema.py` ellenőrzési logikáját felhasználva összehasonlítja a modellek metadatáját a live adatbázissal.
|
||
- Hiányzó táblák esetén `CREATE TABLE` parancsot generál a SQLAlchemy `CreateTable` segítségével.
|
||
- Hiányzó oszlopok esetén `ALTER TABLE ADD COLUMN` parancsot generál, figyelembe véve a PostgreSQL típusokat (String → VARCHAR, Integer → INT, stb.).
|
||
- Kezeli a PostgreSQL enum típusokat (`marketplace.moderation_status`, `marketplace.source_type`) a táblák létrehozása előtt.
|
||
|
||
3. **Biztonsági intézkedések:**
|
||
- SOHA nem töröl semmit (DROP TABLE/COLUMN).
|
||
- Minden módosítás előtt kiírja a tervezett SQL parancsot a konzolra.
|
||
- Aszinkron kapcsolatot használ, és a `run_sync`-et alkalmazza az inspector műveletekhez.
|
||
|
||
4. **Modellek `__init__.py` frissítése:**
|
||
- A fájl megtartja a manuális importokat a kompatibilitás érdekében, de a dinamikus import garantálja, hogy minden modell betöltődik a `Base.metadata` számára.
|
||
|
||
#### Futás és Ellenőrzés:
|
||
|
||
- A script futtatása: `docker exec sf_api python /app/app/scripts/sync_engine.py`
|
||
- A szkript automatikusan futtatja a `compare_schema.py` diagnosztikát a szinkronizálás után, és csak akkor fejeződik be, ha minden zöld (100%-os szinkron).
|
||
- A teszt sikeresen lefutott, és a korábban hiányzó 10 tábla és számos oszlop létrejött.
|
||
|
||
#### Függőségek:
|
||
|
||
- **Bemenet:** SQLAlchemy modellek (`app/models`), adatbázis kapcsolat (settings.SQLALCHEMY_DATABASE_URI)
|
||
- **Kimenet:** Séma szinkronizálás, hiányzó elemek létrehozása, konzol log.
|
||
|
||
### Következő lépések
|
||
|
||
- A script integrálható a CI/CD folyamatba, hogy automatikusan szinkronizálja a sémát fejlesztői környezetekben.
|
||
- További fejlesztés: indexek és constraint-ek ellenőrzése/javítása.
|
||
|
||
---
|
||
|
||
*Megjegyzés:* A Universal Schema Synchronizer jelentősen csökkenti a függőséget az Alembic migrációktól, és lehetővé teszi a gyors séma frissítést fejlesztési és teszt környezetekben. A script csak bővítő műveleteket végez, soha nem töröl, íve biztonságos a használata.
|
||
|
||
---
|
||
|
||
## Automated Schema Registry & Deep Constraint Sync
|
||
|
||
**Dátum:** 2026-03-12
|
||
**Státusz:** Kész ✅
|
||
**Kapcsolódó fájlok:**
|
||
- `backend/app/models/registry.py`
|
||
- `backend/app/database.py`
|
||
- `backend/app/scripts/unified_db_sync.py`
|
||
- `backend/app/scripts/pre_start.sh`
|
||
- `docker-compose.yml`
|
||
|
||
### Technikai Összefoglaló
|
||
|
||
A manuális SQL javítások (pl. Unique Constraint hibák) kiküszöbölésére egy teljesen automatizált, deklaratív szinkronizációs rendszert építettünk ki. A rendszer központi modell regisztert használ, amely dinamikusan betölti az összes SQLAlchemy modellt, és egy kibővített sync engine, amely a hiányzó egyedi kényszereket és indexeket is létrehozza.
|
||
|
||
#### Főbb Implementációk:
|
||
|
||
1. **Központi Modell Regiszter (`registry.py`):**
|
||
- Automatikusan bejárja az `app/models` könyvtárat és importál minden `.py` fájlt.
|
||
- Biztosítja, hogy a `Base.metadata` teljesen feltöltődjenek a táblákkal, kényszerekkel és indexekkel.
|
||
- Két kulcsfüggvény: `load_all_models()` (dinamikus import) és `ensure_models_loaded()` (idempotens betöltés).
|
||
- A `database.py`-ban egy késleltetett import (`ensure_models_loaded`) garantálja, hogy az API indulásakor már minden modell elérhető legyen.
|
||
|
||
2. **Unified Sync Engine (`unified_db_sync.py`):**
|
||
- A korábbi `sync_engine.py` kibővítése, amely most már a hiányzó **UniqueConstraint** és **Index** objektumokat is detektálja és javítja.
|
||
- A `inspector.get_unique_constraints()` és `inspector.get_indexes()` metódusokkal összehasonlítja a modellben definiált kényszereket az adatbázis aktuális állapotával.
|
||
- Hiányzó kényszer esetén `ALTER TABLE ... ADD CONSTRAINT UNIQUE` SQL parancsot generál és végrehajt (ha `--apply` kapcsolóval futtatjuk).
|
||
- Hiányzó index esetén `CREATE INDEX` parancsot generál.
|
||
- A script támogatja a dry‑run módot (`--apply` nélkül), amikor csak kiírja a javasolt SQL‑eket.
|
||
|
||
3. **Startup Automatizálás (`pre_start.sh`):**
|
||
- Egy bash script, amelyet az API konténer indításakor futtatunk.
|
||
- Először lefuttatja az `unified_db_sync.py --apply` parancsot, hogy a séma és a kényszerek szinkronban legyenek.
|
||
- Ha a szinkronizáció sikeres, elindítja a FastAPI szervert (uvicorn).
|
||
|
||
4. **Docker‑compose integráció:**
|
||
- Az `api` szolgáltatás `command` mezője át lett írva a `pre_start.sh` futtatására.
|
||
- Így minden konténer indulás előtt automatikusan lefut a séma‑ és kényszer‑szinkronizáció.
|
||
|
||
#### Tesztelés és Validáció:
|
||
|
||
- **UniqueConstraint hozzáadása a CatalogDiscovery modellhez:** A `CatalogDiscovery` osztályhoz hozzáadtunk egy második egyedi kényszert (`uq_make_model_class`), amely a `make`, `model` és `vehicle_class` oszlopok kombinációját biztosítja egyedinek.
|
||
- **Sync futtatása:** Az `unified_db_sync.py --apply` parancs futtatásakor a script észlelte, hogy a kényszer már létezik az adatbázisban (korábbi migrációk miatt), így nem hozott létre újat. A kimenetben a `✅ Unique constraint on ('make', 'model', 'vehicle_class') exists.` üzenet igazolta, hogy a rendszer helyesen működik.
|
||
- **Adatbázis ellenőrzés:** A PostgreSQL `pg_constraint` táblájában látható, hogy a `uq_make_model_class` kényszer valóban jelen van.
|
||
|
||
#### Függőségek:
|
||
|
||
- **Bemenet:** SQLAlchemy modellek (összes `app/models/*.py`), live PostgreSQL adatbázis kapcsolat.
|
||
- **Kimenet:** Szinkronizált séma, hiányzó táblák, oszlopok, egyedi kényszerek és indexek létrehozva.
|
||
- **Környezet:** Docker konténer (`sf_api`), `shared‑postgres` adatbázis.
|
||
|
||
#### Kapcsolódó Módosítások:
|
||
|
||
- **Modellek:** `asset.py` – a `CatalogDiscovery.__table_args__` kibővítve egy új `UniqueConstraint`-tel.
|
||
- **Database:** `database.py` – `ensure_models_loaded()` függvény bevezetése a körkörös importok elkerülésére.
|
||
- **Scriptek:** `unified_db_sync.py` (új), `pre_start.sh` (új).
|
||
- **Docker:** `docker‑compose.yml` – az `api` service command módosítása.
|
||
|
||
### Következő lépések
|
||
|
||
- A `unified_db_sync.py` továbbfejleszthető a **foreign key** és **check constraint** ellenőrzésével.
|
||
- A script integrálható a CI/CD folyamatba, hogy minden pull request előtt lefusson egy dry‑run és jelezzen, ha a modellváltozások SQL parancsokat igényelnek.
|
||
|
||
---
|