Files
crewli/api/app/Models/Company.php
bert.hausmans 383b4fc5a3 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>
2026-04-29 00:14:12 +02:00

63 lines
1.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Models;
use App\Models\Scopes\OrganisationScope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
final class Company extends Model
{
use HasFactory;
use HasUlids;
use SoftDeletes;
protected static function booted(): void
{
self::addGlobalScope(new OrganisationScope);
}
protected $fillable = [
'organisation_id',
'name',
'type',
'kvk_number',
'contact_first_name',
'contact_last_name',
'contact_email',
'contact_phone',
];
public function getContactFullNameAttribute(): ?string
{
if (! $this->contact_first_name) {
return null;
}
return trim("{$this->contact_first_name} {$this->contact_last_name}");
}
public function organisation(): BelongsTo
{
return $this->belongsTo(Organisation::class);
}
public function persons(): HasMany
{
return $this->hasMany(Person::class);
}
/** @param Builder<self> $query */
public function scopeOrdered(Builder $query): Builder
{
return $query->orderBy('name');
}
}