feat: Add organization member support with memberType field

Implements support for both individual persons and organizations as members.

Changes:
- Added MemberType enum (INDIVIDUAL, ORGANIZATION) to distinguish member types
- Added member_type column to database (defaults to INDIVIDUAL for backward compatibility)
- Added company_name field for organizations
- Made first_name nullable (required for individuals, optional for organizations)
- Implemented conditional validation:
  - INDIVIDUAL members require first_name
  - ORGANIZATION members require company_name
- Updated GraphQL schema with new memberType and companyName fields
- Updated all resolvers to handle new fields and validation
- Added comprehensive unit tests for validation logic
- Updated existing tests to work with new fields
- All 53 tests passing

Technical notes:
- Using memberType instead of 'kind' to avoid GraphQL introspection conflicts
- Using native_enum=False for SQLite compatibility
- Using batch_alter_table for SQLite ALTER COLUMN compatibility
- Backward compatible: existing members automatically become INDIVIDUAL type

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-04 12:15:55 +01:00
co-authored by Claude
parent 630af11403
commit 5053b80998
12 changed files with 375 additions and 40 deletions
@@ -0,0 +1,59 @@
# Spec: Organization Support
## ADDED Requirements
### Requirement: Distinguish member kind
The system MUST allow defining a member as either an individual or an organization.
#### Scenario: Create individual member
**Given** client provides kind="INDIVIDUAL"
**And** first_name="John"
**When** createMember mutation executes
**Then** member is created with kind INDIVIDUAL
**And** first_name is "John"
#### Scenario: Create organization member
**Given** client provides kind="ORGANIZATION"
**And** company_name="Acme Corp"
**When** createMember mutation executes
**Then** member is created with kind ORGANIZATION
**And** company_name is "Acme Corp"
### Requirement: Conditional mandatory fields
The system MUST enforce different mandatory fields based on member kind.
#### Scenario: Organization requires company name
**Given** client provides kind="ORGANIZATION"
**And** company_name is missing or empty
**When** createMember mutation executes
**Then** validation error is raised
**And** error message indicates company_name is required for organizations
#### Scenario: Organization allows missing first name
**Given** client provides kind="ORGANIZATION"
**And** company_name="Acme Corp"
**And** first_name is missing
**When** createMember mutation executes
**Then** member is created successfully
**And** first_name is null
#### Scenario: Individual requires first name
**Given** client provides kind="INDIVIDUAL"
**And** first_name is missing
**When** createMember mutation executes
**Then** validation error is raised
**And** error message indicates first_name is required for individuals
## MODIFIED Requirements
### Requirement: Member data structure
The Member data structure MUST be modified to include kind and company_name, and allow nullable first_name.
#### Scenario: Query member includes new fields
**Given** member exists with kind="ORGANIZATION" and company_name="Acme Corp"
**When** member query executes requesting kind and companyName
**Then** kind is returned as "ORGANIZATION"
**And** companyName is returned as "Acme Corp"