feat(freight): track onboarding-phase edit history for companies

Ticket #238 — pre-approval edits and document uploads write straight
to the live company row with no approval gate and, until now, no
trace. Adds an append-only company_revisions log (diffed field
changes, document uploads) recorded from updateProfile and
uploadCompanyDocuments, exposed via GET /companies/:id/revisions and
shown as "Version history" on the backoffice customer detail page.
This commit is contained in:
Nathnael
2026-07-31 11:45:09 +00:00
parent d67297a601
commit 7c5d96795c
18 changed files with 383 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';
/**
* Append-only audit of company edits made before the company reaches Active
* (the onboarding phase) — that write path has no approval gate and, until
* now, left no trace of what changed (e.g. a phone number or a document).
*/
export class CreateCompanyRevisions3130000000000 implements MigrationInterface {
name = 'CreateCompanyRevisions3130000000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
schema: 'freight',
name: 'company_revisions',
columns: [
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'gen_random_uuid()' },
{ name: 'company_id', type: 'uuid' },
{ name: 'actor_id', type: 'uuid', isNullable: true },
{ name: 'summary', type: 'varchar', length: '255' },
{ name: 'changes', type: 'jsonb', default: "'[]'::jsonb" },
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
],
foreignKeys: [
{
columnNames: ['company_id'],
referencedSchema: 'freight',
referencedTableName: 'companies',
referencedColumnNames: ['id'],
onDelete: 'CASCADE',
},
],
}),
true,
);
await queryRunner.createIndex(
'freight.company_revisions',
new TableIndex({ name: 'idx_company_revisions_company', columnNames: ['company_id'] }),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('freight.company_revisions', true);
}
}