258 lines
8.8 KiB
Markdown
258 lines
8.8 KiB
Markdown
# 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
|
|
```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
|
|
<?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
|