myCRM/CLAUDE.md
olli 00b42e4a4c feat: Add module removal command (app:module:remove)
Implementiert einen sicheren Weg zum Entfernen von Modulen aus dem System:

Features:
- Zeigt Modul-Informationen (Plugin + Datenbank) an
- Widerruft Lizenz automatisch (optional mit --keep-license)
- Löscht Datenbank-Eintrag im Permission-System (optional mit --keep-db-entry)
- Warnt vor verknüpften Permissions
- Zeigt nächste manuelle Schritte (Composer, Bundle, Migrations, Cache)
- Bestätigung erforderlich (überspringbar mit --force)

Dokumentation:
- Command-Hilfe in src/Command/ModuleRemoveCommand.php
- Benutzer-Anleitung in docs/PLUGIN_QUICKSTART.md
- Entwickler-Referenz in CLAUDE.md

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 18:04:10 +01:00

416 lines
12 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
myCRM is a modern, modular CRM application built with Symfony 7.1 LTS, Vue.js 3, and PrimeVue. It features a sophisticated permission system, API Platform for REST APIs, and a single-page application architecture.
## Common Development Commands
### Initial Setup
```bash
composer install
php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate
php bin/console doctrine:fixtures:load # Load test data (admin@mycrm.local/admin123)
npm install
npm run dev
```
### Development Workflow
```bash
# Terminal 1: Backend server
symfony serve -d # OR: php -S localhost:8000 -t public/
# Terminal 2: Frontend with hot reload
npm run watch
# Production build
npm run build
```
### Database Operations
```bash
# Create new migration after entity changes
php bin/console make:migration
php bin/console doctrine:migrations:migrate
# Validate schema matches entities
php bin/console doctrine:schema:validate
# Reset database (dev only)
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate
php bin/console doctrine:fixtures:load
```
### Testing
```bash
# Backend tests
php bin/phpunit
# Validate Doctrine schema
php bin/console doctrine:schema:validate
# Check user permissions (custom command)
php bin/console app:user:permissions <email>
```
### Code Generation
```bash
# Entity with API Platform support
php bin/console make:entity
# Migration after entity changes
php bin/console make:migration
# Security voter
php bin/console make:voter
# API Platform state processor/provider
php bin/console make:state-processor
php bin/console make:state-provider
```
### Cache Management
```bash
php bin/console cache:clear
APP_ENV=prod php bin/console cache:warmup
```
### Module Management (Plugin System)
```bash
# List all installed modules with license status
php bin/console app:module:list
# Register/manage module license
php bin/console app:module:license billing # Interactive
php bin/console app:module:license billing <license-key> # Direct
php bin/console app:module:license billing --validate # Validate
php bin/console app:module:license billing --revoke # Revoke
# Remove a module safely
php bin/console app:module:remove billing # Interactive
php bin/console app:module:remove billing --force # No confirmation
php bin/console app:module:remove billing --keep-license # Keep license
php bin/console app:module:remove billing --keep-db-entry # Keep DB entry
```
## Architecture Overview
### Security System (6 Layers)
The application uses a sophisticated multi-layer security architecture:
1. **Authentication Layer**: Symfony form login + OAuth (PocketId integration)
2. **Standard Roles**: `ROLE_ADMIN`, `ROLE_USER` (stored in User.roles as JSON array)
3. **Module Permission System**: Custom fine-grained permissions
- User → UserRoles (many-to-many) → Role → RolePermissions → Module
- 6 permission types: view, create, edit, delete, export, manage
- Checked via: `$user->hasModulePermission('contacts', 'view')`
4. **Custom Voters**: Entity-level access control (4 voters)
- `ModuleVoter`: Routes permission checks to module system
- `ProjectVoter`: Project ownership + team member checks
- `ProjectTaskVoter`: Inherits project access + admin bypass
- `GitRepositoryVoter`: Requires associated project access
5. **Event Listeners**: Pre-persist security validation
- `ProjectTaskSecurityListener`: Validates project tasks can only be created if user has project access
6. **Query Extensions**: Auto-filter collections
- `ProjectAccessExtension`: Automatically filters project collections based on user access
### Permission Check Flow
```php
// 1. Check via Security component
$this->denyAccessUnlessGranted('view', $contact); // Uses ContactVoter → ModuleVoter
// 2. Direct module check
if ($user->hasModulePermission('contacts', 'edit')) { }
// 3. API Platform security
#[ApiResource(
operations: [
new Get(security: "is_granted('VIEW', object)")
]
)]
```
### Entity Relationships
**Core Entities:**
- **User**: Authentication + role management (dual system: Symfony roles + custom UserRoles)
- **Role**: Groups of module permissions
- **Module**: CRM modules (contacts, projects, tasks, etc.)
- **RolePermission**: Joins Role ↔ Module with 6 boolean flags
- **Contact**: Business contacts (implements ModuleAwareInterface, module='contacts')
- **ContactPerson**: Linked to Contact, inherits permissions
- **Project**: Project management (owner + team members, module='projects')
- **ProjectTask**: Subtasks under projects (standalone tasks admin-only, module='tasks')
- **GitRepository**: Linked to projects, inherits project access
**Key Patterns:**
- **ModuleAwareInterface**: Entities that tie to permission modules
- **Project Access Model**: `$project->hasAccess($user)` returns true if owner OR team member
- **Permission Inheritance**: ContactPerson inherits Contact permissions, GitRepository inherits Project permissions
- **Dual Role System**: User.roles (Symfony standard array) + User.userRoles (ManyToMany with Role entity)
### API Platform Configuration
**Per-Entity Security:**
```php
#[ApiResource(
operations: [
new GetCollection(security: "is_granted('VIEW', 'contacts')"),
new Get(security: "is_granted('VIEW', object)"),
new Post(security: "is_granted('CREATE', 'contacts')"),
new Put(security: "is_granted('EDIT', object)"),
new Delete(security: "is_granted('DELETE', object)")
]
)]
```
**Serialization Groups:**
- `{entity}:read`: Fields exposed in GET responses
- `{entity}:write`: Fields accepted in POST/PUT
- Used to control field visibility based on context
**Common Filters:**
- `SearchFilter`: Text search on specific fields
- `BooleanFilter`: Filter by boolean fields (active, isDebtor, etc.)
- `DateFilter`: Filter by date ranges
- Custom filters for complex queries (e.g., ProjectTaskProjectFilter)
### Frontend Architecture (Vue.js 3)
**Directory Structure:**
```
/assets/js
/components - Reusable components (CrudDataTable, AppMenu, AppTopbar)
/views - Page-level components (ContactManagement, ProjectManagement, Dashboard)
/layout - Sakai layout components (AppLayout, AppSidebar)
/composables - Composition API functions (useAuth, usePermissions)
/stores - Pinia state management
/api - API client wrappers
/router.js - Vue Router SPA navigation
/assets/styles
/layout - Sakai SCSS (topbar, sidebar, menu, responsive)
tailwind.css - Tailwind CSS v4 with PrimeUI plugin
sakai.scss - Sakai layout imports
```
**Component Pattern:**
```vue
<script setup>
import { ref } from 'vue'
import CrudDataTable from '@/components/CrudDataTable.vue'
const items = ref([])
const apiEndpoint = '/api/contacts'
async function loadData() {
const response = await fetch(apiEndpoint)
items.value = await response.json()
}
</script>
```
**PrimeVue Usage:**
- `DataTable`: Main component for entity lists (server-side pagination, filtering, sorting)
- `Dialog`: Modal forms for create/edit
- `Button`, `InputText`, `Dropdown`: Form components
- `Chart`: Dashboard visualizations (Chart.js integration)
- Theme: Aura theme with dark mode support
**Webpack Encore:**
- Entry point: `assets/app.js`
- Aliases: `@``assets/js`, `@images``assets/images`
- Hot reload: `npm run watch`
- Production build: `npm run build`
## Development Conventions
### Backend (Symfony)
**Controllers**: Keep thin, delegate to services
```php
// Good
public function create(Request $request, ContactService $service): JsonResponse
{
$this->denyAccessUnlessGranted('CREATE', 'contacts');
return $this->json($service->createContact($request->toArray()));
}
```
**Services**: Constructor injection, use interfaces
```php
class ContactService
{
public function __construct(
private EntityManagerInterface $em,
private EventDispatcherInterface $dispatcher
) {}
}
```
**Entities**: Doctrine attributes, define relationships
```php
#[ORM\Entity]
#[ORM\Table(name: 'contacts')]
#[ApiResource(/* ... */)]
class Contact implements ModuleAwareInterface
{
public function getModuleName(): string { return 'contacts'; }
}
```
**Voters**: Implement granular permissions
```php
class ContactVoter extends Voter
{
protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, ['VIEW', 'EDIT', 'DELETE'])
&& ($subject instanceof Contact || $subject === 'contacts');
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
// Delegate to ModuleVoter via hasModulePermission()
}
}
```
### Frontend (Vue.js)
**Composables**: Extract reusable logic
```javascript
// composables/usePermissions.js
export function usePermissions() {
const hasPermission = (module, action) => {
// Check user permissions
}
return { hasPermission }
}
```
**Components**: Single File Components with Composition API
```vue
<script setup>
import { ref, onMounted } from 'vue'
const props = defineProps({
apiEndpoint: String
})
const emit = defineEmits(['save', 'cancel'])
// Component logic here
</script>
```
## Testing Strategy
- **PHPUnit**: Backend unit and functional tests
- **Doctrine Schema Validation**: Run in CI to catch schema drift
- **Voter Tests**: Explicitly test permission logic
- **Frontend**: Vitest/Jest for Vue components (when configured)
## Important Files
**Security:**
- `src/Security/Voter/` - All voter implementations
- `src/EventListener/ProjectTaskSecurityListener.php` - Pre-persist validation
- `src/Filter/ProjectAccessExtension.php` - Query auto-filtering
- `src/Entity/User.php` - hasModulePermission() method (lines ~200-220)
**API Configuration:**
- Entity `#[ApiResource]` attributes - Security, operations, filters
- `config/packages/api_platform.yaml` - Global API settings
**Frontend Entry:**
- `assets/app.js` - Vue app initialization
- `assets/js/router.js` - Route definitions and navigation guards
- `webpack.config.js` - Encore configuration
## Key Workflows
### Adding a New Module
1. Create entity with `ModuleAwareInterface`
2. Add to Module entity fixtures/database
3. Create voter (or rely on ModuleVoter)
4. Configure API Platform security
5. Add Vue.js view component
6. Add route to router.js
7. Add menu item to AppMenu.vue
### Adding a New Permission-Controlled Feature
1. Determine permission level (module vs. entity)
2. If entity-level: create/update Voter
3. Add security checks in controller/API
4. Update frontend to check permissions before showing UI
5. Test with different user roles
### Database Schema Changes
1. Modify entity attributes
2. `php bin/console make:migration`
3. Review generated migration
4. `php bin/console doctrine:migrations:migrate`
5. `php bin/console doctrine:schema:validate` to verify
6. Update fixtures if needed
## Common Patterns
**Check Module Permission (Backend):**
```php
if (!$user->hasModulePermission('contacts', 'view')) {
throw new AccessDeniedException();
}
```
**Check Entity Permission (Backend):**
```php
$this->denyAccessUnlessGranted('EDIT', $contact);
```
**API Platform Security:**
```php
#[ApiResource(
security: "is_granted('ROLE_USER')",
operations: [
new Get(security: "is_granted('VIEW', object)")
]
)]
```
**Project Access Check:**
```php
if (!$project->hasAccess($user)) {
throw new AccessDeniedException();
}
```
**Frontend Permission Check:**
```javascript
import { usePermissions } from '@/composables/usePermissions'
const { hasPermission } = usePermissions()
if (hasPermission('contacts', 'create')) {
// Show create button
}
```
## Additional Documentation
- `docs/LOGIN.md` - Authentication system details
- `docs/PERMISSIONS.md` - Modular permission system
- `docs/USER-CRUD.md` - User management with API Platform
- `docs/PROJECT_TASKS_MODULE.md` - Project tasks implementation
- `.github/copilot-instructions.md` - Detailed development guidelines