libra/docs/stories/story-2.2-company-client-ac...

16 KiB

Story 2.2: Company/Corporate Client Account Management

Epic Reference

Epic 2: User Management System

User Story

As an admin, I want to create, view, edit, and manage company/corporate client accounts, So that I can serve corporate clients with their unique data requirements.

Story Context

Existing System Integration

  • Integrates with: users table (company-specific fields), admin_logs table
  • Technology: Livewire Volt (class-based), Flux UI forms, Pest tests
  • Follows pattern: Same CRUD pattern as Story 2.1 Individual Clients
  • Key Files to Create/Modify:
    • resources/views/livewire/admin/users/company/ - Volt components (index, create, edit, show)
    • app/Models/User.php - Add scopeCompany() method
    • resources/lang/ar/messages.php - Arabic translations
    • resources/lang/en/messages.php - English translations
    • tests/Feature/Admin/CompanyClientTest.php - Feature tests

Acceptance Criteria

Create Company Client

  • Form with required fields:
    • Company Name (required)
    • Company Registration Number (required, unique)
    • Contact Person Name (required)
    • Contact Person ID (required)
    • Email Address (required, unique)
    • Phone Number (required)
    • Password (admin-set, required)
    • Preferred Language (Arabic/English dropdown)
  • Validation for all required fields
  • Duplicate email/registration number prevention
  • Success message on creation

Multiple Contact Persons (OUT OF SCOPE)

Note: Multiple contact persons support is deferred to a future enhancement story. This story implements single contact person stored directly on the users table. The contact_persons table migration in Technical Notes is for reference only if this feature is later prioritized.

List View

  • Display all company clients (user_type = 'company')
  • Columns: Company Name, Contact Person, Email, Reg #, Status, Created Date
  • Pagination (10/25/50 per page)
  • Default sort by created date

Search & Filter

  • Search by company name, email, or registration number
  • Filter by status (active/deactivated/all)
  • Real-time search with debounce

Edit Company

  • Edit all company information
  • Update contact person details
  • Validation same as create
  • Success message on update

View Company Profile

  • Display all company information (including contact person details)
  • Show consultation history summary
  • Show timeline history summary

Quality Requirements

  • Bilingual form labels and messages
  • Proper form validation
  • Audit log entries for all operations
  • Tests for CRUD operations

Technical Notes

User Model Scope

public function scopeCompany($query)
{
    return $query->where('user_type', 'company');
}

Database Fields for Company

users table:
  - company_name (nullable, required for company type)
  - company_registration (nullable, unique when not null)
  - contact_person_name (nullable, required for company)
  - contact_person_id (nullable, required for company)

Future Reference: Separate Contact Persons Table

Note: This migration is NOT part of this story. It is preserved here for future reference if multiple contact persons feature is prioritized.

// contact_persons migration (FUTURE - NOT IN SCOPE)
Schema::create('contact_persons', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('name');
    $table->string('national_id');
    $table->string('phone')->nullable();
    $table->string('email')->nullable();
    $table->boolean('is_primary')->default(false);
    $table->timestamps();
});

Validation Rules

public function rules(): array
{
    return [
        'company_name' => ['required', 'string', 'max:255'],
        'company_registration' => ['required', 'string', 'unique:users,company_registration'],
        'contact_person_name' => ['required', 'string', 'max:255'],
        'contact_person_id' => ['required', 'string'],
        'email' => ['required', 'email', 'unique:users,email'],
        'phone' => ['required', 'string'],
        'password' => ['required', 'string', 'min:8'],
        'preferred_language' => ['required', 'in:ar,en'],
    ];
}

Volt Component for Create

Follow the same component structure as Story 2.1 (docs/stories/story-2.1-individual-client-account-management.md).

<?php

use App\Models\User;
use App\Models\AdminLog; // Assumes AdminLog model exists from Story 1.1
use Livewire\Volt\Component;
use Illuminate\Support\Facades\Hash;

new class extends Component {
    public string $company_name = '';
    public string $company_registration = '';
    public string $contact_person_name = '';
    public string $contact_person_id = '';
    public string $email = '';
    public string $phone = '';
    public string $password = '';
    public string $preferred_language = 'ar';

    public function create(): void
    {
        $validated = $this->validate();

        $user = User::create([
            ...$validated,
            'user_type' => 'company',
            'name' => $this->company_name, // For display purposes
            'password' => Hash::make($this->password),
            'status' => 'active',
        ]);

        // Log action (same pattern as Story 2.1)
        AdminLog::create([
            'admin_id' => auth()->id(),
            'action_type' => 'create',
            'target_type' => 'user',
            'target_id' => $user->id,
            'new_values' => $user->only(['company_name', 'email', 'company_registration']),
            'ip_address' => request()->ip(),
        ]);

        // Send welcome email (depends on Story 2.5)

        session()->flash('success', __('messages.company_created'));
        $this->redirect(route('admin.users.index'));
    }
};

