# 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 - [x] 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) - [x] Validation for all required fields - [x] Duplicate email/registration number prevention - [x] 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 - [x] Display all company clients (user_type = 'company') - [x] Columns: Company Name, Contact Person, Email, Reg #, Status, Created Date - [x] Pagination (10/25/50 per page) - [x] Default sort by created date ### Search & Filter - [x] Search by company name, email, or registration number - [x] Filter by status (active/deactivated/all) - [x] Real-time search with debounce ### Edit Company - [x] Edit all company information - [x] Update contact person details - [x] Validation same as create - [x] Success message on update ### View Company Profile - [x] Display all company information (including contact person details) - [x] Show consultation history summary - [x] Show timeline history summary ### Quality Requirements - [x] Bilingual form labels and messages - [x] Proper form validation - [x] Audit log entries for all operations - [x] Tests for CRUD operations ## Technical Notes ### User Model Scope ```php 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. ```php // 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 ```php 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 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 - [x] Successfully create company with all valid required fields - [x] Validation fails when required fields are missing - [x] Validation fails for duplicate email address - [x] Validation fails for duplicate company registration number - [x] Preferred language defaults to Arabic when not specified - [x] Audit log entry created on successful creation #### List View - [x] List displays only company type users (excludes individual/admin) - [x] Pagination works correctly (10/25/50 per page) - [x] Default sort is by created date descending #### Search & Filter - [x] Search by company name returns correct results - [x] Search by email returns correct results - [x] Search by registration number returns correct results - [x] Filter by active status works - [x] Filter by deactivated status works - [x] Combined search and filter works correctly #### Edit Company - [x] Successfully update company information - [x] Validation fails for duplicate email (excluding current record) - [x] Validation fails for duplicate registration number (excluding current record) - [x] Audit log entry created on successful update #### View Profile - [x] Profile displays all company information correctly - [x] Consultation history summary displays (empty state if none) - [x] Timeline history summary displays (empty state if none) #### Bilingual Support - [x] Form labels display correctly in Arabic - [x] Form labels display correctly in English - [x] Validation messages display in user's preferred language ## Definition of Done - [x] Create company client form works - [x] List view displays all company clients - [x] Search and filter functional - [x] Edit company works with validation - [x] View profile shows complete information - [x] Duplicate prevention works - [x] Audit logging implemented - [x] Bilingual support complete - [x] Tests pass for all CRUD operations - [x] 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 - [x] All required fields validated - [x] Unique constraints enforced for email and company_cert_number - [x] Edit form ignores own record for uniqueness validation - [x] Authorization tests for admin-only access - [x] Unauthenticated user redirect tests - [x] Audit logging for create/update operations - [x] Partial match search implementation - [x] 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 ### Recommended Status ✓ **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.