Initial commit MKD fixes
This commit is contained in:
239
backend/INTEGRATION_SALARY_1C.md
Executable file
239
backend/INTEGRATION_SALARY_1C.md
Executable file
@@ -0,0 +1,239 @@
|
||||
# Интеграция зарплатных данных из 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. Убедитесь, что сотрудники существуют в системе и имеют ИНН/СНИЛС
|
||||
Reference in New Issue
Block a user