# Интеграция зарплатных данных из 1С - Инструкция по внедрению ## Шаг 1: Применить миграцию БД Выполните SQL миграцию для создания таблицы истории зарплат: ```bash psql -d mkd_control_center -f backend/migrate_salary_history.sql ``` Или вручную через pgAdmin/psql выполните содержимое файла `backend/migrate_salary_history.sql` ## Шаг 2: Добавить SalaryProcessor в server.js В файле `backend/server.js` добавьте: ```javascript // В начале файла, после других require const SalaryProcessor = require('./salaryProcessor'); const salaryProcessor = new SalaryProcessor(pool); ``` ## Шаг 3: Добавить API endpoint для загрузки зарплатных отчетов Добавьте в `backend/server.js` после существующих финансовых endpoints: ```javascript // POST /api/salary/upload-report - загрузка зарплатного отчета из 1С app.post(`${API_PREFIX}/salary/upload-report`, upload.single('file'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'Файл не загружен' }); } const fileType = path.extname(req.file.originalname).toLowerCase() === '.csv' ? 'CSV' : 'XLSX'; const uploadedBy = req.body.uploadedBy || 'System'; // Создаем запись об отчете const reportResult = await query( `INSERT INTO financial_reports (filename, file_type, uploaded_by, status) VALUES ($1, $2, $3, 'processing') RETURNING id`, [req.file.originalname, fileType, uploadedBy] ); const reportId = reportResult[0].id; // Парсим файл let rows = []; if (fileType === 'CSV') { rows = await fileProcessor.parseCSV(req.file.path); } else { rows = await fileProcessor.parseXLSX(req.file.path); } // Получаем маппинг полей (можно передать в body или использовать по умолчанию) const mapping = req.body.mapping ? JSON.parse(req.body.mapping) : { columnMappings: { // Пример маппинга - настройте под ваш формат 1С 'ФИО': 'employeeIdentifier', 'ИНН': 'inn', 'Период': 'period', 'Оклад': 'baseSalary', 'Начислено': 'actualSalary', 'Премия': 'bonuses', 'Удержано': 'deductions', 'К выплате': 'netSalary', 'Отработано дней': 'workedDays' } }; // Обрабатываем зарплатные данные const result = await salaryProcessor.processSalaryData(rows, mapping, reportId); // Обновляем статус отчета await query( `UPDATE financial_reports SET status = $1, processed_rows = $2, error_rows = $3, error_log = $4 WHERE id = $5`, [ result.errorRows > 0 && result.processedRows > 0 ? 'partial' : result.errorRows > 0 ? 'failed' : 'completed', result.processedRows, result.errorRows, JSON.stringify(result.errors), reportId ] ); // Удаляем файл после обработки fs.unlinkSync(req.file.path); res.json({ success: true, reportId, ...result }); } catch (error) { console.error('Error processing salary report:', error); res.status(500).json({ error: 'Ошибка обработки зарплатного отчета', details: error.message }); } }); // GET /api/salary/history/:employeeId - получить историю зарплат сотрудника app.get(`${API_PREFIX}/salary/history/:employeeId`, async (req, res) => { try { const { employeeId } = req.params; const { year, month } = req.query; let queryText = ` SELECT id, period_month AS "periodMonth", period_year AS "periodYear", base_salary AS "baseSalary", actual_salary AS "actualSalary", bonuses, deductions, net_salary AS "netSalary", worked_days AS "workedDays", worked_hours AS "workedHours", vacation_days AS "vacationDays", sick_leave_days AS "sickLeaveDays", metadata, imported_at AS "importedAt" FROM employee_salary_history WHERE employee_id = $1 `; const params = [employeeId]; if (year) { queryText += ` AND period_year = $${params.length + 1}`; params.push(year); } if (month) { queryText += ` AND period_month = $${params.length + 1}`; params.push(month); } queryText += ' ORDER BY period_year DESC, period_month DESC'; const result = await query(queryText, params); res.json(result); } catch (error) { console.error('Error fetching salary history:', error); res.status(500).json({ error: 'Ошибка получения истории зарплат' }); } }); ``` ## Шаг 4: Формат файла из 1С ### Пример структуры Excel/CSV файла из 1С: | ФИО | ИНН | Период | Оклад | Начислено | Премия | Удержано | К выплате | Отработано дней | |-----|-----|--------|-------|-----------|--------|----------|-----------|-----------------| | Иванов Иван Иванович | 123456789012 | 01.2024 | 50000 | 55000 | 5000 | 7150 | 47850 | 22 | | Петров Петр Петрович | 987654321098 | 01.2024 | 60000 | 65000 | 5000 | 8450 | 56550 | 22 | ### Важные моменты: 1. **Идентификация сотрудника:** - Приоритет: ИНН > СНИЛС > ФИО - ФИО должно совпадать с данными в системе (можно частичное совпадение) 2. **Формат периода:** - Поддерживается: "01.2024", "01/2024", "январь 2024", "2024-01" - Рекомендуется: "MM.YYYY" (например, "01.2024") 3. **Числовые поля:** - Могут быть с пробелами: "50 000" - Могут быть с запятой: "50,000" - Автоматически конвертируются ## Шаг 5: Настройка маппинга полей Если названия колонок в вашем файле 1С отличаются, настройте маппинг: ```javascript const mapping = { columnMappings: { 'ФИО сотрудника': 'employeeIdentifier', 'ИНН сотрудника': 'inn', 'Месяц расчета': 'period', 'Оклад по штатному расписанию': 'baseSalary', 'Начислено всего': 'actualSalary', // ... и т.д. } }; ``` ## Шаг 6: Тестирование 1. Экспортируйте отчет по зарплате из 1С в Excel/CSV 2. Загрузите файл через API: ```bash curl -X POST http://localhost:4000/api/salary/upload-report \ -F "file=@salary_report.xlsx" \ -F "uploadedBy=Admin" ``` 3. Проверьте результат: - Статус обработки - Количество обработанных строк - Ошибки (если есть) ## Шаг 7: Создание UI компонента (опционально) Создайте React компонент для загрузки зарплатных отчетов, аналогичный `ReportUploader.tsx`: ```typescript // components/hr/SalaryReportUploader.tsx // Аналогично существующему ReportUploader, но для зарплатных данных ``` ## Автоматизация (будущее) После настройки HTTP-сервиса в 1С можно добавить автоматическую синхронизацию: ```javascript // В cron или по расписанию app.post(`${API_PREFIX}/salary/sync-from-1c`, async (req, res) => { // Запрос к HTTP-сервису 1С // Обработка ответа // Сохранение данных }); ``` ## Поддержка При возникновении проблем: 1. Проверьте формат файла из 1С 2. Проверьте маппинг полей 3. Проверьте логи ошибок в консоли 4. Убедитесь, что сотрудники существуют в системе и имеют ИНН/СНИЛС