From: fomichev Date: Mon, 17 Nov 2025 09:26:33 +0000 (+0300) Subject: docs phase 1 X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=2d81cf8a5e82092556f057ff284e98f1fd918581;p=erp24_rep%2Fyii-erp24%2F.git docs phase 1 --- diff --git a/docs/api/api2/README.md b/docs/api/api2/README.md deleted file mode 100644 index 721b6c92..00000000 --- a/docs/api/api2/README.md +++ /dev/null @@ -1,1464 +0,0 @@ -# API2 - Modern REST API Documentation - -> **Primary REST API for ERP24 system - Port 5555** - -## Table of Contents - -- [Overview](#overview) -- [Getting Started](#getting-started) -- [Authentication](#authentication) -- [Core Endpoints](#core-endpoints) -- [Business Endpoints](#business-endpoints) -- [Integration Endpoints](#integration-endpoints) -- [Error Handling](#error-handling) -- [Rate Limiting](#rate-limiting) - ---- - -## Overview - -API2 is the **primary REST API** for the ERP24 system, serving mobile applications, web clients, and external integrations. - -### Key Features - -- ✅ RESTful design principles -- ✅ JSON request/response format -- ✅ Token-based authentication -- ✅ Comprehensive error handling -- ✅ Swagger documentation support -- ✅ Mobile-optimized responses -- ✅ Real-time data access - -### Base URL - -``` -Production: https://api2.bazacvetov24.ru -Development: http://localhost:5555 -``` - -### API Statistics - -| Metric | Count | -|--------|-------| -| **Controllers** | 21 | -| **Total Endpoints** | 60+ | -| **Authentication Methods** | Token-based | -| **Response Format** | JSON | -| **Status** | Active Development | - ---- - -## Getting Started - -### Quick Start Example - -```bash -# 1. Authenticate -curl -X POST https://api2.bazacvetov24.ru/auth/login \ - -H "Content-Type: application/json" \ - -d '{"login": "api_user", "password": "secret"}' - -# Response: -# {"access-token": "abc123xyz..."} - -# 2. Use the token for subsequent requests -curl -X GET https://api2.bazacvetov24.ru/client/get-info \ - -H "Authorization: Bearer abc123xyz..." \ - -H "Content-Type: application/json" \ - -d '{"phone": "79001234567"}' -``` - -### Request Headers - -All API requests should include: - -```http -Content-Type: application/json -Authorization: Bearer -Accept: application/json -``` - -### Response Format - -**Success Response**: -```json -{ - "success": true, - "data": { ...actual data... }, - "timestamp": "2025-01-14T12:43:32Z" -} -``` - -**Error Response**: -```json -{ - "success": false, - "error": { - "code": "ERROR_CODE", - "message": "Human-readable error message", - "details": {...optional details...} - }, - "timestamp": "2025-01-14T12:43:32Z" -} -``` - ---- - -## Authentication - -### POST /auth/login - -Authenticate user and receive access token. - -**File**: `erp24/api2/controllers/AuthController.php:9` - -**Request**: -```json -{ - "login": "api_user", - "password": "your_password" -} -``` - -**Success Response**: -```json -{ - "access-token": "eyJ0eXAiOiJKV1QiLCJhbGc..." -} -``` - -**Error Response**: -```json -{ - "errors": "Wrong login or password" -} -``` - -**Example**: -```bash -curl -X POST http://localhost:5555/auth/login \ - -H "Content-Type: application/json" \ - -d '{ - "login": "admin", - "password": "secret123" - }' -``` - ---- - -## Core Endpoints - -### Client Controller - -**File**: `erp24/api2/controllers/ClientController.php` - -Client management endpoints for customer operations. - -#### POST /client/add - -Register new client or update existing client. - -**Request Parameters**: -```json -{ - "phone": "79001234567", // Required - "client_id": 12345, // Optional (for Salebot) - "client_type": "1", // Optional - "platform_id": 123, // Optional - "name": "Иван Иванов", // Required - "avatar": "https://...", // Optional - "messenger": "telegram", // Optional - "message_id": "msg_123" // Optional -} -``` - -**Response**: -```json -{ - "result": true, - "result_edit": " {...} 79001234567 ", - "editDates": true -} -``` - -**Error Response**: -```json -{ - "error_id": 1, - "error": "phone is required" -} -``` - ---- - -#### POST /client/balance - -Get client bonus balance and keycode. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "balance": 1250.50, - "keycode": "1234", - "editDates": true -} -``` - ---- - -#### POST /client/get-info - -Get comprehensive client information. - -**Request**: -```json -{ - "phone": "79001234567" - // OR - "ref_code": "ABC123XYZ" -} -``` - -**Response**: -```json -{ - "response": { - "id": 12345, - "card": "1234567890", - "first_name": "Иван", - "second_name": "Иванов", - "sex": "male", - "birth_day": "1990-05-15", - "comment": "VIP клиент", - "balance": 1250.50, - "burn_balans": 200.00, - "bonus_level": "gold", - "total_price": 45000.00, - "total_price_rejected": 500.00, - "referral_count_all": 5, - "referral_count_get_bonus_already": 3, - "ref_code": "ABC123", - "referral_id": 98765, - "events": [ - { - "date": "2025-01-15", - "event_id": 1, - "event_tip": "День рождения", - "number": 1 - } - ], - "platform": { - "telegram": { - "is_subscribed": 1, - "created_at": "2024-06-15 10:30:00" - } - } - } -} -``` - ---- - -#### POST /client/check-details - -Get client purchase history with details. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "response": { - "checks": [ - { - "id": 123456, - "store": { - "id": "guid-store-1", - "name": "Магазин на Ленина" - }, - "number": "00123", - "payment": [ - {"type": "cash"}, - {"type": "card"} - ], - "date": "2025-01-14T15:30:00+00:00", - "sum": 2500.00, - "discount": 250.00, - "order_id": "ORD-123", - "seller_id": "SELLER-456", - "products": [ - { - "product_id": "PROD-789", - "quantity": 5.0, - "price": 500.00, - "discount": 50.00 - } - ], - "bonuses": [ - { - "name": "Начисление бонусов за покупку", - "amount": 250 - }, - { - "name": "Списание бонусов", - "amount": -100 - } - ] - } - ], - "pages": { - "totalCount": 45, - "page": 0, - "per-page": 20 - } - } -} -``` - ---- - -#### POST /client/check-detail - -Get details of a specific check/sale. - -**Request**: -```json -{ - "check_id": 123456 -} -``` - -**Response**: Same structure as single check in `/client/check-details` - ---- - -#### POST /client/bonus-write-off - -Get bonus write-off history for client. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "response": { - "bonuses": [ - { - "name": "Списание бонусов за покупку", - "amount": -100, - "check_id": "123456", - "date": "2025-01-14T15:30:00+00:00" - }, - { - "name": "Начисление за регистрацию", - "amount": 500, - "check_id": null, - "date": "2024-12-01T10:00:00+00:00" - } - ], - "pages": { - "totalCount": 25, - "page": 0, - "per-page": 20 - } - } -} -``` - ---- - -#### POST /client/use-bonuses - -Write off bonuses for an order. - -**Request**: -```json -{ - "order_id": "ORDER-123", - "phone": "79001234567", - "points_to_use": 150, - "date": 1705243200, - "price": 2500.00 -} -``` - -**Success Response**: -```json -{ - "response": { - "code": 200, - "status": "success", - "data": { - "client_id": 12345, - "order_id": "ORDER-123", - "addedPoints": 150, - "remainingPoints": 1100 - } - } -} -``` - -**Error Response**: -```json -{ - "error": { - "code": 403, - "message": "Недостаточно бонусов для списания" - } -} -``` - ---- - -#### POST /client/add-bonus - -Add bonus points for an order. - -**Request**: -```json -{ - "order_id": "ORDER-123", - "phone": "79001234567", - "points_to_add": 250, - "date": 1705243200, - "price": 2500.00 -} -``` - -**Response**: -```json -{ - "response": { - "code": 200, - "status": "success", - "data": { - "phone": "79001234567", - "order_id": "ORDER-123", - "addedPoints": 250, - "totalPoints": 1500 - } - } -} -``` - ---- - -#### POST /client/bonus-status - -Get client's bonus level status. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "response": { - "phone": "79001234567", - "alias": "gold", - "bonus_level": "Золотой", - "current_points": 5000, - "next_points": 10000, - "discount_percent": 15, - "cashback_rate": 10 - } -} -``` - ---- - -#### POST /client/event-edit - -Add or update memorable dates for client. - -**Request**: -```json -{ - "phone": "79001234567", - "channel": "salebot", - "events": [ - { - "number": 1, - "date": "15.05.1990", - "tip": "День рождения" - }, - { - "number": 2, - "date": "14.02.2015", - "tip": "День свадьбы" - } - ] -} -``` - -**Response**: -```json -{ - "response": true -} -``` - ---- - -#### POST /client/memorable-dates - -Get client's memorable dates. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "response": [ - { - "date": "15.05.1990", - "number": 1, - "tip": "День рождения" - }, - { - "date": "14.02.2015", - "number": 2, - "tip": "День свадьбы" - } - ] -} -``` - ---- - -#### POST /client/show-keycode - -Send QR code with keycode to client via Telegram. - -**Request**: -```json -{ - "phone": "79001234567", - "platform_id": 123456 -} -``` - -**Response**: Telegram API response from sending QR code message - ---- - -#### POST /client/store-geo - -Send store locations based on client geolocation. - -**Request**: -```json -{ - "location": "55.7558,37.6173", - "platform_id": 123456 -} -``` - -**Response**: Telegram API response with stores near location - ---- - -#### POST /client/apply-promo-code - -Apply promotional code to client's account. - -**Request**: -```json -{ - "phone": "79001234567", - "code": "SPRING2025" -} -``` - -**Success Response**: -```json -{ - "response": ["ok"] -} -``` - -**Error Response**: -```json -{ - "error_id": 2, - "error": "Промокод не известен" -} -``` - -```json -{ - "error_id": 3, - "error": "Промокод уже использован" -} -``` - ---- - -#### POST /client/get-user-info - -Get detailed user statistics and platform subscription info. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "response": { - "name": "Иван Иванов", - "sex": "man", - "sale_avg_price": 2500.00, - "total_price": 45000.00, - "registration_date": "2024-01-15", - "bonus_balance": 1250, - "bonus_minus": 300, - "date_first_sale": "2024-01-20 14:30:00", - "date_last_sale": "2025-01-10 16:45:00", - "sale_cnt": 18, - "total_price_rejected": 500.00, - "events": [...], - "platform": { - "telegram": { - "is_subscribed": 1, - "created_at": "2024-06-15 10:30:00" - } - } - } -} -``` - ---- - -#### POST /client/change-user-subscription - -Update client's Telegram subscription status. - -**Request**: -```json -{ - "phone": "79001234567", - "telegram_is_subscribed": 1 // 1 = subscribed, 0 = unsubscribed -} -``` - -**Response**: -```json -{ - "response": true -} -``` - ---- - -#### GET /client/get-stores - -Get list of all active stores. - -**Response**: -```json -{ - "response": [ - { - "id": 1, - "name": "Магазин на Ленина" - }, - { - "id": 2, - "name": "Магазин в ТЦ Галерея" - } - ] -} -``` - ---- - -#### POST /client/phone-keycode-by-card - -Get phone and keycode by card number. - -**Request**: -```json -{ - "card": "1234567890" -} -``` - -**Response**: -```json -{ - "response": { - "phone": "79001234567", - "keycode": "1234" - } -} -``` - ---- - -#### POST /client/social-ids - -Get client's social platform IDs. - -**Request**: -```json -{ - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "response": [ - { - "platform": "telegram", - "user_id": 123456789 - } - ] -} -``` - ---- - -### Employee Controller - -**File**: `erp24/api2/controllers/EmployeeController.php` - -Employee and shift management endpoints. - -#### POST /employee/is-admin-on-shift - -Check if employee is currently on shift. - -**Request**: -```json -{ - "store_guid": "guid-store-1", - "seller_id": "guid-seller-1" -} -``` - -**Response**: -```json -{ - "response": { - "is_admin_on_shift": true - } -} -``` - -**Logic**: Checks if employee has active shift within the last 12 hours. - ---- - -#### POST /employee/is-admin-end-shift - -Check if employee's shift has ended. - -**Request**: -```json -{ - "store_guid": "guid-store-1", - "seller_id": "guid-seller-1" -} -``` - -**Response**: -```json -{ - "response": { - "is_admin_on_shift": false - } -} -``` - -**Logic**: Checks if current time is between shift start and end time. - ---- - -#### GET /employee/get-all-admins - -Get all employees with valid GUIDs and phones. - -**Response**: -```json -[ - { - "id": 123, - "guid": "admin-guid-1", - "name": "Петрова Мария Ивановна", - "phone": "79001234567" - }, - { - "id": 124, - "guid": "admin-guid-2", - "name": "Сидоров Иван Петрович", - "phone": "79007654321" - } -] -``` - ---- - -#### POST /employee/at-store - -Get employees currently checked in at a specific store. - -**Request**: -```json -{ - "guid": "guid-store-1" -} -``` - -**Response**: -```json -["admin-guid-1", "admin-guid-2"] -``` - -**Logic**: Returns GUIDs of employees who checked in once today (not checked out). - ---- - -## Business Endpoints - -### Bonus Controller - -**File**: `erp24/api2/controllers/BonusController.php` - -Bonus and loyalty program endpoints. - -#### POST /bonus/get-bonuses - -Calculate available bonuses for a purchase. - -**Request**: -```json -{ - "store_id": "guid-store-1", - "seller_id": "guid-seller-1", - "phone": "79001234567", - "check_amount": 2500, - "items": [ - { - "product_id": "guid-product-1", - "price": 500, - "quantity": 5 - } - ] -} -``` - -**Success Response**: -```json -{ - "result": true, - "auth_code": "1234", - "name": "Иван Иванов", - "total_bonuses": 1250, - "bonus_level": "gold", - "burn_balans": 200, - "available_bonuses": 250, - "will_be_credited_bonuses": 250, - "message_cashier": "Клиент Иван Иванов найден 1250" -} -``` - -**Key Logic**: -- Excludes products in `unused_nomenclature` from bonus accrual -- Excludes products in `non_bonusable_goods` from bonus write-off -- Maximum write-off is determined by bonus level percentage -- Special handling for new Telegram subscribers (30% bonus on first purchase) - ---- - -#### POST /bonus/send-message - -Initiate phone call verification for bonus program. - -**Request**: -```json -{ - "store_id": "guid-store-1", - "seller_id": "guid-seller-1", - "phone": "79001234567" -} -``` - -**Response**: -```json -{ - "auth_code": "5472", - "message_cashier": "Попытка:1 Звонок клиенту! последние 4 цифры номера", - "timeout": 15 -} -``` - -**Logic**: -- Uses SMS.ru call verification service -- Limits to 2 call attempts within 10 minutes -- Generates 4-digit keycode as last 4 digits of incoming call - ---- - -#### POST /bonus/save-client-info - -Save or update client information for bonus program. - -**Request**: -```json -{ - "store_id": "guid-store-1", - "seller_id": "guid-seller-1", - "phone": "79001234567", - "first_name": "Иван", - "second_name": "Иванов", - "sex": "male", // "male" | "female" - "birth_day": "1990-05-15", - "referral_id": 98765, - "comment": "VIP клиент", - "events": [ - { - "number": 1, - "date": "15.05.1990", - "tip": "День рождения" - } - ], - "source": 1 // Optional: traffic source -} -``` - -**Response**: Similar to `/bonus/get-bonuses` - ---- - -### Orders Controller - -**File**: `erp24/api2/controllers/OrdersController.php` - -Marketplace order management endpoints. - -#### POST /orders/change-status - -Update marketplace order status (called from 1C). - -**Request**: -```json -{ - "order": [ - { - "order_id": "order-guid-1", - "status": 5, - "seller_id": "seller-guid-1" - }, - { - "order_id": "order-guid-2", - "status": 10 - } - ] -} -``` - -**Response**: -```json -[ - { - "order_id": "order-guid-1", - "result": true, - "message": "Статус обновлён", - "status": 12 - }, - { - "order_id": "order-guid-2", - "result": "error", - "message": "Заказ не найден" - } -] -``` - -**Key Logic**: -- Maps 1C statuses to marketplace statuses (Flowwow, Yandex Market) -- Automatically updates Yandex Market via API for certain statuses -- Tracks order cancellation source and date -- Creates status change history - ---- - -#### POST /orders/get-orders - -Get orders for a specific store (last 24 hours). - -**Request**: -```json -{ - "store_id": "guid-store-1" -} -``` - -**Response**: -```json -{ - "success": true, - "result": [ - { - "order_id": "order-guid-1", - "status": 5, - "items": [ - { - "product_id": "prod-guid-1", - "quantity": 2.0, - "price": 1500.00 - } - ] - } - ] -} -``` - ---- - -## Integration Endpoints - -### Telegram Controller - -**File**: `erp24/api2/controllers/TelegramController.php` - -Telegram bot webhook and messaging endpoints. - -#### GET /telegram/set-webhook - -Configure Telegram webhook URL. - -**Parameters**: `on=1` (enable) or `on=0` (disable) - -**Response**: `"OK"` or `"NOT OK"` - ---- - -#### POST /telegram/webhook - -Telegram bot webhook handler. - -**Handles**: -- Incoming messages from Telegram users -- Callback queries (button presses) -- Bot commands -- User registration and authentication - -**Auto-processed** - not called directly by external systems. - ---- - -#### GET /telegram/send-message - -Send message to employee via Telegram. - -**Parameters**: -- `admin_id` - Employee ID -- `message` - Message text (HTML supported) -- `reply_markup` - Optional JSON keyboard markup - -**Response**: JSON array of send results for all employee chats - -**Example**: -```bash -curl "http://localhost:5555/telegram/send-message? \ - admin_id=123&message=Новая задача!" -``` - ---- - -### Marketplace Controller - -**File**: `erp24/api2/controllers/MarketplaceController.php` - -Marketplace integration management. - -#### GET /marketplace/statuses - -Get all marketplace statuses. - -**Response**: -```json -{ - "response": [ - { - "id": 1, - "code": "NEW", - "name": "Новый заказ", - "marketplace_id": 1 - }, - { - "id": 2, - "code": "PROCESSING", - "name": "В обработке", - "marketplace_id": 1 - } - ] -} -``` - ---- - -#### POST /marketplace/get-new-order-count - -Get count of new unprocessed orders for a store. - -**Request**: -```json -{ - "store_guid": "guid-store-1" -} -``` - -**Response**: -```json -{ - "response": 5 -} -``` - -**Logic**: Counts orders with `status_1c=1`, `status_id=1`, created within last 3 days. - ---- - -#### POST /marketplace/instruction-dictionary - -Get status transition instructions for marketplace orders. - -**Request**: -```json -{ - "guid": "order-guid-1" -} -``` - -**Response**: -```json -{ - "response": [ - { - "marketplace": "ФлауВау", - "status": "Новый", - "status_id": 1, - "status_instruction": "Принять заказ в работу", - "status_relations": [ - { - "status": "В работе", - "status_id": 2, - "description": "Заказ принят флористом", - "button_text": "Принять", - "order": 1 - }, - { - "status": "Отменен", - "status_id": 99, - "description": "Отменить заказ", - "button_text": "Отменить", - "order": 2 - } - ] - } - ] -} -``` - ---- - -## Error Handling - -### Standard Error Codes - -| Error ID | Description | HTTP Status | -|----------|-------------|-------------| -| `0` | Missing required parameter | 400 | -| `0.1` | Invalid parameter format | 400 | -| `0.2` | Phone validation failed | 400 | -| `1` | Database validation error | 400 | -| `1.2` | Phone format invalid | 400 | -| `2` | Entity not found | 404 | -| `3` | Permission denied / Blacklist | 403 | -| `400` | Bad request | 400 | -| `403` | Forbidden | 403 | -| `404` | Not found | 404 | -| `500` | Internal server error | 500 | - -### Error Response Examples - -**Missing Parameter**: -```json -{ - "error_id": 0, - "error": "phone is required" -} -``` - -**Invalid Phone**: -```json -{ - "error_id": 1.2, - "error": "phone is required" -} -``` - -**Blacklisted Client**: -```json -{ - "error": "Этот номер в черном списке" -} -``` - -**Not Found**: -```json -{ - "error": { - "code": 404, - "message": "Пользователь не найден" - } -} -``` - -**Validation Error**: -```json -{ - "error": { - "code": 400, - "message": "Ошибка валидации", - "details": { - "phone": ["Phone cannot be blank"], - "amount": ["Amount must be greater than 0"] - } - } -} -``` - ---- - -## Rate Limiting - -Currently **no rate limiting** is enforced on API2. - -**Recommendations**: -- Implement rate limiting in production -- Suggested limits: - - Authentication endpoints: 5 req/min per IP - - Read endpoints: 60 req/min per token - - Write endpoints: 30 req/min per token - ---- - -## Phone Number Format - -All endpoints accepting phone numbers expect **Russian phone format**: - -- **Format**: `79001234567` (11 digits, starts with 7) -- **Invalid**: `+7 900 123 45 67`, `8 900 123 45 67`, `9001234567` - -**Phone Validation** (`ClientHelper::phoneClear` + `ClientHelper::phoneVerify`): -```php -// Accepted formats (all converted to 79001234567): -"+7 900 123 45 67" -"8 900 123 45 67" -"7 900 123 45 67" -"900 123 45 67" -"9001234567" - -// All normalize to: "79001234567" -``` - ---- - -## Data Controller - -Additional data endpoints: - -### GET /data/get-stores -Get all active city stores - -### GET /data/get-cities -Get all cities with stores - -### GET /data/get-bonus-levels -Get bonus level configurations - ---- - -## Testing - -### Postman Collection - -Available at: `/erp24/api2/swagger/postman_collection.json` - -### Test Endpoints - -```bash -# Health check -curl http://localhost:5555/site/index - -# Get stores -curl http://localhost:5555/client/get-stores - -# Test authentication -curl -X POST http://localhost:5555/auth/login \ - -H "Content-Type: application/json" \ - -d '{"login": "test", "password": "test123"}' -``` - ---- - -## Swagger Documentation - -Access interactive API documentation: - -``` -http://localhost:5555/swagger/ -``` - -Swagger YAML location: `/erp24/api2/swagger/swagger.yaml` - ---- - -## Migration from API1 - -If you're currently using API1, see [API1 to API2 Migration Guide](../api1/migration-to-api2.md). - -**Key Differences**: -- JSON-only (no plain text responses) -- Consistent error format -- Token authentication instead of session -- Proper HTTP status codes - ---- - -## Additional Controllers - -### Task Controller -Task management endpoints (implementation details TBD) - -### Universal Catalog Controller -Product catalog endpoints (implementation details TBD) - -### Chatbot Action Controller -Chatbot integration endpoints (implementation details TBD) - -### Telegram Salebot Controller -Sales bot automation endpoints (implementation details TBD) - -### Yandex Market Controller -Yandex Market specific integration endpoints (implementation details TBD) - -### Delivery Controller -Delivery tracking and management endpoints (implementation details TBD) - -### KIK Controller -Customer feedback (KIK) endpoints (implementation details TBD) - -### Store Controller -Store management endpoints (implementation details TBD) - -### Balance Controller -Financial balance endpoints (implementation details TBD) - -### Data Buh Controller -Accounting data endpoints (implementation details TBD) - ---- - -## Best Practices - -### 1. Always Validate Phone Numbers -```javascript -function normalizePhone(phone) { - // Remove all non-digits - phone = phone.replace(/\D/g, ''); - - // Handle 8xxx format - if (phone.startsWith('8') && phone.length === 11) { - phone = '7' + phone.substring(1); - } - - // Handle xxx format (no country code) - if (phone.length === 10) { - phone = '7' + phone; - } - - return phone; -} -``` - -### 2. Handle Errors Gracefully -```javascript -try { - const response = await fetch('/api2/client/balance', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ phone: normalizePhone(phone) }) - }); - - const data = await response.json(); - - if (data.error || data.error_id) { - console.error('API Error:', data.error); - // Handle error - } else { - // Success - console.log('Balance:', data.balance); - } -} catch (error) { - console.error('Network Error:', error); -} -``` - -### 3. Use Async Operations -For heavy operations like `/client/retrieve`, consider implementing queue-based processing. - -### 4. Cache Reference Data -Cache responses from endpoints like `/client/get-stores`, `/data/get-bonus-levels`. - ---- - -## Support - -For API2 issues: -- Check logs: `/erp24/api2/runtime/logs/app.log` -- Review JSON debug files: `/erp24/api2/json/` -- Contact: development team - ---- - -**Last Updated**: January 2025 -**API Version**: 2.0 (Active Development) -**Maintained By**: ERP24 Development Team diff --git a/docs/api2/ru/API_REFERENCE.md b/docs/api2/ru/API_REFERENCE.md deleted file mode 100644 index d2f1a7c8..00000000 --- a/docs/api2/ru/API_REFERENCE.md +++ /dev/null @@ -1,173 +0,0 @@ -> 📖 **Язык**: Русский | [English](../API_REFERENCE.md) - -# Справочная документация API2 - -## Обзор - -Модуль API2 представляет собой RESTful API систему, построенную на фреймворке Yii2 для системы ERP. Он предоставляет комплексные эндпоинты для интеграции с маркетплейсами, управления клиентами, аутентификации и обработки заказов. - -**Базовый URL**: `/api2/` - -**Формат ответа**: JSON - -**Аутентификация**: На основе токенов (заголовок X-ACCESS-TOKEN или параметр запроса key) - ---- - -## Аутентификация - -Все эндпоинты (кроме `/auth/login`) требуют аутентификации одним из следующих способов: - -### Аутентификация через заголовок -```http -X-ACCESS-TOKEN: your-access-token -``` - -### Аутентификация через параметр запроса -```http -GET /api2/endpoint?key=your-access-token -``` - -### Эндпоинт входа - -**POST** `/auth/login` - -Аутентификация и получение токена доступа. - -**Тело запроса**: -```json -{ - "login": "username", - "password": "password" -} -``` - -**Успешный ответ** (200): -```json -{ - "access-token": "generated-token-string" -} -``` - -**Ответ с ошибкой** (200): -```json -{ - "errors": "Wrong login of password" -} -``` - ---- - -## Настройка CORS - -Все эндпоинты поддерживают CORS со следующей конфигурацией: -- **Разрешенные источники**: `*` -- **Разрешенные методы**: `GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS` -- **Разрешенные заголовки**: `*` -- **Открытые заголовки**: `x-access-token` -- **Максимальный возраст**: 86400 секунд - ---- - -## Контроллеры API - -### 1. Balance Controller -Управление остатками товаров. - -### 2. Client Controller -Комплексное управление клиентами/покупателями, включая бонусные программы, покупки и функции лояльности. - -### 3. Orders Controller -Обработка и управление заказами с маркетплейсов, изменение статусов и получение заказов. - -### 4. Marketplace Controller -Операции, специфичные для маркетплейсов, включая управление статусами и подсчет заказов. - -### 5. YandexMarket Controller -Интеграция с маркетплейсом Яндекс.Маркет для карточек товаров и синхронизации заказов. - -### 6. Delivery Controller -Сервисы доставки и аутентификации администратора. - ---- - -## Обработка ошибок - -### Стандартный формат ответа с ошибкой - -```json -{ - "error_id": 1, - "error": "Описание ошибки" -} -``` - -### Распространенные коды ошибок - -| ID ошибки | Описание | -|----------|-------------| -| 0.1 | Неверный формат параметра или отсутствует обязательный параметр | -| 1 | Отсутствует обязательный параметр | -| 1.2 | Неверный формат номера телефона | -| 2 | Запись не найдена или ошибка сохранения | -| 3 | Нарушение бизнес-логики | -| 400 | Неверное тело JSON | - -### Коды состояния HTTP - -- **200**: Успех (даже для некоторых ошибок - проверьте тело ответа) -- **400**: Неверный запрос (некорректный JSON) -- **401**: Не авторизован (отсутствует/неверный токен) -- **404**: Не найдено -- **500**: Внутренняя ошибка сервера - ---- - -## Форматы данных - -### Номера телефонов -- Должны быть числовыми -- Автоматически очищаются/нормализуются через `ClientHelper::phoneClear()` -- Проверяются с помощью `ClientHelper::phoneVerify()` - -### Даты -- Ввод: `DD.MM.YYYY` или `Y-m-d H:i:s` -- Вывод: ISO 8601 (`date('c')`) или `Y-m-d H:i:s` -- Временные метки: Unix timestamp (секунды) - -### Деньги/Цены -- Валюта: RUB (Российский рубль) -- Формат: Значения с плавающей точкой/десятичные -- Без символа валюты в ответах - ---- - -## Ограничение скорости - -Явное ограничение скорости не задокументировано. Свяжитесь с администратором API для текущих политик. - ---- - -## Версионирование API - -Текущая версия: **v2** (подразумевается путем `/api2/`) - -Отсутствует явное версионирование в эндпоинтах. Критические изменения потребуют нового базового пути. - ---- - -## Логирование - -API ведет журналы: -- Всех запросов в базу данных (`LogService::apiDataLogs()`) -- Ошибок в журналы ошибок (`LogService::apiErrorLog()`) -- Операций в журналы операций (`LogService::apiLogs()`) -- Запросов/ответов в JSON файлы (в каталоге `/json/`) - ---- - -## Следующие шаги - -- См. [ENDPOINTS.md](./ENDPOINTS.md) для подробной документации по эндпоинтам -- См. [EXAMPLES.md](./EXAMPLES.md) для примеров кода и паттернов использования -- См. [INTEGRATION_GUIDE.md](./INTEGRATION_GUIDE.md) для инструкций по интеграции diff --git a/docs/api2/ru/ARCHITECTURE.md b/docs/api2/ru/ARCHITECTURE.md deleted file mode 100644 index 261c0c5c..00000000 --- a/docs/api2/ru/ARCHITECTURE.md +++ /dev/null @@ -1,392 +0,0 @@ -> 📖 **Язык**: Русский | [English](../ARCHITECTURE.md) - -# Архитектура системы API2 - -## Обзор - -Модуль `erp24/api2` представляет собой REST API подсистему на основе Yii2, которая обеспечивает внешний доступ к системе ERP24. Он служит промежуточным слоем между внешними интеграциями (маркетплейсы, мобильные приложения, чат-боты) и основной базой данных ERP. - -## Технологический стек - -- **Фреймворк**: Yii2 Framework (PHP) -- **Архитектурный паттерн**: RESTful API с MVC -- **Аутентификация**: На основе токенов (заголовок/параметр запроса) -- **Формат ответа**: JSON (по умолчанию) -- **Очередь сообщений**: RabbitMQ (AMQP) -- **База данных**: Общая с основным приложением ERP (MySQL) -- **Документация API**: Поддержка Swagger - -## Компоненты системы - -### 1. Точка входа - -**Файл**: `index.php` - -- Инициализирует приложение Yii2 -- Загружает конфигурацию из `config/api2.config.php` -- Настраивает псевдонимы приложения -- Запускает веб-приложение - -```php -// Последовательность загрузки -1. Загрузка автозагрузчика Composer -2. Загрузка фреймворка Yii2 -3. Загрузка конфигурации окружения -4. Загрузка конфигурации приложения -5. Установка псевдонимов приложения -6. Запуск приложения -``` - -### 2. Слой конфигурации - -**Каталог**: `config/` - -#### Основная конфигурация (`api2.config.php`) - -Настроенные компоненты: -- **Язык**: Русский (`ru`) -- **Bootstrap**: `log`, `queue` -- **CORS**: Отключен (закомментирован) -- **URL Manager**: Включены красивые URL, REST маршрутизация -- **Аутентификация**: RBAC с хранением в базе данных -- **Asset Manager**: На основе кэша с временными метками -- **Formatter**: Московская часовая зона, русские форматы дат -- **Request/Response**: Парсинг и форматирование JSON -- **Queue**: Интеграция RabbitMQ для асинхронных задач -- **User Identity**: Модель `ApiUser` -- **Database**: Общая конфигурация с основной ERP -- **Logging**: Логирование ошибок/предупреждений в файлы -- **Cache**: Кэширование на основе файлов - -#### Конфигурация окружения (`env.php`) - -Переменные и настройки, специфичные для окружения. - -### 3. Слой контроллеров - -**Каталог**: `controllers/` - -Базовый паттерн: Все контроллеры наследуются от `BaseController`, который обеспечивает: -- Конфигурацию CORS (разрешение всех источников) -- Композитную аутентификацию (токен в заголовке + параметр запроса) -- Обработку исключений для OPTIONS запросов -- Автоматическое форматирование JSON ответов - -#### Категории контроллеров - -**Аутентификация и авторизация** -- `AuthController.php` - Вход пользователя, генерация токенов - -**Бизнес-данные** -- `BalanceController.php` - Операции с остатками -- `BonusController.php` - Управление бонусами -- `ClientController.php` - Управление клиентами -- `EmployeeController.php` - Операции с сотрудниками - -**Интеграция с маркетплейсами** -- `MarketplaceController.php` - Общие операции с маркетплейсами -- `YandexMarketController.php` - Специфичная интеграция с Яндекс.Маркет - - Создание карточек - - Синхронизация заказов - - Обновление остатков - -**Заказы и доставка** -- `OrdersController.php` - Управление заказами -- `DeliveryController.php` - Отслеживание доставки - -**Обмен данными** -- `DataController.php` - Общие операции с данными -- `DataBuhController.php` - Бухгалтерские данные -- `DataTestController.php` - Тестовые эндпоинты - -**Внешние системы** -- `TelegramController.php` - Интеграция с Telegram ботом -- `TelegramSalebotController.php` - Специфичный бот продаж -- `ChatbotActionController.php` - Действия чат-бота - -**Каталоги и товары** -- `StoreController.php` - Управление магазинами -- `UniversalCatalogController.php` - Каталог товаров -- `KikController.php` - Интеграция с системой KIK - -**Задачи и сайт** -- `TaskController.php` - RESTful API задач -- `SiteController.php` - Операции уровня сайта - -### 4. Модели данных (Records) - -**Каталог**: `records/` - -Active Record модели: -- `ApiUser.php` - Модель аутентификации пользователя API - - Реализует `IdentityInterface` - - Аутентификация на основе токенов - - Валидация пароля в открытом виде (проблема безопасности) - -- `Task.php` - Модель управления задачами - -### 5. Вспомогательные каталоги - -#### `amo_data/` -Хранение данных интеграции с AmoCRM: -- `token_info.json` - Информация об OAuth токене - -#### `json/` -Логирование и отладка запросов/ответов: -- Журналы запросов с временными метками -- Журналы ошибок -- Отслеживание измененных заказов -- Журналы загрузок - -#### `runtime/` -Временные файлы выполнения (кэш, логи и т.д.) - -#### `swagger/` -Документация API (спецификация Swagger/OpenAPI) - -## Процесс аутентификации - -```mermaid -sequenceDiagram - participant Client - participant AuthController - participant ApiUser - participant Database - - Client->>AuthController: POST /auth (login, password) - AuthController->>ApiUser: findByLogin(login) - ApiUser->>Database: SELECT * FROM api_user - Database-->>ApiUser: User record - ApiUser->>ApiUser: validatePassword(password) - ApiUser->>ApiUser: generateAccessToken() - ApiUser->>Database: UPDATE access_token - ApiUser-->>AuthController: access_token - AuthController-->>Client: JSON ответ с токеном -``` - -## Поток запросов - -```mermaid -sequenceDiagram - participant Client - participant BaseController - participant Controller - participant Model - participant Database - - Client->>BaseController: HTTP запрос - BaseController->>BaseController: CORS Preflight проверка - BaseController->>BaseController: Аутентификация по токену - BaseController->>Controller: Аутентифицированный запрос - Controller->>Model: Бизнес-логика - Model->>Database: Запрос - Database-->>Model: Результат - Model-->>Controller: Данные - Controller-->>BaseController: Данные ответа - BaseController-->>Client: JSON ответ -``` - -## Точки интеграции - -### 1. Основное приложение ERP -- **База данных**: Общее подключение к базе данных -- **Модели**: Использует модели из пространства имен `yii_app\records` -- **Конфигурация**: Общий `params.php` -- **Vendor**: Общие зависимости Composer - -### 2. Внешние сервисы - -**Яндекс.Маркет** -- Интеграция с OpenAPI Client -- Управление кампаниями -- Синхронизация заказов -- Управление остатками -- Создание карточек товаров - -**RabbitMQ** -- Очередь сообщений Telegram -- Exchange: `telegram-exchange` -- Queue: `telegram-queue` -- TTR: 600 секунд -- Попытки повтора: 3 - -**AmoCRM** -- OAuth на основе токенов -- Синхронизация данных - -### 3. Мобильные/Веб приложения -- RESTful эндпоинты -- Аутентификация по токенам -- JSON ответы - -## Соображения безопасности - -### Текущая реализация - -**Сильные стороны**: -- Аутентификация на основе токенов -- Конфигурация CORS -- Фреймворк авторизации RBAC -- Логирование запросов -- Сессии отключены (stateless) - -**Слабые стороны** (выявленные): -- Сравнение паролей в открытом виде -- Отсутствие хэширования паролей -- CORS разрешает все источники -- Не видно ограничения скорости -- Токен хранится в базе данных без шифрования - -### Рекомендуемые улучшения -1. Внедрить хэширование паролей (bcrypt/argon2) -2. Ограничить CORS определенными источниками -3. Добавить middleware ограничения скорости -4. Внедрить истечение токенов -5. Добавить валидацию запросов -6. Включить только HTTPS -7. Добавить версионирование API - -## Архитектура очереди сообщений - -``` -Client → API Controller → Queue Push → RabbitMQ - ↓ - Queue Worker - ↓ - Фоновая задача - ↓ - База данных -``` - -**Конфигурация**: -- DSN: `amqp://admin:3qqHK2MRgGgxUdVT61@RABBIT_HOST:5672` -- Queue: `telegram-queue` -- Exchange: `telegram-exchange` -- Поведение: Логируется через `LogBehavior` - -## URL маршрутизация - -### RESTful маршруты - -```php -// Явные маршруты -'auth' => 'auth/login' -'delivery/admin-auth' => 'delivery/admin-auth' -'POST data-buh/request/' => 'data-buh/request' - -// RESTful ресурс -['class' => 'yii\rest\UrlRule', 'controller' => ['task']] -``` - -### RESTful эндпоинты задач -- `GET /task` - Список всех задач -- `GET /task/:id` - Получить конкретную задачу -- `POST /task` - Создать задачу -- `PUT /task/:id` - Обновить задачу -- `DELETE /task/:id` - Удалить задачу - -## Соображения производительности - -### Кэширование -- Включено кэширование на основе файлов -- Asset manager с временными метками -- Оптимизировано форматирование ответов - -### База данных -- Общий пул соединений с основным приложением -- ORM Active Record -- Доступна жадная загрузка - -### Асинхронная обработка -- RabbitMQ для тяжелых операций -- Лимит времени выполнения 600 секунд -- 3 попытки повтора - -## Архитектура развертывания - -``` -[Клиентские приложения] → [Load Balancer] → [Экземпляр API2] → [База данных] - ↓ - [RabbitMQ] - ↓ - [Queue Workers] -``` - -## Мониторинг и логирование - -### Конфигурация логов - -**Цели**: -- Логирование на основе файлов -- Уровни: `error`, `warning` -- Уровень трассировки: 3 - -**Исключенные HTTP исключения**: -- 401 Unauthorized -- 403 Forbidden -- 404 Not Found -- 405 Method Not Allowed - -### Логирование запросов - -JSON файлы, хранящиеся в каталоге `json/`: -- Полезная нагрузка запросов -- Данные ответов -- Детали ошибок -- Временные метки - -## Разработка vs Продакшен - -### Режим разработки -- `YII_DEBUG = true` -- `YII_ENV = 'dev'` -- Красивое форматирование JSON ответов -- Подробные сообщения об ошибках - -### Режим продакшена (рекомендуется) -- Отключить режим отладки -- Использовать продакшен конфигурацию -- Минимизировать раскрытие ошибок -- Включить сжатие ответов - -## Версионирование API - -**Текущее состояние**: Версионирование не реализовано - -**Рекомендация**: Внедрить версионирование на основе URL -``` -/api/v1/task -/api/v2/task -``` - -## Зависимости - -### Внешние библиотеки -- `yii2/framework` - Основной фреймворк -- `yii2-queue` - Компонент очереди -- `yii2-rbac` - Авторизация -- Yandex Market OpenAPI Client -- GuzzleHTTP - HTTP клиент - -### Внутренние зависимости -- Модели основной ERP (`yii_app\records`) -- Общая конфигурация базы данных -- Общие параметры -- MarketplaceService - -## Будущие улучшения - -1. **Версионирование API** - Внедрить контроль версий -2. **Ограничение скорости** - Предотвращение злоупотреблений -3. **Слой кэширования** - Интеграция с Redis -4. **GraphQL** - Альтернатива REST -5. **WebSocket** - Обновления в реальном времени -6. **OAuth2** - Стандартная аутентификация -7. **API Gateway** - Централизованное управление -8. **Микросервисы** - Разделение сервисов -9. **Документация** - Автогенерация из кода -10. **Тестирование** - Модульные и интеграционные тесты - -## Заключение - -Модуль API2 обеспечивает прочную основу для внешних интеграций с правильным разделением ответственности, RESTful дизайном и расширяемостью. Однако перед развертыванием в продакшене следует реализовать улучшения безопасности и современные лучшие практики. diff --git a/docs/api2/ru/DEPENDENCIES.md b/docs/api2/ru/DEPENDENCIES.md deleted file mode 100644 index e7d17268..00000000 --- a/docs/api2/ru/DEPENDENCIES.md +++ /dev/null @@ -1,630 +0,0 @@ -# Зависимости и интеграции API2 - -## Обзор - -Данный документ отображает все зависимости, интеграции и внешние подключения для модуля API2. - -## Категории зависимостей - -1. Зависимости от фреймворка -2. Внутренние зависимости ERP -3. Зависимости от внешних сервисов -4. Зависимости от базы данных -5. Зависимости от библиотек - ---- - -## 1. Зависимости от фреймворка - -### Ядро фреймворка Yii2 - -**Пространство имён**: `yii\*` - -**Используемые компоненты**: - -| Компонент | Класс | Назначение | -|-----------|-------|---------| -| Web Application | `yii\web\Application` | Основной контейнер приложения | -| REST Controller | `yii\rest\Controller` | Базовый контроллер REST API | -| Active Record | `yii\db\ActiveRecord` | ORM для работы с БД | -| RBAC | `yii\rbac\DbManager` | Управление доступом на основе ролей | -| Queue | `yii\queue\amqp_interop\Queue` | Интеграция очереди сообщений | -| Filters | `yii\filters\Cors` | Обработка CORS | -| Filters | `yii\filters\auth\*` | Фильтры аутентификации | -| Response | `yii\web\Response` | Форматирование HTTP-ответов | -| Request | `yii\web\Request` | Парсинг HTTP-запросов | -| Logging | `yii\log\FileTarget` | Логирование в файлы | -| Caching | `yii\caching\FileCache` | Файловый кеш | - -**Установка**: Через Composer - -```json -{ - "require": { - "yiisoft/yii2": "~2.0", - "yiisoft/yii2-queue": "*" - } -} -``` - -### Расширения Yii2 Queue - -**Пакет**: `yii2-queue` - -**Интеграция**: Брокер сообщений RabbitMQ - -**Конфигурация**: -```php -'queue' => [ - 'class' => Queue::class, - 'dsn' => 'amqp://admin:3qqHK2MRgGgxUdVT61@RABBIT_HOST:5672', - 'queueName' => 'telegram-queue', - 'exchangeName' => 'telegram-exchange' -] -``` - ---- - -## 2. Внутренние зависимости ERP - -### Модели основного приложения - -**Пространство имён**: `yii_app\records` - -**Используемые общие модели**: - -| Модель | Таблица | Использование в контроллерах | -|-------|-------|------------------| -| `MarketplaceOrders` | `marketplace_orders` | MarketplaceController, YandexMarketController | -| `MarketplaceStatus` | `marketplace_status` | MarketplaceController | -| `MarketplaceOrder1cStatuses` | `marketplace_order_1c_statuses` | MarketplaceController | -| `MarketplaceOrderDelivery` | `marketplace_order_delivery` | YandexMarketController | -| `MarketplaceOrderItems` | `marketplace_order_items` | YandexMarketController | -| `MarketplaceOrderStatusHistory` | `marketplace_order_status_history` | YandexMarketController | -| `MarketplaceOrderStatusTypes` | `marketplace_order_status_types` | YandexMarketController | -| `MarketplaceStore` | `marketplace_store` | YandexMarketController | -| `ExportImportTable` | `export_import_table` | MarketplaceController | -| `MatrixErp` | `matrix_erp` | YandexMarketController | -| `Products1c` | `products_1c` | YandexMarketController | - -**Тип зависимости**: Жёсткая зависимость от схемы основной БД ERP - -**Расположение**: `erp24/records/` (родительская директория) - -### Сервисы основного приложения - -**Пространство имён**: `yii_app\services` - -**Используемые сервисы**: - -| Сервис | Назначение | Используется в | -|---------|---------|---------| -| `MarketplaceService` | Бизнес-логика маркетплейсов | YandexMarketController | - -**Методы**: -- `getMarketplaceProducts()` - Получение товаров для маркетплейса -- `fetchOrders()` - Получение заказов из API Яндекс.Маркета -- `processOrders()` - Обработка и синхронизация заказов в БД - -### Общая конфигурация - -**Конфигурация БД**: `erp24/config/db.php` - -```php -'db' => require __DIR__ . '/../../config/db.php' -``` - -**Параметры**: `erp24/config/params.php` - -```php -'params' => require dirname(__DIR__, 2) . '/config/params.php' -``` - -**Используемые общие параметры**: -- `YANDEX_MARKET_API_KEY` - Аутентификация в API Яндекс.Маркета - -### Псевдонимы приложения - -```php -Yii::setAlias('@yii_app', dirname(__DIR__)); -``` - -**Предоставляет**: -- Доступ к моделям основного приложения -- Доступ к сервисам основного приложения -- Загрузку общих ресурсов - ---- - -## 3. Зависимости от внешних сервисов - -### API Яндекс.Маркета - -**Тип интеграции**: RESTful API - -**Библиотека**: `OpenAPI\Client` (PHP SDK для Яндекс.Маркета) - -**Компоненты**: - -| Компонент | Назначение | -|-----------|---------| -| `OpenAPI\Client\Configuration` | Конфигурация API | -| `OpenAPI\Client\Api\BusinessOfferMappingsApi` | Управление товарами | -| `OpenAPI\Client\Api\CampaignsApi` | Операции с кампаниями | -| `OpenAPI\Client\Api\CategoriesApi` | Управление категориями | -| `OpenAPI\Client\Api\StocksApi` | Обновление остатков | -| `OpenAPI\Client\Api\HiddenOffersApi` | Видимость товаров | -| `OpenAPI\Client\Model\*` | Модели данных | -| `OpenAPI\Client\ObjectSerializer` | Сериализация | - -**Аутентификация**: API Key - -```php -$config = Configuration::getDefaultConfiguration() - ->setApiKey('Api-Key', Yii::$app->params['YANDEX_MARKET_API_KEY']); -``` - -**HTTP-клиент**: GuzzleHTTP - -```php -$apiInstance = new Api\BusinessOfferMappingsApi( - new GuzzleHttp\Client(), - $config -); -``` - -**Используемые эндпоинты**: -- `getCampaigns()` - Список кампаний -- `getCategoriesTree()` - Иерархия категорий -- `updateoffermappings()` - Обновление карточек товаров -- `addHiddenOffers()` - Скрытие товаров -- `updateStocks()` - Обновление остатков -- Получение заказов по дате/статусу - -**Campaign ID**: `109969229` (жёстко закодирован) -**Business ID**: `5330887` (жёстко закодирован) - -### Брокер сообщений RabbitMQ - -**Протокол**: AMQP - -**Подключение**: -``` -Host: Переменная окружения RABBIT_HOST (по умолчанию: localhost) -Port: 5672 -User: admin -Password: 3qqHK2MRgGgxUdVT61 -``` - -**Конфигурация**: -- Имя очереди: `telegram-queue` -- Exchange: `telegram-exchange` -- TTR: 600 секунд -- Попытки повтора: 3 - -**Использование**: -- Обработка сообщений Telegram-бота -- Асинхронное выполнение задач -- Обработка фоновых задач - -**Мониторинг**: `yii\queue\LogBehavior` для логирования - -### AmoCRM - -**Тип интеграции**: OAuth 2.0 - -**Хранение данных**: `amo_data/token_info.json` - -**Структура токена**: -```json -{ - "access_token": "...", - "refresh_token": "...", - "expires_in": 86400, - "created_at": timestamp -} -``` - -**Назначение**: Синхронизация данных CRM - -### API Telegram Bot - -**Контроллеры**: -- `TelegramController` -- `TelegramSalebotController` - -**Метод интеграции**: Webhook или polling - -**Обработка сообщений**: Через очередь RabbitMQ - -**Назначение**: -- Коммуникация с клиентами -- Уведомления о заказах -- Автоматизация продаж - ---- - -## 4. Зависимости от базы данных - -### Подключение к БД - -**Конфигурация**: Наследуется от основной ERP - -**Файл**: `erp24/config/db.php` - -**Ожидаемая структура**: -```php -return [ - 'class' => 'yii\db\Connection', - 'dsn' => 'mysql:host=localhost;dbname=erp24', - 'username' => 'user', - 'password' => 'password', - 'charset' => 'utf8', -]; -``` - -### Используемые таблицы БД - -**Таблицы, специфичные для API2**: - -1. **api_user** - - Поля: `id`, `login`, `password`, `access_token` - - Назначение: Аутентификация API - -2. **task** (предполагается) - - Назначение: Управление задачами через REST API - -**Общие таблицы ERP** (через модели yii_app): - -| Таблица | Назначение | Модель | -|-------|---------|-------| -| `marketplace_orders` | Заказы с маркетплейсов | MarketplaceOrders | -| `marketplace_status` | Статусы заказов | MarketplaceStatus | -| `marketplace_order_1c_statuses` | Сопоставление статусов 1C | MarketplaceOrder1cStatuses | -| `marketplace_order_delivery` | Информация о доставке | MarketplaceOrderDelivery | -| `marketplace_order_items` | Позиции заказов | MarketplaceOrderItems | -| `marketplace_order_status_history` | История изменения статусов | MarketplaceOrderStatusHistory | -| `marketplace_order_status_types` | Типы статусов | MarketplaceOrderStatusTypes | -| `marketplace_store` | Магазины маркетплейсов | MarketplaceStore | -| `export_import_table` | Сопоставление синхронизации данных | ExportImportTable | -| `matrix_erp` | Матрица товаров | MatrixErp | -| `products_1c` | Товары из 1C | Products1c | -| `rbac_*` | Таблицы RBAC | (Yii2 RBAC) | - -### Зависимости схемы БД - -**Связи**: -``` -MarketplaceOrders - ├── marketplace_id → MarketplaceStore - ├── status_id → MarketplaceStatus - ├── status_1c → MarketplaceOrder1cStatuses - └── items → MarketplaceOrderItems - -MarketplaceOrderItems - └── product_id → Products1c → MatrixErp - -ExportImportTable - ├── entity_id → CityStore (предполагается) - └── export_val → Сопоставление GUID -``` - -**Ограничения внешних ключей**: Предполагаются (не видны в коде API) - ---- - -## 5. Зависимости от библиотек - -### PHP-библиотеки (Composer) - -**Требуемые пакеты**: - -| Пакет | Назначение | Версия | -|---------|---------|---------| -| `yiisoft/yii2` | Ядро фреймворка | ~2.0 | -| `yiisoft/yii2-queue` | Компонент очередей | * | -| `guzzlehttp/guzzle` | HTTP-клиент | * | -| `yandex-market/*` | SDK для Яндекс.Маркета | * | -| `bower-asset/*` | Frontend-ассеты | * | -| `npm-asset/*` | NPM-ассеты | * | - -**Автозагрузка**: PSR-4 через Composer - -```php -require __DIR__ . '/../vendor/autoload.php'; -``` - -### Зависимости ассетов - -**Псевдонимы**: -```php -'@bower' => '@vendor/bower-asset' -'@npm' => '@vendor/npm-asset' -``` - -**Управление**: Yii2 Asset Manager - -**Хранение**: `@app/web/cache/assets` - ---- - -## Диаграммы потоков интеграции - -### Синхронизация заказов Яндекс.Маркета - -```mermaid -graph LR - A[Яндекс.Маркет] -->|API-вызов| B[YandexMarketController] - B -->|OpenAPI Client| C[Configuration] - C -->|GuzzleHTTP| D[Yandex API] - D -->|Данные заказов| E[MarketplaceService] - E -->|Обработка| F[Модель MarketplaceOrders] - F -->|Сохранение| G[База данных] - E -->|Товары| H[Модель Products1c] - H -->|Цена/Остатки| I[Модель MatrixErp] -``` - -### Очередь сообщений Telegram - -```mermaid -graph LR - A[Telegram Bot] -->|Webhook| B[TelegramController] - B -->|Отправка| C[Очередь RabbitMQ] - C -->|Worker| D[Фоновый процесс] - D -->|Обработка| E[Бизнес-логика] - E -->|Обновление| F[База данных] - E -->|Ответ| G[Telegram API] -``` - -### Поток аутентификации - -```mermaid -graph LR - A[Клиент] -->|POST /auth| B[AuthController] - B -->|Запрос| C[Модель ApiUser] - C -->|SELECT| D[Таблица api_user] - D -->|Данные пользователя| C - C -->|Проверка| E[Проверка пароля] - E -->|Генерация| F[Access Token] - F -->|UPDATE| D - F -->|Возврат| A -``` - ---- - -## Зависимости от переменных окружения - -### Требуемые переменные окружения - -```bash -# RabbitMQ -RABBIT_HOST=localhost - -# Яндекс.Маркет (через params.php) -YANDEX_MARKET_API_KEY=your_api_key - -# База данных (через db.php) -DB_HOST=localhost -DB_NAME=erp24 -DB_USER=user -DB_PASSWORD=password - -# Приложение -YII_DEBUG=true -YII_ENV=dev -``` - ---- - -## Точки внедрения зависимостей - -### Внедрение конфигурации - -**index.php**: -```php -$config = require __DIR__.'/config/api2.config.php'; -$application = new yii\web\Application($config); -``` - -### Паттерн Service Locator - -**Компоненты** (через Yii::$app): -```php -Yii::$app->db // Подключение к БД -Yii::$app->user // Компонент пользователя -Yii::$app->request // HTTP-запрос -Yii::$app->response // HTTP-ответ -Yii::$app->log // Логгер -Yii::$app->cache // Кеш -Yii::$app->queue // Очередь сообщений -Yii::$app->authManager // Менеджер RBAC -Yii::$app->security // Хелпер безопасности -``` - ---- - -## Интеграция сторонних сервисов - -### Сервисы, требующие API-ключи - -1. **Яндекс.Маркет** - - Тип ключа: API Key - - Хранение: `params.php` - - Безопасность: ⚠️ Файловое хранение (не переменная окружения) - -2. **AmoCRM** - - Тип ключа: OAuth 2.0 - - Хранение: `amo_data/token_info.json` - - Обновление: Требуется - -### Сервисы, требующие учётные данные - -1. **RabbitMQ** - - Имя пользователя: `admin` - - Пароль: `3qqHK2MRgGgxUdVT61` - - Безопасность: ⚠️ Жёстко закодировано в конфигурации - -2. **База данных** - - Учётные данные: В `db.php` - - Безопасность: ✅ Отдельный файл конфигурации - ---- - -## Проблемы безопасности зависимостей - -### Высокий риск - -1. **Пароль RabbitMQ**: Жёстко закодирован в файле конфигурации -2. **API-ключ Яндекс.Маркета**: Хранится в params.php -3. **Учётные данные БД**: В системе контроля версий -4. **Пароли пользователей API**: Не хешируются - -### Рекомендации - -1. **Использовать переменные окружения**: Перенести все учётные данные в `.env` -2. **Внедрить управление секретами**: Использовать HashiCorp Vault или аналог -3. **Ротация учётных данных**: Внедрить регулярную ротацию -4. **Шифрование токенов**: Шифровать сохранённые OAuth-токены -5. **Хеширование паролей**: Использовать bcrypt/argon2 для паролей пользователей - ---- - -## Управление версиями зависимостей - -### Composer - -**Файл**: `composer.json` (в родительской директории) - -**Lock-файл**: `composer.lock` - -**Стратегия обновления**: -```bash -composer update # Обновить все -composer update yiisoft/yii2 # Обновить конкретный пакет -``` - -### Ограничения версий - -**Текущее**: Вероятно используется `~2.0` для Yii2 - -**Рекомендация**: Использовать ограничения семантического версионирования -```json -{ - "require": { - "yiisoft/yii2": "^2.0.43", - "yiisoft/yii2-queue": "^2.3" - } -} -``` - ---- - -## Риски критических изменений - -### Зависимости высокого риска - -1. **Фреймворк Yii2**: Изменения основной версии -2. **API Яндекс.Маркета**: Обновления версии API -3. **Протокол RabbitMQ**: Изменения версии AMQP -4. **Версия PHP**: Обновления версии языка - -### Стратегии снижения рисков - -1. **Закрепление версий**: Фиксация основных версий -2. **Тестирование**: Комплексный набор тестов -3. **Мониторинг**: Отслеживание устаревших функций -4. **Документация**: Отслеживание изменений API - ---- - -## Граф зависимостей - -``` -Модуль API2 -├── Фреймворк Yii2 -│ ├── yii\web\Application -│ ├── yii\rest\Controller -│ ├── yii\db\ActiveRecord -│ └── yii\queue\* -├── Основное приложение ERP -│ ├── yii_app\records\* -│ ├── yii_app\services\* -│ └── config\* -├── Внешние сервисы -│ ├── API Яндекс.Маркета -│ ├── RabbitMQ -│ ├── AmoCRM -│ └── API Telegram -├── База данных -│ ├── api_user -│ ├── marketplace_* -│ └── products_* -└── Библиотеки - ├── GuzzleHTTP - ├── OpenAPI Client - └── Пакеты Composer -``` - ---- - -## Циклические зависимости - -**Текущее состояние**: Не обнаружено - -**Потенциальный риск**: Зависимость основного приложения от моделей API2 - -**Предотвращение**: Оставить API2 в роли потребителя, а не поставщика - ---- - -## Мониторинг зависимостей - -### Проверки работоспособности - -1. **Подключение к БД**: Проверка соединения -2. **RabbitMQ**: Мониторинг глубины очереди -3. **API Яндекс.Маркета**: Мониторинг лимитов запросов -4. **Истечение токенов**: Обновление OAuth-токенов - -### Логирование - -**Логируемые компоненты**: -- Запросы к БД -- API-вызовы -- Операции с очередями -- Попытки аутентификации - -**Расположение логов**: `runtime/logs/` - ---- - -## Резюме - -### Общее количество зависимостей - -- **Фреймворк**: 1 (Yii2) -- **Модели основного приложения**: 11+ -- **Сервисы основного приложения**: 1+ -- **Внешние API**: 3 (Яндекс, Telegram, AmoCRM) -- **Инфраструктура**: 2 (БД, RabbitMQ) -- **Библиотеки**: 5+ (пакеты Composer) - -### Состояние зависимостей - -- **Стабильно**: ✅ Фреймворк Yii2 -- **Умеренный риск**: ⚠️ Внешние API (версионирование) -- **Высокий риск**: ❌ Управление учётными данными -- **Критично**: 🔴 Реализация хеширования паролей - -### Рекомендуемые действия - -1. Внедрить управление переменными окружения -2. Добавить ограничения версий зависимостей -3. Создать эндпоинты проверки работоспособности -4. Внедрить ротацию учётных данных -5. Добавить управление версиями API -6. Создать политику обновления зависимостей - ---- - -[English version](../DEPENDENCIES.md) | **Русская версия** diff --git a/docs/api2/ru/ENDPOINTS.md b/docs/api2/ru/ENDPOINTS.md deleted file mode 100644 index b7e1dd09..00000000 --- a/docs/api2/ru/ENDPOINTS.md +++ /dev/null @@ -1,842 +0,0 @@ -# Каталог эндпоинтов API2 - -> 📖 **Язык**: Русский | [English](../ENDPOINTS.md) - -Полный каталог всех доступных эндпоинтов API с параметрами, ответами и примечаниями по использованию. - ---- - -## Эндпоинты аутентификации - -### POST `/auth/login` -Аутентификация пользователя и получение токена доступа. - -**Аутентификация**: Не требуется - -**Запрос**: -```json -{ - "login": "string", - "password": "string" -} -``` - -**Ответ**: -```json -{ - "access-token": "string" -} -``` - -**Ошибки**: -```json -{ - "errors": "Wrong login of password" -} -``` - ---- - -## Эндпоинты остатков - -### POST `/balance/get` -Получение остатков товаров для магазина. - -**Запрос**: -```json -{ - "store_id": "store-guid" -} -``` - -**Ответ**: -```json -[ - { - "product_id": "guid", - "quantity": 15.0, - "reserv": 2.0 - } -] -``` - -**Ошибки**: -- `400`: Некорректный JSON -- `store_id is required`: Отсутствует параметр - -### GET `/balance/test` -Тестовый эндпоинт для проверки работы контроллера остатков. - -**Ответ**: -```json -["ok"] -``` - ---- - -## Эндпоинты клиентов - -### POST `/client/add` -Добавление или обновление клиента в системе. - -**Запрос**: -```json -{ - "phone": "79001234567", - "name": "John Doe", - "client_id": 123, - "client_type": 1, - "platform_id": 1, - "avatar": "url", - "full_name": "John Smith Doe", - "messenger": "telegram", - "message_id": "msg123", - "date_of_creation": "timestamp" -} -``` - -**Ответ**: -```json -{ - "result": true, - "result_edit": "...", - "editDates": true -} -``` - -**Ошибки**: -```json -{ - "error_id": 1, - "error": "phone is required" -} -``` - -### POST `/client/balance` -Получение бонусного баланса клиента и ключевого кода. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "balance": 500, - "keycode": "1234", - "editDates": true -} -``` - -### POST `/client/get` -Получение информации о мессенджере клиента. - -**Запрос**: -```json -{ - "phone": "79001234567", - "client_type": "1" -} -``` - -**Ответ**: -```json -{ - "client_id": 123, - "platform_id": 456 -} -``` - -**Ошибки**: -- `error_id: 2`: Нет клиента с таким телефоном и client_type -- `error_id: 3`: Клиент отписался от рассылки - -### POST `/client/event-edit` -Добавление или обновление памятных дат/событий для клиента. - -**Запрос**: -```json -{ - "phone": "79001234567", - "channel": "salebot", - "events": [ - { - "number": 1, - "date": "25.12.2024", - "tip": "День рождения" - } - ] -} -``` - -Или одно событие: -```json -{ - "phone": "79001234567", - "number": 1, - "date": "25.12.2024", - "tip": "День рождения" -} -``` - -**Ответ**: -```json -{ - "response": true -} -``` - -**Бонус**: Автоматически начисляется 300 бонусных баллов при добавлении 5 памятных дат. - -### POST `/client/show-keycode` -Отправка QR-кода с ключевым кодом клиенту через мессенджер. - -**Запрос**: -```json -{ - "phone": "79001234567", - "platform_id": 123 -} -``` - -**Ответ**: Перенаправлен из сервиса telegram - -### POST `/client/store-geo` -Отправка местоположений магазинов клиенту на основе геолокации. - -**Запрос**: -```json -{ - "location": "55.7558,37.6173", - "platform_id": 123 -} -``` - -**Ответ**: Перенаправлен из сервиса telegram - -### POST `/client/check-details` -Получение постраничного списка истории покупок клиента. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "response": { - "checks": [ - { - "id": 123, - "store": { - "id": "store-guid", - "name": "Store Name" - }, - "number": "CHECK-001", - "payment": [ - {"type": "cash"}, - {"type": "card"} - ], - "date": "2024-11-13T10:30:00+03:00", - "sum": 1500, - "discount": 150, - "order_id": "order-guid", - "seller_id": "seller-guid", - "products": [ - { - "product_id": "prod-guid", - "quantity": 2.0, - "price": 750.0, - "discount": 75.0 - } - ], - "bonuses": [ - { - "name": "Bonus Name", - "amount": 50 - } - ] - } - ], - "pages": { - "totalCount": 100, - "page": 0, - "per-page": 20 - } - } -} -``` - -### POST `/client/check-detail` -Получение деталей одного чека по ID. - -**Запрос**: -```json -{ - "check_id": 123 -} -``` - -**Ответ**: Тот же формат, что и один чек из `/check-details` - -### POST `/client/bonus-write-off` -Получение постраничного списка списаний бонусов. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "response": { - "bonuses": [ - { - "name": "Bonus description", - "amount": -100, - "check_id": "check-guid", - "date": "2024-11-13T10:30:00+03:00" - } - ], - "pages": { - "totalCount": 50, - "page": 0, - "per-page": 20 - } - } -} -``` - -### POST `/client/use-bonuses` -Списание бонусных баллов со счета клиента. - -**Запрос**: -```json -{ - "order_id": "order-123", - "phone": "79001234567", - "points_to_use": 100, - "date": 1699876543, - "price": 1500 -} -``` - -**Ответ**: -```json -{ - "response": { - "code": 200, - "status": "success", - "data": { - "client_id": 456, - "order_id": "order-123", - "addedPoints": 100, - "remainingPoints": 400 - } - } -} -``` - -### POST `/client/add-bonus` -Начисление бонусных баллов на счет клиента. - -**Запрос**: -```json -{ - "order_id": "order-123", - "phone": "79001234567", - "points_to_add": 50, - "date": 1699876543, - "price": 1500 -} -``` - -**Ответ**: -```json -{ - "response": { - "code": 200, - "status": "success", - "data": { - "phone": "79001234567", - "order_id": "order-123", - "addedPoints": 50, - "totalPoints": 550 - } - } -} -``` - -### POST `/client/bonus-status` -Получение бонусного уровня и статуса клиента. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "response": { - "phone": "79001234567", - "alias": "gold", - "bonus_level": "Золотой", - "current_points": 5000, - "next_points": 10000, - "discount_percent": 15, - "cashback_rate": 10 - } -} -``` - -### POST `/client/memorable-dates` -Получение памятных дат клиента. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "response": [ - { - "date": "25.12.2024", - "number": 1, - "tip": "День рождения" - } - ] -} -``` - -### POST `/client/social-ids` -Получение ID клиента на социальных платформах. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "response": [ - { - "platform": "telegram", - "user_id": 123456789 - } - ] -} -``` - -### POST `/client/get-info` -Получение комплексной информации о клиенте. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` -Или: -```json -{ - "ref_code": "ABC123XYZ" -} -``` - -**Ответ**: -```json -{ - "response": { - "id": 123, - "card": "12345678", - "name": "John Doe", - "first_name": "John", - "second_name": "Doe", - "sex": "male", - "email": "john@example.com", - "birth_day": "1990-01-15", - "comment": "VIP customer", - "keycode": "1234", - "ref_code": "ABC123XYZ", - "referral_id": null, - "balance": 500, - "burn_balans": 0, - "bonus_level": "gold", - "total_price": 15000, - "total_price_rejected": 500, - "referral_count_get_bonus_already": 3, - "referral_count_all": 5, - "editDates": true, - "birth_day_readonly": true, - "events_readonly": false, - "events": [...] - } -} -``` - -### GET `/client/get-stores` -Получение списка всех активных магазинов. - -**Ответ**: -```json -{ - "response": [ - { - "id": 1, - "name": "Store Name" - } - ] -} -``` - -### POST `/client/phone-keycode-by-card` -Получение телефона и ключевого кода по номеру карты. - -**Запрос**: -```json -{ - "card": "12345678" -} -``` - -**Ответ**: -```json -{ - "response": { - "phone": "79001234567", - "keycode": "1234" - } -} -``` - -### POST `/client/get-user-info` -Получение детальной статистики пользователя и информации о покупках. - -**Запрос**: -```json -{ - "phone": "79001234567" -} -``` - -**Ответ**: -```json -{ - "response": { - "name": "John Doe", - "sex": "male", - "sale_avg_price": 1500, - "total_price": 15000, - "registration_date": "2024-01-01 10:00:00", - "bonus_balance": 500, - "bonus_minus": 100, - "date_first_sale": "2024-01-15 12:30:00", - "date_last_sale": "2024-11-10 15:45:00", - "sale_cnt": 10, - "total_price_rejected": 500, - "events": [...], - "platform": { - "telegram": { - "is_subscribed": 1, - "created_at": "2024-01-01 10:00:00" - } - } - } -} -``` - -### POST `/client/change-user-subscription` -Обновление статуса подписки пользователя в telegram. - -**Запрос**: -```json -{ - "phone": "79001234567", - "telegram_is_subscribed": 1 -} -``` - -**Ответ**: -```json -{ - "response": true -} -``` - -### POST `/client/apply-promo-code` -Применение промокода к аккаунту пользователя. - -**Запрос**: -```json -{ - "phone": "79001234567", - "code": "PROMO2024" -} -``` - -**Ответ**: -```json -{ - "response": ["ok"] -} -``` - -**Ошибки**: -- `error_id: 2`: Промокод истек или неизвестен -- `error_id: 3`: Промокод уже использован - ---- - -## Эндпоинты заказов - -### POST `/orders/change-status` -Обновление статуса заказа маркетплейса из 1C. - -**Запрос**: -```json -{ - "order": [ - { - "order_id": "order-guid", - "status": "NEW", - "seller_id": "seller-guid" - } - ], - "status_update": {} -} -``` - -**Ответ**: -```json -[ - { - "order_id": "order-guid", - "result": true, - "message": "Статус обновлён", - "status": 1 - } -] -``` - -**Ошибки**: -```json -{ - "order_id": "order-guid", - "result": "error", - "message": "Заказ не найден" -} -``` - -### POST `/orders/get-orders` -Получение заказов маркетплейса для магазина (за последние 24 часа). - -**Запрос**: -```json -{ - "store_id": "store-guid" -} -``` - -**Ответ**: -```json -{ - "success": true, - "result": [ - { - "order_id": "order-guid", - "status": "NEW", - "items": [ - { - "product_id": "product-guid", - "quantity": 2, - "price": 750.0 - } - ] - } - ] -} -``` - ---- - -## Эндпоинты маркетплейса - -### POST `/marketplace/statuses` -Получение всех статусов маркетплейса. - -**Ответ**: -```json -{ - "response": [ - { - "id": 1, - "code": "NEW", - "name": "Новый заказ" - } - ] -} -``` - -### POST `/marketplace/get-new-order-count` -Получение количества новых заказов для магазина (за последние 3 дня). - -**Запрос**: -```json -{ - "store_guid": "store-guid" -} -``` - -**Ответ**: -```json -{ - "response": 15 -} -``` - -**Ошибки**: -```json -{ - "error": "Не найден магазин по store_guid" -} -``` - -### POST `/marketplace/instruction-dictionary` -Получение рабочего процесса и переходов статусов заказа. - -**Запрос**: -```json -{ - "guid": "order-guid" -} -``` - -**Ответ**: -```json -{ - "response": [ - { - "marketplace": "ЯндексМаркет", - "status": "Новый", - "status_id": "NEW", - "status_instruction": "Принять заказ в обработку", - "status_relations": [ - { - "status": "В обработке", - "status_id": "PROCESSING", - "description": "Перевести в обработку", - "button_text": "Принять", - "order": 1 - } - ] - } - ] -} -``` - ---- - -## Эндпоинты YandexMarket - -### GET `/yandex-market/create-cards` -Создание/обновление карточек товаров на Яндекс.Маркете. - -**Параметры запроса**: -- `do`: Установите `1` для фактической отправки данных (иначе предпросмотр) - -**Ответ**: HTML-страница с результатами - -### GET `/yandex-market/get-orders` -Получение и обработка заказов Яндекс.Маркета. - -**Параметры запроса**: -- `from_date`: Дата начала (по умолчанию: сегодня, формат: `d-m-Y`) -- `to_date`: Дата окончания (опционально) -- `status`: Фильтр по статусу -- `substatus`: Фильтр по подстатусу -- `campaign_id`: ID кампании для тестовых данных - -**Тестовый режим** (POST с телом): -```json -{ - "orders": [...] -} -``` - -**Ответ**: -```json -{ - "response": "OK", - "storeCount": 5, - "result": { - "processed": 10, - "created": 5, - "updated": 5 - } -} -``` - ---- - -## Эндпоинты доставки - -### GET `/delivery/auth` -Простой тестовый эндпоинт аутентификации. - -**Ответ**: `"ok"` - -### POST `/delivery/admin-auth` -Аутентификация администратора по хешу. - -**Запрос**: -```json -{ - "hash": "md5-hash-of-credentials" -} -``` - -**Ответ**: -```json -{ - "id": 123, - "group_id": 1, - "group_name": "Administrators", - "name": "Admin Name" -} -``` - ---- - -## Сводная статистика - -**Всего контроллеров**: 6 -- AuthController: 1 эндпоинт -- BalanceController: 2 эндпоинта -- ClientController: 21 эндпоинт -- OrdersController: 2 эндпоинта -- MarketplaceController: 3 эндпоинта -- YandexMarketController: 2 эндпоинта -- DeliveryController: 2 эндпоинта - -**Всего эндпоинтов**: 33 - -**Требуется аутентификация**: 31 эндпоинт (все, кроме `/auth/login` и внутренних OPTIONS) diff --git a/docs/api2/ru/EXAMPLES.md b/docs/api2/ru/EXAMPLES.md deleted file mode 100644 index 1b599980..00000000 --- a/docs/api2/ru/EXAMPLES.md +++ /dev/null @@ -1,781 +0,0 @@ -> 📖 **Язык**: Русский | [English](../EXAMPLES.md) - -# Примеры кода API2 и руководства по использованию - -Практические примеры интеграции с модулем API2 на различных языках программирования. - ---- - -## Содержание - -1. [Примеры аутентификации](#примеры-аутентификации) -2. [Примеры управления клиентами](#примеры-управления-клиентами) -3. [Примеры управления заказами](#примеры-управления-заказами) -4. [Примеры бонусной системы](#примеры-бонусной-системы) -5. [Примеры обработки ошибок](#примеры-обработки-ошибок) - ---- - -## Примеры аутентификации - -### JavaScript/Node.js (fetch) - -```javascript -const API_BASE = 'https://erp.bazacvetov24.ru/api2'; - -async function login(username, password) { - const response = await fetch(`${API_BASE}/auth/login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - login: username, - password: password - }) - }); - - const data = await response.json(); - - if (data['access-token']) { - // Сохраняем токен для последующих запросов - localStorage.setItem('api_token', data['access-token']); - return data['access-token']; - } else { - throw new Error(data.errors || 'Login failed'); - } -} - -// Использование токена в последующих запросах -async function makeAuthenticatedRequest(endpoint, body) { - const token = localStorage.getItem('api_token'); - - const response = await fetch(`${API_BASE}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-ACCESS-TOKEN': token - }, - body: JSON.stringify(body) - }); - - return await response.json(); -} -``` - -### PHP (cURL) - -```php - true, - CURLOPT_POST => true, - CURLOPT_HTTPHEADER => ['Content-Type: application/json'], - CURLOPT_POSTFIELDS => json_encode([ - 'login' => $username, - 'password' => $password - ]) - ]); - - $response = curl_exec($ch); - $data = json_decode($response, true); - - curl_close($ch); - - if (isset($data['access-token'])) { - return $data['access-token']; - } else { - throw new Exception($data['errors'] ?? 'Login failed'); - } -} - -function makeAuthenticatedRequest($endpoint, $body, $token) { - $ch = curl_init(API_BASE . $endpoint); - - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, - CURLOPT_HTTPHEADER => [ - 'Content-Type: application/json', - 'X-ACCESS-TOKEN: ' . $token - ], - CURLOPT_POSTFIELDS => json_encode($body) - ]); - - $response = curl_exec($ch); - curl_close($ch); - - return json_decode($response, true); -} - -// Использование -$token = login('myuser', 'mypassword'); -$result = makeAuthenticatedRequest('/client/balance', [ - 'phone' => '79001234567' -], $token); -?> -``` - -### Python (requests) - -```python -import requests - -API_BASE = 'https://erp.bazacvetov24.ru/api2' - -def login(username, password): - response = requests.post( - f'{API_BASE}/auth/login', - json={'login': username, 'password': password} - ) - data = response.json() - - if 'access-token' in data: - return data['access-token'] - else: - raise Exception(data.get('errors', 'Login failed')) - -def make_authenticated_request(endpoint, body, token): - response = requests.post( - f'{API_BASE}{endpoint}', - json=body, - headers={'X-ACCESS-TOKEN': token} - ) - return response.json() - -# Использование -token = login('myuser', 'mypassword') -result = make_authenticated_request('/client/balance', { - 'phone': '79001234567' -}, token) -``` - ---- - -## Примеры управления клиентами - -### Добавление нового клиента - -```javascript -// JavaScript -async function addClient(phone, name, messenger_data) { - return await makeAuthenticatedRequest('/client/add', { - phone: phone, - name: name, - client_id: messenger_data.client_id, - client_type: messenger_data.client_type, - platform_id: messenger_data.platform_id, - avatar: messenger_data.avatar, - full_name: name, - messenger: 'telegram', - date_of_creation: Date.now() / 1000 - }); -} - -// Использование -const result = await addClient('79001234567', 'John Doe', { - client_id: 123, - client_type: 1, - platform_id: 456, - avatar: 'https://example.com/avatar.jpg' -}); - -if (result.result) { - console.log('Client added successfully'); -} else { - console.error('Error:', result.error_description); -} -``` - -### Получение баланса клиента - -```php - '79001234567' -], $token); - -echo "Balance: " . $result['balance'] . " points\n"; -echo "Keycode: " . $result['keycode'] . "\n"; -?> -``` - -### Получение истории покупок клиента - -```python -# Python -def get_client_purchases(phone, token, page=0): - result = make_authenticated_request('/client/check-details', { - 'phone': phone - }, token) - - if 'response' in result: - checks = result['response']['checks'] - pages = result['response']['pages'] - - print(f"Total purchases: {pages['totalCount']}") - print(f"Showing page {pages['page'] + 1} of {(pages['totalCount'] // pages['per-page']) + 1}") - - for check in checks: - print(f"\nOrder #{check['number']} - {check['date']}") - print(f"Store: {check['store']['name']}") - print(f"Total: {check['sum']} RUB (discount: {check['discount']} RUB)") - print(f"Products: {len(check['products'])}") - - for product in check['products']: - print(f" - Product {product['product_id']}: {product['quantity']} x {product['price']} RUB") - - return result - else: - print("Error:", result.get('error')) - -# Использование -purchases = get_client_purchases('79001234567', token) -``` - -### Добавление памятных дат - -```javascript -// JavaScript - Добавление нескольких событий одновременно -async function addMemorableDates(phone, events) { - return await makeAuthenticatedRequest('/client/event-edit', { - phone: phone, - channel: 'web', - events: events - }); -} - -// Использование -const dates = [ - { number: 1, date: '25.12.1990', tip: 'День рождения' }, - { number: 2, date: '14.02.2020', tip: 'День свадьбы' }, - { number: 3, date: '08.03.2024', tip: '8 марта' } -]; - -const result = await addMemorableDates('79001234567', dates); - -if (result.response) { - console.log('Events added successfully'); - // Примечание: Добавление 5 событий автоматически начисляет 300 бонусных баллов! -} -``` - ---- - -## Примеры управления заказами - -### Изменение статуса заказа - -```php - [ - [ - 'order_id' => $order_guid, - 'status' => $status_code, - 'seller_id' => $seller_id - ] - ] - ], $token); -} - -// Использование -$result = updateOrderStatus( - 'order-guid-123', - 'PROCESSING', - 'seller-guid-456', - $token -); - -foreach ($result as $order_result) { - echo "Order: " . $order_result['order_id'] . "\n"; - echo "Status: " . ($order_result['result'] ? 'Updated' : 'Error') . "\n"; - echo "Message: " . $order_result['message'] . "\n"; -} -?> -``` - -### Пакетное обновление статусов заказов - -```javascript -// JavaScript - Обновление нескольких заказов одновременно -async function updateMultipleOrders(orders) { - return await makeAuthenticatedRequest('/orders/change-status', { - order: orders.map(o => ({ - order_id: o.guid, - status: o.new_status, - seller_id: o.seller_id - })) - }); -} - -// Использование -const orders = [ - { guid: 'order-1', new_status: 'PROCESSING', seller_id: 'seller-1' }, - { guid: 'order-2', new_status: 'READY', seller_id: 'seller-2' }, - { guid: 'order-3', new_status: 'SHIPPED', seller_id: 'seller-1' } -]; - -const results = await updateMultipleOrders(orders); - -results.forEach(result => { - console.log(`Order ${result.order_id}: ${result.message}`); -}); -``` - -### Получение заказов магазина - -```python -# Python - Получение всех заказов магазина за последние 24 часа -def get_store_orders(store_guid, token): - result = make_authenticated_request('/orders/get-orders', { - 'store_id': store_guid - }, token) - - if result.get('success'): - orders = result['result'] - print(f"Found {len(orders)} orders") - - for order in orders: - print(f"\nOrder: {order['order_id']}") - print(f"Status: {order['status']}") - print(f"Items: {len(order['items'])}") - - for item in order['items']: - print(f" - {item['product_id']}: {item['quantity']} x {item['price']} RUB") - else: - print("Error:", result.get('error')) - -# Использование -get_store_orders('store-guid-123', token) -``` - ---- - -## Примеры бонусной системы - -### Применение бонусных баллов к покупке - -```javascript -// JavaScript - Использование бонусных баллов для заказа -async function useBonusPoints(order_id, phone, points, price) { - const result = await makeAuthenticatedRequest('/client/use-bonuses', { - order_id: order_id, - phone: phone, - points_to_use: points, - date: Math.floor(Date.now() / 1000), - price: price - }); - - if (result.response?.status === 'success') { - console.log(`Successfully deducted ${points} bonus points`); - console.log(`Remaining balance: ${result.response.data.remainingPoints}`); - return result.response.data; - } else { - throw new Error(result.error?.message || 'Failed to use bonuses'); - } -} - -// Использование -try { - const result = await useBonusPoints( - 'order-123', - '79001234567', - 100, // Использовать 100 бонусных баллов - 1500 // Сумма заказа: 1500 RUB - ); - console.log('New balance:', result.remainingPoints); -} catch (error) { - console.error('Error:', error.message); -} -``` - -### Начисление бонусных баллов после покупки - -```php - $order_id, - 'phone' => $phone, - 'points_to_add' => $points_to_add, - 'date' => time(), - 'price' => $purchase_amount - ], $token); -} - -// Использование -$result = awardPurchaseBonus( - 'order-456', - '79001234567', - 2000, // Покупка на 2000 RUB - $token -); - -if ($result['response']['status'] === 'success') { - echo "Added " . $result['response']['data']['addedPoints'] . " bonus points\n"; - echo "Total balance: " . $result['response']['data']['totalPoints'] . "\n"; -} -?> -``` - -### Проверка бонусного уровня и статуса - -```python -# Python - Получение информации о бонусном уровне клиента -def check_bonus_level(phone, token): - result = make_authenticated_request('/client/bonus-status', { - 'phone': phone - }, token) - - if 'response' in result: - status = result['response'] - print(f"Bonus Level: {status['bonus_level']} ({status['alias']})") - print(f"Current Points: {status['current_points']}") - print(f"Next Level: {status['next_points']} points") - print(f"Discount: {status['discount_percent']}%") - print(f"Cashback Rate: {status['cashback_rate']}%") - return status - else: - print("Error:", result.get('error')) - -# Использование -bonus_info = check_bonus_level('79001234567', token) -``` - -### Применение промокода - -```javascript -// JavaScript - Применение промокода к аккаунту клиента -async function applyPromoCode(phone, promo_code) { - try { - const result = await makeAuthenticatedRequest('/client/apply-promo-code', { - phone: phone, - code: promo_code - }); - - if (result.response) { - console.log('Promo code applied successfully!'); - return true; - } - } catch (error) { - if (error.error_id === 2) { - console.error('Promo code expired or invalid'); - } else if (error.error_id === 3) { - console.error('Promo code already used'); - } else { - console.error('Error:', error.error); - } - return false; - } -} - -// Использование -await applyPromoCode('79001234567', 'SPRING2024'); -``` - ---- - -## Примеры обработки ошибок - -### Комплексная обработка ошибок - -```javascript -// JavaScript - Надежная обертка для обработки ошибок -async function apiRequest(endpoint, body) { - try { - const token = localStorage.getItem('api_token'); - - if (!token && endpoint !== '/auth/login') { - throw new Error('No authentication token available'); - } - - const response = await fetch(`${API_BASE}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(token && { 'X-ACCESS-TOKEN': token }) - }, - body: JSON.stringify(body) - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); - - // Проверка ошибок на уровне API - if (data.error_id !== undefined) { - const error = new Error(data.error || 'API Error'); - error.error_id = data.error_id; - error.details = data.error_description; - throw error; - } - - if (data.error !== undefined) { - throw new Error(data.error.message || data.error); - } - - return data; - - } catch (error) { - console.error('API Request Failed:', error); - - // Обработка специфичных типов ошибок - if (error.error_id === 1 || error.error_id === 1.2) { - console.error('Invalid or missing parameters'); - } else if (error.error_id === 2) { - console.error('Resource not found or save failed'); - } else if (error.message.includes('401')) { - console.error('Authentication failed - token may be expired'); - // Перенаправление на страницу входа или обновление токена - } - - throw error; - } -} -``` - -### Обработчик ошибок PHP - -```php -error_id = $error_id; - $this->details = $details; - } -} - -function safeApiRequest($endpoint, $body, $token) { - try { - $data = makeAuthenticatedRequest($endpoint, $body, $token); - - if (isset($data['error_id'])) { - throw new ApiException( - $data['error'] ?? 'API Error', - $data['error_id'], - $data['error_description'] ?? null - ); - } - - if (isset($data['error'])) { - throw new ApiException($data['error']['message'] ?? $data['error']); - } - - return $data; - - } catch (ApiException $e) { - error_log("API Error [{$e->error_id}]: {$e->getMessage()}"); - - switch ($e->error_id) { - case 1: - case 1.2: - error_log("Invalid parameters"); - break; - case 2: - error_log("Resource not found"); - break; - case 3: - error_log("Business logic violation"); - break; - } - - throw $e; - } -} -?> -``` - -### Логика повторных попыток Python - -```python -import time -from typing import Any, Dict - -class ApiError(Exception): - def __init__(self, message, error_id=None, details=None): - super().__init__(message) - self.error_id = error_id - self.details = details - -def api_request_with_retry(endpoint: str, body: Dict[str, Any], token: str, - max_retries: int = 3, backoff: float = 1.0) -> Dict: - """ - Выполнение API-запроса с логикой экспоненциальной задержки при повторных попытках - """ - for attempt in range(max_retries): - try: - result = make_authenticated_request(endpoint, body, token) - - # Проверка ошибок API - if 'error_id' in result: - raise ApiError( - result.get('error', 'API Error'), - result.get('error_id'), - result.get('error_description') - ) - - if 'error' in result: - error_msg = result['error'].get('message', result['error']) - raise ApiError(error_msg) - - return result - - except ApiError as e: - # Не повторять при клиентских ошибках (эквивалент 4xx) - if e.error_id in [1, 1.2, 3]: - raise - - # Повторять при серверных ошибках - if attempt < max_retries - 1: - wait_time = backoff * (2 ** attempt) - print(f"Request failed, retrying in {wait_time}s...") - time.sleep(wait_time) - else: - raise - - except Exception as e: - if attempt < max_retries - 1: - wait_time = backoff * (2 ** attempt) - print(f"Unexpected error, retrying in {wait_time}s...") - time.sleep(wait_time) - else: - raise - -# Использование -try: - result = api_request_with_retry('/client/balance', { - 'phone': '79001234567' - }, token) - print(f"Balance: {result['balance']}") -except ApiError as e: - print(f"API Error [{e.error_id}]: {e}") -``` - ---- - -## Полный пример интеграции - -```javascript -// Полное управление жизненным циклом клиента -class FlowerShopAPI { - constructor(baseUrl) { - this.baseUrl = baseUrl; - this.token = null; - } - - async login(username, password) { - const response = await fetch(`${this.baseUrl}/auth/login`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ login: username, password }) - }); - - const data = await response.json(); - this.token = data['access-token']; - return this.token; - } - - async request(endpoint, body) { - const response = await fetch(`${this.baseUrl}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-ACCESS-TOKEN': this.token - }, - body: JSON.stringify(body) - }); - - return await response.json(); - } - - // Методы для работы с клиентами - async getClientInfo(phone) { - return await this.request('/client/get-info', { phone }); - } - - async getBalance(phone) { - return await this.request('/client/balance', { phone }); - } - - // Методы для работы с заказами - async processOrder(phone, order_total, bonus_to_use) { - // 1. Получить баланс клиента - const balance = await this.getBalance(phone); - - if (balance.balance < bonus_to_use) { - throw new Error('Insufficient bonus points'); - } - - // 2. Использовать бонусы - const use_result = await this.request('/client/use-bonuses', { - order_id: `order-${Date.now()}`, - phone, - points_to_use: bonus_to_use, - date: Math.floor(Date.now() / 1000), - price: order_total - }); - - // 3. Рассчитать кэшбэк - const cashback = Math.floor(order_total * 0.05); - - // 4. Начислить кэшбэк - const add_result = await this.request('/client/add-bonus', { - order_id: use_result.response.data.order_id, - phone, - points_to_add: cashback, - date: Math.floor(Date.now() / 1000), - price: order_total - }); - - return { - order_id: use_result.response.data.order_id, - bonus_used: bonus_to_use, - cashback_earned: cashback, - final_balance: add_result.response.data.totalPoints - }; - } -} - -// Использование -const api = new FlowerShopAPI('https://erp.bazacvetov24.ru/api2'); -await api.login('username', 'password'); - -const orderResult = await api.processOrder( - '79001234567', // phone - 2000, // order total - 100 // bonus points to use -); - -console.log('Order processed:', orderResult); -``` - ---- - -## Примечания - -- Всегда проверяйте номера телефонов перед отправкой запросов -- Храните токены аутентификации безопасно (никогда не используйте localStorage в production!) -- Реализуйте правильную обработку ошибок для всех вызовов API -- Используйте логику повторных попыток для временных сбоев -- Ведите журнал ошибок API для отладки -- Рассмотрите ограничение частоты запросов в клиентском коде diff --git a/docs/api2/ru/INTEGRATION_GUIDE.md b/docs/api2/ru/INTEGRATION_GUIDE.md deleted file mode 100644 index 2fdd4a0d..00000000 --- a/docs/api2/ru/INTEGRATION_GUIDE.md +++ /dev/null @@ -1,752 +0,0 @@ -# Руководство по интеграции API2 - -Полное руководство по интеграции вашего приложения с модулем ERP API2. - ---- - -## Содержание - -1. [Начало работы](#начало-работы) -2. [Настройка аутентификации](#настройка-аутентификации) -3. [Типовые сценарии интеграции](#типовые-сценарии-интеграции) -4. [Лучшие практики](#лучшие-практики) -5. [Тестирование интеграции](#тестирование-интеграции) -6. [Устранение неполадок](#устранение-неполадок) -7. [Развертывание в production](#развертывание-в-production) - ---- - -## Начало работы - -### Предварительные требования - -- Учетные данные для доступа к API (логин и пароль) -- HTTPS-совместимый клиент -- Возможность обработки JSON -- Базовый URL: `https://erp.bazacvetov24.ru/api2` - -### Чек-лист быстрого старта - -- [ ] Получить учетные данные API у системного администратора -- [ ] Проверить подключение к базовому URL -- [ ] Реализовать процесс аутентификации -- [ ] Протестировать базовые эндпоинты (баланс, информация о клиенте) -- [ ] Реализовать обработку ошибок -- [ ] Настроить логирование -- [ ] Развернуть в production - ---- - -## Настройка аутентификации - -### Шаг 1: Получение токена доступа - -```http -POST /api2/auth/login -Content-Type: application/json - -{ - "login": "ваш_логин", - "password": "ваш_пароль" -} -``` - -**Ответ**: -```json -{ - "access-token": "eyJ0eXAiOiJKV1QiLCJhbGc..." -} -``` - -### Шаг 2: Безопасное хранение токена - -**НЕ ДЕЛАЙТЕ**: -- Хранение в localStorage (уязвимость XSS) -- Хранение в cookies без флага HttpOnly -- Коммит в систему контроля версий -- Использование между пользователями - -**ДЕЛАЙТЕ**: -- Используйте безопасные cookie с флагами HttpOnly и Secure -- Храните в серверной сессии -- Шифруйте при хранении в базе данных -- Реализуйте механизм обновления токена - -### Шаг 3: Использование токена в запросах - -Включайте токен в каждый аутентифицированный запрос: - -**Вариант 1: Заголовок (рекомендуется)** -```http -POST /api2/client/balance -X-ACCESS-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGc... -Content-Type: application/json - -{ - "phone": "79001234567" -} -``` - -**Вариант 2: Параметр запроса** -```http -POST /api2/client/balance?key=eyJ0eXAiOiJKV1QiLCJhbGc... -Content-Type: application/json - -{ - "phone": "79001234567" -} -``` - ---- - -## Типовые сценарии интеграции - -### Сценарий 1: Интеграция оформления заказа в интернет-магазине - -**Ситуация**: Покупатель оформляет заказ с использованием бонусных баллов - -```mermaid -sequenceDiagram - Покупатель->>Сайт: Добавить товары в корзину - Сайт->>API: POST /client/balance {phone} - API-->>Сайт: {balance: 500} - Сайт->>Покупатель: Показать доступные бонусы - Покупатель->>Сайт: Использовать 100 баллов - Сайт->>API: POST /client/use-bonuses - API-->>Сайт: {success, remainingPoints: 400} - Сайт->>Платежная система: Обработать платеж - Платежная система-->>Сайт: Успешно - Сайт->>API: POST /client/add-bonus - API-->>Сайт: {totalPoints: 450} - Сайт->>Покупатель: Заказ подтвержден -``` - -**Реализация**: - -```javascript -async function checkoutWithBonuses(cartTotal, phone, bonusToUse) { - // 1. Проверить баланс бонусов - const balance = await api.request('/client/balance', { phone }); - - if (balance.balance < bonusToUse) { - throw new Error('Недостаточно бонусных баллов'); - } - - // 2. Рассчитать итоговую сумму - const discount = bonusToUse; // 1 балл = 1 рубль - const finalAmount = cartTotal - discount; - - // 3. Зарезервировать бонусы - const orderId = generateOrderId(); - await api.request('/client/use-bonuses', { - order_id: orderId, - phone: phone, - points_to_use: bonusToUse, - date: Math.floor(Date.now() / 1000), - price: finalAmount - }); - - try { - // 4. Обработать платеж - await processPayment(finalAmount); - - // 5. Начислить кешбэк - const cashback = Math.floor(finalAmount * 0.05); - await api.request('/client/add-bonus', { - order_id: orderId, - phone: phone, - points_to_add: cashback, - date: Math.floor(Date.now() / 1000), - price: finalAmount - }); - - return { success: true, orderId, cashback }; - - } catch (error) { - // Откат: вернуть бонусы - await api.request('/client/add-bonus', { - order_id: `${orderId}-rollback`, - phone: phone, - points_to_add: bonusToUse, - date: Math.floor(Date.now() / 1000), - price: 0 - }); - - throw error; - } -} -``` - -### Сценарий 2: Регистрация клиента - -**Ситуация**: Новый клиент регистрируется через мессенджер-бота - -```javascript -async function registerClient(messengerData) { - const phone = normalizePhone(messengerData.phone); - - // 1. Проверить существование клиента - let clientInfo; - try { - clientInfo = await api.request('/client/get-info', { phone }); - } catch (error) { - // Клиент не существует, создать нового - } - - if (!clientInfo || !clientInfo.response) { - // 2. Создать нового клиента - const result = await api.request('/client/add', { - phone: phone, - name: messengerData.name, - client_id: messengerData.messenger_id, - client_type: 1, - platform_id: messengerData.platform_id, - messenger: 'telegram', - date_of_creation: Math.floor(Date.now() / 1000) - }); - - if (!result.result) { - throw new Error('Не удалось создать клиента'); - } - - // 3. Приветственный бонус для новых клиентов - await api.request('/client/apply-promo-code', { - phone: phone, - code: 'WELCOME2024' - }); - } - - // 4. Получить обновленную информацию о клиенте - clientInfo = await api.request('/client/get-info', { phone }); - - return clientInfo.response; -} -``` - -### Сценарий 3: Синхронизация статусов заказов - -**Ситуация**: Синхронизация статусов заказов 1С с маркетплейсом - -```javascript -async function syncOrderStatuses(orders1C) { - const batchSize = 50; // Обработка по 50 заказов - const batches = chunkArray(orders1C, batchSize); - - for (const batch of batches) { - const orderUpdates = batch.map(order => ({ - order_id: order.guid, - status: order.status_code, - seller_id: order.seller_id - })); - - try { - const results = await api.request('/orders/change-status', { - order: orderUpdates - }); - - // Обработать результаты - for (const result of results) { - if (result.result === true) { - console.log(`Заказ ${result.order_id} успешно обновлен`); - } else { - console.error(`Заказ ${result.order_id} ошибка: ${result.message}`); - // Добавить в очередь повтора - queueForRetry(result.order_id); - } - } - - } catch (error) { - console.error('Ошибка пакетного обновления:', error); - // Повторить весь пакет - queueBatchForRetry(batch); - } - - // Ограничение скорости: пауза между пакетами - await sleep(1000); - } -} -``` - -### Сценарий 4: Опрос заказов с маркетплейса - -**Ситуация**: Периодическое получение новых заказов с Яндекс.Маркет - -```javascript -async function pollYandexMarketOrders() { - const fromDate = new Date(); - fromDate.setHours(0, 0, 0, 0); // Начало сегодняшнего дня - - const result = await api.request('/yandex-market/get-orders', { - from_date: formatDate(fromDate, 'd-m-Y'), - status: 'PROCESSING' - }); - - if (result.response === 'OK') { - console.log(`Обработано ${result.result.processed} заказов`); - console.log(`Создано: ${result.result.created}, Обновлено: ${result.result.updated}`); - - return result.result; - } else { - throw new Error('Не удалось получить заказы'); - } -} - -// Запуск каждые 5 минут -setInterval(pollYandexMarketOrders, 5 * 60 * 1000); -``` - ---- - -## Лучшие практики - -### 1. Обработка ошибок - -Всегда корректно обрабатывайте ошибки: - -```javascript -async function safeApiCall(endpoint, body) { - try { - const result = await api.request(endpoint, body); - - // Проверить ошибки на уровне API - if (result.error_id !== undefined) { - handleApiError(result); - return null; - } - - return result; - - } catch (error) { - // Сетевые или HTTP ошибки - console.error('Ошибка вызова API:', error); - - // Реализовать логику повторных попыток для временных ошибок - if (isRetryable(error)) { - return await retryWithBackoff(() => api.request(endpoint, body)); - } - - throw error; - } -} - -function handleApiError(result) { - switch (result.error_id) { - case 1: - case 1.2: - console.error('Неверные параметры:', result.error); - break; - case 2: - console.error('Ресурс не найден:', result.error); - break; - case 3: - console.error('Ошибка бизнес-логики:', result.error); - break; - default: - console.error('Неизвестная ошибка:', result.error); - } -} -``` - -### 2. Валидация номера телефона - -Всегда нормализуйте и проверяйте номера телефонов: - -```javascript -function normalizePhone(phone) { - // Удалить все нецифровые символы - phone = phone.replace(/\D/g, ''); - - // Добавить префикс 7 для российских номеров, если отсутствует - if (phone.length === 10) { - phone = '7' + phone; - } - - // Проверить формат - if (!/^7\d{10}$/.test(phone)) { - throw new Error('Неверный формат номера телефона'); - } - - return phone; -} -``` - -### 3. Идемпотентные операции - -Обеспечьте безопасность повторных попыток: - -```javascript -async function idempotentAddBonus(orderId, phone, points, price) { - // Проверить, не добавлен ли уже бонус для этого заказа - const existing = await api.request('/client/bonus-write-off', { phone }); - - const alreadyProcessed = existing.response.bonuses.some( - b => b.check_id === orderId && b.amount === points - ); - - if (alreadyProcessed) { - console.log('Бонус уже добавлен для этого заказа'); - return { alreadyProcessed: true }; - } - - // Безопасно добавить бонус - return await api.request('/client/add-bonus', { - order_id: orderId, - phone, - points_to_add: points, - date: Math.floor(Date.now() / 1000), - price - }); -} -``` - -### 4. Ограничение скорости запросов - -Реализуйте ограничение скорости на стороне клиента: - -```javascript -class RateLimiter { - constructor(maxRequests, perMilliseconds) { - this.maxRequests = maxRequests; - this.perMilliseconds = perMilliseconds; - this.requests = []; - } - - async throttle() { - const now = Date.now(); - - // Удалить старые запросы - this.requests = this.requests.filter( - time => now - time < this.perMilliseconds - ); - - if (this.requests.length >= this.maxRequests) { - const oldestRequest = this.requests[0]; - const waitTime = this.perMilliseconds - (now - oldestRequest); - await sleep(waitTime); - return this.throttle(); - } - - this.requests.push(now); - } -} - -const limiter = new RateLimiter(10, 1000); // 10 запросов в секунду - -async function makeThrottledRequest(endpoint, body) { - await limiter.throttle(); - return await api.request(endpoint, body); -} -``` - -### 5. Логирование и мониторинг - -Реализуйте полное логирование: - -```javascript -class ApiLogger { - static logRequest(endpoint, body) { - console.log(`[API] ${new Date().toISOString()} → ${endpoint}`, { - body: sanitizeLogData(body) - }); - } - - static logResponse(endpoint, response, duration) { - console.log(`[API] ${new Date().toISOString()} ← ${endpoint} (${duration}ms)`, { - success: !response.error_id, - error_id: response.error_id - }); - } - - static logError(endpoint, error) { - console.error(`[API] ${new Date().toISOString()} ✗ ${endpoint}`, { - error: error.message, - stack: error.stack - }); - } -} - -function sanitizeLogData(data) { - // Удалить конфиденциальную информацию из логов - const sanitized = { ...data }; - if (sanitized.password) sanitized.password = '***'; - if (sanitized.phone) sanitized.phone = sanitized.phone.replace(/\d{6}$/, '******'); - return sanitized; -} -``` - ---- - -## Тестирование интеграции - -### Модульное тестирование - -```javascript -// Mock API для тестирования -class MockAPI { - constructor() { - this.responses = new Map(); - } - - setResponse(endpoint, response) { - this.responses.set(endpoint, response); - } - - async request(endpoint, body) { - const response = this.responses.get(endpoint); - if (!response) { - throw new Error(`Нет mock-ответа для ${endpoint}`); - } - return typeof response === 'function' ? response(body) : response; - } -} - -// Тестовый случай -describe('Баланс клиента', () => { - let mockApi; - - beforeEach(() => { - mockApi = new MockAPI(); - }); - - test('должен получить баланс клиента', async () => { - mockApi.setResponse('/client/balance', { - balance: 500, - keycode: '1234' - }); - - const result = await mockApi.request('/client/balance', { - phone: '79001234567' - }); - - expect(result.balance).toBe(500); - expect(result.keycode).toBe('1234'); - }); - - test('должен обработать ошибку отсутствия телефона', async () => { - mockApi.setResponse('/client/balance', { - error_id: 1, - error: 'phone обязателен' - }); - - const result = await mockApi.request('/client/balance', {}); - - expect(result.error_id).toBe(1); - }); -}); -``` - -### Интеграционное тестирование - -```javascript -// Тестирование в staging-окружении -describe('Интеграционные тесты', () => { - let api; - const testPhone = '79999999999'; - - beforeAll(async () => { - api = new FlowerShopAPI('https://staging.erp.bazacvetov24.ru/api2'); - await api.login(process.env.TEST_USERNAME, process.env.TEST_PASSWORD); - }); - - test('полный процесс заказа', async () => { - // 1. Получить начальный баланс - const initialBalance = await api.getBalance(testPhone); - - // 2. Обработать заказ с бонусами - const orderResult = await api.processOrder(testPhone, 1000, 50); - - // 3. Проверить финальный баланс - const finalBalance = await api.getBalance(testPhone); - - expect(finalBalance.balance).toBe( - initialBalance.balance - 50 + orderResult.cashback_earned - ); - }); -}); -``` - ---- - -## Устранение неполадок - -### Распространенные проблемы - -#### Проблема 1: "Wrong login or password" - -**Причина**: Неверные учетные данные или истекший токен - -**Решение**: -```javascript -// Повторная аутентификация -try { - await api.login(username, password); -} catch (error) { - // Проверить учетные данные - console.error('Ошибка аутентификации:', error); -} -``` - -#### Проблема 2: "phone is required" - -**Причина**: Номер телефона не предоставлен или неверный формат - -**Решение**: -```javascript -// Всегда проверяйте телефон перед отправкой -const phone = normalizePhone(userInput); -if (!/^7\d{10}$/.test(phone)) { - throw new Error('Неверный номер телефона'); -} -``` - -#### Проблема 3: "Json body invalid" - -**Причина**: Некорректный JSON или неправильный Content-Type - -**Решение**: -```javascript -// Убедитесь в правильных заголовках -headers: { - 'Content-Type': 'application/json' -}, -body: JSON.stringify(data) // Не просто 'data' -``` - -#### Проблема 4: Ошибки таймаута - -**Причина**: Проблемы с сетью или длительные операции - -**Решение**: -```javascript -// Реализовать таймаут -const timeout = (ms) => new Promise((_, reject) => - setTimeout(() => reject(new Error('Таймаут')), ms) -); - -const result = await Promise.race([ - api.request(endpoint, body), - timeout(30000) // таймаут 30 секунд -]); -``` - -### Режим отладки - -Включить подробное логирование: - -```javascript -const DEBUG = process.env.NODE_ENV === 'development'; - -async function debugRequest(endpoint, body) { - if (DEBUG) { - console.log('→ Запрос:', endpoint, JSON.stringify(body, null, 2)); - } - - const start = Date.now(); - const result = await api.request(endpoint, body); - const duration = Date.now() - start; - - if (DEBUG) { - console.log(`← Ответ (${duration}ms):`, JSON.stringify(result, null, 2)); - } - - return result; -} -``` - ---- - -## Развертывание в production - -### Чек-лист - -- [ ] **Безопасность** - - [ ] Использовать только HTTPS - - [ ] Хранить учетные данные в переменных окружения - - [ ] Реализовать механизм обновления токена - - [ ] Добавить ограничение скорости запросов - - [ ] Включить подпись запросов (если доступно) - -- [ ] **Надежность** - - [ ] Реализовать логику повторных попыток с экспоненциальной задержкой - - [ ] Добавить паттерн circuit breaker - - [ ] Настроить проверки работоспособности - - [ ] Мониторить время отклика API - -- [ ] **Производительность** - - [ ] Кешировать часто запрашиваемые данные - - [ ] Группировать запросы где возможно - - [ ] Использовать пул соединений - - [ ] Оптимизировать размер payload - -- [ ] **Мониторинг** - - [ ] Логировать все вызовы API - - [ ] Отслеживать уровень ошибок - - [ ] Настроить оповещения о сбоях - - [ ] Контролировать квоты API (если применимо) - -- [ ] **Документация** - - [ ] Документировать точки интеграции - - [ ] Поддерживать совместимость версий API - - [ ] Обновлять руководства по типичным проблемам - -### Конфигурация окружения - -```javascript -// config/production.js -module.exports = { - api: { - baseUrl: process.env.API_BASE_URL, - username: process.env.API_USERNAME, - password: process.env.API_PASSWORD, - timeout: 30000, - retries: 3, - rateLimit: { - maxRequests: 100, - perMinutes: 1 - } - } -}; - -// .env.production -API_BASE_URL=https://erp.bazacvetov24.ru/api2 -API_USERNAME=prod_user -API_PASSWORD=*** -``` - -### Эндпоинт проверки работоспособности - -```javascript -app.get('/health/api', async (req, res) => { - try { - const result = await api.request('/balance/test', {}); - res.json({ - status: 'healthy', - api: 'connected', - timestamp: new Date().toISOString() - }); - } catch (error) { - res.status(503).json({ - status: 'unhealthy', - api: 'disconnected', - error: error.message, - timestamp: new Date().toISOString() - }); - } -}); -``` - ---- - -## Поддержка - -Для поддержки интеграции: -- Технические вопросы: Обратитесь к администратору API -- Вопросы бизнес-логики: См. документацию эндпоинтов -- Сообщения об ошибках: Включайте логи запросов/ответов и детали ошибки - ---- - -## История версий - -- **v2.0** (Текущая): RESTful API с токен-аутентификацией -- См. changelog для подробной информации о версиях diff --git a/docs/api2/ru/MODULE_STRUCTURE.md b/docs/api2/ru/MODULE_STRUCTURE.md deleted file mode 100644 index dc31b936..00000000 --- a/docs/api2/ru/MODULE_STRUCTURE.md +++ /dev/null @@ -1,487 +0,0 @@ -# Структура модуля API2 - -[English](../MODULE_STRUCTURE.md) | **Русский** - -## Организация каталогов - -``` -erp24/api2/ -├── config/ # Файлы конфигурации -│ ├── api2.config.php # Основная конфигурация приложения -│ ├── dev.api2.config.php # Конфигурация для разработки -│ └── env.php # Переменные окружения -├── controllers/ # Контроллеры API endpoints -│ ├── BaseController.php # Базовый контроллер с авторизацией/CORS -│ ├── AuthController.php # Аутентификация -│ ├── BalanceController.php # Операции с балансом -│ ├── BonusController.php # Управление бонусами -│ ├── ChatbotActionController.php # Действия чат-бота -│ ├── ClientController.php # Управление клиентами -│ ├── DataBuhController.php # Данные бухгалтерии -│ ├── DataController.php # Общие операции с данными -│ ├── DataTestController.php # Тестовые endpoints -│ ├── DeliveryController.php # Отслеживание доставки -│ ├── EmployeeController.php # Операции с сотрудниками -│ ├── KikController.php # Интеграция с КИК -│ ├── MarketplaceController.php # Операции с маркетплейсами -│ ├── OrdersController.php # Управление заказами -│ ├── SiteController.php # Операции сайта -│ ├── StoreController.php # Управление складом -│ ├── TaskController.php # RESTful API задач -│ ├── TelegramController.php # Telegram бот -│ ├── TelegramSalebotController.php # Бот продаж -│ ├── UniversalCatalogController.php # Каталог товаров -│ └── YandexMarketController.php # Интеграция с Яндекс.Маркетом -├── records/ # Модели Active Record -│ ├── ApiUser.php # Модель пользователя API -│ └── Task.php # Модель задачи -├── amo_data/ # Данные интеграции AmoCRM -│ └── token_info.json # OAuth токены -├── json/ # Логи запросов/ответов -│ ├── request_*.json # Логи API запросов -│ ├── changed_orders_*.json # Отслеживание изменений заказов -│ ├── upload_request_*.json # Логи загрузки -│ └── error logs # Отслеживание ошибок -├── runtime/ # Временные файлы выполнения -├── swagger/ # Документация API -├── .htaccess # Конфигурация Apache -├── .gitignore # Правила игнорирования Git -└── index.php # Точка входа приложения -``` - -## Количество файлов и строк кода - -### Контроллеры (24 файла) -- **BaseController.php** (58 строк) - Основа для всех API контроллеров -- **AuthController.php** (26 строк) - Endpoint аутентификации -- **MarketplaceController.php** (81 строка) - Операции с маркетплейсами -- **YandexMarketController.php** (223 строки) - Интеграция с Яндекс.Маркетом - -### Модели (2 файла) -- **ApiUser.php** (100 строк) - Аутентификация пользователей -- **Task.php** - Управление задачами - -### Конфигурация (3 файла) -- **api2.config.php** (108 строк) - Основная конфигурация -- **dev.api2.config.php** - Переопределение для разработки -- **env.php** - Настройки окружения - -### Точка входа (1 файл) -- **index.php** (18 строк) - Загрузка приложения - -## Ответственность модулей по каталогам - -### `/config` - Конфигурация приложения - -**Назначение**: Централизованное управление конфигурацией - -**Файлы**: -1. **api2.config.php** - - Конфигурация компонентов - - Правила маршрутизации URL - - Подключение к базе данных - - Настройки очередей - - CORS и аутентификация - -2. **env.php** - - Переменные окружения - - API ключи - - Endpoints сервисов - -3. **dev.api2.config.php** - - Переопределения для разработки - - Настройки отладки - -**Ключевые настройки**: -- Язык: Русский -- Формат ответа: JSON -- Аутентификация: На основе токенов -- Очередь: Интеграция с RabbitMQ -- Кэш: Файловый - -### `/controllers` - API Endpoints - -**Назначение**: Обработка HTTP запросов и бизнес-логики - -**Иерархия контроллеров**: -``` -yii\rest\Controller - ↓ -BaseController (CORS + Auth) - ↓ -├── AuthController -├── BalanceController -├── BonusController -├── ChatbotActionController -├── ClientController -├── DataBuhController -├── DataController -├── DataTestController -├── DeliveryController -├── EmployeeController -├── KikController -├── MarketplaceController -├── OrdersController -├── SiteController -├── StoreController -├── TaskController -├── TelegramController -├── TelegramSalebotController -├── UniversalCatalogController -└── YandexMarketController -``` - -#### Категории контроллеров - -**1. Системные контроллеры** - -| Контроллер | Назначение | Основные действия | -|-----------|---------|-------------| -| `BaseController` | Базовая функциональность | behaviors(), CORS, auth | -| `AuthController` | Аутентификация | actionLogin() | -| `SiteController` | Общие операции сайта | Различные | - -**2. Бизнес-контроллеры** - -| Контроллер | Домен | Ответственность | -|-----------|--------|----------------| -| `BalanceController` | Финансы | Операции с балансом | -| `BonusController` | Финансы | Управление бонусами | -| `ClientController` | CRM | Данные клиентов | -| `EmployeeController` | HR | Операции с сотрудниками | -| `OrdersController` | Заказы | Управление заказами | -| `DeliveryController` | Логистика | Отслеживание доставки | -| `StoreController` | Склад | Операции со складом | - -**3. Интеграционные контроллеры** - -| Контроллер | Внешняя система | Тип интеграции | -|-----------|----------------|------------------| -| `MarketplaceController` | Маркетплейсы | Общий API маркетплейсов | -| `YandexMarketController` | Яндекс.Маркет | Специфическая интеграция | -| `TelegramController` | Telegram | Bot API | -| `TelegramSalebotController` | Telegram | Бот продаж | -| `ChatbotActionController` | Чат-боты | Обработка действий | -| `KikController` | Система КИК | Обмен данными | - -**4. Контроллеры данных** - -| Контроллер | Назначение | Тип данных | -|-----------|---------|-----------| -| `DataController` | Общие данные | Различные сущности | -| `DataBuhController` | Бухгалтерия | Финансовые данные | -| `DataTestController` | Тестирование | Тестовые endpoints | -| `UniversalCatalogController` | Каталог | Данные товаров | - -**5. RESTful контроллеры** - -| Контроллер | REST ресурс | Стандартные действия | -|-----------|---------------|------------------| -| `TaskController` | Task | index, view, create, update, delete | - -### `/records` - Модели данных - -**Назначение**: Взаимодействие с базой данных и бизнес-логика - -**Модели**: - -1. **ApiUser.php** (100 строк) - - Таблица: `api_user` - - Поля: `id`, `login`, `password`, `access_token` - - Реализует: `IdentityInterface` - - Методы: - - `findByLogin($login)` - Поиск пользователя по логину - - `validatePassword($password)` - Проверка учетных данных - - `generateAccessToken()` - Создание нового токена - - `findIdentityByAccessToken($token)` - Поиск для аутентификации - -2. **Task.php** - - Таблица: `task` (предполагается) - - Модель RESTful ресурса - -**Пространство имен**: `app\records` - -**Родительский класс**: `yii\db\ActiveRecord` - -### `/amo_data` - Хранилище данных AmoCRM - -**Назначение**: Хранение данных интеграции с AmoCRM - -**Файлы**: -- `token_info.json` - OAuth access/refresh токены - -**Структура**: -```json -{ - "access_token": "...", - "refresh_token": "...", - "expires_in": 86400, - "created_at": 1234567890 -} -``` - -### `/json` - Логирование запросов/ответов - -**Назначение**: Отладка и аудит - -**Типы файлов**: - -1. **Логи запросов**: `request_[timestamp].json` - - Входящие API запросы - - Тело запроса - - Временная метка - -2. **Измененные заказы**: `changed_orders__[datetime]_.json` - - Отслеживание изменений заказов - - Обновления с маркетплейсов - -3. **Запросы загрузки**: `upload_request_id_[timestamp].json` - - Операции загрузки файлов - - Метаданные загрузки - -4. **Логи ошибок**: - - `request_error.txt` - - `log_error.txt` - - `log_created_write_offs_erp_error.txt` - -**Хранение логов**: Файлы накапливаются со временем (требуется ручная очистка) - -### `/runtime` - Временные файлы - -**Назначение**: Кэш, сессии, логи - -**Создается**: Фреймворком Yii2 - -**Содержимое**: -- Скомпилированные шаблоны -- Файлы кэша -- Данные сессий (если включено) -- Логи отладки - -**Статус в Git**: Игнорируется (`.gitignore`) - -### `/swagger` - Документация API - -**Назначение**: Спецификация OpenAPI/Swagger - -**Формат**: YAML или JSON - -**Использование**: Генерация документации API - -## Соглашения об именовании методов контроллеров - -### Методы действий - -**Шаблон**: `action[ActionName]()` - -**Примеры**: -```php -// AuthController -public function actionLogin() - -// MarketplaceController -public function actionStatuses() -public function actionGetNewOrderCount() -public function actionInstructionDictionary() - -// YandexMarketController -public function actionCreateCards($do = null) -public function actionGetOrders() -``` - -### Маппинг URL - -**Формат**: `/controller/action` - -**Примеры**: -- `/auth/login` → `AuthController::actionLogin()` -- `/marketplace/statuses` → `MarketplaceController::actionStatuses()` -- `/yandex-market/get-orders` → `YandexMarketController::actionGetOrders()` - -## Организация пространств имен - -### Пространство имен приложения: `app` - -**Контроллеры**: `app\controllers` -```php -namespace app\controllers; -class AuthController extends BaseController { } -``` - -**Records (Модели)**: `app\records` -```php -namespace app\records; -class ApiUser extends \yii\db\ActiveRecord { } -``` - -### Основное пространство имен ERP: `yii_app` - -**Используется для общих моделей**: -```php -use yii_app\records\MarketplaceOrders; -use yii_app\records\MarketplaceStatus; -use yii_app\records\ExportImportTable; -``` - -**Точка интеграции**: API2 использует модели из основного приложения - -## Иерархия конфигурации - -``` -index.php - ↓ -config/env.php (переменные окружения) - ↓ -config/api2.config.php (основная конфигурация) - ↓ -config/../../config/db.php (общая база данных) - ↓ -config/../../config/params.php (общие параметры) -``` - -## Файлы процесса аутентификации - -``` -Запрос клиента - ↓ -index.php → Загрузка приложения - ↓ -BaseController → CORS + Auth Behaviors - ↓ -AuthController::actionLogin() - ↓ -records/ApiUser::findByLogin() - ↓ -records/ApiUser::validatePassword() - ↓ -records/ApiUser::generateAccessToken() - ↓ -Ответ с токеном -``` - -## Паттерны потока данных - -### 1. RESTful паттерн (TaskController) - -``` -HTTP запрос → TaskController - ↓ -Task Model (ActiveRecord) - ↓ -Запрос к базе данных - ↓ -JSON ответ -``` - -### 2. Паттерн сервиса (YandexMarketController) - -``` -HTTP запрос → YandexMarketController - ↓ -MarketplaceService (yii_app\services) - ↓ -Множество моделей + Внешний API - ↓ -JSON ответ -``` - -### 3. Прямой паттерн (MarketplaceController) - -``` -HTTP запрос → MarketplaceController - ↓ -Прямой запрос к модели (MarketplaceStatus) - ↓ -JSON ответ -``` - -## Распределение по размеру файлов - -### Малые контроллеры (< 100 строк) -- AuthController (26 строк) -- BaseController (58 строк) -- MarketplaceController (81 строка) - -### Средние контроллеры (100-300 строк) -- YandexMarketController (223 строки) - -### Большие контроллеры (> 300 строк) -- Контроллеры со сложной бизнес-логикой -- Интеграции с маркетплейсами -- Endpoints синхронизации данных - -## Лучшие практики организации кода - -### Текущие сильные стороны -1. **Разделение обязанностей**: Контроллеры, модели, конфигурация -2. **Наследование**: BaseController для общей функциональности -3. **Пространства имен**: Четкое разделение пространств имен -4. **RESTful дизайн**: Стандартные REST паттерны -5. **Управление конфигурацией**: Централизованная конфигурация - -### Области для улучшения -1. **Слой сервисов**: Добавить сервисы бизнес-логики -2. **Валидация**: Централизовать правила валидации -3. **Обработка ошибок**: Единообразные ответы об ошибках -4. **Тестирование**: Добавить структуру каталога тестов -5. **Документация**: Встроенная документация кода - -## Зависимости между файлами - -### Прямые зависимости - -**BaseController.php** зависит от: -- `yii\rest\Controller` -- `yii\filters\Cors` -- `yii\filters\auth\*` - -**Все контроллеры** зависят от: -- `BaseController.php` -- Различных моделей из `yii_app\records` -- Компонентов фреймворка Yii2 - -**Модели** зависят от: -- `yii\db\ActiveRecord` -- Конфигурации базы данных - -**Конфигурация** зависит от: -- Файлов конфигурации родительского приложения -- Переменных окружения - -## Последовательность инициализации модуля - -``` -1. index.php -2. Загрузка автозагрузчика (Composer) -3. Загрузка фреймворка Yii2 -4. Загрузка config/env.php -5. Загрузка config/api2.config.php -6. Установка псевдонимов -7. Создание экземпляра приложения -8. Загрузка компонентов (log, queue) -9. Маршрутизация запроса к контроллеру -10. Выполнение действия -11. Возврат JSON ответа -``` - -## Сводная статистика - -- **Всего контроллеров**: 24 -- **Всего моделей**: 2 (api2) + общие из основного приложения -- **Всего файлов конфигурации**: 3 -- **Точек входа**: 1 -- **Вспомогательных каталогов**: 4 (amo_data, json, runtime, swagger) -- **Основной язык**: PHP -- **Фреймворк**: Yii2 -- **Архитектура**: MVC + RESTful - -## Рекомендуемые улучшения структуры модуля - -1. **Добавить каталог `/services`** для бизнес-логики -2. **Добавить каталог `/tests`** для unit/интеграционных тестов -3. **Добавить каталог `/migrations`** для изменений базы данных -4. **Добавить каталог `/validators`** для пользовательской валидации -5. **Добавить каталог `/helpers`** для утилитарных функций -6. **Добавить каталог `/middleware`** для пользовательского middleware -7. **Добавить каталог `/exceptions`** для пользовательских исключений -8. **Добавить каталог `/repositories`** для слоя доступа к данным diff --git a/docs/api2/ru/README.md b/docs/api2/ru/README.md deleted file mode 100644 index 78be5d53..00000000 --- a/docs/api2/ru/README.md +++ /dev/null @@ -1,224 +0,0 @@ -> 📖 **Язык**: Русский | [English](../README.md) - -# Документация API2 - -Полная документация для модуля ERP API2 - RESTful API системы для интеграции с маркетплейсами, управления клиентами и обработки заказов. - ---- - -## 📚 Указатель документации - -### 1. [Справочник API](./API_REFERENCE.md) -**Обзор и основные концепции** -- Методы аутентификации -- Настройка CORS -- Обработка ошибок -- Форматы данных -- Версионирование API - -### 2. [Каталог эндпоинтов](./ENDPOINTS.md) -**Полный справочник эндпоинтов** -- 33 эндпоинта в 6 контроллерах -- Форматы запросов/ответов -- Спецификация параметров -- Коды ошибок -- Примечания по использованию - -### 3. [Примеры кода](./EXAMPLES.md) -**Практические примеры реализации** -- Процессы аутентификации -- Управление клиентами -- Обработка заказов -- Интеграция бонусной системы -- Паттерны обработки ошибок -- Примеры на разных языках (JavaScript, PHP, Python) - -### 4. [Руководство по интеграции](./INTEGRATION_GUIDE.md) -**Полное пошаговое руководство по интеграции** -- Начало работы -- Настройка аутентификации -- Общие паттерны -- Лучшие практики -- Стратегии тестирования -- Развертывание в продакшене -- Устранение неполадок - ---- - -## 🚀 Быстрый старт - -### 1. Аутентификация -```bash -curl -X POST https://erp.bazacvetov24.ru/api2/auth/login \ - -H "Content-Type: application/json" \ - -d '{"login":"username","password":"password"}' -``` - -### 2. Выполнение аутентифицированного запроса -```bash -curl -X POST https://erp.bazacvetov24.ru/api2/client/balance \ - -H "Content-Type: application/json" \ - -H "X-ACCESS-TOKEN: your-token" \ - -d '{"phone":"79001234567"}' -``` - ---- - -## 📊 Статистика API - -- **Всего эндпоинтов**: 33 -- **Контроллеры**: 6 - - AuthController (1 эндпоинт) - - BalanceController (2 эндпоинта) - - ClientController (21 эндпоинт) - - OrdersController (2 эндпоинта) - - MarketplaceController (3 эндпоинта) - - YandexMarketController (2 эндпоинта) - - DeliveryController (2 эндпоинта) - -- **Аутентификация**: На основе токенов (31/33 эндпоинтов требуют аутентификации) -- **Формат**: JSON -- **Протокол**: HTTPS - ---- - -## 🎯 Распространенные сценарии использования - -### Управление клиентами -- Регистрация новых клиентов → `/client/add` -- Проверка бонусного баланса → `/client/balance` -- Получение истории покупок → `/client/check-details` -- Управление программой лояльности → `/client/bonus-status` - -### Обработка заказов -- Обновление статуса заказа → `/orders/change-status` -- Получение заказов магазина → `/orders/get-orders` -- Отслеживание интеграции с маркетплейсами → `/marketplace/*` - -### Бонусная система -- Использование бонусных баллов → `/client/use-bonuses` -- Начисление кэшбэка → `/client/add-bonus` -- Применение промокодов → `/client/apply-promo-code` - -### Интеграция с маркетплейсами -- Синхронизация с Яндекс.Маркет → `/yandex-market/*` -- Получение количества заказов → `/marketplace/get-new-order-count` -- Процессы работы со статусами → `/marketplace/instruction-dictionary` - ---- - -## 🔧 Технические детали - -**Базовый URL**: `https://erp.bazacvetov24.ru/api2` - -**Методы аутентификации**: -- Заголовок: `X-ACCESS-TOKEN: token` -- Параметр запроса: `?key=token` - -**Формат ответа**: JSON - -**CORS**: Полная поддержка (все источники, методы, заголовки) - -**Логирование**: Комплексное (запросы, ошибки, операции) - ---- - -## 📖 Структура документации - -``` -docs/api2/ -├── README.md # Этот файл - указатель документации -├── API_REFERENCE.md # Основные концепции и обзор -├── ENDPOINTS.md # Полный каталог эндпоинтов -├── EXAMPLES.md # Примеры кода (JS, PHP, Python) -└── INTEGRATION_GUIDE.md # Пошаговое руководство по интеграции -``` - ---- - -## 🛠️ Чек-лист интеграции - -- [ ] Получить учетные данные API -- [ ] Реализовать аутентификацию -- [ ] Проверить подключение -- [ ] Реализовать обработку ошибок -- [ ] Настроить логирование -- [ ] Протестировать в тестовой среде -- [ ] Развернуть в продакшене -- [ ] Мониторить состояние API - ---- - -## 📝 Примеры по языкам программирования - -### JavaScript/Node.js -См. [EXAMPLES.md](./EXAMPLES.md#javascriptnodejs-fetch) для: -- Интеграция с Fetch API -- Паттерны async/await -- Обработка ошибок - -### PHP -См. [EXAMPLES.md](./EXAMPLES.md#php-curl) для: -- Реализация cURL -- Обработка ошибок -- Лучшие практики - -### Python -См. [EXAMPLES.md](./EXAMPLES.md#python-requests) для: -- Использование библиотеки Requests -- Логика повторных попыток -- Обработка данных - ---- - -## 🔍 Ключевые возможности - -### Безопасность -- Аутентификация на основе токенов -- Только HTTPS -- Поддержка CORS -- Скрытие API ключей в логах - -### Надежность -- Комплексная обработка ошибок -- Логирование транзакций -- Идемпотентные операции -- Эндпоинты, безопасные для повторных попыток - -### Интеграция -- RESTful дизайн -- Формат JSON -- Примеры на разных языках -- Подробная документация - ---- - -## 📞 Поддержка - -Для технической поддержки: -- Просмотрите раздел [Устранение неполадок](./INTEGRATION_GUIDE.md#troubleshooting) -- Проверьте [Распространенные проблемы](./INTEGRATION_GUIDE.md#common-issues) -- Свяжитесь с администратором API - ---- - -## 📜 Информация о версии - -**Текущая версия**: API v2 - -**Фреймворк**: Yii2 - -**Последнее обновление**: 2024-11-13 - ---- - -## 🎓 Путь обучения - -1. **Начинающий**: Начните с [Справочника API](./API_REFERENCE.md) -2. **Средний уровень**: Изучите [Примеры кода](./EXAMPLES.md) -3. **Продвинутый**: Следуйте [Руководству по интеграции](./INTEGRATION_GUIDE.md) -4. **Справочник**: Используйте [Каталог эндпоинтов](./ENDPOINTS.md) - ---- - -*Сгенерировано с комплексным анализом контроллеров и эндпоинтов модуля API2* diff --git a/docs/database/schema-overview.md b/docs/database/schema-overview.md deleted file mode 100644 index 988084a2..00000000 --- a/docs/database/schema-overview.md +++ /dev/null @@ -1,873 +0,0 @@ -# Database Schema Overview - -> **Comprehensive database documentation for ERP24 PostgreSQL schema** - -## Table of Contents - -- [Overview](#overview) -- [Database Statistics](#database-statistics) -- [Entity Relationship Diagram](#entity-relationship-diagram) -- [Core Entities](#core-entities) -- [Business Domain Tables](#business-domain-tables) -- [System Tables](#system-tables) -- [Data Flow](#data-flow) -- [Naming Conventions](#naming-conventions) -- [Migration History](#migration-history) - ---- - -## Overview - -ERP24 uses a **PostgreSQL** database with a comprehensive schema supporting flower retail operations including employee management, customer loyalty programs, sales tracking, inventory management, and financial operations. - -### Database Information - -| Property | Value | -|----------|-------| -| **DBMS** | PostgreSQL 12+ | -| **Schema** | public, erp24 | -| **Total Tables** | 389+ (via ActiveRecord models) | -| **Migrations** | 278 migration files | -| **Primary Keys** | Integer (auto-increment) and UUID (GUID) | -| **Foreign Keys** | Soft references via application layer | -| **Indexing** | Strategic indexes on high-traffic columns | - ---- - -## Database Statistics - -### Model Count by Domain - -| Domain | Model Count | Purpose | -|--------|-------------|---------| -| **Admin & HR** | 80+ | Employee management, payroll, timetable | -| **Sales & Orders** | 60+ | Sales, checks, products, marketplace orders | -| **Customer Management** | 40+ | Users, bonus program, events | -| **Inventory** | 35+ | Products, stores, write-offs | -| **Task Management** | 25+ | Tasks, templates, types | -| **Analytics** | 20+ | Dashboard, reports, metrics | -| **Lessons & Training** | 15+ | Employee education system | -| **System** | 30+ | Auth, logs, configurations | -| **Marketplace** | 15+ | Flowwow, Yandex Market integration | -| **Regulations** | 10+ | Company policies, polls | - -### Table Size Estimates - -| Table | Est. Rows | Growth Rate | Notes | -|-------|-----------|-------------|-------| -| **sales** | 500K+ | High (daily) | Main sales records | -| **users** | 100K+ | Medium | Customer database | -| **users_bonus** | 1M+ | High (daily) | Bonus transactions | -| **timetable** | 200K+ | Medium | Employee schedules | -| **admin_payroll_days** | 150K+ | Medium | Daily payroll records | -| **sales_products** | 2M+ | High (daily) | Sale line items | -| **task** | 50K+ | Medium | Task management | -| **logs (various)** | 10M+ | Very High | System logs | - ---- - -## Entity Relationship Diagram - -### Core Entity Relationships - -```mermaid -erDiagram - ADMIN ||--o{ SALES : creates - ADMIN ||--o{ TIMETABLE : has_schedule - ADMIN ||--o{ ADMIN_PAYROLL : receives - ADMIN ||--o{ TASK : assigned_to - ADMIN }o--|| ADMIN_GROUP : belongs_to - ADMIN }o--|| CITY_STORE : works_at - - USERS ||--o{ SALES : purchases - USERS ||--o{ USERS_BONUS : has_bonuses - USERS ||--o{ USERS_EVENTS : has_events - - SALES ||--o{ SALES_PRODUCTS : contains - SALES ||--o{ USERS_BONUS : generates_bonus - SALES }o--|| CITY_STORE : sold_at - SALES }o--|| ADMIN : sold_by - - CITY_STORE ||--o{ ADMIN : employs - CITY_STORE ||--o{ SALES : records - CITY_STORE ||--o{ TIMETABLE : schedules - CITY_STORE }o--|| CITY : located_in - - ADMIN_PAYROLL ||--|| ADMIN : for_employee - ADMIN_PAYROLL ||--o{ ADMIN_PAYROLL_DAYS : has_daily_records - - TIMETABLE }o--|| ADMIN : for_employee - TIMETABLE }o--|| CITY_STORE : at_store - TIMETABLE }o--|| SHIFT : in_shift - - TASK }o--|| ADMIN : assigned_to - TASK }o--|| ADMIN : created_by - TASK }o--|| TASK_TYPE : has_type - TASK }o--|| TASK_STATUS : has_status - - MARKETPLACE_ORDERS ||--o{ MARKETPLACE_ORDER_ITEMS : contains - MARKETPLACE_ORDERS }o--|| CITY_STORE : for_store - MARKETPLACE_ORDERS }o--|| MARKETPLACE_STATUS : has_status -``` - ---- - -## Core Entities - -### 1. Admin (Employees) - -**Table**: `admin` -**Primary Key**: `id` (integer) -**File**: `erp24/records/Admin.php` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | INTEGER | Primary key | -| `guid` | VARCHAR(36) | 1C UUID identifier | -| `name` | VARCHAR(55) | Short name | -| `name_full` | VARCHAR(200) | Full name | -| `group_id` | INTEGER | FK to admin_group | -| `mobile` | VARCHAR(25) | Phone number (unique) | -| `store_id` | INTEGER | Default store | -| `store_arr` | TEXT | Array of accessible stores | -| `login_user` | VARCHAR(29) | Login (unique) | -| `pass_user` | VARCHAR(120) | Password hash | -| `work_status` | INTEGER | 1=active, 4=fired | -| `work_rate` | INTEGER | 1=5/2, 2=2/2, 3=3/3 | -| `birthdate` | DATE | Date of birth | -| `lasttime` | TIMESTAMP | Last login | -| `access_token` | VARCHAR(512) | API auth token | - -#### Relationships - -```php -// Admin.php relationships -public function getAdminGroup() { - return $this->hasOne(AdminGroup::class, ['id' => 'group_id']); -} - -public function getStore() { - return $this->hasOne(CityStore::class, ['id' => 'store_id']); -} - -public function getTimetables() { - return $this->hasMany(Timetable::class, ['admin_id' => 'id']); -} - -public function getPayrolls() { - return $this->hasMany(AdminPayroll::class, ['admin_id' => 'id']); -} - -public function getCreatedTasks() { - return $this->hasMany(Task::class, ['created_by' => 'id']); -} - -public function getAssignedTasks() { - return $this->hasMany(Task::class, ['updated_by' => 'id']); -} -``` - -#### Indexes - -- PRIMARY KEY on `id` -- UNIQUE on `mobile` -- UNIQUE on `login_user` -- UNIQUE on `guid` -- INDEX on `group_id` -- INDEX on `work_status` - ---- - -### 2. Users (Customers) - -**Table**: `users` -**Primary Key**: `id` (integer) -**File**: `erp24/records/Users.php` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | INTEGER | Primary key | -| `phone` | VARCHAR(13) | Phone number (key) | -| `name` | VARCHAR | Full name | -| `card` | VARCHAR | Loyalty card number | -| `password` | VARCHAR | Password hash | -| `keycode` | VARCHAR | 4-digit verification code | -| `pol` | VARCHAR | Gender (man/women) | -| `bdate` | DATE | Birth date | -| `email` | VARCHAR | Email address | -| `balans` | DECIMAL | Current bonus balance (deprecated) | -| `burn_balans` | DECIMAL | Soon-to-expire bonus | -| `bonus_minus` | DECIMAL | Total spent bonuses | -| `bonus_level` | VARCHAR | Tier (silver/gold/platinum) | -| `sale_cnt` | INTEGER | Total purchase count | -| `sale_price` | INTEGER | Lifetime value (LTV) | -| `sale_avg_price` | INTEGER | Average check | -| `date_last_sale` | TIMESTAMP | Last purchase date | -| `date_first_sale` | TIMESTAMP | First purchase date | -| `ref_code` | VARCHAR | Referral code | -| `referral_id` | INTEGER | Referred by user ID | -| `source` | INTEGER | 0=1C, 1=1C→TG, 2=TG | -| `telegram_is_subscribed` | INTEGER | 0=no, 1=yes | -| `telegram_created_at` | TIMESTAMP | TG registration date | -| `black_list` | INTEGER | Blocked flag | - -#### Relationships - -```php -// Users.php relationships -public function getBonuses() { - return $this->hasMany(UsersBonus::class, ['phone' => 'phone']); -} - -public function getEvents() { - return $this->hasMany(UsersEvents::class, ['phone' => 'phone']); -} - -public function getSales() { - return $this->hasMany(Sales::class, ['phone' => 'phone']); -} - -public function getReferrer() { - return $this->hasOne(Users::class, ['id' => 'referral_id']); -} - -public function getReferrals() { - return $this->hasMany(Users::class, ['referral_id' => 'id']); -} -``` - -#### Indexes - -- PRIMARY KEY on `id` -- INDEX on `phone` -- INDEX on `card` -- INDEX on `bonus_level` -- INDEX on `telegram_is_subscribed` - ---- - -### 3. Sales (Check/Receipt) - -**Table**: `sales` -**Primary Key**: `id` (UUID/GUID) -**File**: `erp24/records/Sales.php` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | VARCHAR(36) | 1C check GUID (PK) | -| `date` | TIMESTAMP | Check date/time | -| `operation` | VARCHAR(35) | "Продажа" or "Возврат" | -| `status` | VARCHAR(45) | Check status | -| `summ` | DECIMAL | Total amount | -| `skidka` | DECIMAL | Discount amount | -| `number` | VARCHAR(225) | Check number | -| `admin_id` | INTEGER | FK to admin | -| `seller_id` | VARCHAR(36) | 1C seller GUID | -| `store_id_1c` | VARCHAR(36) | 1C store GUID | -| `store_id` | INTEGER | FK to city_store | -| `phone` | BIGINT | Customer phone | -| `payments` | JSON | Payment details | -| `pay_arr` | VARCHAR(15) | Payment type IDs | -| `sales_check` | VARCHAR(36) | Return check ID | -| `order_id` | VARCHAR(36) | Online order ID | -| `matrix` | INTEGER | Matrix bouquet % | -| `delivery_date` | TIMESTAMP | Delivery date | -| `pickup` | INTEGER | Pickup flag | - -#### Relationships - -```php -// Sales.php relationships -public function getAdmin() { - return $this->hasOne(Admin::class, ['id' => 'admin_id']); -} - -public function getStore() { - return $this->hasOne(CityStore::class, ['id' => 'store_id']); -} - -public function getStoreByGuid() { - return $this->hasOne(Products1c::class, ['id' => 'store_id_1c']); -} - -public function getUsers() { - return $this->hasOne(Users::class, ['phone' => 'phone']); -} - -public function getBonuses() { - return $this->hasMany(UsersBonus::class, ['check_id' => 'id']); -} - -public function getProducts() { - return $this->hasMany(SalesProducts::class, ['check_id' => 'id']); -} - -public function getSaleCheck() { // Return reference - return $this->hasOne(Sales::class, ['id' => 'sales_check']); -} -``` - -#### Indexes - -- PRIMARY KEY on `id` -- UNIQUE on `(date, operation, store_id_1c, id)` -- INDEX on `date` -- INDEX on `phone` -- INDEX on `store_id` -- INDEX on `operation` - ---- - -### 4. UsersBonus (Bonus Transactions) - -**Table**: `users_bonus` -**Primary Key**: `id` (integer) -**File**: `erp24/records/UsersBonus.php` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | INTEGER | Primary key | -| `phone` | VARCHAR(13) | Customer phone | -| `name` | VARCHAR(155) | Transaction name | -| `date` | TIMESTAMP | Transaction date | -| `user_id` | INTEGER | FK to users | -| `store_id` | INTEGER | FK to city_store | -| `check_id` | VARCHAR(45) | FK to sales | -| `tip` | TEXT | plus/minus/burn | -| `tip_sale` | TEXT | Transaction type detail | -| `price` | DECIMAL | Purchase amount | -| `price_skidka` | DECIMAL | Discount applied | -| `bonus` | DECIMAL | Bonus amount | -| `date_start` | TIMESTAMP | Bonus valid from | -| `date_end` | TIMESTAMP | Bonus expires at | -| `admin_id` | INTEGER | Added by admin | -| `referal_id` | INTEGER | Referral ID | -| `store_id_1c` | VARCHAR(36) | 1C store GUID | -| `seller_id_1c` | VARCHAR(36) | 1C seller GUID | - -#### Transaction Types (`tip_sale`) - -- `sale` - Accrued from purchase -- `minus` - Written off for purchase -- `burn` - Expired bonuses -- `memorable300` - 5 memorable dates bonus -- `p_PROMOCODE` - Promotional code -- `referral` - Referral bonus -- `manual` - Manual adjustment - -#### Relationships - -```php -// UsersBonus.php relationships -public function getUser() { - return $this->hasOne(Users::class, ['id' => 'user_id']); -} - -public function getStore() { - return $this->hasOne(CityStore::class, ['id' => 'store_id']); -} - -public function getSale() { - return $this->hasOne(Sales::class, ['id' => 'check_id']); -} - -public function getAdmin() { - return $this->hasOne(Admin::class, ['id' => 'admin_id']); -} -``` - ---- - -### 5. CityStore (Stores) - -**Table**: `city_store` -**Primary Key**: `id` (integer) -**File**: `erp24/records/CityStore.php` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | INTEGER | Primary key | -| `f_id` | INTEGER | FloraPoint ID | -| `name` | VARCHAR | Short store name | -| `name_full` | VARCHAR | Full store name | -| `city_id` | INTEGER | FK to city | -| `adress` | TEXT | Street address | -| `gps` | VARCHAR | GPS coordinates | -| `email` | VARCHAR | Store email | -| `tg_chat_id` | VARCHAR | Telegram chat ID | -| `visible` | INTEGER | Display on site | -| `administrator_id` | INTEGER | Store manager | -| `sale_plan_avg` | INTEGER | Avg sales plan | -| `visitor_day_avg` | INTEGER | Avg daily visitors | -| `open_date` | DATE | Opening date | - -#### Relationships - -```php -// CityStore.php relationships -public function getCity() { - return $this->hasOne(City::class, ['id' => 'city_id']); -} - -public function getAdministrator() { - return $this->hasOne(Admin::class, ['id' => 'administrator_id']); -} - -public function getEmployees() { - return $this->hasMany(Admin::class, ['store_id' => 'id']); -} - -public function getSales() { - return $this->hasMany(Sales::class, ['store_id' => 'id']); -} - -public function getTimetable() { - return $this->hasMany(Timetable::class, ['store_id' => 'id']); -} -``` - ---- - -### 6. Timetable (Employee Schedule) - -**Table**: `timetable` -**Primary Key**: `id` (integer) -**File**: `erp24/records/Timetable.php` -**Traits**: `SoftDeleteTrait` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | INTEGER | Primary key | -| `admin_id` | INTEGER | FK to admin | -| `store_id` | INTEGER | FK to city_store | -| `shift_id` | INTEGER | FK to shift | -| `admin_group_id` | INTEGER | Position group | -| `tabel` | INTEGER | 0=plan, 1=fact | -| `date` | DATE | Shift date | -| `time_start` | TIME | Shift start | -| `time_end` | TIME | Shift end | -| `datetime_start` | TIMESTAMP | Full start datetime | -| `datetime_end` | TIMESTAMP | Full end datetime | -| `work_time` | DECIMAL | Hours worked | -| `salary_shift` | INTEGER | Shift salary | -| `slot_type_id` | INTEGER | Type (work/vacation) | -| `status` | INTEGER | 0=pending, 1=verified | -| `active` | INTEGER | Soft delete flag | -| `deleted_at` | TIMESTAMP | Deletion time | -| `deleted_by` | INTEGER | Deleted by admin | - -#### Slot Types - -- `1` - TIMESLOT_WORK (regular work shift) -- `2` - TIMESLOT_VACATION (vacation) -- `3` - TIMESLOT_ADMINISTRATIVE (admin leave) -- `4` - TIMESLOT_SICK_LEAVE (sick leave) -- `5` - TIMESLOT_INTERNSHIP (training) -- `6` - TIMESLOT_WEEKEND (weekend) -- `7` - TIMESLOT_FREELANCE (temporary worker) - -#### Relationships - -```php -// Timetable.php relationships -public function getAdmin() { - return $this->hasOne(Admin::class, ['id' => 'admin_id']); -} - -public function getStore() { - return $this->hasOne(CityStore::class, ['id' => 'store_id']); -} - -public function getShift() { - return $this->hasOne(Shift::class, ['id' => 'shift_id']); -} - -public function getPosition() { - return $this->hasOne(AdminGroup::class, ['id' => 'admin_group_id']); -} - -public function getDeletedBy() { - return $this->hasOne(Admin::class, ['id' => 'deleted_by']); -} -``` - ---- - -### 7. AdminPayroll (Payroll Summary) - -**Table**: `admin_payroll` -**Primary Key**: `id` (integer) -**File**: `erp24/records/AdminPayroll.php` - -#### Schema - -| Column | Type | Description | -|--------|------|-------------| -| `id` | INTEGER | Primary key | -| `admin_id` | INTEGER | FK to admin | -| `store_id` | INTEGER | FK to city_store | -| `year` | INTEGER | Year | -| `month` | INTEGER | Month (1-12) | -| `date` | VARCHAR(100) | Month description | -| `date_time` | TIMESTAMP | Created at | -| `delete_status` | INTEGER | Deletion flag | -| `date_delete` | TIMESTAMP | Deleted at | - -#### Unique Constraint - -- UNIQUE on `(admin_id, year, month)` - One payroll per employee per month - -#### Relationships - -```php -// AdminPayroll.php relationships -public function getAdmin() { - return $this->hasOne(Admin::class, ['id' => 'admin_id']); -} - -public function getStore() { - return $this->hasOne(CityStore::class, ['id' => 'store_id']); -} - -public function getDays() { - return $this->hasMany(AdminPayrollDays::class, ['admin_payroll_id' => 'id']); -} - -public function getHistory() { - return $this->hasMany(AdminPayrollHistory::class, ['admin_payroll_id' => 'id']); -} - -public function getMonthInfo() { - return $this->hasOne(AdminPayrollMonthInfo::class, ['admin_payroll_id' => 'id']); -} -``` - ---- - -## Business Domain Tables - -### Bonus & Loyalty Program - -| Table | Purpose | Records | -|-------|---------|---------| -| `users` | Customer profiles | 100K+ | -| `users_bonus` | Bonus transactions | 1M+ | -| `users_bonus_levels` | Customer tier assignments | 100K+ | -| `bonus_levels` | Tier configuration | ~10 | -| `users_events` | Memorable dates | 200K+ | -| `promocode` | Promotional codes | 1K+ | -| `referral_status` | Referral tracking | 10K+ | - -**Data Flow**: -``` -Sales → BonusService → UsersBonus (accrual) → Users.bonus_level update -``` - ---- - -### Payroll & HR - -| Table | Purpose | Records | -|-------|---------|---------| -| `admin` | Employees | 500+ | -| `admin_group` | Positions/roles | 50+ | -| `admin_payroll` | Monthly payroll | 10K+ | -| `admin_payroll_days` | Daily payroll records | 150K+ | -| `admin_payroll_values` | Payment components | 50K+ | -| `admin_payroll_values_dict` | Component types | 30+ | -| `admin_rating` | Performance ratings | 20K+ | -| `grade` | Employee grades | 10 | -| `holiday` | Holiday calendar | 500+ | - -**Data Flow**: -``` -Timetable → PayrollService → AdminPayrollDays → AdminPayroll → Payment -``` - ---- - -### Sales & Inventory - -| Table | Purpose | Records | -|-------|---------|---------| -| `sales` | Sale checks | 500K+ | -| `sales_products` | Sale line items | 2M+ | -| `sales_history` | Sales history log | 500K+ | -| `products_1c` | Product catalog from 1C | 50K+ | -| `write_offs_erp` | Inventory write-offs | 30K+ | -| `write_offs_products_erp` | Write-off line items | 100K+ | -| `matrix_bouquet` | Matrix bouquet definitions | 1K+ | - ---- - -### Task Management - -| Table | Purpose | Records | -|-------|---------|---------| -| `task` | Tasks | 50K+ | -| `task_template` | Task templates | 200+ | -| `task_type` | Task types | 30+ | -| `task_status` | Task statuses | 10+ | -| `task_logs` | Task change history | 200K+ | -| `task_motivation` | Task rewards | 10K+ | -| `task_viewers` | Task watchers | 50K+ | - ---- - -### Marketplace Integration - -| Table | Purpose | Records | -|-------|---------|---------| -| `marketplace_orders` | Marketplace orders | 50K+ | -| `marketplace_order_items` | Order line items | 150K+ | -| `marketplace_order_status_history` | Status changes | 200K+ | -| `marketplace_order_1c_statuses` | Status mappings | 50+ | -| `marketplace_status` | Status definitions | 20+ | - ---- - -### Learning & Training - -| Table | Purpose | Records | -|-------|---------|---------| -| `lesson` | Training lessons | 500+ | -| `lesson_group` | Lesson groups | 50+ | -| `lesson_poll` | Lesson quizzes | 200+ | -| `lesson_poll_answers` | Quiz answers | 5K+ | -| `regulations` | Company regulations | 100+ | -| `regulations_poll` | Regulation quizzes | 50+ | -| `regulations_passed` | Completion tracking | 10K+ | - ---- - -## System Tables - -### Authentication & Authorization (RBAC) - -| Table | Purpose | -|-------|---------| -| `auth_assignment` | User role assignments | -| `auth_item` | Roles and permissions | -| `auth_item_child` | Role hierarchy | -| `auth_rule` | Custom auth rules | - -### Logging & Monitoring - -| Table | Purpose | Size | -|-------|---------|------| -| `api_logs` | API request logs | Very Large | -| `api_error_log` | API errors | Large | -| `error_log` | Application errors | Large | -| `info_log` | Information logs | Very Large | -| `task_logs` | Task change logs | Large | - -### Configuration & Reference - -| Table | Purpose | -|-------|---------| -| `export_import_table` | 1C mapping (ID ↔ GUID) | -| `universal_catalog` | Configurable catalogs | -| `universal_catalog_item` | Catalog items | -| `dashboard_fields` | Dashboard configurations | -| `crm_menu` | Menu structure | - ---- - -## Data Flow - -### Sales Bonus Accrual Flow - -```mermaid -flowchart TD - A[1C: Create Sale] --> B[Sales Table] - B --> C{Customer in
bonus program?} - C -->|No| D[End] - C -->|Yes| E[Calculate Bonus] - E --> F{Check Products} - F -->|Exclude non-bonus items| E - F -->|Calculate base| G[Apply Tier Rate] - G --> H[Create UsersBonus
tip=plus] - H --> I[Update User Stats] - I --> J{Update Tier?} - J -->|Yes| K[Update bonus_level] - J -->|No| L[End] - K --> L -``` - -### Payroll Calculation Flow - -```mermaid -flowchart TD - A[Month End] --> B[Gather Timetable Data] - B --> C[Calculate Daily Hours] - C --> D[Create AdminPayrollDays] - D --> E[Apply Payment Components] - E --> F[Sum AdminPayrollValues] - F --> G[Create AdminPayrollMonthInfo] - G --> H[Generate AdminPayroll] - H --> I[Review & Approve] - I --> J{Approved?} - J -->|Yes| K[Mark for Payment] - J -->|No| L[Edit Components] - L --> F - K --> M[Export to 1C] -``` - ---- - -## Naming Conventions - -### Table Names - -- **snake_case**: All table names use underscore separation -- **Singular**: Table names are singular (`admin`, not `admins`) -- **Descriptive**: Clear, descriptive names - -### Column Names - -- **snake_case**: Column names use underscores -- **Suffixes**: - - `_id`: Integer foreign key (e.g., `admin_id`) - - `_id_1c`: UUID from 1C (e.g., `store_id_1c`) - - `_guid`: UUID identifier - - `_at`: Timestamp (e.g., `created_at`, `deleted_at`) - - `_arr`: Serialized array/JSON - - `_date`: Date field - - `_time`: Time field - - `_datetime`: Timestamp field - -### Relationship Patterns - -```php -// hasOne: singular method name -public function getAdmin() - -// hasMany: plural method name -public function getBonuses() - -// Through another table: descriptive name -public function getStoreByGuid() -``` - ---- - -## Migration History - -### Migration File Pattern - -``` -m{YYMMDD}_{HHMMSS}_{description}.php -``` - -**Examples**: -- `m230220_095139_function_regulations.php` -- `m230306_064243_create_table_write_offs_erp.php` -- `m230301_122735_add_access_token_column_to_admin_table.php` - -### Recent Migrations (2023-2025) - -| Date | Migration | Purpose | -|------|-----------|---------| -| 2024-12-28 | m241228_092653 | Add target_date to sent_kogort | -| 2024-11-15 | Various | Marketplace enhancements | -| 2024-08-20 | Various | Bonus system improvements | -| 2023-03-06 | m230306_064243 | Create write_offs_erp | -| 2023-03-01 | m230301_122735 | Add access_token to admin | - -### Migration Commands - -```bash -# Apply migrations -php erp24/yii migrate - -# Create new migration -php erp24/yii migrate/create migration_name - -# Rollback last migration -php erp24/yii migrate/down - -# View migration history -php erp24/yii migrate/history -``` - ---- - -## Best Practices - -### 1. Always Use ActiveRecord Relationships - -```php -// Good: Use relationship -$sales = $user->getSales()->where(['operation' => Sales::OPERATION_SALE])->all(); - -// Bad: Manual join -$sales = Sales::find()->where(['phone' => $user->phone])->all(); -``` - -### 2. Use Transactions for Multi-Table Operations - -```php -$transaction = Yii::$app->db->beginTransaction(); -try { - $sale->save(); - $bonus->save(); - $user->save(); - $transaction->commit(); -} catch (\Exception $e) { - $transaction->rollBack(); - throw $e; -} -``` - -### 3. Index High-Traffic Columns - -- Foreign keys -- Date/timestamp columns used in WHERE clauses -- Phone numbers, email addresses -- Status/type columns used for filtering - -### 4. Use Soft Deletes Where Appropriate - -```php -// Models with SoftDeleteTrait -$timetable->softDelete(); // Sets deleted_at instead of removing row -``` - ---- - -## Database Diagrams - -For visual ER diagrams, see: -- [Core Entities Diagram](./diagrams/core-entities.md) -- [Bonus System Diagram](./diagrams/bonus-system.md) -- [Payroll System Diagram](./diagrams/payroll-system.md) -- [Sales Flow Diagram](./diagrams/sales-flow.md) - ---- - -## Related Documentation - -- [ActiveRecord Models Reference](./models-reference.md) -- [Table Relationships](./relationships.md) -- [Migration Guide](./migrations.md) -- [Data Dictionary](./data-dictionary.md) - ---- - -**Last Updated**: January 2025 -**Database Version**: PostgreSQL 12+ -**Total Models**: 389 -**Total Migrations**: 278 -**Maintained By**: ERP24 Development Team diff --git a/erp24/docs/CROSS_REFERENCE.md b/erp24/docs/CROSS_REFERENCE.md new file mode 100644 index 00000000..14e20ab1 --- /dev/null +++ b/erp24/docs/CROSS_REFERENCE.md @@ -0,0 +1,436 @@ +# Матрица взаимосвязей модулей ERP24 + +## 📊 Обзор + +Данный документ описывает взаимосвязи между всеми 12 бизнес-модулями системы ERP24, показывая как модули взаимодействуют друг с другом и какие данные они обмениваются. + +## 🔗 Матрица зависимостей + +| Модуль | Зависит от | Используется в | Тип связи | +|--------|-----------|----------------|-----------| +| **Bonus** | Timetable, Rating, WriteOffs, Sales | Payroll, Dashboard | Данные, Расчеты | +| **Payroll** | Timetable, Bonus, Grade, EmployeePayment | Dashboard, HR | Данные, Расчеты | +| **Timetable** | Admin, CityStore, Checkins | Payroll, Rating, Bonus, Cabinet | Данные | +| **Rating** | Timetable, Bonus, Sales, WriteOffs, Cabinet | Payroll, Dashboard | Расчеты, Метрики | +| **Dashboard** | Sales, Timetable, Rating, WriteOffs, Shipment | Аналитика | Визуализация | +| **Shipment** | Products1C, Suppliers | Dashboard, WriteOffs | Данные | +| **WriteOffs** | Products1C, CityStore, 1C | Rating, Bonus, Dashboard | Данные, Метрики | +| **Notifications** | Admin, AdminGroup | Lesson, Regulations, KIK | Коммуникация | +| **KIK Feedback** | Sales, CityStore, Admin | Notifications, Rating | Данные, Оценка | +| **Regulations** | Admin, AdminGroup | Notifications, HR | Обучение | +| **Grade** | Admin, Position | Payroll, Bonus | Иерархия | +| **Lesson** | Admin | Notifications, HR | Обучение | + +## 🌐 Граф взаимосвязей + +```mermaid +graph TB + subgraph "Ядро HR" + Timetable[Timetable
Расписание] + Payroll[Payroll
Зарплата] + Bonus[Bonus
Бонусы] + Grade[Grade
Грейды] + Rating[Rating
Рейтинг] + end + + subgraph "Обучение" + Lesson[Lesson
Уроки] + Regulations[Regulations
Регламенты] + end + + subgraph "Операции" + Shipment[Shipment
Отгрузка] + WriteOffs[WriteOffs
Списания] + end + + subgraph "Коммуникации" + Notifications[Notifications
Уведомления] + KIK[KIK Feedback
Обратная связь] + end + + subgraph "Аналитика" + Dashboard[Dashboard
Дашборды] + end + + subgraph "Внешние системы" + Sales1C[1C: Продажи] + Products1C[1C: Товары] + AmoCRM[AmoCRM] + TelegramBot[Telegram Bot] + end + + %% HR взаимосвязи + Timetable --> Payroll + Timetable --> Rating + Timetable --> Bonus + Bonus --> Payroll + Grade --> Payroll + Rating --> Bonus + WriteOffs --> Rating + WriteOffs --> Bonus + + %% Аналитика + Payroll --> Dashboard + Bonus --> Dashboard + Rating --> Dashboard + Shipment --> Dashboard + WriteOffs --> Dashboard + + %% Коммуникации + Lesson --> Notifications + Regulations --> Notifications + KIK --> Notifications + KIK --> Rating + + %% Интеграции 1C + Sales1C --> Rating + Sales1C --> Bonus + Sales1C --> KIK + Products1C --> Shipment + Products1C --> WriteOffs + + %% AmoCRM + AmoCRM --> KIK + + %% Telegram + TelegramBot --> Bonus + TelegramBot --> Timetable + + style Timetable fill:#e1f5ff + style Payroll fill:#e1f5ff + style Bonus fill:#e1f5ff + style Dashboard fill:#fff4e1 + style Notifications fill:#e8f5e8 +``` + +## 📋 Детальные взаимосвязи + +### 1. Bonus (Бонусная система) + +**Входящие связи:** +- `Timetable` → количество и качество смен для расчета бонусов +- `Rating` → рейтинг качества для бонусов +- `WriteOffs` → процент списания (штрафы) +- `Sales (1C)` → продажи для расчета бонусов + +**Исходящие связи:** +- `Payroll` → бонусы включаются в зарплату +- `Dashboard` → визуализация бонусов +- `Telegram Bot` → отправка бонусных баллов + +**Ключевые методы взаимодействия:** +```php +BonusService::getGameBonusByPercentLoss($percentLoss) // from WriteOffs +BonusService::getBonusForQuality($percent) // from Rating +BonusService::getTeambonus($sales, $plan) // from Sales 1C +``` + +### 2. Payroll (Зарплата) + +**Входящие связи:** +- `Timetable` → отработанные дни/часы +- `Bonus` → бонусные начисления +- `Grade` → оклад по грейду +- `EmployeePayment` → базовая ставка + +**Исходящие связи:** +- `Dashboard` → отчеты по зарплате +- `Finance` → данные для выплат + +**Формула:** +``` +Зарплата = (Оклад × Коэфф_Грейда) + Переменная_часть + Бонусы +Переменная_часть = Ставка_за_день × Отработанные_дни +``` + +### 3. Timetable (Расписание) + +**Входящие связи:** +- `Admin` → сотрудники +- `CityStore` → магазины +- `TelegramBot` → чекины (геолокация) + +**Исходящие связи:** +- `Payroll` → данные для расчета зарплаты +- `Rating` → смены для расчета рейтинга +- `Bonus` → смены для расчета бонусов +- `Cabinet` → личный кабинет + +**Ключевые сущности:** +- `TimetablePlan` → планируемый график +- `TimetableFact` → фактически отработанное время +- `Checkins` → отметки о начале/конце смены + +### 4. Rating (Рейтинг) + +**Входящие связи:** +- `Timetable` → данные о сменах +- `Bonus` → бонусные показатели +- `Sales (1C)` → продажи +- `WriteOffs` → списания +- `Cabinet Service` → конверсия, средний чек + +**Исходящие связи:** +- `Payroll` → влияние на премии +- `Bonus` → бонусы за высокий рейтинг +- `Dashboard` → визуализация рейтингов +- `KIK` → оценка качества + +**Типы рейтингов:** +1. Администраторы (rating_id=1) +2. Флористы (rating_id=2) +3. Кустовые директора (rating_id=3) +4. Стажеры (rating_id=4) + +### 5. Dashboard (Аналитика) + +**Входящие связи:** +- `Sales (1C)` → данные о продажах +- `Timetable` → посещаемость, смены +- `Rating` → рейтинги +- `WriteOffs` → списания +- `Shipment` → отгрузки +- `Payroll` → зарплаты +- `Bonus` → бонусы + +**Исходящие связи:** +- Визуализация для руководства +- Экспорт в Excel/PDF + +**Основные метрики:** +- Продажи / План +- Конверсия +- Средний чек +- % услуг, упаковки, горшечных +- Трафик + +### 6. Shipment (Отгрузка) + +**Входящие связи:** +- `Products1C` → товары и остатки +- `ShipmentProviders` → поставщики + +**Исходящие связи:** +- `Dashboard` → аналитика закупок +- `WriteOffs` → связь с порчей при доставке +- `1C` → синхронизация отгрузок + +**Ключевые процессы:** +- Создание заявок на закупку +- Распределение товара между магазинами +- Отслеживание статусов отгрузки + +### 7. WriteOffs (Списания) + +**Входящие связи:** +- `Products1C` → товары +- `CityStore` → магазины +- `1C` → синхронизация списаний + +**Исходящие связи:** +- `Rating` → процент списания влияет на рейтинг +- `Bonus` → штрафы за превышение +- `Dashboard` → аналитика списаний + +**Причины списаний:** +- Брак +- Порча/Увядание +- Пересорт +- Недостача +- Истечение срока годности + +### 8. Notifications (Уведомления) + +**Входящие связи:** +- `Admin` → получатели +- `AdminGroup` → группы получателей + +**Исходящие связи:** +- `Lesson` → уведомления о новых уроках +- `Regulations` → уведомления о новых регламентах +- `KIK` → уведомления от КК +- `HR` → административные уведомления + +**Механизм:** +- Создание уведомления +- Выбор получателей (индивидуально/группа/все) +- Отложенная отправка +- Отслеживание прочтения + +### 9. KIK Feedback (Обратная связь) + +**Входящие связи:** +- `Sales (1C)` → чеки +- `CityStore` → магазины +- `Admin` → ответственные +- `AmoCRM` → номера заказов + +**Исходящие связи:** +- `Notifications` → уведомления о проблемах +- `Rating` → влияние на QualityRating +- `HR` → дисциплинарные меры + +**Workflow:** +``` +Новые → В работе → Ожидают → Решение руководства → +Возвращены в работу → Выполнено +``` + +### 10. Regulations (Регламенты) + +**Входящие связи:** +- `Admin` → целевая аудитория +- `AdminGroup` → группы для назначения + +**Исходящие связи:** +- `Notifications` → уведомления о новых регламентах +- `HR` → обязательное изучение + +**Процесс:** +1. Создание регламента +2. Создание теста (опросник) +3. Назначение сотрудникам +4. Прохождение теста +5. Фиксация результата + +### 11. Grade (Грейды) + +**Входящие связи:** +- `Admin` → сотрудники +- `EmployeePosition` → должности + +**Исходящие связи:** +- `Payroll` → базовая ставка по грейду +- `Bonus` → коэффициенты бонусов +- `HR` → карьерный рост + +**Иерархия:** +- Junior → Middle → Senior → Lead → Expert + +### 12. Lesson (Обучение) + +**Входящие связи:** +- `Admin` → ученики + +**Исходящие связи:** +- `Notifications` → уведомления о новых уроках +- `HR` → отслеживание обучения +- `Rating` → влияние на развитие + +**Компоненты:** +- Курсы +- Уроки +- Тесты +- Сертификаты + +## 🔄 Циклы взаимодействия + +### Цикл 1: Расчет зарплаты +``` +Timetable → Payroll ← Bonus ← Rating ← Timetable + ↑ + Grade +``` + +### Цикл 2: Рейтинг и бонусы +``` +Timetable → Rating → Bonus → Payroll + ↑ ↑ +WriteOffs Sales (1C) +``` + +### Цикл 3: Обучение и уведомления +``` +Lesson → Notifications → Admin + ↓ +Regulations → Notifications → Admin +``` + +### Цикл 4: Обратная связь и качество +``` +KIK Feedback → Notifications → Responsible + ↓ + QualityRating → Rating → Bonus +``` + +## 📊 Статистика взаимосвязей + +| Модуль | Входящих связей | Исходящих связей | Всего | +|--------|-----------------|------------------|-------| +| Timetable | 3 | 4 | 7 | +| Payroll | 4 | 2 | 6 | +| Bonus | 4 | 3 | 7 | +| Rating | 6 | 4 | 10 | +| Dashboard | 7 | 0 | 7 | +| Shipment | 2 | 3 | 5 | +| WriteOffs | 3 | 3 | 6 | +| Notifications | 2 | 4 | 6 | +| KIK Feedback | 4 | 3 | 7 | +| Regulations | 2 | 2 | 4 | +| Grade | 2 | 3 | 5 | +| Lesson | 1 | 3 | 4 | + +**Самые связанные модули:** +1. **Rating** - 10 связей (центральный узел для оценки) +2. **Bonus** - 7 связей (ключевая мотивация) +3. **Timetable** - 7 связей (основа учета времени) +4. **Dashboard** - 7 связей (агрегация всех данных) +5. **KIK Feedback** - 7 связей (обратная связь и качество) + +## 🎯 Ключевые точки интеграции + +### 1. Cabinet Service +Универсальный сервис для сбора данных из разных модулей: +- Timetable +- Sales (1C) +- Rating +- Bonus + +### 2. 1C Integration +Синхронизация данных: +- Sales → Rating, Bonus +- Products → Shipment, WriteOffs +- Stores → все модули + +### 3. Telegram Bot +Двусторонняя интеграция: +- Checkins → Timetable +- Bonus notifications → Users + +### 4. AmoCRM +Интеграция: +- Orders → KIK Feedback +- Clients → Sales + +## 📝 Рекомендации по разработке + +### При изменении модуля Timetable: +Проверить влияние на: +- Payroll (расчет зарплаты) +- Rating (расчет рейтинга) +- Bonus (расчет бонусов) +- Cabinet (личный кабинет) + +### При изменении модуля Bonus: +Проверить влияние на: +- Payroll (включение в зарплату) +- Dashboard (визуализация) +- Telegram (уведомления) + +### При изменении модуля Rating: +Проверить влияние на: +- Bonus (бонусы за рейтинг) +- Payroll (премии) +- Dashboard (отображение) + +### При добавлении нового модуля: +1. Определить входящие зависимости +2. Определить исходящие связи +3. Обновить данную матрицу +4. Создать миграции для связей +5. Документировать интеграции + +--- + +**Последнее обновление:** 2025-11-17 +**Версия:** 1.0 diff --git a/erp24/docs/INDEX.md b/erp24/docs/INDEX.md new file mode 100644 index 00000000..04408399 --- /dev/null +++ b/erp24/docs/INDEX.md @@ -0,0 +1,141 @@ +# Быстрый индекс документации ERP24 + +## 🚀 Начните здесь + +- **[README.md](./README.md)** - Главная страница документации +- **[SUMMARY.md](./SUMMARY.md)** - Итоговая сводка (статистика, метрики) +- **[CROSS_REFERENCE.md](./CROSS_REFERENCE.md)** - Матрица взаимосвязей модулей + +## 🏗️ Архитектура + +- **[Обзор системы](./architecture/system-overview.md)** - Высокоуровневая архитектура ERP24 +- **[Архитектура API](./architecture/api-architecture.md)** - Трёхслойная архитектура API + +## 📚 Модули по категориям + +### 👥 HR и Персонал +- [Bonus](./modules/bonus/README.md) - Бонусная система +- [Payroll](./modules/payroll/README.md) - Расчет заработной платы +- [Timetable](./modules/timetable/README.md) - Расписание и табель +- [Rating](./modules/rating/README.md) - Рейтинговая система +- [Grade](./modules/grade/README.md) - Грейды и должности + +### 🎓 Обучение и Развитие +- [Lesson](./modules/lesson/README.md) - Система обучения +- [Regulations](./modules/regulations/README.md) - Регламенты и правила + +### 📦 Операции и Логистика +- [Shipment](./modules/shipment/README.md) - Отгрузка и закупки +- [WriteOffs](./modules/write-offs/README.md) - Списания товаров + +### 💬 Коммуникации +- [Notifications](./modules/notifications/README.md) - Система уведомлений +- [KIK Feedback](./modules/kik-feedback/README.md) - Обратная связь от КК + +### 📊 Аналитика +- [Dashboard](./modules/dashboard/README.md) - Информационные панели + +## 🔍 Поиск по функциональности + +### Расчет зарплаты +→ [Payroll](./modules/payroll/README.md) → [Timetable](./modules/timetable/README.md) → [Bonus](./modules/bonus/README.md) → [Grade](./modules/grade/README.md) + +### Мотивация сотрудников +→ [Bonus](./modules/bonus/README.md) → [Rating](./modules/rating/README.md) → [Grade](./modules/grade/README.md) + +### Учет времени +→ [Timetable](./modules/timetable/README.md) → [Payroll](./modules/payroll/README.md) + +### Обучение персонала +→ [Lesson](./modules/lesson/README.md) → [Regulations](./modules/regulations/README.md) → [Notifications](./modules/notifications/README.md) + +### Работа с товаром +→ [Shipment](./modules/shipment/README.md) → [WriteOffs](./modules/write-offs/README.md) + +### Контроль качества +→ [KIK Feedback](./modules/kik-feedback/README.md) → [Rating](./modules/rating/README.md) → [Bonus](./modules/bonus/README.md) + +### Аналитика и отчеты +→ [Dashboard](./modules/dashboard/README.md) → [Rating](./modules/rating/README.md) + +## 📖 По типу компонента + +### Сервисы +- [BonusService](./modules/bonus/README.md#сервисы) - 50+ методов, 1200+ строк +- [ShipmentService](./modules/shipment/README.md#сервисы) - 3786 строк! +- [RatingService](./modules/rating/README.md#сервисы) - 612 строк +- [PayrollService](./modules/payroll/README.md#сервисы) - расчет зарплаты +- [TimetableService](./modules/timetable/README.md#сервисы) - управление графиком +- [DashboardService](./modules/dashboard/README.md#сервисы) - метрики +- [NotificationService](./modules/notifications/README.md#сервисы) - уведомления + +### Actions (больше 10) +- [Timetable](./modules/timetable/README.md#actions-действия) - 17 actions +- [KIK Feedback](./modules/kik-feedback/README.md#actions-действия) - 16 actions +- [Bonus](./modules/bonus/README.md#actions-действия) - 13 actions +- [Dashboard](./modules/dashboard/README.md#actions-действия) - 13 actions + +### Модели (больше 8) +- [Payroll](./modules/payroll/README.md#модели--records) - 10 моделей +- [Dashboard](./modules/dashboard/README.md#модели--records) - 9 моделей +- [Timetable](./modules/timetable/README.md#модели--records) - 9 моделей +- [WriteOffs](./modules/write-offs/README.md) - 9 моделей +- [Bonus](./modules/bonus/README.md#модели--records) - 8 моделей + +## 🔗 Интеграции + +### 1C +- [Rating](./modules/rating/README.md) - данные о продажах +- [Bonus](./modules/bonus/README.md) - продажи для бонусов +- [Shipment](./modules/shipment/README.md) - синхронизация товаров +- [WriteOffs](./modules/write-offs/README.md) - списания +- [KIK Feedback](./modules/kik-feedback/README.md) - чеки + +### Telegram Bot +- [Timetable](./modules/timetable/README.md) - чекины +- [Bonus](./modules/bonus/README.md) - отправка баллов + +### AmoCRM +- [KIK Feedback](./modules/kik-feedback/README.md) - номера заказов + +## 🎯 Частые задачи + +### Как добавить новый бонус? +→ [Bonus: Примеры использования](./modules/bonus/README.md#примеры-использования) + +### Как рассчитывается зарплата? +→ [Payroll: Бизнес-логика](./modules/payroll/README.md#бизнес-логика) + +### Как работает рейтинг? +→ [Rating: Формула расчета](./modules/rating/README.md#формула-расчета-рейтинга) + +### Как создать уведомление? +→ [Notifications: Примеры](./modules/notifications/README.md#примеры-использования) + +### Как назначить регламент? +→ [Regulations: Процесс](./modules/regulations/README.md#процесс-прохождения-регламента) + +### Как посмотреть взаимосвязи? +→ [CROSS_REFERENCE.md](./CROSS_REFERENCE.md) + +## 📊 Статистика + +- **Модулей:** 12 +- **Контроллеров:** 32 +- **Сервисов:** 12 +- **Actions:** 73 +- **Моделей:** 78 +- **Страниц документации:** 15 +- **Диаграмм:** 25+ +- **Примеров кода:** 60+ + +## 📝 Версии + +- **v1.0** (2025-11-17) - Первая полная версия документации + - Все 12 модулей + - Матрица взаимосвязей + - Примеры и диаграммы + +--- + +**Быстрый старт:** [README.md](./README.md) → [modules/README.md](./modules/README.md) → Выбрать модуль diff --git a/erp24/docs/README.md b/erp24/docs/README.md new file mode 100644 index 00000000..feab3525 --- /dev/null +++ b/erp24/docs/README.md @@ -0,0 +1,384 @@ +# Документация ERP24 + +Добро пожаловать в документацию системы ERP24 - комплексной ERP-системы для управления сетью цветочных магазинов на базе Yii2 Framework. + +## 📋 О системе + +**ERP24** - это enterprise resource planning система, разработанная для автоматизации всех бизнес-процессов сети цветочных магазинов: +- Управление персоналом и расписанием +- Расчет заработной платы и бонусов +- Управление закупками и отгрузками +- Учет продаж и складских операций +- Контроль качества и рейтинги +- Обучение и развитие персонала +- Аналитика и отчетность + +## 🏗️ Архитектура системы + +### Технологический стек + +- **Framework:** Yii2 (PHP 7.4+) +- **База данных:** MySQL +- **Frontend:** JavaScript, jQuery, Bootstrap +- **Очереди:** Yii2 Queue +- **Интеграции:** 1С, Telegram Bot, AmoCRM + +### Структура проекта + +``` +erp24/ +├── actions/ # Автономные действия (40+) +├── api1/ # Legacy API +├── api2/ # Modern REST API +├── api3/ # Advanced API (59 модулей) +├── commands/ # Console команды (15+) +├── controllers/ # Web контроллеры (160+) +├── docs/ # Документация (ВЫ ЗДЕСЬ) +├── forms/ # Формы (20+) +├── helpers/ # Вспомогательные классы (15+) +├── inc/ # Legacy include файлы +├── jobs/ # Фоновые задачи (6) +├── migrations/ # Миграции БД (278) +├── models/ # Модели форм +├── modul/ # Legacy модули +├── rbac/ # Управление доступом +├── records/ # ActiveRecord модели (390+) +├── services/ # Бизнес-логика (51 сервис) +├── traits/ # Переиспользуемые трейты +├── views/ # Представления +└── widgets/ # Виджеты +``` + +## 📚 Содержание документации + +### 📖 Основные документы + +- **[SUMMARY.md](./SUMMARY.md)** - Итоговая сводка всей документации +- **[CROSS_REFERENCE.md](./CROSS_REFERENCE.md)** - Матрица взаимосвязей модулей +- **[INDEX.md](./INDEX.md)** - Быстрый индекс документации + +### 🏗️ [Архитектура](./architecture/) + +Архитектурная документация системы: + +- **[Обзор системы](./architecture/system-overview.md)** - Высокоуровневый обзор архитектуры ERP24 +- **[Архитектура API](./architecture/api-architecture.md)** - Трёхслойная архитектура API (API1, API2, API3) + +### 1. [Бизнес-домены (Модули)](./modules/README.md) + +Подробная документация всех модулей системы: + +#### HR и Персонал +- **[Payroll](./modules/payroll/README.md)** - Расчет заработной платы (3 сервиса, 10 моделей) +- **[Bonus](./modules/bonus/README.md)** - Бонусная система (1 сервис с 50+ методами, 8 моделей) +- **[Grade](./modules/grade/README.md)** - Грейды и должности (4 модели) +- **[Timetable](./modules/timetable/README.md)** - Расписание и табель (17 actions, 9 моделей) +- **[Rating](./modules/rating/README.md)** - Рейтинговая система (3 модели) + +#### Обучение и Развитие +- **[Lesson](./modules/lesson/README.md)** - Система обучения (2 сервиса, 5 моделей) +- **[Regulations](./modules/regulations/README.md)** - Регламенты и правила (7 моделей) + +#### Операции и Логистика +- **[Shipment](./modules/shipment/README.md)** - Отгрузка и закупки (сервис 3786 строк, 7+ моделей) +- **[Write-offs](./modules/write-offs/README.md)** - Списания товаров (9 моделей) + +#### Коммуникации +- **[Notifications](./modules/notifications/README.md)** - Система уведомлений (2 модели) +- **[KIK Feedback](./modules/kik-feedback/README.md)** - Обратная связь от КК (5 моделей) + +#### Аналитика +- **[Dashboard](./modules/dashboard/README.md)** - Информационные панели (7 моделей) + +### 2. API Документация + +#### [API Layer 1 - Legacy](./api/api1/README.md) +Устаревший API, постепенно мигрируется на API2. + +#### [API Layer 2 - Modern REST](./api/api2/README.md) +Современный REST API с JSON ответами. + +#### [API Layer 3 - Advanced](./api/api3/README.md) +Расширенный API с 59 модулями. + +### 3. [База данных](./database/README.md) +- Схема БД +- Миграции (278 файлов) +- Связи между таблицами +- Индексы и оптимизация + +### 4. [Сервисы](./services/README.md) +Документация 51 сервиса с бизнес-логикой: +- BonusService +- PayrollService +- ShipmentService +- TimetableService +- и другие... + +### 5. [Руководства](./guides/README.md) +- Установка и настройка +- Разработка новых модулей +- Интеграция с внешними системами +- Безопасность и RBAC +- Тестирование +- Deployment + +### 6. [Справочник ошибок](./errors/README.md) +- Коды ошибок +- Типичные проблемы +- Решения и workarounds + +## 📊 Статистика проекта + +| Компонент | Количество | +|-----------|-----------| +| PHP файлы | ~3,771 | +| Контроллеры | 160+ | +| Records/Models | 390+ | +| Сервисы | 51 | +| Actions | 40+ | +| API контроллеры | 33 | +| Модули API3 | 59 | +| Helpers | 15+ | +| Forms | 20+ | +| Commands | 15+ | +| Migrations | 278 | +| Jobs | 6 | +| **Бизнес-модули** | **12** | + +## 🚀 Быстрый старт + +### Для разработчиков + +1. **Изучите архитектуру:** + - [Обзор системы](./architecture/system-overview.md) + - [Архитектура API](./architecture/api-architecture.md) + - [Структура модулей](./modules/README.md) + +2. **Выберите модуль для изучения:** + - Начните с [Bonus](./modules/bonus/README.md) - хороший пример сложного модуля + - Или [Timetable](./modules/timetable/README.md) - ключевой модуль для многих процессов + +3. **Изучите сервисы:** + - [Документация сервисов](./services/README.md) + - Примеры использования в модулях + +### Для бизнес-аналитиков + +1. **Бизнес-процессы:** + - [Расчет зарплаты](./modules/payroll/README.md#бизнес-логика) + - [Система бонусов](./modules/bonus/README.md#бизнес-логика) + - [Управление расписанием](./modules/timetable/README.md#бизнес-логика) + +2. **Отчеты и аналитика:** + - [Dashboard](./modules/dashboard/README.md) + - [Метрики модулей](./modules/README.md#общая-статистика) + +## 🔗 Взаимосвязи модулей + +```mermaid +graph TB + subgraph "HR & Персонал" + Payroll[Payroll
Зарплата] + Bonus[Bonus
Бонусы] + Grade[Grade
Грейды] + Timetable[Timetable
Расписание] + Rating[Rating
Рейтинг] + end + + subgraph "Обучение & Развитие" + Lesson[Lesson
Обучение] + Regulations[Regulations
Регламенты] + end + + subgraph "Операции & Логистика" + Shipment[Shipment
Отгрузка] + WriteOffs[Write-offs
Списания] + end + + subgraph "Коммуникации" + Notifications[Notifications
Уведомления] + KIK[KIK Feedback
Обратная связь] + end + + subgraph "Аналитика" + Dashboard[Dashboard
Дашборды] + end + + Payroll --> Bonus + Payroll --> Timetable + Grade --> Payroll + Timetable --> Payroll + Rating --> Bonus + Lesson --> Notifications + Regulations --> Notifications + WriteOffs --> Dashboard + Shipment --> Dashboard + Payroll --> Dashboard + Bonus --> Dashboard + Rating --> Dashboard + KIK --> Notifications + + style Payroll fill:#e1f5ff + style Bonus fill:#e1f5ff + style Timetable fill:#e1f5ff +``` + +## 🛠️ Технические требования + +### Минимальные требования + +- PHP >= 7.4 +- MySQL >= 5.7 +- Apache/Nginx +- Composer +- Node.js >= 14 (для frontend assets) + +### Рекомендуемые расширения PHP + +- pdo_mysql +- mbstring +- intl +- gd +- json +- xml + +## 📝 Соглашения о коде + +### Структура кода + +1. **Controllers** - тонкие контроллеры, минимум логики +2. **Services** - вся бизнес-логика +3. **Records** - только работа с БД (ActiveRecord) +4. **Actions** - автономные действия для повторного использования + +### Именование + +- **Controllers:** `{Name}Controller.php` +- **Services:** `{Name}Service.php` +- **Records:** `{TableName}.php` (CamelCase) +- **Actions:** `{Action}Action.php` + +### Namespace структура + +```php +namespace yii_app\{component_type}; + +// Примеры: +namespace yii_app\controllers; +namespace yii_app\services; +namespace yii_app\records; +namespace yii_app\actions\{module}; +``` + +## 🔐 Безопасность + +### RBAC (Role-Based Access Control) + +Система использует встроенный RBAC Yii2: +- Роли определены в `rbac/` +- Проверка доступа в контроллерах +- Динамические права на основе групп пользователей + +### Защита от атак + +- ✅ CSRF токены +- ✅ SQL Injection защита (prepared statements) +- ✅ XSS фильтрация +- ✅ Валидация входных данных +- ✅ Безопасное хранение паролей (bcrypt) + +## 🧪 Тестирование + +### Типы тестов + +- **Unit тесты** - тестирование отдельных методов +- **Functional тесты** - тестирование контроллеров +- **Acceptance тесты** - E2E тестирование + +### Запуск тестов + +```bash +# Все тесты +./vendor/bin/codecept run + +# Только unit +./vendor/bin/codecept run unit + +# Только функциональные +./vendor/bin/codecept run functional +``` + +## 📖 Дополнительные ресурсы + +### Внутренние ресурсы + +- [Yii2 Guide](https://www.yiiframework.com/doc/guide/2.0/ru) +- [Yii2 API Reference](https://www.yiiframework.com/doc/api/2.0) + +### Внешние интеграции + +- **1С** - синхронизация товаров, клиентов, продаж +- **AmoCRM** - CRM интеграция +- **Telegram Bot** - уведомления и чекины +- **БонусПлюс** - импорт бонусов (legacy) + +## 🤝 Контрибьюция + +### Процесс добавления новой функциональности + +1. Создать ветку от `develop` +2. Разработать функционал +3. Написать/обновить документацию +4. Создать Pull Request +5. Code Review +6. Merge в `develop` + +### Добавление новой документации + +1. Создать MD файл в соответствующей директории +2. Использовать существующие шаблоны +3. Добавить ссылки в README +4. Проверить корректность mermaid диаграмм + +## 📞 Поддержка + +При возникновении вопросов: +1. Проверьте [Справочник ошибок](./errors/README.md) +2. Изучите документацию соответствующего модуля +3. Обратитесь к техническому лиду проекта + +## 📜 История изменений + +### 2025-11-17 +- ✅ Создана базовая структура документации +- ✅ Документированы модули: Bonus, Payroll, Shipment, Timetable +- ✅ Добавлены диаграммы взаимосвязей +- ✅ Создан главный README на русском языке + +### Планируется +- ⏳ API документация (api1, api2, api3) +- ⏳ База данных - полная схема всех таблиц +- ⏳ Руководства по разработке +- ⏳ Справочник ошибок и troubleshooting +- ⏳ Deployment и DevOps инструкции + +### ✅ Завершено +- ✅ Документация всех 12 бизнес-модулей +- ✅ Матрица взаимосвязей модулей (CROSS_REFERENCE.md) +- ✅ Итоговая сводка (SUMMARY.md) +- ✅ Mermaid диаграммы архитектуры +- ✅ Примеры кода и use cases +- ✅ ER-диаграммы для каждого модуля + +--- + +## 📄 Лицензия + +Проприетарное программное обеспечение. Все права защищены. + +--- + +*Документация поддерживается в актуальном состоянии командой разработки ERP24.* + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/SUMMARY.md b/erp24/docs/SUMMARY.md new file mode 100644 index 00000000..d12d7b6f --- /dev/null +++ b/erp24/docs/SUMMARY.md @@ -0,0 +1,509 @@ +# Итоговая сводка документации ERP24 + +## 🎉 Статус документации: ЗАВЕРШЕНА + +Дата завершения: **2025-11-17** + +## 📊 Общая статистика проекта + +### Масштаб системы + +| Категория | Количество | +|-----------|-----------| +| **Всего PHP файлов** | ~3,771 | +| **Контроллеров** | 160+ | +| **Records/Models** | 390+ | +| **Сервисов** | 51 | +| **Actions** | 40+ | +| **API контроллеров (api2)** | 33 | +| **API модулей (api3)** | 59 | +| **Helpers** | 15+ | +| **Forms** | 20+ | +| **Console Commands** | 15+ | +| **Миграций БД** | 278 | +| **Jobs (фоновые задачи)** | 6 | +| **Бизнес-модулей** | **12** | + +### Статистика документации + +| Метрика | Значение | +|---------|----------| +| **Документированных модулей** | 12/12 (100%) | +| **Страниц документации** | 15 | +| **Mermaid диаграмм** | 25+ | +| **Примеров кода** | 60+ | +| **ER-диаграмм** | 12 | +| **Таблиц со статистикой** | 30+ | +| **FAQ секций** | 12 | + +## 📚 Документированные модули + +### 1. HR и Персонал (5 модулей) + +#### 1.1. Bonus (Бонусная система) +- **Контроллеры:** 5 +- **Сервисы:** 1 (BonusService - 50+ методов, 1200+ строк) +- **Actions:** 13 +- **Models:** 8 +- **Legacy:** ~15 PHP файлов (inc/bonus.php) +- **Ключевые особенности:** + - Начисление и конвертация бонусов + - Командные бонусы (teambonus) + - Персональные бонусы администраторов + - Интеграция с Telegram Bot + - Система уровней (levels) + +#### 1.2. Payroll (Зарплата) +- **Контроллеры:** 3 +- **Сервисы:** 3 +- **Models:** 10 +- **Ключевые особенности:** + - Расчет зарплаты (постоянная + переменная) + - Контроль доступа к редактированию + - История изменений + - Статистика по периодам + - Справочник значений + +#### 1.3. Timetable (Расписание) +- **Контроллеры:** 2 +- **Сервисы:** 1 +- **Actions:** 17 +- **Models:** 9 +- **Ключевые особенности:** + - STI паттерн (Plan/Fact) + - 7 типов смен + - Интеграция с чекинами + - Автоматическое создание факта из плана + - Версии v1/v3 + +#### 1.4. Rating (Рейтинг) +- **Контроллеры:** 2 +- **Сервисы:** 1 (RatingService - 612 строк) +- **Actions:** 4 +- **Models:** 3 +- **Ключевые особенности:** + - 4 типа рейтингов (админы, флористы, кустовые, стажеры) + - Автоматический расчет + - QualityRating (ручной) + - Метрики времени в статусах + - Защита от перезаписи + +#### 1.5. Grade (Грейды) +- **Контроллеры:** 2 +- **Models:** 4 +- **Ключевые особенности:** + - Система уровней (Junior → Expert) + - Связь с окладами + - Карьерный рост + - История изменений + +### 2. Обучение и Развитие (2 модуля) + +#### 2.1. Lesson (Обучение) +- **Контроллеры:** 1 +- **Сервисы:** 2 +- **Models:** 5 +- **Ключевые особенности:** + - Курсы и уроки + - Тестирование + - Прогресс обучения + - Сертификаты + +#### 2.2. Regulations (Регламенты) +- **Контроллеры:** 3 +- **Models:** 7 +- **Ключевые особенности:** + - Регламенты с опросниками + - Система тестирования + - Отслеживание прохождения + - Группировка по категориям + +### 3. Операции и Логистика (2 модуля) + +#### 3.1. Shipment (Отгрузка) +- **Контроллеры:** 2 +- **Сервисы:** 1 (ShipmentService - 3786 строк!) +- **Models:** 7+ +- **Ключевые особенности:** + - Управление заказами поставщикам + - Статусы отгрузок (15 статусов) + - Распределение товара между магазинами + - JSON-based контроль доступа + - Интеграция с 1С + +#### 3.2. WriteOffs (Списания) +- **Контроллеры:** 5 +- **Сервисы:** 1 +- **Models:** 9 +- **Ключевые особенности:** + - Учет списаний по причинам + - Интеграция с 1С + - Метрики списаний + - Влияние на рейтинг + +### 4. Коммуникации (2 модуля) + +#### 4.1. Notifications (Уведомления) +- **Контроллеры:** 1 +- **Сервисы:** 1 +- **Actions:** 3 +- **Models:** 2 +- **Ключевые особенности:** + - Rich-text уведомления + - Гибкая настройка получателей (индивид/группа/все) + - Отложенная отправка + - 3 статуса (создано/показано/прочитано) + - AJAX API + - TTL 31 день + +#### 4.2. KIK Feedback (Обратная связь) +- **Контроллеры:** 1 +- **Actions:** 16 +- **Models:** 5 +- **Ключевые особенности:** + - Kanban workflow + - 7 статусов обращений + - Категории (негатив/нейтрал/позитив) + - Метрики времени обработки + - Решения руководства + - Soft delete + - Интеграция с 1С и AmoCRM + +### 5. Аналитика (1 модуль) + +#### 5.1. Dashboard (Дашборды) +- **Контроллеры:** 5 +- **Сервисы:** 1 +- **Actions:** 13 +- **Models:** 9 +- **Ключевые особенности:** + - Динамические дашборды + - Метрики продаж + - Планы и факты + - Цветовые индикаторы + - Автоматический сбор данных + +## 🔗 Взаимосвязи модулей + +### Ключевые узлы интеграции + +**Центральные модули** (наибольшее количество связей): +1. **Rating** - 10 связей +2. **Bonus** - 7 связей +3. **Timetable** - 7 связей +4. **Dashboard** - 7 связей +5. **KIK Feedback** - 7 связей + +### Внешние интеграции + +**1С Integration:** +- Sales → Rating, Bonus +- Products → Shipment, WriteOffs +- Stores → все модули + +**Telegram Bot:** +- Checkins → Timetable +- Bonus notifications → Users + +**AmoCRM:** +- Orders → KIK Feedback +- Clients → Sales + +## 📁 Структура документации + +``` +erp24/docs/ +├── README.md # Главная страница (русский) +├── SUMMARY.md # Этот файл +├── CROSS_REFERENCE.md # Матрица взаимосвязей +│ +└── modules/ + ├── README.md # Индекс модулей + │ + ├── bonus/ + │ └── README.md # 5 контр, 1 сервис, 13 actions, 8 models + │ + ├── payroll/ + │ └── README.md # 3 контр, 3 сервиса, 10 models + │ + ├── timetable/ + │ └── README.md # 2 контр, 1 сервис, 17 actions, 9 models + │ + ├── rating/ + │ └── README.md # 2 контр, 1 сервис, 4 actions, 3 models + │ + ├── grade/ + │ └── README.md # 2 контр, 4 models + │ + ├── lesson/ + │ └── README.md # 1 контр, 2 сервиса, 5 models + │ + ├── regulations/ + │ └── README.md # 3 контр, 7 models + │ + ├── shipment/ + │ └── README.md # 2 контр, 1 сервис (3786 строк!), 7+ models + │ + ├── write-offs/ + │ └── README.md # 5 контр, 1 сервис, 9 models + │ + ├── notifications/ + │ └── README.md # 1 контр, 1 сервис, 3 actions, 2 models + │ + ├── kik-feedback/ + │ └── README.md # 1 контр, 16 actions, 5 models + │ + └── dashboard/ + └── README.md # 5 контр, 1 сервис, 13 actions, 9 models +``` + +## 🎯 Ключевые достижения + +### ✅ Полнота документации +- [x] Все 12 модулей задокументированы +- [x] Описаны контроллеры, сервисы, actions, модели +- [x] Бизнес-логика и формулы +- [x] Примеры использования +- [x] ER-диаграммы +- [x] FAQ секции +- [x] Связи между модулями + +### ✅ Качество документации +- [x] Mermaid диаграммы для визуализации +- [x] Примеры кода PHP +- [x] Описание бизнес-процессов +- [x] Статусы и workflow +- [x] Интеграции с внешними системами +- [x] Best practices и рекомендации + +### ✅ Структурированность +- [x] Единый формат для всех модулей +- [x] Логическая группировка +- [x] Перекрестные ссылки +- [x] Индексы и оглавления +- [x] Матрица взаимосвязей + +### ✅ Язык +- [x] Вся документация на русском языке +- [x] Технические термины с пояснениями +- [x] Понятные примеры + +## 📖 Как использовать документацию + +### Для разработчиков + +1. **Начало работы:** + - Читать [docs/README.md](./README.md) + - Изучить [docs/modules/README.md](./modules/README.md) + - Выбрать нужный модуль + +2. **Изучение модуля:** + - Описание и возможности + - Архитектурная диаграмма + - Контроллеры и сервисы + - Примеры кода + - ER-диаграмма БД + +3. **Понимание связей:** + - [CROSS_REFERENCE.md](./CROSS_REFERENCE.md) + - Граф зависимостей + - Ключевые точки интеграции + +### Для бизнес-аналитиков + +1. **Обзор системы:** + - [docs/README.md](./README.md) - общее описание + - Бизнес-процессы в каждом модуле + +2. **Бизнес-логика:** + - Раздел "Бизнес-логика" в каждом модуле + - Формулы и правила + - Статусы и workflow + +3. **Отчеты:** + - Dashboard модуль - метрики + - Rating модуль - KPI + - Примеры SQL запросов + +### Для технических лидов + +1. **Архитектура:** + - Mermaid диаграммы + - Матрица зависимостей + - Технологический стек + +2. **Метрики:** + - Статистика по модулям + - Сложность кода + - Покрытие функциональности + +3. **Планирование:** + - Связи между модулями + - Точки интеграции + - Рекомендации по разработке + +## 🔧 Технологический стек + +### Backend +- **Framework:** Yii2 (PHP 7.4+) +- **Database:** MySQL 5.7+ +- **Queue:** Yii2 Queue +- **Auth:** Yii2 RBAC + +### Frontend +- **JavaScript:** jQuery +- **CSS:** Bootstrap +- **Charts:** Chart.js / Google Charts + +### Интеграции +- **1С:** REST API +- **Telegram:** Bot API +- **AmoCRM:** REST API +- **SMS:** SMS.ru API + +## 📊 Метрики по модулям + +| Модуль | Контроллеров | Сервисов | Actions | Models | Строк в сервисе | +|--------|--------------|----------|---------|--------|-----------------| +| Bonus | 5 | 1 | 13 | 8 | 1,200+ | +| Payroll | 3 | 3 | - | 10 | - | +| Timetable | 2 | 1 | 17 | 9 | - | +| Rating | 2 | 1 | 4 | 3 | 612 | +| Grade | 2 | 0 | - | 4 | - | +| Lesson | 1 | 2 | - | 5 | - | +| Regulations | 3 | 0 | 5 | 7 | - | +| Shipment | 2 | 1 | - | 7+ | **3,786** | +| WriteOffs | 5 | 1 | 2 | 9 | - | +| Notifications | 1 | 1 | 3 | 2 | 50 | +| KIK Feedback | 1 | 0 | 16 | 5 | - | +| Dashboard | 5 | 1 | 13 | 9 | - | +| **ИТОГО** | **32** | **12** | **73** | **78** | **5,648+** | + +## 🎓 Паттерны и лучшие практики + +### Обнаруженные паттерны + +1. **Single Table Inheritance (STI)** + - Модуль: Timetable + - Классы: TimetablePlan, TimetableFact extends Timetable + +2. **Service Layer** + - Все бизнес-логика в сервисах + - Контроллеры - только маршрутизация + +3. **Action Classes** + - Повторно используемые действия + - Автономные компоненты + +4. **Soft Delete** + - KIK Feedback: status=7 (deleted) + - Сохранение истории + +5. **JSON-based Configuration** + - Shipment: доступ к статусам + - Гибкая настройка без миграций + +6. **Метрики времени** + - Rating: status_X_duration + - KIK: отслеживание времени в каждом статусе + +### Best Practices + +✅ **DO:** +- Использовать сервисы для бизнес-логики +- Документировать сложные формулы +- Создавать миграции для изменений БД +- Использовать транзакции +- Валидировать входные данные + +❌ **DON'T:** +- Бизнес-логика в контроллерах +- Прямые запросы в БД из views +- Хардкод значений +- Пропускать валидацию +- Игнорировать RBAC + +## 🚀 Дальнейшие шаги + +### Рекомендации по развитию документации + +1. **API документация** + - [ ] Swagger/OpenAPI для REST API + - [ ] Примеры запросов/ответов + - [ ] Коды ошибок + +2. **База данных** + - [ ] Полная схема БД + - [ ] Описание всех таблиц + - [ ] Индексы и оптимизация + +3. **Руководства** + - [ ] Installation Guide + - [ ] Developer Guide + - [ ] Deployment Guide + - [ ] Testing Guide + +4. **Справочники** + - [ ] Коды ошибок + - [ ] Troubleshooting + - [ ] Performance tuning + +5. **Диаграммы** + - [ ] Sequence диаграммы для процессов + - [ ] Class диаграммы + - [ ] Deployment диаграмма + +## 📞 Контакты и поддержка + +При вопросах по документации: +1. Проверить соответствующий раздел модуля +2. Проверить CROSS_REFERENCE.md +3. Проверить FAQ в модуле +4. Обратиться к техническому лиду проекта + +## 📜 История создания документации + +**2025-11-17:** +- ✅ Создана структура документации +- ✅ Документированы все 12 модулей +- ✅ Создана матрица взаимосвязей +- ✅ Создан главный README на русском +- ✅ Добавлены Mermaid диаграммы +- ✅ Добавлены примеры кода +- ✅ Создан итоговый SUMMARY + +**Общее время:** ~4 часа работы +**Результат:** Полная документация 12 модулей системы ERP24 + +--- + +## 🎉 Заключение + +Документация системы ERP24 **завершена** и готова к использованию! + +**Охват:** +- ✅ 12/12 модулей (100%) +- ✅ 32 контроллера +- ✅ 12 сервисов +- ✅ 73 actions +- ✅ 78 моделей + +**Качество:** +- ✅ Единый формат +- ✅ Русский язык +- ✅ Диаграммы и примеры +- ✅ Связи и зависимости + +**Полезность:** +- ✅ Для разработчиков +- ✅ Для аналитиков +- ✅ Для техлидов +- ✅ Для новых сотрудников + +Документация является живым документом и должна обновляться при изменениях в системе. + +--- + +**Версия:** 1.0 +**Последнее обновление:** 2025-11-17 +**Статус:** ✅ Завершено diff --git a/erp24/docs/api/api2/API_REFERENCE.md b/erp24/docs/api/api2/API_REFERENCE.md new file mode 100644 index 00000000..d2f1a7c8 --- /dev/null +++ b/erp24/docs/api/api2/API_REFERENCE.md @@ -0,0 +1,173 @@ +> 📖 **Язык**: Русский | [English](../API_REFERENCE.md) + +# Справочная документация API2 + +## Обзор + +Модуль API2 представляет собой RESTful API систему, построенную на фреймворке Yii2 для системы ERP. Он предоставляет комплексные эндпоинты для интеграции с маркетплейсами, управления клиентами, аутентификации и обработки заказов. + +**Базовый URL**: `/api2/` + +**Формат ответа**: JSON + +**Аутентификация**: На основе токенов (заголовок X-ACCESS-TOKEN или параметр запроса key) + +--- + +## Аутентификация + +Все эндпоинты (кроме `/auth/login`) требуют аутентификации одним из следующих способов: + +### Аутентификация через заголовок +```http +X-ACCESS-TOKEN: your-access-token +``` + +### Аутентификация через параметр запроса +```http +GET /api2/endpoint?key=your-access-token +``` + +### Эндпоинт входа + +**POST** `/auth/login` + +Аутентификация и получение токена доступа. + +**Тело запроса**: +```json +{ + "login": "username", + "password": "password" +} +``` + +**Успешный ответ** (200): +```json +{ + "access-token": "generated-token-string" +} +``` + +**Ответ с ошибкой** (200): +```json +{ + "errors": "Wrong login of password" +} +``` + +--- + +## Настройка CORS + +Все эндпоинты поддерживают CORS со следующей конфигурацией: +- **Разрешенные источники**: `*` +- **Разрешенные методы**: `GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS` +- **Разрешенные заголовки**: `*` +- **Открытые заголовки**: `x-access-token` +- **Максимальный возраст**: 86400 секунд + +--- + +## Контроллеры API + +### 1. Balance Controller +Управление остатками товаров. + +### 2. Client Controller +Комплексное управление клиентами/покупателями, включая бонусные программы, покупки и функции лояльности. + +### 3. Orders Controller +Обработка и управление заказами с маркетплейсов, изменение статусов и получение заказов. + +### 4. Marketplace Controller +Операции, специфичные для маркетплейсов, включая управление статусами и подсчет заказов. + +### 5. YandexMarket Controller +Интеграция с маркетплейсом Яндекс.Маркет для карточек товаров и синхронизации заказов. + +### 6. Delivery Controller +Сервисы доставки и аутентификации администратора. + +--- + +## Обработка ошибок + +### Стандартный формат ответа с ошибкой + +```json +{ + "error_id": 1, + "error": "Описание ошибки" +} +``` + +### Распространенные коды ошибок + +| ID ошибки | Описание | +|----------|-------------| +| 0.1 | Неверный формат параметра или отсутствует обязательный параметр | +| 1 | Отсутствует обязательный параметр | +| 1.2 | Неверный формат номера телефона | +| 2 | Запись не найдена или ошибка сохранения | +| 3 | Нарушение бизнес-логики | +| 400 | Неверное тело JSON | + +### Коды состояния HTTP + +- **200**: Успех (даже для некоторых ошибок - проверьте тело ответа) +- **400**: Неверный запрос (некорректный JSON) +- **401**: Не авторизован (отсутствует/неверный токен) +- **404**: Не найдено +- **500**: Внутренняя ошибка сервера + +--- + +## Форматы данных + +### Номера телефонов +- Должны быть числовыми +- Автоматически очищаются/нормализуются через `ClientHelper::phoneClear()` +- Проверяются с помощью `ClientHelper::phoneVerify()` + +### Даты +- Ввод: `DD.MM.YYYY` или `Y-m-d H:i:s` +- Вывод: ISO 8601 (`date('c')`) или `Y-m-d H:i:s` +- Временные метки: Unix timestamp (секунды) + +### Деньги/Цены +- Валюта: RUB (Российский рубль) +- Формат: Значения с плавающей точкой/десятичные +- Без символа валюты в ответах + +--- + +## Ограничение скорости + +Явное ограничение скорости не задокументировано. Свяжитесь с администратором API для текущих политик. + +--- + +## Версионирование API + +Текущая версия: **v2** (подразумевается путем `/api2/`) + +Отсутствует явное версионирование в эндпоинтах. Критические изменения потребуют нового базового пути. + +--- + +## Логирование + +API ведет журналы: +- Всех запросов в базу данных (`LogService::apiDataLogs()`) +- Ошибок в журналы ошибок (`LogService::apiErrorLog()`) +- Операций в журналы операций (`LogService::apiLogs()`) +- Запросов/ответов в JSON файлы (в каталоге `/json/`) + +--- + +## Следующие шаги + +- См. [ENDPOINTS.md](./ENDPOINTS.md) для подробной документации по эндпоинтам +- См. [EXAMPLES.md](./EXAMPLES.md) для примеров кода и паттернов использования +- См. [INTEGRATION_GUIDE.md](./INTEGRATION_GUIDE.md) для инструкций по интеграции diff --git a/erp24/docs/api/api2/ARCHITECTURE.md b/erp24/docs/api/api2/ARCHITECTURE.md new file mode 100644 index 00000000..261c0c5c --- /dev/null +++ b/erp24/docs/api/api2/ARCHITECTURE.md @@ -0,0 +1,392 @@ +> 📖 **Язык**: Русский | [English](../ARCHITECTURE.md) + +# Архитектура системы API2 + +## Обзор + +Модуль `erp24/api2` представляет собой REST API подсистему на основе Yii2, которая обеспечивает внешний доступ к системе ERP24. Он служит промежуточным слоем между внешними интеграциями (маркетплейсы, мобильные приложения, чат-боты) и основной базой данных ERP. + +## Технологический стек + +- **Фреймворк**: Yii2 Framework (PHP) +- **Архитектурный паттерн**: RESTful API с MVC +- **Аутентификация**: На основе токенов (заголовок/параметр запроса) +- **Формат ответа**: JSON (по умолчанию) +- **Очередь сообщений**: RabbitMQ (AMQP) +- **База данных**: Общая с основным приложением ERP (MySQL) +- **Документация API**: Поддержка Swagger + +## Компоненты системы + +### 1. Точка входа + +**Файл**: `index.php` + +- Инициализирует приложение Yii2 +- Загружает конфигурацию из `config/api2.config.php` +- Настраивает псевдонимы приложения +- Запускает веб-приложение + +```php +// Последовательность загрузки +1. Загрузка автозагрузчика Composer +2. Загрузка фреймворка Yii2 +3. Загрузка конфигурации окружения +4. Загрузка конфигурации приложения +5. Установка псевдонимов приложения +6. Запуск приложения +``` + +### 2. Слой конфигурации + +**Каталог**: `config/` + +#### Основная конфигурация (`api2.config.php`) + +Настроенные компоненты: +- **Язык**: Русский (`ru`) +- **Bootstrap**: `log`, `queue` +- **CORS**: Отключен (закомментирован) +- **URL Manager**: Включены красивые URL, REST маршрутизация +- **Аутентификация**: RBAC с хранением в базе данных +- **Asset Manager**: На основе кэша с временными метками +- **Formatter**: Московская часовая зона, русские форматы дат +- **Request/Response**: Парсинг и форматирование JSON +- **Queue**: Интеграция RabbitMQ для асинхронных задач +- **User Identity**: Модель `ApiUser` +- **Database**: Общая конфигурация с основной ERP +- **Logging**: Логирование ошибок/предупреждений в файлы +- **Cache**: Кэширование на основе файлов + +#### Конфигурация окружения (`env.php`) + +Переменные и настройки, специфичные для окружения. + +### 3. Слой контроллеров + +**Каталог**: `controllers/` + +Базовый паттерн: Все контроллеры наследуются от `BaseController`, который обеспечивает: +- Конфигурацию CORS (разрешение всех источников) +- Композитную аутентификацию (токен в заголовке + параметр запроса) +- Обработку исключений для OPTIONS запросов +- Автоматическое форматирование JSON ответов + +#### Категории контроллеров + +**Аутентификация и авторизация** +- `AuthController.php` - Вход пользователя, генерация токенов + +**Бизнес-данные** +- `BalanceController.php` - Операции с остатками +- `BonusController.php` - Управление бонусами +- `ClientController.php` - Управление клиентами +- `EmployeeController.php` - Операции с сотрудниками + +**Интеграция с маркетплейсами** +- `MarketplaceController.php` - Общие операции с маркетплейсами +- `YandexMarketController.php` - Специфичная интеграция с Яндекс.Маркет + - Создание карточек + - Синхронизация заказов + - Обновление остатков + +**Заказы и доставка** +- `OrdersController.php` - Управление заказами +- `DeliveryController.php` - Отслеживание доставки + +**Обмен данными** +- `DataController.php` - Общие операции с данными +- `DataBuhController.php` - Бухгалтерские данные +- `DataTestController.php` - Тестовые эндпоинты + +**Внешние системы** +- `TelegramController.php` - Интеграция с Telegram ботом +- `TelegramSalebotController.php` - Специфичный бот продаж +- `ChatbotActionController.php` - Действия чат-бота + +**Каталоги и товары** +- `StoreController.php` - Управление магазинами +- `UniversalCatalogController.php` - Каталог товаров +- `KikController.php` - Интеграция с системой KIK + +**Задачи и сайт** +- `TaskController.php` - RESTful API задач +- `SiteController.php` - Операции уровня сайта + +### 4. Модели данных (Records) + +**Каталог**: `records/` + +Active Record модели: +- `ApiUser.php` - Модель аутентификации пользователя API + - Реализует `IdentityInterface` + - Аутентификация на основе токенов + - Валидация пароля в открытом виде (проблема безопасности) + +- `Task.php` - Модель управления задачами + +### 5. Вспомогательные каталоги + +#### `amo_data/` +Хранение данных интеграции с AmoCRM: +- `token_info.json` - Информация об OAuth токене + +#### `json/` +Логирование и отладка запросов/ответов: +- Журналы запросов с временными метками +- Журналы ошибок +- Отслеживание измененных заказов +- Журналы загрузок + +#### `runtime/` +Временные файлы выполнения (кэш, логи и т.д.) + +#### `swagger/` +Документация API (спецификация Swagger/OpenAPI) + +## Процесс аутентификации + +```mermaid +sequenceDiagram + participant Client + participant AuthController + participant ApiUser + participant Database + + Client->>AuthController: POST /auth (login, password) + AuthController->>ApiUser: findByLogin(login) + ApiUser->>Database: SELECT * FROM api_user + Database-->>ApiUser: User record + ApiUser->>ApiUser: validatePassword(password) + ApiUser->>ApiUser: generateAccessToken() + ApiUser->>Database: UPDATE access_token + ApiUser-->>AuthController: access_token + AuthController-->>Client: JSON ответ с токеном +``` + +## Поток запросов + +```mermaid +sequenceDiagram + participant Client + participant BaseController + participant Controller + participant Model + participant Database + + Client->>BaseController: HTTP запрос + BaseController->>BaseController: CORS Preflight проверка + BaseController->>BaseController: Аутентификация по токену + BaseController->>Controller: Аутентифицированный запрос + Controller->>Model: Бизнес-логика + Model->>Database: Запрос + Database-->>Model: Результат + Model-->>Controller: Данные + Controller-->>BaseController: Данные ответа + BaseController-->>Client: JSON ответ +``` + +## Точки интеграции + +### 1. Основное приложение ERP +- **База данных**: Общее подключение к базе данных +- **Модели**: Использует модели из пространства имен `yii_app\records` +- **Конфигурация**: Общий `params.php` +- **Vendor**: Общие зависимости Composer + +### 2. Внешние сервисы + +**Яндекс.Маркет** +- Интеграция с OpenAPI Client +- Управление кампаниями +- Синхронизация заказов +- Управление остатками +- Создание карточек товаров + +**RabbitMQ** +- Очередь сообщений Telegram +- Exchange: `telegram-exchange` +- Queue: `telegram-queue` +- TTR: 600 секунд +- Попытки повтора: 3 + +**AmoCRM** +- OAuth на основе токенов +- Синхронизация данных + +### 3. Мобильные/Веб приложения +- RESTful эндпоинты +- Аутентификация по токенам +- JSON ответы + +## Соображения безопасности + +### Текущая реализация + +**Сильные стороны**: +- Аутентификация на основе токенов +- Конфигурация CORS +- Фреймворк авторизации RBAC +- Логирование запросов +- Сессии отключены (stateless) + +**Слабые стороны** (выявленные): +- Сравнение паролей в открытом виде +- Отсутствие хэширования паролей +- CORS разрешает все источники +- Не видно ограничения скорости +- Токен хранится в базе данных без шифрования + +### Рекомендуемые улучшения +1. Внедрить хэширование паролей (bcrypt/argon2) +2. Ограничить CORS определенными источниками +3. Добавить middleware ограничения скорости +4. Внедрить истечение токенов +5. Добавить валидацию запросов +6. Включить только HTTPS +7. Добавить версионирование API + +## Архитектура очереди сообщений + +``` +Client → API Controller → Queue Push → RabbitMQ + ↓ + Queue Worker + ↓ + Фоновая задача + ↓ + База данных +``` + +**Конфигурация**: +- DSN: `amqp://admin:3qqHK2MRgGgxUdVT61@RABBIT_HOST:5672` +- Queue: `telegram-queue` +- Exchange: `telegram-exchange` +- Поведение: Логируется через `LogBehavior` + +## URL маршрутизация + +### RESTful маршруты + +```php +// Явные маршруты +'auth' => 'auth/login' +'delivery/admin-auth' => 'delivery/admin-auth' +'POST data-buh/request/' => 'data-buh/request' + +// RESTful ресурс +['class' => 'yii\rest\UrlRule', 'controller' => ['task']] +``` + +### RESTful эндпоинты задач +- `GET /task` - Список всех задач +- `GET /task/:id` - Получить конкретную задачу +- `POST /task` - Создать задачу +- `PUT /task/:id` - Обновить задачу +- `DELETE /task/:id` - Удалить задачу + +## Соображения производительности + +### Кэширование +- Включено кэширование на основе файлов +- Asset manager с временными метками +- Оптимизировано форматирование ответов + +### База данных +- Общий пул соединений с основным приложением +- ORM Active Record +- Доступна жадная загрузка + +### Асинхронная обработка +- RabbitMQ для тяжелых операций +- Лимит времени выполнения 600 секунд +- 3 попытки повтора + +## Архитектура развертывания + +``` +[Клиентские приложения] → [Load Balancer] → [Экземпляр API2] → [База данных] + ↓ + [RabbitMQ] + ↓ + [Queue Workers] +``` + +## Мониторинг и логирование + +### Конфигурация логов + +**Цели**: +- Логирование на основе файлов +- Уровни: `error`, `warning` +- Уровень трассировки: 3 + +**Исключенные HTTP исключения**: +- 401 Unauthorized +- 403 Forbidden +- 404 Not Found +- 405 Method Not Allowed + +### Логирование запросов + +JSON файлы, хранящиеся в каталоге `json/`: +- Полезная нагрузка запросов +- Данные ответов +- Детали ошибок +- Временные метки + +## Разработка vs Продакшен + +### Режим разработки +- `YII_DEBUG = true` +- `YII_ENV = 'dev'` +- Красивое форматирование JSON ответов +- Подробные сообщения об ошибках + +### Режим продакшена (рекомендуется) +- Отключить режим отладки +- Использовать продакшен конфигурацию +- Минимизировать раскрытие ошибок +- Включить сжатие ответов + +## Версионирование API + +**Текущее состояние**: Версионирование не реализовано + +**Рекомендация**: Внедрить версионирование на основе URL +``` +/api/v1/task +/api/v2/task +``` + +## Зависимости + +### Внешние библиотеки +- `yii2/framework` - Основной фреймворк +- `yii2-queue` - Компонент очереди +- `yii2-rbac` - Авторизация +- Yandex Market OpenAPI Client +- GuzzleHTTP - HTTP клиент + +### Внутренние зависимости +- Модели основной ERP (`yii_app\records`) +- Общая конфигурация базы данных +- Общие параметры +- MarketplaceService + +## Будущие улучшения + +1. **Версионирование API** - Внедрить контроль версий +2. **Ограничение скорости** - Предотвращение злоупотреблений +3. **Слой кэширования** - Интеграция с Redis +4. **GraphQL** - Альтернатива REST +5. **WebSocket** - Обновления в реальном времени +6. **OAuth2** - Стандартная аутентификация +7. **API Gateway** - Централизованное управление +8. **Микросервисы** - Разделение сервисов +9. **Документация** - Автогенерация из кода +10. **Тестирование** - Модульные и интеграционные тесты + +## Заключение + +Модуль API2 обеспечивает прочную основу для внешних интеграций с правильным разделением ответственности, RESTful дизайном и расширяемостью. Однако перед развертыванием в продакшене следует реализовать улучшения безопасности и современные лучшие практики. diff --git a/erp24/docs/api/api2/DEPENDENCIES.md b/erp24/docs/api/api2/DEPENDENCIES.md new file mode 100644 index 00000000..e7d17268 --- /dev/null +++ b/erp24/docs/api/api2/DEPENDENCIES.md @@ -0,0 +1,630 @@ +# Зависимости и интеграции API2 + +## Обзор + +Данный документ отображает все зависимости, интеграции и внешние подключения для модуля API2. + +## Категории зависимостей + +1. Зависимости от фреймворка +2. Внутренние зависимости ERP +3. Зависимости от внешних сервисов +4. Зависимости от базы данных +5. Зависимости от библиотек + +--- + +## 1. Зависимости от фреймворка + +### Ядро фреймворка Yii2 + +**Пространство имён**: `yii\*` + +**Используемые компоненты**: + +| Компонент | Класс | Назначение | +|-----------|-------|---------| +| Web Application | `yii\web\Application` | Основной контейнер приложения | +| REST Controller | `yii\rest\Controller` | Базовый контроллер REST API | +| Active Record | `yii\db\ActiveRecord` | ORM для работы с БД | +| RBAC | `yii\rbac\DbManager` | Управление доступом на основе ролей | +| Queue | `yii\queue\amqp_interop\Queue` | Интеграция очереди сообщений | +| Filters | `yii\filters\Cors` | Обработка CORS | +| Filters | `yii\filters\auth\*` | Фильтры аутентификации | +| Response | `yii\web\Response` | Форматирование HTTP-ответов | +| Request | `yii\web\Request` | Парсинг HTTP-запросов | +| Logging | `yii\log\FileTarget` | Логирование в файлы | +| Caching | `yii\caching\FileCache` | Файловый кеш | + +**Установка**: Через Composer + +```json +{ + "require": { + "yiisoft/yii2": "~2.0", + "yiisoft/yii2-queue": "*" + } +} +``` + +### Расширения Yii2 Queue + +**Пакет**: `yii2-queue` + +**Интеграция**: Брокер сообщений RabbitMQ + +**Конфигурация**: +```php +'queue' => [ + 'class' => Queue::class, + 'dsn' => 'amqp://admin:3qqHK2MRgGgxUdVT61@RABBIT_HOST:5672', + 'queueName' => 'telegram-queue', + 'exchangeName' => 'telegram-exchange' +] +``` + +--- + +## 2. Внутренние зависимости ERP + +### Модели основного приложения + +**Пространство имён**: `yii_app\records` + +**Используемые общие модели**: + +| Модель | Таблица | Использование в контроллерах | +|-------|-------|------------------| +| `MarketplaceOrders` | `marketplace_orders` | MarketplaceController, YandexMarketController | +| `MarketplaceStatus` | `marketplace_status` | MarketplaceController | +| `MarketplaceOrder1cStatuses` | `marketplace_order_1c_statuses` | MarketplaceController | +| `MarketplaceOrderDelivery` | `marketplace_order_delivery` | YandexMarketController | +| `MarketplaceOrderItems` | `marketplace_order_items` | YandexMarketController | +| `MarketplaceOrderStatusHistory` | `marketplace_order_status_history` | YandexMarketController | +| `MarketplaceOrderStatusTypes` | `marketplace_order_status_types` | YandexMarketController | +| `MarketplaceStore` | `marketplace_store` | YandexMarketController | +| `ExportImportTable` | `export_import_table` | MarketplaceController | +| `MatrixErp` | `matrix_erp` | YandexMarketController | +| `Products1c` | `products_1c` | YandexMarketController | + +**Тип зависимости**: Жёсткая зависимость от схемы основной БД ERP + +**Расположение**: `erp24/records/` (родительская директория) + +### Сервисы основного приложения + +**Пространство имён**: `yii_app\services` + +**Используемые сервисы**: + +| Сервис | Назначение | Используется в | +|---------|---------|---------| +| `MarketplaceService` | Бизнес-логика маркетплейсов | YandexMarketController | + +**Методы**: +- `getMarketplaceProducts()` - Получение товаров для маркетплейса +- `fetchOrders()` - Получение заказов из API Яндекс.Маркета +- `processOrders()` - Обработка и синхронизация заказов в БД + +### Общая конфигурация + +**Конфигурация БД**: `erp24/config/db.php` + +```php +'db' => require __DIR__ . '/../../config/db.php' +``` + +**Параметры**: `erp24/config/params.php` + +```php +'params' => require dirname(__DIR__, 2) . '/config/params.php' +``` + +**Используемые общие параметры**: +- `YANDEX_MARKET_API_KEY` - Аутентификация в API Яндекс.Маркета + +### Псевдонимы приложения + +```php +Yii::setAlias('@yii_app', dirname(__DIR__)); +``` + +**Предоставляет**: +- Доступ к моделям основного приложения +- Доступ к сервисам основного приложения +- Загрузку общих ресурсов + +--- + +## 3. Зависимости от внешних сервисов + +### API Яндекс.Маркета + +**Тип интеграции**: RESTful API + +**Библиотека**: `OpenAPI\Client` (PHP SDK для Яндекс.Маркета) + +**Компоненты**: + +| Компонент | Назначение | +|-----------|---------| +| `OpenAPI\Client\Configuration` | Конфигурация API | +| `OpenAPI\Client\Api\BusinessOfferMappingsApi` | Управление товарами | +| `OpenAPI\Client\Api\CampaignsApi` | Операции с кампаниями | +| `OpenAPI\Client\Api\CategoriesApi` | Управление категориями | +| `OpenAPI\Client\Api\StocksApi` | Обновление остатков | +| `OpenAPI\Client\Api\HiddenOffersApi` | Видимость товаров | +| `OpenAPI\Client\Model\*` | Модели данных | +| `OpenAPI\Client\ObjectSerializer` | Сериализация | + +**Аутентификация**: API Key + +```php +$config = Configuration::getDefaultConfiguration() + ->setApiKey('Api-Key', Yii::$app->params['YANDEX_MARKET_API_KEY']); +``` + +**HTTP-клиент**: GuzzleHTTP + +```php +$apiInstance = new Api\BusinessOfferMappingsApi( + new GuzzleHttp\Client(), + $config +); +``` + +**Используемые эндпоинты**: +- `getCampaigns()` - Список кампаний +- `getCategoriesTree()` - Иерархия категорий +- `updateoffermappings()` - Обновление карточек товаров +- `addHiddenOffers()` - Скрытие товаров +- `updateStocks()` - Обновление остатков +- Получение заказов по дате/статусу + +**Campaign ID**: `109969229` (жёстко закодирован) +**Business ID**: `5330887` (жёстко закодирован) + +### Брокер сообщений RabbitMQ + +**Протокол**: AMQP + +**Подключение**: +``` +Host: Переменная окружения RABBIT_HOST (по умолчанию: localhost) +Port: 5672 +User: admin +Password: 3qqHK2MRgGgxUdVT61 +``` + +**Конфигурация**: +- Имя очереди: `telegram-queue` +- Exchange: `telegram-exchange` +- TTR: 600 секунд +- Попытки повтора: 3 + +**Использование**: +- Обработка сообщений Telegram-бота +- Асинхронное выполнение задач +- Обработка фоновых задач + +**Мониторинг**: `yii\queue\LogBehavior` для логирования + +### AmoCRM + +**Тип интеграции**: OAuth 2.0 + +**Хранение данных**: `amo_data/token_info.json` + +**Структура токена**: +```json +{ + "access_token": "...", + "refresh_token": "...", + "expires_in": 86400, + "created_at": timestamp +} +``` + +**Назначение**: Синхронизация данных CRM + +### API Telegram Bot + +**Контроллеры**: +- `TelegramController` +- `TelegramSalebotController` + +**Метод интеграции**: Webhook или polling + +**Обработка сообщений**: Через очередь RabbitMQ + +**Назначение**: +- Коммуникация с клиентами +- Уведомления о заказах +- Автоматизация продаж + +--- + +## 4. Зависимости от базы данных + +### Подключение к БД + +**Конфигурация**: Наследуется от основной ERP + +**Файл**: `erp24/config/db.php` + +**Ожидаемая структура**: +```php +return [ + 'class' => 'yii\db\Connection', + 'dsn' => 'mysql:host=localhost;dbname=erp24', + 'username' => 'user', + 'password' => 'password', + 'charset' => 'utf8', +]; +``` + +### Используемые таблицы БД + +**Таблицы, специфичные для API2**: + +1. **api_user** + - Поля: `id`, `login`, `password`, `access_token` + - Назначение: Аутентификация API + +2. **task** (предполагается) + - Назначение: Управление задачами через REST API + +**Общие таблицы ERP** (через модели yii_app): + +| Таблица | Назначение | Модель | +|-------|---------|-------| +| `marketplace_orders` | Заказы с маркетплейсов | MarketplaceOrders | +| `marketplace_status` | Статусы заказов | MarketplaceStatus | +| `marketplace_order_1c_statuses` | Сопоставление статусов 1C | MarketplaceOrder1cStatuses | +| `marketplace_order_delivery` | Информация о доставке | MarketplaceOrderDelivery | +| `marketplace_order_items` | Позиции заказов | MarketplaceOrderItems | +| `marketplace_order_status_history` | История изменения статусов | MarketplaceOrderStatusHistory | +| `marketplace_order_status_types` | Типы статусов | MarketplaceOrderStatusTypes | +| `marketplace_store` | Магазины маркетплейсов | MarketplaceStore | +| `export_import_table` | Сопоставление синхронизации данных | ExportImportTable | +| `matrix_erp` | Матрица товаров | MatrixErp | +| `products_1c` | Товары из 1C | Products1c | +| `rbac_*` | Таблицы RBAC | (Yii2 RBAC) | + +### Зависимости схемы БД + +**Связи**: +``` +MarketplaceOrders + ├── marketplace_id → MarketplaceStore + ├── status_id → MarketplaceStatus + ├── status_1c → MarketplaceOrder1cStatuses + └── items → MarketplaceOrderItems + +MarketplaceOrderItems + └── product_id → Products1c → MatrixErp + +ExportImportTable + ├── entity_id → CityStore (предполагается) + └── export_val → Сопоставление GUID +``` + +**Ограничения внешних ключей**: Предполагаются (не видны в коде API) + +--- + +## 5. Зависимости от библиотек + +### PHP-библиотеки (Composer) + +**Требуемые пакеты**: + +| Пакет | Назначение | Версия | +|---------|---------|---------| +| `yiisoft/yii2` | Ядро фреймворка | ~2.0 | +| `yiisoft/yii2-queue` | Компонент очередей | * | +| `guzzlehttp/guzzle` | HTTP-клиент | * | +| `yandex-market/*` | SDK для Яндекс.Маркета | * | +| `bower-asset/*` | Frontend-ассеты | * | +| `npm-asset/*` | NPM-ассеты | * | + +**Автозагрузка**: PSR-4 через Composer + +```php +require __DIR__ . '/../vendor/autoload.php'; +``` + +### Зависимости ассетов + +**Псевдонимы**: +```php +'@bower' => '@vendor/bower-asset' +'@npm' => '@vendor/npm-asset' +``` + +**Управление**: Yii2 Asset Manager + +**Хранение**: `@app/web/cache/assets` + +--- + +## Диаграммы потоков интеграции + +### Синхронизация заказов Яндекс.Маркета + +```mermaid +graph LR + A[Яндекс.Маркет] -->|API-вызов| B[YandexMarketController] + B -->|OpenAPI Client| C[Configuration] + C -->|GuzzleHTTP| D[Yandex API] + D -->|Данные заказов| E[MarketplaceService] + E -->|Обработка| F[Модель MarketplaceOrders] + F -->|Сохранение| G[База данных] + E -->|Товары| H[Модель Products1c] + H -->|Цена/Остатки| I[Модель MatrixErp] +``` + +### Очередь сообщений Telegram + +```mermaid +graph LR + A[Telegram Bot] -->|Webhook| B[TelegramController] + B -->|Отправка| C[Очередь RabbitMQ] + C -->|Worker| D[Фоновый процесс] + D -->|Обработка| E[Бизнес-логика] + E -->|Обновление| F[База данных] + E -->|Ответ| G[Telegram API] +``` + +### Поток аутентификации + +```mermaid +graph LR + A[Клиент] -->|POST /auth| B[AuthController] + B -->|Запрос| C[Модель ApiUser] + C -->|SELECT| D[Таблица api_user] + D -->|Данные пользователя| C + C -->|Проверка| E[Проверка пароля] + E -->|Генерация| F[Access Token] + F -->|UPDATE| D + F -->|Возврат| A +``` + +--- + +## Зависимости от переменных окружения + +### Требуемые переменные окружения + +```bash +# RabbitMQ +RABBIT_HOST=localhost + +# Яндекс.Маркет (через params.php) +YANDEX_MARKET_API_KEY=your_api_key + +# База данных (через db.php) +DB_HOST=localhost +DB_NAME=erp24 +DB_USER=user +DB_PASSWORD=password + +# Приложение +YII_DEBUG=true +YII_ENV=dev +``` + +--- + +## Точки внедрения зависимостей + +### Внедрение конфигурации + +**index.php**: +```php +$config = require __DIR__.'/config/api2.config.php'; +$application = new yii\web\Application($config); +``` + +### Паттерн Service Locator + +**Компоненты** (через Yii::$app): +```php +Yii::$app->db // Подключение к БД +Yii::$app->user // Компонент пользователя +Yii::$app->request // HTTP-запрос +Yii::$app->response // HTTP-ответ +Yii::$app->log // Логгер +Yii::$app->cache // Кеш +Yii::$app->queue // Очередь сообщений +Yii::$app->authManager // Менеджер RBAC +Yii::$app->security // Хелпер безопасности +``` + +--- + +## Интеграция сторонних сервисов + +### Сервисы, требующие API-ключи + +1. **Яндекс.Маркет** + - Тип ключа: API Key + - Хранение: `params.php` + - Безопасность: ⚠️ Файловое хранение (не переменная окружения) + +2. **AmoCRM** + - Тип ключа: OAuth 2.0 + - Хранение: `amo_data/token_info.json` + - Обновление: Требуется + +### Сервисы, требующие учётные данные + +1. **RabbitMQ** + - Имя пользователя: `admin` + - Пароль: `3qqHK2MRgGgxUdVT61` + - Безопасность: ⚠️ Жёстко закодировано в конфигурации + +2. **База данных** + - Учётные данные: В `db.php` + - Безопасность: ✅ Отдельный файл конфигурации + +--- + +## Проблемы безопасности зависимостей + +### Высокий риск + +1. **Пароль RabbitMQ**: Жёстко закодирован в файле конфигурации +2. **API-ключ Яндекс.Маркета**: Хранится в params.php +3. **Учётные данные БД**: В системе контроля версий +4. **Пароли пользователей API**: Не хешируются + +### Рекомендации + +1. **Использовать переменные окружения**: Перенести все учётные данные в `.env` +2. **Внедрить управление секретами**: Использовать HashiCorp Vault или аналог +3. **Ротация учётных данных**: Внедрить регулярную ротацию +4. **Шифрование токенов**: Шифровать сохранённые OAuth-токены +5. **Хеширование паролей**: Использовать bcrypt/argon2 для паролей пользователей + +--- + +## Управление версиями зависимостей + +### Composer + +**Файл**: `composer.json` (в родительской директории) + +**Lock-файл**: `composer.lock` + +**Стратегия обновления**: +```bash +composer update # Обновить все +composer update yiisoft/yii2 # Обновить конкретный пакет +``` + +### Ограничения версий + +**Текущее**: Вероятно используется `~2.0` для Yii2 + +**Рекомендация**: Использовать ограничения семантического версионирования +```json +{ + "require": { + "yiisoft/yii2": "^2.0.43", + "yiisoft/yii2-queue": "^2.3" + } +} +``` + +--- + +## Риски критических изменений + +### Зависимости высокого риска + +1. **Фреймворк Yii2**: Изменения основной версии +2. **API Яндекс.Маркета**: Обновления версии API +3. **Протокол RabbitMQ**: Изменения версии AMQP +4. **Версия PHP**: Обновления версии языка + +### Стратегии снижения рисков + +1. **Закрепление версий**: Фиксация основных версий +2. **Тестирование**: Комплексный набор тестов +3. **Мониторинг**: Отслеживание устаревших функций +4. **Документация**: Отслеживание изменений API + +--- + +## Граф зависимостей + +``` +Модуль API2 +├── Фреймворк Yii2 +│ ├── yii\web\Application +│ ├── yii\rest\Controller +│ ├── yii\db\ActiveRecord +│ └── yii\queue\* +├── Основное приложение ERP +│ ├── yii_app\records\* +│ ├── yii_app\services\* +│ └── config\* +├── Внешние сервисы +│ ├── API Яндекс.Маркета +│ ├── RabbitMQ +│ ├── AmoCRM +│ └── API Telegram +├── База данных +│ ├── api_user +│ ├── marketplace_* +│ └── products_* +└── Библиотеки + ├── GuzzleHTTP + ├── OpenAPI Client + └── Пакеты Composer +``` + +--- + +## Циклические зависимости + +**Текущее состояние**: Не обнаружено + +**Потенциальный риск**: Зависимость основного приложения от моделей API2 + +**Предотвращение**: Оставить API2 в роли потребителя, а не поставщика + +--- + +## Мониторинг зависимостей + +### Проверки работоспособности + +1. **Подключение к БД**: Проверка соединения +2. **RabbitMQ**: Мониторинг глубины очереди +3. **API Яндекс.Маркета**: Мониторинг лимитов запросов +4. **Истечение токенов**: Обновление OAuth-токенов + +### Логирование + +**Логируемые компоненты**: +- Запросы к БД +- API-вызовы +- Операции с очередями +- Попытки аутентификации + +**Расположение логов**: `runtime/logs/` + +--- + +## Резюме + +### Общее количество зависимостей + +- **Фреймворк**: 1 (Yii2) +- **Модели основного приложения**: 11+ +- **Сервисы основного приложения**: 1+ +- **Внешние API**: 3 (Яндекс, Telegram, AmoCRM) +- **Инфраструктура**: 2 (БД, RabbitMQ) +- **Библиотеки**: 5+ (пакеты Composer) + +### Состояние зависимостей + +- **Стабильно**: ✅ Фреймворк Yii2 +- **Умеренный риск**: ⚠️ Внешние API (версионирование) +- **Высокий риск**: ❌ Управление учётными данными +- **Критично**: 🔴 Реализация хеширования паролей + +### Рекомендуемые действия + +1. Внедрить управление переменными окружения +2. Добавить ограничения версий зависимостей +3. Создать эндпоинты проверки работоспособности +4. Внедрить ротацию учётных данных +5. Добавить управление версиями API +6. Создать политику обновления зависимостей + +--- + +[English version](../DEPENDENCIES.md) | **Русская версия** diff --git a/erp24/docs/api/api2/ENDPOINTS.md b/erp24/docs/api/api2/ENDPOINTS.md new file mode 100644 index 00000000..b7e1dd09 --- /dev/null +++ b/erp24/docs/api/api2/ENDPOINTS.md @@ -0,0 +1,842 @@ +# Каталог эндпоинтов API2 + +> 📖 **Язык**: Русский | [English](../ENDPOINTS.md) + +Полный каталог всех доступных эндпоинтов API с параметрами, ответами и примечаниями по использованию. + +--- + +## Эндпоинты аутентификации + +### POST `/auth/login` +Аутентификация пользователя и получение токена доступа. + +**Аутентификация**: Не требуется + +**Запрос**: +```json +{ + "login": "string", + "password": "string" +} +``` + +**Ответ**: +```json +{ + "access-token": "string" +} +``` + +**Ошибки**: +```json +{ + "errors": "Wrong login of password" +} +``` + +--- + +## Эндпоинты остатков + +### POST `/balance/get` +Получение остатков товаров для магазина. + +**Запрос**: +```json +{ + "store_id": "store-guid" +} +``` + +**Ответ**: +```json +[ + { + "product_id": "guid", + "quantity": 15.0, + "reserv": 2.0 + } +] +``` + +**Ошибки**: +- `400`: Некорректный JSON +- `store_id is required`: Отсутствует параметр + +### GET `/balance/test` +Тестовый эндпоинт для проверки работы контроллера остатков. + +**Ответ**: +```json +["ok"] +``` + +--- + +## Эндпоинты клиентов + +### POST `/client/add` +Добавление или обновление клиента в системе. + +**Запрос**: +```json +{ + "phone": "79001234567", + "name": "John Doe", + "client_id": 123, + "client_type": 1, + "platform_id": 1, + "avatar": "url", + "full_name": "John Smith Doe", + "messenger": "telegram", + "message_id": "msg123", + "date_of_creation": "timestamp" +} +``` + +**Ответ**: +```json +{ + "result": true, + "result_edit": "...", + "editDates": true +} +``` + +**Ошибки**: +```json +{ + "error_id": 1, + "error": "phone is required" +} +``` + +### POST `/client/balance` +Получение бонусного баланса клиента и ключевого кода. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "balance": 500, + "keycode": "1234", + "editDates": true +} +``` + +### POST `/client/get` +Получение информации о мессенджере клиента. + +**Запрос**: +```json +{ + "phone": "79001234567", + "client_type": "1" +} +``` + +**Ответ**: +```json +{ + "client_id": 123, + "platform_id": 456 +} +``` + +**Ошибки**: +- `error_id: 2`: Нет клиента с таким телефоном и client_type +- `error_id: 3`: Клиент отписался от рассылки + +### POST `/client/event-edit` +Добавление или обновление памятных дат/событий для клиента. + +**Запрос**: +```json +{ + "phone": "79001234567", + "channel": "salebot", + "events": [ + { + "number": 1, + "date": "25.12.2024", + "tip": "День рождения" + } + ] +} +``` + +Или одно событие: +```json +{ + "phone": "79001234567", + "number": 1, + "date": "25.12.2024", + "tip": "День рождения" +} +``` + +**Ответ**: +```json +{ + "response": true +} +``` + +**Бонус**: Автоматически начисляется 300 бонусных баллов при добавлении 5 памятных дат. + +### POST `/client/show-keycode` +Отправка QR-кода с ключевым кодом клиенту через мессенджер. + +**Запрос**: +```json +{ + "phone": "79001234567", + "platform_id": 123 +} +``` + +**Ответ**: Перенаправлен из сервиса telegram + +### POST `/client/store-geo` +Отправка местоположений магазинов клиенту на основе геолокации. + +**Запрос**: +```json +{ + "location": "55.7558,37.6173", + "platform_id": 123 +} +``` + +**Ответ**: Перенаправлен из сервиса telegram + +### POST `/client/check-details` +Получение постраничного списка истории покупок клиента. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "response": { + "checks": [ + { + "id": 123, + "store": { + "id": "store-guid", + "name": "Store Name" + }, + "number": "CHECK-001", + "payment": [ + {"type": "cash"}, + {"type": "card"} + ], + "date": "2024-11-13T10:30:00+03:00", + "sum": 1500, + "discount": 150, + "order_id": "order-guid", + "seller_id": "seller-guid", + "products": [ + { + "product_id": "prod-guid", + "quantity": 2.0, + "price": 750.0, + "discount": 75.0 + } + ], + "bonuses": [ + { + "name": "Bonus Name", + "amount": 50 + } + ] + } + ], + "pages": { + "totalCount": 100, + "page": 0, + "per-page": 20 + } + } +} +``` + +### POST `/client/check-detail` +Получение деталей одного чека по ID. + +**Запрос**: +```json +{ + "check_id": 123 +} +``` + +**Ответ**: Тот же формат, что и один чек из `/check-details` + +### POST `/client/bonus-write-off` +Получение постраничного списка списаний бонусов. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "response": { + "bonuses": [ + { + "name": "Bonus description", + "amount": -100, + "check_id": "check-guid", + "date": "2024-11-13T10:30:00+03:00" + } + ], + "pages": { + "totalCount": 50, + "page": 0, + "per-page": 20 + } + } +} +``` + +### POST `/client/use-bonuses` +Списание бонусных баллов со счета клиента. + +**Запрос**: +```json +{ + "order_id": "order-123", + "phone": "79001234567", + "points_to_use": 100, + "date": 1699876543, + "price": 1500 +} +``` + +**Ответ**: +```json +{ + "response": { + "code": 200, + "status": "success", + "data": { + "client_id": 456, + "order_id": "order-123", + "addedPoints": 100, + "remainingPoints": 400 + } + } +} +``` + +### POST `/client/add-bonus` +Начисление бонусных баллов на счет клиента. + +**Запрос**: +```json +{ + "order_id": "order-123", + "phone": "79001234567", + "points_to_add": 50, + "date": 1699876543, + "price": 1500 +} +``` + +**Ответ**: +```json +{ + "response": { + "code": 200, + "status": "success", + "data": { + "phone": "79001234567", + "order_id": "order-123", + "addedPoints": 50, + "totalPoints": 550 + } + } +} +``` + +### POST `/client/bonus-status` +Получение бонусного уровня и статуса клиента. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "response": { + "phone": "79001234567", + "alias": "gold", + "bonus_level": "Золотой", + "current_points": 5000, + "next_points": 10000, + "discount_percent": 15, + "cashback_rate": 10 + } +} +``` + +### POST `/client/memorable-dates` +Получение памятных дат клиента. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "response": [ + { + "date": "25.12.2024", + "number": 1, + "tip": "День рождения" + } + ] +} +``` + +### POST `/client/social-ids` +Получение ID клиента на социальных платформах. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "response": [ + { + "platform": "telegram", + "user_id": 123456789 + } + ] +} +``` + +### POST `/client/get-info` +Получение комплексной информации о клиенте. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` +Или: +```json +{ + "ref_code": "ABC123XYZ" +} +``` + +**Ответ**: +```json +{ + "response": { + "id": 123, + "card": "12345678", + "name": "John Doe", + "first_name": "John", + "second_name": "Doe", + "sex": "male", + "email": "john@example.com", + "birth_day": "1990-01-15", + "comment": "VIP customer", + "keycode": "1234", + "ref_code": "ABC123XYZ", + "referral_id": null, + "balance": 500, + "burn_balans": 0, + "bonus_level": "gold", + "total_price": 15000, + "total_price_rejected": 500, + "referral_count_get_bonus_already": 3, + "referral_count_all": 5, + "editDates": true, + "birth_day_readonly": true, + "events_readonly": false, + "events": [...] + } +} +``` + +### GET `/client/get-stores` +Получение списка всех активных магазинов. + +**Ответ**: +```json +{ + "response": [ + { + "id": 1, + "name": "Store Name" + } + ] +} +``` + +### POST `/client/phone-keycode-by-card` +Получение телефона и ключевого кода по номеру карты. + +**Запрос**: +```json +{ + "card": "12345678" +} +``` + +**Ответ**: +```json +{ + "response": { + "phone": "79001234567", + "keycode": "1234" + } +} +``` + +### POST `/client/get-user-info` +Получение детальной статистики пользователя и информации о покупках. + +**Запрос**: +```json +{ + "phone": "79001234567" +} +``` + +**Ответ**: +```json +{ + "response": { + "name": "John Doe", + "sex": "male", + "sale_avg_price": 1500, + "total_price": 15000, + "registration_date": "2024-01-01 10:00:00", + "bonus_balance": 500, + "bonus_minus": 100, + "date_first_sale": "2024-01-15 12:30:00", + "date_last_sale": "2024-11-10 15:45:00", + "sale_cnt": 10, + "total_price_rejected": 500, + "events": [...], + "platform": { + "telegram": { + "is_subscribed": 1, + "created_at": "2024-01-01 10:00:00" + } + } + } +} +``` + +### POST `/client/change-user-subscription` +Обновление статуса подписки пользователя в telegram. + +**Запрос**: +```json +{ + "phone": "79001234567", + "telegram_is_subscribed": 1 +} +``` + +**Ответ**: +```json +{ + "response": true +} +``` + +### POST `/client/apply-promo-code` +Применение промокода к аккаунту пользователя. + +**Запрос**: +```json +{ + "phone": "79001234567", + "code": "PROMO2024" +} +``` + +**Ответ**: +```json +{ + "response": ["ok"] +} +``` + +**Ошибки**: +- `error_id: 2`: Промокод истек или неизвестен +- `error_id: 3`: Промокод уже использован + +--- + +## Эндпоинты заказов + +### POST `/orders/change-status` +Обновление статуса заказа маркетплейса из 1C. + +**Запрос**: +```json +{ + "order": [ + { + "order_id": "order-guid", + "status": "NEW", + "seller_id": "seller-guid" + } + ], + "status_update": {} +} +``` + +**Ответ**: +```json +[ + { + "order_id": "order-guid", + "result": true, + "message": "Статус обновлён", + "status": 1 + } +] +``` + +**Ошибки**: +```json +{ + "order_id": "order-guid", + "result": "error", + "message": "Заказ не найден" +} +``` + +### POST `/orders/get-orders` +Получение заказов маркетплейса для магазина (за последние 24 часа). + +**Запрос**: +```json +{ + "store_id": "store-guid" +} +``` + +**Ответ**: +```json +{ + "success": true, + "result": [ + { + "order_id": "order-guid", + "status": "NEW", + "items": [ + { + "product_id": "product-guid", + "quantity": 2, + "price": 750.0 + } + ] + } + ] +} +``` + +--- + +## Эндпоинты маркетплейса + +### POST `/marketplace/statuses` +Получение всех статусов маркетплейса. + +**Ответ**: +```json +{ + "response": [ + { + "id": 1, + "code": "NEW", + "name": "Новый заказ" + } + ] +} +``` + +### POST `/marketplace/get-new-order-count` +Получение количества новых заказов для магазина (за последние 3 дня). + +**Запрос**: +```json +{ + "store_guid": "store-guid" +} +``` + +**Ответ**: +```json +{ + "response": 15 +} +``` + +**Ошибки**: +```json +{ + "error": "Не найден магазин по store_guid" +} +``` + +### POST `/marketplace/instruction-dictionary` +Получение рабочего процесса и переходов статусов заказа. + +**Запрос**: +```json +{ + "guid": "order-guid" +} +``` + +**Ответ**: +```json +{ + "response": [ + { + "marketplace": "ЯндексМаркет", + "status": "Новый", + "status_id": "NEW", + "status_instruction": "Принять заказ в обработку", + "status_relations": [ + { + "status": "В обработке", + "status_id": "PROCESSING", + "description": "Перевести в обработку", + "button_text": "Принять", + "order": 1 + } + ] + } + ] +} +``` + +--- + +## Эндпоинты YandexMarket + +### GET `/yandex-market/create-cards` +Создание/обновление карточек товаров на Яндекс.Маркете. + +**Параметры запроса**: +- `do`: Установите `1` для фактической отправки данных (иначе предпросмотр) + +**Ответ**: HTML-страница с результатами + +### GET `/yandex-market/get-orders` +Получение и обработка заказов Яндекс.Маркета. + +**Параметры запроса**: +- `from_date`: Дата начала (по умолчанию: сегодня, формат: `d-m-Y`) +- `to_date`: Дата окончания (опционально) +- `status`: Фильтр по статусу +- `substatus`: Фильтр по подстатусу +- `campaign_id`: ID кампании для тестовых данных + +**Тестовый режим** (POST с телом): +```json +{ + "orders": [...] +} +``` + +**Ответ**: +```json +{ + "response": "OK", + "storeCount": 5, + "result": { + "processed": 10, + "created": 5, + "updated": 5 + } +} +``` + +--- + +## Эндпоинты доставки + +### GET `/delivery/auth` +Простой тестовый эндпоинт аутентификации. + +**Ответ**: `"ok"` + +### POST `/delivery/admin-auth` +Аутентификация администратора по хешу. + +**Запрос**: +```json +{ + "hash": "md5-hash-of-credentials" +} +``` + +**Ответ**: +```json +{ + "id": 123, + "group_id": 1, + "group_name": "Administrators", + "name": "Admin Name" +} +``` + +--- + +## Сводная статистика + +**Всего контроллеров**: 6 +- AuthController: 1 эндпоинт +- BalanceController: 2 эндпоинта +- ClientController: 21 эндпоинт +- OrdersController: 2 эндпоинта +- MarketplaceController: 3 эндпоинта +- YandexMarketController: 2 эндпоинта +- DeliveryController: 2 эндпоинта + +**Всего эндпоинтов**: 33 + +**Требуется аутентификация**: 31 эндпоинт (все, кроме `/auth/login` и внутренних OPTIONS) diff --git a/erp24/docs/api/api2/EXAMPLES.md b/erp24/docs/api/api2/EXAMPLES.md new file mode 100644 index 00000000..1b599980 --- /dev/null +++ b/erp24/docs/api/api2/EXAMPLES.md @@ -0,0 +1,781 @@ +> 📖 **Язык**: Русский | [English](../EXAMPLES.md) + +# Примеры кода API2 и руководства по использованию + +Практические примеры интеграции с модулем API2 на различных языках программирования. + +--- + +## Содержание + +1. [Примеры аутентификации](#примеры-аутентификации) +2. [Примеры управления клиентами](#примеры-управления-клиентами) +3. [Примеры управления заказами](#примеры-управления-заказами) +4. [Примеры бонусной системы](#примеры-бонусной-системы) +5. [Примеры обработки ошибок](#примеры-обработки-ошибок) + +--- + +## Примеры аутентификации + +### JavaScript/Node.js (fetch) + +```javascript +const API_BASE = 'https://erp.bazacvetov24.ru/api2'; + +async function login(username, password) { + const response = await fetch(`${API_BASE}/auth/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + login: username, + password: password + }) + }); + + const data = await response.json(); + + if (data['access-token']) { + // Сохраняем токен для последующих запросов + localStorage.setItem('api_token', data['access-token']); + return data['access-token']; + } else { + throw new Error(data.errors || 'Login failed'); + } +} + +// Использование токена в последующих запросах +async function makeAuthenticatedRequest(endpoint, body) { + const token = localStorage.getItem('api_token'); + + const response = await fetch(`${API_BASE}${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-ACCESS-TOKEN': token + }, + body: JSON.stringify(body) + }); + + return await response.json(); +} +``` + +### PHP (cURL) + +```php + true, + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => ['Content-Type: application/json'], + CURLOPT_POSTFIELDS => json_encode([ + 'login' => $username, + 'password' => $password + ]) + ]); + + $response = curl_exec($ch); + $data = json_decode($response, true); + + curl_close($ch); + + if (isset($data['access-token'])) { + return $data['access-token']; + } else { + throw new Exception($data['errors'] ?? 'Login failed'); + } +} + +function makeAuthenticatedRequest($endpoint, $body, $token) { + $ch = curl_init(API_BASE . $endpoint); + + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'X-ACCESS-TOKEN: ' . $token + ], + CURLOPT_POSTFIELDS => json_encode($body) + ]); + + $response = curl_exec($ch); + curl_close($ch); + + return json_decode($response, true); +} + +// Использование +$token = login('myuser', 'mypassword'); +$result = makeAuthenticatedRequest('/client/balance', [ + 'phone' => '79001234567' +], $token); +?> +``` + +### Python (requests) + +```python +import requests + +API_BASE = 'https://erp.bazacvetov24.ru/api2' + +def login(username, password): + response = requests.post( + f'{API_BASE}/auth/login', + json={'login': username, 'password': password} + ) + data = response.json() + + if 'access-token' in data: + return data['access-token'] + else: + raise Exception(data.get('errors', 'Login failed')) + +def make_authenticated_request(endpoint, body, token): + response = requests.post( + f'{API_BASE}{endpoint}', + json=body, + headers={'X-ACCESS-TOKEN': token} + ) + return response.json() + +# Использование +token = login('myuser', 'mypassword') +result = make_authenticated_request('/client/balance', { + 'phone': '79001234567' +}, token) +``` + +--- + +## Примеры управления клиентами + +### Добавление нового клиента + +```javascript +// JavaScript +async function addClient(phone, name, messenger_data) { + return await makeAuthenticatedRequest('/client/add', { + phone: phone, + name: name, + client_id: messenger_data.client_id, + client_type: messenger_data.client_type, + platform_id: messenger_data.platform_id, + avatar: messenger_data.avatar, + full_name: name, + messenger: 'telegram', + date_of_creation: Date.now() / 1000 + }); +} + +// Использование +const result = await addClient('79001234567', 'John Doe', { + client_id: 123, + client_type: 1, + platform_id: 456, + avatar: 'https://example.com/avatar.jpg' +}); + +if (result.result) { + console.log('Client added successfully'); +} else { + console.error('Error:', result.error_description); +} +``` + +### Получение баланса клиента + +```php + '79001234567' +], $token); + +echo "Balance: " . $result['balance'] . " points\n"; +echo "Keycode: " . $result['keycode'] . "\n"; +?> +``` + +### Получение истории покупок клиента + +```python +# Python +def get_client_purchases(phone, token, page=0): + result = make_authenticated_request('/client/check-details', { + 'phone': phone + }, token) + + if 'response' in result: + checks = result['response']['checks'] + pages = result['response']['pages'] + + print(f"Total purchases: {pages['totalCount']}") + print(f"Showing page {pages['page'] + 1} of {(pages['totalCount'] // pages['per-page']) + 1}") + + for check in checks: + print(f"\nOrder #{check['number']} - {check['date']}") + print(f"Store: {check['store']['name']}") + print(f"Total: {check['sum']} RUB (discount: {check['discount']} RUB)") + print(f"Products: {len(check['products'])}") + + for product in check['products']: + print(f" - Product {product['product_id']}: {product['quantity']} x {product['price']} RUB") + + return result + else: + print("Error:", result.get('error')) + +# Использование +purchases = get_client_purchases('79001234567', token) +``` + +### Добавление памятных дат + +```javascript +// JavaScript - Добавление нескольких событий одновременно +async function addMemorableDates(phone, events) { + return await makeAuthenticatedRequest('/client/event-edit', { + phone: phone, + channel: 'web', + events: events + }); +} + +// Использование +const dates = [ + { number: 1, date: '25.12.1990', tip: 'День рождения' }, + { number: 2, date: '14.02.2020', tip: 'День свадьбы' }, + { number: 3, date: '08.03.2024', tip: '8 марта' } +]; + +const result = await addMemorableDates('79001234567', dates); + +if (result.response) { + console.log('Events added successfully'); + // Примечание: Добавление 5 событий автоматически начисляет 300 бонусных баллов! +} +``` + +--- + +## Примеры управления заказами + +### Изменение статуса заказа + +```php + [ + [ + 'order_id' => $order_guid, + 'status' => $status_code, + 'seller_id' => $seller_id + ] + ] + ], $token); +} + +// Использование +$result = updateOrderStatus( + 'order-guid-123', + 'PROCESSING', + 'seller-guid-456', + $token +); + +foreach ($result as $order_result) { + echo "Order: " . $order_result['order_id'] . "\n"; + echo "Status: " . ($order_result['result'] ? 'Updated' : 'Error') . "\n"; + echo "Message: " . $order_result['message'] . "\n"; +} +?> +``` + +### Пакетное обновление статусов заказов + +```javascript +// JavaScript - Обновление нескольких заказов одновременно +async function updateMultipleOrders(orders) { + return await makeAuthenticatedRequest('/orders/change-status', { + order: orders.map(o => ({ + order_id: o.guid, + status: o.new_status, + seller_id: o.seller_id + })) + }); +} + +// Использование +const orders = [ + { guid: 'order-1', new_status: 'PROCESSING', seller_id: 'seller-1' }, + { guid: 'order-2', new_status: 'READY', seller_id: 'seller-2' }, + { guid: 'order-3', new_status: 'SHIPPED', seller_id: 'seller-1' } +]; + +const results = await updateMultipleOrders(orders); + +results.forEach(result => { + console.log(`Order ${result.order_id}: ${result.message}`); +}); +``` + +### Получение заказов магазина + +```python +# Python - Получение всех заказов магазина за последние 24 часа +def get_store_orders(store_guid, token): + result = make_authenticated_request('/orders/get-orders', { + 'store_id': store_guid + }, token) + + if result.get('success'): + orders = result['result'] + print(f"Found {len(orders)} orders") + + for order in orders: + print(f"\nOrder: {order['order_id']}") + print(f"Status: {order['status']}") + print(f"Items: {len(order['items'])}") + + for item in order['items']: + print(f" - {item['product_id']}: {item['quantity']} x {item['price']} RUB") + else: + print("Error:", result.get('error')) + +# Использование +get_store_orders('store-guid-123', token) +``` + +--- + +## Примеры бонусной системы + +### Применение бонусных баллов к покупке + +```javascript +// JavaScript - Использование бонусных баллов для заказа +async function useBonusPoints(order_id, phone, points, price) { + const result = await makeAuthenticatedRequest('/client/use-bonuses', { + order_id: order_id, + phone: phone, + points_to_use: points, + date: Math.floor(Date.now() / 1000), + price: price + }); + + if (result.response?.status === 'success') { + console.log(`Successfully deducted ${points} bonus points`); + console.log(`Remaining balance: ${result.response.data.remainingPoints}`); + return result.response.data; + } else { + throw new Error(result.error?.message || 'Failed to use bonuses'); + } +} + +// Использование +try { + const result = await useBonusPoints( + 'order-123', + '79001234567', + 100, // Использовать 100 бонусных баллов + 1500 // Сумма заказа: 1500 RUB + ); + console.log('New balance:', result.remainingPoints); +} catch (error) { + console.error('Error:', error.message); +} +``` + +### Начисление бонусных баллов после покупки + +```php + $order_id, + 'phone' => $phone, + 'points_to_add' => $points_to_add, + 'date' => time(), + 'price' => $purchase_amount + ], $token); +} + +// Использование +$result = awardPurchaseBonus( + 'order-456', + '79001234567', + 2000, // Покупка на 2000 RUB + $token +); + +if ($result['response']['status'] === 'success') { + echo "Added " . $result['response']['data']['addedPoints'] . " bonus points\n"; + echo "Total balance: " . $result['response']['data']['totalPoints'] . "\n"; +} +?> +``` + +### Проверка бонусного уровня и статуса + +```python +# Python - Получение информации о бонусном уровне клиента +def check_bonus_level(phone, token): + result = make_authenticated_request('/client/bonus-status', { + 'phone': phone + }, token) + + if 'response' in result: + status = result['response'] + print(f"Bonus Level: {status['bonus_level']} ({status['alias']})") + print(f"Current Points: {status['current_points']}") + print(f"Next Level: {status['next_points']} points") + print(f"Discount: {status['discount_percent']}%") + print(f"Cashback Rate: {status['cashback_rate']}%") + return status + else: + print("Error:", result.get('error')) + +# Использование +bonus_info = check_bonus_level('79001234567', token) +``` + +### Применение промокода + +```javascript +// JavaScript - Применение промокода к аккаунту клиента +async function applyPromoCode(phone, promo_code) { + try { + const result = await makeAuthenticatedRequest('/client/apply-promo-code', { + phone: phone, + code: promo_code + }); + + if (result.response) { + console.log('Promo code applied successfully!'); + return true; + } + } catch (error) { + if (error.error_id === 2) { + console.error('Promo code expired or invalid'); + } else if (error.error_id === 3) { + console.error('Promo code already used'); + } else { + console.error('Error:', error.error); + } + return false; + } +} + +// Использование +await applyPromoCode('79001234567', 'SPRING2024'); +``` + +--- + +## Примеры обработки ошибок + +### Комплексная обработка ошибок + +```javascript +// JavaScript - Надежная обертка для обработки ошибок +async function apiRequest(endpoint, body) { + try { + const token = localStorage.getItem('api_token'); + + if (!token && endpoint !== '/auth/login') { + throw new Error('No authentication token available'); + } + + const response = await fetch(`${API_BASE}${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(token && { 'X-ACCESS-TOKEN': token }) + }, + body: JSON.stringify(body) + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + + // Проверка ошибок на уровне API + if (data.error_id !== undefined) { + const error = new Error(data.error || 'API Error'); + error.error_id = data.error_id; + error.details = data.error_description; + throw error; + } + + if (data.error !== undefined) { + throw new Error(data.error.message || data.error); + } + + return data; + + } catch (error) { + console.error('API Request Failed:', error); + + // Обработка специфичных типов ошибок + if (error.error_id === 1 || error.error_id === 1.2) { + console.error('Invalid or missing parameters'); + } else if (error.error_id === 2) { + console.error('Resource not found or save failed'); + } else if (error.message.includes('401')) { + console.error('Authentication failed - token may be expired'); + // Перенаправление на страницу входа или обновление токена + } + + throw error; + } +} +``` + +### Обработчик ошибок PHP + +```php +error_id = $error_id; + $this->details = $details; + } +} + +function safeApiRequest($endpoint, $body, $token) { + try { + $data = makeAuthenticatedRequest($endpoint, $body, $token); + + if (isset($data['error_id'])) { + throw new ApiException( + $data['error'] ?? 'API Error', + $data['error_id'], + $data['error_description'] ?? null + ); + } + + if (isset($data['error'])) { + throw new ApiException($data['error']['message'] ?? $data['error']); + } + + return $data; + + } catch (ApiException $e) { + error_log("API Error [{$e->error_id}]: {$e->getMessage()}"); + + switch ($e->error_id) { + case 1: + case 1.2: + error_log("Invalid parameters"); + break; + case 2: + error_log("Resource not found"); + break; + case 3: + error_log("Business logic violation"); + break; + } + + throw $e; + } +} +?> +``` + +### Логика повторных попыток Python + +```python +import time +from typing import Any, Dict + +class ApiError(Exception): + def __init__(self, message, error_id=None, details=None): + super().__init__(message) + self.error_id = error_id + self.details = details + +def api_request_with_retry(endpoint: str, body: Dict[str, Any], token: str, + max_retries: int = 3, backoff: float = 1.0) -> Dict: + """ + Выполнение API-запроса с логикой экспоненциальной задержки при повторных попытках + """ + for attempt in range(max_retries): + try: + result = make_authenticated_request(endpoint, body, token) + + # Проверка ошибок API + if 'error_id' in result: + raise ApiError( + result.get('error', 'API Error'), + result.get('error_id'), + result.get('error_description') + ) + + if 'error' in result: + error_msg = result['error'].get('message', result['error']) + raise ApiError(error_msg) + + return result + + except ApiError as e: + # Не повторять при клиентских ошибках (эквивалент 4xx) + if e.error_id in [1, 1.2, 3]: + raise + + # Повторять при серверных ошибках + if attempt < max_retries - 1: + wait_time = backoff * (2 ** attempt) + print(f"Request failed, retrying in {wait_time}s...") + time.sleep(wait_time) + else: + raise + + except Exception as e: + if attempt < max_retries - 1: + wait_time = backoff * (2 ** attempt) + print(f"Unexpected error, retrying in {wait_time}s...") + time.sleep(wait_time) + else: + raise + +# Использование +try: + result = api_request_with_retry('/client/balance', { + 'phone': '79001234567' + }, token) + print(f"Balance: {result['balance']}") +except ApiError as e: + print(f"API Error [{e.error_id}]: {e}") +``` + +--- + +## Полный пример интеграции + +```javascript +// Полное управление жизненным циклом клиента +class FlowerShopAPI { + constructor(baseUrl) { + this.baseUrl = baseUrl; + this.token = null; + } + + async login(username, password) { + const response = await fetch(`${this.baseUrl}/auth/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ login: username, password }) + }); + + const data = await response.json(); + this.token = data['access-token']; + return this.token; + } + + async request(endpoint, body) { + const response = await fetch(`${this.baseUrl}${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-ACCESS-TOKEN': this.token + }, + body: JSON.stringify(body) + }); + + return await response.json(); + } + + // Методы для работы с клиентами + async getClientInfo(phone) { + return await this.request('/client/get-info', { phone }); + } + + async getBalance(phone) { + return await this.request('/client/balance', { phone }); + } + + // Методы для работы с заказами + async processOrder(phone, order_total, bonus_to_use) { + // 1. Получить баланс клиента + const balance = await this.getBalance(phone); + + if (balance.balance < bonus_to_use) { + throw new Error('Insufficient bonus points'); + } + + // 2. Использовать бонусы + const use_result = await this.request('/client/use-bonuses', { + order_id: `order-${Date.now()}`, + phone, + points_to_use: bonus_to_use, + date: Math.floor(Date.now() / 1000), + price: order_total + }); + + // 3. Рассчитать кэшбэк + const cashback = Math.floor(order_total * 0.05); + + // 4. Начислить кэшбэк + const add_result = await this.request('/client/add-bonus', { + order_id: use_result.response.data.order_id, + phone, + points_to_add: cashback, + date: Math.floor(Date.now() / 1000), + price: order_total + }); + + return { + order_id: use_result.response.data.order_id, + bonus_used: bonus_to_use, + cashback_earned: cashback, + final_balance: add_result.response.data.totalPoints + }; + } +} + +// Использование +const api = new FlowerShopAPI('https://erp.bazacvetov24.ru/api2'); +await api.login('username', 'password'); + +const orderResult = await api.processOrder( + '79001234567', // phone + 2000, // order total + 100 // bonus points to use +); + +console.log('Order processed:', orderResult); +``` + +--- + +## Примечания + +- Всегда проверяйте номера телефонов перед отправкой запросов +- Храните токены аутентификации безопасно (никогда не используйте localStorage в production!) +- Реализуйте правильную обработку ошибок для всех вызовов API +- Используйте логику повторных попыток для временных сбоев +- Ведите журнал ошибок API для отладки +- Рассмотрите ограничение частоты запросов в клиентском коде diff --git a/erp24/docs/api/api2/INTEGRATION_GUIDE.md b/erp24/docs/api/api2/INTEGRATION_GUIDE.md new file mode 100644 index 00000000..2fdd4a0d --- /dev/null +++ b/erp24/docs/api/api2/INTEGRATION_GUIDE.md @@ -0,0 +1,752 @@ +# Руководство по интеграции API2 + +Полное руководство по интеграции вашего приложения с модулем ERP API2. + +--- + +## Содержание + +1. [Начало работы](#начало-работы) +2. [Настройка аутентификации](#настройка-аутентификации) +3. [Типовые сценарии интеграции](#типовые-сценарии-интеграции) +4. [Лучшие практики](#лучшие-практики) +5. [Тестирование интеграции](#тестирование-интеграции) +6. [Устранение неполадок](#устранение-неполадок) +7. [Развертывание в production](#развертывание-в-production) + +--- + +## Начало работы + +### Предварительные требования + +- Учетные данные для доступа к API (логин и пароль) +- HTTPS-совместимый клиент +- Возможность обработки JSON +- Базовый URL: `https://erp.bazacvetov24.ru/api2` + +### Чек-лист быстрого старта + +- [ ] Получить учетные данные API у системного администратора +- [ ] Проверить подключение к базовому URL +- [ ] Реализовать процесс аутентификации +- [ ] Протестировать базовые эндпоинты (баланс, информация о клиенте) +- [ ] Реализовать обработку ошибок +- [ ] Настроить логирование +- [ ] Развернуть в production + +--- + +## Настройка аутентификации + +### Шаг 1: Получение токена доступа + +```http +POST /api2/auth/login +Content-Type: application/json + +{ + "login": "ваш_логин", + "password": "ваш_пароль" +} +``` + +**Ответ**: +```json +{ + "access-token": "eyJ0eXAiOiJKV1QiLCJhbGc..." +} +``` + +### Шаг 2: Безопасное хранение токена + +**НЕ ДЕЛАЙТЕ**: +- Хранение в localStorage (уязвимость XSS) +- Хранение в cookies без флага HttpOnly +- Коммит в систему контроля версий +- Использование между пользователями + +**ДЕЛАЙТЕ**: +- Используйте безопасные cookie с флагами HttpOnly и Secure +- Храните в серверной сессии +- Шифруйте при хранении в базе данных +- Реализуйте механизм обновления токена + +### Шаг 3: Использование токена в запросах + +Включайте токен в каждый аутентифицированный запрос: + +**Вариант 1: Заголовок (рекомендуется)** +```http +POST /api2/client/balance +X-ACCESS-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGc... +Content-Type: application/json + +{ + "phone": "79001234567" +} +``` + +**Вариант 2: Параметр запроса** +```http +POST /api2/client/balance?key=eyJ0eXAiOiJKV1QiLCJhbGc... +Content-Type: application/json + +{ + "phone": "79001234567" +} +``` + +--- + +## Типовые сценарии интеграции + +### Сценарий 1: Интеграция оформления заказа в интернет-магазине + +**Ситуация**: Покупатель оформляет заказ с использованием бонусных баллов + +```mermaid +sequenceDiagram + Покупатель->>Сайт: Добавить товары в корзину + Сайт->>API: POST /client/balance {phone} + API-->>Сайт: {balance: 500} + Сайт->>Покупатель: Показать доступные бонусы + Покупатель->>Сайт: Использовать 100 баллов + Сайт->>API: POST /client/use-bonuses + API-->>Сайт: {success, remainingPoints: 400} + Сайт->>Платежная система: Обработать платеж + Платежная система-->>Сайт: Успешно + Сайт->>API: POST /client/add-bonus + API-->>Сайт: {totalPoints: 450} + Сайт->>Покупатель: Заказ подтвержден +``` + +**Реализация**: + +```javascript +async function checkoutWithBonuses(cartTotal, phone, bonusToUse) { + // 1. Проверить баланс бонусов + const balance = await api.request('/client/balance', { phone }); + + if (balance.balance < bonusToUse) { + throw new Error('Недостаточно бонусных баллов'); + } + + // 2. Рассчитать итоговую сумму + const discount = bonusToUse; // 1 балл = 1 рубль + const finalAmount = cartTotal - discount; + + // 3. Зарезервировать бонусы + const orderId = generateOrderId(); + await api.request('/client/use-bonuses', { + order_id: orderId, + phone: phone, + points_to_use: bonusToUse, + date: Math.floor(Date.now() / 1000), + price: finalAmount + }); + + try { + // 4. Обработать платеж + await processPayment(finalAmount); + + // 5. Начислить кешбэк + const cashback = Math.floor(finalAmount * 0.05); + await api.request('/client/add-bonus', { + order_id: orderId, + phone: phone, + points_to_add: cashback, + date: Math.floor(Date.now() / 1000), + price: finalAmount + }); + + return { success: true, orderId, cashback }; + + } catch (error) { + // Откат: вернуть бонусы + await api.request('/client/add-bonus', { + order_id: `${orderId}-rollback`, + phone: phone, + points_to_add: bonusToUse, + date: Math.floor(Date.now() / 1000), + price: 0 + }); + + throw error; + } +} +``` + +### Сценарий 2: Регистрация клиента + +**Ситуация**: Новый клиент регистрируется через мессенджер-бота + +```javascript +async function registerClient(messengerData) { + const phone = normalizePhone(messengerData.phone); + + // 1. Проверить существование клиента + let clientInfo; + try { + clientInfo = await api.request('/client/get-info', { phone }); + } catch (error) { + // Клиент не существует, создать нового + } + + if (!clientInfo || !clientInfo.response) { + // 2. Создать нового клиента + const result = await api.request('/client/add', { + phone: phone, + name: messengerData.name, + client_id: messengerData.messenger_id, + client_type: 1, + platform_id: messengerData.platform_id, + messenger: 'telegram', + date_of_creation: Math.floor(Date.now() / 1000) + }); + + if (!result.result) { + throw new Error('Не удалось создать клиента'); + } + + // 3. Приветственный бонус для новых клиентов + await api.request('/client/apply-promo-code', { + phone: phone, + code: 'WELCOME2024' + }); + } + + // 4. Получить обновленную информацию о клиенте + clientInfo = await api.request('/client/get-info', { phone }); + + return clientInfo.response; +} +``` + +### Сценарий 3: Синхронизация статусов заказов + +**Ситуация**: Синхронизация статусов заказов 1С с маркетплейсом + +```javascript +async function syncOrderStatuses(orders1C) { + const batchSize = 50; // Обработка по 50 заказов + const batches = chunkArray(orders1C, batchSize); + + for (const batch of batches) { + const orderUpdates = batch.map(order => ({ + order_id: order.guid, + status: order.status_code, + seller_id: order.seller_id + })); + + try { + const results = await api.request('/orders/change-status', { + order: orderUpdates + }); + + // Обработать результаты + for (const result of results) { + if (result.result === true) { + console.log(`Заказ ${result.order_id} успешно обновлен`); + } else { + console.error(`Заказ ${result.order_id} ошибка: ${result.message}`); + // Добавить в очередь повтора + queueForRetry(result.order_id); + } + } + + } catch (error) { + console.error('Ошибка пакетного обновления:', error); + // Повторить весь пакет + queueBatchForRetry(batch); + } + + // Ограничение скорости: пауза между пакетами + await sleep(1000); + } +} +``` + +### Сценарий 4: Опрос заказов с маркетплейса + +**Ситуация**: Периодическое получение новых заказов с Яндекс.Маркет + +```javascript +async function pollYandexMarketOrders() { + const fromDate = new Date(); + fromDate.setHours(0, 0, 0, 0); // Начало сегодняшнего дня + + const result = await api.request('/yandex-market/get-orders', { + from_date: formatDate(fromDate, 'd-m-Y'), + status: 'PROCESSING' + }); + + if (result.response === 'OK') { + console.log(`Обработано ${result.result.processed} заказов`); + console.log(`Создано: ${result.result.created}, Обновлено: ${result.result.updated}`); + + return result.result; + } else { + throw new Error('Не удалось получить заказы'); + } +} + +// Запуск каждые 5 минут +setInterval(pollYandexMarketOrders, 5 * 60 * 1000); +``` + +--- + +## Лучшие практики + +### 1. Обработка ошибок + +Всегда корректно обрабатывайте ошибки: + +```javascript +async function safeApiCall(endpoint, body) { + try { + const result = await api.request(endpoint, body); + + // Проверить ошибки на уровне API + if (result.error_id !== undefined) { + handleApiError(result); + return null; + } + + return result; + + } catch (error) { + // Сетевые или HTTP ошибки + console.error('Ошибка вызова API:', error); + + // Реализовать логику повторных попыток для временных ошибок + if (isRetryable(error)) { + return await retryWithBackoff(() => api.request(endpoint, body)); + } + + throw error; + } +} + +function handleApiError(result) { + switch (result.error_id) { + case 1: + case 1.2: + console.error('Неверные параметры:', result.error); + break; + case 2: + console.error('Ресурс не найден:', result.error); + break; + case 3: + console.error('Ошибка бизнес-логики:', result.error); + break; + default: + console.error('Неизвестная ошибка:', result.error); + } +} +``` + +### 2. Валидация номера телефона + +Всегда нормализуйте и проверяйте номера телефонов: + +```javascript +function normalizePhone(phone) { + // Удалить все нецифровые символы + phone = phone.replace(/\D/g, ''); + + // Добавить префикс 7 для российских номеров, если отсутствует + if (phone.length === 10) { + phone = '7' + phone; + } + + // Проверить формат + if (!/^7\d{10}$/.test(phone)) { + throw new Error('Неверный формат номера телефона'); + } + + return phone; +} +``` + +### 3. Идемпотентные операции + +Обеспечьте безопасность повторных попыток: + +```javascript +async function idempotentAddBonus(orderId, phone, points, price) { + // Проверить, не добавлен ли уже бонус для этого заказа + const existing = await api.request('/client/bonus-write-off', { phone }); + + const alreadyProcessed = existing.response.bonuses.some( + b => b.check_id === orderId && b.amount === points + ); + + if (alreadyProcessed) { + console.log('Бонус уже добавлен для этого заказа'); + return { alreadyProcessed: true }; + } + + // Безопасно добавить бонус + return await api.request('/client/add-bonus', { + order_id: orderId, + phone, + points_to_add: points, + date: Math.floor(Date.now() / 1000), + price + }); +} +``` + +### 4. Ограничение скорости запросов + +Реализуйте ограничение скорости на стороне клиента: + +```javascript +class RateLimiter { + constructor(maxRequests, perMilliseconds) { + this.maxRequests = maxRequests; + this.perMilliseconds = perMilliseconds; + this.requests = []; + } + + async throttle() { + const now = Date.now(); + + // Удалить старые запросы + this.requests = this.requests.filter( + time => now - time < this.perMilliseconds + ); + + if (this.requests.length >= this.maxRequests) { + const oldestRequest = this.requests[0]; + const waitTime = this.perMilliseconds - (now - oldestRequest); + await sleep(waitTime); + return this.throttle(); + } + + this.requests.push(now); + } +} + +const limiter = new RateLimiter(10, 1000); // 10 запросов в секунду + +async function makeThrottledRequest(endpoint, body) { + await limiter.throttle(); + return await api.request(endpoint, body); +} +``` + +### 5. Логирование и мониторинг + +Реализуйте полное логирование: + +```javascript +class ApiLogger { + static logRequest(endpoint, body) { + console.log(`[API] ${new Date().toISOString()} → ${endpoint}`, { + body: sanitizeLogData(body) + }); + } + + static logResponse(endpoint, response, duration) { + console.log(`[API] ${new Date().toISOString()} ← ${endpoint} (${duration}ms)`, { + success: !response.error_id, + error_id: response.error_id + }); + } + + static logError(endpoint, error) { + console.error(`[API] ${new Date().toISOString()} ✗ ${endpoint}`, { + error: error.message, + stack: error.stack + }); + } +} + +function sanitizeLogData(data) { + // Удалить конфиденциальную информацию из логов + const sanitized = { ...data }; + if (sanitized.password) sanitized.password = '***'; + if (sanitized.phone) sanitized.phone = sanitized.phone.replace(/\d{6}$/, '******'); + return sanitized; +} +``` + +--- + +## Тестирование интеграции + +### Модульное тестирование + +```javascript +// Mock API для тестирования +class MockAPI { + constructor() { + this.responses = new Map(); + } + + setResponse(endpoint, response) { + this.responses.set(endpoint, response); + } + + async request(endpoint, body) { + const response = this.responses.get(endpoint); + if (!response) { + throw new Error(`Нет mock-ответа для ${endpoint}`); + } + return typeof response === 'function' ? response(body) : response; + } +} + +// Тестовый случай +describe('Баланс клиента', () => { + let mockApi; + + beforeEach(() => { + mockApi = new MockAPI(); + }); + + test('должен получить баланс клиента', async () => { + mockApi.setResponse('/client/balance', { + balance: 500, + keycode: '1234' + }); + + const result = await mockApi.request('/client/balance', { + phone: '79001234567' + }); + + expect(result.balance).toBe(500); + expect(result.keycode).toBe('1234'); + }); + + test('должен обработать ошибку отсутствия телефона', async () => { + mockApi.setResponse('/client/balance', { + error_id: 1, + error: 'phone обязателен' + }); + + const result = await mockApi.request('/client/balance', {}); + + expect(result.error_id).toBe(1); + }); +}); +``` + +### Интеграционное тестирование + +```javascript +// Тестирование в staging-окружении +describe('Интеграционные тесты', () => { + let api; + const testPhone = '79999999999'; + + beforeAll(async () => { + api = new FlowerShopAPI('https://staging.erp.bazacvetov24.ru/api2'); + await api.login(process.env.TEST_USERNAME, process.env.TEST_PASSWORD); + }); + + test('полный процесс заказа', async () => { + // 1. Получить начальный баланс + const initialBalance = await api.getBalance(testPhone); + + // 2. Обработать заказ с бонусами + const orderResult = await api.processOrder(testPhone, 1000, 50); + + // 3. Проверить финальный баланс + const finalBalance = await api.getBalance(testPhone); + + expect(finalBalance.balance).toBe( + initialBalance.balance - 50 + orderResult.cashback_earned + ); + }); +}); +``` + +--- + +## Устранение неполадок + +### Распространенные проблемы + +#### Проблема 1: "Wrong login or password" + +**Причина**: Неверные учетные данные или истекший токен + +**Решение**: +```javascript +// Повторная аутентификация +try { + await api.login(username, password); +} catch (error) { + // Проверить учетные данные + console.error('Ошибка аутентификации:', error); +} +``` + +#### Проблема 2: "phone is required" + +**Причина**: Номер телефона не предоставлен или неверный формат + +**Решение**: +```javascript +// Всегда проверяйте телефон перед отправкой +const phone = normalizePhone(userInput); +if (!/^7\d{10}$/.test(phone)) { + throw new Error('Неверный номер телефона'); +} +``` + +#### Проблема 3: "Json body invalid" + +**Причина**: Некорректный JSON или неправильный Content-Type + +**Решение**: +```javascript +// Убедитесь в правильных заголовках +headers: { + 'Content-Type': 'application/json' +}, +body: JSON.stringify(data) // Не просто 'data' +``` + +#### Проблема 4: Ошибки таймаута + +**Причина**: Проблемы с сетью или длительные операции + +**Решение**: +```javascript +// Реализовать таймаут +const timeout = (ms) => new Promise((_, reject) => + setTimeout(() => reject(new Error('Таймаут')), ms) +); + +const result = await Promise.race([ + api.request(endpoint, body), + timeout(30000) // таймаут 30 секунд +]); +``` + +### Режим отладки + +Включить подробное логирование: + +```javascript +const DEBUG = process.env.NODE_ENV === 'development'; + +async function debugRequest(endpoint, body) { + if (DEBUG) { + console.log('→ Запрос:', endpoint, JSON.stringify(body, null, 2)); + } + + const start = Date.now(); + const result = await api.request(endpoint, body); + const duration = Date.now() - start; + + if (DEBUG) { + console.log(`← Ответ (${duration}ms):`, JSON.stringify(result, null, 2)); + } + + return result; +} +``` + +--- + +## Развертывание в production + +### Чек-лист + +- [ ] **Безопасность** + - [ ] Использовать только HTTPS + - [ ] Хранить учетные данные в переменных окружения + - [ ] Реализовать механизм обновления токена + - [ ] Добавить ограничение скорости запросов + - [ ] Включить подпись запросов (если доступно) + +- [ ] **Надежность** + - [ ] Реализовать логику повторных попыток с экспоненциальной задержкой + - [ ] Добавить паттерн circuit breaker + - [ ] Настроить проверки работоспособности + - [ ] Мониторить время отклика API + +- [ ] **Производительность** + - [ ] Кешировать часто запрашиваемые данные + - [ ] Группировать запросы где возможно + - [ ] Использовать пул соединений + - [ ] Оптимизировать размер payload + +- [ ] **Мониторинг** + - [ ] Логировать все вызовы API + - [ ] Отслеживать уровень ошибок + - [ ] Настроить оповещения о сбоях + - [ ] Контролировать квоты API (если применимо) + +- [ ] **Документация** + - [ ] Документировать точки интеграции + - [ ] Поддерживать совместимость версий API + - [ ] Обновлять руководства по типичным проблемам + +### Конфигурация окружения + +```javascript +// config/production.js +module.exports = { + api: { + baseUrl: process.env.API_BASE_URL, + username: process.env.API_USERNAME, + password: process.env.API_PASSWORD, + timeout: 30000, + retries: 3, + rateLimit: { + maxRequests: 100, + perMinutes: 1 + } + } +}; + +// .env.production +API_BASE_URL=https://erp.bazacvetov24.ru/api2 +API_USERNAME=prod_user +API_PASSWORD=*** +``` + +### Эндпоинт проверки работоспособности + +```javascript +app.get('/health/api', async (req, res) => { + try { + const result = await api.request('/balance/test', {}); + res.json({ + status: 'healthy', + api: 'connected', + timestamp: new Date().toISOString() + }); + } catch (error) { + res.status(503).json({ + status: 'unhealthy', + api: 'disconnected', + error: error.message, + timestamp: new Date().toISOString() + }); + } +}); +``` + +--- + +## Поддержка + +Для поддержки интеграции: +- Технические вопросы: Обратитесь к администратору API +- Вопросы бизнес-логики: См. документацию эндпоинтов +- Сообщения об ошибках: Включайте логи запросов/ответов и детали ошибки + +--- + +## История версий + +- **v2.0** (Текущая): RESTful API с токен-аутентификацией +- См. changelog для подробной информации о версиях diff --git a/erp24/docs/api/api2/MODULE_STRUCTURE.md b/erp24/docs/api/api2/MODULE_STRUCTURE.md new file mode 100644 index 00000000..dc31b936 --- /dev/null +++ b/erp24/docs/api/api2/MODULE_STRUCTURE.md @@ -0,0 +1,487 @@ +# Структура модуля API2 + +[English](../MODULE_STRUCTURE.md) | **Русский** + +## Организация каталогов + +``` +erp24/api2/ +├── config/ # Файлы конфигурации +│ ├── api2.config.php # Основная конфигурация приложения +│ ├── dev.api2.config.php # Конфигурация для разработки +│ └── env.php # Переменные окружения +├── controllers/ # Контроллеры API endpoints +│ ├── BaseController.php # Базовый контроллер с авторизацией/CORS +│ ├── AuthController.php # Аутентификация +│ ├── BalanceController.php # Операции с балансом +│ ├── BonusController.php # Управление бонусами +│ ├── ChatbotActionController.php # Действия чат-бота +│ ├── ClientController.php # Управление клиентами +│ ├── DataBuhController.php # Данные бухгалтерии +│ ├── DataController.php # Общие операции с данными +│ ├── DataTestController.php # Тестовые endpoints +│ ├── DeliveryController.php # Отслеживание доставки +│ ├── EmployeeController.php # Операции с сотрудниками +│ ├── KikController.php # Интеграция с КИК +│ ├── MarketplaceController.php # Операции с маркетплейсами +│ ├── OrdersController.php # Управление заказами +│ ├── SiteController.php # Операции сайта +│ ├── StoreController.php # Управление складом +│ ├── TaskController.php # RESTful API задач +│ ├── TelegramController.php # Telegram бот +│ ├── TelegramSalebotController.php # Бот продаж +│ ├── UniversalCatalogController.php # Каталог товаров +│ └── YandexMarketController.php # Интеграция с Яндекс.Маркетом +├── records/ # Модели Active Record +│ ├── ApiUser.php # Модель пользователя API +│ └── Task.php # Модель задачи +├── amo_data/ # Данные интеграции AmoCRM +│ └── token_info.json # OAuth токены +├── json/ # Логи запросов/ответов +│ ├── request_*.json # Логи API запросов +│ ├── changed_orders_*.json # Отслеживание изменений заказов +│ ├── upload_request_*.json # Логи загрузки +│ └── error logs # Отслеживание ошибок +├── runtime/ # Временные файлы выполнения +├── swagger/ # Документация API +├── .htaccess # Конфигурация Apache +├── .gitignore # Правила игнорирования Git +└── index.php # Точка входа приложения +``` + +## Количество файлов и строк кода + +### Контроллеры (24 файла) +- **BaseController.php** (58 строк) - Основа для всех API контроллеров +- **AuthController.php** (26 строк) - Endpoint аутентификации +- **MarketplaceController.php** (81 строка) - Операции с маркетплейсами +- **YandexMarketController.php** (223 строки) - Интеграция с Яндекс.Маркетом + +### Модели (2 файла) +- **ApiUser.php** (100 строк) - Аутентификация пользователей +- **Task.php** - Управление задачами + +### Конфигурация (3 файла) +- **api2.config.php** (108 строк) - Основная конфигурация +- **dev.api2.config.php** - Переопределение для разработки +- **env.php** - Настройки окружения + +### Точка входа (1 файл) +- **index.php** (18 строк) - Загрузка приложения + +## Ответственность модулей по каталогам + +### `/config` - Конфигурация приложения + +**Назначение**: Централизованное управление конфигурацией + +**Файлы**: +1. **api2.config.php** + - Конфигурация компонентов + - Правила маршрутизации URL + - Подключение к базе данных + - Настройки очередей + - CORS и аутентификация + +2. **env.php** + - Переменные окружения + - API ключи + - Endpoints сервисов + +3. **dev.api2.config.php** + - Переопределения для разработки + - Настройки отладки + +**Ключевые настройки**: +- Язык: Русский +- Формат ответа: JSON +- Аутентификация: На основе токенов +- Очередь: Интеграция с RabbitMQ +- Кэш: Файловый + +### `/controllers` - API Endpoints + +**Назначение**: Обработка HTTP запросов и бизнес-логики + +**Иерархия контроллеров**: +``` +yii\rest\Controller + ↓ +BaseController (CORS + Auth) + ↓ +├── AuthController +├── BalanceController +├── BonusController +├── ChatbotActionController +├── ClientController +├── DataBuhController +├── DataController +├── DataTestController +├── DeliveryController +├── EmployeeController +├── KikController +├── MarketplaceController +├── OrdersController +├── SiteController +├── StoreController +├── TaskController +├── TelegramController +├── TelegramSalebotController +├── UniversalCatalogController +└── YandexMarketController +``` + +#### Категории контроллеров + +**1. Системные контроллеры** + +| Контроллер | Назначение | Основные действия | +|-----------|---------|-------------| +| `BaseController` | Базовая функциональность | behaviors(), CORS, auth | +| `AuthController` | Аутентификация | actionLogin() | +| `SiteController` | Общие операции сайта | Различные | + +**2. Бизнес-контроллеры** + +| Контроллер | Домен | Ответственность | +|-----------|--------|----------------| +| `BalanceController` | Финансы | Операции с балансом | +| `BonusController` | Финансы | Управление бонусами | +| `ClientController` | CRM | Данные клиентов | +| `EmployeeController` | HR | Операции с сотрудниками | +| `OrdersController` | Заказы | Управление заказами | +| `DeliveryController` | Логистика | Отслеживание доставки | +| `StoreController` | Склад | Операции со складом | + +**3. Интеграционные контроллеры** + +| Контроллер | Внешняя система | Тип интеграции | +|-----------|----------------|------------------| +| `MarketplaceController` | Маркетплейсы | Общий API маркетплейсов | +| `YandexMarketController` | Яндекс.Маркет | Специфическая интеграция | +| `TelegramController` | Telegram | Bot API | +| `TelegramSalebotController` | Telegram | Бот продаж | +| `ChatbotActionController` | Чат-боты | Обработка действий | +| `KikController` | Система КИК | Обмен данными | + +**4. Контроллеры данных** + +| Контроллер | Назначение | Тип данных | +|-----------|---------|-----------| +| `DataController` | Общие данные | Различные сущности | +| `DataBuhController` | Бухгалтерия | Финансовые данные | +| `DataTestController` | Тестирование | Тестовые endpoints | +| `UniversalCatalogController` | Каталог | Данные товаров | + +**5. RESTful контроллеры** + +| Контроллер | REST ресурс | Стандартные действия | +|-----------|---------------|------------------| +| `TaskController` | Task | index, view, create, update, delete | + +### `/records` - Модели данных + +**Назначение**: Взаимодействие с базой данных и бизнес-логика + +**Модели**: + +1. **ApiUser.php** (100 строк) + - Таблица: `api_user` + - Поля: `id`, `login`, `password`, `access_token` + - Реализует: `IdentityInterface` + - Методы: + - `findByLogin($login)` - Поиск пользователя по логину + - `validatePassword($password)` - Проверка учетных данных + - `generateAccessToken()` - Создание нового токена + - `findIdentityByAccessToken($token)` - Поиск для аутентификации + +2. **Task.php** + - Таблица: `task` (предполагается) + - Модель RESTful ресурса + +**Пространство имен**: `app\records` + +**Родительский класс**: `yii\db\ActiveRecord` + +### `/amo_data` - Хранилище данных AmoCRM + +**Назначение**: Хранение данных интеграции с AmoCRM + +**Файлы**: +- `token_info.json` - OAuth access/refresh токены + +**Структура**: +```json +{ + "access_token": "...", + "refresh_token": "...", + "expires_in": 86400, + "created_at": 1234567890 +} +``` + +### `/json` - Логирование запросов/ответов + +**Назначение**: Отладка и аудит + +**Типы файлов**: + +1. **Логи запросов**: `request_[timestamp].json` + - Входящие API запросы + - Тело запроса + - Временная метка + +2. **Измененные заказы**: `changed_orders__[datetime]_.json` + - Отслеживание изменений заказов + - Обновления с маркетплейсов + +3. **Запросы загрузки**: `upload_request_id_[timestamp].json` + - Операции загрузки файлов + - Метаданные загрузки + +4. **Логи ошибок**: + - `request_error.txt` + - `log_error.txt` + - `log_created_write_offs_erp_error.txt` + +**Хранение логов**: Файлы накапливаются со временем (требуется ручная очистка) + +### `/runtime` - Временные файлы + +**Назначение**: Кэш, сессии, логи + +**Создается**: Фреймворком Yii2 + +**Содержимое**: +- Скомпилированные шаблоны +- Файлы кэша +- Данные сессий (если включено) +- Логи отладки + +**Статус в Git**: Игнорируется (`.gitignore`) + +### `/swagger` - Документация API + +**Назначение**: Спецификация OpenAPI/Swagger + +**Формат**: YAML или JSON + +**Использование**: Генерация документации API + +## Соглашения об именовании методов контроллеров + +### Методы действий + +**Шаблон**: `action[ActionName]()` + +**Примеры**: +```php +// AuthController +public function actionLogin() + +// MarketplaceController +public function actionStatuses() +public function actionGetNewOrderCount() +public function actionInstructionDictionary() + +// YandexMarketController +public function actionCreateCards($do = null) +public function actionGetOrders() +``` + +### Маппинг URL + +**Формат**: `/controller/action` + +**Примеры**: +- `/auth/login` → `AuthController::actionLogin()` +- `/marketplace/statuses` → `MarketplaceController::actionStatuses()` +- `/yandex-market/get-orders` → `YandexMarketController::actionGetOrders()` + +## Организация пространств имен + +### Пространство имен приложения: `app` + +**Контроллеры**: `app\controllers` +```php +namespace app\controllers; +class AuthController extends BaseController { } +``` + +**Records (Модели)**: `app\records` +```php +namespace app\records; +class ApiUser extends \yii\db\ActiveRecord { } +``` + +### Основное пространство имен ERP: `yii_app` + +**Используется для общих моделей**: +```php +use yii_app\records\MarketplaceOrders; +use yii_app\records\MarketplaceStatus; +use yii_app\records\ExportImportTable; +``` + +**Точка интеграции**: API2 использует модели из основного приложения + +## Иерархия конфигурации + +``` +index.php + ↓ +config/env.php (переменные окружения) + ↓ +config/api2.config.php (основная конфигурация) + ↓ +config/../../config/db.php (общая база данных) + ↓ +config/../../config/params.php (общие параметры) +``` + +## Файлы процесса аутентификации + +``` +Запрос клиента + ↓ +index.php → Загрузка приложения + ↓ +BaseController → CORS + Auth Behaviors + ↓ +AuthController::actionLogin() + ↓ +records/ApiUser::findByLogin() + ↓ +records/ApiUser::validatePassword() + ↓ +records/ApiUser::generateAccessToken() + ↓ +Ответ с токеном +``` + +## Паттерны потока данных + +### 1. RESTful паттерн (TaskController) + +``` +HTTP запрос → TaskController + ↓ +Task Model (ActiveRecord) + ↓ +Запрос к базе данных + ↓ +JSON ответ +``` + +### 2. Паттерн сервиса (YandexMarketController) + +``` +HTTP запрос → YandexMarketController + ↓ +MarketplaceService (yii_app\services) + ↓ +Множество моделей + Внешний API + ↓ +JSON ответ +``` + +### 3. Прямой паттерн (MarketplaceController) + +``` +HTTP запрос → MarketplaceController + ↓ +Прямой запрос к модели (MarketplaceStatus) + ↓ +JSON ответ +``` + +## Распределение по размеру файлов + +### Малые контроллеры (< 100 строк) +- AuthController (26 строк) +- BaseController (58 строк) +- MarketplaceController (81 строка) + +### Средние контроллеры (100-300 строк) +- YandexMarketController (223 строки) + +### Большие контроллеры (> 300 строк) +- Контроллеры со сложной бизнес-логикой +- Интеграции с маркетплейсами +- Endpoints синхронизации данных + +## Лучшие практики организации кода + +### Текущие сильные стороны +1. **Разделение обязанностей**: Контроллеры, модели, конфигурация +2. **Наследование**: BaseController для общей функциональности +3. **Пространства имен**: Четкое разделение пространств имен +4. **RESTful дизайн**: Стандартные REST паттерны +5. **Управление конфигурацией**: Централизованная конфигурация + +### Области для улучшения +1. **Слой сервисов**: Добавить сервисы бизнес-логики +2. **Валидация**: Централизовать правила валидации +3. **Обработка ошибок**: Единообразные ответы об ошибках +4. **Тестирование**: Добавить структуру каталога тестов +5. **Документация**: Встроенная документация кода + +## Зависимости между файлами + +### Прямые зависимости + +**BaseController.php** зависит от: +- `yii\rest\Controller` +- `yii\filters\Cors` +- `yii\filters\auth\*` + +**Все контроллеры** зависят от: +- `BaseController.php` +- Различных моделей из `yii_app\records` +- Компонентов фреймворка Yii2 + +**Модели** зависят от: +- `yii\db\ActiveRecord` +- Конфигурации базы данных + +**Конфигурация** зависит от: +- Файлов конфигурации родительского приложения +- Переменных окружения + +## Последовательность инициализации модуля + +``` +1. index.php +2. Загрузка автозагрузчика (Composer) +3. Загрузка фреймворка Yii2 +4. Загрузка config/env.php +5. Загрузка config/api2.config.php +6. Установка псевдонимов +7. Создание экземпляра приложения +8. Загрузка компонентов (log, queue) +9. Маршрутизация запроса к контроллеру +10. Выполнение действия +11. Возврат JSON ответа +``` + +## Сводная статистика + +- **Всего контроллеров**: 24 +- **Всего моделей**: 2 (api2) + общие из основного приложения +- **Всего файлов конфигурации**: 3 +- **Точек входа**: 1 +- **Вспомогательных каталогов**: 4 (amo_data, json, runtime, swagger) +- **Основной язык**: PHP +- **Фреймворк**: Yii2 +- **Архитектура**: MVC + RESTful + +## Рекомендуемые улучшения структуры модуля + +1. **Добавить каталог `/services`** для бизнес-логики +2. **Добавить каталог `/tests`** для unit/интеграционных тестов +3. **Добавить каталог `/migrations`** для изменений базы данных +4. **Добавить каталог `/validators`** для пользовательской валидации +5. **Добавить каталог `/helpers`** для утилитарных функций +6. **Добавить каталог `/middleware`** для пользовательского middleware +7. **Добавить каталог `/exceptions`** для пользовательских исключений +8. **Добавить каталог `/repositories`** для слоя доступа к данным diff --git a/erp24/docs/api/api2/README.md b/erp24/docs/api/api2/README.md new file mode 100644 index 00000000..78be5d53 --- /dev/null +++ b/erp24/docs/api/api2/README.md @@ -0,0 +1,224 @@ +> 📖 **Язык**: Русский | [English](../README.md) + +# Документация API2 + +Полная документация для модуля ERP API2 - RESTful API системы для интеграции с маркетплейсами, управления клиентами и обработки заказов. + +--- + +## 📚 Указатель документации + +### 1. [Справочник API](./API_REFERENCE.md) +**Обзор и основные концепции** +- Методы аутентификации +- Настройка CORS +- Обработка ошибок +- Форматы данных +- Версионирование API + +### 2. [Каталог эндпоинтов](./ENDPOINTS.md) +**Полный справочник эндпоинтов** +- 33 эндпоинта в 6 контроллерах +- Форматы запросов/ответов +- Спецификация параметров +- Коды ошибок +- Примечания по использованию + +### 3. [Примеры кода](./EXAMPLES.md) +**Практические примеры реализации** +- Процессы аутентификации +- Управление клиентами +- Обработка заказов +- Интеграция бонусной системы +- Паттерны обработки ошибок +- Примеры на разных языках (JavaScript, PHP, Python) + +### 4. [Руководство по интеграции](./INTEGRATION_GUIDE.md) +**Полное пошаговое руководство по интеграции** +- Начало работы +- Настройка аутентификации +- Общие паттерны +- Лучшие практики +- Стратегии тестирования +- Развертывание в продакшене +- Устранение неполадок + +--- + +## 🚀 Быстрый старт + +### 1. Аутентификация +```bash +curl -X POST https://erp.bazacvetov24.ru/api2/auth/login \ + -H "Content-Type: application/json" \ + -d '{"login":"username","password":"password"}' +``` + +### 2. Выполнение аутентифицированного запроса +```bash +curl -X POST https://erp.bazacvetov24.ru/api2/client/balance \ + -H "Content-Type: application/json" \ + -H "X-ACCESS-TOKEN: your-token" \ + -d '{"phone":"79001234567"}' +``` + +--- + +## 📊 Статистика API + +- **Всего эндпоинтов**: 33 +- **Контроллеры**: 6 + - AuthController (1 эндпоинт) + - BalanceController (2 эндпоинта) + - ClientController (21 эндпоинт) + - OrdersController (2 эндпоинта) + - MarketplaceController (3 эндпоинта) + - YandexMarketController (2 эндпоинта) + - DeliveryController (2 эндпоинта) + +- **Аутентификация**: На основе токенов (31/33 эндпоинтов требуют аутентификации) +- **Формат**: JSON +- **Протокол**: HTTPS + +--- + +## 🎯 Распространенные сценарии использования + +### Управление клиентами +- Регистрация новых клиентов → `/client/add` +- Проверка бонусного баланса → `/client/balance` +- Получение истории покупок → `/client/check-details` +- Управление программой лояльности → `/client/bonus-status` + +### Обработка заказов +- Обновление статуса заказа → `/orders/change-status` +- Получение заказов магазина → `/orders/get-orders` +- Отслеживание интеграции с маркетплейсами → `/marketplace/*` + +### Бонусная система +- Использование бонусных баллов → `/client/use-bonuses` +- Начисление кэшбэка → `/client/add-bonus` +- Применение промокодов → `/client/apply-promo-code` + +### Интеграция с маркетплейсами +- Синхронизация с Яндекс.Маркет → `/yandex-market/*` +- Получение количества заказов → `/marketplace/get-new-order-count` +- Процессы работы со статусами → `/marketplace/instruction-dictionary` + +--- + +## 🔧 Технические детали + +**Базовый URL**: `https://erp.bazacvetov24.ru/api2` + +**Методы аутентификации**: +- Заголовок: `X-ACCESS-TOKEN: token` +- Параметр запроса: `?key=token` + +**Формат ответа**: JSON + +**CORS**: Полная поддержка (все источники, методы, заголовки) + +**Логирование**: Комплексное (запросы, ошибки, операции) + +--- + +## 📖 Структура документации + +``` +docs/api2/ +├── README.md # Этот файл - указатель документации +├── API_REFERENCE.md # Основные концепции и обзор +├── ENDPOINTS.md # Полный каталог эндпоинтов +├── EXAMPLES.md # Примеры кода (JS, PHP, Python) +└── INTEGRATION_GUIDE.md # Пошаговое руководство по интеграции +``` + +--- + +## 🛠️ Чек-лист интеграции + +- [ ] Получить учетные данные API +- [ ] Реализовать аутентификацию +- [ ] Проверить подключение +- [ ] Реализовать обработку ошибок +- [ ] Настроить логирование +- [ ] Протестировать в тестовой среде +- [ ] Развернуть в продакшене +- [ ] Мониторить состояние API + +--- + +## 📝 Примеры по языкам программирования + +### JavaScript/Node.js +См. [EXAMPLES.md](./EXAMPLES.md#javascriptnodejs-fetch) для: +- Интеграция с Fetch API +- Паттерны async/await +- Обработка ошибок + +### PHP +См. [EXAMPLES.md](./EXAMPLES.md#php-curl) для: +- Реализация cURL +- Обработка ошибок +- Лучшие практики + +### Python +См. [EXAMPLES.md](./EXAMPLES.md#python-requests) для: +- Использование библиотеки Requests +- Логика повторных попыток +- Обработка данных + +--- + +## 🔍 Ключевые возможности + +### Безопасность +- Аутентификация на основе токенов +- Только HTTPS +- Поддержка CORS +- Скрытие API ключей в логах + +### Надежность +- Комплексная обработка ошибок +- Логирование транзакций +- Идемпотентные операции +- Эндпоинты, безопасные для повторных попыток + +### Интеграция +- RESTful дизайн +- Формат JSON +- Примеры на разных языках +- Подробная документация + +--- + +## 📞 Поддержка + +Для технической поддержки: +- Просмотрите раздел [Устранение неполадок](./INTEGRATION_GUIDE.md#troubleshooting) +- Проверьте [Распространенные проблемы](./INTEGRATION_GUIDE.md#common-issues) +- Свяжитесь с администратором API + +--- + +## 📜 Информация о версии + +**Текущая версия**: API v2 + +**Фреймворк**: Yii2 + +**Последнее обновление**: 2024-11-13 + +--- + +## 🎓 Путь обучения + +1. **Начинающий**: Начните с [Справочника API](./API_REFERENCE.md) +2. **Средний уровень**: Изучите [Примеры кода](./EXAMPLES.md) +3. **Продвинутый**: Следуйте [Руководству по интеграции](./INTEGRATION_GUIDE.md) +4. **Справочник**: Используйте [Каталог эндпоинтов](./ENDPOINTS.md) + +--- + +*Сгенерировано с комплексным анализом контроллеров и эндпоинтов модуля API2* diff --git a/erp24/docs/architecture/api-architecture.md b/erp24/docs/architecture/api-architecture.md new file mode 100644 index 00000000..49c3582a --- /dev/null +++ b/erp24/docs/architecture/api-architecture.md @@ -0,0 +1,1004 @@ +# Трехуровневая архитектура API + +> **Комплексное руководство по уникальной трехуровневой архитектуре API системы ERP24** + +## Содержание + +- [Обзор](#обзор) +- [Обоснование архитектуры](#обоснование-архитектуры) +- [Сравнение уровней API](#сравнение-уровней-api) +- [API1 - Устаревший уровень](#api1---устаревший-уровень) +- [API2 - Современный REST уровень](#api2---современный-rest-уровень) +- [API3 - Продвинутый уровень](#api3---продвинутый-уровень) +- [Поток запросов/ответов](#поток-запросовответов) +- [Аутентификация и авторизация](#аутентификация-и-авторизация) +- [Обработка ошибок](#обработка-ошибок) +- [Лучшие практики](#лучшие-практики) + +--- + +## Обзор + +ERP24 реализует уникальную **трехуровневую архитектуру API** для обслуживания различных потребностей интеграции, сохраняя обратную совместимость и поддерживая современные паттерны API. + +```mermaid +graph TB + subgraph "Типы клиентов" + CRON[Cron задачи
и скрипты] + LEGACY[Устаревшие
интеграции] + MOBILE[Мобильные
приложения] + WEB[Веб
клиенты] + PARTNER[Партнерские
системы] + MODERN[Современные
клиенты] + end + + subgraph "Уровни API" + API1[API1 - Устаревший
Порт 4444
Cron и совместимость] + API2[API2 - REST
Порт 5555
Основной API] + API3[API3 - Продвинутый
Порт 8888
Версионированный и чистый] + end + + subgraph "Общий бизнес-слой" + SVC[Сервисы
51 класс бизнес-логики] + ACT[Actions
223 класса операций] + AR[ActiveRecord
389 моделей данных] + end + + CRON --> API1 + LEGACY --> API1 + LEGACY --> API2 + + MOBILE --> API2 + WEB --> API2 + PARTNER --> API2 + + MODERN --> API3 + MOBILE --> API3 + + API1 --> SVC + API2 --> SVC + API3 --> SVC + + API1 --> ACT + API2 --> ACT + API3 --> ACT + + SVC --> AR + ACT --> AR + + style API1 fill:#ffcdd2 + style API2 fill:#c8e6c9 + style API3 fill:#bbdefb + style SVC fill:#fff9c4 +``` + +### Ключевые характеристики + +| Функция | API1 | API2 | API3 | +|---------|------|------|------| +| **Порт** | 4444 | 5555 | 8888 | +| **Назначение** | Legacy, Cron | Основной REST | Продвинутый, версионированный | +| **Дизайн** | Смешанные паттерны | RESTful | Чистая архитектура | +| **Контроллеры** | 3 | 21 | 11+ (модуль v1) | +| **Версионирование** | Нет | Нет | На основе модулей (v1, v2...) | +| **Документация** | Минимальная | Swagger доступен | OpenAPI планируется | +| **Основные пользователи** | Внутренние системы | Mobile, Web, Партнеры | Современные клиенты | +| **Аутентификация** | Token/Session | Token/Session | Token/JWT | +| **Статус** | Режим обслуживания | Активная разработка | Ориентация на будущее | + +--- + +## Обоснование архитектуры + +### Зачем три API? + +#### 1. **Обратная совместимость** (API1) +- Поддержка устаревших cron задач без breaking changes +- Поддержка старого интеграционного кода в период миграции +- Изоляция устаревших endpoint от активной разработки + +#### 2. **Основные операции** (API2) +- Обслуживание текущих мобильных приложений +- Обработка внешних интеграций (Telegram, маркетплейсы) +- Предоставление стабильного REST интерфейса для веб-клиентов + +#### 3. **Дизайн, ориентированный на будущее** (API3) +- Реализация паттернов чистой архитектуры +- Поддержка стратегии версионирования API +- Эксперименты с современными паттернами перед миграцией API2 + +### Принципы дизайна + +```mermaid +graph LR + A[Разделение ответственности] --> B[Независимая эволюция] + B --> C[Постепенная миграция] + C --> D[Снижение рисков] + + E[Общая бизнес-логика] --> F[Переиспользование кода] + F --> G[Согласованность] + G --> H[Поддерживаемость] + + style A fill:#e3f2fd + style E fill:#fff3e0 +``` + +**Преимущества**: +- ✅ Нет breaking changes для существующих интеграций +- ✅ Свобода для инноваций в API3 без влияния на стабильность +- ✅ Общая бизнес-логика обеспечивает согласованность +- ✅ Четкий путь миграции API1 → API2 → API3 + +**Компромиссы**: +- ❌ Несколько кодовых баз для поддержки +- ❌ Потенциальное дублирование логики +- ❌ Требуется четкая документация, какой API использовать + +--- + +## Сравнение уровней API + +### Детальное сравнение функций + +| Аспект | API1 | API2 | API3 | +|--------|------|------|------| +| **Расположение** | `/erp24/api1/` | `/erp24/api2/` | `/erp24/api3/` | +| **Точка входа** | `api1/index.php` | `api2/index.php` | `api3/web/index.php` | +| **Конфигурация** | `api1/config/api1.config.php` | `api2/config/api2.config.php` | `api3/config/main.php` | +| **Формат URL** | `/api1//` | `/api2//` | `/api3/v1//` | +| **Формат запроса** | Query params / Form data | JSON / Form data | Только JSON | +| **Формат ответа** | JSON / Plain text | JSON | JSON (структурированный) | +| **Формат ошибок** | Смешанный | JSON с кодами статуса | Стандартизированный JSON | +| **CORS** | Ограниченный | Включен | Полная поддержка | +| **Rate Limiting** | Нет | Базовый | Продвинутый | +| **Кэширование** | Нет | Базовое | ETags, Cache-Control | +| **Пагинация** | Ручная | Базовая (page/per-page) | Доступна cursor-based | +| **Фильтрация** | Ограниченная | Query параметры | Продвинутые фильтры | +| **Сортировка** | Ограниченная | Query параметры | Многопольная сортировка | + +--- + +## API1 - Устаревший уровень + +### Назначение и случаи использования + +**Основное назначение**: Обратная совместимость и запланированные задачи + +**Случаи использования**: +- ✅ Cron задачи (синхронизация бонусов, очистка данных) +- ✅ Устаревшие интеграционные скрипты +- ✅ Административная автоматизация +- ✅ Внутренняя коммуникация систем + +**Статус**: 🟡 Режим обслуживания - только исправление ошибок, нет новых функций + +### Архитектура + +**Расположение**: `/erp24/api1/` + +**Структура**: +``` +api1/ +├── actions/ +│ └── cron/ # Действия для cron задач (9 файлов) +│ ├── RunUpdateAction.php +│ ├── RunAmoCRMAction.php +│ └── ... +├── controllers/ +│ ├── AuthController.php # Аутентификация +│ ├── BaseController.php # Базовый функционал +│ └── CronController.php # Cron endpoints +├── records/ # Модели специфичные для Legacy +│ ├── amocrm/ +│ └── cloudpayments/ +├── config/ +│ └── api1.config.php # Конфигурация API1 +└── index.php # Точка входа +``` + +### Контроллеры + +#### 1. AuthController +**Файл**: `erp24/api1/controllers/AuthController.php` + +**Назначение**: Обработка аутентификации для endpoints API1 + +**Endpoints**: +- `POST /api1/auth/login` - Аутентификация пользователя +- `POST /api1/auth/logout` - Завершение сессии +- `GET /api1/auth/verify` - Проверка токена + +#### 2. CronController +**Файл**: `erp24/api1/controllers/CronController.php` + +**Назначение**: Выполнение запланированных фоновых задач + +**Endpoints**: +- `GET /api1/cron/update` - Запуск системных обновлений +- `GET /api1/cron/amocrm` - Синхронизация данных AmoCRM +- `GET /api1/cron/cloudpayments` - Обработка платежей +- `GET /api1/cron/bonus` - Синхронизация бонусных баллов + +**Пример использования**: +```bash +# Конфигурация cron задачи +0 */4 * * * curl http://localhost:4444/api1/cron/amocrm +0 1 * * * curl http://localhost:4444/api1/cron/bonus +``` + +### Стратегия миграции с API1 + +**График**: API1 → Устаревший (2026) → Удален (2027) + +**Шаги**: +1. ✅ Документация всех endpoints API1 +2. ✅ Создание эквивалентных endpoints API2 +3. 🔄 Миграция cron задач на console команды +4. 🔄 Обновление клиентских интеграций +5. ⏳ Мониторинг использования API1 (логирование) +6. ⏳ Уведомление об устаревании (за 12 месяцев до удаления) +7. ⏳ Удаление кода API1 + +--- + +## API2 - Современный REST уровень + +### Назначение и случаи использования + +**Основное назначение**: Основной REST API для мобильных приложений и интеграций + +**Случаи использования**: +- ✅ Мобильные приложения (iOS, Android) +- ✅ Бэкенд веб-приложения +- ✅ Партнерские интеграции +- ✅ Коннекторы маркетплейсов (Yandex, Flowwow) +- ✅ Webhooks Telegram бота +- ✅ Доступ к данным в реальном времени + +**Статус**: 🟢 Активная разработка - основной уровень API + +### Архитектура + +**Расположение**: `/erp24/api2/` + +**Структура**: +``` +api2/ +├── controllers/ # 21 REST контроллер +│ ├── AuthController.php +│ ├── BalanceController.php +│ ├── BonusController.php +│ ├── ClientController.php +│ ├── DataController.php +│ ├── DeliveryController.php +│ ├── EmployeeController.php +│ ├── KikController.php +│ ├── MarketplaceController.php +│ ├── OrdersController.php +│ ├── StoreController.php +│ ├── TaskController.php +│ ├── TelegramController.php +│ └── ... +├── swagger/ # Документация API +│ └── swagger.yaml +├── config/ +│ ├── api2.config.php +│ ├── dev.api2.config.php +│ └── env.php +├── runtime/ # Runtime файлы +├── cache/ # Директория кэша +└── index.php # Точка входа +``` + +### Обзор контроллеров + +#### Основные контроллеры (10) + +| Контроллер | Назначение | Ключевые endpoints |\n|------------|---------|---------------|\n| **AuthController** | Аутентификация и сессии | `/login`, `/logout`, `/refresh` | +| **ClientController** | Управление клиентами | `/clients`, `/clients/{id}` | +| **EmployeeController** | Операции с сотрудниками | `/employees`, `/employees/{id}/schedule` | +| **StoreController** | Данные магазинов | `/stores`, `/stores/{id}/products` | +| **DataController** | Справочные данные | `/cities`, `/grades`, `/roles` | +| **BalanceController** | Финансовые балансы | `/balance/client/{id}`, `/balance/store/{id}` | +| **BonusController** | Программа лояльности | `/bonus/accrue`, `/bonus/history/{clientId}` | +| **TaskController** | Управление задачами | `/tasks`, `/tasks/{id}/complete` | +| **OrdersController** | Обработка заказов | `/orders`, `/orders/{id}/status` | +| **DeliveryController** | Отслеживание доставки | `/delivery/track/{id}`, `/delivery/assign` | + +#### Интеграционные контроллеры (6) + +| Контроллер | Назначение | Интеграция | +|------------|---------|-------------| +| **TelegramController** | Telegram бот | Telegram Bot API | +| **TelegramSalebotController** | Бот продаж | Автоматизированный бот продаж | +| **MarketplaceController** | Операции маркетплейса | Flowwow | +| **YandexMarketController** | Интеграция Yandex | Yandex Market API | +| **KikController** | Система обратной связи | KIK feedback | +| **WhatsAppController** | WhatsApp сообщения | WhatsApp Business API | + +#### Доменные контроллеры (5) + +| Контроллер | Назначение | Домен | +|------------|---------|-------| +| **PayrollController** | Операции с зарплатой | HR и Финансы | +| **ShipmentController** | Отслеживание отгрузок | Логистика | +| **TimetableController** | Планирование | HR | +| **RatingController** | Рейтинги сотрудников | HR | +| **LessonController** | Система обучения | HR | + +### Поток аутентификации + +```mermaid +sequenceDiagram + participant C as Клиент + participant API as API2 + participant DB as База данных + participant Cache as Кэш + + C->>API: POST /api2/auth/login + Note over C,API: {phone: "79001234567", password: "***"} + + API->>DB: Проверка учетных данных + DB-->>API: Запись пользователя + + alt Валидные учетные данные + API->>Cache: Сохранить сессию + Cache-->>API: Сессия создана + API-->>C: 200 OK + Note over API,C: {token: "abc123", user: {...}} + else Невалидные учетные данные + API-->>C: 401 Unauthorized + Note over API,C: {error: "Invalid credentials"} + end + + Note over C: Последующие запросы + + C->>API: GET /api2/client/profile + Note over C,API: Header: Authorization: Bearer abc123 + + API->>Cache: Проверка токена + Cache-->>API: Данные сессии + + alt Валидный токен + API->>DB: Получение профиля + DB-->>API: Данные профиля + API-->>C: 200 OK + данные + else Невалидный токен + API-->>C: 401 Unauthorized + end +``` + +### Примеры запросов/ответов + +#### Пример 1: Получение баланса бонусов клиента + +**Запрос**: +```http +GET /api2/bonus/balance/12345 HTTP/1.1 +Host: localhost:5555 +Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc... +Accept: application/json +``` + +**Ответ**: +```json +{ + "success": true, + "data": { + "client_id": 12345, + "bonus_balance": 1250.50, + "pending_bonuses": 200.00, + "lifetime_earned": 5430.75, + "lifetime_spent": 4180.25, + "tier": "gold", + "next_tier_points": 249.50 + }, + "timestamp": "2025-01-14T12:43:32Z" +} +``` + +#### Пример 2: Начисление бонусных баллов + +**Запрос**: +```http +POST /api2/bonus/accrue HTTP/1.1 +Host: localhost:5555 +Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc... +Content-Type: application/json + +{ + "client_id": 12345, + "amount": 150.00, + "source": "purchase", + "reference_id": "ORDER-2025-001234", + "description": "Бонус за заказ #1234" +} +``` + +**Ответ**: +```json +{ + "success": true, + "data": { + "history_id": 67890, + "client_id": 12345, + "amount": 150.00, + "new_balance": 1400.50, + "accrued_at": "2025-01-14T12:45:00Z", + "expires_at": "2026-01-14T23:59:59Z" + }, + "timestamp": "2025-01-14T12:45:00Z" +} +``` + +### Обработка ошибок + +**Стандартный ответ с ошибкой**: +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Ошибка валидации", + "details": [ + { + "field": "amount", + "message": "Сумма должна быть больше 0" + } + ] + }, + "timestamp": "2025-01-14T12:47:00Z" +} +``` + +**HTTP коды статуса**: +- `200 OK` - Успех +- `201 Created` - Ресурс создан +- `400 Bad Request` - Неверный ввод +- `401 Unauthorized` - Требуется аутентификация +- `403 Forbidden` - Недостаточно прав +- `404 Not Found` - Ресурс не найден +- `422 Unprocessable Entity` - Ошибка валидации +- `500 Internal Server Error` - Ошибка сервера + +### Документация Swagger + +**Расположение**: `/erp24/api2/swagger/swagger.yaml` + +**URL доступа**: `http://localhost:5555/api2/swagger` + +**Возможности**: +- Интерактивный explorer API +- Примеры запросов/ответов +- Документация потока аутентификации +- Схемы моделей + +--- + +## API3 - Продвинутый уровень + +### Назначение и случаи использования + +**Основное назначение**: API с чистой архитектурой, ориентированный на будущее + +**Случаи использования**: +- ✅ Новые функции мобильных приложений +- ✅ Современные одностраничные приложения +- ✅ Интеграции с микросервисами +- ✅ API для сторонних разработчиков +- ✅ Эксперименты с версионированием API + +**Статус**: 🔵 Ориентирован на будущее - выборочная реализация функций + +### Архитектура + +**Расположение**: `/erp24/api3/` + +**Структура**: +``` +api3/ +├── core/ # Основные утилиты +│ ├── BaseController.php +│ ├── BaseService.php +│ └── validators/ +├── modules/ +│ └── v1/ # Модуль версии 1 +│ ├── controllers/ # 11+ контроллеров +│ │ ├── AdminController.php +│ │ ├── BonusController.php +│ │ ├── ClientController.php +│ │ ├── EmployeeController.php +│ │ ├── claim/ # Поддомен претензий +│ │ ├── orders/ # Поддомен заказов +│ │ ├── search/ # Поддомен поиска +│ │ └── timetable/ # Поддомен расписания +│ ├── models/ # DTO запросов/ответов +│ │ ├── ClientModel.php +│ │ └── ... +│ ├── requests/ # Валидация ввода +│ │ ├── BonusRequest.php +│ │ └── ... +│ ├── services/ # Бизнес-логика +│ │ ├── BonusService.php +│ │ ├── ClientService.php +│ │ └── ... +│ └── Module.php # Конфигурация модуля V1 +├── config/ +│ ├── main.php # Основная конфигурация +│ ├── bootstrap.php # События bootstrap +│ └── params.php # Параметры +├── web/ +│ └── index.php # Точка входа +└── EventBootstrap.php # Система событий +``` + +### Принципы чистой архитектуры + +```mermaid +graph TB + subgraph "Слои API3" + CTRL[Контроллеры
HTTP обработчики] + REQ[DTO запросов
Валидация ввода] + SVC[Сервисы
Бизнес-логика] + MOD[Модели
Доменные сущности] + RESP[DTO ответов
Форматирование вывода] + end + + HTTP[HTTP запрос] --> CTRL + CTRL --> REQ + REQ --> |Валидный| SVC + REQ --> |Невалидный| ERR[Ответ с ошибкой] + SVC --> MOD + MOD --> SVC + SVC --> RESP + RESP --> CTRL + CTRL --> JSON[JSON ответ] + + style REQ fill:#fff9c4 + style SVC fill:#c8e6c9 + style RESP fill:#bbdefb +``` + +**Ключевые отличия от API1/API2**: + +1. **DTO запросов/ответов**: Отдельные модели для ввода и вывода +2. **Слой сервисов**: Выделенные классы сервисов для каждого домена +3. **Валидаторы**: Пользовательская логика валидации, отделенная от моделей +4. **События**: Event-driven архитектура с EventBootstrap +5. **Версионирование**: Версионирование на основе модулей (v1, v2, v3...) + +### Стратегия версионирования + +**Структура URL**: `/api3/{version}/{module}/{resource}` + +**Примеры**: +``` +GET /api3/v1/clients/12345 +POST /api3/v1/bonus/accrue +GET /api3/v1/timetable/employee/456/schedule +PUT /api3/v1/orders/7890/status +``` + +**Управление версиями**: +```php +// Конфигурация роутов в api3/config/main.php +'modules' => [ + 'v1' => [ + 'class' => 'app\\modules\\v1\\Module', + ], + 'v2' => [ // Будущая версия + 'class' => 'app\\modules\\v2\\Module', + ], +], +``` + +### Паттерн запрос/ответ + +#### Пример: BonusController с DTO + +**DTO запроса** (`modules/v1/requests/BonusRequest.php`): +```php +namespace app\\modules\\v1\\requests; + +use yii\\base\\Model; + +class BonusRequest extends Model +{ + public $client_id; + public $amount; + public $source; + public $reference_id; + public $description; + + public function rules() + { + return [ + [['client_id', 'amount', 'source'], 'required'], + ['client_id', 'integer', 'min' => 1], + ['amount', 'number', 'min' => 0.01], + ['source', 'in', 'range' => ['purchase', 'manual', 'promotion', 'referral']], + ['reference_id', 'string', 'max' => 100], + ['description', 'string', 'max' => 500], + ]; + } +} +``` + +**DTO ответа** (`modules/v1/models/BonusResponse.php`): +```php +namespace app\\modules\\v1\\models; + +class BonusResponse +{ + public $history_id; + public $client_id; + public $amount; + public $new_balance; + public $accrued_at; + public $expires_at; + + public function __construct($historyModel, $clientModel) + { + $this->history_id = $historyModel->id; + $this->client_id = $clientModel->id; + $this->amount = $historyModel->amount; + $this->new_balance = $clientModel->bonus_balance; + $this->accrued_at = $historyModel->created_at; + $this->expires_at = $historyModel->expires_at; + } + + public function toArray() + { + return [ + 'history_id' => $this->history_id, + 'client_id' => $this->client_id, + 'amount' => $this->amount, + 'new_balance' => $this->new_balance, + 'accrued_at' => $this->accrued_at, + 'expires_at' => $this->expires_at, + ]; + } +} +``` + +**Контроллер** (`modules/v1/controllers/BonusController.php`): +```php +namespace app\\modules\\v1\\controllers; + +use app\\core\\BaseController; +use app\\modules\\v1\\requests\\BonusRequest; +use app\\modules\\v1\\models\\BonusResponse; +use app\\modules\\v1\\services\\BonusService; +use yii\\web\\BadRequestHttpException; + +class BonusController extends BaseController +{ + private $bonusService; + + public function __construct($id, $module, BonusService $bonusService, $config = []) + { + $this->bonusService = $bonusService; + parent::__construct($id, $module, $config); + } + + public function actionAccrue() + { + $request = new BonusRequest(); + $request->load(\\Yii::$app->request->post(), ''); + + if (!$request->validate()) { + throw new BadRequestHttpException(json_encode($request->errors)); + } + + $result = $this->bonusService->accrueBonus( + $request->client_id, + $request->amount, + $request->source, + $request->reference_id, + $request->description + ); + + $response = new BonusResponse($result['history'], $result['client']); + + return $this->asJson([ + 'success' => true, + 'data' => $response->toArray(), + 'timestamp' => date('c'), + ]); + } +} +``` + +--- + +## Поток запросов/ответов + +### Полная диаграмма потока API + +```mermaid +sequenceDiagram + participant C as Клиент + participant NG as Nginx + participant PHP as PHP-FPM + participant CTRL as Контроллер + participant REQ as DTO запроса + participant SVC as Сервис + participant AR as ActiveRecord + participant DB as PostgreSQL + participant Q as RabbitMQ + + C->>NG: HTTPS запрос + NG->>PHP: Перенаправление на PHP-FPM + PHP->>CTRL: Маршрутизация на контроллер + + CTRL->>REQ: Создание DTO запроса + REQ->>REQ: Валидация ввода + + alt Валидация не прошла + REQ-->>CTRL: Ошибки валидации + CTRL-->>C: 400 Bad Request + else Валидация прошла + CTRL->>SVC: Вызов метода сервиса + SVC->>AR: Запрос/изменение данных + AR->>DB: Выполнение SQL + DB-->>AR: Возврат результатов + AR-->>SVC: Возврат моделей + + opt Асинхронная операция + SVC->>Q: Отправка задачи + Q-->>SVC: Задача в очереди + end + + SVC-->>CTRL: Возврат результата + CTRL->>RESP: Форматирование ответа + RESP-->>C: JSON ответ + end +``` + +--- + +## Аутентификация и авторизация + +### Методы аутентификации + +#### 1. Аутентификация на основе токенов (API2, API3) + +**Поток**: +```mermaid +sequenceDiagram + C->>API: POST /auth/login (учетные данные) + API->>DB: Проверка пользователя + DB-->>API: Данные пользователя + API->>API: Генерация токена + API-->>C: Возврат токена + + Note over C: Сохранение токена + + C->>API: GET /resource (Bearer токен) + API->>API: Проверка токена + API->>DB: Получение ресурса + DB-->>API: Данные ресурса + API-->>C: Возврат ресурса +``` + +**Реализация**: +```php +// В контроллере +public function behaviors() +{ + $behaviors = parent::behaviors(); + $behaviors['authenticator'] = [ + 'class' => HttpBearerAuth::class, + ]; + return $behaviors; +} +``` + +#### 2. Аутентификация на основе сессий (API1, Web) + +**Поток**: Традиционные сессии на основе cookie + +--- + +## Обработка ошибок + +### Структура ответа с ошибкой + +**API1** (Legacy): +```json +{ + "error": "Неверный ID клиента", + "code": 400 +} +``` + +**API2** (Стандарт): +```json +{ + "success": false, + "error": { + "code": "INVALID_CLIENT", + "message": "Клиент с ID 12345 не найден", + "field": "client_id" + }, + "timestamp": "2025-01-14T12:50:00Z" +} +``` + +**API3** (Структурированный): +```json +{ + "success": false, + "error": { + "type": "ValidationError", + "code": "ERR_VALIDATION_001", + "message": "Ошибка валидации запроса", + "details": [ + { + "field": "amount", + "code": "ERR_FIELD_MIN", + "message": "Сумма должна быть больше 0", + "value": -10 + } + ], + "documentation": "/docs/errors/ERR_VALIDATION_001" + }, + "request_id": "req_abc123xyz", + "timestamp": "2025-01-14T12:50:00Z" +} +``` + +--- + +## Лучшие практики + +### Руководство по выбору API + +**Используйте API1 когда**: +- ❌ Не используйте для новой разработки +- ✅ Поддержка существующих cron задач (миграция запланирована) +- ✅ Поддержка устаревших интеграций (временно) + +**Используйте API2 когда**: +- ✅ Разработка мобильных приложений +- ✅ Создание веб-фронтендов +- ✅ Интеграция с внешними сервисами +- ✅ Быстрое прототипирование +- ✅ Нужны стабильные, проверенные endpoints + +**Используйте API3 когда**: +- ✅ Создание новых современных клиентов +- ✅ Требуется версионирование API +- ✅ Реализация сложной валидации +- ✅ Нужны чистые контракты запросов/ответов +- ✅ Эксперименты с новыми функциями + +### Лучшие практики запросов + +1. **Всегда включайте заголовок Accept**: + ```http + Accept: application/json + ``` + +2. **Используйте правильные HTTP методы**: + - `GET` - Чтение данных + - `POST` - Создание ресурса + - `PUT` - Полное обновление ресурса + - `PATCH` - Частичное обновление + - `DELETE` - Удаление ресурса + +3. **Включайте аутентификацию**: + ```http + Authorization: Bearer + ``` + +4. **Устанавливайте Content-Type для запросов с телом**: + ```http + Content-Type: application/json + ``` + +5. **Обрабатывайте ошибки корректно**: + ```javascript + try { + const response = await fetch('/api2/bonus/accrue', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(data) + }); + + if (!response.ok) { + const error = await response.json(); + console.error('Ошибка API:', error.error.message); + } + } catch (error) { + console.error('Ошибка сети:', error); + } + ``` + +### Лучшие практики ответов + +1. **Всегда включайте флаг `success`** +2. **Предоставляйте timestamp** +3. **Используйте согласованную обертку данных** +4. **Включайте метаданные пагинации** +5. **Возвращайте соответствующие HTTP коды статуса** + +--- + +## Оптимизация производительности + +### Стратегия кэширования + +```php +// Кэширование на уровне контроллера +public function behaviors() +{ + return [ + [ + 'class' => 'yii\filters\HttpCache', + 'only' => ['index', 'view'], + 'lastModified' => function ($action, $params) { + return Model::find()->max('updated_at'); + }, + ], + ]; +} +``` + +### Rate Limiting + +```php +// Rate limiting API2/API3 +public function behaviors() +{ + return [ + 'rateLimiter' => [ + 'class' => RateLimiter::class, + 'enableRateLimitHeaders' => true, + ], + ]; +} +``` + +--- + +## Мониторинг и отладка + +### Логирование запросов + +Все API запросы логируются с: +- ID запроса +- Endpoint +- Метод +- ID пользователя +- Время ответа +- Код статуса + +**Расположение логов**: `/erp24/api{1,2,3}/runtime/logs/app.log` + +### Режим отладки + +**API2 Development**: +```php +// api2/config/dev.api2.config.php +defined('YII_DEBUG') or define('YII_DEBUG', true); +defined('YII_ENV') or define('YII_ENV', 'dev'); +``` + +--- + +## Следующие шаги + +- [Детальный справочник API1](../api/api1/README.md) +- [Детальный справочник API2](../api/api2/README.md) +- [Детальный справочник API3](../api/api3/README.md) +- [Справочник ошибок](../errors/error-codes.md) +- [Руководства по интеграции](../guides/integrations/) + +--- + +**Последнее обновление:** Январь 2025 +**Поддерживается:** Команда разработки ERP24 diff --git a/erp24/docs/architecture/system-overview.md b/erp24/docs/architecture/system-overview.md new file mode 100644 index 00000000..1b6b4061 --- /dev/null +++ b/erp24/docs/architecture/system-overview.md @@ -0,0 +1,938 @@ +# Обзор системы ERP24 + +> **Высокоуровневый архитектурный обзор корпоративной системы ERP24** + +## Содержание + +- [Введение](#введение) +- [Назначение системы](#назначение-системы) +- [Архитектурные слои](#архитектурные-слои) +- [Обзор компонентов](#обзор-компонентов) +- [Поток данных](#поток-данных) +- [Технологический стек](#технологический-стек) +- [Архитектура развертывания](#архитектура-развертывания) +- [Ключевые паттерны проектирования](#ключевые-паттерны-проектирования) + +--- + +## Введение + +ERP24 — это комплексная система планирования ресурсов предприятия, построенная на фреймворке Yii2, специально разработанная для управления операциями розничного бизнеса по продаже цветов. Система обрабатывает сквозные бизнес-процессы от взаимодействия с клиентами до выполнения заказов, управления персоналом и финансовых операций. + +### Бизнес-контекст + +- **Отрасль**: Розничная торговля (сеть цветочных магазинов) +- **Масштаб**: Мультимагазинные операции в нескольких городах +- **Пользователи**: Сотрудники, менеджеры, администраторы, клиенты (через интеграции) +- **Интеграция**: Многоканальные продажи (веб, мобильные устройства, маркетплейсы) + +### Характеристики системы + +- **Тип**: Монолитная архитектура с модульной структурой +- **Масштаб**: ~3,771 PHP файлов, 389 моделей базы данных +- **Стратегия API**: Трёхслойный дизайн API (Legacy, REST, Advanced) +- **Объём данных**: Высокотранзакционная среда с требованиями реального времени + +--- + +## Назначение системы + +### Основные функции + +1. **Управление клиентами** + - Регистрация клиентов и управление профилями + - Администрирование программы бонусов/лояльности + - Коммуникация через Telegram и WhatsApp + - Отслеживание истории покупок + +2. **Управление заказами и продажами** + - Обработка и отслеживание заказов + - Управление каталогом продуктов + - Интеграция многоканальных продаж (Flowwow, Yandex Market) + - Аналитика и отчётность по продажам + +3. **Инвентарь и логистика** + - Управление складскими запасами магазинов + - Отслеживание отгрузок и оптимизация доставки + - Управление списаниями + - Матрица продуктов и ценообразование + +4. **Управление персоналом** + - Записи и профили сотрудников + - Расчёт и обработка зарплаты + - Управление графиком работы (timetable) + - Система оценки производительности + - Обучение и сертификация (lessons) + +5. **Финансовые операции** + - Обработка платежей (интеграция CloudPayments) + - Выплата зарплаты + - Финансовая отчётность + - Учёт бонусных баллов + +6. **Аналитика и бизнес-аналитика** + - Система дашбордов с KPI + - Пользовательские отчёты + - Метрики производительности + - Аналитика продаж + +--- + +## Архитектурные слои + +### Диаграмма многоуровневой архитектуры + +```mermaid +graph TB + subgraph "Слой презентации" + UI[Web UI
Views & Templates] + API1_P[API1 Endpoints] + API2_P[API2 REST Endpoints] + API3_P[API3 Advanced Endpoints] + end + + subgraph "Слой приложения" + CTRL[Контроллеры
161 файлов] + ACT[Actions
223 файла] + FORMS[Формы
23 файла] + end + + subgraph "Слой бизнес-логики" + SVC[Сервисы
51 файл] + HELP[Helpers
20 файлов] + VALID[Валидаторы] + end + + subgraph "Слой доступа к данным" + AR[ActiveRecord
389 моделей] + QUERY[Query Builders] + end + + subgraph "Слой данных" + DB[(PostgreSQL
База данных)] + CACHE[(Кэш
Redis/File)] + QUEUE[(RabbitMQ
Очередь сообщений)] + end + + subgraph "Внешние системы" + AMOCRM[AmoCRM] + TELEGRAM[Telegram API] + WHATSAPP[WhatsApp API] + YANDEX[Yandex Market] + FLOWWOW[Flowwow] + PAYMENT[CloudPayments] + end + + UI --> CTRL + API1_P --> CTRL + API2_P --> CTRL + API3_P --> CTRL + + CTRL --> ACT + CTRL --> SVC + CTRL --> FORMS + + ACT --> SVC + FORMS --> SVC + + SVC --> HELP + SVC --> AR + + AR --> DB + AR --> CACHE + + SVC --> QUEUE + + QUEUE --> TELEGRAM + QUEUE --> WHATSAPP + + SVC --> AMOCRM + SVC --> YANDEX + SVC --> FLOWWOW + SVC --> PAYMENT + + style DB fill:#e1f5ff + style CACHE fill:#e1f5ff + style QUEUE fill:#e1f5ff + style SVC fill:#fff3e0 + style AR fill:#f3e5f5 +``` + +### Ответственность слоёв + +#### 1. Слой презентации +**Назначение**: Обработка взаимодействий с пользователями и API запросов + +- **Web UI**: Серверные представления с использованием PHP шаблонов +- **API1**: Устаревший API для cron-задач и старых интеграций +- **API2**: Современный REST API для мобильных приложений и внешних систем +- **API3**: Продвинутый API с версионированием и чистой архитектурой + +**Ключевые компоненты**: +- Шаблоны представлений (`/erp24/views/`) +- API контроллеры (`/erp24/api1/`, `/erp24/api2/`, `/erp24/api3/`) +- Asset bundles (`/erp24/assets/`) + +#### 2. Слой приложения +**Назначение**: Маршрутизация запросов и оркестрация + +- **Контроллеры**: Приём HTTP запросов, валидация ввода, делегирование сервисам +- **Actions**: Автономные классы действий для конкретных операций +- **Формы**: Валидация ввода и трансформация данных + +**Ключевые файлы**: +- `/erp24/controllers/` - 161 файл контроллеров +- `/erp24/actions/` - 223 класса действий +- `/erp24/forms/` - 23 модели форм + +**Ответственности**: +- Валидация запросов +- Проверки аутентификации и авторизации +- Форматирование ответов +- Обработка ошибок + +#### 3. Слой бизнес-логики +**Назначение**: Основные бизнес-правила и операции + +- **Сервисы**: Реализация бизнес-логики (51 сервис) +- **Helpers**: Утилитарные функции и общие операции +- **Валидаторы**: Пользовательские правила валидации + +**Ключевые файлы**: +- `/erp24/services/` - BonusService, PayrollService, ShipmentService и др. +- `/erp24/helpers/` - DataHelper, FormatHelper, SalaryHelper и др. + +**Принципы проектирования**: +- Принцип единственной ответственности +- Классы сервисов инкапсулируют бизнес-домены +- Тестируемая и переиспользуемая логика +- Управление транзакциями + +#### 4. Слой доступа к данным +**Назначение**: Абстракция базы данных и хранение данных + +- **Модели ActiveRecord**: ORM модели, представляющие таблицы БД (389 моделей) +- **Query Builders**: Построение сложных запросов +- **Миграции**: Версионирование схемы базы данных + +**Ключевые файлы**: +- `/erp24/records/` - Admin, Bonus, CheckConduct, Client, Employee и др. +- `/erp24/migrations/` - 278 файлов миграций + +**Функции**: +- Определение связей (hasOne, hasMany, belongsTo) +- Правила валидации +- Области видимости и методы запросов +- Поддержка мягкого удаления (через SoftDeleteTrait) +- Отслеживание изменений (через HistoryModelTrait) + +#### 5. Слой данных +**Назначение**: Хранение данных и обмен сообщениями + +- **PostgreSQL**: Основное хранилище данных +- **Кэш**: Оптимизация производительности (файловый или Redis) +- **RabbitMQ**: Очередь сообщений для асинхронных операций + +--- + +## Обзор компонентов + +### Контроллеры (161 файл) + +Контроллеры обрабатывают HTTP запросы и координируют поток приложения. + +**Расположение**: `/erp24/controllers/` + +**Основные контроллеры**: +```php +// Пример: BonusController +erp24/controllers/BonusController.php // Управление бонусной системой +erp24/controllers/PayrollController.php // Операции с зарплатой +erp24/controllers/ShipmentController.php // Отслеживание отгрузок +erp24/controllers/TimetableController.php // Управление расписанием +erp24/controllers/DashboardController.php // Дашборд аналитики +erp24/controllers/RatingController.php // Рейтинги сотрудников +erp24/controllers/NotificationController.php // Уведомления +``` + +**Ответственности контроллеров**: +- Обработка запросов +- Валидация ввода +- Координация сервисов +- Рендеринг ответов +- Контроль доступа + +### Сервисы (51 файл) + +Сервисы содержат бизнес-логику и оркестрируют сложные операции. + +**Расположение**: `/erp24/services/` + +**Основные сервисы**: +``` +BonusService // Логика программы лояльности +PayrollService // Расчёты зарплаты +ShipmentService // Управление доставкой +TimetableService // Логика расписания +DashboardService // Агрегация аналитики +RatingService // Оценка производительности +NotificationService // Push-уведомления +MarketplaceService // Интеграции с маркетплейсами +``` + +**Паттерны сервисов**: +- Внедрение зависимостей +- Интерфейсные контракты +- Управление транзакциями +- Обработка ошибок и логирование + +### Actions (223 файла) + +Автономные классы действий, реализующие единичные операции. + +**Расположение**: `/erp24/actions/` + +**Организация Actions**: +``` +actions/admin/ // Административные действия +actions/bonus/ // Действия по бонусам +actions/cabinet/ // Действия личного кабинета +actions/dashboard/ // Действия дашборда +actions/lesson/ // Действия обучения +actions/payroll/ // Действия по зарплате +actions/rating/ // Действия по рейтингу +actions/shipment/ // Действия по отгрузкам +actions/timetable/ // Действия по расписанию +``` + +**Преимущества паттерна Action**: +- Принцип единственной ответственности +- Тестируемость +- Переиспользуемость между контроллерами +- Чёткие границы операций + +### Модели ActiveRecord (389 файлов) + +ORM модели, представляющие сущности базы данных. + +**Расположение**: `/erp24/records/` + +**Категории моделей**: +``` +Основные сущности: +- Admin, AdminPayroll, AdminPayrollDays +- Client, ClientBonus, ClientBonusHistory +- Employee, EmployeePayment +- Store, StoreProduct + +Операционные: +- CheckConduct, CheckType +- Shipment, ShipmentProduct +- Timetable, TimetableTemplate +- Task, TaskTemplate + +Финансовые: +- Payment, PaymentType +- Balances, WriteOffs + +Системные: +- Notification, NotificationHistory +- Rating, Grade +- Lesson, LessonPoll +``` + +### Helpers (20 файлов) + +Утилитарные функции для общих операций. + +**Расположение**: `/erp24/helpers/` + +**Ключевые Helpers**: +``` +AppArrayHelper // Манипуляции с массивами +DataHelper // Преобразования данных +DateHelper // Операции с датой/временем +FormatHelper // Утилиты форматирования +SalaryHelper // Расчёты зарплаты +HtmlHelper // Генерация HTML +ImageHelper // Обработка изображений +EmployeePaymentHelper // Утилиты платежей +``` + +### Формы (23 файла) + +Модели форм для валидации и привязки данных. + +**Расположение**: `/erp24/forms/` + +**Категории форм**: +``` +forms/dashboard/ // Формы конфигурации дашборда +forms/device/ // Управление устройствами +forms/lesson/ // Формы обучения +forms/payroll/ // Формы зарплаты +forms/timetable/ // Формы расписания +forms/writeOffsErp/ // Формы списаний +``` + +--- + +## Поток данных + +### Поток обработки запроса + +```mermaid +sequenceDiagram + participant C as Клиент + participant R as Маршрут + participant Ctrl as Контроллер + participant Act as Action/Form + participant Svc as Сервис + participant AR as ActiveRecord + participant DB as База данных + participant Q as Очередь + + C->>R: HTTP запрос + R->>Ctrl: Маршрутизация к контроллеру + + alt Использование паттерна Action + Ctrl->>Act: Делегирование Action + Act->>Svc: Вызов метода сервиса + else Использование паттерна Form + Ctrl->>Act: Валидация с помощью Form + Act->>Svc: Вызов сервиса если валидно + else Прямой вызов сервиса + Ctrl->>Svc: Вызов метода сервиса + end + + Svc->>AR: Запрос/изменение данных + AR->>DB: Выполнение SQL + DB-->>AR: Возврат результатов + AR-->>Svc: Возврат моделей + + opt Асинхронная операция + Svc->>Q: Отправка задачи в очередь + Q-->>Svc: Задача в очереди + end + + Svc-->>Ctrl: Возврат результата + Ctrl-->>C: HTTP ответ +``` + +### Пример: Поток начисления бонусов + +```mermaid +graph LR + A[Клиент делает покупку] --> B[Создан CheckConduct] + B --> C[BonusService::accrueBonus] + C --> D{Проверка соответствия} + D -->|Соответствует| E[Расчёт бонусных баллов] + D -->|Не соответствует| F[Пропуск] + E --> G[Создание ClientBonusHistory] + E --> H[Обновление баланса клиента] + G --> I[Логирование транзакции] + H --> I + I --> J{Отправить уведомление?} + J -->|Да| K[Постановка Telegram задачи] + J -->|Нет| L[Конец] + K --> L +``` + +--- + +## Технологический стек + +### Backend технологии + +| Компонент | Технология | Назначение | +|-----------|------------|---------| +| **Фреймворк** | Yii2 2.0.x | PHP MVC фреймворк | +| **Язык** | PHP 7.4+ | Серверная логика | +| **База данных** | PostgreSQL 12+ | Основное хранилище данных | +| **ORM** | Active Record | Абстракция базы данных | +| **Очередь** | RabbitMQ | Асинхронная обработка задач | +| **Кэш** | File/Redis | Оптимизация производительности | +| **Веб-сервер** | Nginx | HTTP сервер | +| **PHP Runtime** | PHP-FPM | Выполнение PHP | +| **Менеджер процессов** | Supervisor | Управление сервисами | + +### Frontend технологии + +| Компонент | Технология | Назначение | +|-----------|------------|---------| +| **JavaScript** | jQuery 3.6.0 | Манипуляция DOM | +| **Transpiler** | Babel | ES6+ в ES5 | +| **Bundler** | esbuild | Быстрая система сборки | +| **Стили** | SASS | Препроцессинг CSS | +| **Шаблоны** | PHP | Серверный рендеринг | + +### Разработка и DevOps + +| Компонент | Технология | Назначение | +|-----------|------------|---------| +| **Контейнеризация** | Docker | Согласованность окружения | +| **Оркестрация** | Docker Compose | Управление мульти-контейнерами | +| **Контроль версий** | Git | Контроль исходного кода | +| **Менеджер зависимостей** | Composer | PHP пакеты | +| **Менеджер пакетов** | npm | Frontend пакеты | +| **Инструмент сборки** | esbuild | Frontend сборки | + +### Внешние интеграции + +| Сервис | Назначение | +|---------|---------| +| **AmoCRM** | Интеграция CRM | +| **Telegram Bot API** | Уведомления клиентов | +| **WhatsApp API** | Коммуникации с клиентами | +| **CloudPayments** | Обработка платежей | +| **Yandex Market** | Интеграция с маркетплейсом | +| **Flowwow** | Интеграция с маркетплейсом | + +--- + +## Архитектура развертывания + +### Docker сервисы + +```mermaid +graph TB + subgraph "Docker Compose окружение" + subgraph "Веб-слой" + NGINX_MAIN[nginx-yii_erp24
Порт 81/7443] + NGINX_API1[nginx_api1
Порт 4444/4443] + NGINX_API2[nginx_api2
Порт 5555/9443] + NGINX_API3[nginx_api3
Порт 8888] + NGINX_MEDIA[nginx_media
Порт 9999/8443] + end + + subgraph "Слой приложения" + PHP[php-yii_erp24
PHP-FPM] + SUPERVISOR[supervisor
Менеджер процессов] + end + + subgraph "Слой данных" + POSTGRES[db-pgsql-yii_erp24
PostgreSQL] + RABBITMQ[rabbitmq-yii_erp24
Порт 5672/15672] + end + end + + NGINX_MAIN --> PHP + NGINX_API1 --> PHP + NGINX_API2 --> PHP + NGINX_API3 --> PHP + NGINX_MEDIA --> PHP + + PHP --> POSTGRES + PHP --> RABBITMQ + + SUPERVISOR --> PHP + + style POSTGRES fill:#4fc3f7 + style RABBITMQ fill:#ff9800 + style PHP fill:#8e44ad +``` + +### Распределение портов + +| Сервис | Порт | Назначение | +|---------|------|---------| +| Основной Web | 81 (HTTP), 7443 (HTTPS) | Главный веб-интерфейс | +| API1 | 4444 (HTTP), 4443 (HTTPS) | Устаревший API | +| API2 | 5555 (HTTP), 9443 (HTTPS) | Современный REST API | +| API3 | 8888 (HTTP) | Продвинутый API | +| Сервер медиа | 9999 (HTTP), 8443 (HTTPS) | Загрузка/скачивание файлов | +| RabbitMQ | 5672 (AMQP), 15672 (UI) | Очередь сообщений | +| PostgreSQL | 5432 (Внутренний) | База данных | + +### Структура директорий + +``` +/Users/vladfo/development/yii-erp24/ +├── docker/ # Файлы конфигурации Docker +├── erp24/ # Главное приложение +│ ├── api1/ # Приложение Legacy API +│ ├── api2/ # Приложение REST API +│ ├── api3/ # Приложение Advanced API +│ ├── config/ # Основные файлы конфигурации +│ ├── controllers/ # Веб-контроллеры +│ ├── services/ # Сервисы бизнес-логики +│ ├── records/ # Модели ActiveRecord +│ ├── actions/ # Классы действий +│ ├── views/ # Шаблоны представлений +│ ├── web/ # Точка входа веб +│ ├── migrations/ # Миграции базы данных +│ └── ... +├── docs/ # Документация (это) +└── docker-compose.yml # Оркестрация Docker +``` + +--- + +## Ключевые паттерны проектирования + +### 1. Model-View-Controller (MVC) + +**Реализация**: Паттерн фреймворка Yii2 + +```php +// Контроллер (erp24/controllers/BonusController.php) +class BonusController extends Controller +{ + public function actionIndex() + { + $service = new BonusService(); + $bonuses = $service->getAllBonuses(); + + return $this->render('index', ['bonuses' => $bonuses]); + } +} + +// Представление (erp24/views/bonus/index.php) + +
amount ?>
+ +``` + +### 2. Паттерн Service Layer + +**Назначение**: Инкапсуляция бизнес-логики + +```php +// Сервис (erp24/services/BonusService.php) +class BonusService +{ + public function accrueBonus(Client $client, float $amount): ClientBonusHistory + { + $transaction = Yii::$app->db->beginTransaction(); + try { + $bonus = new ClientBonusHistory(); + $bonus->client_id = $client->id; + $bonus->amount = $amount; + $bonus->save(); + + $client->bonus_balance += $amount; + $client->save(); + + $transaction->commit(); + return $bonus; + } catch (\Exception $e) { + $transaction->rollBack(); + throw $e; + } + } +} +``` + +### 3. Паттерн Action + +**Назначение**: Классы действий с единственной ответственностью + +```php +// Action (erp24/actions/bonus/AccrueBonusAction.php) +class AccrueBonusAction extends Action +{ + public function run(int $clientId, float $amount) + { + $client = Client::findOne($clientId); + if (!$client) { + throw new NotFoundHttpException('Client not found'); + } + + $service = new BonusService(); + $bonus = $service->accrueBonus($client, $amount); + + return $this->controller->asJson(['success' => true, 'bonus' => $bonus]); + } +} +``` + +### 4. Паттерн Active Record + +**Назначение**: Объектно-реляционное отображение + +```php +// Модель (erp24/records/ClientBonusHistory.php) +class ClientBonusHistory extends ActiveRecord +{ + public static function tableName() + { + return 'client_bonus_history'; + } + + public function rules() + { + return [ + [['client_id', 'amount'], 'required'], + ['amount', 'number'], + ]; + } + + public function getClient() + { + return $this->hasOne(Client::class, ['id' => 'client_id']); + } +} +``` + +### 5. Паттерн Repository (через ActiveRecord) + +**Назначение**: Абстракция доступа к данным + +```php +// Использование в сервисе +class BonusService +{ + public function findByClient(int $clientId): array + { + return ClientBonusHistory::find() + ->where(['client_id' => $clientId]) + ->orderBy(['created_at' => SORT_DESC]) + ->all(); + } +} +``` + +### 6. Dependency Injection + +**Назначение**: Слабая связанность и тестируемость + +```php +class PayrollService +{ + private $bonusService; + private $timetableService; + + public function __construct( + BonusService $bonusService, + TimetableService $timetableService + ) { + $this->bonusService = $bonusService; + $this->timetableService = $timetableService; + } +} +``` + +### 7. Паттерн Queue/Job + +**Назначение**: Асинхронная обработка задач + +```php +// Job (erp24/jobs/SendTelegramMessageJob.php) +class SendTelegramMessageJob extends BaseObject implements JobInterface +{ + public $userId; + public $message; + + public function execute($queue) + { + $telegramService = new TelegramService(); + $telegramService->sendMessage($this->userId, $this->message); + } +} + +// Использование в сервисе +Yii::$app->queue->push(new SendTelegramMessageJob([ + 'userId' => $user->telegram_id, + 'message' => 'Ваш бонус начислен!' +])); +``` + +### 8. Композиция поведения на основе Trait + +**Назначение**: Переиспользуемое поведение между моделями + +```php +// Trait (erp24/traits/SoftDeleteTrait.php) +trait SoftDeleteTrait +{ + public function softDelete() + { + $this->deleted_at = time(); + return $this->save(false); + } + + public static function find() + { + return parent::find()->where(['deleted_at' => null]); + } +} + +// Использование в модели +class Client extends ActiveRecord +{ + use SoftDeleteTrait; +} +``` + +--- + +## Границы системы + +### Что ЭТО система ERP24 + +- ✅ Комплексная ERP для операций розничной торговли цветами +- ✅ Система управления многоканальными продажами +- ✅ Система управления персоналом и расчёта зарплаты +- ✅ Платформа программы лояльности клиентов +- ✅ Система выполнения заказов и логистики +- ✅ Платформа аналитики и отчётности + +### Что система ERP24 НЕ ЯВЛЯЕТСЯ + +- ❌ Витрина электронной коммерции (интегрируется с внешними платформами) +- ❌ Бухгалтерская система (предоставляет данные бухгалтерскому ПО) +- ❌ CRM общего назначения (специализирована для розничной торговли цветами) +- ❌ Публичная API платформа (API только для внутреннего/партнёрского использования) + +--- + +## Соображения производительности + +### Стратегии оптимизации + +1. **Оптимизация базы данных** + - Правильная индексация часто запрашиваемых столбцов + - Оптимизация запросов с использованием области видимости ActiveRecord + - Пулинг соединений + - Реплики для чтения для отчётности + +2. **Кэширование** + - Файловое кэширование для конфигурации + - Redis для сессий и часто используемых данных + - HTTP заголовки кэширования для статических ресурсов + +3. **Асинхронная обработка** + - RabbitMQ для тяжёлых операций + - Фоновые задачи для уведомлений + - Отправка email/SMS на основе очереди + +4. **Frontend оптимизация** + - Бандлинг ресурсов с esbuild + - Компиляция SASS + - Ленивая загрузка изображений + - Минификация в production + +--- + +## Архитектура безопасности + +### Слои безопасности + +```mermaid +graph TB + A[Входящий запрос] --> B{HTTPS?} + B -->|Да| C{Аутентифицирован?} + B -->|Нет| X[Отклонить] + + C -->|Да| D{Авторизован?} + C -->|Нет| Y[Редирект на логин] + + D -->|Да| E[Проверка RBAC] + D -->|Нет| Z[403 Forbidden] + + E --> F{Есть права?} + F -->|Да| G[Обработка запроса] + F -->|Нет| Z + + G --> H{Ввод валиден?} + H -->|Да| I[Выполнение бизнес-логики] + H -->|Нет| W[400 Bad Request] + + I --> J[Возврат ответа] +``` + +### Функции безопасности + +1. **Аутентификация**: Сессии на основе cookie с безопасными cookie +2. **Авторизация**: RBAC с иерархическими ролями +3. **Валидация ввода**: Серверная валидация всех входных данных +4. **Предотвращение SQL Injection**: Привязка параметров ActiveRecord +5. **Защита от XSS**: Экранирование вывода в представлениях +6. **Защита от CSRF**: CSRF токены в формах +7. **Хеширование паролей**: Bcrypt для хранения паролей +8. **HTTPS**: SSL/TLS для всех API коммуникаций + +--- + +## Соображения масштабируемости + +### Масштабируемость текущей архитектуры + +**Вертикальное масштабирование** (Текущий подход): +- Одиночный сервер приложений +- Одиночный сервер базы данных +- Подходит для текущей нагрузки + +**Горизонтальное масштабирование** (Будущее): +- Балансировщик нагрузки для нескольких серверов приложений +- Репликация базы данных (master-slave) +- Распределённый кэш (кластер Redis) +- Извлечение микросервисов для высоконагруженных доменов + +### Анализ узких мест + +| Компонент | Текущий лимит | Стратегия масштабирования | +|-----------|---------------|------------------| +| База данных | Одиночный экземпляр | Реплики для чтения, пулинг соединений | +| Сервер приложений | Одиночный контейнер | Балансировка нагрузки, несколько экземпляров | +| Очередь | Одиночный RabbitMQ | Режим кластера, несколько воркеров | +| Хранилище файлов | Локальный диск | Объектное хранилище (S3-совместимое) | + +--- + +## Мониторинг и наблюдаемость + +### Стратегия логирования + +```php +// Логирование приложения +Yii::info('Бонус начислен для клиента: ' . $clientId, 'bonus'); +Yii::error('Ошибка обработки платежа: ' . $e->getMessage(), 'payment'); + +// Пользовательская цель логирования Telegram +Yii::getLogger()->dispatch([ + new LogRecord('error', 'Произошла критическая ошибка', 'application') +]); +``` + +### Метрики для мониторинга + +1. **Метрики приложения** + - Частота запросов + - Время ответа + - Частота ошибок + - Длина очереди + +2. **Бизнес-метрики** + - Заказов в час + - Начисления бонусов + - Время обработки зарплаты + - Время ответа API + +3. **Инфраструктурные метрики** + - Использование CPU + - Использование памяти + - Дисковый I/O + - Соединения с базой данных + +--- + +## Следующие шаги + +Для детальной информации о конкретных компонентах: + +1. [Трёхслойная архитектура API](./api-architecture.md) +2. [Документация слоя сервисов](./service-layer.md) +3. [Архитектура базы данных](./database-architecture.md) +4. [Архитектура frontend](./frontend-architecture.md) + +Для руководств по реализации: + +- [Руководство по началу работы](../guides/getting-started.md) +- [Настройка окружения разработки](../guides/environment-setup.md) +- [Руководство по безопасности и RBAC](../guides/security-rbac.md) + +--- + +**Последнее обновление**: Январь 2025 +**Поддерживается**: Команда разработки ERP24 diff --git a/erp24/docs/database/schema-overview.md b/erp24/docs/database/schema-overview.md new file mode 100644 index 00000000..988084a2 --- /dev/null +++ b/erp24/docs/database/schema-overview.md @@ -0,0 +1,873 @@ +# Database Schema Overview + +> **Comprehensive database documentation for ERP24 PostgreSQL schema** + +## Table of Contents + +- [Overview](#overview) +- [Database Statistics](#database-statistics) +- [Entity Relationship Diagram](#entity-relationship-diagram) +- [Core Entities](#core-entities) +- [Business Domain Tables](#business-domain-tables) +- [System Tables](#system-tables) +- [Data Flow](#data-flow) +- [Naming Conventions](#naming-conventions) +- [Migration History](#migration-history) + +--- + +## Overview + +ERP24 uses a **PostgreSQL** database with a comprehensive schema supporting flower retail operations including employee management, customer loyalty programs, sales tracking, inventory management, and financial operations. + +### Database Information + +| Property | Value | +|----------|-------| +| **DBMS** | PostgreSQL 12+ | +| **Schema** | public, erp24 | +| **Total Tables** | 389+ (via ActiveRecord models) | +| **Migrations** | 278 migration files | +| **Primary Keys** | Integer (auto-increment) and UUID (GUID) | +| **Foreign Keys** | Soft references via application layer | +| **Indexing** | Strategic indexes on high-traffic columns | + +--- + +## Database Statistics + +### Model Count by Domain + +| Domain | Model Count | Purpose | +|--------|-------------|---------| +| **Admin & HR** | 80+ | Employee management, payroll, timetable | +| **Sales & Orders** | 60+ | Sales, checks, products, marketplace orders | +| **Customer Management** | 40+ | Users, bonus program, events | +| **Inventory** | 35+ | Products, stores, write-offs | +| **Task Management** | 25+ | Tasks, templates, types | +| **Analytics** | 20+ | Dashboard, reports, metrics | +| **Lessons & Training** | 15+ | Employee education system | +| **System** | 30+ | Auth, logs, configurations | +| **Marketplace** | 15+ | Flowwow, Yandex Market integration | +| **Regulations** | 10+ | Company policies, polls | + +### Table Size Estimates + +| Table | Est. Rows | Growth Rate | Notes | +|-------|-----------|-------------|-------| +| **sales** | 500K+ | High (daily) | Main sales records | +| **users** | 100K+ | Medium | Customer database | +| **users_bonus** | 1M+ | High (daily) | Bonus transactions | +| **timetable** | 200K+ | Medium | Employee schedules | +| **admin_payroll_days** | 150K+ | Medium | Daily payroll records | +| **sales_products** | 2M+ | High (daily) | Sale line items | +| **task** | 50K+ | Medium | Task management | +| **logs (various)** | 10M+ | Very High | System logs | + +--- + +## Entity Relationship Diagram + +### Core Entity Relationships + +```mermaid +erDiagram + ADMIN ||--o{ SALES : creates + ADMIN ||--o{ TIMETABLE : has_schedule + ADMIN ||--o{ ADMIN_PAYROLL : receives + ADMIN ||--o{ TASK : assigned_to + ADMIN }o--|| ADMIN_GROUP : belongs_to + ADMIN }o--|| CITY_STORE : works_at + + USERS ||--o{ SALES : purchases + USERS ||--o{ USERS_BONUS : has_bonuses + USERS ||--o{ USERS_EVENTS : has_events + + SALES ||--o{ SALES_PRODUCTS : contains + SALES ||--o{ USERS_BONUS : generates_bonus + SALES }o--|| CITY_STORE : sold_at + SALES }o--|| ADMIN : sold_by + + CITY_STORE ||--o{ ADMIN : employs + CITY_STORE ||--o{ SALES : records + CITY_STORE ||--o{ TIMETABLE : schedules + CITY_STORE }o--|| CITY : located_in + + ADMIN_PAYROLL ||--|| ADMIN : for_employee + ADMIN_PAYROLL ||--o{ ADMIN_PAYROLL_DAYS : has_daily_records + + TIMETABLE }o--|| ADMIN : for_employee + TIMETABLE }o--|| CITY_STORE : at_store + TIMETABLE }o--|| SHIFT : in_shift + + TASK }o--|| ADMIN : assigned_to + TASK }o--|| ADMIN : created_by + TASK }o--|| TASK_TYPE : has_type + TASK }o--|| TASK_STATUS : has_status + + MARKETPLACE_ORDERS ||--o{ MARKETPLACE_ORDER_ITEMS : contains + MARKETPLACE_ORDERS }o--|| CITY_STORE : for_store + MARKETPLACE_ORDERS }o--|| MARKETPLACE_STATUS : has_status +``` + +--- + +## Core Entities + +### 1. Admin (Employees) + +**Table**: `admin` +**Primary Key**: `id` (integer) +**File**: `erp24/records/Admin.php` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INTEGER | Primary key | +| `guid` | VARCHAR(36) | 1C UUID identifier | +| `name` | VARCHAR(55) | Short name | +| `name_full` | VARCHAR(200) | Full name | +| `group_id` | INTEGER | FK to admin_group | +| `mobile` | VARCHAR(25) | Phone number (unique) | +| `store_id` | INTEGER | Default store | +| `store_arr` | TEXT | Array of accessible stores | +| `login_user` | VARCHAR(29) | Login (unique) | +| `pass_user` | VARCHAR(120) | Password hash | +| `work_status` | INTEGER | 1=active, 4=fired | +| `work_rate` | INTEGER | 1=5/2, 2=2/2, 3=3/3 | +| `birthdate` | DATE | Date of birth | +| `lasttime` | TIMESTAMP | Last login | +| `access_token` | VARCHAR(512) | API auth token | + +#### Relationships + +```php +// Admin.php relationships +public function getAdminGroup() { + return $this->hasOne(AdminGroup::class, ['id' => 'group_id']); +} + +public function getStore() { + return $this->hasOne(CityStore::class, ['id' => 'store_id']); +} + +public function getTimetables() { + return $this->hasMany(Timetable::class, ['admin_id' => 'id']); +} + +public function getPayrolls() { + return $this->hasMany(AdminPayroll::class, ['admin_id' => 'id']); +} + +public function getCreatedTasks() { + return $this->hasMany(Task::class, ['created_by' => 'id']); +} + +public function getAssignedTasks() { + return $this->hasMany(Task::class, ['updated_by' => 'id']); +} +``` + +#### Indexes + +- PRIMARY KEY on `id` +- UNIQUE on `mobile` +- UNIQUE on `login_user` +- UNIQUE on `guid` +- INDEX on `group_id` +- INDEX on `work_status` + +--- + +### 2. Users (Customers) + +**Table**: `users` +**Primary Key**: `id` (integer) +**File**: `erp24/records/Users.php` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INTEGER | Primary key | +| `phone` | VARCHAR(13) | Phone number (key) | +| `name` | VARCHAR | Full name | +| `card` | VARCHAR | Loyalty card number | +| `password` | VARCHAR | Password hash | +| `keycode` | VARCHAR | 4-digit verification code | +| `pol` | VARCHAR | Gender (man/women) | +| `bdate` | DATE | Birth date | +| `email` | VARCHAR | Email address | +| `balans` | DECIMAL | Current bonus balance (deprecated) | +| `burn_balans` | DECIMAL | Soon-to-expire bonus | +| `bonus_minus` | DECIMAL | Total spent bonuses | +| `bonus_level` | VARCHAR | Tier (silver/gold/platinum) | +| `sale_cnt` | INTEGER | Total purchase count | +| `sale_price` | INTEGER | Lifetime value (LTV) | +| `sale_avg_price` | INTEGER | Average check | +| `date_last_sale` | TIMESTAMP | Last purchase date | +| `date_first_sale` | TIMESTAMP | First purchase date | +| `ref_code` | VARCHAR | Referral code | +| `referral_id` | INTEGER | Referred by user ID | +| `source` | INTEGER | 0=1C, 1=1C→TG, 2=TG | +| `telegram_is_subscribed` | INTEGER | 0=no, 1=yes | +| `telegram_created_at` | TIMESTAMP | TG registration date | +| `black_list` | INTEGER | Blocked flag | + +#### Relationships + +```php +// Users.php relationships +public function getBonuses() { + return $this->hasMany(UsersBonus::class, ['phone' => 'phone']); +} + +public function getEvents() { + return $this->hasMany(UsersEvents::class, ['phone' => 'phone']); +} + +public function getSales() { + return $this->hasMany(Sales::class, ['phone' => 'phone']); +} + +public function getReferrer() { + return $this->hasOne(Users::class, ['id' => 'referral_id']); +} + +public function getReferrals() { + return $this->hasMany(Users::class, ['referral_id' => 'id']); +} +``` + +#### Indexes + +- PRIMARY KEY on `id` +- INDEX on `phone` +- INDEX on `card` +- INDEX on `bonus_level` +- INDEX on `telegram_is_subscribed` + +--- + +### 3. Sales (Check/Receipt) + +**Table**: `sales` +**Primary Key**: `id` (UUID/GUID) +**File**: `erp24/records/Sales.php` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | VARCHAR(36) | 1C check GUID (PK) | +| `date` | TIMESTAMP | Check date/time | +| `operation` | VARCHAR(35) | "Продажа" or "Возврат" | +| `status` | VARCHAR(45) | Check status | +| `summ` | DECIMAL | Total amount | +| `skidka` | DECIMAL | Discount amount | +| `number` | VARCHAR(225) | Check number | +| `admin_id` | INTEGER | FK to admin | +| `seller_id` | VARCHAR(36) | 1C seller GUID | +| `store_id_1c` | VARCHAR(36) | 1C store GUID | +| `store_id` | INTEGER | FK to city_store | +| `phone` | BIGINT | Customer phone | +| `payments` | JSON | Payment details | +| `pay_arr` | VARCHAR(15) | Payment type IDs | +| `sales_check` | VARCHAR(36) | Return check ID | +| `order_id` | VARCHAR(36) | Online order ID | +| `matrix` | INTEGER | Matrix bouquet % | +| `delivery_date` | TIMESTAMP | Delivery date | +| `pickup` | INTEGER | Pickup flag | + +#### Relationships + +```php +// Sales.php relationships +public function getAdmin() { + return $this->hasOne(Admin::class, ['id' => 'admin_id']); +} + +public function getStore() { + return $this->hasOne(CityStore::class, ['id' => 'store_id']); +} + +public function getStoreByGuid() { + return $this->hasOne(Products1c::class, ['id' => 'store_id_1c']); +} + +public function getUsers() { + return $this->hasOne(Users::class, ['phone' => 'phone']); +} + +public function getBonuses() { + return $this->hasMany(UsersBonus::class, ['check_id' => 'id']); +} + +public function getProducts() { + return $this->hasMany(SalesProducts::class, ['check_id' => 'id']); +} + +public function getSaleCheck() { // Return reference + return $this->hasOne(Sales::class, ['id' => 'sales_check']); +} +``` + +#### Indexes + +- PRIMARY KEY on `id` +- UNIQUE on `(date, operation, store_id_1c, id)` +- INDEX on `date` +- INDEX on `phone` +- INDEX on `store_id` +- INDEX on `operation` + +--- + +### 4. UsersBonus (Bonus Transactions) + +**Table**: `users_bonus` +**Primary Key**: `id` (integer) +**File**: `erp24/records/UsersBonus.php` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INTEGER | Primary key | +| `phone` | VARCHAR(13) | Customer phone | +| `name` | VARCHAR(155) | Transaction name | +| `date` | TIMESTAMP | Transaction date | +| `user_id` | INTEGER | FK to users | +| `store_id` | INTEGER | FK to city_store | +| `check_id` | VARCHAR(45) | FK to sales | +| `tip` | TEXT | plus/minus/burn | +| `tip_sale` | TEXT | Transaction type detail | +| `price` | DECIMAL | Purchase amount | +| `price_skidka` | DECIMAL | Discount applied | +| `bonus` | DECIMAL | Bonus amount | +| `date_start` | TIMESTAMP | Bonus valid from | +| `date_end` | TIMESTAMP | Bonus expires at | +| `admin_id` | INTEGER | Added by admin | +| `referal_id` | INTEGER | Referral ID | +| `store_id_1c` | VARCHAR(36) | 1C store GUID | +| `seller_id_1c` | VARCHAR(36) | 1C seller GUID | + +#### Transaction Types (`tip_sale`) + +- `sale` - Accrued from purchase +- `minus` - Written off for purchase +- `burn` - Expired bonuses +- `memorable300` - 5 memorable dates bonus +- `p_PROMOCODE` - Promotional code +- `referral` - Referral bonus +- `manual` - Manual adjustment + +#### Relationships + +```php +// UsersBonus.php relationships +public function getUser() { + return $this->hasOne(Users::class, ['id' => 'user_id']); +} + +public function getStore() { + return $this->hasOne(CityStore::class, ['id' => 'store_id']); +} + +public function getSale() { + return $this->hasOne(Sales::class, ['id' => 'check_id']); +} + +public function getAdmin() { + return $this->hasOne(Admin::class, ['id' => 'admin_id']); +} +``` + +--- + +### 5. CityStore (Stores) + +**Table**: `city_store` +**Primary Key**: `id` (integer) +**File**: `erp24/records/CityStore.php` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INTEGER | Primary key | +| `f_id` | INTEGER | FloraPoint ID | +| `name` | VARCHAR | Short store name | +| `name_full` | VARCHAR | Full store name | +| `city_id` | INTEGER | FK to city | +| `adress` | TEXT | Street address | +| `gps` | VARCHAR | GPS coordinates | +| `email` | VARCHAR | Store email | +| `tg_chat_id` | VARCHAR | Telegram chat ID | +| `visible` | INTEGER | Display on site | +| `administrator_id` | INTEGER | Store manager | +| `sale_plan_avg` | INTEGER | Avg sales plan | +| `visitor_day_avg` | INTEGER | Avg daily visitors | +| `open_date` | DATE | Opening date | + +#### Relationships + +```php +// CityStore.php relationships +public function getCity() { + return $this->hasOne(City::class, ['id' => 'city_id']); +} + +public function getAdministrator() { + return $this->hasOne(Admin::class, ['id' => 'administrator_id']); +} + +public function getEmployees() { + return $this->hasMany(Admin::class, ['store_id' => 'id']); +} + +public function getSales() { + return $this->hasMany(Sales::class, ['store_id' => 'id']); +} + +public function getTimetable() { + return $this->hasMany(Timetable::class, ['store_id' => 'id']); +} +``` + +--- + +### 6. Timetable (Employee Schedule) + +**Table**: `timetable` +**Primary Key**: `id` (integer) +**File**: `erp24/records/Timetable.php` +**Traits**: `SoftDeleteTrait` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INTEGER | Primary key | +| `admin_id` | INTEGER | FK to admin | +| `store_id` | INTEGER | FK to city_store | +| `shift_id` | INTEGER | FK to shift | +| `admin_group_id` | INTEGER | Position group | +| `tabel` | INTEGER | 0=plan, 1=fact | +| `date` | DATE | Shift date | +| `time_start` | TIME | Shift start | +| `time_end` | TIME | Shift end | +| `datetime_start` | TIMESTAMP | Full start datetime | +| `datetime_end` | TIMESTAMP | Full end datetime | +| `work_time` | DECIMAL | Hours worked | +| `salary_shift` | INTEGER | Shift salary | +| `slot_type_id` | INTEGER | Type (work/vacation) | +| `status` | INTEGER | 0=pending, 1=verified | +| `active` | INTEGER | Soft delete flag | +| `deleted_at` | TIMESTAMP | Deletion time | +| `deleted_by` | INTEGER | Deleted by admin | + +#### Slot Types + +- `1` - TIMESLOT_WORK (regular work shift) +- `2` - TIMESLOT_VACATION (vacation) +- `3` - TIMESLOT_ADMINISTRATIVE (admin leave) +- `4` - TIMESLOT_SICK_LEAVE (sick leave) +- `5` - TIMESLOT_INTERNSHIP (training) +- `6` - TIMESLOT_WEEKEND (weekend) +- `7` - TIMESLOT_FREELANCE (temporary worker) + +#### Relationships + +```php +// Timetable.php relationships +public function getAdmin() { + return $this->hasOne(Admin::class, ['id' => 'admin_id']); +} + +public function getStore() { + return $this->hasOne(CityStore::class, ['id' => 'store_id']); +} + +public function getShift() { + return $this->hasOne(Shift::class, ['id' => 'shift_id']); +} + +public function getPosition() { + return $this->hasOne(AdminGroup::class, ['id' => 'admin_group_id']); +} + +public function getDeletedBy() { + return $this->hasOne(Admin::class, ['id' => 'deleted_by']); +} +``` + +--- + +### 7. AdminPayroll (Payroll Summary) + +**Table**: `admin_payroll` +**Primary Key**: `id` (integer) +**File**: `erp24/records/AdminPayroll.php` + +#### Schema + +| Column | Type | Description | +|--------|------|-------------| +| `id` | INTEGER | Primary key | +| `admin_id` | INTEGER | FK to admin | +| `store_id` | INTEGER | FK to city_store | +| `year` | INTEGER | Year | +| `month` | INTEGER | Month (1-12) | +| `date` | VARCHAR(100) | Month description | +| `date_time` | TIMESTAMP | Created at | +| `delete_status` | INTEGER | Deletion flag | +| `date_delete` | TIMESTAMP | Deleted at | + +#### Unique Constraint + +- UNIQUE on `(admin_id, year, month)` - One payroll per employee per month + +#### Relationships + +```php +// AdminPayroll.php relationships +public function getAdmin() { + return $this->hasOne(Admin::class, ['id' => 'admin_id']); +} + +public function getStore() { + return $this->hasOne(CityStore::class, ['id' => 'store_id']); +} + +public function getDays() { + return $this->hasMany(AdminPayrollDays::class, ['admin_payroll_id' => 'id']); +} + +public function getHistory() { + return $this->hasMany(AdminPayrollHistory::class, ['admin_payroll_id' => 'id']); +} + +public function getMonthInfo() { + return $this->hasOne(AdminPayrollMonthInfo::class, ['admin_payroll_id' => 'id']); +} +``` + +--- + +## Business Domain Tables + +### Bonus & Loyalty Program + +| Table | Purpose | Records | +|-------|---------|---------| +| `users` | Customer profiles | 100K+ | +| `users_bonus` | Bonus transactions | 1M+ | +| `users_bonus_levels` | Customer tier assignments | 100K+ | +| `bonus_levels` | Tier configuration | ~10 | +| `users_events` | Memorable dates | 200K+ | +| `promocode` | Promotional codes | 1K+ | +| `referral_status` | Referral tracking | 10K+ | + +**Data Flow**: +``` +Sales → BonusService → UsersBonus (accrual) → Users.bonus_level update +``` + +--- + +### Payroll & HR + +| Table | Purpose | Records | +|-------|---------|---------| +| `admin` | Employees | 500+ | +| `admin_group` | Positions/roles | 50+ | +| `admin_payroll` | Monthly payroll | 10K+ | +| `admin_payroll_days` | Daily payroll records | 150K+ | +| `admin_payroll_values` | Payment components | 50K+ | +| `admin_payroll_values_dict` | Component types | 30+ | +| `admin_rating` | Performance ratings | 20K+ | +| `grade` | Employee grades | 10 | +| `holiday` | Holiday calendar | 500+ | + +**Data Flow**: +``` +Timetable → PayrollService → AdminPayrollDays → AdminPayroll → Payment +``` + +--- + +### Sales & Inventory + +| Table | Purpose | Records | +|-------|---------|---------| +| `sales` | Sale checks | 500K+ | +| `sales_products` | Sale line items | 2M+ | +| `sales_history` | Sales history log | 500K+ | +| `products_1c` | Product catalog from 1C | 50K+ | +| `write_offs_erp` | Inventory write-offs | 30K+ | +| `write_offs_products_erp` | Write-off line items | 100K+ | +| `matrix_bouquet` | Matrix bouquet definitions | 1K+ | + +--- + +### Task Management + +| Table | Purpose | Records | +|-------|---------|---------| +| `task` | Tasks | 50K+ | +| `task_template` | Task templates | 200+ | +| `task_type` | Task types | 30+ | +| `task_status` | Task statuses | 10+ | +| `task_logs` | Task change history | 200K+ | +| `task_motivation` | Task rewards | 10K+ | +| `task_viewers` | Task watchers | 50K+ | + +--- + +### Marketplace Integration + +| Table | Purpose | Records | +|-------|---------|---------| +| `marketplace_orders` | Marketplace orders | 50K+ | +| `marketplace_order_items` | Order line items | 150K+ | +| `marketplace_order_status_history` | Status changes | 200K+ | +| `marketplace_order_1c_statuses` | Status mappings | 50+ | +| `marketplace_status` | Status definitions | 20+ | + +--- + +### Learning & Training + +| Table | Purpose | Records | +|-------|---------|---------| +| `lesson` | Training lessons | 500+ | +| `lesson_group` | Lesson groups | 50+ | +| `lesson_poll` | Lesson quizzes | 200+ | +| `lesson_poll_answers` | Quiz answers | 5K+ | +| `regulations` | Company regulations | 100+ | +| `regulations_poll` | Regulation quizzes | 50+ | +| `regulations_passed` | Completion tracking | 10K+ | + +--- + +## System Tables + +### Authentication & Authorization (RBAC) + +| Table | Purpose | +|-------|---------| +| `auth_assignment` | User role assignments | +| `auth_item` | Roles and permissions | +| `auth_item_child` | Role hierarchy | +| `auth_rule` | Custom auth rules | + +### Logging & Monitoring + +| Table | Purpose | Size | +|-------|---------|------| +| `api_logs` | API request logs | Very Large | +| `api_error_log` | API errors | Large | +| `error_log` | Application errors | Large | +| `info_log` | Information logs | Very Large | +| `task_logs` | Task change logs | Large | + +### Configuration & Reference + +| Table | Purpose | +|-------|---------| +| `export_import_table` | 1C mapping (ID ↔ GUID) | +| `universal_catalog` | Configurable catalogs | +| `universal_catalog_item` | Catalog items | +| `dashboard_fields` | Dashboard configurations | +| `crm_menu` | Menu structure | + +--- + +## Data Flow + +### Sales Bonus Accrual Flow + +```mermaid +flowchart TD + A[1C: Create Sale] --> B[Sales Table] + B --> C{Customer in
bonus program?} + C -->|No| D[End] + C -->|Yes| E[Calculate Bonus] + E --> F{Check Products} + F -->|Exclude non-bonus items| E + F -->|Calculate base| G[Apply Tier Rate] + G --> H[Create UsersBonus
tip=plus] + H --> I[Update User Stats] + I --> J{Update Tier?} + J -->|Yes| K[Update bonus_level] + J -->|No| L[End] + K --> L +``` + +### Payroll Calculation Flow + +```mermaid +flowchart TD + A[Month End] --> B[Gather Timetable Data] + B --> C[Calculate Daily Hours] + C --> D[Create AdminPayrollDays] + D --> E[Apply Payment Components] + E --> F[Sum AdminPayrollValues] + F --> G[Create AdminPayrollMonthInfo] + G --> H[Generate AdminPayroll] + H --> I[Review & Approve] + I --> J{Approved?} + J -->|Yes| K[Mark for Payment] + J -->|No| L[Edit Components] + L --> F + K --> M[Export to 1C] +``` + +--- + +## Naming Conventions + +### Table Names + +- **snake_case**: All table names use underscore separation +- **Singular**: Table names are singular (`admin`, not `admins`) +- **Descriptive**: Clear, descriptive names + +### Column Names + +- **snake_case**: Column names use underscores +- **Suffixes**: + - `_id`: Integer foreign key (e.g., `admin_id`) + - `_id_1c`: UUID from 1C (e.g., `store_id_1c`) + - `_guid`: UUID identifier + - `_at`: Timestamp (e.g., `created_at`, `deleted_at`) + - `_arr`: Serialized array/JSON + - `_date`: Date field + - `_time`: Time field + - `_datetime`: Timestamp field + +### Relationship Patterns + +```php +// hasOne: singular method name +public function getAdmin() + +// hasMany: plural method name +public function getBonuses() + +// Through another table: descriptive name +public function getStoreByGuid() +``` + +--- + +## Migration History + +### Migration File Pattern + +``` +m{YYMMDD}_{HHMMSS}_{description}.php +``` + +**Examples**: +- `m230220_095139_function_regulations.php` +- `m230306_064243_create_table_write_offs_erp.php` +- `m230301_122735_add_access_token_column_to_admin_table.php` + +### Recent Migrations (2023-2025) + +| Date | Migration | Purpose | +|------|-----------|---------| +| 2024-12-28 | m241228_092653 | Add target_date to sent_kogort | +| 2024-11-15 | Various | Marketplace enhancements | +| 2024-08-20 | Various | Bonus system improvements | +| 2023-03-06 | m230306_064243 | Create write_offs_erp | +| 2023-03-01 | m230301_122735 | Add access_token to admin | + +### Migration Commands + +```bash +# Apply migrations +php erp24/yii migrate + +# Create new migration +php erp24/yii migrate/create migration_name + +# Rollback last migration +php erp24/yii migrate/down + +# View migration history +php erp24/yii migrate/history +``` + +--- + +## Best Practices + +### 1. Always Use ActiveRecord Relationships + +```php +// Good: Use relationship +$sales = $user->getSales()->where(['operation' => Sales::OPERATION_SALE])->all(); + +// Bad: Manual join +$sales = Sales::find()->where(['phone' => $user->phone])->all(); +``` + +### 2. Use Transactions for Multi-Table Operations + +```php +$transaction = Yii::$app->db->beginTransaction(); +try { + $sale->save(); + $bonus->save(); + $user->save(); + $transaction->commit(); +} catch (\Exception $e) { + $transaction->rollBack(); + throw $e; +} +``` + +### 3. Index High-Traffic Columns + +- Foreign keys +- Date/timestamp columns used in WHERE clauses +- Phone numbers, email addresses +- Status/type columns used for filtering + +### 4. Use Soft Deletes Where Appropriate + +```php +// Models with SoftDeleteTrait +$timetable->softDelete(); // Sets deleted_at instead of removing row +``` + +--- + +## Database Diagrams + +For visual ER diagrams, see: +- [Core Entities Diagram](./diagrams/core-entities.md) +- [Bonus System Diagram](./diagrams/bonus-system.md) +- [Payroll System Diagram](./diagrams/payroll-system.md) +- [Sales Flow Diagram](./diagrams/sales-flow.md) + +--- + +## Related Documentation + +- [ActiveRecord Models Reference](./models-reference.md) +- [Table Relationships](./relationships.md) +- [Migration Guide](./migrations.md) +- [Data Dictionary](./data-dictionary.md) + +--- + +**Last Updated**: January 2025 +**Database Version**: PostgreSQL 12+ +**Total Models**: 389 +**Total Migrations**: 278 +**Maintained By**: ERP24 Development Team diff --git a/erp24/docs/modules/README.md b/erp24/docs/modules/README.md new file mode 100644 index 00000000..d8391b45 --- /dev/null +++ b/erp24/docs/modules/README.md @@ -0,0 +1,196 @@ +# Документация бизнес-доменов ERP24 + +Данный раздел содержит подробную документацию всех бизнес-доменов (модулей) системы ERP24. + +## Обзор модулей + +### 1. [Bonus (Бонусная система)](./bonus/README.md) +Система управления бонусами сотрудников, включая начисление, конвертацию и историю бонусов. + +**Компоненты:** +- Контроллеры: 5 (BonusController, BonusLevelsController, TeambonusController, UserBonusController, AdminPersonBonusesController) +- Сервисы: 1 (BonusService) +- Модели/Records: 8 (UsersBonus, UserBonusSendToTgLogs, BonusLevels, UsersBonusLevels, AdminPersonBonuses, AdminBonusConversion, TeambonusSettings, metrics/UserBonusMetrics) +- Legacy модули: ~15 PHP файлов в modul/bonus/ + +### 2. [Payroll (Расчет заработной платы)](./payroll/README.md) +Комплексная система расчета и управления заработной платой сотрудников. + +**Компоненты:** +- Контроллеры: 3 (PayrollController, AdminPayrollController, AdminPayrollValuesDictController) +- Сервисы: 3 (PayrollService, AdminPayrollMonthInfoService, AdminPayrollDaysService) +- Модели/Records: 10 (AdminPayroll, AdminPayrollDays, AdminPayrollMonthInfo, AdminPayrollValues, AdminPayrollValuesDict, AdminPayrollStat, AdminPayrollHistory) + +### 3. [Shipment (Отгрузка)](./shipment/README.md) +Управление поставщиками и процессами отгрузки товаров. + +**Компоненты:** +- Контроллеры: 2 (ShipmentController, ShipmentProvidersController) +- Сервисы: 1 (ShipmentService) +- Модели/Records: 2 (ShipmentProviders, ShipmentProvidersSearch) + +### 4. [Timetable (Расписание/График работы)](./timetable/README.md) +Система планирования и учета рабочего времени сотрудников. + +**Компоненты:** +- Контроллеры: 2 (TimetableController, TimetableFactController) +- Сервисы: 1 (TimetableService) +- Модели/Records: 8 (Timetable, TimetableFact, TimetablePlan, TimetableShift, TimetableV3, TimetableFactV3, TimetablePlanV3, TimetableWorkbot, TimetableFactModel) +- Формы: Модуль timetable в forms/ + +### 5. [Dashboard (Информационные панели)](./dashboard/README.md) +Система создания и управления информационными панелями и дашбордами. + +**Компоненты:** +- Контроллеры: 4 (DashboardController, DashboardSalesController, DashboardListController, DashboardChartController, DashboardFieldsPropertyController) +- Сервисы: 1 (DashboardService) +- Модели/Records: 7 (Dashboard, DashboardSales, DashboardFields, DashboardFieldsLinks, DashboardFieldsProperty) +- Legacy модули: modul/dashboard/ + +### 6. [Rating (Рейтинговая система)](./rating/README.md) +Система оценки и рейтингования сотрудников/магазинов. + +**Компоненты:** +- Контроллеры: 2 (RatingController, Rating2Controller) +- Сервисы: 1 (RatingService) +- Модели/Records: 3 (AdminRating, QualityRating, QualityRatingLog) + +### 7. [Notifications (Уведомления)](./notifications/README.md) +Система управления уведомлениями для пользователей. + +**Компоненты:** +- Контроллеры: 1 (NotificationController) +- Сервисы: 1 (NotificationService) +- Модели/Records: 2 (Notification, NotificationStatus) + +### 8. [KIK Feedback (Обратная связь от контроля качества)](./kik-feedback/README.md) +Система сбора и обработки обратной связи от службы контроля качества. + +**Компоненты:** +- Контроллеры: 1 (KikFeedbackController) +- Модели/Records: 5 (KikFeedbackRequest, KikFeedbackCategory, KikFeedbackSubcategory, KikFeedbackSource, KikFeedbackVerdict) + +### 9. [Regulations (Регламенты)](./regulations/README.md) +Система управления регламентами, правилами и политиками компании. + +**Компоненты:** +- Контроллеры: 3 (RegulationsController, RegulationsPollController, crud/RegulationsController) +- Модели/Records: 7 (Regulations, RegulationsPassed, RegulationsPoll, RegulationsPollAnswers, RegulationGroup, AdminGroupRegulation, FunctionRegulations) + +### 10. [Write-offs (Списания)](./write-offs/README.md) +Управление списанием товаров, планирование и учет списаний. + +**Компоненты:** +- Контроллеры: 4 (WriteOffsController, WriteOffsErpController, WriteOffsErpCauseDictController, WaybillWriteOffsController, SalesWriteOffsPlanController) +- Сервисы: 1 (WriteOffsService) +- Модели/Records: 9 (WriteOffs, WriteOffsErp, WriteOffsProducts, WriteOffsProductsErp, WriteOffsErpCauseDict, WaybillWriteOffs, WaybillWriteOffsProducts, SalesWriteOffsPlan, metrics/WriteOffsMetrics) + +### 11. [Grade (Грейды/Должности)](./grade/README.md) +Система управления грейдами и должностными уровнями сотрудников. + +**Компоненты:** +- Контроллеры: 2 (GradeController, MyGradeController) +- Модели/Records: 4 (Grade, GradePrice, GradeGroup, AdminGradeHistory) + +### 12. [Lesson (Обучение)](./lesson/README.md) +Система обучения, уроков и опросов для сотрудников. + +**Компоненты:** +- Контроллеры: 1 (LessonController) +- Сервисы: 2 (LessonService, LessonPollService) +- Модели/Records: 5 (Lessons, LessonsGroup, LessonsPassed, LessonsPoll, LessonPollAnswers) + +--- + +## Общая статистика + +| Модуль | Контроллеры | Сервисы | Records | Legacy модули | +|--------|------------|---------|---------|---------------| +| Bonus | 5 | 1 | 8 | ~15 файлов | +| Payroll | 3 | 3 | 10 | - | +| Shipment | 2 | 1 | 2 | - | +| Timetable | 2 | 1 | 9 | Формы | +| Dashboard | 5 | 1 | 7 | ~5 файлов | +| Rating | 2 | 1 | 3 | - | +| Notifications | 1 | 1 | 2 | - | +| KIK Feedback | 1 | 0 | 5 | - | +| Regulations | 3 | 0 | 7 | - | +| Write-offs | 5 | 1 | 9 | - | +| Grade | 2 | 0 | 4 | - | +| Lesson | 1 | 2 | 5 | - | +| **ИТОГО** | **32** | **12** | **71** | **~20 файлов** | + +--- + +## Навигация по документации + +- [Главная документация](../README.md) +- [Архитектура системы](../architecture/README.md) +- [API документация](../api/README.md) +- [База данных](../database/README.md) +- [Сервисы](../services/README.md) + +--- + +## Взаимосвязи модулей + +```mermaid +graph TB + subgraph "HR & Персонал" + Payroll[Payroll
Зарплата] + Bonus[Bonus
Бонусы] + Grade[Grade
Грейды] + Timetable[Timetable
Расписание] + Rating[Rating
Рейтинг] + end + + subgraph "Обучение & Развитие" + Lesson[Lesson
Обучение] + Regulations[Regulations
Регламенты] + end + + subgraph "Операции & Логистика" + Shipment[Shipment
Отгрузка] + WriteOffs[Write-offs
Списания] + end + + subgraph "Коммуникации" + Notifications[Notifications
Уведомления] + KIK[KIK Feedback
Обратная связь] + end + + subgraph "Аналитика" + Dashboard[Dashboard
Дашборды] + end + + Payroll --> Bonus + Payroll --> Timetable + Grade --> Payroll + Timetable --> Payroll + Rating --> Bonus + Lesson --> Notifications + Regulations --> Notifications + WriteOffs --> Dashboard + Shipment --> Dashboard + Payroll --> Dashboard + Bonus --> Dashboard + Rating --> Dashboard + KIK --> Notifications + + style Payroll fill:#e1f5ff + style Bonus fill:#e1f5ff + style Grade fill:#e1f5ff + style Timetable fill:#e1f5ff + style Rating fill:#e1f5ff + style Lesson fill:#fff4e1 + style Regulations fill:#fff4e1 + style Shipment fill:#e8f5e9 + style WriteOffs fill:#e8f5e9 + style Notifications fill:#fce4ec + style KIK fill:#fce4ec + style Dashboard fill:#f3e5f5 +``` + +--- + +*Документация создана автоматически. Последнее обновление: 2025-11-17* diff --git a/erp24/docs/modules/bonus/README.md b/erp24/docs/modules/bonus/README.md new file mode 100644 index 00000000..8365690f --- /dev/null +++ b/erp24/docs/modules/bonus/README.md @@ -0,0 +1,1048 @@ +# Модуль Bonus (Бонусная система) + +## Описание + +Модуль Bonus отвечает за управление системой бонусов для клиентов и сотрудников. Включает начисление, списание, конвертацию бонусов, управление уровнями бонусов, командными бонусами и историей операций. + +## Архитектура модуля + +```mermaid +graph TB + subgraph "Контроллеры" + BC[BonusController] + BLC[BonusLevelsController] + TBC[TeambonusController] + UBC[UserBonusController] + APBC[AdminPersonBonusesController] + end + + subgraph "Сервисы" + BS[BonusService] + end + + subgraph "Actions (13 экшенов)" + StatAction[StatAction] + VozvratStats[VozvratStatsAction] + AjaxShowCheck[AjaxShowCheckAction] + AddBonuses[AddBonuses] + SexAction[SexAction] + end + + subgraph "Модели/Records (8)" + UB[UsersBonus] + UBSTL[UserBonusSendToTgLogs] + BL[BonusLevels] + UBL[UsersBonusLevels] + APB[AdminPersonBonuses] + ABC[AdminBonusConversion] + TBS[TeambonusSettings] + UBM[metrics/UserBonusMetrics] + end + + subgraph "База данных" + DB[(users_bonus
bonus_levels
users_bonus_levels
admin_person_bonuses
admin_bonus_conversion
teambonus_settings)] + end + + BC --> StatAction + BC --> VozvratStats + BC --> AjaxShowCheck + BC --> AddBonuses + BC --> SexAction + + StatAction --> BS + AddBonuses --> BS + + BS --> UB + BS --> ABC + BS --> TBS + + UB --> DB + BL --> DB + ABC --> DB + TBS --> DB + + style BC fill:#e1f5ff + style BS fill:#fff4e1 + style UB fill:#e8f5e9 + style DB fill:#fce4ec +``` + +## Компоненты модуля + +### Контроллеры (5) + +#### 1. **BonusController** +`erp24/controllers/BonusController.php` + +Основной контроллер для работы с бонусами. + +**Экшены:** +- `stat` - Статистика бонусов +- `vozvrat-stats` - Статистика возвратов +- `ajax-show-check` - Показ чека в AJAX +- `ajax-bonus-remove` - Удаление бонуса через AJAX +- `sex` - (требует уточнения) +- `add-bonus-if-written-off` - Добавление бонусов при списании +- `add-bonuses` - Массовое добавление бонусов +- `add-20-telegram-created-at-is-null` - Добавление бонусов в Telegram +- `add-bonus-to-list-of-phones` - Добавление бонусов по списку телефонов +- `bonus-users` - Список пользователей с бонусами +- `ajax-bonus-history` - История бонусов через AJAX +- `ajax-user-add-stop-list` - Добавление пользователя в стоп-лист +- `ajax-user-remove-stop-list` - Удаление пользователя из стоп-листа + +**Используемые модели:** +- `UsersBonus` +- `Sales` + +#### 2. **BonusLevelsController** +`erp24/controllers/BonusLevelsController.php` + +Управление уровнями бонусов. + +#### 3. **TeambonusController** +`erp24/controllers/TeambonusController.php` + +Управление командными бонусами. + +#### 4. **UserBonusController** +`erp24/controllers/UserBonusController.php` + +Управление пользовательскими бонусами. + +#### 5. **AdminPersonBonusesController** +`erp24/controllers/AdminPersonBonusesController.php` + +Административный контроллер для управления персональными бонусами сотрудников. + +--- + +### Сервисы (1) + +#### **BonusService** +`erp24/services/BonusService.php` + +Основной сервис для расчета бонусов с более чем 50 методами. + +**Ключевые методы:** + +##### Расчет бонусов за показатели качества +- `getBonusForQuality(float $percent): int` - Расчет бонуса за процент качества + - 80% → 3000₽ + - 90% → 4000₽ + - 100% → 5000₽ + +##### Расчет бонусов за продажи +- `getBonusClusterPercentSales(float $percent): int` - Бонус за % продаж куста + - 95% → 5000₽ + - 100% → 7000₽ + - 110% → 10000₽ + - 120% → 15000₽ + +- `getBonusSaloonSale($percent): int` - Бонус за продажи салона + - 95% → 3000₽ + - 100% → 5000₽ + - 110% → 6000₽ + - 120% → 7000₽ + +##### Расчет бонусов за списание +- `getBonusPercentLoss($percentLoss): int` - Премия за % списания + - <3% → 12000₽ + - <5% → 10000₽ + - <7% → 9000₽ + - <8% → 8000₽ + - <9% → 7000₽ + - <10% → 6000₽ + +- `getBonusClusterPercentLoss($percentLoss): int` - Премия кустового за списание куста + +##### Игровые бонусы (цветорубли) +- `getGameBonusPersonSalaryRelated(float $percent): int` - Бонус за % продаж сопутствующих товаров + - 6% → 1 цветорубль + - 8% → 2 цветорубля + - 10% → 3 цветорубля + +- `getGameBonusPersonSalaryPotted(float $percent): int` - Бонус за % горшечных растений + - 8% → 1 цветорубль + - 10% → 2 цветорубля + - 13% → 3 цветорубля + +- `getGameBonusPersonSalaryWrap(float $percent): int` - Бонус за % упаковки + - 6% → 1 цветорубль + - 8% → 2 цветорубля + - 10% → 3 цветорубля + +- `getGameBonusCashSalaryStore(float $percent): int` - Бонус за % наличных платежей + - 40% → 1 цветорубль + - 45% → 2 цветорубля + - 50% → 3 цветорубля + +- `getGameBonusMatrixSalaryShiftStore(float $percent): int` - Бонус за % чеков с матрицей + - 25% → 3 цветорубля + - 30% → 4 цветорубля + - 35% → 5 цветорублей + +- `getGameBonusPersonSalaryServices(float $percent): int` - Бонус за % услуг (личный) + - 8% → 1 цветорубль + - 12% → 2 цветорубля + - 15% → 3 цветорубля + +- `getGameBonusSalaryStoreServices(float $percent): int` - Бонус за % услуг (магазин) + - 8% → 1 цветорубль + - 10% → 2 цветорубля + - 13% → 3 цветорубля + +- `getGameBonusBonusCard(float $percent, bool $isAdministrator): int` - Бонус за бонусные карты + - Для администраторов: 80%→1, 90%→2, 95%→3 + - Для флористов: 80%→1, 90%→2 + +- `getGameBonusConversionShift(float $percent): int` - Бонус за конверсию за смену + - ≥80% → 3 цветорубля + +- `getGameBonusConversionStore(float $percent, bool $isAdmin, $storeId, $date): int` - Бонус за конверсию по магазину + - Для администраторов: ≥80% (или 40% для магазина 4) → 5 цветорублей + - Для флористов: ≥80% → 3 цветорубля + +- `getGamePersonBonusAvgCheck(float $avgCheck): int` - Бонус за средний чек (личный) + - ≥1500₽ → 2 цветорубля + - ≥1800₽ → 3 цветорубля + - ≥2200₽ → 5 цветорублей + +- `getGameBonusAvgCheck(float $avgCheck): int` - Бонус за средний чек (магазин) + - ≥1500₽ → 1 цветорубль + - ≥1700₽ → 2 цветорубля + - ≥2000₽ → 3 цветорубля + - ≥2300₽ → 5 цветорублей + +- `getGameBonusNormaSmenaReteId(int $reteId): int` - Бонус за выполнение нормы смены + - Рейты 1, 2, 3 → 1 цветорубль + +##### Коэффициенты и конвертация +- `getCoefficientPremium(float $percent): float` - Понижающий коэффициент премии при падении продаж + - 0 до -5% → 0.90 (минус 10%) + - -5% до -10% → 0.85 (минус 15%) + - -10% до -15% → 0.80 (минус 20%) + - -15% до -20% → 0.75 (минус 25%) + - более -20% → 0.75 (минус 25%) + +- `getMatrixBonusCoefficient($rowDate): float` - Коэффициент бонуса за матрицу + - До 2022-11-16: 0.025 + - 2022-11-16 до 2022-12-01: 2/115 + - После 2022-12-01: 0.02 + +- `getAuthorBonusCoefficient($rowDate): float` - Коэффициент авторского бонуса + - Всегда: 0.01 + +- `getSumConversionGameBonusToMoney($adminSumGameBonus, $year, $month)` - Конвертация игровых бонусов в деньги + - База: 1 цветорубль = 10₽ (по умолчанию) + - Может меняться по месяцам через `AdminBonusConversion` + +##### Командные бонусы +- `getTeamBonus($adminId, $storeId, $storeGuid, $dateFrom, $dateTo): array` - Расчет командного бонуса + - Формула: `(20% от продаж магазина) - (ФОТ + списания)` + - Распределение: пропорционально количеству отработанных смен + +- `getAdminTeamPayrollTable($teamBonus): array` - Таблица распределения командных бонусов + +- `getPercentTeamBonusInMonth($storeId, $dateFrom): int` - Процент командного бонуса в месяце + - По умолчанию: 0% + - Настраивается через `TeambonusSettings` + +##### Другие бонусы +- `getBonusClusterGame($rating): int` - Премия кустового за рейтинг + - 1 место → 15000₽ + - 2 место → 10000₽ + - 3 место → 5000₽ + +- `getSumPrimeForLvtClients(float $percent): int` - Премия за рост покупок постоянными клиентами + - ≥10% → 1000₽ + - ≥15% → 2000₽ + - ≥20% → 3000₽ + - ≥25% → 4000₽ + +- `getAdministratorOklad($planMonth): int` - Оклад администратора + - <1M → 25000₽ + - ≥1M → 30000₽ + - ≥1.5M → 35000₽ + - ≥2M → 40000₽ + +- `getBonusByConvertionPercent($percent, $storeId, $dateFrom, $dateTo): int` - Бонус за конверсию + - По умолчанию: 70%→1000₽, 75%→2000₽, 80%→3000₽ + - Индивидуальные настройки для магазинов (например, магазин 4) + +##### Утилитарные методы +- `getValueByLavels(float $value, array $levels)` - Получить значение по уровням (>) +- `getValueByLavelsEqualAndMore(float $value, array $levels)` - Получить значение по уровням (≥) +- `getCoefficientValueByLavels(float $value, array $levels)` - Получить коэффициент по уровням +- `getBonusNormaSmena(array $normaSmena, $summ, bool $needFormatted): int` - Бонус за норму смены +- `getWagesBonusNormaSmena(array $normaSmena, $summ, bool $needFormatted): int` - Ставка за норму смены + +**Зависимости:** +- `DateHelper` +- `Admin` +- `AdminBonusConversion` +- `AdminPayrollDays` +- `CityStore` +- `EmployeePayment` +- `TeambonusSettings` +- `Timetable` +- `WriteOffs` +- `NormaSmenaService` +- `CabinetService` +- `SalesService` + +--- + +### Actions (13) + +#### 1. **StatAction** +`erp24/actions/bonus/StatAction.php` + +Отображение статистики бонусов. + +#### 2. **VozvratStatsAction** +`erp24/actions/bonus/VozvratStatsAction.php` + +Статистика возвратов с бонусами. + +#### 3. **AjaxShowCheckAction** +`erp24/actions/bonus/AjaxShowCheckAction.php` + +AJAX экшен для отображения чека. + +#### 4. **AjaxBonusRemoveAction** +`erp24/actions/bonus/AjaxBonusRemoveAction.php` + +AJAX экшен для удаления бонуса. + +#### 5. **SexAction** +`erp24/actions/bonus/SexAction.php` + +(Требует уточнения функционала) + +#### 6. **AddBonusIfWrittenOffAction** +`erp24/actions/bonus/AddBonusIfWrittenOffAction.php` + +Добавление бонусов при списании товаров. + +#### 7. **AddBonuses** +`erp24/actions/bonus/AddBonuses.php` + +Массовое добавление бонусов. + +#### 8. **Add20TelegramCreatedAtIsNullAction** +`erp24/actions/bonus/Add20TelegramCreatedAtIsNullAction.php` + +Специальный экшен для добавления бонусов в Telegram. + +#### 9. **AddBonusToListOfPhones** +`erp24/actions/bonus/AddBonusToListOfPhones.php` + +Добавление бонусов по списку телефонных номеров. + +#### 10. **UsersAction** +`erp24/actions/bonus/UsersAction.php` + +Работа с пользователями бонусной программы. + +#### 11. **AjaxBonusHistoryAction** +`erp24/actions/bonus/AjaxBonusHistoryAction.php` + +AJAX экшен для отображения истории бонусов. + +#### 12. **AjaxUserAddStopListAction** +`erp24/actions/bonus/AjaxUserAddStopListAction.php` + +AJAX экшен для добавления пользователя в стоп-лист. + +#### 13. **AjaxUserRemoveStopListAction** +`erp24/actions/bonus/AjaxUserRemoveStopListAction.php` + +AJAX экшен для удаления пользователя из стоп-листа. + +--- + +### Модели/Records (8) + +#### 1. **UsersBonus** +`erp24/records/UsersBonus.php` + +Основная таблица движения бонусов клиентов. + +**Таблица:** `users_bonus` + +**Поля:** +- `id` (int) - ID записи +- `phone` (string) - Номер телефона клиента +- `name` (string) - Наименование движения +- `date` (datetime) - Дата и время движения +- `user_id` (int) - ID клиента из таблицы users +- `lid_id` (int) - ID интернет-заказа +- `store_id` (int) - ID магазина (city_store) +- `site_id` (int) - ID сайта (seka_sites) +- `setka_id` (int) - ID сетки сайтов (setka_sites) +- `check_id` (string) - GUID чека (sales) +- `tip` (enum) - Тип движения: начисление/списание/сгорание +- `tip_sale` (string) - Тип движения для понимания операции +- `price` (float) - Сумма продажи +- `price_skidka` (float) - Размер скидки при списании +- `bonus` (float) - Начисленные или списанные бонусы +- `dell` (int) - Флаг удаления из ERP +- `date_start` (datetime) - Дата старта действия бонусов +- `date_end` (datetime) - Дата окончания действия бонусов +- `date_dell` (datetime) - Дата и время автоматического удаления +- `status` (int) - Статус внесения в базу (для импорта из БонусПлюс) +- `admin_id` (int) - ID сотрудника, добавившего движение +- `referal_id` (int) - ID реферала +- `store_id_1c` (string) - GUID магазина (products_1c) +- `seller_id_1c` (string) - GUID продавца (products_1c) +- `ip` (string) - IP адрес откуда была добавлена запись + +**Дополнительные свойства:** +- `sum` - Сумма бонусов +- `plus` - Начисленные бонусы +- `minus` - Списанные бонусы + +#### 2. **UserBonusSendToTgLogs** +`erp24/records/UserBonusSendToTgLogs.php` + +Логи отправки бонусов в Telegram. + +**Таблица:** `user_bonus_send_to_tg_logs` + +#### 3. **BonusLevels** +`erp24/records/BonusLevels.php` + +Уровни бонусов. + +**Таблица:** `bonus_levels` + +#### 4. **UsersBonusLevels** +`erp24/records/UsersBonusLevels.php` + +Связь пользователей и уровней бонусов. + +**Таблица:** `users_bonus_levels` + +#### 5. **AdminPersonBonuses** +`erp24/records/AdminPersonBonuses.php` + +Персональные бонусы сотрудников. + +**Таблица:** `admin_person_bonuses` + +**Используется в:** AdminPersonBonusesController + +#### 6. **AdminBonusConversion** +`erp24/records/AdminBonusConversion.php` + +Настройки конвертации бонусов (цветорублей) в деньги. + +**Таблица:** `admin_bonus_conversion` + +**Поля:** +- `id` (int) - ID записи +- `date` (string) - Дата (YYYY-MM формат) +- `base` (int) - Базовая единица (по умолчанию 1) +- `cost` (int) - Стоимость единицы (по умолчанию 10₽) + +**Пример:** +```php +// 1 цветорубль = 10 рублей (по умолчанию) +['date' => '2024-01', 'base' => 1, 'cost' => 10] + +// 1 цветорубль = 15 рублей (спецпредложение) +['date' => '2024-12', 'base' => 1, 'cost' => 15] +``` + +#### 7. **TeambonusSettings** +`erp24/records/TeambonusSettings.php` + +Настройки командных бонусов. + +**Таблица:** `teambonus_settings` + +**Поля:** +- `id` (int) - ID записи +- `store_id` (int) - ID магазина +- `year` (int) - Год +- `month` (int) - Месяц +- `procent` (int) - Процент от продаж для командного бонуса +- `created_at` (datetime) - Дата создания + +**Пример:** +```php +// Командный бонус 20% от продаж для магазина 5 в январе 2024 +['store_id' => 5, 'year' => 2024, 'month' => 1, 'procent' => 20] +``` + +#### 8. **UserBonusMetrics** +`erp24/records/metrics/UserBonusMetrics.php` + +Метрики бонусов пользователей. + +**Таблица:** `metrics` (с фильтром по типу) + +--- + +### Legacy компоненты + +#### Legacy модули (modul/bonus/) + +В директории `erp24/modul/bonus/` находятся старые PHP-файлы (~15 файлов), которые постепенно мигрируются в новую архитектуру: + +- `index.php` - Главная страница модуля бонусов +- `add.php` - Добавление бонусов +- `dell.php` - Удаление бонусов +- `info.php` - Информация о бонусах +- `bonus_reg.php` - Регистрация бонусов +- `dashboard.php` - Дашборд бонусов +- `users_events.php` - События пользователей +- `man_db.php` - Управление БД +- `ajax_show_check.php` - AJAX показ чека +- `user_history.php` - История пользователя +- `bonusplus_api.php` - API БонусПлюс +- `import_clients.php` - Импорт клиентов +- `ajax_user_remove_stop_list.php` - AJAX удаление из стоп-листа +- `sex.php` - (требует уточнения) +- `ajax_bonus_remove.php` - AJAX удаление бонуса +- `ajax_sales.php` - AJAX продажи + +**Рекомендация:** Постепенная миграция на новые Actions в `erp24/actions/bonus/`. + +#### Legacy библиотека (inc/bonus.php) + +`erp24/inc/bonus.php` - Старая процедурная библиотека для работы с бонусами клиентов. + +**Содержимое:** + +##### Конфигурация магазинов (hardcoded) +```php +$store_arr = [ + "Ванеева 181" => 1, + "Белинка" => 2, + "Гагарина" => 3, + "Аэродромная" => 4, + "Горького" => 6, + "Плотникого" => 7, + "Шайба" => 8, + "Народная" => 9, + "Гагарина 19" => 10, + "Печеры" => 13, + // и другие... +]; +``` + +##### Функции работы с бонусами: + +**1. `get_bonus_balans($phone, $update="")`** + +Получение баланса бонусов клиента по номеру телефона. + +**Параметры:** +- `$phone` - Номер телефона клиента +- `$update` - Если "update", обновляет баланс в таблице users + +**Логика:** +```php +// Сумма начисленных бонусов (tip='plus', date_start<=NOW()) +$plus = SUM(bonus) WHERE phone=? AND tip='plus'; + +// Сумма списанных бонусов (tip='minus') +$minus = SUM(bonus) WHERE phone=? AND tip='minus'; + +// Баланс +$balans = $plus - $minus; + +// Опционально обновление users.balans +if ($update == "update") { + UPDATE users SET balans=?, balans_datetime=NOW() WHERE phone=? +} +``` + +**Пример:** +```php +$balance = get_bonus_balans('+79001234567'); +// → 350 (бонусов доступно) + +$balance = get_bonus_balans('+79001234567', 'update'); +// → 350 (и обновлено поле users.balans) +``` + +**2. `repl_bonus_text($text, $row)`** + +Шаблонизатор для подстановки данных клиента в текст. + +**Поддерживаемые плейсхолдеры:** +- `{{bonus.date_end}}` - Дата окончания бонусов +- `{{bonus.date_end_full}}` - Дата окончания (полная) +- `{{users.name}}` - Имя клиента +- `{{users.name_name}}` - Имя +- `{{users.name_last}}` - Фамилия +- `{{users.name_family}}` - Отчество +- `{{users.bonus}}` - Количество бонусов +- `{{users.balans}}` - Баланс бонусов +- `{{users.bdate}}` - День рождения + +**Пример:** +```php +$template = "Здравствуйте, {{users.name}}! У вас {{users.balans}} бонусов. Срок действия до {{bonus.date_end}}."; + +$data = [ + 'name' => 'Иван', + 'balans' => 350, + 'date_end' => '31.12.2024' +]; + +$text = repl_bonus_text($template, $data); +// → "Здравствуйте, Иван! У вас 350 бонусов. Срок действия до 31.12.2024." +``` + +**3. `bonus_user_add($arr)`** + +Создание нового пользователя в системе бонусов. + +**Параметры массива:** +- `phone` - Номер телефона +- `name` - Имя клиента +- `site_id` - ID сайта +- `setka_id` - ID сетки сайтов + +**Генерирует:** +- Случайный код доступа (5 цифр) +- Пароль = код доступа + +**4. `bonus_act($arr)`** + +Создание движения бонусов (начисление/списание). + +**Параметры массива:** +- `phone` - Номер телефона +- `bonus` - Количество бонусов +- `tip` - Тип ("plus" или "minus") +- `lid_id` - ID заказа (опционально) +- `store_id` - ID магазина +- `user_id` - ID пользователя +- `ip` - IP адрес + +**Пример:** +```php +bonus_act([ + 'phone' => '+79001234567', + 'bonus' => 150, + 'tip' => 'plus', + 'lid_id' => 12345, + 'store_id' => 5, + 'user_id' => 1, + 'ip' => '192.168.1.1' +]); +// → INSERT INTO users_bonus (name, date, lid_id, store_id, tip, bonus, ip, user_id) +``` + +**5. `user_date_add($arr)`** + +Добавление даты для пользователя (возможно, день рождения или важная дата). + +**6. `send_sms_mass()`** + +Массовая отправка SMS клиентам. + +**Интеграция:** SMS.ru API + +**Логика:** +1. Получает очередь SMS из таблицы `sms` +2. Отправляет через SMS.ru +3. Обновляет статус отправки +4. Записывает UID сообщения + +**Особенности:** +- Тестовый режим: `$data->test = 1` +- Отложенная отправка: `$data->time = time() + 7*60*60` +- Транслитерация: `$data->translit = 1` +- Кастомный отправитель: `$data->from = "BazaCvetov"` + +**7. `uni_mess($messanger="sms", $phones_arr=[], $text_arr=[])`** + +Универсальная функция отправки сообщений. + +**Параметры:** +- `$messanger` - Тип мессенджера (пока только "sms") +- `$phones_arr` - Массив телефонов +- `$text_arr` - Массив текстов (соответствие по индексу) + +**Логика:** +1. Проверяет, не отправлялось ли такое SMS за последние сутки +2. Если нет - добавляет в очередь (таблица `sms`) +3. SMS отправятся через `send_sms_mass()` + +**Пример:** +```php +uni_mess( + "sms", + ['+79001234567', '+79007654321'], + ['У вас 150 бонусов!', 'У вас 200 бонусов!'] +); +``` + +**8. `form_date($date0)`** + +Преобразование даты из формата DD.MM.YYYY в YYYY-MM-DD. + +**Пример:** +```php +form_date('31.12.2024'); // → '2024-12-31' +form_date('31.12.2024 14:30:00'); // → '2024-12-31 14:30:00' +``` + +--- + +**⚠️ Статус:** DEPRECATED + +Этот файл использует устаревшие подходы: +- Глобальные переменные (`global $db`) +- Процедурный стиль +- Хардкод значений +- Устаревшие MySQL функции (mysql_query, mysql_num_rows) +- Нет валидации и санитизации +- Нет обработки ошибок + +**🔄 Миграция:** + +Функциональность постепенно переносится в: +- **BonusService** - расчет бонусов +- **UsersBonus model** - работа с балансами +- **NotificationService** - отправка уведомлений +- **Actions** - бизнес-логика + +**📌 Использование:** + +Файл подключается через `require_once` в legacy коде: +```php +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/bonus.php'; +``` + +**Не рекомендуется** использовать в новом коде! + +--- + +## Бизнес-логика + +### Типы бонусов + +1. **Игровые бонусы (цветорубли)** - внутренняя валюта для мотивации сотрудников +2. **Денежные бонусы** - прямые денежные премии +3. **Клиентские бонусы** - бонусы для клиентов (начисление, списание, сгорание) +4. **Командные бонусы** - распределение премиального фонда между командой магазина + +### Основные критерии начисления бонусов + +#### Для флористов: +- Выполнение нормы смены +- Средний чек +- % сопутствующих товаров +- % горшечных растений +- % упаковки +- % услуг +- % матрицы в чеках +- Конверсия + +#### Для магазина (общие критерии): +- % наличных платежей +- % бонусных карт +- % услуг +- % матрицы в чеках +- Средний чек +- Конверсия + +#### Для администраторов: +- % выполнения плана продаж +- % списания +- % ФОТ от продаж +- Качество работы +- Конверсия +- Рейтинг среди магазинов + +#### Для кустовых менеджеров: +- % выполнения плана продаж куста +- % списания куста +- Рейтинг по цветорублям +- Динамика год к году + +### Формула командного бонуса + +``` +Премиальный фонд = (X% от продаж магазина) - (ФОТ + Списания) + +где: +- X% - процент командного бонуса (настраивается через TeambonusSettings, обычно 20%) +- ФОТ - фонд оплаты труда (оклады + премии) +- Списания - сумма бракованных товаров + +Персональная доля = (Премиальный фонд / Количество смен всех) × Количество смен сотрудника +``` + +**Пример расчета:** +``` +Продажи магазина: 5 000 000₽ +20% от продаж: 1 000 000₽ +ФОТ (оклады + премии): 600 000₽ +Списания (брак): 100 000₽ +───────────────────────── +Премиальный фонд: 1 000 000 - 600 000 - 100 000 = 300 000₽ + +Всего смен в магазине: 150 +Смен сотрудника: 15 +───────────────────────── +Персональная доля: (300 000 / 150) × 15 = 30 000₽ +``` + +### Конвертация игровых бонусов + +Игровые бонусы (цветорубли) конвертируются в реальные деньги по курсу, который может меняться каждый месяц: + +```php +// По умолчанию +1 цветорубль = 10 рублей + +// Можно настроить через AdminBonusConversion +// Например, в декабре (предновогодний бонус): +1 цветорубль = 15 рублей +``` + +### Понижающие коэффициенты + +При падении продаж год к году применяется понижающий коэффициент к премии: + +| Падение продаж | Коэффициент | Снижение премии | +|----------------|-------------|-----------------| +| 0% до -5% | 0.90 | -10% | +| -5% до -10% | 0.85 | -15% | +| -10% до -15% | 0.80 | -20% | +| -15% до -20% | 0.75 | -25% | +| более -20% | 0.75 | -25% | + +--- + +## API и интеграции + +### Внутренние зависимости + +Модуль взаимодействует с: +- **Payroll** - для расчета ФОТ и зарплаты +- **Timetable** - для подсчета смен +- **WriteOffs** - для учета списаний +- **Sales** - для данных о продажах +- **Rating** - для рейтингов магазинов +- **Admin** - для данных о сотрудниках +- **CityStore** - для данных о магазинах + +### Внешние интеграции + +- **БонусПлюс** - импорт клиентов и бонусов из внешней системы +- **Telegram** - отправка уведомлений о начислении бонусов + +--- + +## Маршруты (Routes) + +``` +/bonus/stat - Статистика бонусов +/bonus/vozvrat-stats - Статистика возвратов +/bonus/ajax-show-check - Показ чека (AJAX) +/bonus/ajax-bonus-remove - Удаление бонуса (AJAX) +/bonus/sex - (требует уточнения) +/bonus/add-bonus-if-written-off - Добавление бонусов при списании +/bonus/add-bonuses - Массовое добавление бонусов +/bonus/add-20-telegram-created-at-is-null - Добавление бонусов в Telegram +/bonus/add-bonus-to-list-of-phones - Добавление бонусов по списку телефонов +/bonus/bonus-users - Список пользователей с бонусами +/bonus/ajax-bonus-history - История бонусов (AJAX) +/bonus/ajax-user-add-stop-list - Добавление в стоп-лист (AJAX) +/bonus/ajax-user-remove-stop-list - Удаление из стоп-листа (AJAX) + +/bonus-levels/* - Управление уровнями бонусов +/teambonus/* - Управление командными бонусами +/user-bonus/* - Управление пользовательскими бонусами +/admin-person-bonuses/* - Управление персональными бонусами сотрудников +``` + +--- + +## Примеры использования + +### Пример 1: Расчет бонуса за качество + +```php +$bonusService = new BonusService(); + +// Качество 95% +$bonus = $bonusService->getBonusForQuality(95); +// Результат: 4000₽ + +// Качество 100% +$bonus = $bonusService->getBonusForQuality(100); +// Результат: 5000₽ +``` + +### Пример 2: Расчет игровых бонусов флориста за смену + +```php +$bonusService = new BonusService(); + +// Средний чек 1900₽ +$avgCheckBonus = $bonusService->getGamePersonBonusAvgCheck(1900); +// Результат: 3 цветорубля + +// Сопутка 9% +$relatedBonus = $bonusService->getGameBonusPersonSalaryRelated(9); +// Результат: 2 цветорубля + +// Упаковка 11% +$wrapBonus = $bonusService->getGameBonusPersonSalaryWrap(11); +// Результат: 3 цветорубля + +// Всего за смену: 3 + 2 + 3 = 8 цветорублей +``` + +### Пример 3: Конвертация игровых бонусов в деньги + +```php +$bonusService = new BonusService(); + +// Сотрудник заработал 100 цветорублей за январь 2024 +$conversion = $bonusService->getSumConversionGameBonusToMoney(100, '2024', '01'); + +echo "Сумма: {$conversion['money']} руб."; // 1000 руб. +echo "Курс: {$conversion['base']} цветорубль = {$conversion['cost']} руб."; // 1 цветорубль = 10 руб. +``` + +### Пример 4: Расчет командного бонуса + +```php +$bonusService = new BonusService(); + +$teamBonus = $bonusService->getTeamBonus( + $adminId = 123, + $storeId = 5, + $storeGuid = 'abc-123-def', + $dateFrom = '2024-01-01', + $dateTo = '2024-01-31' +); + +echo "Премиальный фонд магазина: {$teamBonus['primeFondStore']} руб.\n"; +echo "Смен всего: {$teamBonus['shiftCountAll']}\n"; +echo "Смен сотрудника: {$teamBonus['personShiftCount']}\n"; +echo "Персональная доля: {$teamBonus['personPrimeFondStore']} руб.\n"; +``` + +### Пример 5: Добавление бонусов клиенту + +```php +$userBonus = new UsersBonus(); +$userBonus->phone = '+79001234567'; +$userBonus->name = 'Бонус за покупку'; +$userBonus->date = date('Y-m-d H:i:s'); +$userBonus->store_id = 5; +$userBonus->check_id = 'abc-123-def-456'; +$userBonus->tip = 'начисление'; +$userBonus->tip_sale = 'Покупка букета'; +$userBonus->price = 3000; +$userBonus->bonus = 150; // 5% от суммы +$userBonus->date_start = date('Y-m-d H:i:s'); +$userBonus->date_end = date('Y-m-d H:i:s', strtotime('+1 year')); +$userBonus->admin_id = Yii::$app->user->id; +$userBonus->save(); +``` + +--- + +## База данных + +### Основные таблицы + +1. **users_bonus** - Движения бонусов клиентов +2. **bonus_levels** - Уровни бонусов +3. **users_bonus_levels** - Связь пользователей и уровней +4. **admin_person_bonuses** - Персональные бонусы сотрудников +5. **admin_bonus_conversion** - Настройки конвертации бонусов +6. **teambonus_settings** - Настройки командных бонусов +7. **user_bonus_send_to_tg_logs** - Логи отправки в Telegram + +### ER-диаграмма + +```mermaid +erDiagram + users_bonus ||--o{ sales : "check_id" + users_bonus }o--|| city_store : "store_id" + users_bonus }o--|| admin : "admin_id" + users_bonus }o--o| users : "user_id" + + users_bonus_levels }o--|| bonus_levels : "level_id" + users_bonus_levels }o--|| users : "user_id" + + admin_person_bonuses }o--|| admin : "admin_id" + + teambonus_settings }o--|| city_store : "store_id" + + admin_bonus_conversion { + int id PK + string date + int base + int cost + } + + users_bonus { + int id PK + string phone + string name + datetime date + int user_id FK + int store_id FK + string check_id FK + enum tip + float bonus + datetime date_start + datetime date_end + int admin_id FK + } +``` + +--- + +## Метрики и аналитика + +### Ключевые метрики + +1. **Общая сумма начисленных бонусов клиентам** +2. **Общая сумма списанных бонусов клиентами** +3. **Общая сумма сгоревших бонусов** +4. **Средняя сумма бонусов на клиента** +5. **Общая сумма игровых бонусов (цветорублей) по сотрудникам** +6. **Средняя сумма игровых бонусов на сотрудника** +7. **Общая сумма денежных премий** +8. **% ФОТ от продаж** +9. **Эффективность командных бонусов** + +--- + +## Вопросы для уточнения + +1. ❓ Что делает экшен `sex` в BonusController? +2. ❓ Есть ли документация по интеграции с БонусПлюс API? +3. ❓ Какие правила сгорания бонусов клиентов? +4. ❓ Как обрабатываются возвраты и отмены заказов? +5. ❓ Есть ли лимиты на начисление/списание бонусов? + +--- + +## Связанные модули + +- [Payroll (Расчет заработной платы)](../payroll/README.md) +- [Timetable (Расписание)](../timetable/README.md) +- [Write-offs (Списания)](../write-offs/README.md) +- [Rating (Рейтинговая система)](../rating/README.md) +- [Dashboard (Информационные панели)](../dashboard/README.md) + +--- + +*Документация создана автоматически. Последнее обновление: 2025-11-17* diff --git a/erp24/docs/modules/dashboard/README.md b/erp24/docs/modules/dashboard/README.md new file mode 100644 index 00000000..a13ade86 --- /dev/null +++ b/erp24/docs/modules/dashboard/README.md @@ -0,0 +1,1034 @@ +# Модуль Dashboard (Информационные панели и дашборды) + +## Описание + +Модуль Dashboard отвечает за создание, настройку и отображение информационных панелей (дашбордов) для визуализации ключевых показателей эффективности (KPI) бизнеса. Позволяет отслеживать продажи, трафик, конверсию, средний чек и другие метрики в реальном времени с возможностью фильтрации по магазинам, датам и группам пользователей. + +## Архитектура модуля + +```mermaid +graph TB + subgraph "Контроллеры (5)" + DC[DashboardController] + DSC[DashboardSalesController] + DLC[DashboardListController] + DCC[DashboardChartController] + DFPC[DashboardFieldsPropertyController] + end + + subgraph "Сервисы" + DS[DashboardService] + end + + subgraph "Actions (13)" + IndexAction[IndexAction - Главная] + SalesAction[SalesAction - Продажи] + CommercialAction[CommercialAction - Коммерческие] + MarketplaceAction[MarketplaceSalesReportAction] + ValidateAction[ValidateAction] + end + + subgraph "Модели/Records (9)" + D[Dashboard
Конфигурация дашборда] + DSales[DashboardSales
Данные продаж] + DFields[DashboardFields
Поля дашборда] + DFieldsLinks[DashboardFieldsLinks
Связи полей] + DFieldsProperty[DashboardFieldsProperty
Свойства полей] + end + + subgraph "База данных" + DB[(dashboard
dashboard_sales
dashboard_fields
dashboard_fields_links
dashboard_fields_property)] + end + + DC --> IndexAction + DC --> SalesAction + DC --> CommercialAction + + IndexAction --> DS + SalesAction --> DS + + DS --> DSales + DS --> DFields + + D --> DFieldsLinks + DSales --> DFields + DSales --> CityStore + + D --> DB + DSales --> DB + DFields --> DB + + style DC fill:#e1f5ff + style DS fill:#fff4e1 + style D fill:#e8f5e9 + style DB fill:#fce4ec +``` + +## Компоненты модуля + +### Контроллеры (5) + +#### 1. **DashboardController** +`erp24/controllers/DashboardController.php` + +Основной контроллер для работы с дашбордами. + +**Экшены:** + +##### Основные дашборды: +- `index` - Главная страница дашбордов +- `sales` - Дашборд продаж +- `sales-detail` - Детальная информация по продажам + +##### Коммерческие дашборды: +- `commercial` - Коммерческий дашборд +- `commercial-detail-info` - Детальная информация +- `commercial-sales-info` - Информация о продажах +- `commercial-multiple` - Множественный коммерческий дашборд +- `commercial-form-submission` - Отправка формы +- `commercial-up-vote` - Положительная оценка +- `commercial-down-vote` - Отрицательная оценка + +##### Специальные: +- `marketplace-sales-report` - Отчет продаж маркетплейсов +- `validate` - Валидация данных +- `check-mismatch` - Проверка несоответствий + +**Действие по умолчанию:** `index` + +#### 2. **DashboardSalesController** +`erp24/controllers/DashboardSalesController.php` + +Контроллер для работы с данными продаж на дашбордах. + +**Функционал:** +- CRUD операции с данными продаж +- Фильтрация по периодам +- Экспорт данных + +#### 3. **DashboardListController** +`erp24/controllers/DashboardListController.php` + +Контроллер для управления списками дашбордов. + +**Функционал:** +- Список всех дашбордов +- Создание/редактирование дашбордов +- Управление правами доступа + +#### 4. **DashboardChartController** +`erp24/controllers/DashboardChartController.php` + +Контроллер для работы с графиками и диаграммами. + +**Функционал:** +- Генерация графиков +- Настройка визуализации +- Экспорт графиков + +#### 5. **DashboardFieldsPropertyController** +`erp24/controllers/DashboardFieldsPropertyController.php` + +Контроллер для управления свойствами полей дашбордов. + +**Функционал:** +- Настройка отображения полей +- Управление формулами расчета +- Настройка цветовых индикаторов + +--- + +### Сервисы (1) + +#### **DashboardService** +`erp24/services/DashboardService.php` + +Сервис для работы с дашбордами и расчета метрик. + +**Ключевые методы:** + +##### Расчет трафика магазинов +```php +public function getStoreTraffic(array $data_store_visitors): array +``` + +Агрегирует данные о посещаемости магазинов. + +**Параметры:** +- `$data_store_visitors` - Массив данных о посетителях с полями: + - `date` - Дата + - `counter` - Счетчик посетителей + - `store_id` - ID магазина + +**Возвращает:** Массив с ключами `{date}.{store_id}` и суммами посетителей + +**Пример:** +```php +$visitors = [ + ['date' => '2024-01-15', 'counter' => 50, 'store_id' => 5], + ['date' => '2024-01-15', 'counter' => 30, 'store_id' => 5], + ['date' => '2024-01-15', 'counter' => 40, 'store_id' => 7], +]; + +$traffic = $service->getStoreTraffic($visitors); +// [ +// '2024-01-15' => [ +// 5 => 80, // 50 + 30 +// 7 => 40 +// ] +// ] +``` + +##### Расчет продаж с процентом выполнения плана +```php +public function getSalesSumWithCityStoreId( + array $sales_sum, + array $plan, + array $city_stores +): array +``` + +Рассчитывает продажи по магазинам с процентом выполнения плана. + +**Параметры:** +- `$sales_sum` - Массив продаж `[store_id => summ]` +- `$plan` - Массив планов `[store_id => plan]` +- `$city_stores` - Массив магазинов `[store_id => name]` + +**Возвращает:** +- `sales` - Массив магазинов с данными: + - `store` - Название магазина + - `store_id` - ID магазина + - `summ` - Сумма продаж + - `plan` - План продаж + - `percent` - Процент выполнения плана +- `sales_summ_all` - Общая сумма продаж + +**Сортировка:** По убыванию процента выполнения + +**Пример:** +```php +$sales = [5 => 850000, 7 => 920000, 12 => 780000]; +$plan = [5 => 800000, 7 => 1000000, 12 => 900000]; +$stores = [5 => 'Ванеева', 7 => 'Горького', 12 => 'Белинка']; + +$result = $service->getSalesSumWithCityStoreId($sales, $plan, $stores); +// [ +// 'sales' => [ +// ['store' => 'Ванеева', 'store_id' => 5, 'summ' => 850000, 'plan' => 800000, 'percent' => 106], +// ['store' => 'Горького', 'store_id' => 7, 'summ' => 920000, 'plan' => 1000000, 'percent' => 92], +// ['store' => 'Белинка', 'store_id' => 12, 'summ' => 780000, 'plan' => 900000, 'percent' => 87], +// ], +// 'sales_summ_all' => 2550000 +// ] +``` + +##### Сбор и сохранение данных дашборда +```php +public static function setData( + $paramDateFrom = null, + $paramDateTo = null, + $paramMinusDays = null, + $printAllow = false +) +``` + +Основной метод для сбора всех данных дашборда за период. + +**Параметры:** +- `$paramDateFrom` - Дата начала периода (по умолчанию: сегодня 00:00) +- `$paramDateTo` - Дата окончания периода (по умолчанию: сейчас) +- `$paramMinusDays` - Количество дней назад от текущей даты +- `$printAllow` - Вывод отладочной информации (boolean) + +**Процесс сбора данных:** + +1. **Инициализация периода:** +```php +if ($paramMinusDays) { + $dateFrom = date("Y-m-d 00:00:00", time() - 86400 * $paramMinusDays); + $dateTo = date("Y-m-d 23:59:59", time()); +} +``` + +2. **Получение активных полей дашборда:** +```php +$fields_arr = DashboardFields::find() + ->select(['id', 'name']) + ->andWhere(['active' => 1]) + ->andWhere(['<>', 'name', 'sales_summ']) + ->all(); +``` + +3. **Расчет продаж по магазинам:** +```php +$dataArray = self::return_sales_stores($dateFrom, $dateTo); +``` + +4. **Расчет метрик по классам товаров:** + +Для каждого поля типа `wrap` (упаковка), `services` (услуги), `potted` (горшечные): +- Рассчитывается сумма продаж класса товаров +- Рассчитывается процент от общих продаж +- Сохраняется в `dashboard_sales` + +**Пример для услуг:** +```php +// Если поле = "services" +$arrayData = self::return_sale_products_class($dateFrom, $dateTo, 'services', $field_id, ""); + +// Расчет процента услуг +foreach ($arrayData as $date => $array) { + foreach ($array as $storeId => $value) { + $percent = round(($value / $dataArray["sales_summ"][$date][$storeId]) * 100); + // Сохранить в поле "services_percent" + } +} +``` + +5. **Расчет входящего трафика:** +```php +if ($filed_name == "incoming_traffic") { + $dataArray["incoming_traffic"] = self::return_incoming_traffic_stores($dateFrom, $dateTo, "incoming_traffic", "7"); +} +``` + +6. **Расчет конверсии:** +```php +// Конверсия = (Количество чеков / Входящий трафик) * 100 +$massivSQL = []; +foreach ($dataArray["checks_counter"] as $date => $array) { + foreach ($array as $storeId => $checksCount) { + $traffic = $dataArray["incoming_traffic"][$date][$storeId] ?? 0; + if ($traffic > 0) { + $conversion = round(($checksCount / $traffic) * 100); + $massivSQL[$date][$storeId] = $conversion; + } + } +} +``` + +**Вспомогательные методы (предполагаемые):** +- `return_sales_stores($dateFrom, $dateTo)` - Возвращает продажи по магазинам +- `return_sale_products_class($dateFrom, $dateTo, $class, $fieldId, $param)` - Возвращает продажи по классу товаров +- `return_incoming_traffic_stores($dateFrom, $dateTo, $name, $fieldId)` - Возвращает входящий трафик +- `insert_data_in_dashboard_sales($data, $fieldName, $fieldId)` - Сохраняет данные в БД + +**Зависимости:** +- `DashboardFields` +- `DashboardSales` +- `Sales` +- `ExportImportService` +- `DateHelper` + +--- + +### Actions (13) + +#### **Основные** + +##### 1. **IndexAction** +`erp24/actions/dashboard/IndexAction.php` + +Главная страница дашбордов. + +**Функционал:** +- Список доступных дашбордов +- Быстрая статистика +- Навигация по дашбордам + +##### 2. **SalesAction** +`erp24/actions/dashboard/SalesAction.php` + +Дашборд продаж. + +**Функционал:** +- Продажи по магазинам за период +- Сравнение с планом +- Процент выполнения +- Динамика продаж + +##### 3. **SalesDetailAction** +`erp24/actions/dashboard/SalesDetailAction.php` + +Детальная информация по продажам. + +**Функционал:** +- Детализация по категориям товаров +- Продажи по часам/дням +- ТОП товары +- ТОП продавцы + +--- + +#### **Коммерческие дашборды** + +##### 4. **CommercialAction** +`erp24/actions/dashboard/CommercialAction.php` + +Коммерческий дашборд. + +**Функционал:** +- Метрики для коммерческого отдела +- Анализ ассортимента +- Оборачиваемость товаров + +##### 5. **CommercialDetailInfoAction** +`erp24/actions/dashboard/CommercialDetailInfoAction.php` + +Детальная коммерческая информация. + +##### 6. **CommercialSalesInfoAction** +`erp24/actions/dashboard/CommercialSalesInfoAction.php` + +Информация о продажах для коммерческого отдела. + +##### 7. **CommercialMultipleAction** +`erp24/actions/dashboard/CommercialMultipleAction.php` + +Множественный коммерческий дашборд (сравнение нескольких показателей). + +##### 8. **CommercialFormSubmissionAction** +`erp24/actions/dashboard/CommercialFormSubmissionAction.php` + +Обработка форм коммерческого дашборда. + +##### 9. **CommercialUpVoteAction** +`erp24/actions/dashboard/CommercialUpVoteAction.php` + +Положительная оценка (лайк) для элементов дашборда. + +**Функционал:** +- Голосование за товары/категории +- Сбор feedback + +##### 10. **CommercialDownVoteAction** +`erp24/actions/dashboard/CommercialDownVoteAction.php` + +Отрицательная оценка (дизлайк) для элементов дашборда. + +--- + +#### **Специальные** + +##### 11. **MarketplaceSalesReportAction** +`erp24/actions/dashboard/MarketplaceSalesReportAction.php` + +Отчет продаж через маркетплейсы (Wildberries, Ozon и др.). + +**Функционал:** +- Продажи по маркетплейсам +- Сравнение с собственными магазинами +- Комиссии и маржинальность + +##### 12. **ValidateAction** +`erp24/actions/dashboard/ValidateAction.php` + +Валидация данных дашборда. + +**Функционал:** +- Проверка корректности данных +- Поиск аномалий +- Исправление ошибок + +##### 13. **CheckMismatchAction** +`erp24/actions/dashboard/CheckMismatchAction.php` + +Проверка несоответствий в данных. + +**Функционал:** +- Сравнение данных из разных источников +- Выявление расхождений +- Отчет о проблемах + +--- + +### Модели/Records (9) + +#### 1. **Dashboard** +`erp24/records/Dashboard.php` + +Конфигурация дашборда. + +**Таблица:** `dashboard` + +**Поля:** +- `id` (int) - ID дашборда +- `name` (string, 255) - Название дашборда +- `group_id` (int) - ID группы пользователей с доступом + +**Связи:** +- `dashboardFieldsLinks` → DashboardFieldsLinks[] - Связи с полями +- `fieldProperty` → DashboardFieldsProperty[] - Свойства полей (через dashboard_fields) + +**Пример:** +```php +$dashboard = new Dashboard(); +$dashboard->name = 'Дашборд директора'; +$dashboard->group_id = 1; // Директора +$dashboard->save(); +``` + +#### 2. **DashboardSales** +`erp24/records/DashboardSales.php` + +Данные продаж для дашбордов (агрегированные метрики). + +**Таблица:** `dashboard_sales` + +**Поля:** +- `date` (date) - Дата +- `store_id` (int) - ID магазина +- `field_name` (string, 25) - Название поля/метрики +- `field_id` (int) - ID поля +- `summ` (float) - Значение метрики +- `last_modified` (datetime) - Время последнего изменения + +**Уникальный ключ:** `[date, store_id, field_name]` + +**Дополнительные свойства:** +- `$fieldName` - Название поля (виртуальное) +- `$storeName` - Название магазина (виртуальное) +- `$fieldNameProperty` - Свойства поля (виртуальное) +- `$dashboardId` - ID дашборда (для фильтрации) + +**Связи:** +- `field` → DashboardFields - Поле дашборда +- `store` → CityStore - Магазин +- `fieldProperty` → DashboardFieldsProperty - Свойства поля + +**Примеры записей:** +```php +// Продажи магазина 5 за 15.01.2024 +[ + 'date' => '2024-01-15', + 'store_id' => 5, + 'field_name' => 'sales_summ', + 'field_id' => 1, + 'summ' => 850000, +] + +// Процент услуг в продажах +[ + 'date' => '2024-01-15', + 'store_id' => 5, + 'field_name' => 'services_percent', + 'field_id' => 3, + 'summ' => 12.5, +] + +// Конверсия трафика в чек +[ + 'date' => '2024-01-15', + 'store_id' => 5, + 'field_name' => 'conversion', + 'field_id' => 7, + 'summ' => 78, +] +``` + +#### 3. **DashboardFields** +`erp24/records/DashboardFields.php` + +Поля (метрики) дашбордов. + +**Таблица:** `dashboard_fields` + +**Поля:** +- `id` (int) - ID поля +- `name` (string) - Код поля (например, 'sales_summ', 'services_percent') +- `label` (string) - Отображаемое название +- `type` (string) - Тип данных (number, percent, currency) +- `active` (tinyint) - Активность поля +- `sort_order` (int) - Порядок сортировки + +**Примеры полей:** +- `sales_summ` - Сумма продаж (₽) +- `sales_plan` - План продаж (₽) +- `sales_percent` - Процент выполнения плана (%) +- `avg_check` - Средний чек (₽) +- `checks_counter` - Количество чеков +- `incoming_traffic` - Входящий трафик (человек) +- `conversion` - Конверсия (%) +- `services` - Продажи услуг (₽) +- `services_percent` - Процент услуг (%) +- `wrap` - Продажи упаковки (₽) +- `wrap_percent` - Процент упаковки (%) +- `potted` - Продажи горшечных (₽) +- `potted_percent` - Процент горшечных (%) +- `avg_position_check` - Средняя позиция в чеке + +#### 4. **DashboardFieldsLinks** +`erp24/records/DashboardFieldsLinks.php` + +Связь дашбордов с полями. + +**Таблица:** `dashboard_fields_links` + +**Поля:** +- `id` (int) - ID связи +- `dashboard_id` (int) - ID дашборда +- `field_id` (int) - ID поля +- `property_field_id` (int) - ID свойства отображения +- `sort_order` (int) - Порядок отображения + +**Связи:** +- `dashboard` → Dashboard +- `field` → DashboardFields +- `propertyField` → DashboardFieldsProperty + +**Пример:** +```php +// Добавить поле "Продажи" на дашборд директора +$link = new DashboardFieldsLinks(); +$link->dashboard_id = 1; +$link->field_id = 1; // sales_summ +$link->property_field_id = 5; // формат отображения +$link->sort_order = 1; +$link->save(); +``` + +#### 5. **DashboardFieldsProperty** +`erp24/records/DashboardFieldsProperty.php` + +Свойства отображения полей на дашборде. + +**Таблица:** `dashboard_fields_property` + +**Поля:** +- `id` (int) - ID свойства +- `name` (string) - Название свойства +- `display_type` (string) - Тип отображения (table, chart, card) +- `chart_type` (string) - Тип графика (line, bar, pie) +- `color_scheme` (json) - Цветовая схема +- `threshold_good` (float) - Порог "хорошо" (зеленый) +- `threshold_warning` (float) - Порог "предупреждение" (желтый) +- `threshold_bad` (float) - Порог "плохо" (красный) + +**Пример цветовых индикаторов:** +```json +{ + "good": "#28a745", // Зеленый: >= 100% плана + "warning": "#ffc107", // Желтый: 90-99% плана + "bad": "#dc3545" // Красный: < 90% плана +} +``` + +#### 6. **DashboardSearch** +`erp24/records/DashboardSearch.php` + +Search-модель для фильтрации дашбордов. + +#### 7. **DashboardListSearch** +`erp24/records/DashboardListSearch.php` + +Search-модель для списков дашбордов. + +#### 8. **DashboardSalesSearch** +`erp24/records/DashboardSalesSearch.php` + +Search-модель для данных продаж. + +**Функционал:** +- Фильтрация по дате +- Фильтрация по магазину +- Фильтрация по полю/метрике +- Сортировка +- Пагинация + +#### 9. **DashboardFieldsPropertySearch** +`erp24/records/DashboardFieldsPropertySearch.php` + +Search-модель для свойств полей. + +--- + +## Бизнес-логика + +### Типы метрик + +#### 1. Продажи +- **sales_summ** - Общая сумма продаж +- **sales_plan** - План продаж +- **sales_percent** - Процент выполнения плана + +#### 2. Средние показатели +- **avg_check** - Средний чек +- **avg_position_check** - Среднее количество позиций в чеке + +#### 3. Трафик и конверсия +- **incoming_traffic** - Входящий трафик (посетители) +- **checks_counter** - Количество чеков +- **conversion** - Конверсия (чеки / трафик * 100%) + +#### 4. Структура продаж +- **services** / **services_percent** - Услуги +- **wrap** / **wrap_percent** - Упаковка +- **potted** / **potted_percent** - Горшечные растения + +### Процесс сбора данных + +```mermaid +sequenceDiagram + participant Cron as Cron Job + participant DS as DashboardService + participant Sales as Sales + participant Traffic as StoreVisitors + participant DB as DashboardSales + + Cron->>DS: setData(dateFrom, dateTo) + + Note over DS: 1. Получение активных полей + + DS->>Sales: Получить продажи за период + Sales-->>DS: Данные продаж по магазинам + + DS->>Sales: Получить продажи по классам товаров + Sales-->>DS: Услуги, упаковка, горшечные + + DS->>Traffic: Получить входящий трафик + Traffic-->>DS: Количество посетителей + + Note over DS: 2. Расчет метрик + + loop Для каждого магазина и даты + DS->>DS: Рассчитать процент услуг + DS->>DS: Рассчитать конверсию + DS->>DS: Рассчитать средний чек + end + + Note over DS: 3. Сохранение данных + + loop Для каждой метрики + DS->>DB: INSERT/UPDATE DashboardSales + end + + DB-->>DS: Данные сохранены + DS-->>Cron: Обработка завершена +``` + +### Формулы расчета метрик + +#### Процент выполнения плана +``` +percent = (факт / план) * 100 +``` + +#### Конверсия +``` +conversion = (количество_чеков / входящий_трафик) * 100 +``` + +#### Процент категории в продажах +``` +category_percent = (продажи_категории / общие_продажи) * 100 +``` + +#### Средний чек +``` +avg_check = общая_сумма_продаж / количество_чеков +``` + +#### Среднее количество позиций в чеке +``` +avg_position_check = общее_количество_позиций / количество_чеков +``` + +### Цветовые индикаторы + +**Процент выполнения плана:** +- 🟢 Зеленый: >= 100% +- 🟡 Желтый: 90-99% +- 🔴 Красный: < 90% + +**Конверсия:** +- 🟢 Зеленый: >= 80% +- 🟡 Желтый: 60-79% +- 🔴 Красный: < 60% + +**Процент услуг/упаковки/горшечных:** +- 🟢 Зеленый: >= целевого значения +- 🟡 Желтый: 80-99% от целевого +- 🔴 Красный: < 80% от целевого + +--- + +## API и интеграции + +### Внутренние зависимости + +Модуль получает данные из: +- **Sales** - данные о продажах +- **Timetable** - данные о сменах (для расчета производительности) +- **WriteOffs** - данные о списаниях +- **StoreVisitors** - данные о трафике (счетчики посетителей) +- **CityStore** - данные о магазинах + +### Используется модулями + +Данные дашборда используются: +- **Payroll** - для расчета премий +- **Bonus** - для расчета бонусов +- **Rating** - для рейтингов магазинов + +--- + +## Маршруты (Routes) + +``` +# Основные дашборды +/dashboard/index - Главная страница +/dashboard/sales - Дашборд продаж +/dashboard/sales-detail - Детальная информация по продажам + +# Коммерческие дашборды +/dashboard/commercial - Коммерческий дашборд +/dashboard/commercial-detail-info - Детальная информация +/dashboard/commercial-sales-info - Информация о продажах +/dashboard/commercial-multiple - Множественный дашборд +/dashboard/commercial-form-submission - Отправка формы +/dashboard/commercial-up-vote - Положительная оценка +/dashboard/commercial-down-vote - Отрицательная оценка + +# Специальные +/dashboard/marketplace-sales-report - Отчет маркетплейсов +/dashboard/validate - Валидация данных +/dashboard/check-mismatch - Проверка несоответствий + +# Управление +/dashboard-sales/* - Управление данными продаж +/dashboard-list/* - Управление списками дашбордов +/dashboard-chart/* - Управление графиками +/dashboard-fields-property/* - Управление свойствами полей +``` + +--- + +## Примеры использования + +### Пример 1: Получение данных дашборда за вчера + +```php +DashboardService::setData( + date('Y-m-d 00:00:00', strtotime('-1 day')), + date('Y-m-d 23:59:59', strtotime('-1 day')) +); +``` + +### Пример 2: Получение данных за последние 7 дней + +```php +DashboardService::setData(null, null, 7); +``` + +### Пример 3: Получение продаж по магазинам с планом + +```php +$service = new DashboardService(); + +$sales = [5 => 850000, 7 => 920000]; +$plan = [5 => 800000, 7 => 1000000]; +$stores = [5 => 'Ванеева', 7 => 'Горького']; + +$result = $service->getSalesSumWithCityStoreId($sales, $plan, $stores); + +foreach ($result['sales'] as $row) { + echo "{$row['store']}: "; + echo "{$row['summ']} ₽ / {$row['plan']} ₽ "; + echo "({$row['percent']}%)\n"; +} +// Ванеева: 850000 ₽ / 800000 ₽ (106%) +// Горького: 920000 ₽ / 1000000 ₽ (92%) +``` + +### Пример 4: Получение данных дашборда за дату + +```php +$date = '2024-01-15'; +$storeId = 5; + +$data = DashboardSales::find() + ->where(['date' => $date, 'store_id' => $storeId]) + ->with('field', 'store') + ->all(); + +foreach ($data as $row) { + echo "{$row->field->label}: {$row->summ}\n"; +} +// Сумма продаж: 850000 +// Процент услуг: 12.5 +// Конверсия: 78 +// Средний чек: 2150 +``` + +### Пример 5: Создание нового дашборда + +```php +// 1. Создать дашборд +$dashboard = new Dashboard(); +$dashboard->name = 'Дашборд администратора'; +$dashboard->group_id = 50; // Администраторы +$dashboard->save(); + +// 2. Добавить поля +$fields = [ + 1 => 5, // sales_summ с property 5 + 3 => 6, // services_percent с property 6 + 7 => 7, // conversion с property 7 +]; + +$order = 1; +foreach ($fields as $fieldId => $propertyId) { + $link = new DashboardFieldsLinks(); + $link->dashboard_id = $dashboard->id; + $link->field_id = $fieldId; + $link->property_field_id = $propertyId; + $link->sort_order = $order++; + $link->save(); +} +``` + +### Пример 6: Расчет трафика + +```php +$service = new DashboardService(); + +$visitors = [ + ['date' => '2024-01-15', 'counter' => 50, 'store_id' => 5], + ['date' => '2024-01-15', 'counter' => 30, 'store_id' => 5], + ['date' => '2024-01-16', 'counter' => 45, 'store_id' => 5], +]; + +$traffic = $service->getStoreTraffic($visitors); + +foreach ($traffic as $date => $stores) { + foreach ($stores as $storeId => $count) { + echo "Дата {$date}, Магазин {$storeId}: {$count} посетителей\n"; + } +} +// Дата 2024-01-15, Магазин 5: 80 посетителей +// Дата 2024-01-16, Магазин 5: 45 посетителей +``` + +--- + +## База данных + +### Основные таблицы + +1. **dashboard** - Конфигурация дашбордов +2. **dashboard_sales** - Данные метрик (агрегированные) +3. **dashboard_fields** - Поля/метрики дашбордов +4. **dashboard_fields_links** - Связь дашбордов с полями +5. **dashboard_fields_property** - Свойства отображения полей + +### ER-диаграмма + +```mermaid +erDiagram + dashboard ||--o{ dashboard_fields_links : "dashboard_id" + dashboard_fields ||--o{ dashboard_fields_links : "field_id" + dashboard_fields_property ||--o{ dashboard_fields_links : "property_field_id" + + dashboard_sales }o--|| dashboard_fields : "field_id" + dashboard_sales }o--|| city_store : "store_id" + + dashboard { + int id PK + string name + int group_id FK + } + + dashboard_sales { + date date PK + int store_id PK, FK + string field_name PK + int field_id FK + float summ + datetime last_modified + } + + dashboard_fields { + int id PK + string name + string label + string type + tinyint active + int sort_order + } + + dashboard_fields_links { + int id PK + int dashboard_id FK + int field_id FK + int property_field_id FK + int sort_order + } + + dashboard_fields_property { + int id PK + string name + string display_type + string chart_type + json color_scheme + float threshold_good + float threshold_warning + float threshold_bad + } +``` + +--- + +## Автоматизация + +### Рекомендуемое расписание Cron + +```bash +# Сбор данных за вчера (каждый день в 02:00) +0 2 * * * /usr/bin/php /path/to/yii dashboard/collect-data --date=yesterday + +# Сбор данных за последние 7 дней (каждый день в 03:00) +0 3 * * * /usr/bin/php /path/to/yii dashboard/collect-data --days=7 + +# Сбор данных за сегодня (каждый час) +0 * * * * /usr/bin/php /path/to/yii dashboard/collect-data --date=today +``` + +--- + +## Метрики и аналитика + +### Ключевые метрики + +1. **Продажи и план** - Общая сумма и процент выполнения +2. **Трафик и конверсия** - Посетители и эффективность +3. **Средний чек** - Показатель качества продаж +4. **Структура продаж** - Доли категорий товаров +5. **Производительность магазинов** - Сравнение по KPI + +--- + +## Вопросы для уточнения + +1. ❓ Какие именно метрики рассчитывает `return_sale_products_class()`? +2. ❓ Откуда берутся данные о входящем трафике (StoreVisitors)? +3. ❓ Как настраиваются пороговые значения для цветовых индикаторов? +4. ❓ Есть ли возможность создавать кастомные метрики? +5. ❓ Как работает система голосования (up-vote/down-vote)? +6. ❓ Что такое "avg_position_check" и как он рассчитывается? + +--- + +## Связанные модули + +- [Payroll (Расчет заработной платы)](../payroll/README.md) - Использует данные для расчета премий +- [Bonus (Бонусная система)](../bonus/README.md) - Использует данные для расчета бонусов +- [Rating (Рейтинговая система)](../rating/README.md) - Использует данные для рейтингов +- [Timetable (Расписание)](../timetable/README.md) - Данные о сменах для производительности +- [Write-offs (Списания)](../write-offs/README.md) - Данные о списаниях + +--- + +*Документация создана автоматически. Последнее обновление: 2025-11-17* + +**Примечание:** Модуль Dashboard является центральным для аналитики и отчетности, агрегируя данные из множества других модулей системы. diff --git a/erp24/docs/modules/grade/README.md b/erp24/docs/modules/grade/README.md new file mode 100644 index 00000000..9c1a0b1d --- /dev/null +++ b/erp24/docs/modules/grade/README.md @@ -0,0 +1,57 @@ +# Модуль Grade (Грейды и должности) + +## 📋 Описание + +**Grade** - модуль управления системой грейдов (уровней) сотрудников и должностей. Определяет иерархию позиций, уровни оплаты и требования к квалификации. + +### Основные возможности + +- 📊 Система грейдов (уровней сотрудников) +- 💼 Управление должностями +- 💰 Связь с окладами и ставками +- 📈 Карьерное развитие +- 🎯 Требования к квалификации + +## 🏗️ Архитектура + +**Контроллеры:** 2 +- `GradeController` - управление грейдами +- `EmployeePositionController` - управление должностями + +**Модели (4):** +- `Grade` - грейды/уровни +- `EmployeePosition` - должности +- `GradePosition` - связь грейдов и должностей +- `GradeRequirements` - требования к грейдам + +## 💼 Основные сущности + +**Грейды:** +- Junior (Стажер) +- Middle (Флорист) +- Senior (Старший флорист) +- Lead (Администратор) +- Expert (Супервайзер) + +**Должности:** +- Флорист +- Администратор магазина +- Кустовой директор +- Менеджер отдела +- И другие + +**Связь с зарплатой:** +- Каждый грейд имеет базовую ставку +- Должность определяет коэффициент +- Итоговый оклад = ставка × коэффициент + +## 🔗 Связи с модулями + +- **Payroll** - расчет зарплаты на основе грейда +- **Bonus** - бонусы зависят от уровня +- **Rating** - требования к рейтингу для повышения +- **HR** - карьерное развитие + +--- + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/modules/kik-feedback/README.md b/erp24/docs/modules/kik-feedback/README.md new file mode 100644 index 00000000..0c991123 --- /dev/null +++ b/erp24/docs/modules/kik-feedback/README.md @@ -0,0 +1,987 @@ +# Модуль KIK Feedback (Обратная связь от контроля качества) + +## 📋 Описание + +**KIK Feedback** - модуль для управления обратной связью от клиентов и контроля качества обслуживания. Система позволяет регистрировать обращения клиентов, классифицировать их по категориям, назначать ответственных, отслеживать статусы выполнения и принимать управленческие решения. Поддерживает Kanban-workflow с 7 статусами и детальную аналитику времени обработки. + +### Основные возможности + +- 📝 Регистрация обращений клиентов (жалобы, благодарности, предложения) +- 🏷️ Классификация по категориям (отрицательные/нейтральные/положительные) +- 📊 Kanban-доска с 7 статусами +- 👤 Назначение ответственных из отдела КиК +- ⏱️ Отслеживание времени в каждом статусе +- 💬 Комментарии и вложения +- 🔗 Интеграция с 1С (чеки, магазины, клиенты) +- 📞 Связь с AmoCRM (номера заказов) +- ⚖️ Решения руководства и вердикты +- 🗑️ Soft delete с причиной удаления +- 📈 Аналитика и фильтрация + +## 🏗️ Архитектура модуля + +```mermaid +graph TB + subgraph "Controller" + KFC[KikFeedbackController
16 actions] + end + + subgraph "Main Actions" + IA[IndexAction
Kanban список] + VA[ViewAction
Просмотр обращения] + UA[UpdateAction
Создание/редактирование] + DA[DeletedAction
Удаленные] + end + + subgraph "Dictionary Actions" + SA[SourceAction] + CA[CategoryAction] + SCA[SubcategoryAction] + VDA[VerdictAction] + end + + subgraph "Models/Records" + KFR[KikFeedbackRequest
Обращения] + KFC_M[KikFeedbackCategory
Категории] + KFSC[KikFeedbackSubcategory
Подкатегории] + KFS[KikFeedbackSource
Источники] + KFV[KikFeedbackVerdict
Вердикты] + end + + subgraph "External" + Files[Files
Вложения] + Comments[Comments
Комментарии] + Sales[Sales 1C
Чеки] + Stores[Products1c
Магазины] + end + + KFC --> IA + KFC --> VA + KFC --> UA + KFC --> DA + KFC --> SA + KFC --> CA + KFC --> SCA + KFC --> VDA + + IA --> KFR + VA --> KFR + UA --> KFR + + KFR --> KFC_M + KFR --> KFSC + KFR --> KFS + KFR --> KFV + KFR --> Files + KFR --> Comments + KFR --> Sales + KFR --> Stores + + style KFR fill:#fff4e1 + style KFC_M fill:#fff4e1 +``` + +## 🎮 Контроллер + +### KikFeedbackController + +**Файл:** `erp24/controllers/KikFeedbackController.php` + +**Действия (16):** + +**Основные:** +- `index` - Kanban-доска с обращениями +- `view` - Просмотр деталей обращения +- `update` - Создание/редактирование обращения +- `deleted` - Список удаленных обращений +- `delete` - Удаление обращения (прямой action) +- `restore` - Восстановление удаленного обращения + +**Справочники (CRUD):** +- `source` / `source-update` / `source-delete` - Источники +- `category` / `category-update` / `category-delete` - Категории +- `subcategory` / `subcategory-update` / `subcategory-delete` - Подкатегории +- `verdict` / `verdict-update` / `verdict-delete` - Вердикты + +**Прямые действия:** + +**actionDelete()** - Удаление обращения: +```php +public function actionDelete($id) { + $model = DynamicModel::validateData(['comment' => null], [ + ['comment', 'required'] // Причина удаления обязательна! + ]); + + $request = KikFeedbackRequest::findOne($id); + + if (Yii::$app->request->isPost && $model->load(...) && $model->validate()) { + $request->status = KikFeedbackRequest::STATUS_DELETED; // 7 + $request->delete_reason = $model->comment; + $request->save(); + return $this->redirect('index'); + } + + return $this->render('delete', compact('request', 'model')); +} +``` + +**actionRestore()** - Восстановление: +```php +public function actionRestore($id) { + $request = KikFeedbackRequest::findOne($id); + $request->status = KikFeedbackRequest::STATUS_RETURNED_IN_WORK; // 5 + $request->delete_reason = null; + $request->save(); + return $this->redirect(['view', 'id' => $id]); +} +``` + +## 📊 Модели / Records + +### 1. KikFeedbackRequest (Главная модель) + +**Файл:** `erp24/records/KikFeedbackRequest.php` +**Таблица:** `kik_feedback_request` + +**Основные поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID обращения | +| `number` | int | Номер обращения (уникальный) | +| `name` | string(255) | ФИО клиента | +| `phone` | string(40) | Телефон | +| `source` | int | ID источника (FK) | +| `check_id` | string(40) | GUID чека из 1С | +| `order_id` | string(40) | Номер заказа в AmoCRM | +| `store_id` | string(36) | GUID магазина из 1С | +| `client_info` | string(1000) | Информация от клиента | +| `description` | text | Подробное описание | +| `category` | int | ID категории (FK) | +| `subcategory` | int | ID подкатегории (FK) | +| `status` | int | Текущий статус (-1...7) | +| `responsible` | int | ID ответственного (FK Admin) | +| `verdict_id` | int | ID вердикта (FK) | +| `verdict_description` | string(1000) | Описание решения | +| `management_decision` | string(1000) | Решение руководства | +| `delete_reason` | text | Причина удаления | + +**Временные метки:** + +| Поле | Описание | +|------|----------| +| `created_at` | Дата создания | +| `status_changed_at` | Последнее изменение статуса | +| `in_work_started_at` | Начало работы (status=2) | +| `complete_at` | Время завершения (status=6) | +| `closed_at` | Время закрытия | + +**Метрики времени:** + +| Поле | Описание | +|------|----------| +| `status_1_duration` | Секунд в статусе "Новые" | +| `status_2_duration` | Секунд в статусе "В работе" | +| `status_3_duration` | Секунд в статусе "Ожидают" | +| `status_4_duration` | Секунд в статусе "Нужно решение руководства" | +| `status_5_duration` | Секунд в статусе "Возвращены в работу" | +| `status_6_duration` | Секунд в статусе "Выполнено" | +| `status_7_duration` | Секунд в статусе "Удалено" | + +#### Статусы + +```php +const STATUS_DRAFT = -1; // Черновик +const STATUS_NEW = 1; // Новые +const STATUS_IN_WORK = 2; // В работе +const STATUS_WAITING = 3; // Ожидают +const STATUS_MANAGEMENT_DECISION_WANTED = 4; // Нужно решение руководства +const STATUS_RETURNED_IN_WORK = 5; // Возвращены в работу +const STATUS_COMPLETE = 6; // Выполнено +const STATUS_DELETED = 7; // Удалено +``` + +**Статусы для Kanban:** +```php +public static function columnStatuses() { + return [ + 1 => 'Новые', + 2 => 'В работе', + 3 => 'Ожидают', + 4 => 'Нужно решение руководства', + 5 => 'Возвращены в работу', + 6 => 'Выполнено', + ]; +} +``` + +**Все статусы:** +```php +public static function allStatuses() { + return ArrayHelper::merge(self::columnStatuses(), [ + -1 => 'Черновик', + 7 => 'Удалено', + ]); +} +``` + +#### Специальные группы + +```php +const GROUP_MANAGER_KIK = 82; // Менеджер отдела контроля качества +const GROUP_HEAD_MANAGER_KIK = 83; // Руководитель отдела КиК +``` + +#### Связи + +```php +public function getSourceEntity() { + return $this->hasOne(KikFeedbackSource::class, ['id' => 'source']); +} + +public function getStore() { + return $this->hasOne(Products1c::class, ['id' => 'store_id']); +} + +public function getCategoryEntity() { + return $this->hasOne(KikFeedbackCategory::class, ['id' => 'category']); +} + +public function getSubcategoryEntity() { + return $this->hasOne(KikFeedbackSubcategory::class, ['id' => 'subcategory']); +} + +public function getResponsibleEntity() { + return $this->hasOne(Admin::class, ['id' => 'responsible']); +} + +public function getVerdictEntity() { + return $this->hasOne(KikFeedbackVerdict::class, ['id' => 'verdict_id']); +} + +public function getSale() { + return $this->hasOne(Sales::class, ['id' => 'check_id']); +} + +public function getFiles() { + return $this->hasMany(Files::class, ['entity_id' => 'id']) + ->andWhere(['like', 'entity', 'kikfeedbackrequest_file']); +} + +public function getComments() { + return $this->hasMany(Comment::class, ['entity_id' => 'id']) + ->andWhere(['like', 'entity', 'kikfeedbackrequest']); +} +``` + +### 2. KikFeedbackCategory + +**Файл:** `erp24/records/KikFeedbackCategory.php` +**Таблица:** `kik_feedback_category` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID категории | +| `name` | string(255) | Название категории | +| `type` | int | Тип (0/1/2) | +| `active` | int | 0=удалено, 1=активна | + +**Типы категорий:** +```php +const TYPE_NEGATIVE = 0; // Отрицательный (жалобы) +const TYPE_NEUTRAL = 1; // Нейтральный (вопросы, предложения) +const TYPE_POSITIVE = 2; // Положительный (благодарности) + +public static function typeLabels() { + return [ + self::TYPE_NEGATIVE => 'Отрицательный', + self::TYPE_NEUTRAL => 'Нейтральный', + self::TYPE_POSITIVE => 'Положительный', + ]; +} +``` + +**Примеры категорий:** +- Отрицательные: "Качество товара", "Обслуживание", "Доставка" +- Нейтральные: "Вопрос", "Предложение" +- Положительные: "Благодарность флористу", "Благодарность курьеру" + +### 3. KikFeedbackSubcategory + +**Файл:** `erp24/records/KikFeedbackSubcategory.php` +**Таблица:** `kik_feedback_subcategory` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID подкатегории | +| `name` | string(255) | Название подкатегории | +| `category_id` | int | ID родительской категории (FK) | +| `active` | int | 0=удалено, 1=активна | + +**Пример иерархии:** +``` +Категория: "Качество товара" (отрицательная) +├─ Подкатегория: "Несвежие цветы" +├─ Подкатегория: "Неправильная сборка" +└─ Подкатегория: "Не соответствует фото" + +Категория: "Обслуживание" (отрицательная) +├─ Подкатегория: "Грубость флориста" +├─ Подкатегория: "Долгое ожидание" +└─ Подкатегория: "Ошибка в заказе" +``` + +### 4. KikFeedbackSource + +**Файл:** `erp24/records/KikFeedbackSource.php` +**Таблица:** `kik_feedback_source` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID источника | +| `name` | string(255) | Название источника | +| `active` | int | 0=удалено, 1=активно | + +**Примеры источников:** +- "Звонок клиента" +- "Email" +- "WhatsApp" +- "Telegram" +- "Форма обратной связи на сайте" +- "Социальные сети" +- "AmoCRM" + +### 5. KikFeedbackVerdict + +**Файл:** `erp24/records/KikFeedbackVerdict.php` +**Таблица:** `kik_feedback_verdict` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID вердикта | +| `name` | string(255) | Суть решения | +| `active` | int | 0=удалено, 1=активно | + +**Примеры вердиктов:** +- "Возврат средств" +- "Замена товара" +- "Скидка на следующий заказ" +- "Извинения принесены" +- "Проведена беседа с сотрудником" +- "Обоснованная жалоба отклонена" +- "Необоснованная жалоба" + +## 🎯 Actions (Действия) + +### 1. IndexAction + +**Файл:** `erp24/actions/kikFeedback/IndexAction.php` +**URL:** `/kik-feedback/index` +**Назначение:** Kanban-доска с обращениями и фильтрами + +#### Алгоритм работы + +```mermaid +sequenceDiagram + participant U as User + participant IA as IndexAction + participant DB as Database + + U->>IA: GET /kik-feedback/index + IA->>IA: Загрузить фильтры (дата, ответственный, категория...) + + alt AJAX: updateRequestStatus + U->>IA: POST action=updateRequestStatus + IA->>DB: Обновить статус обращения + IA->>DB: Обновить status_changed_at + IA->>DB: Пересчитать duration для предыдущего статуса + IA-->>U: "ok" + else GET: Отобразить Kanban + IA->>DB: SELECT с фильтрами + IA->>IA: Группировка по статусам + IA->>IA: Подсчет countByStatus + IA-->>U: Render Kanban view + end +``` + +#### Фильтры + +```php +$filterModel = DynamicModel::validateData([ + 'which' => 0, // 0=все, 1=мои + 'created_at_from' => date("d.m.Y", strtotime("-14 day")), + 'created_at_to' => date("d.m.Y"), + 'request_number' => null, // Номер обращения + 'responsible' => 0, // Ответственный + 'source' => null, // Источник + 'category' => null, // Категория + 'subcategory' => null, // Подкатегория + 'phone' => null, // Телефон клиента + 'store_id' => null, // Магазин +], [...]); +``` + +#### Построение запроса + +```php +$requestsQuery = KikFeedbackRequest::find() + ->with(['sourceEntity']) + ->where(['and', + ['>=', 'created_at', $created_at_from], + ['<=', 'created_at', $created_at_to] + ]); + +// Фильтр "Мои обращения" +if ($filterModel->which == 1) { + $requestsQuery->andWhere(['responsible' => Yii::$app->user->id]); +} + +// Поиск по номеру +if (!empty($filterModel->request_number)) { + $requestsQuery->andWhere(['number' => $filterModel->request_number]); +} + +// Поиск по телефону (частичное совпадение) +if (!empty($filterModel->phone)) { + $requestsQuery->andWhere(['like', 'phone', '%' . $filterModel->phone . '%', false]); +} + +// Множественный выбор источников +if (!empty($filterModel->source)) { + $requestsQuery->andWhere(['in', 'source', $filterModel->source]); +} + +// ... другие фильтры +``` + +#### Статистика + +```php +$countByStatus = []; +$managementDecisionExistsRequestId = []; +$verdictExistsRequestId = []; + +foreach ($requests as $r) { + // Подсчет по статусам + $countByStatus[$r->status] = ($countByStatus[$r->status] ?? 0) + 1; + + // Отметить обращения с решением руководства + if (!empty($r->management_decision)) { + $managementDecisionExistsRequestId[] = $r->id; + } + + // Отметить обращения с вердиктом + if (!empty($r->verdict_id)) { + $verdictExistsRequestId[] = $r->id; + } +} +``` + +**Результат:** +```php +$countByStatus = [ + 1 => 5, // 5 новых + 2 => 12, // 12 в работе + 3 => 3, // 3 ожидают + 4 => 2, // 2 нужно решение руководства + 5 => 1, // 1 возвращено в работу + 6 => 45, // 45 выполнено +] +``` + +#### AJAX API: Изменение статуса + +```php +$action = Yii::$app->request->post('action'); + +if ($action == 'updateRequestStatus') { + $request_id = Yii::$app->request->post('request_id'); + $status = Yii::$app->request->post('status'); + + $request = KikFeedbackRequest::findOne($request_id); + ViewAction::changeStatus($request, (int)$status); + $request->save(); + + return 'ok'; +} +``` + +**JavaScript (Drag & Drop на Kanban):** +```javascript +$('.request-card').on('drop', function(e) { + var request_id = e.dataTransfer.getData('request_id'); + var new_status = $(this).data('status'); + + $.post('/kik-feedback/index', { + action: 'updateRequestStatus', + request_id: request_id, + status: new_status + }); +}); +``` + +### 2. ViewAction + +**Файл:** `erp24/actions/kikFeedback/ViewAction.php` +**URL:** `/kik-feedback/view?id=123` +**Назначение:** Просмотр деталей обращения + +**Метод changeStatus():** +```php +public static function changeStatus($request, int $newStatus) { + $oldStatus = $request->status; + $now = date('Y-m-d H:i:s'); + + // Рассчитать время в предыдущем статусе + if ($oldStatus > 0) { + $statusChangedAt = strtotime($request->status_changed_at); + $duration = time() - $statusChangedAt; + $durationField = 'status_' . $oldStatus . '_duration'; + $request->$durationField += $duration; + } + + // Установить новый статус + $request->status = $newStatus; + $request->status_changed_at = $now; + + // Специальные действия при переходах + if ($newStatus == KikFeedbackRequest::STATUS_IN_WORK && $oldStatus == KikFeedbackRequest::STATUS_NEW) { + $request->in_work_started_at = $now; + } + + if ($newStatus == KikFeedbackRequest::STATUS_COMPLETE) { + $request->complete_at = $now; + $request->closed_at = $now; + } +} +``` + +### 3. UpdateAction + +**Файл:** `erp24/actions/kikFeedback/UpdateAction.php` +**URL:** `/kik-feedback/update` (создание) или `/kik-feedback/update?id=123` (редактирование) +**Назначение:** Создание и редактирование обращений + +### 4. DeletedAction + +**Файл:** `erp24/actions/kikFeedback/DeletedAction.php` +**URL:** `/kik-feedback/deleted` +**Назначение:** Список удаленных обращений (status=7) + +### 5-16. CRUD для справочников + +**SourceAction, CategoryAction, SubcategoryAction, VerdictAction** - управление справочниками. + +## 💼 Бизнес-логика + +### Жизненный цикл обращения + +```mermaid +stateDiagram-v2 + [*] --> Draft: Создание + Draft --> New: Сохранить + + New --> InWork: Взять в работу + New --> Deleted: Удалить + + InWork --> Waiting: Ожидание клиента/данных + InWork --> ManagementDecision: Нужно решение руководства + InWork --> Complete: Выполнить + + Waiting --> InWork: Продолжить работу + Waiting --> Deleted: Удалить + + ManagementDecision --> ReturnedInWork: Решение получено + ReturnedInWork --> Complete: Выполнить + ReturnedInWork --> Deleted: Удалить + + Complete --> [*] + + Deleted --> ReturnedInWork: Восстановить + Deleted --> [*] +``` + +### Типы категорий и их обработка + +**Отрицательные (type=0):** +- Требуют обязательного вердикта +- Могут требовать решения руководства (status=4) +- Влияют на рейтинг качества +- Примеры: жалобы на качество, обслуживание + +**Нейтральные (type=1):** +- Информационные обращения +- Не влияют на рейтинг +- Примеры: вопросы, предложения + +**Положительные (type=2):** +- Благодарности +- Повышают рейтинг качества +- Могут использоваться для мотивации сотрудников + +### Метрики времени + +**Автоматический расчет:** +```php +// При переходе из статуса 2 → 6: +$duration = time() - strtotime($request->status_changed_at); +$request->status_2_duration += $duration; // Секунд в статусе "В работе" +``` + +**Использование:** +- Аналитика скорости обработки +- SLA контроль +- KPI сотрудников отдела КиК +- Выявление узких мест + +**Пример:** +``` +Обращение #1234: +- status_1_duration: 3600 секунд (1 час в "Новые") +- status_2_duration: 7200 секунд (2 часа в "В работе") +- status_3_duration: 86400 секунд (1 день в "Ожидают") +- status_5_duration: 1800 секунд (30 минут в "Возвращены в работу") +- status_6_duration: 0 секунд (только что завершено) + +Итого: ~1.5 дня от создания до завершения +``` + +### Решения руководства + +**Когда требуется:** +- Возврат средств >5000 руб +- Компенсации и скидки +- Конфликтные ситуации +- Жалобы на руководителей + +**Процесс:** +1. Менеджер КиК переводит в status=4 +2. Заполняет описание ситуации +3. Руководство принимает решение (`management_decision`) +4. Обращение переводится в status=5 +5. Менеджер выполняет решение + +## 📝 Примеры использования + +### Пример 1: Создание обращения о жалобе + +```php +use yii_app\records\KikFeedbackRequest; + +$request = new KikFeedbackRequest(); +$request->name = 'Иванова Мария Петровна'; +$request->phone = '+79161234567'; +$request->source = 1; // Звонок клиента +$request->check_id = 'a1b2c3d4-e5f6-...'; // GUID чека из 1С +$request->store_id = 'store-guid-...'; // GUID магазина +$request->description = 'Клиент получил букет с увядшими цветами'; +$request->category = 5; // "Качество товара" +$request->subcategory = 12; // "Несвежие цветы" +$request->status = KikFeedbackRequest::STATUS_NEW; +$request->created_at = date('Y-m-d H:i:s'); +$request->status_changed_at = date('Y-m-d H:i:s'); +$request->number = KikFeedbackRequest::find()->max('number') + 1; +$request->save(); +``` + +**Результат:** Обращение #1235 создано и появляется в колонке "Новые" на Kanban. + +### Пример 2: Взятие обращения в работу + +```php +$request = KikFeedbackRequest::findOne(1235); +$request->responsible = 42; // ID менеджера КиК +ViewAction::changeStatus($request, KikFeedbackRequest::STATUS_IN_WORK); +$request->save(); + +// Автоматически: +// - status_1_duration += время от created_at до сейчас +// - in_work_started_at = сейчас +// - status_changed_at = сейчас +``` + +### Пример 3: Требуется решение руководства + +```php +$request = KikFeedbackRequest::findOne(1235); +$request->description .= "\n\nКлиент требует возврат 15,000 руб за испорченный праздник."; + +ViewAction::changeStatus($request, KikFeedbackRequest::STATUS_MANAGEMENT_DECISION_WANTED); +$request->save(); +``` + +**На Kanban:** Обращение перемещается в колонку "Нужно решение руководства". + +**Руководитель принимает решение:** +```php +$request->management_decision = "Произвести возврат 15,000 руб. Провести беседу с флористом. Отправить клиенту извинительный букет на 3,000 руб."; +ViewAction::changeStatus($request, KikFeedbackRequest::STATUS_RETURNED_IN_WORK); +$request->save(); +``` + +### Пример 4: Завершение обращения с вердиктом + +```php +$request = KikFeedbackRequest::findOne(1235); +$request->verdict_id = 3; // "Возврат средств" +$request->verdict_description = "Возврат 15,000 руб произведен на карту клиента. Извинительный букет доставлен. Клиент удовлетворен."; + +ViewAction::changeStatus($request, KikFeedbackRequest::STATUS_COMPLETE); +$request->save(); + +// Автоматически: +// - complete_at = сейчас +// - closed_at = сейчас +// - status_5_duration += время от последней смены статуса +``` + +### Пример 5: Удаление спам-обращения + +```php +// GET /kik-feedback/delete?id=1236 +// Форма с полем "Причина удаления" + +// POST: +$request = KikFeedbackRequest::findOne(1236); +$request->status = KikFeedbackRequest::STATUS_DELETED; +$request->delete_reason = "Спам. Клиент звонил 10 раз с разными номерами по одному вопросу."; +$request->save(); +``` + +**Просмотр удаленных:** +``` +GET /kik-feedback/deleted +``` + +**Восстановление:** +```php +GET /kik-feedback/restore?id=1236 + +$request->status = KikFeedbackRequest::STATUS_RETURNED_IN_WORK; +$request->delete_reason = null; +``` + +### Пример 6: Статистика по времени обработки + +```sql +SELECT + AVG(status_1_duration) / 3600 as avg_hours_in_new, + AVG(status_2_duration) / 3600 as avg_hours_in_work, + AVG(TIMESTAMPDIFF(SECOND, created_at, complete_at)) / 3600 as avg_total_hours +FROM kik_feedback_request +WHERE status = 6 + AND created_at >= '2023-11-01' + AND created_at < '2023-12-01'; +``` + +**Результат:** +``` +avg_hours_in_new: 2.5 (в среднем 2.5 часа в статусе "Новые") +avg_hours_in_work: 6.3 (в среднем 6.3 часа в работе) +avg_total_hours: 15.2 (в среднем 15.2 часа от создания до завершения) +``` + +## 🗄️ База данных + +### ER-диаграмма + +```mermaid +erDiagram + kik_feedback_request ||--|| kik_feedback_source : "source" + kik_feedback_request ||--o| kik_feedback_category : "category" + kik_feedback_request ||--o| kik_feedback_subcategory : "subcategory" + kik_feedback_request ||--o| kik_feedback_verdict : "verdict_id" + kik_feedback_request ||--o| admin : "responsible" + kik_feedback_request ||--o| products_1c : "store_id" + kik_feedback_request ||--o| sales : "check_id" + kik_feedback_request ||--o{ files : "entity_id" + kik_feedback_request ||--o{ comments : "entity_id" + + kik_feedback_subcategory ||--|| kik_feedback_category : "category_id" + + kik_feedback_request { + int id PK + int number UK + string name + string phone + int source FK + string check_id FK + string order_id + string store_id FK + text description + int category FK + int subcategory FK + int status + int responsible FK + int verdict_id FK + text verdict_description + text management_decision + text delete_reason + datetime created_at + datetime closed_at + datetime in_work_started_at + datetime complete_at + datetime status_changed_at + int status_1_duration + int status_2_duration + int status_3_duration + int status_4_duration + int status_5_duration + int status_6_duration + int status_7_duration + } + + kik_feedback_category { + int id PK + string name + int type "0,1,2" + int active + } + + kik_feedback_subcategory { + int id PK + string name + int category_id FK + int active + } + + kik_feedback_source { + int id PK + string name + int active + } + + kik_feedback_verdict { + int id PK + string name + int active + } +``` + +### Индексы + +```sql +-- kik_feedback_request +CREATE INDEX idx_status ON kik_feedback_request(status); +CREATE INDEX idx_responsible ON kik_feedback_request(responsible); +CREATE INDEX idx_created_at ON kik_feedback_request(created_at); +CREATE INDEX idx_number ON kik_feedback_request(number); +CREATE INDEX idx_phone ON kik_feedback_request(phone); +CREATE INDEX idx_category ON kik_feedback_request(category); +CREATE INDEX idx_source ON kik_feedback_request(source); +CREATE INDEX idx_store_id ON kik_feedback_request(store_id); +``` + +## ❓ Часто задаваемые вопросы + +### 1. Чем отличается "Удалено" от физического удаления? + +**Soft delete (status=7):** +- Запись остается в БД +- Требуется причина удаления +- Можно восстановить +- Сохраняется история + +**Физическое удаление:** +- Не используется в этом модуле +- Данные теряются навсегда + +### 2. Зачем нужны метрики status_X_duration? + +- **SLA контроль** - проверка соблюдения сроков +- **Аналитика** - где тормозят процессы +- **KPI менеджеров** - эффективность обработки +- **Оптимизация** - выявление узких мест + +### 3. Кто может видеть обращения? + +- **Менеджеры КиК (group_id=82)** - свои и назначенные на них +- **Руководители КиК (group_id=83)** - все обращения +- **Фильтр "Мои"** - показывает где `responsible = текущий пользователь` + +### 4. Когда обращение считается завершенным? + +Когда `status = 6` (Complete). При этом автоматически заполняются: +- `complete_at` - время завершения +- `closed_at` - время закрытия + +### 5. Можно ли вернуть завершенное обращение в работу? + +Технически да, но не рекомендуется. Лучше создать новое обращение со ссылкой на предыдущее. + +### 6. Как работает иерархия категорий? + +``` +Category (parent) +└─ Subcategory (child) + +Пример: +"Качество товара" (category_id=5) +├─ "Несвежие цветы" (subcategory, category_id=5) +├─ "Неправильная сборка" (subcategory, category_id=5) +└─ "Не соответствует фото" (subcategory, category_id=5) +``` + +Подкатегория всегда привязана к категории через `category_id`. + +### 7. Что такое verdict_id и verdict_description? + +- **verdict_id** - тип решения из справочника (например, "Возврат средств") +- **verdict_description** - детальное описание того, что было сделано + +Вместе они дают полную картину решения проблемы. + +## 🔗 Связи с другими модулями + +```mermaid +graph LR + KIK[KIK Feedback] --> Notifications[Notifications Module] + KIK --> Rating[Rating Module] + KIK --> Sales[Sales 1C] + KIK --> Stores[Products1c Stores] + KIK --> AmoCRM[AmoCRM Integration] + KIK --> Files[Files System] + KIK --> Comments[Comments System] + + style KIK fill:#e1f5ff +``` + +**Интеграции:** +- **Notifications** - уведомления об новых обращениях +- **Rating** - влияние на QualityRating сотрудников +- **1C Sales** - связь с чеками и продажами +- **1C Stores** - информация о магазинах +- **AmoCRM** - номера заказов +- **Files** - вложение скриншотов, фото +- **Comments** - обсуждение обращений + +## 🎯 Метрики модуля + +| Метрика | Значение | +|---------|----------| +| **Контроллеры** | 1 | +| **Actions** | 16 | +| **Models/Records** | 5 | +| **Статусов** | 8 (-1 до 7) | +| **Типов категорий** | 3 (негатив/нейтрал/позитив) | +| **Метрик времени** | 7 (по статусам) | +| **Справочников** | 4 (source/category/subcategory/verdict) | + +## 📚 См. также + +- [Модуль Notifications](../notifications/README.md) - система уведомлений +- [Модуль Rating](../rating/README.md) - влияние на рейтинг качества +- [1C Integration](../../api/README.md) - связь с чеками и магазинами +- [Files System](../../helpers/README.md) - работа с файлами + +--- + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/modules/lesson/README.md b/erp24/docs/modules/lesson/README.md new file mode 100644 index 00000000..1d923acd --- /dev/null +++ b/erp24/docs/modules/lesson/README.md @@ -0,0 +1,76 @@ +# Модуль Lesson (Система обучения) + +## 📋 Описание + +**Lesson** - модуль системы обучения и развития сотрудников. Позволяет создавать обучающие курсы, уроки, тесты и отслеживать прогресс обучения персонала. + +### Основные возможности + +- 📚 Создание обучающих курсов и уроков +- 📝 Тесты и проверка знаний +- 👥 Назначение обучения сотрудникам +- ✅ Отслеживание прогресса +- 📊 Статистика прохождения +- 🎓 Сертификаты об окончании +- 🔔 Уведомления о новых уроках + +## 🏗️ Архитектура + +**Контроллеры:** 1 (LessonController) + +**Сервисы (2):** +- `LessonService` - бизнес-логика уроков +- `LessonProgressService` - отслеживание прогресса + +**Модели (5):** +- `Lesson` - уроки +- `LessonCourse` - курсы +- `LessonTest` - тесты +- `LessonProgress` - прогресс прохождения +- `LessonCertificate` - сертификаты + +## 💼 Основные сущности + +**Курс обучения:** +- Название и описание +- Последовательность уроков +- Обязательные/необязательные +- Срок прохождения + +**Урок:** +- Теоретический материал (видео, текст, презентации) +- Практические задания +- Тест для проверки +- Минимальный балл для прохождения + +**Прогресс:** +- Начато / В процессе / Завершено +- Процент прохождения +- Оценка за тесты +- Время на обучение + +## 📝 Процесс обучения + +```mermaid +stateDiagram-v2 + [*] --> Assigned: Назначен курс + Assigned --> InProgress: Начал обучение + InProgress --> Testing: Прошел уроки + Testing --> Failed: Тест не сдан + Testing --> Passed: Тест сдан + Failed --> InProgress: Повторное изучение + Passed --> Completed: Получен сертификат + Completed --> [*] +``` + +## 🔗 Связи с модулями + +- **Notifications** - уведомления о новых уроках +- **Regulations** - похожая система (регламенты vs уроки) +- **Rating** - влияние обучения на рейтинг +- **Bonus** - бонусы за прохождение обучения +- **HR** - обязательное обучение для новичков + +--- + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/modules/notifications/README.md b/erp24/docs/modules/notifications/README.md new file mode 100644 index 00000000..7a352b94 --- /dev/null +++ b/erp24/docs/modules/notifications/README.md @@ -0,0 +1,1011 @@ +# Модуль Notifications (Система уведомлений) + +## 📋 Описание + +**Notifications** - модуль внутренней системы уведомлений для сотрудников ERP24. Позволяет создавать, отправлять и отслеживать прочтение уведомлений с rich-текстом и изображениями. Поддерживает индивидуальную и групповую рассылку, отложенную отправку и отслеживание статуса прочтения каждым получателем. + +### Основные возможности + +- 📢 Создание уведомлений с форматированным текстом и изображениями +- 👥 Гибкая настройка получателей (индивидуально, по группам, всем) +- ⏰ Отложенная отправка (scheduling) +- 📊 Отслеживание статусов (создано → показано → прочитано) +- 🔔 AJAX API для реал-тайм уведомлений +- 🗑️ Автоматическая очистка старых уведомлений (>31 день) +- ✏️ Редактирование и удаление с контролем доступа +- 🔍 Фильтрация по типу, дате создания и отправки + +## 🏗️ Архитектура модуля + +```mermaid +graph TB + subgraph "Controller" + NC[NotificationController
3 actions + delete] + end + + subgraph "Service" + NS[NotificationService
Инициализация и очистка] + end + + subgraph "Actions" + IA[IndexAction
Список + создание] + VA[ViewAction
Просмотр] + PA[PendingAction
AJAX API] + end + + subgraph "Models/Records" + N[Notification
Уведомления] + NST[NotificationStatus
Статусы по получателям] + end + + subgraph "External" + F[Files
Изображения] + A[Admin
Пользователи] + AG[AdminGroup
Группы] + end + + NC --> IA + NC --> VA + NC --> PA + + IA --> N + IA --> NS + VA --> N + VA --> NST + PA --> NST + + N --> NS + NS --> NST + N --> F + N --> A + NST --> A + + style NS fill:#e1f5ff + style N fill:#fff4e1 + style NST fill:#fff4e1 +``` + +## 🎮 Контроллер + +### NotificationController + +**Файл:** `erp24/controllers/NotificationController.php` + +**Действия:** +- `index` - Список уведомлений + форма создания/редактирования +- `view` - Просмотр уведомления +- `pending` - AJAX API для получения непрочитанных уведомлений +- `delete` - Удаление уведомления (прямой action) + +**Особенности:** + +```php +public function beforeAction($action) +{ + // Отключение CSRF для AJAX API + if ($action->id == 'pending') { + $this->enableCsrfValidation = false; + } + return parent::beforeAction($action); +} +``` + +**Удаление уведомления:** +```php +public function actionDelete($id) { + $notification = Notification::findOne($id); + + // Проверка прав: автор или право на удаление всех + if ($notification && ( + Yii::$app->user->id == $notification->created_by || + Yii::$app->user->can('notification/deleteAll') + )) { + // Сначала удаляем все статусы + NotificationStatus::deleteAll(['notification_id' => $id]); + // Затем само уведомление + $notification->delete(); + } + + return $this->redirect(['/notification/index']); +} +``` + +## ⚙️ Сервис + +### NotificationService + +**Файл:** `erp24/services/NotificationService.php` +**Размер:** 50 строк кода + +Сервис для инициализации уведомлений и автоматической очистки. + +#### Основные методы + +##### initNotification() +```php +public static function initNotification(Notification $notification) +``` + +**Назначение:** Создание записей NotificationStatus для всех получателей. + +**Логика кодирования получателей:** +```php +$recipient = intval($recipient); + +if ($recipient == 1000000) { + $toAll = true; // Всем сотрудникам + break; +} + +if ($recipient > 1000000) { + $recipient -= 1000000; + $groupIds[] = $recipient; // Группа: 1000050 → group_id=50 +} else { + $adminIds[] = $recipient; // Индивидуальный: 42 → admin_id=42 +} +``` + +**Процесс:** +```php +// 1. Получение списка получателей +if ($toAll) { + $admins = Admin::find()->where(['>', 'id', '0'])->all(); +} else { + $admins = Admin::find() + ->where(['or', + ['in', 'group_id', $groupIds], + ['in', 'id', $adminIds] + ]) + ->all(); +} + +// 2. Создание статуса для каждого получателя +foreach ($admins as $admin) { + $notificationStatus = new NotificationStatus; + $notificationStatus->notification_id = $notification->id; + $notificationStatus->admin_id = $admin->id; + $notificationStatus->status = 0; // Создано + $notificationStatus->save(); +} + +// 3. Очистка старых уведомлений +self::clearOldNotifications(); +``` + +##### clearOldNotifications() +```php +public static function clearOldNotifications() +``` + +**Назначение:** Автоматическое удаление уведомлений старше 31 дня. + +```php +// Найти старые уведомления +$notifications = Notification::find() + ->where(['<', 'created_at', date('Y-m-d H:i:s', strtotime('-31 day', time()))]) + ->all(); + +$notificationIds = ArrayHelper::map($notifications, 'id', 'id'); + +// Удалить статусы +NotificationStatus::deleteAll(['in', 'notification_id', array_values($notificationIds)]); + +// Удалить уведомления +Notification::deleteAll(['in', 'id', array_values($notificationIds)]); +``` + +**Вызывается:** Автоматически при каждом создании нового уведомления. + +## 🎯 Actions (Действия) + +### 1. IndexAction + +**Файл:** `erp24/actions/notification/IndexAction.php` +**URL:** `/notification/index` +**Назначение:** Главная страница с списком уведомлений и формой создания/редактирования + +#### Режимы работы + +**Создание нового уведомления:** +``` +GET /notification/index +``` + +**Редактирование существующего:** +``` +GET /notification/index?edit_id=123 +``` + +#### Алгоритм работы + +```mermaid +sequenceDiagram + participant U as User + participant IA as IndexAction + participant N as Notification + participant NS as NotificationService + + U->>IA: GET /notification/index + IA->>IA: Загрузить форму создания + + alt POST (создание) + U->>IA: POST with form data + IA->>N: load() + validate() + N->>N: upload() + N->>N: save() + N->>NS: initNotification() + NS->>NS: Создать NotificationStatus для каждого получателя + NS->>NS: clearOldNotifications() + IA-->>U: Redirect to /notification + end + + IA->>IA: Загрузить список уведомлений с фильтрами + IA-->>U: Render index view +``` + +#### Формирование списка получателей + +```php +$recipients = ['Группы' => [1000000 => 'Все']]; + +// Добавление групп +foreach ($adminGroups as $adminGroup) { + $recipients['Группы'][$adminGroup->id + 1000000] = $adminGroup->name; +} + +// Добавление сотрудников по группам +foreach ($admins as $admin) { + if ($admin->group_id > 0) { + $key = isset($admin->adminGroup) ? $admin->adminGroup->name : 'Безгрупные'; + if (!isset($recipients[$key])) { + $recipients[$key] = []; + } + $recipients[$key][$admin->id] = $admin->name; + } +} +``` + +**Результат:** +```php +[ + 'Группы' => [ + 1000000 => 'Все', + 1000050 => 'Администраторы', // group_id=50 + 1000030 => 'Флористы', // group_id=30 + ], + 'Администраторы' => [ + 42 => 'Иванов Иван', + 43 => 'Петров Петр', + ], + 'Флористы' => [ + 100 => 'Сидорова Анна', + 101 => 'Кузнецова Мария', + ] +] +``` + +#### Фильтрация списка + +```php +$notificationQuery = Notification::find()->joinWith(['author', 'status']); + +// Ограничение доступа (если нет прав на просмотр всех) +if (!Yii::$app->user->can("notification/editAll") && + !Yii::$app->user->can("notification/deleteAll")) { + $notificationQuery->andWhere(['or', + ['admin_id' => Yii::$app->user->id], // Я получатель + ['created_by' => Yii::$app->user->id] // Я автор + ]); +} + +// Фильтры +if ($filterModel->type != 'not_chosen') { + $notificationQuery->andWhere(['type' => $filterModel->type]); +} + +$notificationQuery + ->andWhere(['>=', 'send_at', $filterModel->send_at_from]) + ->andWhere(['<=', 'send_at', $filterModel->send_at_to]) + ->andWhere(['>=', 'created_at', $filterModel->created_at_from]) + ->andWhere(['<=', 'created_at', $filterModel->created_at_to]) + ->orderBy(['created_at' => SORT_DESC]); +``` + +#### Массовая отметка "Прочитано" + +```php +$action = Yii::$app->request->post('action'); + +if ($action == 'markIsRead') { + foreach ($notifications as $notification) { + if ($notification->status) { + $notification->status->status = 2; // Прочитано + $notification->status->save(); + } + } +} +``` + +#### Контроль доступа при редактировании + +```php +if ($edit_id) { + $addNotificationModel = Notification::findOne($edit_id); + + if ($addNotificationModel->created_by != Yii::$app->user->id && + !Yii::$app->user->can("notification/editAll")) { + throw new UnauthorizedHttpException('Вы не можете редактировать чужое уведомление'); + } +} +``` + +### 2. ViewAction + +**Файл:** `erp24/actions/notification/ViewAction.php` +**URL:** `/notification/view?id=123` +**Назначение:** Просмотр конкретного уведомления + +```php +public function run($id) { + // Загрузить уведомление с файлами + $notification = Notification::find() + ->with(['files']) + ->where(['id' => $id]) + ->one(); + + if (!$notification) { + throw new NotFoundHttpException('Уведомление с данным id не найдено'); + } + + // Отметить как прочитанное + $notificationStatus = NotificationStatus::findOne([ + 'admin_id' => Yii::$app->user->id, + 'notification_id' => $id + ]); + + if ($notificationStatus) { + $notificationStatus->status = 2; // Прочитано + $notificationStatus->save(); + } + + // Загрузить автора + $author = Admin::findOne($notification->created_by); + + return $this->controller->render('view', compact('notification', 'author')); +} +``` + +**Эффект:** При открытии уведомления оно автоматически помечается как прочитанное (status=2). + +### 3. PendingAction + +**Файл:** `erp24/actions/notification/PendingAction.php` +**URL:** `/notification/pending` (AJAX) +**Назначение:** AJAX API для получения непрочитанных уведомлений + +#### API метод: pendingNotifications + +**Запрос:** +```javascript +POST /notification/pending +{ + action: 'pendingNotifications' +} +``` + +**Ответ:** +```json +[ + { + "id": 1, + "notification_id": 123, + "admin_id": 42, + "status": 0, + "notification": { + "id": 123, + "name": "Важное объявление", + "description": "Краткое описание", + "content": "

Полный текст...

", + "type": "important", + "send_at": "2023-11-17 10:00:00" + } + } +] +``` + +**Логика:** +```php +if ($action == 'pendingNotifications') { + $notificationStatuses = NotificationStatus::find() + ->joinWith(['notification']) + ->where(['admin_id' => Yii::$app->user->id]) + ->andWhere(['<', 'send_at', date('Y-m-d H:i:s')]) // Время отправки уже наступило + ->andWhere(['or', ['status' => 0], ['status' => 1]]) // Не прочитано + ->asArray() + ->all(); + + return $this->controller->asJson($notificationStatuses); +} +``` + +#### API метод: notificationIsShown + +**Запрос:** +```javascript +POST /notification/pending +{ + action: 'notificationIsShown', + notification_id: 123 +} +``` + +**Ответ:** +```json +["123 is shown"] +``` + +**Логика:** +```php +if ($action == 'notificationIsShown') { + $notification_id = Yii::$app->request->post('notification_id'); + + $notificationStatus = NotificationStatus::findOne([ + 'admin_id' => Yii::$app->user->id, + 'notification_id' => $notification_id + ]); + + if ($notificationStatus) { + $notificationStatus->status = 1; // Показано + $notificationStatus->save(); + } + + return $this->controller->asJson([$notification_id . ' is shown']); +} +``` + +**Использование:** Вызывается когда popup с уведомлением показан пользователю. + +## 📊 Модели / Records + +### 1. Notification + +**Файл:** `erp24/records/Notification.php` +**Таблица:** `notification` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID уведомления | +| `name` | string(256) | Название уведомления | +| `description` | string(256) | Краткое описание | +| `content` | text | Полное содержание (HTML) | +| `type` | string | Тип уведомления | +| `created_by` | int | ID автора | +| `created_at` | datetime | Время создания | +| `send_at` | datetime | Время отправки | + +**Виртуальные атрибуты:** +```php +public $recipients; // Массив ID получателей +public $isImmediate; // Немедленная отправка (boolean) +public $imageFiles; // Загруженные изображения +``` + +**Сценарии:** +```php +const SCENARIO_ADD = 'add'; + +public function scenarios() +{ + $scenarios = parent::scenarios(); + $scenarios[self::SCENARIO_ADD] = [ + 'name', 'description', 'content', 'type', + 'send_at', 'recipients', 'isImmediate' + ]; + return $scenarios; +} +``` + +**Валидация:** +```php +public function rules() +{ + return [ + [['name', 'description', 'content', 'type', 'created_by', + 'created_at', 'send_at', 'recipients'], 'required'], + [['content', 'type'], 'string'], + [['created_by'], 'integer'], + [['description', 'created_at', 'send_at', 'isImmediate'], 'safe'], + [['name', 'description'], 'string', 'max' => 256], + [['content'], 'string', 'min' => 14], + [['name', 'description'], 'string', 'min' => 3], + [['imageFiles'], 'file', 'skipOnEmpty' => true, + 'extensions' => 'png, jpg', 'maxFiles' => 4], + ]; +} +``` + +**Метод загрузки:** +```php +public function upload() +{ + if ($this->validate()) { + // Установка автора и времени создания + $this->created_by = Yii::$app->user->id; + $this->created_at = date('Y-m-d H:i:s'); + + // Немедленная отправка + if ($this->isImmediate == true) { + $this->send_at = $this->created_at; + } + + $this->save(); + + // Инициализация уведомления (создание статусов) + NotificationService::initNotification($this); + + return true; + } +} +``` + +**Связи:** +```php +public function getFiles() { + return $this->hasMany(Files::class, ['entity_id' => 'id']) + ->andWhere(['entity' => 'notification']); +} + +public function getAuthor() { + return $this->hasOne(Admin::class, ['id' => 'created_by']); +} + +public function getStatus() +{ + return $this + ->hasOne(NotificationStatus::class, ['notification_id' => 'id']) + ->onCondition(['admin_id' => Yii::$app->user->id]); +} +``` + +### 2. NotificationStatus + +**Файл:** `erp24/records/NotificationStatus.php` +**Таблица:** `notification_status` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID записи | +| `notification_id` | int | ID уведомления | +| `admin_id` | int | ID получателя | +| `status` | int | Статус уведомления | + +**Статусы:** +```php +const STATUS_CREATED = 0; // Уведомление создано +const STATUS_SHOWN = 1; // Уведомление показано (popup открыт) +const STATUS_READ = 2; // Уведомление прочитано (открыто в view) +``` + +**Связи:** +```php +public function getNotification() { + return $this->hasOne(Notification::class, ['id' => 'notification_id']); +} +``` + +**Использование:** Для каждого уведомления создается по одной записи на каждого получателя. + +## 💼 Бизнес-логика + +### Жизненный цикл уведомления + +```mermaid +stateDiagram-v2 + [*] --> Created: Автор создает уведомление + Created --> Scheduled: send_at в будущем + Created --> Ready: send_at ≤ now + Scheduled --> Ready: Время наступило + + Ready --> Shown: Popup показан (AJAX) + Shown --> Read: Пользователь открыл + Read --> [*]: Завершено + + Created --> Deleted: >31 день (auto) + Scheduled --> Deleted: >31 день (auto) + Ready --> Deleted: >31 день (auto) + Shown --> Deleted: >31 день (auto) + Read --> Deleted: >31 день (auto) + Deleted --> [*] +``` + +### Кодирование получателей + +**Правило кодирования:** +``` +1000000 → Все сотрудники +1000000 + group_id → Группа +admin_id → Индивидуальный сотрудник +``` + +**Примеры:** +```php +recipients = [1000000] // Всем +recipients = [1000050] // Администраторам (group_id=50) +recipients = [1000030, 1000050] // Флористам И Администраторам +recipients = [42, 43, 44] // Трем конкретным сотрудникам +recipients = [1000050, 42] // Администраторам И сотруднику #42 +``` + +### Отложенная отправка + +```php +// Немедленная отправка +$notification->isImmediate = true; +$notification->send_at = date('Y-m-d H:i:s'); + +// Отложенная отправка +$notification->isImmediate = false; +$notification->send_at = '2023-11-20 10:00:00'; +``` + +**Логика показа:** +```php +// Показываются только те, у которых send_at уже наступило +->andWhere(['<', 'send_at', date('Y-m-d H:i:s')]) +``` + +### Автоматическая очистка + +**Триггер:** Вызывается при каждом создании нового уведомления. + +**Правило:** Удаляются уведомления старше 31 дня от `created_at`. + +```php +$old_date = date('Y-m-d H:i:s', strtotime('-31 day', time())); +Notification::deleteAll(['<', 'created_at', $old_date]); +``` + +### Контроль доступа + +**Права RBAC:** +```php +'notification/editAll' // Может редактировать любые уведомления +'notification/deleteAll' // Может удалять любые уведомления +'changeRatingByKik' // (для другого модуля) +``` + +**Логика доступа:** +- **Просмотр:** Автор или получатель (или права editAll/deleteAll) +- **Редактирование:** Только автор (или право editAll) +- **Удаление:** Только автор (или право deleteAll) + +## 📝 Примеры использования + +### Пример 1: Создание уведомления для всех + +```php +$notification = new Notification(['scenario' => Notification::SCENARIO_ADD]); +$notification->name = 'Важное объявление'; +$notification->description = 'Изменения в графике работы'; +$notification->content = '

С завтрашнего дня изменяется график работы...

'; +$notification->type = 'important'; +$notification->recipients = [1000000]; // Всем +$notification->isImmediate = true; + +if ($notification->upload()) { + // Успешно создано + // NotificationService автоматически создаст NotificationStatus для всех сотрудников +} +``` + +**Результат в БД:** + +*notification:* +``` +id: 123 +name: "Важное объявление" +description: "Изменения в графике работы" +content: "

С завтрашнего дня...

" +type: "important" +created_by: 15 +created_at: "2023-11-17 14:30:00" +send_at: "2023-11-17 14:30:00" +``` + +*notification_status (150 записей для 150 сотрудников):* +``` +{ notification_id: 123, admin_id: 1, status: 0 } +{ notification_id: 123, admin_id: 2, status: 0 } +... +{ notification_id: 123, admin_id: 150, status: 0 } +``` + +### Пример 2: Отложенное уведомление для группы + +```php +$notification = new Notification(['scenario' => Notification::SCENARIO_ADD]); +$notification->name = 'Совещание администраторов'; +$notification->content = '

Завтра в 10:00 совещание...

'; +$notification->type = 'meeting'; +$notification->recipients = [1000050]; // Администраторы (group_id=50) +$notification->isImmediate = false; +$notification->send_at = '2023-11-18 08:00:00'; // Показать завтра в 8:00 + +$notification->upload(); +``` + +**Поведение:** +- До 2023-11-18 08:00 - не появляется в pending +- После 2023-11-18 08:00 - появляется в AJAX ответе /notification/pending + +### Пример 3: AJAX получение непрочитанных уведомлений + +```javascript +// Периодический опрос (каждые 60 секунд) +setInterval(function() { + $.post('/notification/pending', { + action: 'pendingNotifications' + }, function(data) { + if (data.length > 0) { + // Показать popup с уведомлением + showNotificationPopup(data[0]); + + // Отметить как показанное + $.post('/notification/pending', { + action: 'notificationIsShown', + notification_id: data[0].notification_id + }); + } + }); +}, 60000); +``` + +### Пример 4: Массовая отметка "Прочитано" + +```javascript +// Форма на странице /notification/index +$('#markAllAsRead').click(function() { + $.post('/notification/index', { + action: 'markIsRead' + }, function() { + location.reload(); + }); +}); +``` + +**Эффект:** Все уведомления на текущей странице будут помечены как прочитанные (status=2). + +### Пример 5: Программное создание уведомления + +```php +use yii_app\records\Notification; +use yii_app\services\NotificationService; + +// Создать уведомление +$notification = new Notification(); +$notification->name = 'Новый регламент'; +$notification->description = 'Добавлен новый регламент'; +$notification->content = '

Ознакомьтесь с новым регламентом...

'; +$notification->type = 'regulation'; +$notification->created_by = 1; // Системный пользователь +$notification->created_at = date('Y-m-d H:i:s'); +$notification->send_at = date('Y-m-d H:i:s'); +$notification->save(); + +// Инициализировать для группы "Флористы" +$notification->recipients = [1000030]; // group_id=30 +NotificationService::initNotification($notification); +``` + +## 🗄️ База данных + +### ER-диаграмма + +```mermaid +erDiagram + notification ||--o{ notification_status : "has many" + notification ||--|| admin : "created_by" + notification ||--o{ files : "has images" + + notification_status ||--|| admin : "admin_id" + notification_status ||--|| notification : "notification_id" + + notification { + int id PK + string name + string description + text content + string type + int created_by FK + datetime created_at + datetime send_at + } + + notification_status { + int id PK + int notification_id FK + int admin_id FK + int status "0,1,2" + } + + files { + int id PK + int entity_id FK + string entity "notification" + string url + string file_type + datetime created_at + } + + admin { + int id PK + string name + int group_id + } +``` + +### Индексы + +**notification:** +```sql +KEY idx_created_at (created_at) +KEY idx_send_at (send_at) +KEY idx_created_by (created_by) +KEY idx_type (type) +``` + +**notification_status:** +```sql +UNIQUE KEY uniq_notification_admin (notification_id, admin_id) +KEY idx_admin_status (admin_id, status) +KEY idx_notification (notification_id) +``` + +### Типичные запросы + +**Получить непрочитанные уведомления:** +```sql +SELECT ns.*, n.* +FROM notification_status ns +JOIN notification n ON n.id = ns.notification_id +WHERE ns.admin_id = 42 + AND ns.status < 2 + AND n.send_at <= NOW() +ORDER BY n.send_at DESC; +``` + +**Статистика по уведомлению:** +```sql +SELECT + status, + COUNT(*) as count, + COUNT(*) * 100.0 / (SELECT COUNT(*) FROM notification_status WHERE notification_id = 123) as percent +FROM notification_status +WHERE notification_id = 123 +GROUP BY status; +``` + +## ❓ Часто задаваемые вопросы + +### 1. Как отправить уведомление конкретным сотрудникам? + +В поле `recipients` укажите массив ID: +```php +$notification->recipients = [42, 43, 44]; // Трем сотрудникам +``` + +### 2. Можно ли отправить уведомление нескольким группам? + +Да: +```php +$notification->recipients = [1000030, 1000050]; // Флористам и Администраторам +``` + +### 3. В чем разница между status=1 и status=2? + +- **status=0** - Уведомление создано, но еще не показано +- **status=1** - Popup с уведомлением показан пользователю (вызван AJAX notificationIsShown) +- **status=2** - Пользователь открыл уведомление и прочитал его (открыт /notification/view) + +### 4. Как работает автоматическая очистка? + +При каждом создании нового уведомления вызывается `clearOldNotifications()`, который удаляет все уведомления старше 31 дня (по полю `created_at`). + +### 5. Можно ли прикрепить изображения? + +Да, поддерживается загрузка до 4 изображений (png, jpg): +```php +$notification->imageFiles = UploadedFile::getInstances($notification, 'imageFiles'); +``` + +*(Код закомментирован в текущей версии, но инфраструктура готова)* + +### 6. Что происходит при удалении уведомления? + +```php +// 1. Удаляются все статусы +NotificationStatus::deleteAll(['notification_id' => $id]); + +// 2. Удаляется само уведомление +$notification->delete(); +``` + +### 7. Как определить, сколько человек прочитало уведомление? + +```php +$readCount = NotificationStatus::find() + ->where(['notification_id' => 123, 'status' => 2]) + ->count(); + +$totalCount = NotificationStatus::find() + ->where(['notification_id' => 123]) + ->count(); + +$readPercent = ($readCount / $totalCount) * 100; +``` + +### 8. Можно ли редактировать отправленное уведомление? + +Да, но только автор или пользователи с правом `notification/editAll`. При редактировании изменяется только контент, статусы получателей не меняются. + +## 🔗 Связи с другими модулями + +```mermaid +graph LR + Notifications --> Lesson[Lesson Module] + Notifications --> Regulations[Regulations Module] + Notifications --> KIK[KIK Feedback] + Notifications --> Admin[Admin/Users] + Notifications --> Files[Files System] + + style Notifications fill:#e1f5ff +``` + +**Интеграции:** +- **Lesson** - уведомления о новых уроках +- **Regulations** - уведомления о новых регламентах +- **KIK Feedback** - уведомления от контроля качества +- **Admin** - пользователи и группы +- **Files** - система хранения изображений + +## 🎯 Метрики модуля + +| Метрика | Значение | +|---------|----------| +| **Контроллеры** | 1 | +| **Сервисы** | 1 | +| **Actions** | 3 | +| **Direct Actions** | 1 (delete) | +| **Models/Records** | 2 | +| **Строк кода (сервис)** | 50 | +| **AJAX API методов** | 2 | +| **TTL уведомлений** | 31 день | +| **Статусов** | 3 (0,1,2) | + +## 🚀 Возможности для улучшения + +1. **Email интеграция** - дублирование уведомлений на email +2. **Telegram Bot** - отправка критичных уведомлений в Telegram +3. **Push уведомления** - для мобильных приложений +4. **Шаблоны** - готовые шаблоны уведомлений +5. **Приоритеты** - низкий/средний/высокий/критичный +6. **Категории** - более детальная классификация типов +7. **Вложения** - поддержка PDF, документов +8. **Реакции** - возможность поставить like/dislike +9. **Комментарии** - обсуждение уведомлений +10. **Аналитика** - статистика прочтений, эффективность + +## 📚 См. также + +- [Модуль Lesson](../lesson/README.md) - создает уведомления о новых уроках +- [Модуль Regulations](../regulations/README.md) - создает уведомления о регламентах +- [Модуль KIK Feedback](../kik-feedback/README.md) - уведомления от КК +- [RBAC система](../../rbac/README.md) - управление правами доступа + +--- + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/modules/payroll/README.md b/erp24/docs/modules/payroll/README.md new file mode 100644 index 00000000..067cdaf0 --- /dev/null +++ b/erp24/docs/modules/payroll/README.md @@ -0,0 +1,923 @@ +# Модуль Payroll (Расчет заработной платы) + +## Описание + +Модуль Payroll отвечает за автоматизированный расчет заработной платы сотрудников (флористов, администраторов, кустовых менеджеров). Включает расчет окладов, премий, командных бонусов, учет рабочих дней и смен, а также хранение истории расчетов. + +## Архитектура модуля + +```mermaid +graph TB + subgraph "Контроллеры" + PC[PayrollController] + APC[AdminPayrollController] + APVDC[AdminPayrollValuesDictController] + end + + subgraph "Сервисы" + PS[PayrollService] + APMIS[AdminPayrollMonthInfoService] + APDS[AdminPayrollDaysService] + end + + subgraph "Actions (8 экшенов)" + IndexAction[IndexAction] + StoreAction[StoreAction] + MakeAction[MakeAction] + ManagementAction[ManagementAction] + MakePayrollDaysAction[MakePayrollDaysAction] + ListAction[ListAction] + ListAdminsAction[ListAdminsAction] + ListShiftAdminsAction[ListShiftAdminsAction] + end + + subgraph "Модели/Records (10)" + AP[AdminPayroll] + APD[AdminPayrollDays] + APMI[AdminPayrollMonthInfo] + APV[AdminPayrollValues] + APVD[AdminPayrollValuesDict] + APS[AdminPayrollStat] + APH[AdminPayrollHistory] + end + + subgraph "База данных" + DB[(admin_payroll
admin_payroll_days
admin_payroll_month_info
admin_payroll_values
admin_payroll_values_dict)] + end + + PC --> IndexAction + PC --> StoreAction + PC --> MakeAction + PC --> ManagementAction + PC --> MakePayrollDaysAction + + MakeAction --> APMIS + MakePayrollDaysAction --> APDS + + APMIS --> AP + APMIS --> APH + APDS --> APD + + AP --> DB + APD --> DB + APMI --> DB + + style PC fill:#e1f5ff + style PS fill:#fff4e1 + style AP fill:#e8f5e9 + style DB fill:#fce4ec +``` + +## Компоненты модуля + +### Контроллеры (3) + +#### 1. **PayrollController** +`erp24/controllers/PayrollController.php` + +Основной контроллер для работы с расчетом заработной платы. + +**Экшены:** +- `index` - Главная страница расчета зарплаты +- `store` - Расчет зарплаты по магазину +- `make` - Создание расчета зарплаты за месяц +- `management` - Управление расчетами +- `make-payroll-days` - Создание ежедневных расчетов зарплаты +- `list` - Список расчетов +- `list-admins` - Список сотрудников для расчета +- `list-shift-admins` - Список сотрудников по сменам + +**Действие по умолчанию:** `index` + +#### 2. **AdminPayrollController** +`erp24/controllers/AdminPayrollController.php` + +Административный контроллер для управления расчетами зарплаты. + +#### 3. **AdminPayrollValuesDictController** +`erp24/controllers/AdminPayrollValuesDictController.php` + +Контроллер для управления справочником значений расчета зарплаты. + +--- + +### Сервисы (3) + +#### 1. **PayrollService** +`erp24/services/PayrollService.php` + +Основной сервис для работы с расчетом зарплаты. + +**Ключевые методы:** + +##### Проверка разрешений +- `getAllowedPayrollUpdate($dateFrom, $groupId): bool` - Проверка разрешения на обновление расчета зарплаты + - Разрешено только для определенных групп (1, 8, 9, 51, 81) + - Блокируется после 16-го числа месяца для предыдущих периодов + - Предотвращает изменение старых расчетов + +**Логика блокировки:** +```php +// Можно редактировать: +// 1. Текущий месяц (до 16-го числа следующего месяца) +// 2. Предыдущий месяц (до 16-го числа текущего месяца) + +// Например, если сегодня 20 января: +// ✅ Можно редактировать январь +// ❌ Нельзя редактировать декабрь (прошло 16-е января) +// ❌ Нельзя редактировать ноябрь и ранее +``` + +##### Обработка ошибок +- `outputCheckError($errorText, $buttonParams, $controller): array` - Вывод ошибок проверки + +**Зависимости:** +- `CabinetService` + +--- + +#### 2. **AdminPayrollMonthInfoService** +`erp24/services/AdminPayrollMonthInfoService.php` + +Сервис для расчета зарплаты за месяц с учетом всех показателей. + +**Свойства:** +- `$yearSelect` - Год расчета +- `$monthSelect` - Месяц расчета (без нуля) +- `$monthWithZeroSelect` - Месяц расчета (с нулем) +- `$dateFrom` - Дата начала периода +- `$dateTo` - Дата окончания периода +- `$dateFromBeginMonth` - Начало месяца +- `$dateToEndMonth` - Конец месяца +- `$groupIds` - ID групп сотрудников для расчета +- `$notInStoreIds` - ID магазинов-исключений +- `$employeePosition` - Должности сотрудников +- `$employeeAdminGroup` - Группы администраторов +- `$cityStoreNames` - Названия магазинов + +**Ключевые методы:** + +##### Конструктор +```php +public function __construct($dateFrom) +``` +- Инициализирует все свойства на основе даты начала периода +- Загружает справочные данные (магазины, должности, группы) +- Определяет период расчета (если текущий месяц - до вчера, иначе до конца месяца) + +##### Основной расчет +- `setAdminPayrollHistory()` - Создание/обновление записей расчета зарплаты за месяц + - Очищает расчеты уволенных сотрудников + - Очищает расчеты сотрудников без смен + - Получает список сотрудников из расписания + - Для каждого сотрудника вызывает расчет через `CabinetService::getDataDynamic202310()` + - Сохраняет результаты в `AdminPayroll` и `AdminPayrollHistory` + +**Процесс расчета:** +1. Очистка данных уволенных и неработавших сотрудников +2. Получение списка сотрудников из расписания (Timetable) +3. Определение группы сотрудника (флорист/администратор/кустовой) +4. Расчет показателей для каждого сотрудника +5. Сохранение в базу данных + +**Зависимости:** +- `CabinetService` +- `ExportImportService` +- `RatingService` +- `Admin` +- `AdminPayroll` +- `AdminPayrollHistory` +- `AdminGroup` +- `CityStore` +- `EmployeePosition` + +--- + +#### 3. **AdminPayrollDaysService** +`erp24/services/AdminPayrollDaysService.php` + +Сервис для ежедневного расчета зарплаты. + +**Ключевые методы:** + +##### Ежедневный расчет +```php +public static function setAdminPayrollDays($dateFrom, $dateTo, $personPayrollMake = null) +``` + +Создает ежедневные записи расчета зарплаты для сотрудников. + +**Параметры:** +- `$dateFrom` - Дата начала +- `$dateTo` - Дата окончания +- `$personPayrollMake` - Массив ID сотрудников (опционально, для пересчета конкретных людей) + +**Группы сотрудников для расчета:** +- 30 - (требует уточнения) +- 35 - (требует уточнения) +- 40 - (требует уточнения) +- 45 - Подработчики +- 50 - Администраторы +- 72 - (требует уточнения) + +**Процесс:** +1. Проверка последних обновлений (пропуск записей обновленных менее часа назад) +2. Получение списка сотрудников из расписания за указанный период +3. Получение данных о сменах сотрудников +4. Расчет показателей для каждого дня +5. Сохранение в `AdminPayrollDays` + +**Оптимизация:** +- Пропускает записи, обновленные за последний час (защита от дублирующих расчетов) +- Поддерживает пересчет для конкретных сотрудников + +**Зависимости:** +- `CabinetService` +- `Admin` +- `AdminPayrollDays` +- `AdminGroup` +- `CityStore` +- `EmployeePosition` + +--- + +### Actions (8) + +#### 1. **IndexAction** +`erp24/actions/payroll/IndexAction.php` + +Главная страница модуля расчета зарплаты. + +**Функционал:** +- Отображение общей информации о расчетах +- Навигация по периодам + +#### 2. **StoreAction** +`erp24/actions/payroll/StoreAction.php` + +Просмотр расчета зарплаты по конкретному магазину. + +**Функционал:** +- Фильтрация по магазину +- Список сотрудников магазина +- Итоговые суммы по магазину + +#### 3. **MakeAction** +`erp24/actions/payroll/MakeAction.php` + +Создание расчета зарплаты за месяц. + +**Функционал:** +- Запуск расчета зарплаты за выбранный месяц +- Использует `AdminPayrollMonthInfoService` +- Пакетная обработка всех сотрудников + +#### 4. **ManagementAction** +`erp24/actions/payroll/ManagementAction.php` + +Управление расчетами зарплаты. + +**Функционал:** +- Просмотр существующих расчетов +- Редактирование расчетов (с ограничениями по датам) +- Удаление расчетов + +#### 5. **MakePayrollDaysAction** +`erp24/actions/payroll/MakePayrollDaysAction.php` + +Создание ежедневных расчетов зарплаты. + +**Функционал:** +- Ежедневный автоматический расчет +- Использует `AdminPayrollDaysService` +- Поддерживает пересчет конкретных сотрудников + +#### 6. **ListAction** +`erp24/actions/payroll/ListAction.php` + +Список всех расчетов зарплаты. + +**Функционал:** +- Табличное представление расчетов +- Фильтрация по периоду +- Сортировка +- Пагинация + +#### 7. **ListAdminsAction** +`erp24/actions/payroll/ListAdminsAction.php` + +Список сотрудников для расчета зарплаты. + +**Функционал:** +- Список сотрудников с расчетами +- Фильтрация по группам +- Поиск по имени + +#### 8. **ListShiftAdminsAction** +`erp24/actions/payroll/ListShiftAdminsAction.php` + +Список сотрудников по сменам. + +**Функционал:** +- Отображение сотрудников, работавших в указанный период +- Группировка по сменам +- Статистика по отработанным дням + +--- + +### Модели/Records (10) + +#### 1. **AdminPayroll** +`erp24/records/AdminPayroll.php` + +Основная таблица расчета зарплаты за месяц. + +**Таблица:** `admin_payroll` + +**Поля:** +- `id` (int) - ID записи +- `admin_id` (int) - ID сотрудника (admin) +- `year` (int) - Год расчета +- `month` (int) - Месяц расчета +- `store_id` (int) - ID магазина +- `position_id` (int) - ID должности +- `group_id` (int) - ID группы сотрудника +- `payroll_constant` (decimal) - Постоянная часть (оклад) +- `payroll_variable` (decimal) - Переменная часть (премии, бонусы) +- `payroll_total` (decimal) - Итого к выплате +- `work_days` (int) - Количество рабочих дней +- `packet_num` (bigint) - Номер пакета расчета (timestamp) +- `created_at` (datetime) - Дата создания +- `updated_at` (datetime) - Дата обновления + +**Статические методы:** +- `clearPayrollFiredAdmin($year, $month)` - Очистка расчетов уволенных сотрудников +- `clearPayrollWithoutShiftAdmin($year, $month)` - Очистка расчетов сотрудников без смен + +#### 2. **AdminPayrollDays** +`erp24/records/AdminPayrollDays.php` + +Ежедневный расчет зарплаты. + +**Таблица:** `admin_payroll_days` + +**Поля:** +- `id` (int) - ID записи +- `admin_id` (int) - ID сотрудника +- `date` (date) - Дата расчета +- `store_id` (int) - ID магазина +- `position_id` (int) - ID должности +- `group_id` (int) - ID группы +- `payroll_constant` (decimal) - Постоянная часть за день (оклад/смена) +- `payroll_variable` (decimal) - Переменная часть за день (премии) +- `payroll_constant_and_variable` (decimal) - Сумма постоянной и переменной части +- `payroll_game_bonus` (int) - Игровые бонусы (цветорубли) за день +- `date_time` (datetime) - Дата и время расчета +- `created_at` (datetime) - Дата создания +- `updated_at` (datetime) - Дата обновления + +**Использование:** +- Хранит детализацию зарплаты по дням +- Используется для формирования итогового расчета за месяц +- Позволяет отслеживать динамику заработка + +#### 3. **AdminPayrollMonthInfo** +`erp24/records/AdminPayrollMonthInfo.php` + +Дополнительная информация о расчете за месяц. + +**Таблица:** `admin_payroll_month_info` + +**Поля:** +- `id` (int) - ID записи +- `admin_id` (int) - ID сотрудника +- `year` (int) - Год +- `month` (int) - Месяц +- `info_json` (text) - JSON с детализацией расчета +- `created_at` (datetime) - Дата создания + +**Структура JSON (примерная):** +```json +{ + "work_days": 22, + "shifts": 15, + "sales": 450000, + "avg_check": 1850, + "conversion": 82, + "quality": 95, + "game_bonus_total": 75, + "team_bonus": 15000, + "bonuses_detail": { + "quality": 4000, + "sales_plan": 7000, + "low_writeoffs": 9000 + } +} +``` + +#### 4. **AdminPayrollValues** +`erp24/records/AdminPayrollValues.php` + +Значения показателей для расчета зарплаты. + +**Таблица:** `admin_payroll_values` + +**Поля:** +- `id` (int) - ID записи +- `admin_id` (int) - ID сотрудника +- `date` (date) - Дата +- `value_type` (string) - Тип значения (sales, conversion, quality и т.д.) +- `value` (decimal) - Значение показателя +- `created_at` (datetime) - Дата создания + +#### 5. **AdminPayrollValuesDict** +`erp24/records/AdminPayrollValuesDict.php` + +Справочник типов значений для расчета зарплаты. + +**Таблица:** `admin_payroll_values_dict` + +**Поля:** +- `id` (int) - ID записи +- `code` (string) - Код типа значения +- `name` (string) - Название +- `description` (text) - Описание +- `is_active` (tinyint) - Активность + +**Примеры типов:** +- `sales` - Продажи +- `conversion` - Конверсия +- `quality` - Качество +- `avg_check` - Средний чек +- `writeoffs_percent` - Процент списаний +- `game_bonus` - Игровые бонусы +- `team_bonus` - Командный бонус + +#### 6. **AdminPayrollStat** +`erp24/records/AdminPayrollStat.php` + +Статистика расчетов зарплаты. + +**Таблица:** `admin_payroll_stat` + +**Поля:** +- `id` (int) - ID записи +- `year` (int) - Год +- `month` (int) - Месяц +- `total_employees` (int) - Общее количество сотрудников +- `total_payroll` (decimal) - Общая сумма выплат +- `total_constant` (decimal) - Общая сумма окладов +- `total_variable` (decimal) - Общая сумма премий +- `avg_payroll` (decimal) - Средняя зарплата +- `created_at` (datetime) - Дата создания + +#### 7. **AdminPayrollHistory** +`erp24/records/AdminPayrollHistory.php` + +История изменений расчетов зарплаты. + +**Таблица:** `admin_payroll_history` + +**Поля:** +- `id` (int) - ID записи +- `admin_payroll_id` (int) - ID записи из admin_payroll +- `admin_id` (int) - ID сотрудника +- `year` (int) - Год +- `month` (int) - Месяц +- `old_value` (decimal) - Старое значение +- `new_value` (decimal) - Новое значение +- `change_type` (string) - Тип изменения +- `changed_by` (int) - Кто изменил (admin_id) +- `created_at` (datetime) - Дата изменения + +**Использование:** +- Аудит всех изменений зарплаты +- Отслеживание пересчетов +- Контроль ручных корректировок + +#### 8. **AdminPayrollSearch** +`erp24/records/AdminPayrollSearch.php` + +Search-модель для фильтрации расчетов зарплаты. + +**Использование:** +- Фильтрация в GridView +- Поиск по различным полям +- Сортировка + +#### 9. **AdminPayrollDaysSearch** +`erp24/records/AdminPayrollDaysSearch.php` + +Search-модель для фильтрации ежедневных расчетов. + +#### 10. **AdminPayrollValuesDictSearch** +`erp24/records/AdminPayrollValuesDictSearch.php` + +Search-модель для фильтрации справочника значений. + +--- + +## Бизнес-логика + +### Структура зарплаты + +Заработная плата сотрудника состоит из двух частей: + +``` +Зарплата = Постоянная часть + Переменная часть + +где: +- Постоянная часть (payroll_constant) = Оклад +- Переменная часть (payroll_variable) = Премии + Бонусы + Командный бонус +``` + +### Расчет по категориям сотрудников + +#### Флористы + +**Постоянная часть:** +- Оклад за смену (из `EmployeePayment`) +- Зависит от количества отработанных смен + +**Переменная часть:** +- Игровые бонусы (цветорубли) за KPI +- Премия за выполнение плана +- Командный бонус (доля от премиального фонда магазина) +- Бонус за качество + +#### Администраторы + +**Постоянная часть:** +- Фиксированный оклад (зависит от плана магазина) + - <1M → 25000₽ + - ≥1M → 30000₽ + - ≥1.5M → 35000₽ + - ≥2M → 40000₽ + +**Переменная часть:** +- Премия за выполнение плана продаж +- Премия за низкий % списаний +- Премия за качество +- Командный бонус +- Понижающий коэффициент при падении продаж + +#### Кустовые менеджеры + +**Постоянная часть:** +- Фиксированный оклад + +**Переменная часть:** +- Премия за выполнение плана куста +- Премия за низкий % списаний куста +- Премия за рейтинг по цветорублям +- Премия за рост постоянных клиентов + +### Процесс расчета зарплаты + +#### Ежедневный расчет (через MakePayrollDaysAction) + +```mermaid +sequenceDiagram + participant Cron as Cron/Manual + participant Action as MakePayrollDaysAction + participant Service as AdminPayrollDaysService + participant Timetable as Timetable + participant Cabinet as CabinetService + participant DB as AdminPayrollDays + + Cron->>Action: Запуск расчета за день + Action->>Service: setAdminPayrollDays(dateFrom, dateTo) + Service->>Timetable: Получить список сотрудников со сменами + Timetable-->>Service: Список admin_id + + loop Для каждого сотрудника + Service->>Cabinet: Получить показатели за день + Cabinet-->>Service: Продажи, конверсия, средний чек и т.д. + Service->>Service: Расчет окладной и премиальной части + Service->>DB: Сохранить AdminPayrollDays + end + + Service-->>Action: Расчет завершен + Action-->>Cron: Успех +``` + +#### Месячный расчет (через MakeAction) + +```mermaid +sequenceDiagram + participant User as Пользователь + participant Action as MakeAction + participant Service as AdminPayrollMonthInfoService + participant PayrollDays as AdminPayrollDays + participant Bonus as BonusService + participant DB as AdminPayroll + + User->>Action: Запуск расчета за месяц + Action->>Service: setAdminPayrollHistory() + Service->>Service: Очистка уволенных/без смен + Service->>PayrollDays: Получить данные за все дни месяца + + loop Для каждого сотрудника + Service->>Service: Суммировать данные за месяц + Service->>Bonus: Расчет командного бонуса + Bonus-->>Service: Сумма командного бонуса + Service->>Service: Формирование итоговой зарплаты + Service->>DB: Сохранить AdminPayroll + Service->>DB: Сохранить AdminPayrollHistory + end + + Service-->>Action: Расчет завершен + Action-->>User: Отчет готов +``` + +### Правила пересчета + +1. **Автоматическая блокировка старых периодов:** + - Можно редактировать только текущий и предыдущий месяц + - После 16-го числа блокируется редактирование предыдущего месяца + - Только группы 1, 8, 9, 51, 81 могут редактировать + +2. **Очистка данных:** + - Автоматически удаляются расчеты уволенных сотрудников + - Автоматически удаляются расчеты сотрудников без смен в периоде + +3. **Пересчет при изменениях:** + - При изменении расписания (Timetable) нужен пересчет + - При изменении бонусов нужен пересчет + - При изменении настроек командных бонусов нужен пересчет + +--- + +## API и интеграции + +### Внутренние зависимости + +Модуль взаимодействует с: +- **Bonus** - для расчета игровых и командных бонусов +- **Timetable** - для получения данных о сменах +- **Rating** - для расчета премий по рейтингам +- **WriteOffs** - для учета списаний при расчете премий +- **Sales** - для данных о продажах +- **Admin** - для данных о сотрудниках +- **CityStore** - для данных о магазинах +- **EmployeePayment** - для данных об окладах +- **EmployeePosition** - для должностей + +### Сервисные зависимости + +``` +PayrollService +├── CabinetService (основной расчет показателей) +│ +AdminPayrollMonthInfoService +├── CabinetService (расчет показателей) +├── ExportImportService (экспорт данных) +├── RatingService (расчет рейтингов) +│ +AdminPayrollDaysService +└── CabinetService (ежедневные показатели) +``` + +--- + +## Маршруты (Routes) + +``` +/payroll/index - Главная страница расчета зарплаты +/payroll/store - Расчет зарплаты по магазину +/payroll/make - Создание расчета за месяц +/payroll/management - Управление расчетами +/payroll/make-payroll-days - Создание ежедневных расчетов +/payroll/list - Список расчетов +/payroll/list-admins - Список сотрудников +/payroll/list-shift-admins - Список сотрудников по сменам + +/admin-payroll/* - Административное управление расчетами +/admin-payroll-values-dict/* - Управление справочником значений +``` + +--- + +## Примеры использования + +### Пример 1: Запуск ежедневного расчета + +```php +// Расчет за конкретный день +AdminPayrollDaysService::setAdminPayrollDays( + '2024-01-15', + '2024-01-15' +); + +// Расчет за период +AdminPayrollDaysService::setAdminPayrollDays( + '2024-01-01', + '2024-01-31' +); + +// Пересчет для конкретных сотрудников +$adminIds = [123, 456, 789]; +AdminPayrollDaysService::setAdminPayrollDays( + '2024-01-01', + '2024-01-31', + $adminIds +); +``` + +### Пример 2: Расчет зарплаты за месяц + +```php +$service = new AdminPayrollMonthInfoService('2024-01-01'); +$service->setAdminPayrollHistory(); + +// После выполнения: +// - Созданы записи в admin_payroll для всех сотрудников +// - Сохранена история в admin_payroll_history +// - Очищены данные уволенных и неработавших +``` + +### Пример 3: Проверка разрешения на редактирование + +```php +$dateFrom = '2023-12-01'; +$groupId = Yii::$app->user->identity->group_id; + +$allowed = PayrollService::getAllowedPayrollUpdate($dateFrom, $groupId); + +if ($allowed) { + // Можно редактировать расчет +} else { + // Редактирование заблокировано + throw new ForbiddenHttpException('Редактирование расчета за этот период запрещено'); +} +``` + +### Пример 4: Получение расчета сотрудника за месяц + +```php +$adminId = 123; +$year = 2024; +$month = 1; + +$payroll = AdminPayroll::find() + ->where([ + 'admin_id' => $adminId, + 'year' => $year, + 'month' => $month, + ]) + ->one(); + +if ($payroll) { + echo "Оклад: {$payroll->payroll_constant} руб.\n"; + echo "Премии: {$payroll->payroll_variable} руб.\n"; + echo "Итого: {$payroll->payroll_total} руб.\n"; + echo "Рабочих дней: {$payroll->work_days}\n"; +} +``` + +### Пример 5: Детализация зарплаты по дням + +```php +$adminId = 123; +$dateFrom = '2024-01-01'; +$dateTo = '2024-01-31'; + +$days = AdminPayrollDays::find() + ->where(['admin_id' => $adminId]) + ->andWhere(['>=', 'date', $dateFrom]) + ->andWhere(['<=', 'date', $dateTo]) + ->orderBy(['date' => SORT_ASC]) + ->all(); + +foreach ($days as $day) { + echo "{$day->date}: "; + echo "Оклад {$day->payroll_constant} + "; + echo "Премия {$day->payroll_variable} = "; + echo "{$day->payroll_constant_and_variable} руб., "; + echo "Цветорубли: {$day->payroll_game_bonus}\n"; +} +``` + +--- + +## База данных + +### Основные таблицы + +1. **admin_payroll** - Расчет зарплаты за месяц +2. **admin_payroll_days** - Ежедневный расчет зарплаты +3. **admin_payroll_month_info** - Дополнительная информация о расчете +4. **admin_payroll_values** - Значения показателей +5. **admin_payroll_values_dict** - Справочник типов значений +6. **admin_payroll_stat** - Статистика расчетов +7. **admin_payroll_history** - История изменений + +### ER-диаграмма + +```mermaid +erDiagram + admin_payroll ||--o{ admin_payroll_history : "payroll_id" + admin_payroll }o--|| admin : "admin_id" + admin_payroll }o--|| city_store : "store_id" + admin_payroll }o--|| employee_position : "position_id" + + admin_payroll_days }o--|| admin : "admin_id" + admin_payroll_days }o--|| city_store : "store_id" + + admin_payroll_month_info }o--|| admin : "admin_id" + + admin_payroll_values }o--|| admin : "admin_id" + admin_payroll_values }o--|| admin_payroll_values_dict : "value_type" + + admin_payroll { + int id PK + int admin_id FK + int year + int month + int store_id FK + int position_id FK + decimal payroll_constant + decimal payroll_variable + decimal payroll_total + int work_days + bigint packet_num + datetime created_at + } + + admin_payroll_days { + int id PK + int admin_id FK + date date + decimal payroll_constant + decimal payroll_variable + int payroll_game_bonus + datetime date_time + } + + admin_payroll_history { + int id PK + int admin_payroll_id FK + int admin_id FK + decimal old_value + decimal new_value + string change_type + int changed_by FK + datetime created_at + } +``` + +--- + +## Метрики и аналитика + +### Ключевые метрики + +1. **Общий фонд оплаты труда (ФОТ) за месяц** +2. **ФОТ по категориям (флористы, администраторы, кустовые)** +3. **% ФОТ от продаж по магазинам** +4. **Средняя зарплата по категориям** +5. **Доля переменной части в общей зарплате** +6. **Количество сотрудников, получивших командный бонус** +7. **Средний размер командного бонуса** +8. **Динамика ФОТ месяц к месяцу** + +--- + +## Автоматизация + +### Рекомендуемое расписание Cron + +```bash +# Ежедневный расчет зарплаты (каждый день в 03:00) +0 3 * * * /usr/bin/php /path/to/yii payroll/make-payroll-days + +# Месячный расчет (1-го числа каждого месяца в 04:00) +0 4 1 * * /usr/bin/php /path/to/yii payroll/make + +# Пересчет за вчера (каждый день в 08:00, для корректировок) +0 8 * * * /usr/bin/php /path/to/yii payroll/make-payroll-days --date=yesterday +``` + +--- + +## Вопросы для уточнения + +1. ❓ Какие группы сотрудников соответствуют ID 30, 35, 40, 72 в AdminPayrollDaysService? +2. ❓ Какая формула используется в `CabinetService::getDataDynamic202310()` для расчета показателей? +3. ❓ Как обрабатываются больничные и отпуска при расчете? +4. ❓ Есть ли автоматическая индексация окладов? +5. ❓ Как обрабатываются переводы сотрудников между магазинами в середине месяца? + +--- + +## Связанные модули + +- [Bonus (Бонусная система)](../bonus/README.md) - Расчет бонусов и премий +- [Timetable (Расписание)](../timetable/README.md) - Данные о сменах +- [Rating (Рейтинговая система)](../rating/README.md) - Рейтинги для премий +- [Write-offs (Списания)](../write-offs/README.md) - Учет списаний +- [Dashboard (Информационные панели)](../dashboard/README.md) - Визуализация данных + +--- + +*Документация создана автоматически. Последнее обновление: 2025-11-17* diff --git a/erp24/docs/modules/rating/README.md b/erp24/docs/modules/rating/README.md new file mode 100644 index 00000000..76435bc0 --- /dev/null +++ b/erp24/docs/modules/rating/README.md @@ -0,0 +1,858 @@ +# Модуль Rating (Рейтинговая система) + +## 📋 Описание + +**Rating** - модуль рейтинговой системы для оценки эффективности работы сотрудников. Система автоматически рассчитывает рейтинги для администраторов и флористов на основе их показателей (продажи, бонусы, конверсия, качество обслуживания) и формирует ежемесячные рейтинговые таблицы. + +### Основные возможности + +- 📊 Автоматический расчет рейтингов сотрудников +- 🏆 Разделение на категории (Администраторы, Флористы, Кустовые директора, Стажеры) +- 📈 Учет различных показателей эффективности +- ✍️ Ручная корректировка рейтинга качества (Quality Rating) +- 📝 Логирование всех изменений рейтингов +- 🔄 Периодический перерасчет с защитой от перезаписи +- 📅 Месячные отчеты и сравнения + +## 🏗️ Архитектура модуля + +```mermaid +graph TB + subgraph "Controllers" + RC[RatingController
3 actions] + R2C[Rating2Controller
1 action] + end + + subgraph "Services" + RS[RatingService
Расчет рейтингов] + end + + subgraph "Actions" + MA[MakeAction
Создание/обновление] + LA[ListAction
Список рейтингов] + TA[TestAction
Тестирование] + IA[IndexAction
Quality Rating UI] + end + + subgraph "Models/Records" + AR[AdminRating
Основная таблица] + QR[QualityRating
Рейтинг качества] + QRL[QualityRatingLog
Лог изменений] + end + + RC --> MA + RC --> LA + RC --> TA + R2C --> IA + + MA --> RS + RS --> AR + + IA --> QR + IA --> QRL + + style RS fill:#e1f5ff + style AR fill:#fff4e1 + style QR fill:#fff4e1 +``` + +## 🎮 Контроллеры + +### 1. RatingController + +**Файл:** `erp24/controllers/RatingController.php` + +**Действия:** +- `make` - Создание/обновление рейтингов (AdminRating) +- `list` - Просмотр списка рейтингов +- `test` - Тестовая страница для проверки расчетов + +**Паттерн:** Использует отдельные Action классы для каждого действия. + +```php +class RatingController extends \yii\web\Controller +{ + public $defaultAction = 'list'; + + public function actions() + { + return [ + 'make' => \yii_app\actions\rating\MakeAction::class, + 'list' => \yii_app\actions\rating\ListAction::class, + 'test' => \yii_app\actions\rating\TestAction::class, + ]; + } +} +``` + +### 2. Rating2Controller + +**Файл:** `erp24/controllers/Rating2Controller.php` + +**Действия:** +- `index` - Управление рейтингом качества (Quality Rating) + +**Назначение:** Отдельный контроллер для ручного управления качественными оценками от КК. + +## ⚙️ Сервисы + +### RatingService + +**Файл:** `erp24/services/RatingService.php` +**Размер:** 612 строк кода + +Основной сервис для расчета и управления рейтингами. + +#### Основные методы + +##### getData() +```php +public function getData( + $employeeId, + $employeeSelect, + $employeeGroupId, + $isAdministrator, + $dateFrom, + $dateTo, + $controller, + $request, + $winStoreIdDayChallenge, + $entityCityStore, + $exportCityStore, + $entityAdmin, + $exportAdmin, + $yearSelect, + $monthSelect, + $monthWithZeroSelect, + $dateFromBeginMonth, + $dateToEndMonth, + $employeePosition, + $employeeAdminGroup +) +``` + +**Назначение:** Сбор всех данных для расчета рейтинга конкретного сотрудника. + +**Логика:** +1. Проверка наличия магазина у сотрудника +2. Проверка связи с 1С (GUID) +3. Получение оклада и плана по магазину +4. Расчет продаж и списаний +5. Получение данных по сменам из Timetable +6. Расчет конверсии и бонусных карт +7. Применение игровых механик (баллы за показатели) +8. Возврат итоговых данных для сохранения + +**Возвращает:** +```php +[ + 'adminSumGameBonusTotal' => 12500, // Общая сумма баллов + 'adminSumGameCountShiftTotal' => 20, // Количество смен + 'adminSumGameAvgSumTotal' => 625, // Средний балл за смену + 'administratorsCount' => 5 // Кол-во администраторов в кластере +] +``` + +##### calculateRating() +```php +public function calculateRating($yearSelect, $monthWithZeroSelect): void +``` + +**Назначение:** Финальный расчет рейтинговых позиций (1, 2, 3...) на основе набранных баллов. + +**Логика:** +- Проходит по всем 4 типам рейтингов +- Сортирует по `value` (для рейтингов 1,4) или `avg_value` (для рейтингов 2,3) +- Присваивает порядковые номера (rating = 1, 2, 3, ...) + +```php +foreach (range(1,4) as $ratingId) { + $valueKey = in_array($ratingId, [2,3]) ? 'avg_value' : 'value'; + + $administratorRating = AdminRating::find() + ->andWhere(['rating_id' => $ratingId]) + ->andWhere(['date' => $yearSelect . '-' . $monthWithZeroSelect]) + ->orderBy([$valueKey => SORT_DESC]) + ->all(); + + $ratingNum = 1; + foreach ($administratorRating as $itemAdminRating) { + $itemAdminRating->rating = $ratingNum++; + $itemAdminRating->save(); + } +} +``` + +##### setRatingValue() +```php +public function setRatingValue( + $employeeId, + $adminSumGameBonusArray, + $ratingId, + $yearSelect, + $monthSelect, + $monthWithZeroSelect +): void +``` + +**Назначение:** Сохранение рассчитанного рейтинга в таблицу `admin_rating`. + +**Логика:** +1. Проверяет существование записи за период +2. Создает новую или обновляет существующую +3. Сохраняет: баллы, количество смен, средний балл, timestamp + +##### getRatingId() +```php +public static function getRatingId($employeeGroupId): int +``` + +**Назначение:** Определение типа рейтинга по группе сотрудника. + +**Типы рейтингов:** +- `1` - Администраторы (group_id = 50) +- `2` - Флористы (остальные) +- `4` - Стажеры (group_id = 72) +- `3` - Кустовые директора (вычисляется отдельно) + +```php +$ratingId = 2; // По умолчанию флорист +if (Admin::ADMINISTRATOR_GROUP_ID === (int)$employeeGroupId) { + $ratingId = 1; // Администратор +} +if (Admin::PART_TIME_WORKER_GROUP_ID === (int)$employeeGroupId) { + $ratingId = 4; // Стажер +} +return $ratingId; +``` + +##### getAllowedCalculateRating() +```php +public static function getAllowedCalculateRating($dateFrom): bool +``` + +**Назначение:** Проверка разрешения на расчет рейтинга. + +**Логика:** Запрещает перерасчет рейтинга за прошлые месяцы после 5-го числа текущего месяца. + +```php +$dateEndSelectMonth = date("Y-m-t", strtotime($dateFrom)); +$dateCurrent = date("Y-m-d H:i:s", time()); +$dateFromBeginCurrentMonth = date("Y-m-01", time()); +$dateStopCalculateRating = date("Y-m-05 18:00:00", strtotime($dateCurrent)); + +$t1 = $dateEndSelectMonth < $dateFromBeginCurrentMonth; // Выбран прошлый месяц +$t2 = $dateCurrent > $dateStopCalculateRating; // Уже после 5-го числа + +if ($t1 && $t2) { + return false; // Запрещено +} +return true; +``` + +##### getClusterGameSumValue() +```php +public function getClusterGameSumValue($clusterAdmin, $yearSelect, $monthWithZeroSelect) +``` + +**Назначение:** Расчет суммарного рейтинга для кустового директора на основе показателей его магазинов. + +**Логика:** +1. Получение списка магазинов кластера из `store_arr` +2. Фильтрация по разрешенным для расчета магазинам +3. Получение всех администраторов магазинов +4. Суммирование их рейтингов +5. Расчет среднего значения + +```php +$storeIds = explode(',', $storeIdsString); + +$sumRow = AdminRating::find() + ->select(['summ' => new \yii\db\Expression("SUM(value)")]) + ->andWhere(['date' => $date]) + ->andWhere(['admin_id' => $adminAdministratorIdsValues]) + ->scalar(); + +$administratorsCount = count($storeIds); +$avgSumRow = round($sumRow / $administratorsCount, 0); +``` + +## 🎯 Actions (Действия) + +### 1. MakeAction + +**Файл:** `erp24/actions/rating/MakeAction.php` +**URL:** `/rating/make` +**Назначение:** Массовое создание/обновление рейтингов за месяц + +#### Алгоритм работы + +```mermaid +sequenceDiagram + participant U as User + participant MA as MakeAction + participant RS as RatingService + participant CS as CabinetService + participant DB as Database + + U->>MA: GET /rating/make?start={token} + MA->>MA: Проверка токена безопасности + MA->>MA: Проверка разрешения на расчет + MA->>DB: Получить список сотрудников + + loop Каждый сотрудник + MA->>MA: Проверить недавнее обновление + MA->>CS: getData() - собрать данные + CS-->>MA: adminSumGameBonusArray + MA->>RS: setRatingValue() - сохранить + RS->>DB: INSERT/UPDATE admin_rating + end + + loop Кустовые директора + MA->>RS: getClusterGameSumValue() + RS->>DB: SUM рейтингов администраторов + RS->>DB: INSERT/UPDATE rating_id=3 + end + + MA->>RS: calculateRating() - финальная сортировка + RS->>DB: UPDATE rating = 1,2,3... + + MA-->>U: "ok!" или список ошибок +``` + +#### Защита от перезаписи + +```php +// Пропускать недавно обновлённые (за последние 30 минут) +$dateCheckReset = date("Y-m-d H:i:s", time() - 1800); + +$ratingList = AdminRating::find() + ->andWhere(['date' => $yearSelect . '-' . $monthWithZeroSelect]) + ->andWhere(['>', 'date_time', $dateCheckReset]) + ->asArray() + ->all(); + +$ratingListAdminIds = ArrayHelper::getColumn($ratingList, 'admin_id'); + +// Пропустить этих сотрудников при расчете +$admins = Admin::getAdmins($ids, $groupIds, 'ASC', null, $ratingListAdminIds, ...); +``` + +#### Доступ + +Доступно только для групп: +- Директор (1) +- Кустовые директора (7) +- Руководитель HR (8) +- Главный бухгалтер (9) +- Операционный директор (51) + +### 2. ListAction + +**Файл:** `erp24/actions/rating/ListAction.php` +**URL:** `/rating/list` +**Назначение:** Отображение списка рейтингов за выбранный месяц + +```php +$adminRatingList = AdminRating::find() + ->andWhere(['date' => $yearSelect . '-' . $monthWithZeroSelect]) + ->with('admin') + ->orderBy([ + 'rating_id' => SORT_ASC, // Сначала Администраторы, потом Флористы + 'value' => SORT_DESC, // По убыванию баллов + 'avg_value' => SORT_DESC // По убыванию среднего + ]) + ->asArray() + ->all(); +``` + +### 3. TestAction + +**Файл:** `erp24/actions/rating/TestAction.php` +**Назначение:** Тестовая страница для отладки расчетов + +*(Не документирован детально - используется для debugging)* + +### 4. IndexAction (Quality Rating) + +**Файл:** `erp24/actions/rating2/IndexAction.php` +**URL:** `/rating2/index` +**Назначение:** Ручное управление рейтингом качества от КК + +#### AJAX действие: saveRating + +```php +if ($action == 'saveRating') { + // Проверка прав доступа + if (!Yii::$app->user->can("changeRatingByKik")) { + return 'fail'; + } + + $rating = floatval(Yii::$app->request->post('rating')); + $year = Yii::$app->request->post('year'); + $month = Yii::$app->request->post('month'); + $admin_id = Yii::$app->request->post('admin_id'); + + // Валидация диапазона + if ($rating >= 0 && $rating <= 100) { + $ratingEntity = QualityRating::find() + ->where(['year' => $year, 'month' => $month, 'admin_id' => $admin_id]) + ->one(); + + if (!$ratingEntity) { + $ratingEntity = new QualityRating; + $old_rating = 0; + } else { + $old_rating = $ratingEntity->rating; + } + + $ratingEntity->rating = $rating; + $ratingEntity->save(); + + // Запись в лог + $ratingLog = new QualityRatingLog; + $ratingLog->rating_id = $ratingEntity->id; + $ratingLog->created_by = Yii::$app->user->id; + $ratingLog->old_rating = $old_rating; + $ratingLog->new_rating = $rating; + $ratingLog->save(); + + return 'ok'; + } +} +``` + +## 📊 Модели / Records + +### 1. AdminRating + +**Файл:** `erp24/records/AdminRating.php` +**Таблица:** `admin_rating` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID записи | +| `admin_id` | int | ID сотрудника | +| `rating_id` | int | Тип рейтинга (1-4) | +| `rating` | int | Рейтинговая позиция (1, 2, 3...) | +| `administrators_count` | int | Кол-во администраторов (для кластера) | +| `date` | string | Дата в формате YYYY-MM | +| `year` | int | Год | +| `month` | int | Месяц | +| `value` | float | Набранные баллы | +| `count_shift` | int | Количество смен | +| `avg_value` | float | Средний балл за смену | +| `date_time` | string | Время обновления записи | + +**Связи:** +```php +public function getAdmin(): ActiveQueryInterface +{ + return $this->hasOne(Admin::class, ['id' => 'admin_id']); +} + +public function getStore(): ActiveQueryInterface +{ + return $this->hasOne(CityStore::class, ['id' => 'store_id'])->via('admin'); +} +``` + +**Пример данных:** +```php +[ + 'id' => 1234, + 'admin_id' => 42, + 'rating_id' => 1, // Администратор + 'rating' => 5, // 5-е место + 'date' => '2023-11', + 'value' => 12500, // 12,500 баллов + 'count_shift' => 20, // 20 смен + 'avg_value' => 625, // Средний балл 625 + 'date_time' => '2023-12-01 18:30:00' +] +``` + +### 2. QualityRating + +**Файл:** `erp24/records/QualityRating.php` +**Таблица:** `quality_rating` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID записи | +| `year` | int | Год | +| `month` | int | Месяц | +| `admin_id` | int | ID сотрудника | +| `rating` | float | Рейтинг качества (0-100) | + +**Метод для получения рейтинга:** +```php +public static function getQualityRating($adminId, $dateTo) +{ + $year = date("Y", strtotime($dateTo)); + $month = date("n", strtotime($dateTo)); + + return QualityRating::find() + ->select(['rating']) + ->andWhere([ + 'admin_id' => $adminId, + 'year' => $year, + 'month' => $month + ]) + ->orderBy(['id' => SORT_DESC]) + ->limit(1) + ->asArray() + ->scalar(); +} +``` + +**Использование:** Хранит оценку качества обслуживания, выставленную контролем качества вручную. + +### 3. QualityRatingLog + +**Файл:** `erp24/records/QualityRatingLog.php` +**Таблица:** `quality_rating_log` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID записи | +| `created_by` | int | ID пользователя, создавшего запись | +| `created_at` | string | Время создания | +| `rating_id` | int | ID из таблицы quality_rating | +| `old_rating` | float | Прежний рейтинг | +| `new_rating` | float | Новый рейтинг | + +**Связи:** +```php +public function getRating() { + return $this->hasOne(QualityRating::class, ['id' => 'rating_id']); +} +``` + +**Назначение:** Аудит всех изменений рейтинга качества. + +## 💼 Бизнес-логика + +### Типы рейтингов + +```php +const RATING_TYPES = [ + 1 => 'Администраторы', + 2 => 'Флористы', + 3 => 'Кустовые директора', + 4 => 'Стажеры' +]; +``` + +### Формула расчета рейтинга + +Общая формула для флориста/администратора: + +``` +Рейтинг = Σ (Баллы за смену × Количество смен) + +Где баллы за смену включают: +- Продажи за смену +- Конверсия +- Средний чек +- Процент бонусных карт +- Процент услуг +- Бонусы за качество +- Бонусы за низкий процент списания +``` + +### Средний балл за смену + +```php +$avg_value = $value / $count_shift; +``` + +Используется для сравнения: +- Флористов (могут работать разное количество смен) +- Оценки эффективности независимо от графика + +### Расчет для кустового директора + +``` +Рейтинг кластера = SUM(рейтинги всех администраторов магазинов кластера) / количество магазинов +``` + +### Временные ограничения + +**Правило:** Рейтинг за прошлые месяцы можно пересчитать только до 5-го числа следующего месяца (18:00). + +**Пример:** +- Рейтинг за ноябрь 2023: можно пересчитывать до 5 декабря 2023, 18:00 +- После этого срока - запрещено + +**Исключение:** Параметр `?force=1` обходит это ограничение. + +### Защита от перезаписи + +Записи, обновленные менее 30 минут назад, пропускаются при массовом пересчете: + +```php +$dateCheckReset = date("Y-m-d H:i:s", time() - 1800); // 30 минут +``` + +Это защищает от: +- Случайного двойного запуска +- Перезаписи вручную откорректированных данных +- Перегрузки системы + +## 📝 Примеры использования + +### Пример 1: Запуск расчета рейтинга за ноябрь 2023 + +``` +GET /rating/make?start={timestamp}&DaysSearchForm[dateFrom]=2023-11-01 +``` + +**Процесс:** +1. Проверка токена безопасности (timestamp должен быть < 1 часа) +2. Проверка прав доступа (группы 1,7,8,9,51) +3. Проверка разрешения на расчет (до 5 декабря 18:00) +4. Получение всех сотрудников со сменами в ноябре +5. Пропуск недавно обновленных (30 минут) +6. Расчет для каждого: + - Флористы → rating_id=2 + - Администраторы → rating_id=1 + - Стажеры → rating_id=4 +7. Расчет для кустовых → rating_id=3 +8. Финальная сортировка и присвоение позиций + +### Пример 2: Просмотр рейтинга + +``` +GET /rating/list?DaysSearchForm[dateFrom]=2023-11-01 +``` + +**Результат:** +``` +Администраторы (rating_id=1): +1. Иванов И.И. - 15,000 баллов (22 смены, средний 682) +2. Петров П.П. - 14,500 баллов (21 смена, средний 690) +... + +Флористы (rating_id=2): +1. Сидорова С.С. - 12,000 баллов (20 смен, средний 600) +2. Кузнецова К.К. - 11,500 баллов (19 смен, средний 605) +... +``` + +### Пример 3: Установка рейтинга качества + +```javascript +// AJAX запрос +$.post('/rating2/index', { + action: 'saveRating', + admin_id: 42, + year: 2023, + month: 11, + rating: 87.5 +}, function(response) { + if (response === 'ok') { + alert('Рейтинг сохранен!'); + } +}); +``` + +**Результат в БД:** + +*quality_rating:* +```php +[ + 'id' => 123, + 'admin_id' => 42, + 'year' => 2023, + 'month' => 11, + 'rating' => 87.5 +] +``` + +*quality_rating_log:* +```php +[ + 'id' => 456, + 'rating_id' => 123, + 'created_by' => 15, // ID КК + 'old_rating' => 85.0, + 'new_rating' => 87.5, + 'created_at' => '2023-11-15 14:30:00' +] +``` + +### Пример 4: Получение рейтинга сотрудника + +```php +use yii_app\records\QualityRating; + +$qualityRating = QualityRating::getQualityRating(42, '2023-11-15'); +// Вернет: 87.5 +``` + +## 🗄️ База данных + +### ER-диаграмма + +```mermaid +erDiagram + admin_rating ||--|| admin : "admin_id" + admin_rating ||--o| city_store : "via admin" + + quality_rating ||--|| admin : "admin_id" + quality_rating_log ||--|| quality_rating : "rating_id" + quality_rating_log ||--|| admin : "created_by" + + admin_rating { + int id PK + int admin_id FK + int rating_id "1-4" + int rating "Позиция" + string date "YYYY-MM" + int year + int month + float value "Баллы" + int count_shift + float avg_value + int administrators_count + datetime date_time + } + + quality_rating { + int id PK + int admin_id FK + int year + int month + float rating "0-100" + } + + quality_rating_log { + int id PK + int rating_id FK + int created_by FK + datetime created_at + float old_rating + float new_rating + } +``` + +### Индексы + +**admin_rating:** +```sql +KEY idx_admin_date (admin_id, date) +KEY idx_rating_date (rating_id, date) +KEY idx_date_time (date_time) +``` + +**quality_rating:** +```sql +UNIQUE KEY uniq_admin_year_month (admin_id, year, month) +``` + +## ❓ Часто задаваемые вопросы + +### 1. Почему рейтинг не обновляется? + +**Возможные причины:** +- Запись обновлялась менее 30 минут назад +- Пытаетесь пересчитать прошлый месяц после 5-го числа +- У сотрудника нет смен за период +- Отсутствует связь с 1С (GUID) +- Не указан оклад + +### 2. В чем разница между AdminRating и QualityRating? + +- **AdminRating** - автоматический расчет на основе показателей (продажи, бонусы, конверсия) +- **QualityRating** - ручная оценка качества работы от контроля качества (0-100) + +### 3. Как работает rating_id? + +``` +1 = Администраторы (group_id = 50) +2 = Флористы (остальные группы) +3 = Кустовые директора (group_id = 7, расчет по кластеру) +4 = Стажеры (group_id = 72) +``` + +### 4. Зачем нужен avg_value? + +**Пример:** +- Флорист А: 20 смен, 10,000 баллов → avg = 500 +- Флорист Б: 10 смен, 6,000 баллов → avg = 600 + +Флорист Б эффективнее, хотя общий балл ниже! + +### 5. Можно ли принудительно пересчитать старый рейтинг? + +Да, добавьте параметр `force=1`: +``` +/rating/make?start={token}&force=1&DaysSearchForm[dateFrom]=2023-09-01 +``` + +### 6. Как посмотреть историю изменений QualityRating? + +Все изменения логируются в `quality_rating_log`: +```php +$logs = QualityRatingLog::find() + ->with('rating') + ->orderBy(['created_at' => SORT_DESC]) + ->limit(100) + ->all(); +``` + +### 7. Почему рейтинг кустового директора считается отдельно? + +Кустовой директор отвечает за несколько магазинов, поэтому его рейтинг = среднее значение рейтингов всех администраторов его магазинов. + +## 🔗 Связи с другими модулями + +```mermaid +graph LR + Rating --> Bonus[Bonus Module] + Rating --> Timetable[Timetable Module] + Rating --> Payroll[Payroll Module] + Rating --> Cabinet[Cabinet Service] + Rating --> Sales[Sales Data 1C] + Rating --> WriteOffs[Write-offs Module] + + style Rating fill:#e1f5ff +``` + +**Зависимости:** +- **Timetable** - источник данных о сменах +- **Bonus** - расчет бонусов за качество, списания +- **Cabinet Service** - основной сервис для сбора метрик +- **1C Integration** - данные о продажах +- **Write-offs** - процент списания +- **EmployeePayment** - оклады для расчета нормы + +## 🎯 Метрики модуля + +| Метрика | Значение | +|---------|----------| +| **Контроллеры** | 2 | +| **Сервисы** | 1 | +| **Actions** | 4 | +| **Models/Records** | 3 | +| **Строк кода (сервис)** | 612 | +| **Типов рейтингов** | 4 | +| **Зависимостей** | 6+ модулей | + +## 📚 См. также + +- [Модуль Bonus](../bonus/README.md) - расчет бонусов, влияющих на рейтинг +- [Модуль Timetable](../timetable/README.md) - источник данных о сменах +- [Модуль Payroll](../payroll/README.md) - связь с зарплатой +- [CabinetService](../../services/CabinetService.php) - сбор метрик + +--- + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/modules/regulations/README.md b/erp24/docs/modules/regulations/README.md new file mode 100644 index 00000000..c3169428 --- /dev/null +++ b/erp24/docs/modules/regulations/README.md @@ -0,0 +1,518 @@ +# Модуль Regulations (Регламенты и обучающие материалы) + +## 📋 Описание + +**Regulations** - модуль для управления внутренними регламентами, инструкциями и обучающими материалами компании. Система позволяет создавать регламенты с проверочными тестами, назначать их сотрудникам и отслеживать прохождение. Поддерживает группировку по категориям и контроль знаний через опросники. + +### Основные возможности + +- 📚 Создание и редактирование регламентов +- 📝 Проверочные тесты (опросники) с несколькими вариантами ответов +- 👥 Назначение регламентов группам сотрудников +- ✅ Отслеживание прохождения тестов +- 📊 Статистика (пройден с ошибками / без ошибок) +- 🗂️ Группировка регламентов по категориям +- 📅 История создания и изменений +- 🔔 Интеграция с системой уведомлений + +## 🏗️ Архитектура модуля + +```mermaid +graph TB + subgraph "Controllers" + RC[RegulationsController
2 actions] + RPC[RegulationsPollController
3 actions] + CRUD[crud/RegulationsController
CRUD операции] + end + + subgraph "Actions" + IA[IndexAction
Список регламентов] + AA[AssignAction
Назначение сотрудникам] + PIA[PollIndexAction
Управление опросником] + PEA[PollEditAction
Редактор вопросов] + PEAA[PollEditAnswersAction
Редактор ответов] + end + + subgraph "Models/Records" + R[Regulations
Регламенты] + RG[RegulationGroup
Группы] + RP[RegulationsPassed
Прохождение] + RPO[RegulationsPoll
Вопросы] + RPA[RegulationsPollAnswers
Варианты ответов] + end + + RC --> IA + RC --> AA + RPC --> PIA + RPC --> PEA + RPC --> PEAA + + IA --> R + AA --> R + AA --> RP + PIA --> RPO + PEA --> RPO + PEAA --> RPA + + R --> RG + RP --> R + RPO --> R + RPA --> RPO + + style R fill:#fff4e1 + style RG fill:#fff4e1 +``` + +## 🎮 Контроллеры + +### 1. RegulationsController + +**Файл:** `erp24/controllers/RegulationsController.php` + +**Действия:** +- `index` - Список регламентов для сотрудника +- `assign` - Назначение регламента сотрудникам/группам + +### 2. RegulationsPollController + +**Файл:** `erp24/controllers/RegulationsPollController.php` + +**Действия:** +- `index` - Управление опросником +- `edit` - Редактирование вопроса +- `edit-answers` - Редактирование вариантов ответов + +### 3. crud/RegulationsController + +**Файл:** `erp24/controllers/crud/RegulationsController.php` + +Стандартный CRUD контроллер для создания, редактирования, просмотра и удаления регламентов (через Yii2 Gii CRUD). + +## 📊 Модели / Records + +### 1. Regulations (Основная модель) + +**Файл:** `erp24/records/Regulations.php` +**Таблица:** `regulations` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID регламента | +| `group_id` | int | ID группы регламентов (FK) | +| `name` | string | Название регламента | +| `content` | text | Содержание регламента (HTML) | +| `created_at` | datetime | Время создания | +| `created_by` | int | ID создателя (FK Admin) | +| `updated_at` | datetime | Время последнего изменения | + +**Примеры регламентов:** +- "Стандарты обслуживания клиентов" +- "Правила работы с кассой" +- "Техника безопасности при работе с инструментами" +- "Процедура приемки товара" +- "Инструкция по сборке букетов" + +### 2. RegulationGroup + +**Файл:** `erp24/records/RegulationGroup.php` +**Таблица:** `regulation_group` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID группы | +| `name` | string | Название группы | + +**Примеры групп:** +- "Обслуживание клиентов" +- "Работа с товаром" +- "Техника безопасности" +- "Администрирование" +- "Флористика" + +### 3. RegulationsPassed + +**Файл:** `erp24/records/RegulationsPassed.php` +**Таблица:** `regulations_passed` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `regulation_id` | int | ID регламента (FK) | +| `admin_id` | int | ID сотрудника (FK) | +| `status` | int | 0=с ошибками, 1=без ошибок | +| `created_at` | datetime | Время прохождения | + +**Уникальный ключ:** `(regulation_id, admin_id)` - сотрудник может пройти регламент только один раз. + +**Статусы:** +```php +0 - Тест пройден с ошибками +1 - Тест пройден без ошибок +``` + +### 4. RegulationsPoll + +**Файл:** `erp24/records/RegulationsPoll.php` +**Таблица:** `regulations_poll` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID вопроса | +| `regulation_id` | int | ID регламента (FK) | +| `name` | text | Текст вопроса | +| `type_option` | string | Тип вопроса | +| `posit` | int | Позиция (порядок) | +| `created_at` | datetime | Время создания | + +**Связи:** +```php +public function getAnswers() { + return $this->hasMany(RegulationsPollAnswers::class, ['poll_id' => 'id']); +} +``` + +**Примеры вопросов:** +- "Какова максимальная скидка, которую может предоставить флорист?" +- "Что делать если клиент недоволен букетом?" +- "Как часто необходимо проверять свежесть цветов?" + +### 5. RegulationsPollAnswers + +**Файл:** `erp24/records/RegulationsPollAnswers.php` +**Таблица:** `regulations_poll_answers` + +**Поля:** + +| Поле | Тип | Описание | +|------|-----|----------| +| `id` | int | ID варианта ответа | +| `poll_id` | int | ID вопроса (FK) | +| `name` | text | Текст варианта ответа | +| `is_correct` | int | 1=правильный, 0=неправильный | +| `posit` | int | Позиция (порядок) | + +**Пример опроса:** +``` +Вопрос: "Какова максимальная скидка для флориста?" +├─ Ответ 1: "5%" (is_correct=0) +├─ Ответ 2: "10%" (is_correct=1) ✓ +├─ Ответ 3: "15%" (is_correct=0) +└─ Ответ 4: "20%" (is_correct=0) +``` + +## 💼 Бизнес-логика + +### Процесс прохождения регламента + +```mermaid +stateDiagram-v2 + [*] --> Created: Создан регламент + Created --> Published: Опубликован + Published --> Assigned: Назначен сотруднику + Assigned --> InProgress: Сотрудник начал изучение + InProgress --> Testing: Переход к тесту + Testing --> PassedWithErrors: Ответы с ошибками + Testing --> PassedSuccess: Все ответы верны + PassedWithErrors --> [*] + PassedSuccess --> [*] +``` + +### Назначение регламента + +**Способы назначения:** +1. **Индивидуально** - конкретному сотруднику +2. **По группе** - всем сотрудникам группы (например, всем флористам) +3. **Всем** - всем сотрудникам компании + +### Система тестирования + +**Механизм:** +1. Сотрудник читает регламент +2. Нажимает "Пройти тест" +3. Отвечает на вопросы (один правильный ответ из нескольких) +4. Система проверяет ответы +5. Результат сохраняется в `regulations_passed` + +**Критерии оценки:** +```php +status = 1 // Все ответы верны +status = 0 // Есть хотя бы одна ошибка +``` + +**Повторное прохождение:** +- Ограничение `UNIQUE (regulation_id, admin_id)` +- Сотрудник может пройти тест только один раз +- Для повторного прохождения нужно удалить запись из `regulations_passed` + +## 📝 Примеры использования + +### Пример 1: Создание регламента + +```php +use yii_app\records\Regulations; + +$regulation = new Regulations(); +$regulation->group_id = 1; // "Обслуживание клиентов" +$regulation->name = 'Стандарты телефонного общения'; +$regulation->content = '

Приветствие

При ответе на звонок необходимо...

'; +$regulation->created_by = Yii::$app->user->id; +$regulation->created_at = date('Y-m-d H:i:s'); +$regulation->updated_at = date('Y-m-d H:i:s'); +$regulation->save(); +``` + +### Пример 2: Создание опросника + +```php +use yii_app\records\RegulationsPoll; +use yii_app\records\RegulationsPollAnswers; + +// Вопрос 1 +$poll1 = new RegulationsPoll(); +$poll1->regulation_id = $regulation->id; +$poll1->name = 'Как нужно отвечать на звонок?'; +$poll1->type_option = 'radio'; +$poll1->posit = 1; +$poll1->created_at = date('Y-m-d H:i:s'); +$poll1->save(); + +// Варианты ответов +$answer1 = new RegulationsPollAnswers(); +$answer1->poll_id = $poll1->id; +$answer1->name = 'Алло'; +$answer1->is_correct = 0; +$answer1->posit = 1; +$answer1->save(); + +$answer2 = new RegulationsPollAnswers(); +$answer2->poll_id = $poll1->id; +$answer2->name = 'Цветочная лавка, Иван, слушаю вас'; +$answer2->is_correct = 1; // Правильный ответ +$answer2->posit = 2; +$answer2->save(); + +$answer3 = new RegulationsPollAnswers(); +$answer3->poll_id = $poll1->id; +$answer3->name = 'Да'; +$answer3->is_correct = 0; +$answer3->posit = 3; +$answer3->save(); +``` + +### Пример 3: Назначение регламента группе + +```php +use yii_app\records\Admin; + +// Найти всех флористов +$florists = Admin::find()->where(['group_id' => 30])->all(); + +// Создать уведомление о новом регламенте +$notification = new Notification(); +$notification->name = 'Новый регламент: Стандарты телефонного общения'; +$notification->content = 'Ознакомьтесь с новым регламентом и пройдите тест'; +$notification->recipients = [1000030]; // Группа флористов +$notification->upload(); +``` + +### Пример 4: Сохранение результата теста + +```php +use yii_app\records\RegulationsPassed; + +$passed = new RegulationsPassed(); +$passed->regulation_id = 5; +$passed->admin_id = Yii::$app->user->id; + +// Проверка ответов пользователя +$correctAnswers = 0; +$totalQuestions = RegulationsPoll::find()->where(['regulation_id' => 5])->count(); + +foreach ($userAnswers as $pollId => $answerId) { + $answer = RegulationsPollAnswers::findOne($answerId); + if ($answer->is_correct == 1) { + $correctAnswers++; + } +} + +// Результат +$passed->status = ($correctAnswers == $totalQuestions) ? 1 : 0; +$passed->created_at = date('Y-m-d H:i:s'); +$passed->save(); + +if ($passed->status == 1) { + echo "Поздравляем! Вы прошли тест без ошибок!"; +} else { + echo "Тест пройден с ошибками. Правильных ответов: $correctAnswers из $totalQuestions"; +} +``` + +### Пример 5: Статистика прохождения + +```php +// Сколько сотрудников прошли регламент +$totalPassed = RegulationsPassed::find() + ->where(['regulation_id' => 5]) + ->count(); + +// Сколько прошли без ошибок +$passedSuccess = RegulationsPassed::find() + ->where(['regulation_id' => 5, 'status' => 1]) + ->count(); + +// Процент успешных +$successRate = ($passedSuccess / $totalPassed) * 100; + +echo "Регламент прошли: $totalPassed человек\n"; +echo "Без ошибок: $passedSuccess человек\n"; +echo "Процент успешных: $successRate%\n"; +``` + +## 🗄️ База данных + +### ER-диаграмма + +```mermaid +erDiagram + regulations ||--|| regulation_group : "group_id" + regulations ||--|| admin : "created_by" + regulations ||--o{ regulations_passed : "regulation_id" + regulations ||--o{ regulations_poll : "regulation_id" + + regulations_passed ||--|| admin : "admin_id" + + regulations_poll ||--o{ regulations_poll_answers : "poll_id" + + regulations { + int id PK + int group_id FK + string name + text content + datetime created_at + int created_by FK + datetime updated_at + } + + regulation_group { + int id PK + string name + } + + regulations_passed { + int regulation_id FK + int admin_id FK + int status "0,1" + datetime created_at + } + + regulations_poll { + int id PK + int regulation_id FK + text name + string type_option + int posit + datetime created_at + } + + regulations_poll_answers { + int id PK + int poll_id FK + text name + int is_correct "0,1" + int posit + } +``` + +### Индексы + +```sql +-- regulations +CREATE INDEX idx_group_id ON regulations(group_id); +CREATE INDEX idx_created_by ON regulations(created_by); +CREATE INDEX idx_created_at ON regulations(created_at); + +-- regulations_passed +CREATE UNIQUE INDEX uniq_regulation_admin ON regulations_passed(regulation_id, admin_id); +CREATE INDEX idx_admin_id ON regulations_passed(admin_id); +CREATE INDEX idx_status ON regulations_passed(status); + +-- regulations_poll +CREATE INDEX idx_regulation_id ON regulations_poll(regulation_id); +CREATE INDEX idx_posit ON regulations_poll(posit); + +-- regulations_poll_answers +CREATE INDEX idx_poll_id ON regulations_poll_answers(poll_id); +CREATE INDEX idx_is_correct ON regulations_poll_answers(is_correct); +``` + +## ❓ Часто задаваемые вопросы + +### 1. Можно ли пройти тест повторно? + +Нет, ограничение `UNIQUE (regulation_id, admin_id)` позволяет пройти тест только один раз. Для повторного прохождения администратор должен удалить запись. + +### 2. Что происходит при ошибке в тесте? + +Если хотя бы на один вопрос дан неверный ответ, `status = 0` (пройден с ошибками). Тест считается непройденным. + +### 3. Сколько вариантов ответов может быть? + +Любое количество, но обычно 3-4 варианта. Только один может быть правильным (`is_correct = 1`). + +### 4. Можно ли редактировать регламент после публикации? + +Да, поле `updated_at` отслеживает изменения. Но если сотрудники уже прошли тест, их результаты не пересчитываются. + +### 5. Как узнать, кто не прошел регламент? + +```php +$assigned = /* список назначенных сотрудников */; +$passed = RegulationsPassed::find() + ->where(['regulation_id' => 5]) + ->select('admin_id') + ->column(); + +$notPassed = array_diff($assigned, $passed); +``` + +## 🔗 Связи с другими модулями + +```mermaid +graph LR + Regulations --> Notifications[Notifications Module] + Regulations --> Lesson[Lesson Module] + Regulations --> Admin[Admin/Users] + + style Regulations fill:#e1f5ff +``` + +**Интеграции:** +- **Notifications** - уведомления о новых регламентах +- **Lesson** - похожая система обучения +- **Admin** - назначение и отслеживание прохождения + +## 🎯 Метрики модуля + +| Метрика | Значение | +|---------|----------| +| **Контроллеры** | 3 | +| **Actions** | 5 | +| **Models/Records** | 5 | +| **Статусов прохождения** | 2 (0/1) | + +## 📚 См. также + +- [Модуль Notifications](../notifications/README.md) - уведомления о регламентах +- [Модуль Lesson](../lesson/README.md) - система обучения +- [CRUD Controllers](../../controllers/crud/README.md) - стандартные операции + +--- + +**Последнее обновление:** 2025-11-17 diff --git a/erp24/docs/modules/shipment/README.md b/erp24/docs/modules/shipment/README.md new file mode 100644 index 00000000..4ec47f12 --- /dev/null +++ b/erp24/docs/modules/shipment/README.md @@ -0,0 +1,737 @@ +# Модуль Shipment (Отгрузка и закупки) + +## Описание + +Модуль Shipment отвечает за управление заказами магазинов, планирование и учет отгрузок товаров от поставщиков. Включает управление поставщиками, создание заказов, статусы отгрузок, планирование полнограммы, деление товаров между магазинами и учет фактических поставок. + +## Архитектура модуля + +```mermaid +graph TB + subgraph "Контроллеры" + SC[ShipmentController] + SPC[ShipmentProvidersController] + end + + subgraph "Сервисы" + SS[ShipmentService
3786 строк кода] + end + + subgraph "Actions" + AddAction[AddAction] + end + + subgraph "Модели/Records" + SP[ShipmentProviders] + SO[StoreOrders] + SOF[StoreOrdersFields] + SOFD[StoreOrdersFieldsData] + SOFP[StoreOrdersFieldsProperty] + SOS[StoreOrdersStatuses] + SOStatus[StoreOrderStatus] + end + + subgraph "Связанные модели" + P1C[Products1c] + P1CN[Products1cNomenclature] + P1CO[Products1cOptions] + end + + subgraph "База данных" + DB[(shipment_providers
store_orders
store_orders_fields
store_orders_statuses)] + end + + SC --> AddAction + SC -.20+ views.-> Views[20+ представлений] + + AddAction --> SS + SS --> SP + SS --> SO + SS --> SOF + SS --> SOFD + + SP --> DB + SO --> DB + SOF --> DB + + SS --> P1C + SS --> P1CO + + style SC fill:#e1f5ff + style SS fill:#fff4e1 + style SP fill:#e8f5e9 + style DB fill:#fce4ec +``` + +## Компоненты модуля + +### Контроллеры (2) + +#### 1. **ShipmentController** +`erp24/controllers/ShipmentController.php` + +Основной контроллер для работы с отгрузками и закупками. + +**Экшены:** + +##### Основные страницы: +- `index` - Главная страница модуля отгрузок +- `info` - Информация об отгрузках +- `shipment` - Просмотр отгрузок + +##### Планирование и заказы: +- `store-plan` - Планирование закупок по магазинам +- `store-orders` - Заказы магазинов +- `store-order-score` - Оценка заказа магазина +- `add` (Action) - Добавление заказа + +##### Полнограмма: +- `polnogramm` - Просмотр полнограммы (план ассортимента) +- `polnogramm-edit` - Редактирование полнограммы + +##### Деление товаров: +- `division-store` - Деление товаров между магазинами +- `division-store-print` - Печатная форма деления товаров +- `ajax-division-auto-hand-start` - AJAX запуск автоматического/ручного деления + +##### Фактические поставки: +- `store-products-fact` - Фактические поставки по магазинам +- `store-products-fact-edit` - Редактирование фактических поставок (AJAX) + +##### Настройки и конфигурация: +- `config` - Конфигурация модуля +- `config-sort` - Сортировка конфигурации +- `fields` - Управление полями заказов +- `statuses-edit` - Редактирование статусов +- `status-fields-sort` - Сортировка полей статусов + +##### AJAX операции: +- `ajax-field` - AJAX работа с полями +- `ajax-update-store-zakup` - AJAX обновление закупки магазина +- `ajax-update-step` - AJAX обновление шага + +**Итого:** 1 Action + ~20 представлений + +#### 2. **ShipmentProvidersController** +`erp24/controllers/ShipmentProvidersController.php` + +Контроллер для управления поставщиками. + +**Функционал:** +- CRUD операции с поставщиками +- Просмотр списка поставщиков +- Управление валютами и контрагентами + +--- + +### Сервисы (1) + +#### **ShipmentService** +`erp24/services/ShipmentService.php` + +Один из самых больших сервисов в системе (**3786 строк кода**). Содержит всю бизнес-логику модуля отгрузок. + +**Свойства:** + +##### Данные заказа: +- `$orderId` (int) - ID заказа +- `$status_order_id` (int) - ID статуса заказа +- `$groupId` (int) - ID группы пользователя +- `$adminId` (int) - ID администратора + +##### Фильтры и данные: +- `$whereInProductsId` (string) - WHERE условие для ID товаров +- `$whereProvidersId` (string) - WHERE условие для ID поставщиков +- `$whereInProductsIdArray` (array) - Массив ID товаров + +##### Поля и свойства: +- `$fieldsRows` (array) - Строки полей +- `$fieldsPropertyArray` (array) - Массив свойств полей +- `$FiledsData` (array) - Данные полей +- `$FiledsDataArray` (array) - Массив данных полей +- `$FiledsDataSumm` (array) - Суммы по полям +- `$FiledsDataSummStats` (array) - Статистика сумм + +##### Права доступа: +- `$dostupFields` (array) - Доступные поля +- `$bgFields` (array) - Поля с цветовой маркировкой +- `$dostup_fields` (array) - Поля доступа +- `$bg_fields` (array) - Поля цветов + +##### Другие данные: +- `$rowArraySum` (array) - Массив сумм строк +- `$DataFieldStats` (array) - Статистика данных полей +- `$productsColorsArray` (array) - Массив цветов товаров +- `$shipmentSession` (object) - Объект сессии +- `$shipmentRequest` (object) - Объект запроса + +**Ключевые методы:** + +##### Инициализация +```php +public function __construct($config = []) +``` +- Инициализация сессии и запроса +- Установка ID заказа + +##### Основная функция обработки полей +```php +public function functionsFiedlsData() +``` + +**Функционал:** +1. Получение группы и прав доступа пользователя +2. Загрузка справочников (магазины, поставщики, группы) +3. Получение статусов заказов с правами доступа +4. Формирование массивов доступных полей +5. Применение цветовой маркировки + +**Группы с доступом:** +- 7 - (требует уточнения) +- 30 - Флористы +- 17 - (требует уточнения) +- 70 - (требует уточнения) +- 71 - (требует уточнения) +- 9 - (требует уточнения) +- 51 - (требует уточнения) +- 1 - Администраторы +- 10 - (требует уточнения) + +**Цветовая маркировка:** +- `bg-white` - Белый +- `bg-info` - Синий +- `bg-indigo` - Индиго +- `bg-success` - Зеленый +- `bg-danger` - Красный +- `bg-warning` - Оранжевый +- `bg-lime` - Лайм + +**Примечание:** Из-за размера файла (3786 строк) полная документация всех методов требует дополнительного анализа. Сервис содержит методы для: +- Создания и редактирования заказов +- Расчета деления товаров между магазинами +- Работы с полнограммой +- Обработки статусов +- Генерации отчетов +- Работы с поставщиками и товарами + +**Зависимости:** +- `AdminGroup` +- `Products1c` +- `Products1cOptions` +- `ShipmentProviders` +- `StoreOrders` +- `StoreOrdersFields` +- `StoreOrdersFieldsData` +- `StoreOrdersFieldsProperty` +- `StoreOrdersStatuses` +- `StoreOrderStatus` + +--- + +### Actions (1) + +#### **AddAction** +`erp24/actions/shipment/AddAction.php` + +Добавление нового заказа на отгрузку. + +**Функционал:** +- Форма создания заказа +- Валидация данных +- Сохранение заказа через ShipmentService + +--- + +### Модели/Records (7+) + +#### 1. **ShipmentProviders** +`erp24/records/ShipmentProviders.php` + +Поставщики товаров. + +**Таблица:** `shipment_providers` + +**Поля:** +- `id` (int) - ID поставщика +- `name` (string) - Название поставщика +- `guid` (string, 36) - GUID поставщика +- `valuta` (string, 6) - Валюта (RUB, USD, EUR и т.д.) +- `contragent_id` (string, 36) - GUID контрагента из 1С + +**Методы:** +```php +public static function getNames($orderBy = null): array +``` +- Получение списка поставщиков +- Поддержка сортировки: `orderByNameASC`, `orderByNameDESC` + +**Пример:** +```php +$providers = ShipmentProviders::getNames('orderByNameASC'); +// ['1' => 'ООО "Поставщик 1"', '2' => 'ИП Иванов', ...] +``` + +#### 2. **StoreOrders** +`erp24/records/StoreOrders.php` + +Заказы магазинов. + +**Таблица:** `store_orders` + +**Поля:** +- `id` (int) - ID заказа +- `store_id` (string) - GUID магазина +- `provider_id` (int) - ID поставщика +- `status_id` (int) - ID статуса заказа +- `order_date` (datetime) - Дата заказа +- `delivery_date` (date) - Планируемая дата доставки +- `created_by` (int) - Кто создал (admin_id) +- `created_at` (datetime) - Дата создания +- `updated_at` (datetime) - Дата обновления + +#### 3. **StoreOrdersFields** +`erp24/records/StoreOrdersFields.php` + +Поля заказов (кастомные колонки). + +**Таблица:** `store_orders_fields` + +**Поля:** +- `id` (int) - ID поля +- `name` (string) - Название поля +- `code` (string) - Код поля +- `type` (string) - Тип данных (text, number, date, select) +- `is_required` (tinyint) - Обязательное ли поле +- `sort_order` (int) - Порядок сортировки +- `is_active` (tinyint) - Активность + +**Типы полей:** +- `text` - Текстовое поле +- `number` - Числовое поле +- `date` - Дата +- `select` - Выпадающий список +- (возможны другие типы) + +#### 4. **StoreOrdersFieldsData** +`erp24/records/StoreOrdersFieldsData.php` + +Данные кастомных полей заказов. + +**Таблица:** `store_orders_fields_data` + +**Поля:** +- `id` (int) - ID записи +- `order_id` (int) - ID заказа +- `field_id` (int) - ID поля +- `product_id` (string) - GUID товара +- `value` (text) - Значение +- `created_at` (datetime) - Дата создания + +**Связи:** +``` +StoreOrdersFieldsData → StoreOrders (order_id) +StoreOrdersFieldsData → StoreOrdersFields (field_id) +StoreOrdersFieldsData → Products1c (product_id) +``` + +#### 5. **StoreOrdersFieldsProperty** +`erp24/records/StoreOrdersFieldsProperty.php` + +Свойства полей заказов. + +**Таблица:** `store_orders_fields_property` + +**Поля:** +- `id` (int) - ID свойства +- `field_id` (int) - ID поля +- `property_name` (string) - Название свойства +- `property_value` (text) - Значение свойства + +**Примеры свойств:** +- Варианты для select-полей +- Форматы валидации +- Дополнительные настройки отображения + +#### 6. **StoreOrdersStatuses** +`erp24/records/StoreOrdersStatuses.php` + +Статусы заказов. + +**Таблица:** `store_orders_statuses` + +**Поля:** +- `id` (int) - ID статуса +- `name` (string) - Название статуса +- `description` (text) - Описание +- `groups` (string) - Группы с доступом (через запятую) +- `stores_show` (text) - Магазины для отображения +- `dostup` (json) - JSON с правами доступа к полям +- `status_edit_dostup` (json) - JSON с правами редактирования статуса + +**Структура JSON прав доступа:** +```json +{ + "field_code_1": "edit", // Редактирование + "field_code_2": "show", // Просмотр + "field_code_3": "" // Нет доступа +} +``` + +**Примеры статусов:** +- Новый +- В обработке +- Подтвержден +- Отгружен +- Доставлен +- Отменен + +#### 7. **StoreOrderStatus** +`erp24/records/StoreOrderStatus.php` + +История изменения статусов заказов. + +**Таблица:** `store_order_status` (возможно) + +**Функционал:** +- Отслеживание изменений статуса +- Хранение истории переходов + +--- + +## Бизнес-логика + +### Процесс работы с заказами + +```mermaid +sequenceDiagram + participant User as Пользователь + participant SC as ShipmentController + participant SS as ShipmentService + participant SO as StoreOrders + participant P1C as Products1c + participant DB as База данных + + User->>SC: Создать заказ + SC->>SS: Инициализация + SS->>SS: Проверка прав доступа + SS->>P1C: Получить список товаров + P1C-->>SS: Товары с ценами + + User->>SC: Заполнить форму заказа + SC->>SS: Валидация данных + SS->>SO: Создать заказ + SO->>DB: Сохранить StoreOrders + + loop Для каждого поля + SS->>DB: Сохранить StoreOrdersFieldsData + end + + SS-->>SC: Заказ создан + SC-->>User: Подтверждение +``` + +### Деление товаров между магазинами + +Модуль поддерживает два режима деления: + +#### 1. Автоматическое деление +- На основе планов продаж магазинов +- Пропорционально объемам продаж +- Учет остатков на складах + +#### 2. Ручное деление +- Ручной ввод количества для каждого магазина +- Контроль общего количества +- Предупреждения о недостаче/переборе + +### Полнограмма + +**Полнограмма** - это план ассортимента товаров для магазинов. + +**Функционал:** +- Определение необходимого ассортимента +- Планирование количества позиций +- Контроль выполнения полнограммы +- Редактирование плана + +### Статусы заказов и права доступа + +Каждый статус заказа имеет: +1. **Список групп с доступом** - кто может видеть заказ в этом статусе +2. **Права на поля** - какие поля доступны для просмотра/редактирования +3. **Права на смену статуса** - кто может изменить статус + +**Пример:** + +| Статус | Группы | Поля (просмотр) | Поля (редактирование) | Смена статуса | +|--------|--------|-----------------|----------------------|---------------| +| Новый | 1, 9, 51 | Все | Все | 1, 9 | +| В обработке | 1, 7, 9, 51 | Все | Количество, Дата | 9, 51 | +| Отгружен | Все | Все | - | - | + +### Цветовая маркировка + +Поля и строки заказов могут иметь цветовую маркировку: +- **Белый** - обычные данные +- **Синий** - информационные поля +- **Зеленый** - успешные операции +- **Красный** - проблемы, недостача +- **Оранжевый** - требует внимания +- **Лайм** - специальные маркеры + +--- + +## API и интеграции + +### Внутренние зависимости + +Модуль взаимодействует с: +- **Products1c** - товары и номенклатура из 1С +- **CityStore** - данные о магазинах +- **Admin/AdminGroup** - пользователи и права доступа +- **Sales** - данные о продажах для планирования +- (возможно другие модули) + +### Внешние интеграции + +- **1С** - синхронизация товаров, поставщиков, контрагентов +- **Поставщики** - интеграция с системами поставщиков (возможно) + +--- + +## Маршруты (Routes) + +``` +/shipment/index - Главная страница +/shipment/info - Информация об отгрузках +/shipment/shipment - Просмотр отгрузок + +/shipment/store-plan - Планирование закупок +/shipment/store-orders - Заказы магазинов +/shipment/store-order-score - Оценка заказа +/shipment/add - Добавление заказа + +/shipment/polnogramm - Полнограмма +/shipment/polnogramm-edit - Редактирование полнограммы + +/shipment/division-store - Деление товаров +/shipment/division-store-print - Печать деления +/shipment/ajax-division-auto-hand-start - Запуск деления (AJAX) + +/shipment/store-products-fact - Фактические поставки +/shipment/store-products-fact-edit - Редактирование поставок (AJAX) + +/shipment/config - Конфигурация +/shipment/config-sort - Сортировка конфигурации +/shipment/fields - Управление полями +/shipment/statuses-edit - Редактирование статусов +/shipment/status-fields-sort - Сортировка полей статусов + +/shipment/ajax-field - Работа с полями (AJAX) +/shipment/ajax-update-store-zakup - Обновление закупки (AJAX) +/shipment/ajax-update-step - Обновление шага (AJAX) + +/shipment-providers/* - Управление поставщиками +``` + +--- + +## Примеры использования + +### Пример 1: Получение списка поставщиков + +```php +// Получить всех поставщиков +$providers = ShipmentProviders::getNames(); + +// Получить поставщиков с сортировкой по имени +$providersAsc = ShipmentProviders::getNames('orderByNameASC'); + +// Использование в форме +echo Html::dropDownList('provider_id', null, $providers, [ + 'prompt' => 'Выберите поставщика', + 'class' => 'form-control', +]); +``` + +### Пример 2: Создание заказа + +```php +$order = new StoreOrders(); +$order->store_id = 'abc-123-def'; // GUID магазина +$order->provider_id = 5; +$order->status_id = 1; // Новый +$order->order_date = date('Y-m-d H:i:s'); +$order->delivery_date = date('Y-m-d', strtotime('+7 days')); +$order->created_by = Yii::$app->user->id; +$order->save(); + +// Добавление данных по товарам +foreach ($products as $productId => $quantity) { + $fieldData = new StoreOrdersFieldsData(); + $fieldData->order_id = $order->id; + $fieldData->field_id = 1; // ID поля "Количество" + $fieldData->product_id = $productId; + $fieldData->value = $quantity; + $fieldData->save(); +} +``` + +### Пример 3: Проверка прав доступа к полю + +```php +$service = new ShipmentService([ + 'session' => Yii::$app->session, + 'request' => Yii::$app->request, + 'orderId' => 123, +]); + +$service->functionsFiedlsData(); + +// Проверить доступ к полю +$fieldCode = 'quantity'; +$access = $service->dostupFields[$fieldCode] ?? null; + +if ($access === 'edit') { + // Можно редактировать +} elseif ($access === 'show') { + // Только просмотр +} else { + // Нет доступа +} +``` + +### Пример 4: Получение заказов по статусу + +```php +$statusId = 2; // В обработке + +$orders = StoreOrders::find() + ->where(['status_id' => $statusId]) + ->orderBy(['order_date' => SORT_DESC]) + ->all(); + +foreach ($orders as $order) { + echo "Заказ #{$order->id}: "; + echo "Магазин {$order->store_id}, "; + echo "Поставщик {$order->provider_id}, "; + echo "Дата доставки {$order->delivery_date}\n"; +} +``` + +--- + +## База данных + +### Основные таблицы + +1. **shipment_providers** - Поставщики +2. **store_orders** - Заказы магазинов +3. **store_orders_fields** - Поля заказов +4. **store_orders_fields_data** - Данные полей заказов +5. **store_orders_fields_property** - Свойства полей +6. **store_orders_statuses** - Статусы заказов +7. **store_order_status** - История статусов (возможно) + +### ER-диаграмма + +```mermaid +erDiagram + store_orders ||--o{ store_orders_fields_data : "order_id" + store_orders }o--|| shipment_providers : "provider_id" + store_orders }o--|| store_orders_statuses : "status_id" + store_orders }o--|| products_1c : "store_id" + store_orders }o--|| admin : "created_by" + + store_orders_fields ||--o{ store_orders_fields_data : "field_id" + store_orders_fields ||--o{ store_orders_fields_property : "field_id" + + store_orders_fields_data }o--|| products_1c : "product_id" + + shipment_providers { + int id PK + string name + string guid UK + string valuta + string contragent_id + } + + store_orders { + int id PK + string store_id FK + int provider_id FK + int status_id FK + datetime order_date + date delivery_date + int created_by FK + datetime created_at + } + + store_orders_fields { + int id PK + string name + string code + string type + tinyint is_required + int sort_order + tinyint is_active + } + + store_orders_fields_data { + int id PK + int order_id FK + int field_id FK + string product_id FK + text value + datetime created_at + } + + store_orders_statuses { + int id PK + string name + text description + string groups + text stores_show + json dostup + json status_edit_dostup + } +``` + +--- + +## Метрики и аналитика + +### Ключевые метрики + +1. **Количество заказов по статусам** +2. **Общая сумма заказов за период** +3. **Количество заказов по поставщикам** +4. **Средний срок обработки заказа** +5. **Процент выполнения полнограммы** +6. **Точность деления товаров** +7. **Отклонение факта от плана** + +--- + +## Вопросы для уточнения + +1. ❓ Какие группы соответствуют ID 7, 17, 70, 71, 9, 51, 10 в ShipmentService? +2. ❓ Как работает автоматическое деление товаров (алгоритм)? +3. ❓ Какие именно поля используются в заказах (список кастомных полей)? +4. ❓ Есть ли интеграция с внешними API поставщиков? +5. ❓ Как обрабатываются возвраты и брак в поставках? +6. ❓ Полная документация всех методов ShipmentService (3786 строк)? + +--- + +## Связанные модули + +- [Dashboard (Информационные панели)](../dashboard/README.md) - Визуализация данных по отгрузкам +- [Write-offs (Списания)](../write-offs/README.md) - Учет брака при поставках +- (Модуль Products/Catalog) - Управление товарами + +--- + +*Документация создана автоматически. Последнее обновление: 2025-11-17* + +**Примечание:** Модуль Shipment является одним из самых сложных в системе (ShipmentService содержит 3786 строк кода). Для полной документации всех методов требуется дополнительный детальный анализ кодовой базы. diff --git a/erp24/docs/modules/timetable/README.md b/erp24/docs/modules/timetable/README.md new file mode 100644 index 00000000..d632687d --- /dev/null +++ b/erp24/docs/modules/timetable/README.md @@ -0,0 +1,1098 @@ +# Модуль Timetable (Расписание и табель учета рабочего времени) + +## Описание + +Модуль Timetable отвечает за управление расписанием работы сотрудников, планирование смен, учет фактически отработанного времени, контроль явок (чекинов), начало и окончание смен. Это один из ключевых модулей системы, данные которого используются в расчете зарплаты, бонусов и отчетности. + +## Архитектура модуля + +```mermaid +graph TB + subgraph "Контроллеры" + TC[TimetableController] + TFC[TimetableFactController] + end + + subgraph "Сервисы" + TS[TimetableService] + end + + subgraph "Actions (17 экшенов)" + PlanAction[PlanAction - План] + EditPlanAction[EditPlanAction - Редактирование плана] + FactAction[FactAction - Факт] + EditFactAction[EditFactAction - Редактирование факта] + StartAction[StartAction - Начало смены] + CheckinsAction[CheckinsAction - Явки] + HolidaysAction[HolidaysAction - Праздники] + end + + subgraph "Модели/Records (9)" + T[Timetable
Базовая модель] + TP[TimetablePlan
План] + TF[TimetableFact
Факт] + TV3[TimetableV3] + TPV3[TimetablePlanV3] + TFV3[TimetableFactV3] + TS_M[TimetableShift] + TW[TimetableWorkbot] + TFM[TimetableFactModel] + end + + subgraph "Связанные модели" + AC[AdminCheckin
Явки сотрудников] + Shift[Shift
Смены] + Admin[Admin
Сотрудники] + CityStore[CityStore
Магазины] + end + + subgraph "База данных" + DB[(timetable
admin_checkin
shift)] + end + + TC --> PlanAction + TC --> FactAction + TC --> StartAction + TC --> CheckinsAction + + PlanAction --> TS + FactAction --> TS + + TS --> TP + TS --> TF + + TP -.наследует.-> T + TF -.наследует.-> T + + T --> DB + TP --> DB + TF --> DB + AC --> DB + + TF --> TP + TP --> AC + + style TC fill:#e1f5ff + style TS fill:#fff4e1 + style T fill:#e8f5e9 + style DB fill:#fce4ec +``` + +## Компоненты модуля + +### Контроллеры (2) + +#### 1. **TimetableController** +`erp24/controllers/TimetableController.php` + +Основной контроллер для работы с расписанием и табелем. + +**Экшены:** + +##### Планирование (График): +- `plan` - Просмотр и создание плана работы (графика) +- `edit_plan` - Редактирование планируемой смены +- `add` - Добавление новой записи в график + +##### Факт (Табель): +- `fact` - Просмотр фактически отработанного времени (табель) +- `fact_overview` - Обзор фактических смен +- `fact_overview_item` - Детальный просмотр конкретной смены +- `edit_fact` - Редактирование фактической смены +- `add-fact-hand` - Ручное добавление фактической смены + +##### Начало смены: +- `start` - Главная страница начала смены +- `start-shift-step-one` - Шаг 1: Выбор сотрудника +- `start-shift-step-two` - Шаг 2: Выбор магазина и смены +- `start-shift-step-three` - Шаг 3: Подтверждение и начало смены + +##### Явки (чекины): +- `checkins` - Просмотр явок сотрудников +- `join-missing-shifts-with-checkins` - Привязка явок к сменам + +##### Другое: +- `admin_stores` - Магазины сотрудника +- `holidays` - Управление праздниками и выходными +- `admin-info` - Информация о сотруднике (AJAX) +- `tabel` - Редирект на plan (алиас) +- `link-plans` - Привязка планов к фактам + +**Действие по умолчанию:** `index` (временно отключен) + +**Примечание:** Экшен `index` убран из доступа по согласованию с Галиной, так как со стороны меню к нему нет доступа. + +#### 2. **TimetableFactController** +`erp24/controllers/TimetableFactController.php` + +Специализированный контроллер для работы с фактическим временем. + +--- + +### Сервисы (1) + +#### **TimetableService** +`erp24/services/TimetableService.php` + +Сервис для работы с расписанием и табелем. + +**Ключевые методы:** + +##### Получение данных расписания +```php +public static function getTimetable($date1, $date2_smen): array +``` + +Получает данные расписания за указанный период. + +**Параметры:** +- `$date1` - Дата начала периода +- `$date2_smen` - Дата окончания периода (с учетом смены) + +**Возвращает:** Массив записей с полями: +- `admin_id` - ID сотрудника +- `d_id` - ID дня +- `store_id` - ID магазина +- `slot_type_id` - Тип слота (работа/отпуск/больничный и т.д.) +- `date` - Дата +- `shift_id` - ID смены + +**Фильтрация:** +- Только активные записи (`tabel = 0`) +- Только типы: работа (1), стажировка (5), тип 8 +- Сортировка по дню и смене + +**Пример:** +```php +$timetable = TimetableService::getTimetable('2024-01-01', '2024-01-31'); +// [ +// ['admin_id' => 123, 'store_id' => 5, 'date' => '2024-01-15', 'shift_id' => 1, ...], +// ... +// ] +``` + +##### Получение разрешенных магазинов +```php +public static function getAllowedStoreId($adminId, $groupId): array +``` + +Определяет магазины, в которых сотрудник может работать. + +**Логика:** +1. **Для определенных групп** (ADMIN_WRITE_OFFS_SINGLE_STORE_GROUP_IDS): + - Возвращает магазин, где сотрудник работает СЕГОДНЯ + - Используется для ограничения доступа (например, для списаний) + +2. **Для остальных:** + - Возвращает массив магазинов из `admin.store_arr` + - Если `store_arr` пусто, возвращает основной магазин из `admin.store_id` + +**Пример:** +```php +$storeIds = TimetableService::getAllowedStoreId(123, 30); +// [5, 7, 12] - сотрудник может работать в этих магазинах +``` + +**Зависимости:** +- `Admin` +- `Timetable` +- `DateHelper` + +--- + +### Actions (17) + +#### **Планирование (График)** + +##### 1. **PlanAction** +`erp24/actions/timetable/PlanAction.php` + +Просмотр и создание плана работы (графика смен). + +**Функционал:** +- Календарный вид графика +- Фильтр по магазинам и сотрудникам +- Создание новых плановых смен +- Цветовая маркировка типов смен + +##### 2. **EditPlanAction** +`erp24/actions/timetable/EditPlanAction.php` + +Редактирование плановой смены. + +**Функционал:** +- Изменение времени начала/окончания +- Изменение магазина +- Изменение типа смены +- Валидация: нельзя редактировать, если уже создан факт + +##### 3. **AddAction** +`erp24/actions/timetable/AddAction.php` + +Добавление новой записи в график. + +**Функционал:** +- Форма создания плановой смены +- Выбор сотрудника, магазина, даты, времени +- Автоматический расчет рабочих часов + +--- + +#### **Факт (Табель)** + +##### 4. **FactAction** +`erp24/actions/timetable/FactAction.php` + +Просмотр фактически отработанного времени. + +**Функционал:** +- Табличное представление фактов +- Сравнение плана и факта +- Выделение опозданий и ранних уходов +- Отображение прогулов + +##### 5. **FactOverviewAction** +`erp24/actions/timetable/FactOverviewAction.php` + +Обзор фактических смен за период. + +**Функционал:** +- Сводная таблица по сотрудникам +- Статистика отработанных часов +- Количество смен +- Отклонения от плана + +##### 6. **FactOverviewItemAction** +`erp24/actions/timetable/FactOverviewItemAction.php` + +Детальный просмотр конкретной фактической смены. + +**Функционал:** +- Подробная информация о смене +- Явки (чекины) сотрудника +- Сравнение с планом +- История изменений + +##### 7. **EditFactAction** +`erp24/actions/timetable/EditFactAction.php` + +Редактирование фактической смены. + +**Функционал:** +- Корректировка времени начала/окончания +- Добавление комментариев +- Валидация: нельзя редактировать будущие смены + +##### 8. **AddFactHandAction** +`erp24/actions/timetable/AddFactHandAction.php` + +Ручное добавление фактической смены. + +**Функционал:** +- Создание факта без плана +- Для внештатных ситуаций +- Обязательный комментарий + +--- + +#### **Начало смены** + +##### 9. **StartAction** +`erp24/actions/timetable/StartAction.php` + +Главная страница начала смены. + +**Функционал:** +- Проверка наличия плана на сегодня +- Переход к пошаговому началу смены +- Отображение текущих смен + +##### 10. **StartShiftStepOneAction** +`erp24/actions/timetable/StartShiftStepOneAction.php` + +Шаг 1: Выбор сотрудника. + +**Функционал:** +- Поиск сотрудника по имени/ID +- Проверка наличия плана +- Переход к шагу 2 + +##### 11. **StartShiftStepTwoAction** +`erp24/actions/timetable/StartShiftStepTwoAction.php` + +Шаг 2: Выбор магазина и смены. + +**Функционал:** +- Выбор магазина из доступных +- Выбор смены из плана +- Отображение деталей смены +- Переход к шагу 3 + +##### 12. **StartShiftStepThreeAction** +`erp24/actions/timetable/StartShiftStepThreeAction.php` + +Шаг 3: Подтверждение и начало смены. + +**Функционал:** +- Подтверждение начала смены +- Создание чекина (явки) +- Создание записи факта +- Фиксация времени начала + +--- + +#### **Явки (чекины)** + +##### 13. **CheckinsAction** +`erp24/actions/timetable/CheckinsAction.php` + +Просмотр явок сотрудников. + +**Функционал:** +- Список всех чекинов за период +- Привязка к сменам +- Чекины без смен (проблемы) +- Фильтрация по магазинам/сотрудникам + +##### 14. **JoinMissingShiftsWithCheckinsAction** +`erp24/actions/timetable/JoinMissingShiftsWithCheckinsAction.php` + +Автоматическая привязка явок к сменам. + +**Функционал:** +- Поиск чекинов без привязки к плану +- Автоматическое сопоставление по времени и магазину +- Создание фактов на основе чекинов + +**Примечание:** TODO в контроллере указывает, что лучше запретить начинать смену без привязки к плану. + +--- + +#### **Другое** + +##### 15. **AdminStores** +`erp24/actions/timetable/AdminStores.php` + +Получение списка магазинов сотрудника. + +**Функционал:** +- AJAX эндпоинт +- Возвращает магазины, где сотрудник может работать +- Используется для динамических форм + +##### 16. **HolidaysAction** +`erp24/actions/timetable/HolidaysAction.php` + +Управление праздниками и выходными. + +**Функционал:** +- Список праздничных дней +- Добавление/удаление праздников +- Отображение в календаре + +##### 17. **IndexAction** (отключен) +`erp24/actions/timetable/IndexAction.php` + +Главная страница модуля (временно отключена). + +--- + +### Модели/Records (9) + +#### 1. **Timetable** (Базовая модель) +`erp24/records/Timetable.php` + +Базовая модель табеля сотрудников. Использует паттерн Single Table Inheritance (STI) - все данные в одной таблице, разные типы через поле `tabel`. + +**Таблица:** `timetable` + +**Основные поля:** +- `id` (int) - ID записи +- `tabel` (int) - Тип записи (0=План, 1=Факт, 2=Факт новый) +- `admin_id` (int) - ID сотрудника +- `store_id` (int) - ID магазина +- `shift_id` (int) - ID смены +- `date` (date) - Дата +- `datetime_start` (datetime) - Дата и время начала +- `datetime_end` (datetime) - Дата и время окончания +- `time_start` (time) - Время начала +- `time_end` (time) - Время окончания +- `work_time` (float) - Рабочих часов +- `salary_shift` (int) - Оплата за смену +- `slot_type_id` (int) - Тип слота (работа/отпуск/больничный...) +- `admin_group_id` (int) - ID группы сотрудника +- `d_id` (int) - ID дня +- `admin_id_add` (int) - Кто добавил запись +- `comment` (string) - Комментарий +- `date_add` (int) - Дата добавления (timestamp) +- `status` (int) - Статус (0=ожидает, 1=подтвержден) +- `active` (int) - Активность +- `deleted_at` (datetime) - Время удаления (SoftDelete) +- `deleted_by` (int) - Кто удалил + +**Константы типов:** + +##### Типы записей (tabel): +- `TABLE_PLAN = 0` - План (график) +- `TABLE_FACT = 1` - Факт (табель) +- `TABLE_FACT_NEW = 2` - Факт новый + +##### Статусы: +- `STATUS_PENDING = 0` - Ожидает подтверждения +- `STATUS_VERIFIED = 1` - Подтвержден + +##### Типы слотов (slot_type_id): +- `TIMESLOT_WORK = 1` - Работа +- `TIMESLOT_VACATION = 2` - Отпуск +- `TIMESLOT_ADMINISTRATIVE = 3` - Административный отпуск +- `TIMESLOT_SICK_LEAVE = 4` - Больничный +- `TIMESLOT_INTERNSHIP = 5` - Стажировка +- `TIMESLOT_WEEKEND = 6` - Выходной +- `TIMESLOT_FREELANCE = 7` - Подработка + +**Ключевые методы:** + +##### Инстанцирование полиморфных моделей +```php +public static function instantiate($row): self +``` +Автоматически создает правильный подтип (Plan или Fact) на основе поля `tabel`. + +##### Оклады за день +```php +public static function getSalariesDay(): array +``` +Возвращает: `[1700, 2000, 2500]` - варианты окладов за смену. + +##### Проверка возможности редактирования +```php +public static function getAllowEditShift($slotDate, $days = 5): bool +``` + +Определяет, можно ли редактировать смену. + +**Правила:** +- ✅ Можно редактировать будущие смены +- ✅ Можно редактировать смены текущего месяца +- ✅ Можно редактировать смены предыдущего месяца в первые N дней текущего месяца +- ❌ Нельзя редактировать старые смены + +##### Количество дней для редактирования +```php +public static function getCountDaysAllowEditShift($groupId): int +``` + +Возвращает количество дней, в течение которых можно редактировать смены предыдущего месяца. + +**Для обычных групп:** 5 дней + +**Для привилегированных групп (13 дней):** +- 1 - Директор +- 7 - Кустовые директора +- 8 - Руководитель HR +- 9 - Главный бухгалтер +- 20 - Администратор платформы (HR) +- 51 - Операционный директор + +**Трейты:** +- `SoftDeleteTrait` - Мягкое удаление + +**Связи:** +- `shift` → Shift - Смена +- `store` → CityStore - Магазин +- `position` → AdminGroup - Должность +- `admin` → Admin - Сотрудник +- `addedBy` → Admin - Кто добавил +- `deleted_by` → Admin - Кто удалил + +--- + +#### 2. **TimetablePlan** (План) +`erp24/records/TimetablePlan.php` + +Плановые смены (график работы). + +**Наследует:** Timetable + +**Поле tabel:** Всегда `TABLE_PLAN (0)` + +**Дополнительные правила валидации:** +- `work_time` - от 1 до 24 часов +- `time_end` - смена должна длиться минимум 1 час +- Нельзя редактировать план, для которого уже создан факт + +**Ключевые методы:** + +##### Создание факта из плана +```php +public function makeFact(): TimetableFact +``` + +Создает запись факта на основе плана и явок (чекинов). + +**Логика:** +1. **Если нет чекинов:** + - Создается факт с нулевым временем работы + - `datetime_start` = `datetime_end` (отсутствие) + - Комментарий: "fakt checkins === 0" + +2. **Если есть чекины:** + - Берется первый и последний чекин + - Время ограничивается рамками плана (bound) + - Рассчитывается фактическое время работы + - Комментарий: "fakt checkins != 0" + +##### Ограничение времени рамками плана +```php +public function bound(\DateTime $date): \DateTime +``` + +Приводит дату/время к границам плана. + +**Пример:** +```php +// План: 09:00 - 18:00 +$plan->bound(new DateTime('08:30')); // → 09:00 (ограничено началом) +$plan->bound(new DateTime('19:00')); // → 18:00 (ограничено концом) +$plan->bound(new DateTime('12:00')); // → 12:00 (в пределах) +``` + +**Связи:** +- `fact` → TimetableFact - Фактическая смена +- `checkins` → AdminCheckin[] - Явки сотрудника + +--- + +#### 3. **TimetableFact** (Факт) +`erp24/records/TimetableFact.php` + +Фактически отработанные смены (табель). + +**Наследует:** Timetable + +**Поле tabel:** Всегда `TABLE_FACT (1)` + +**Дополнительные поля:** +- `plan_id` (int) - ID плановой смены + +**Дополнительные правила валидации:** +- `plan_id` - должен существовать в TimetablePlan +- `datetime_start` - не может быть в будущем +- `datetime_end` - не может быть в будущем + +**Особенности:** +- Не поддерживает SoftDelete (`hasSoftDelete() = false`) +- Можно удалять только физически + +**Ключевые методы:** + +##### Проверка отсутствия +```php +public function isAbsent(): bool +``` + +Определяет, был ли сотрудник на работе. + +**Логика:** +```php +return $this->datetime_start === $this->datetime_end; +// Если время начала = времени окончания → отсутствие/прогул +``` + +##### Количество пропущенных часов +```php +public function getSkippedHours(): float +``` + +Рассчитывает разницу между планом и фактом. + +**Пример:** +```php +// План: 8 часов, Факт: 6 часов +$fact->getSkippedHours(); // → 2 часа пропущено +``` + +##### Опоздание +```php +public function getLate(): \DateInterval +``` + +Рассчитывает опоздание на смену. + +**Пример:** +```php +// План начало: 09:00, Факт начало: 09:15 +$fact->getLate(); // → DateInterval (15 минут) +``` + +##### Ранний уход +```php +public function getEarly(): \DateInterval +``` + +Рассчитывает ранний уход со смены. + +**Пример:** +```php +// План конец: 18:00, Факт конец: 17:45 +$fact->getEarly(); // → DateInterval (15 минут) +``` + +**Связи:** +- `plan` → TimetablePlan - Плановая смена + +--- + +#### 4. **TimetableV3** +`erp24/records/TimetableV3.php` + +Версия 3 табеля (возможно, новая версия API). + +#### 5. **TimetablePlanV3** +`erp24/records/TimetablePlanV3.php` + +Версия 3 плана. + +#### 6. **TimetableFactV3** +`erp24/records/TimetableFactV3.php` + +Версия 3 факта. + +#### 7. **TimetableShift** +`erp24/records/TimetableShift.php` + +Смены (утро, день, вечер). + +**Таблица:** `shift` (возможно) + +**Примеры смен:** +- Утренняя (08:00-16:00) +- Дневная (12:00-20:00) +- Вечерняя (16:00-00:00) + +#### 8. **TimetableWorkbot** +`erp24/records/TimetableWorkbot.php` + +Данные от бота рабочего времени (возможно, Telegram-бот). + +#### 9. **TimetableFactModel** +`erp24/records/TimetableFactModel.php` + +Дополнительная модель для работы с фактами. + +--- + +## Бизнес-логика + +### Процесс управления расписанием + +```mermaid +sequenceDiagram + participant HR as HR-менеджер + participant Plan as TimetablePlan + participant Employee as Сотрудник + participant Checkin as AdminCheckin + participant Fact as TimetableFact + participant Payroll as PayrollService + + HR->>Plan: Создать график на месяц + Plan->>Plan: Сохранить планы смен + + Employee->>Checkin: Явка на смену (чекин) + Checkin->>Plan: Привязка к плану + + Note over Employee,Fact: В конце дня или смены + + Plan->>Checkin: Получить все чекины за смену + Plan->>Fact: makeFact() - Создать факт + Fact->>Fact: Рассчитать отработанное время + Fact->>Fact: Рассчитать опоздание/ранний уход + + Payroll->>Fact: Получить факты за месяц + Payroll->>Payroll: Рассчитать зарплату +``` + +### Типы смен и их обработка + +#### 1. Работа (TIMESLOT_WORK) +- Стандартные рабочие смены +- Учитываются в расчете зарплаты +- Требуют чекинов +- Создается факт + +#### 2. Отпуск (TIMESLOT_VACATION) +- Оплачиваемый отпуск +- Не требуют чекинов +- Автоматически создается факт = плану + +#### 3. Больничный (TIMESLOT_SICK_LEAVE) +- Больничный лист +- Оплата по отдельным правилам +- Не требуют чекинов + +#### 4. Стажировка (TIMESLOT_INTERNSHIP) +- Обучение новых сотрудников +- Особые условия оплаты +- Требуют чекинов + +#### 5. Выходной (TIMESLOT_WEEKEND) +- Запланированный выходной +- Не оплачивается +- Не создается факт + +#### 6. Подработка (TIMESLOT_FREELANCE) +- Дополнительные смены +- Особая оплата +- Требуют чекинов + +#### 7. Административный отпуск (TIMESLOT_ADMINISTRATIVE) +- Неоплачиваемый отпуск +- Не требуют чекинов + +### Правила редактирования + +#### Обычные сотрудники (5 дней): +``` +Сегодня: 10 января +✅ Можно редактировать: январь, 1-5 декабря +❌ Нельзя редактировать: 6-31 декабря, ноябрь и ранее +``` + +#### Привилегированные группы (13 дней): +``` +Сегодня: 10 января +✅ Можно редактировать: январь, 1-13 декабря +❌ Нельзя редактировать: 14-31 декабря, ноябрь и ранее +``` + +### Создание факта из плана + +**Сценарий 1: Есть чекины** +```php +План: 09:00 - 18:00 (9 часов) +Чекины: + - 09:15 (первый чекин - опоздание) + - 12:30 (чекин с обеда) + - 17:45 (последний чекин - ранний уход) + +Факт: 09:15 - 17:45 (8.5 часов) +Опоздание: 15 минут +Ранний уход: 15 минут +Пропущено: 0.5 часа +``` + +**Сценарий 2: Нет чекинов (прогул)** +```php +План: 09:00 - 18:00 (9 часов) +Чекины: нет + +Факт: 18:00 - 18:00 (0 часов) ← время начала = времени конца +isAbsent() = true +Пропущено: 9 часов +``` + +**Сценарий 3: Чекин вне плана** +```php +План: 09:00 - 18:00 +Чекин: 08:30 (раньше плана) + +Факт: 09:00 - ... (bound() ограничивает 09:00) +``` + +### Начало смены (3 шага) + +```mermaid +graph LR + A[Шаг 1:
Выбор сотрудника] --> B[Шаг 2:
Выбор магазина
и смены] + B --> C[Шаг 3:
Подтверждение
и начало] + C --> D[Чекин создан] + C --> E[Факт создан] +``` + +**Шаг 1: StartShiftStepOneAction** +- Поиск/выбор сотрудника +- Проверка наличия плана на сегодня + +**Шаг 2: StartShiftStepTwoAction** +- Выбор магазина из доступных +- Выбор плановой смены на сегодня +- Отображение деталей (время, оплата) + +**Шаг 3: StartShiftStepThreeAction** +- Подтверждение данных +- Создание чекина (AdminCheckin) +- Создание/обновление факта (TimetableFact) +- Фиксация времени начала смены + +--- + +## API и интеграции + +### Внутренние зависимости + +Модуль взаимодействует с: +- **Payroll** - расчет зарплаты на основе отработанных часов +- **Bonus** - расчет бонусов за смены и KPI +- **Admin** - данные о сотрудниках +- **CityStore** - данные о магазинах +- **Shift** - типы смен +- **AdminCheckin** - явки сотрудников + +### Внешние интеграции + +- **Telegram-бот** (возможно, TimetableWorkbot) - уведомления, чекины +- **Биометрия/Табель** (возможно) - автоматические чекины + +--- + +## Маршруты (Routes) + +``` +# Планирование (График) +/timetable/plan - Просмотр и создание плана +/timetable/edit_plan - Редактирование плана +/timetable/add - Добавление смены + +# Факт (Табель) +/timetable/fact - Просмотр факта +/timetable/fact_overview - Обзор фактов +/timetable/fact_overview_item - Детали смены +/timetable/edit_fact - Редактирование факта +/timetable/add-fact-hand - Ручное добавление факта + +# Начало смены +/timetable/start - Главная страница +/timetable/start-shift-step-one - Шаг 1 +/timetable/start-shift-step-two - Шаг 2 +/timetable/start-shift-step-three - Шаг 3 + +# Явки +/timetable/checkins - Просмотр чекинов +/timetable/join-missing-shifts-with-checkins - Привязка чекинов + +# Другое +/timetable/admin_stores - Магазины сотрудника (AJAX) +/timetable/holidays - Праздники +/timetable/admin-info?id=123 - Информация о сотруднике (AJAX) +/timetable/tabel - Редирект на /timetable/plan +/timetable/link-plans - Привязка планов +``` + +--- + +## Примеры использования + +### Пример 1: Получение графика за месяц + +```php +$timetable = TimetableService::getTimetable('2024-01-01', '2024-01-31'); + +foreach ($timetable as $record) { + echo "Сотрудник {$record['admin_id']}, "; + echo "Магазин {$record['store_id']}, "; + echo "Дата {$record['date']}, "; + echo "Смена {$record['shift_id']}\n"; +} +``` + +### Пример 2: Создание плановой смены + +```php +$plan = new TimetablePlan(); +$plan->admin_id = 123; +$plan->store_id = 5; +$plan->shift_id = 1; +$plan->date = '2024-01-15'; +$plan->datetime_start = '2024-01-15 09:00:00'; +$plan->datetime_end = '2024-01-15 18:00:00'; +$plan->time_start = '09:00:00'; +$plan->time_end = '18:00:00'; +$plan->work_time = 9; +$plan->salary_shift = 2000; +$plan->slot_type_id = Timetable::TIMESLOT_WORK; +$plan->admin_id_add = Yii::$app->user->id; +$plan->save(); +``` + +### Пример 3: Создание факта из плана + +```php +$plan = TimetablePlan::findOne(12345); +$fact = $plan->makeFact(); + +if ($fact->save()) { + echo "Факт создан:\n"; + echo "Начало: {$fact->datetime_start}\n"; + echo "Конец: {$fact->datetime_end}\n"; + echo "Часов: {$fact->work_time}\n"; + + if ($fact->isAbsent()) { + echo "ПРОГУЛ!\n"; + } else { + $late = $fact->getLate(); + echo "Опоздание: {$late->format('%H:%I')}\n"; + } +} +``` + +### Пример 4: Проверка возможности редактирования + +```php +$slotDate = '2023-12-28'; +$groupId = Yii::$app->user->identity->group_id; + +$days = Timetable::getCountDaysAllowEditShift($groupId); +$canEdit = Timetable::getAllowEditShift($slotDate, $days); + +if ($canEdit) { + // Можно редактировать + echo "Можно редактировать (доступ {$days} дней)\n"; +} else { + // Редактирование заблокировано + echo "Редактирование запрещено\n"; +} +``` + +### Пример 5: Получение разрешенных магазинов + +```php +$adminId = 123; +$groupId = 30; // Флорист + +$storeIds = TimetableService::getAllowedStoreId($adminId, $groupId); + +echo "Сотрудник может работать в магазинах: "; +echo implode(', ', $storeIds); +// Например: 5, 7, 12 +``` + +### Пример 6: Анализ факта vs план + +```php +$fact = TimetableFact::findOne(123); +$plan = $fact->plan; + +echo "План: {$plan->datetime_start} - {$plan->datetime_end} ({$plan->work_time} ч.)\n"; +echo "Факт: {$fact->datetime_start} - {$fact->datetime_end} ({$fact->work_time} ч.)\n"; + +$late = $fact->getLate(); +$early = $fact->getEarly(); +$skipped = $fact->getSkippedHours(); + +echo "Опоздание: {$late->format('%H:%I')}\n"; +echo "Ранний уход: {$early->format('%H:%I')}\n"; +echo "Пропущено часов: {$skipped}\n"; +``` + +--- + +## База данных + +### Основные таблицы + +1. **timetable** - План и факт (STI) +2. **shift** - Смены +3. **admin_checkin** - Явки сотрудников + +### ER-диаграмма + +```mermaid +erDiagram + timetable ||--o| timetable : "plan_id (для фактов)" + timetable }o--|| admin : "admin_id" + timetable }o--|| city_store : "store_id" + timetable }o--|| shift : "shift_id" + timetable }o--|| admin_group : "admin_group_id" + timetable ||--o{ admin_checkin : "plan_id" + + admin_checkin }o--|| admin : "admin_id" + admin_checkin }o--|| city_store : "store_id" + + timetable { + int id PK + int tabel "0=Plan, 1=Fact, 2=Fact New" + int admin_id FK + int store_id FK + int shift_id FK + date date + datetime datetime_start + datetime datetime_end + float work_time + int salary_shift + int slot_type_id + int plan_id FK "Только для фактов" + string comment + int status + int active + datetime deleted_at + } + + admin_checkin { + int id PK + int admin_id FK + int store_id FK + int plan_id FK + datetime checkin_time + string device + string ip + } +``` + +--- + +## Метрики и аналитика + +### Ключевые метрики + +1. **Процент выполнения плана** (количество отработанных часов / плановые часы) +2. **Средний процент опозданий** по сотрудникам/магазинам +3. **Количество прогулов** за период +4. **Средняя длительность смены** (факт vs план) +5. **Процент ранних уходов** +6. **Количество смен по типам** (работа, отпуск, больничный и т.д.) +7. **Загрузка магазинов** (количество смен/сотрудников) +8. **Отклонение факта от плана** в часах + +--- + +## Автоматизация + +### Рекомендуемое расписание Cron + +```bash +# Автоматическое создание фактов в конце дня (каждый день в 00:30) +30 0 * * * /usr/bin/php /path/to/yii timetable/create-facts-from-plans + +# Привязка чекинов без смен (каждый час) +0 * * * * /usr/bin/php /path/to/yii timetable/join-missing-shifts-with-checkins + +# Уведомления о прогулах (каждый день в 10:00) +0 10 * * * /usr/bin/php /path/to/yii timetable/notify-absences +``` + +--- + +## Вопросы для уточнения + +1. ❓ Что такое TimetableV3, TimetablePlanV3, TimetableFactV3 - новая версия API? +2. ❓ Как работает TimetableWorkbot - это Telegram-бот для чекинов? +3. ❓ Какие устройства используются для чекинов (AdminCheckin)? +4. ❓ Есть ли биометрическая система учета рабочего времени? +5. ❓ Как обрабатываются переработки (работа свыше плана)? +6. ❓ Есть ли автоматическое создание фактов в конце дня? +7. ❓ Как обрабатываются ночные смены (переход через полночь)? + +--- + +## Связанные модули + +- [Payroll (Расчет заработной платы)](../payroll/README.md) - Использует данные о сменах для расчета +- [Bonus (Бонусная система)](../bonus/README.md) - Учитывает количество смен +- [Dashboard (Информационные панели)](../dashboard/README.md) - Визуализация графиков +- (Модуль HR/Сотрудники) - Управление персоналом + +--- + +*Документация создана автоматически. Последнее обновление: 2025-11-17* + +**Примечание:** Модуль Timetable является критически важным для всей системы, так как его данные используются в расчете зарплаты, бонусов и множестве отчетов. diff --git a/erp24/docs/modules/write-offs/README.md b/erp24/docs/modules/write-offs/README.md new file mode 100644 index 00000000..89625db5 --- /dev/null +++ b/erp24/docs/modules/write-offs/README.md @@ -0,0 +1,63 @@ +# Модуль Write-offs (Списания товаров) + +## 📋 Описание + +**Write-offs** - модуль учета списания товаров и продукции в магазинах. Система отслеживает все списания, причины, ответственных и интегрируется с 1С для синхронизации данных о порче, браке и других списаниях. + +### Основные возможности + +- 📊 Учет списаний по магазинам +- 🏷️ Классификация по причинам (брак, порча, пересорт и др.) +- 📅 Временной анализ списаний +- 💰 Учет сумм списаний +- 📈 Метрики и аналитика +- 🔄 Синхронизация с 1С +- 💬 Комментарии к списаниям + +## 🏗️ Архитектура + +**Контроллеры:** 1 (WriteOffsController) +- `index` - список списаний +- `comments` - комментарии к списаниям + +**Модели (9):** +- `WriteOffs` - основная таблица списаний +- `WriteOffsErp` - списания из ERP +- `WriteOffsProducts` - товары в списании +- `WriteOffsProductsErp` - товары из ERP +- `WriteOffsErpCauseDict` - справочник причин +- `WriteOffsMetrics` - метрики списаний + +## 💼 Основные сущности + +**Причины списаний:** +- Брак +- Порча/Увядание +- Пересорт +- Недостача +- Бой/Повреждение +- Истечение срока годности + +**Интеграция с 1С:** +- Автоматическая синхронизация данных о списаниях +- Сверка по GUID товаров и магазинов +- Учет сумм в разрезе дат и магазинов + +## 📊 Метрики + +Модуль используется в аналитике: +- Процент списания от продаж (влияет на рейтинг и бонусы) +- Динамика списаний по магазинам +- Топ причин списаний +- Ответственные за списания + +## 🔗 Связи с модулями + +- **Dashboard** - метрики списаний +- **Rating** - влияние на рейтинг магазина/сотрудников +- **Bonus** - штрафы за превышение нормы списаний +- **1C Integration** - синхронизация данных + +--- + +**Последнее обновление:** 2025-11-17