feat(companies): add kvk_number column for B2B identity binding (WS-6)

WS-6 binding-target registry references company.kvk_number as a B2B
identity-key candidate. The column needed to exist on the model
before the registry could legitimately reference it. Nullable
because not every Company has a registered KvK (foreign companies,
partners, agencies); identity-key publish guards enforce presence
where required, not at schema level.

Changes:
- New migration `2026_04_28_140000_add_kvk_number_to_companies_table`
  adds nullable string column + index after `type`.
- Company::$fillable expanded.
- CompanyFactory generates an 8-digit KvK by default.
- CompanyKvkNumberTest covers attribute persistence, nullability,
  and information_schema-verified index existence.
- SCHEMA.md → v2.8 with the new column row + indexes line.
- Schema dump regenerated (CI fast-path).

Migration step counts in 5 backfill tests bumped +1 (the new
migration sits at the top of the migration stack):
  - FormFieldBindingMigrationTest:           18→19, 16→17
  - ConditionalLogicBackfillTest:             7→8
  - FormFieldConfigBackfillAndDropTest:      13→14
  - FormFieldOptionsBackfillTest:             3→4
  - FormFieldValidationRuleBackfillTest:     16→17

Refs: WS-6 sessie 3a binding-target drift audit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 20:17:56 +02:00
parent ccc9dc905b
commit 383b4fc5a3
11 changed files with 116 additions and 29 deletions

View File

@@ -18,6 +18,7 @@ final class CompanyFactory extends Factory
'organisation_id' => Organisation::factory(),
'name' => fake('nl_NL')->company(),
'type' => fake()->randomElement(['supplier', 'partner', 'agency', 'venue', 'other']),
'kvk_number' => fake()->numerify('########'),
'contact_first_name' => fake('nl_NL')->firstName(),
'contact_last_name' => fake('nl_NL')->lastName(),
'contact_email' => fake()->companyEmail(),

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Adds a nullable kvk_number column to companies. Crewli's binding-target
* registry references company.kvk_number as a B2B identity-key candidate
* (per WS-6 RFC binding registry); the column needed to exist on the
* model first.
*
* Nullable: not every Company has a registered KvK (foreign companies,
* partners, agencies). Identity-key validation runs at publish time per
* RequiresIdentityKeyBinding guard, not at the schema level.
*
* No backfill pre-launch the table is short and seed-driven.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('companies', function (Blueprint $table): void {
$table->string('kvk_number')->nullable()->after('type');
$table->index('kvk_number');
});
}
public function down(): void
{
Schema::table('companies', function (Blueprint $table): void {
$table->dropIndex(['kvk_number']);
$table->dropColumn('kvk_number');
});
}
};

View File

@@ -111,6 +111,7 @@ CREATE TABLE `companies` (
`organisation_id` char(26) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`type` enum('supplier','partner','agency','venue','other') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`kvk_number` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`contact_first_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`contact_last_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`contact_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
@@ -120,6 +121,7 @@ CREATE TABLE `companies` (
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `companies_organisation_id_index` (`organisation_id`),
KEY `companies_kvk_number_index` (`kvk_number`),
CONSTRAINT `companies_organisation_id_foreign` FOREIGN KEY (`organisation_id`) REFERENCES `organisations` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1747,3 +1749,4 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (152,'2026_04_27_10
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (153,'2026_04_27_100001_backfill_form_field_options',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (154,'2026_04_27_100002_drop_form_field_options_json_columns',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (155,'2026_04_28_100000_restore_default_crowd_type_id_foreign_key',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (156,'2026_04_28_140000_add_kvk_number_to_companies_table',3);