Testing Requirements

Test Approach

  • Feature tests using Pest with Volt::test() for Livewire components
  • Factory-based test data generation

Key Test Scenarios

Create Company Client

  • Successfully create company with all valid required fields
  • Validation fails when required fields are missing
  • Validation fails for duplicate email address
  • Validation fails for duplicate company registration number
  • Preferred language defaults to Arabic when not specified
  • Audit log entry created on successful creation

List View

  • List displays only company type users (excludes individual/admin)
  • Pagination works correctly (10/25/50 per page)
  • Default sort is by created date descending

Search & Filter

  • Search by company name returns correct results
  • Search by email returns correct results
  • Search by registration number returns correct results
  • Filter by active status works
  • Filter by deactivated status works
  • Combined search and filter works correctly

Edit Company

  • Successfully update company information
  • Validation fails for duplicate email (excluding current record)
  • Validation fails for duplicate registration number (excluding current record)
  • Audit log entry created on successful update

View Profile

  • Profile displays all company information correctly
  • Consultation history summary displays (empty state if none)
  • Timeline history summary displays (empty state if none)

Bilingual Support

  • Form labels display correctly in Arabic
  • Form labels display correctly in English
  • Validation messages display in user's preferred language

Definition of Done

  • Create company client form works
  • List view displays all company clients
  • Search and filter functional
  • Edit company works with validation
  • View profile shows complete information
  • Duplicate prevention works
  • Audit logging implemented
  • Bilingual support complete
  • Tests pass for all CRUD operations
  • Code formatted with Pint

Dependencies

  • Epic 1: Authentication system, database schema
  • Story 1.1: Database schema must include the following columns in users table:
    • user_type (enum: 'individual', 'company', 'admin')
    • status (enum: 'active', 'deactivated')
    • phone (string)
    • preferred_language (enum: 'ar', 'en')
    • company_name (nullable string)
    • company_registration (nullable string, unique when not null)
    • contact_person_name (nullable string)
    • contact_person_id (nullable string)
    • national_id (nullable string, unique when not null)
  • Story 2.1: CRUD patterns established in docs/stories/story-2.1-individual-client-account-management.md

Risk Assessment

  • Primary Risk: Complex contact persons relationship
  • Mitigation: Start simple (single contact), enhance later if needed
  • Rollback: Use simple fields on users table

Estimation

Complexity: Medium Estimated Effort: 4-5 hours


Dev Agent Record

Status

Ready for Review

Agent Model Used

Claude Opus 4.5 (claude-opus-4-5-20251101)

File List

Created:

  • resources/views/livewire/admin/clients/company/index.blade.php - List view with search, filter, pagination
  • resources/views/livewire/admin/clients/company/create.blade.php - Create company form
  • resources/views/livewire/admin/clients/company/edit.blade.php - Edit company form
  • resources/views/livewire/admin/clients/company/show.blade.php - Company profile view
  • tests/Feature/Admin/CompanyClientTest.php - 39 feature tests

Modified:

  • routes/web.php - Added company client routes
  • lang/en/clients.php - Added company-specific translations
  • lang/ar/clients.php - Added Arabic company-specific translations

Change Log

Date Change Reason
2025-12-26 Created company CRUD Volt components Story requirement
2025-12-26 Added routes for company client management Story requirement
2025-12-26 Added bilingual translations (EN/AR) Story requirement
2025-12-26 Created 39 feature tests Story requirement

Completion Notes

  • Used company_cert_number field (existing schema) instead of company_registration mentioned in story
  • User model already had scopeCompanies() method from Story 2.1 setup
  • All 239 tests pass (39 new company client tests + existing tests)
  • Components follow same pattern as Individual Client components from Story 2.1
  • Flux UI free components used throughout

Debug Log References

None - no blocking issues encountered

Future Recommendations

  • Consider adding navigation link in admin sidebar to Company Clients page
  • Multiple contact persons feature is deferred (as noted in story)
  • Welcome email on company creation depends on Story 2.5

QA Results

