Files
Lesstime/migrations/Version20260622100916.php
matthieu 8313c759c6
Auto Tag Develop / tag (push) Successful in 9s
Migration modular monolith DDD (0.1 → 3.3) (#17)
## Migration modular monolith DDD — Lesstime (0.1 → 3.3)

Cette MR regroupe l'intégralité de la refonte en monolithe modulaire (strangler progressif, additif). Elle remplace les MR stackées de Phase 1 (#12–#16), désormais incluses ici.

**Ne pas merger avant validation fonctionnelle** : branche destinée à être testée telle quelle.

### Périmètre — 9 modules sous `src/Module/`
| Phase | Module | Contenu |
|------|--------|---------|
| 0.1 | (socle) | infrastructure modulaire, `ModuleInterface`, mapping Doctrine par module |
| 0.2 | (socle front) | auto-détection des layers Nuxt sous `frontend/modules/*` |
| 1.1 | **Core** | Identité (User/Auth), Notifications, Notifier |
| 1.2 | Core | RBAC fin (permissions `module.resource.action`, sidebar gated) |
| 1.3 | Core | Audit log (`#[Auditable]`, listener, provider DBAL) |
| 2.1 | **TimeTracking** | TimeEntry + MCP + export |
| 2.2 | **ProjectManagement** | cœur métier Projets/Tâches + 38 MCP tools |
| 2.3 | **Absence** | demandes, soldes, policies, justificatifs |
| 2.4 | **Directory** | Clients (migrés) + **Prospects** (nouveau, conversion → Client) |
| 2.5 | **Mail** | intégration IMAP OVH + liens tâches |
| 2.6 | **Integration** | Gitea / BookStack / Zimbra / Share |
| 3.1 | **Reporting** | rapports transverses (DBAL read-only, 0 import inter-module) |
| 3.2 | **ClientPortal** | portail client (ROLE_CLIENT cloisonné, tickets, notifications) |
| 3.3 | (finition) | nettoyage legacy — `src/Entity` vide, app 100% modulaire |

### Architecture
- Découplage inter-modules par **contrats** (`UserInterface`, `ProjectInterface`, `TaskInterface`, `TaskTagInterface`, `ClientInterface`, `ClientTicketInterface`, `LeaveProfileInterface`) + `resolve_target_entities` 100% modulaire (aucune cible legacy).
- Repositories : interface `Domain/Repository` + implémentation `Infrastructure/Doctrine`, bindées.
- Reporting en DBAL read-only pur (aucun import d'entité d'un autre module).
- Chaque migration de module : déplacement à comportement préservé (API publique et noms d'outils MCP inchangés), migrations **additives** uniquement (zéro destructif).

### Sécurité
- ROLE_CLIENT cloisonné : un utilisateur client n'accède qu'à `/portal` et à ses propres tickets (filtrés par `allowedProjects`), interdit sur toute l'API interne.
- Correctif : interdiction pour un client de créer un lien vers le partage SMB (upload uniquement).

### QA non-régression (branche reconstruite from scratch)
- Migrations from scratch + fixtures : OK.
- Compilation dev + prod : OK.
- **180 tests PHPUnit verts**, php-cs-fixer clean, ~96 routes, **66 outils MCP** tous sous `App\Module\*`.
- Smoke test runtime multi-rôles (admin / ROLE_USER / ROLE_CLIENT) : 44 vérifications HTTP, **0 écart**, cloisonnement client étanche.
- Build Nuxt OK, 9 layers, 0 import legacy résiduel.

### Points à arbitrer (hors périmètre de cette migration)
- Durcissement MCP/IDOR pré-existant (`userId` explicite sans scoping sur certains tools TimeTracking/Absence/TaskDocument) — ticket dédié recommandé.
- Validation fonctionnelle de **Prospect** et **ClientPortal** (conçus depuis les specs disque).
- **Harmonisation visuelle Malio finale** (3.3) — finition esthétique inter-modules laissée au PO.

---

## ⚠️ Déploiement / migration des données — à ne pas oublier

### 1. Resynchroniser les séquences PostgreSQL après tout import/restore de dump
Si la prod (ou tout environnement) est **montée depuis un dump** (`pg_restore` / `COPY`), les lignes sont chargées avec leurs `id` explicites **sans avancer les séquences** → au premier `INSERT` : `duplicate key value violates unique constraint "..._pkey"` (constaté en local sur `notification`, `task`, `time_entry`…).

À lancer **juste après chaque restore/import** :

```sql
DO $$
DECLARE r RECORD; maxid BIGINT; seq TEXT;
BEGIN
  FOR r IN SELECT table_name, column_name FROM information_schema.columns WHERE table_schema='public'
  LOOP
    seq := pg_get_serial_sequence(quote_ident(r.table_name), r.column_name);
    IF seq IS NOT NULL THEN
      EXECUTE format('SELECT COALESCE(MAX(%I),0) FROM %I', r.column_name, r.table_name) INTO maxid;
      PERFORM setval(seq, GREATEST(maxid,1), maxid > 0);
    END IF;
  END LOOP;
END $$;
```

> Ne concerne **pas** une prod qui tourne déjà (séquences avancées organiquement) — uniquement le cas restore/import. Idempotent, sans risque.

### 2. Fix dénormalisation des collections typées-contrat (code, inclus dans la branche)
Les relations **to-many** typées par une interface `Shared\Domain\Contract\*` (`TimeEntry::tags` → `TaskTagInterface`, `Task::collaborators` → `UserInterface`) étaient **indénormalisables par API Platform** (mono-valué OK via IRI, collection KO) → **tout POST/PATCH portant une telle collection renvoyait 400/500**. Corrigé par un dénormaliseur générique `ContractRelationDenormalizer` (réutilise `resolve_target_entities`, zéro couplage par-entité) + test fonctionnel de non-régression.

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #17
2026-06-23 13:50:42 +00:00

223 lines
22 KiB
PHP

<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260622100916 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE commercial_report (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, subject VARCHAR(255) NOT NULL, body TEXT DEFAULT NULL, occurred_at DATE NOT NULL, type VARCHAR(32) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updated_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, client_id INT DEFAULT NULL, prospect_id INT DEFAULT NULL, author_id INT DEFAULT NULL, created_by INT DEFAULT NULL, updated_by INT DEFAULT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE INDEX IDX_886919D819EB6921 ON commercial_report (client_id)');
$this->addSql('CREATE INDEX IDX_886919D8D182060A ON commercial_report (prospect_id)');
$this->addSql('CREATE INDEX IDX_886919D8F675F31B ON commercial_report (author_id)');
$this->addSql('CREATE INDEX IDX_886919D8DE12AB56 ON commercial_report (created_by)');
$this->addSql('CREATE INDEX IDX_886919D816FE72E1 ON commercial_report (updated_by)');
$this->addSql('CREATE TABLE directory_address (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, label VARCHAR(255) DEFAULT NULL, street VARCHAR(255) DEFAULT NULL, street_complement VARCHAR(255) DEFAULT NULL, postal_code VARCHAR(20) DEFAULT NULL, city VARCHAR(255) DEFAULT NULL, country VARCHAR(2) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updated_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, client_id INT DEFAULT NULL, prospect_id INT DEFAULT NULL, created_by INT DEFAULT NULL, updated_by INT DEFAULT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE INDEX IDX_6E5D970719EB6921 ON directory_address (client_id)');
$this->addSql('CREATE INDEX IDX_6E5D9707D182060A ON directory_address (prospect_id)');
$this->addSql('CREATE INDEX IDX_6E5D9707DE12AB56 ON directory_address (created_by)');
$this->addSql('CREATE INDEX IDX_6E5D970716FE72E1 ON directory_address (updated_by)');
$this->addSql('CREATE TABLE directory_contact (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, first_name VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, job_title VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, phone_primary VARCHAR(50) DEFAULT NULL, phone_secondary VARCHAR(50) DEFAULT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updated_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, client_id INT DEFAULT NULL, prospect_id INT DEFAULT NULL, created_by INT DEFAULT NULL, updated_by INT DEFAULT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE INDEX IDX_2F711EBE19EB6921 ON directory_contact (client_id)');
$this->addSql('CREATE INDEX IDX_2F711EBED182060A ON directory_contact (prospect_id)');
$this->addSql('CREATE INDEX IDX_2F711EBEDE12AB56 ON directory_contact (created_by)');
$this->addSql('CREATE INDEX IDX_2F711EBE16FE72E1 ON directory_contact (updated_by)');
$this->addSql('CREATE TABLE report_document (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, original_name VARCHAR(255) NOT NULL, file_name VARCHAR(255) DEFAULT NULL, mime_type VARCHAR(100) NOT NULL, size INT NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, commercial_report_id INT NOT NULL, uploaded_by_id INT DEFAULT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE INDEX IDX_D809130B4AB5D1D4 ON report_document (commercial_report_id)');
$this->addSql('CREATE INDEX IDX_D809130BA2B28FE8 ON report_document (uploaded_by_id)');
$this->addSql('ALTER TABLE commercial_report ADD CONSTRAINT FK_886919D819EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE commercial_report ADD CONSTRAINT FK_886919D8D182060A FOREIGN KEY (prospect_id) REFERENCES prospect (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE commercial_report ADD CONSTRAINT FK_886919D8F675F31B FOREIGN KEY (author_id) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE commercial_report ADD CONSTRAINT FK_886919D8DE12AB56 FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE commercial_report ADD CONSTRAINT FK_886919D816FE72E1 FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_address ADD CONSTRAINT FK_6E5D970719EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_address ADD CONSTRAINT FK_6E5D9707D182060A FOREIGN KEY (prospect_id) REFERENCES prospect (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_address ADD CONSTRAINT FK_6E5D9707DE12AB56 FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_address ADD CONSTRAINT FK_6E5D970716FE72E1 FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_contact ADD CONSTRAINT FK_2F711EBE19EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_contact ADD CONSTRAINT FK_2F711EBED182060A FOREIGN KEY (prospect_id) REFERENCES prospect (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_contact ADD CONSTRAINT FK_2F711EBEDE12AB56 FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE directory_contact ADD CONSTRAINT FK_2F711EBE16FE72E1 FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
$this->addSql('ALTER TABLE report_document ADD CONSTRAINT FK_D809130B4AB5D1D4 FOREIGN KEY (commercial_report_id) REFERENCES commercial_report (id) ON DELETE CASCADE NOT DEFERRABLE');
$this->addSql('ALTER TABLE report_document ADD CONSTRAINT FK_D809130BA2B28FE8 FOREIGN KEY (uploaded_by_id) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE');
// Ownership CHECK constraints: each row must belong to a client or a prospect.
$this->addSql('ALTER TABLE directory_contact ADD CONSTRAINT chk_contact_owner CHECK (client_id IS NOT NULL OR prospect_id IS NOT NULL)');
$this->addSql('ALTER TABLE directory_address ADD CONSTRAINT chk_address_owner CHECK (client_id IS NOT NULL OR prospect_id IS NOT NULL)');
$this->addSql('ALTER TABLE commercial_report ADD CONSTRAINT chk_report_owner CHECK (client_id IS NOT NULL OR prospect_id IS NOT NULL)');
// Data migration: copy existing inline addresses into directory_address before dropping the columns.
$this->addSql("INSERT INTO directory_address (label, street, postal_code, city, country, client_id, created_at, updated_at)
SELECT 'Principale', street, postal_code, city, 'FR', id, NOW(), NOW()
FROM client
WHERE COALESCE(street, '') <> '' OR COALESCE(city, '') <> '' OR COALESCE(postal_code, '') <> ''");
$this->addSql("INSERT INTO directory_address (label, street, postal_code, city, country, prospect_id, created_at, updated_at)
SELECT 'Principale', street, postal_code, city, 'FR', id, NOW(), NOW()
FROM prospect
WHERE COALESCE(street, '') <> '' OR COALESCE(city, '') <> '' OR COALESCE(postal_code, '') <> ''");
$this->addSql('COMMENT ON COLUMN absence_balance.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_balance.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_balance.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_balance.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_policy.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_policy.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_policy.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN absence_policy.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.updated_by IS \'\'');
$this->addSql('ALTER TABLE client DROP street');
$this->addSql('ALTER TABLE client DROP city');
$this->addSql('ALTER TABLE client DROP postal_code');
$this->addSql('COMMENT ON COLUMN client.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN client.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN client.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN client.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN project.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN project.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN project.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN project.updated_by IS \'\'');
$this->addSql('ALTER TABLE prospect DROP street');
$this->addSql('ALTER TABLE prospect DROP city');
$this->addSql('ALTER TABLE prospect DROP postal_code');
$this->addSql('COMMENT ON COLUMN prospect.status IS \'\'');
$this->addSql('COMMENT ON COLUMN prospect.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN prospect.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN prospect.converted_client_id IS \'\'');
$this->addSql('COMMENT ON COLUMN prospect.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN prospect.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN share_configuration.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN share_configuration.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN share_configuration.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN share_configuration.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN task.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN task.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN task.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN task.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN time_entry.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN time_entry.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN time_entry.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN time_entry.updated_by IS \'\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.created_at IS \'\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.updated_at IS \'\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.created_by IS \'\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.updated_by IS \'\'');
$this->addSql('DROP INDEX idx_75ea56e016ba31db');
$this->addSql('DROP INDEX idx_75ea56e0fb7336f0');
$this->addSql('DROP INDEX idx_75ea56e0e3bd61ce');
$this->addSql('ALTER TABLE messenger_messages ALTER id DROP DEFAULT');
$this->addSql('ALTER TABLE messenger_messages ALTER id ADD GENERATED BY DEFAULT AS IDENTITY');
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE commercial_report DROP CONSTRAINT FK_886919D819EB6921');
$this->addSql('ALTER TABLE commercial_report DROP CONSTRAINT FK_886919D8D182060A');
$this->addSql('ALTER TABLE commercial_report DROP CONSTRAINT FK_886919D8F675F31B');
$this->addSql('ALTER TABLE commercial_report DROP CONSTRAINT FK_886919D8DE12AB56');
$this->addSql('ALTER TABLE commercial_report DROP CONSTRAINT FK_886919D816FE72E1');
$this->addSql('ALTER TABLE directory_address DROP CONSTRAINT FK_6E5D970719EB6921');
$this->addSql('ALTER TABLE directory_address DROP CONSTRAINT FK_6E5D9707D182060A');
$this->addSql('ALTER TABLE directory_address DROP CONSTRAINT FK_6E5D9707DE12AB56');
$this->addSql('ALTER TABLE directory_address DROP CONSTRAINT FK_6E5D970716FE72E1');
$this->addSql('ALTER TABLE directory_contact DROP CONSTRAINT FK_2F711EBE19EB6921');
$this->addSql('ALTER TABLE directory_contact DROP CONSTRAINT FK_2F711EBED182060A');
$this->addSql('ALTER TABLE directory_contact DROP CONSTRAINT FK_2F711EBEDE12AB56');
$this->addSql('ALTER TABLE directory_contact DROP CONSTRAINT FK_2F711EBE16FE72E1');
$this->addSql('ALTER TABLE report_document DROP CONSTRAINT FK_D809130B4AB5D1D4');
$this->addSql('ALTER TABLE report_document DROP CONSTRAINT FK_D809130BA2B28FE8');
$this->addSql('DROP TABLE commercial_report');
$this->addSql('DROP TABLE directory_address');
$this->addSql('DROP TABLE directory_contact');
$this->addSql('DROP TABLE report_document');
$this->addSql('COMMENT ON COLUMN absence_balance.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN absence_balance.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN absence_balance.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN absence_balance.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN absence_policy.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN absence_policy.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN absence_policy.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN absence_policy.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN book_stack_configuration.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('ALTER TABLE client ADD street VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE client ADD city VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE client ADD postal_code VARCHAR(20) DEFAULT NULL');
$this->addSql('COMMENT ON COLUMN client.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN client.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN client.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN client.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN gitea_configuration.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN mail_configuration.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('DROP INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750');
$this->addSql('ALTER TABLE messenger_messages ALTER id SET DEFAULT nextval(\'messenger_messages_id_seq\'::regclass)');
$this->addSql('ALTER TABLE messenger_messages ALTER id DROP IDENTITY');
$this->addSql('CREATE INDEX idx_75ea56e016ba31db ON messenger_messages (delivered_at)');
$this->addSql('CREATE INDEX idx_75ea56e0fb7336f0 ON messenger_messages (queue_name)');
$this->addSql('CREATE INDEX idx_75ea56e0e3bd61ce ON messenger_messages (available_at)');
$this->addSql('COMMENT ON COLUMN project.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN project.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN project.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN project.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('ALTER TABLE prospect ADD street VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE prospect ADD city VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE prospect ADD postal_code VARCHAR(20) DEFAULT NULL');
$this->addSql('COMMENT ON COLUMN prospect.status IS \'Prospect pipeline status (ProspectStatus enum: new, contacted, qualified, won, lost)\'');
$this->addSql('COMMENT ON COLUMN prospect.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN prospect.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN prospect.converted_client_id IS \'Client created when the prospect is converted (FK client.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN prospect.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN prospect.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN share_configuration.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN share_configuration.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN share_configuration.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN share_configuration.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN task.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN task.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN task.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN task.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN time_entry.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN time_entry.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN time_entry.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN time_entry.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.created_at IS \'Creation timestamp (Timestampable, set on prePersist)\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.updated_at IS \'Last update timestamp (Timestampable, set on prePersist/preUpdate)\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.created_by IS \'User who created the entry (Blamable, FK user.id, SET NULL on delete)\'');
$this->addSql('COMMENT ON COLUMN zimbra_configuration.updated_by IS \'User who last updated the entry (Blamable, FK user.id, SET NULL on delete)\'');
}
}