Review Date: 2025-12-26

Reviewed By: Quinn (Test Architect)

Code Quality Assessment

The implementation is well-structured and follows established patterns from Story 2.1 (Individual Clients). The code demonstrates:

  • Consistent architecture: All Volt components follow the class-based pattern established in the codebase
  • Proper separation of concerns: Each CRUD operation has its own dedicated component
  • Clean validation logic: Rules are properly defined with appropriate error messages
  • Effective use of Eloquent: Scopes, eager loading, and relationship counts are used efficiently
  • Bilingual support: Complete translations for both Arabic and English

Refactoring Performed

None required. The implementation is clean and follows project conventions.

Compliance Check

  • Coding Standards: ✓ Pint formatting passes, follows Laravel/Livewire best practices
  • Project Structure: ✓ Files placed in correct locations (resources/views/livewire/admin/clients/company/)
  • Testing Strategy: ✓ 39 comprehensive Pest tests using Volt::test() pattern
  • All ACs Met: ✓ All acceptance criteria verified and implemented

Requirements Traceability

AC # Acceptance Criteria Test Coverage
1.1 Create form with all required fields admin can create company client with all valid data
1.2 Validation for required fields 8 tests covering each required field validation
1.3 Duplicate email/registration prevention cannot create company with duplicate email, cannot create company with duplicate registration number
1.4 Success message on creation Verified via redirect assertion
2.1 Display company clients only index page displays only company clients
2.2 Correct columns displayed profile page displays all company information
2.3 Pagination pagination works with different per page values
2.4 Default sort by created_at company clients sorted by created_at desc by default
3.1 Search by company name can search companies by company name (partial match)
3.2 Search by email can search companies by email (partial match)
3.3 Search by registration number can search companies by registration number (partial match)
3.4 Filter by status can filter companies by active status, can filter companies by deactivated status
3.5 Combined search/filter combined search and filter works correctly
4.1 Edit company information can edit existing company information
4.2 Validation on edit validation fails for duplicate email excluding own record, validation fails for duplicate registration number excluding own record
5.1 View company profile profile page displays all company information
5.2 Consultation history company profile shows consultation count
5.3 Timeline history company profile shows timeline count
6.1 Bilingual support Verified via translation files (EN/AR complete)
6.2 Audit logging admin log entry created on successful company creation, admin log entry created on successful company update

Test Architecture Assessment

Test Coverage: Excellent (39 tests, 112 assertions)

Category Tests Status
Create Operations 16 ✓ PASS
List View 3 ✓ PASS
Search & Filter 7 ✓ PASS
Edit Operations 7 ✓ PASS
View Profile 4 ✓ PASS
Authorization 2 ✓ PASS

Test Design Quality:

  • Uses factory states (company(), individual(), admin(), deactivated()) effectively
  • Proper isolation with beforeEach setup
  • Clear, descriptive test names following "can/cannot" pattern
  • Covers happy paths, validation failures, and edge cases

Improvements Checklist

  • All required fields validated
  • Unique constraints enforced for email and company_cert_number
  • Edit form ignores own record for uniqueness validation
  • Authorization tests for admin-only access
  • Unauthenticated user redirect tests
  • Audit logging for create/update operations
  • Partial match search implementation
  • Real-time search with debounce (300ms)
  • Consider adding delete/deactivate functionality (future story)
  • Consider adding bulk operations (future story)
  • Consider adding export functionality (future story)

Security Review

Status: PASS

  • ✓ Admin middleware enforced on all company client routes
  • ✓ Active user middleware applied
  • ✓ Form validation prevents SQL injection via parameterized queries
  • ✓ Password hashing using Hash::make()
  • ✓ National ID hidden in serialization (User model $hidden)
  • ✓ No direct user input in raw SQL
  • ✓ Authorization tests verify non-admin access is forbidden

Performance Considerations

Status: PASS

  • ✓ Efficient eager loading with loadCount() for consultation/timeline counts
  • ✓ Proper pagination implementation (10/25/50 per page)
  • ✓ Search uses database-level LIKE queries (indexed columns recommended for production)
  • ✓ No N+1 query issues detected
  • ✓ Debounced real-time search (300ms) prevents excessive requests

Files Modified During Review

None - no refactoring was needed.

Gate Status

Gate: PASS → docs/qa/gates/2.2-company-client-account-management.yml

Ready for Done

The implementation fully meets all acceptance criteria with comprehensive test coverage, proper security measures, and follows established project patterns. No blocking issues found.