diff --git a/README.md b/README.md
index a649aab..0108228 100644
--- a/README.md
+++ b/README.md
@@ -33,3 +33,6 @@
7. **[Шаблон кода для работы с S3-хранилищем](S3Connector/)**
- Шаблон с методами получения файлов из хранилища, работающего по протоколу S3.
+
+7. **[Шаблон обработки ответов от коннектора к SAP](SAPConnector/)**
+ - Шаблон с методами синхронизации орг. структуры с SAP.
diff --git a/SAPConnector/ImportRuleDepartmentManagerServerFunctions.cs b/SAPConnector/ImportRuleDepartmentManagerServerFunctions.cs
new file mode 100644
index 0000000..76bde04
--- /dev/null
+++ b/SAPConnector/ImportRuleDepartmentManagerServerFunctions.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Sungero.Core;
+using Sungero.CoreEntities;
+using DirRX.SapIntegration.ImportRuleDepartmentManager;
+
+namespace DirRX.SapIntegration.Server
+{
+ partial class ImportRuleDepartmentManagerFunctions
+ {
+ ///
+ /// Выполнить сохранение полученных данных в RX.
+ ///
+ /// Матрица с ответом от SAP.
+ /// Структурированный лог.
+ public override void SaveData(List> response, List logs)
+ {
+ var managers = new List();
+
+ // Получить руководящие должности, где OType = S, Vrsign = B и Vrelat = 012.
+ var filteredResponse = response.Where(x =>
+ x.Any(y => y.Key == Constants.ImportRuleBase.IsStructuralExist && y.Value == "1") &&
+ x.Any(y => y.Key == Constants.ImportRuleBase.SAPStructuralFields.Otype && y.Value == "S") &&
+ x.Any(y => y.Key == Constants.ImportRuleBase.SAPStructuralFields.Vrsign && y.Value == "B") &&
+ x.Any(y => y.Key == Constants.ImportRuleBase.SAPStructuralFields.Vrelat && y.Value == "012")
+ );
+ foreach (var responseItem in filteredResponse)
+ {
+ var manager = ParseResponseItem(responseItem, response, logs);
+ if (manager != null)
+ managers.Add(manager);
+ }
+
+ if (managers.Any())
+ {
+ ImportDepartmentManagers(managers, logs);
+ }
+ }
+
+ ///
+ /// Обработка строки матрицы с ответом от SAP.
+ ///
+ /// Строка матрицы с ответом от SAP.
+ /// Полная матрица с ответом от SAP.
+ /// Структурированный лог.
+ /// Свойства сущности в структурированном виде.
+ public virtual DirRX.SapIntegration.Structures.ImportRuleDepartmentManager.DepartmentManager ParseResponseItem(System.Collections.Generic.Dictionary responseItem,
+ List> response,
+ List logs)
+ {
+ var departmentSapId = string.Empty;
+ var managerSapId = string.Empty;
+ var jobTitleSapId = responseItem[Constants.ImportRuleBase.SAPObjectFields.SapID];
+ // Получить вышестоящий элемент - подразделение.
+ var parentId = responseItem[Constants.ImportRuleBase.SAPStructuralFields.Pup];
+ if (parentId != "0")
+ {
+ var parentNode = response.FirstOrDefault(x => x.Any(y => y.Key == Constants.ImportRuleBase.SAPStructuralFields.Seqnr && y.Value == parentId));
+ if (parentNode != null)
+ {
+ departmentSapId = parentNode[Constants.ImportRuleBase.SAPObjectFields.SapID];
+ }
+ }
+ else
+ {
+ var errorMessage = string.Format("Нет вышестоящих элементов (подразделений) для должности {0}.", jobTitleSapId);
+ Logger.Error(errorMessage);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+
+ // Получить сотрудника по штатной должности.
+ var jobTitle = Solution.JobTitles.GetAll().Where(x => x.IDSAP != null && x.IDSAP == jobTitleSapId).FirstOrDefault();
+ var managers = Solution.Employees.GetAll().Where(x => Solution.JobTitles.Equals(x.JobTitle, jobTitle) && x.Status == Sungero.CoreEntities.DatabookEntry.Status.Active);
+ if (managers.Count() > 1)
+ {
+ var errorMessage = string.Format("Найдено больше одного сотрудника для должности {0}.", jobTitleSapId);
+ Logger.Error(errorMessage);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ else
+ {
+ var manager = managers.FirstOrDefault();
+ if (manager != null)
+ managerSapId = manager.IDSAP;
+ else
+ {
+ var errorMessage = string.Format("Не найдено ни одного сотрудника для должности {0}.", jobTitleSapId);
+ Logger.Error(errorMessage);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(departmentSapId) && !string.IsNullOrWhiteSpace(managerSapId))
+ {
+ var managerData = new DirRX.SapIntegration.Structures.ImportRuleDepartmentManager.DepartmentManager();
+ managerData.DepartmentSapId = departmentSapId;
+ managerData.ManagerSapId = managerSapId;
+ return managerData;
+ }
+ else
+ return null;
+ }
+
+ ///
+ /// Процедура импорта руководителей подразделений.
+ ///
+ /// Структурированный набор данных по импортируемым руководителям подразделений.
+ /// Список структурированных логов.
+ [Remote]
+ public virtual void ImportDepartmentManagers(List
+ items, List logs)
+ {
+ var i = 0;
+ var total = items.Count;
+ foreach (var item in items)
+ {
+ i++;
+ var department = Solution.Departments.GetAll().Where(d => d.IDSAP == item.DepartmentSapId).FirstOrDefault();
+ if (department != null)
+ {
+ var manager = Solution.Employees.Null;
+ if (!string.IsNullOrWhiteSpace(item.ManagerSapId))
+ manager = Solution.Employees.GetAll().Where(x => x.IDSAP == item.ManagerSapId).FirstOrDefault();
+ if (!Solution.Departments.Equals(department.Manager, manager))
+ department.Manager = manager;
+ }
+
+ try
+ {
+ if (department.State.IsChanged)
+ {
+ Logger.DebugFormat("Обновление руководителей подразделений: {0}/{1}", i, total);
+ department.Save();
+ Logger.Debug(string.Format("Заполнение руководителя Подразделения {0}.", department.Name));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при заполнении руководителя Подразделения {0}. Подробности: {1}.", department.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SAPConnector/ImportRuleDepartmentServerFunctions.cs b/SAPConnector/ImportRuleDepartmentServerFunctions.cs
new file mode 100644
index 0000000..988e72d
--- /dev/null
+++ b/SAPConnector/ImportRuleDepartmentServerFunctions.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Sungero.Core;
+using Sungero.CoreEntities;
+using DirRX.SapIntegration.ImportRuleDepartment;
+
+namespace DirRX.SapIntegration.Server
+{
+ partial class ImportRuleDepartmentFunctions
+ {
+ #region Импорт Подразделений из SAP.
+
+ ///
+ /// Выполнить сохранение полученных данных в RX.
+ ///
+ /// Матрица с ответом от SAP.
+ /// Структурированный лог.
+ public override void SaveData(List> response, List logs)
+ {
+ var departments = new List();
+
+ var filteredResponse = response.Where(x => x.Any(y => y.Key == Constants.ImportRuleBase.IsStructuralExist && y.Value == "1"));
+ foreach (var responseItem in filteredResponse)
+ {
+ departments.Add(ParseResponseItem(responseItem, response));
+ }
+
+ if (departments.Any())
+ {
+ var businessUnitSapId = departments.FirstOrDefault().BusinessUnitSapId;
+ ImportDepartments(businessUnitSapId, departments, logs);
+ UpdateDepartamentsHeadOffice(departments, logs);
+ CloseDepartments(businessUnitSapId, departments, logs);
+ }
+ }
+
+ ///
+ /// Обработка строки матрицы с ответом от SAP.
+ ///
+ /// Строка матрицы с ответом от SAP.
+ /// Свойства сущности в структурированном виде.
+ public virtual DirRX.SapIntegration.Structures.ImportRuleDepartment.Department ParseResponseItem(System.Collections.Generic.Dictionary responseItem,
+ List> response)
+ {
+ // Получить ИД ведущего подразделения через связь Pup - Seqnr - Objid.
+ var headOfficeSapId = string.Empty;
+ var itemUpId = responseItem[Constants.ImportRuleBase.SAPStructuralFields.Pup];
+ if (itemUpId != "0")
+ {
+ var itemUp = response.FirstOrDefault(x => x.Any(y => y.Key == Constants.ImportRuleBase.SAPStructuralFields.Seqnr && y.Value == itemUpId));
+ if (itemUp != null)
+ headOfficeSapId = itemUp[Constants.ImportRuleBase.SAPObjectFields.SapID];
+ }
+
+ var department = new DirRX.SapIntegration.Structures.ImportRuleDepartment.Department();
+ department.SapId = responseItem[Constants.ImportRuleBase.SAPObjectFields.SapID];
+ department.Name = responseItem[Constants.ImportRuleBase.SAPObjectFields.Name];
+ department.BusinessUnitSapId = responseItem[Constants.ImportRuleBase.RequestParamOrgUnintSAPID];
+ department.HeadOfficeSapId = headOfficeSapId;
+ // Если данные пришли из SAP то статс записи всегда Действующий. Статус Закрытая будет проставлен для тех записей, которые не пришли из SAP.
+ department.Status = Sungero.Company.Department.Status.Active;
+ return department;
+ }
+
+ ///
+ /// Процедура обновления головного подразделения для подразделений.
+ ///
+ /// Структурированный набор данных по импортируемым подразделениям.
+ [Remote]
+ public void UpdateDepartamentsHeadOffice(List items, List logs)
+ {
+ var i = 0;
+ var total = items.Count;
+ foreach (var item in items)
+ {
+ i++;
+ var department = Solution.Departments.GetAll().Where(x => x.IDSAP == item.SapId).FirstOrDefault();
+ if (department != null)
+ {
+ var headOffice = Solution.Departments.Null;
+ if (!string.IsNullOrWhiteSpace(item.HeadOfficeSapId))
+ headOffice = Solution.Departments.GetAll().Where(d => d.IDSAP == item.HeadOfficeSapId).FirstOrDefault();
+ if (!Solution.Departments.Equals(department.HeadOffice, headOffice))
+ department.HeadOffice = headOffice;
+
+ try
+ {
+ if (department.State.IsChanged)
+ {
+ Logger.DebugFormat("Обновление головного подразделения: {0}/{1}", i, total);
+ department.Save();
+ Logger.Debug(string.Format("Обновление головного подразделения карточки Подразделения {0}", department.Name));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении головного подразделения карточки Подразделения {0}. Подробности: {1}.", department.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Процедура импорта Подразделений.
+ ///
+ /// Код SAP Нашей организации.
+ /// Структурированный набор данных по импортируемым Подразделений.
+ /// Структурированный лог.
+ [Remote]
+ public void ImportDepartments(string businessUnitSapId, List items, List logs)
+ {
+ var businessUnit = Solution.BusinessUnits.GetAll().Where(x => x.IDSAP == businessUnitSapId).FirstOrDefault();
+
+ var i = 0;
+ var total = items.Count;
+ foreach (var item in items)
+ {
+ i++;
+ var department = Solution.Departments.GetAll().Where(x => x.IDSAP == item.SapId).FirstOrDefault();
+ if (department == null)
+ {
+ department = Solution.Departments.Create();
+ department.IDSAP = item.SapId;
+ }
+
+ if (department.Name != item.Name)
+ department.Name = item.Name;
+
+ if (businessUnit != null && !Solution.BusinessUnits.Equals(department.BusinessUnit, businessUnit))
+ department.BusinessUnit = businessUnit;
+
+ if (department.Status != item.Status)
+ department.Status = item.Status;
+
+ try
+ {
+ if (department.State.IsChanged || department.State.IsInserted)
+ {
+ Logger.DebugFormat("Импорт подразделений: {0}/{1}", i, total);
+ department.Save();
+ Logger.Debug(string.Format("Обновление/создание карточки Подразделения {0}.", department.Name));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки Подразделения {0}. Подробности: {1}.", department.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+
+ ///
+ /// Закрыть подразделения, которые не пришли при синхронизации и у которых заполнено свойство IDSAP.
+ ///
+ /// Код SAP Нашей организации.
+ /// Структурированный набор данных по импортируемым Подразделениям.
+ /// Структурированный лог.
+ public void CloseDepartments(string businessUnitSapId, List items, List logs)
+ {
+ var businessUnit = Solution.BusinessUnits.GetAll().Where(x => x.IDSAP == businessUnitSapId).FirstOrDefault();
+
+ // Составить список обработанных подразделений, так как в linq нет трансляции для поиска по списку структур.
+ var updatedDepartmentSapIDs = new List();
+ foreach (var department in items)
+ updatedDepartmentSapIDs.Add(department.SapId);
+
+ // Составить список подразделений не обработанных при синхронизации, так как linq не осилит сравнение через contains несколько тысяч значений без замыкания в ToList.
+ var allDepartmentSapIDs = Solution.Departments.GetAll()
+ .Where(d => d.IDSAP != null
+ && Solution.BusinessUnits.Equals(d.BusinessUnit, businessUnit)
+ && d.Status == Sungero.CoreEntities.DatabookEntry.Status.Active)
+ .Select(d => d.IDSAP)
+ .ToList();
+ var notSynchedDepartments = allDepartmentSapIDs.Where(d => !updatedDepartmentSapIDs.Contains(d)).ToList();
+ var i = 0;
+ var total = notSynchedDepartments.Count;
+ foreach (var departmentSapID in notSynchedDepartments)
+ {
+ i++;
+ var department = Solution.Departments.GetAll()
+ .Where(d => d.IDSAP == departmentSapID
+ && Solution.BusinessUnits.Equals(d.BusinessUnit, businessUnit)
+ && d.Status == Sungero.CoreEntities.DatabookEntry.Status.Active)
+ .FirstOrDefault();
+
+ if (department.Status != Sungero.CoreEntities.DatabookEntry.Status.Closed)
+ department.Status = Sungero.CoreEntities.DatabookEntry.Status.Closed;
+
+ try
+ {
+ if (department.State.IsChanged)
+ {
+ Logger.DebugFormat("Закрытие подразделений: {0}/{1}", i, total);
+ department.Save();
+ Logger.DebugFormat("Закрытие Подразделения {0}.", department.Name);
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки Подразделения {0}. Подробности: {1}.", department.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/SAPConnector/ImportRuleEmployeeServerFunctions.cs b/SAPConnector/ImportRuleEmployeeServerFunctions.cs
new file mode 100644
index 0000000..3e877d6
--- /dev/null
+++ b/SAPConnector/ImportRuleEmployeeServerFunctions.cs
@@ -0,0 +1,355 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Sungero.Core;
+using Sungero.CoreEntities;
+using DirRX.SapIntegration.ImportRuleEmployee;
+
+namespace DirRX.SapIntegration.Server
+{
+ partial class ImportRuleEmployeeFunctions
+ {
+
+ #region Импорт Сотрудников из SAP.
+
+ ///
+ /// Выполнить сохранение полученных данных в RX.
+ ///
+ /// Матрица с ответом от SAP.
+ /// Структурированный лог.
+ public override void SaveData(List> response, List logs)
+ {
+ var employees = new List();
+
+ foreach (var responseItem in response)
+ employees.Add(ParseResponseItem(responseItem));
+
+ if (employees.Any())
+ {
+ var businessUnitSapId = employees.FirstOrDefault().BusinessUnitSapId;
+ ImportEmployees(employees, logs);
+ UpdateEmployeesManager(employees, logs);
+ CloseEmployees(businessUnitSapId, employees, logs);
+ }
+ }
+
+ ///
+ /// Обработка строки матрицы с ответом от SAP.
+ ///
+ /// Строка матрицы с ответом от SAP.
+ /// Свойства сущности в структурированном виде.
+ public virtual DirRX.SapIntegration.Structures.ImportRuleEmployee.Employee ParseResponseItem(System.Collections.Generic.Dictionary responseItem)
+ {
+ var employee = new DirRX.SapIntegration.Structures.ImportRuleEmployee.Employee();
+ employee.SapId = responseItem[Constants.ImportRuleEmployee.SAPFields.SapID];
+ employee.LastName = responseItem[Constants.ImportRuleEmployee.SAPFields.LastName];
+ employee.FirstName = responseItem[Constants.ImportRuleEmployee.SAPFields.FirstName];
+ employee.MiddleName = responseItem[Constants.ImportRuleEmployee.SAPFields.MiddleName];
+ employee.LoginName = responseItem[Constants.ImportRuleEmployee.SAPFields.LoginName];
+ employee.DepartmentSapId = responseItem[Constants.ImportRuleEmployee.SAPFields.DepartmentSapId];
+ employee.JobTitleSapId = responseItem[Constants.ImportRuleEmployee.SAPFields.JobTitleSapId];
+ employee.Email = responseItem[Constants.ImportRuleEmployee.SAPFields.Email];
+ employee.Phone = responseItem[Constants.ImportRuleEmployee.SAPFields.Phone];
+ employee.NameSAP = responseItem[Constants.ImportRuleEmployee.SAPFields.NameSAP];
+ employee.ManagerSapId = responseItem[Constants.ImportRuleEmployee.SAPFields.ManagerSapId];
+ employee.BusinessUnitSapId = responseItem[Constants.ImportRuleBase.RequestParamOrgUnintSAPID];
+ // Если данные пришли из SAP то статс записи всегда Действующий. Статус Закрытая будет проставлен для тех записей, которые не пришли из SAP.
+ employee.Status = Sungero.Company.Employee.Status.Active;
+ return employee;
+ }
+
+ ///
+ /// Процедура импорта Персон.
+ ///
+ /// Сессия.
+ /// Данные по сотруднику в виде структуры.
+ /// Фиксация логов.
+ /// Персона
+ private Sungero.Parties.IPerson ImportPerson(Sungero.Domain.Session session,
+ DirRX.SapIntegration.Structures.ImportRuleEmployee.Employee employee,
+ List logs)
+ {
+ var person = session.GetAll().Where(x => x.IDSAP == employee.SapId)
+ .Select(x => x.Person).FirstOrDefault();
+
+ if (person == null)
+ person = session.Create();
+
+ if (person.LastName != employee.LastName)
+ person.LastName = employee.LastName;
+
+ if (person.FirstName != employee.FirstName)
+ person.FirstName = employee.FirstName;
+
+ if (person.MiddleName != employee.MiddleName)
+ person.MiddleName = employee.MiddleName;
+
+ if (person.Status != employee.Status)
+ person.Status = employee.Status;
+
+ try
+ {
+ if (person.State.IsChanged)
+ {
+ // Либо Save, либо session.SubmitChanges. Сейчас SubmitChanges чтобы не было дублей персон.
+ //person.Save();
+
+ Logger.Debug(string.Format("Обновление/создание карточки персоны {0} {1} {2}.", person.LastName, person.FirstName, person.MiddleName));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки персоны {0} {1} {2}. Подробности: {3}.", person.LastName, person.FirstName, person.MiddleName, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ return person;
+ }
+
+ ///
+ /// Процедура импорта Учетных записей.
+ ///
+ /// Сессия.
+ /// Данные по сотруднику в виде структуры.
+ /// Фиксация логов.
+ /// Логин.
+ private Sungero.CoreEntities.ILogin ImportLogin(Sungero.Domain.Session session,
+ DirRX.SapIntegration.Structures.ImportRuleEmployee.Employee employee,
+ List logs)
+ {
+ if (string.IsNullOrWhiteSpace(employee.LoginName))
+ return Sungero.CoreEntities.Logins.Null;
+
+ var login = session.GetAll().Where(l => l.LoginName == employee.LoginName).FirstOrDefault();
+ if (login == null)
+ {
+ login = session.Create();
+ login.LoginName = employee.LoginName;
+ }
+
+ if (login.TypeAuthentication != Sungero.CoreEntities.Login.TypeAuthentication.Windows)
+ login.TypeAuthentication = Sungero.CoreEntities.Login.TypeAuthentication.Windows;
+
+ try
+ {
+ if (login.State.IsChanged)
+ {
+ // Либо Save, либо session.SubmitChanges. Сейчас SubmitChanges чтобы не было дублей персон.
+ //login.Save();
+ Logger.Debug(string.Format("Обновление/создание карточки учетной записи {0}.", login.LoginName));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки учетной записи {0}. Подробности: {1}.", login.LoginName, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ return login;
+ }
+
+ ///
+ /// Процедура импорта Сотрудников.
+ ///
+ /// Структурированный набор данных по импортируемым Сотрудникам.
+ /// Структурированный лог.
+ ///
+ [Remote]
+ public void ImportEmployees(List items, List logs)
+ {
+ var i = 0;
+ var total = items.Count;
+ foreach (var item in items)
+ {
+ i++;
+ // Транзакция для обхода наведенной ошибки "Транзакция откатилась" при сохранении всех последующих сущностей.
+ Transactions.Execute(
+ () =>
+ {
+ // Сессия для атомарности операции - создать логин, персону и сотрудника, либо не создать ничего. Без сессии создаются дубли персон, так как поиск персоны идет от сотрудника который не создается.
+ using (var session = new Sungero.Domain.Session())
+ {
+ var employee = session.GetAll().Where(x => x.IDSAP == item.SapId).FirstOrDefault();
+ if (employee == null)
+ {
+ employee = session.Create();
+ employee.IDSAP = item.SapId;
+ }
+
+ var person = ImportPerson(session, item, logs);
+ if (person != null && (!Sungero.Parties.People.Equals(employee.Person, person) || !Equals(person.Name, employee.Name)))
+ employee.Person = person;
+
+ var login = ImportLogin(session, item, logs);
+ if (!Sungero.CoreEntities.Logins.Equals(employee.Login, login))
+ employee.Login = login;
+
+ var department = Solution.Departments.GetAll().Where(d => d.IDSAP == item.DepartmentSapId).FirstOrDefault();
+ if (department != null && !Solution.Departments.Equals(employee.Department, department))
+ employee.Department = department;
+
+ var jobTitle = Solution.JobTitles.GetAll().Where(j => j.IDSAP == item.JobTitleSapId).FirstOrDefault();
+ if (jobTitle != null && !Solution.JobTitles.Equals(employee.JobTitle, jobTitle))
+ employee.JobTitle = jobTitle;
+
+ if (employee.NameSAP != item.NameSAP)
+ employee.NameSAP = item.NameSAP;
+
+ if (employee.Phone != item.Phone)
+ employee.Phone = item.Phone;
+
+ if (employee.Status != item.Status)
+ employee.Status = item.Status;
+
+ if (employee.Email != item.Email)
+ employee.Email = item.Email;
+
+ if (string.IsNullOrWhiteSpace(employee.Email))
+ {
+ // Проверки (!= true и != false) нужны, чтобы исключить установку признака изменения сущности (IsChanged).
+ if (employee.NeedNotifyExpiredAssignments != false)
+ employee.NeedNotifyExpiredAssignments = false;
+
+ if (employee.NeedNotifyNewAssignments != false)
+ employee.NeedNotifyNewAssignments = false;
+ }
+
+ try
+ {
+ if (employee.State.IsChanged)
+ {
+ Logger.DebugFormat("Импорт сотрудников: {0}/{1}", i, total);
+ // Либо Save, либо session.SubmitChanges. Сейчас один SubmitChanges (вместо 3 Save 3-х сущностей) чтобы не было дублей персон.
+ //employee.Save();
+ session.SubmitChanges();
+ Logger.Debug(string.Format("Обновление/создание карточки Сотрудника {0}.", employee.Name));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки Сотрудника {0}. Подробности: {1}.", employee.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ });
+ }
+ }
+
+ ///
+ /// Процедура заполнения руководителя у сотрудника.
+ ///
+ /// Структурированный набор данных по импортируемым Сотрудникам.
+ /// Структурированный лог.
+ [Remote]
+ public void UpdateEmployeesManager(List items, List logs)
+ {
+ var i = 0;
+ var total = items.Count;
+ foreach (var item in items)
+ {
+ i++;
+ var employee = Solution.Employees.GetAll().Where(x => x.IDSAP == item.SapId).FirstOrDefault();
+ if (employee != null)
+ {
+ var manager = Solution.Employees.Null;
+ if (!string.IsNullOrWhiteSpace(item.ManagerSapId))
+ manager = Solution.Employees.GetAll().Where(e => e.IDSAP == item.ManagerSapId).FirstOrDefault();
+ if (!Solution.Employees.Equals(employee.Manager, manager))
+ employee.Manager = manager;
+
+ try
+ {
+ if (employee.State.IsChanged)
+ {
+ Logger.DebugFormat("Обновление руководителей сотрудников: {0}/{1}", i, total);
+ employee.Save();
+ Logger.Debug(string.Format("Обновление руководителя Сотрудника {0}.", employee.Name));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении руководителя Сотрудника {0}. Подробности: {1}.", employee.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Закрыть сотрудников, которые не пришли при синхронизации и у которых заполнено свойство IDSAP.
+ ///
+ /// Код SAP Нашей организации.
+ /// Структурированный набор данных по импортируемым Сотрудникам.
+ /// Структурированный лог.
+ public void CloseEmployees(string businessUnitSapId, List items, List logs)
+ {
+ var businessUnit = Solution.BusinessUnits.GetAll().Where(x => x.IDSAP == businessUnitSapId).FirstOrDefault();
+
+ // Составить список обработанных сотрудников, так как в linq нет трансляции для поиска по списку структур.
+ var updatedEmployeeSapIDs = new List();
+ foreach (var employee in items)
+ updatedEmployeeSapIDs.Add(employee.SapId);
+
+ // Составить список сотрудников не обработанных при синхронизации, так как linq не осилит сравнение через contains несколько тысяч значений без замыкания в ToList.
+ var allEmployeeSapIDs = Solution.Employees.GetAll()
+ .Where(e => e.IDSAP != null
+ && Solution.BusinessUnits.Equals(e.Department.BusinessUnit, businessUnit)
+ && e.Status == Sungero.CoreEntities.DatabookEntry.Status.Active)
+ .Select(e => e.IDSAP)
+ .ToList();
+ var notSynchedEmployees = allEmployeeSapIDs.Where(e => !updatedEmployeeSapIDs.Contains(e)).ToList();
+
+ var i = 0;
+ var total = notSynchedEmployees.Count;
+ foreach (var employeeSapID in notSynchedEmployees)
+ {
+ i++;
+ var employee = Solution.Employees.GetAll()
+ .Where(e => e.IDSAP == employeeSapID
+ && Solution.BusinessUnits.Equals(e.Department.BusinessUnit, businessUnit)
+ && e.Status == Sungero.CoreEntities.DatabookEntry.Status.Active)
+ .FirstOrDefault();
+ if (employee != null && !string.IsNullOrWhiteSpace(employee.IDSAP))
+ {
+ if (employee.Status != Sungero.CoreEntities.DatabookEntry.Status.Closed)
+ employee.Status = Sungero.CoreEntities.DatabookEntry.Status.Closed;
+
+ try
+ {
+ if (employee.State.IsChanged)
+ {
+ Logger.DebugFormat("Закрытие сотрудников: {0}/{1}", i, total);
+ employee.Save();
+ Logger.DebugFormat("Закрытие Сотрудника {0}.", employee.Name);
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки Сотрудника {0}. Подробности: {1}.", employee.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+ }
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/SAPConnector/ImportRuleJobTitleServerFunctions.cs b/SAPConnector/ImportRuleJobTitleServerFunctions.cs
new file mode 100644
index 0000000..c1776ba
--- /dev/null
+++ b/SAPConnector/ImportRuleJobTitleServerFunctions.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Sungero.Core;
+using Sungero.CoreEntities;
+using DirRX.SapIntegration.ImportRuleJobTitle;
+
+namespace DirRX.SapIntegration.Server
+{
+ partial class ImportRuleJobTitleFunctions
+ {
+
+ #region Импорт Должностей из SAP.
+
+ ///
+ /// Выполнить сохранение полученных данных в RX.
+ ///
+ /// Матрица с ответом от SAP.
+ /// Структурированный лог.
+ public override void SaveData(List> response, List logs)
+ {
+ var jobTitles = new List();
+
+ foreach (var responseItem in response)
+ jobTitles.Add(ParseResponseItem(responseItem));
+
+ if (jobTitles.Any())
+ ImportJobTitles(jobTitles, logs);
+ }
+
+ ///
+ /// Обработка строки матрицы с ответом от SAP.
+ ///
+ /// Строка матрицы с ответом от SAP.
+ /// Свойства сущности в структурированном виде.
+ public virtual DirRX.SapIntegration.Structures.ImportRuleJobTitle.JobTitle ParseResponseItem(System.Collections.Generic.Dictionary responseItem)
+ {
+ var jobTitle = new DirRX.SapIntegration.Structures.ImportRuleJobTitle.JobTitle();
+ jobTitle.SapId = responseItem[Constants.ImportRuleBase.SAPObjectFields.SapID];
+ jobTitle.Name = responseItem[Constants.ImportRuleBase.SAPObjectFields.Name];
+ return jobTitle;
+ }
+
+ ///
+ /// Процедура импорта Должностей.
+ ///
+ /// Структурированный набор данных по импортируемым Должностям.
+ /// Список структурированных логов.
+ [Remote]
+ public void ImportJobTitles(List items, List logs)
+ {
+ var i = 0;
+ var total = items.Count;
+ foreach (var item in items)
+ {
+ i++;
+ var jobTitle = Solution.JobTitles.GetAll().Where(x => x.IDSAP == item.SapId).FirstOrDefault();
+ if (jobTitle == null)
+ {
+ jobTitle = Solution.JobTitles.Create();
+ jobTitle.IDSAP = item.SapId;
+ }
+
+ if (jobTitle.Name != item.Name)
+ jobTitle.Name = item.Name;
+
+ try
+ {
+ if (jobTitle.State.IsChanged)
+ {
+ Logger.DebugFormat("Импорт должностей: {0}/{1}", i, total);
+ jobTitle.Save();
+ Logger.Debug(string.Format("Обновление/создание карточки Должности {0}.", jobTitle.Name));
+ }
+ }
+ catch (Exception ex)
+ {
+ var errorMessage = string.Format("Ошибка при обновлении карточки Должности {0}. Подробности: {1}.", jobTitle.Name, ex.Message);
+ Logger.Error(errorMessage, ex);
+ logs.Add(DirRX.SapIntegration.Functions.Module.CreateLogItem(_obj.Name,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageLevel.ResponseLevel,
+ DirRX.SapIntegration.Constants.Module.Logging.MessageType.Error,
+ errorMessage));
+ }
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/SAPConnector/README.md b/SAPConnector/README.md
new file mode 100644
index 0000000..4222f59
--- /dev/null
+++ b/SAPConnector/README.md
@@ -0,0 +1,81 @@
+# Шаблоны обработки
+Шаблон с примерами обработки ответов на запросы орг. структуры в SAP. Примеры основаны на коннекторе к SAP.
+
+### Описание кейса, когда можно применить
+1. Обработка ответа от SAP по орг. структуре, полученного с помощью коннектора к SAP.
+2. Синхронизация орг. структуры, когда подготовлены данные в виде словаря и на их основе создаются/обновляются записи в Directum RX.
+
+### Использование
+1. Скопируйте реализованные серверные функции из файлов ImportRuleDepartmentManagerServerFunctions.cs, ImportRuleDepartmentServerFunctions.cs, ImportRuleEmployeeServerFunctions.cs, ImportRuleJobTitleServerFunctions.cs в серверные функции соответствующих записей коннектора к SAP.
+2. Скорректируйте функции, т.к. перечень реквизитов из примера может отличаться от, тех, что есть у Вас. В первую очередь функции ParseResponseItem, ImportPerson, ImportLogin, ImportEmployees, ImportDepartments.
+
+### Примеры запросов в SAP
+
+#### Должности/Подразделения
+```
+POST http://appm.local:8005/sap/bc/srt/rfc/sap/zws_hr_orgunitext_data_get/200/zws_hr_orgunitext_data_get/zws_hr_orgunitext_data_get
+```
+
+```
+
+
+
+
+ 2020-07-09
+ 50000708
+ 01
+ O
+ PLSTE
+ MDT1
+
+
+
+```
+
+#### Руководители подразделений
+
+```
+POST http://appm.local:8005/sap/bc/srt/rfc/sap/zws_hr_orgunitext_data_get/200/zws_hr_orgunitext_data_get/zws_hr_orgunitext_data_get
+```
+
+```
+
+
+
+
+
+
+
+
+
+
+ 2020-06-16
+
+ O
+ 50000708
+
+ 01
+
+ ORGC
+
+
+
+```
+
+#### Сотрудники
+
+```
+POST https://appm.local:1443/sap/bc/srt/rfc/sap/zws_hr_sync_edoc_manage/200/zws_hr_sync_edoc_manage/edoc
+```
+
+```
+
+
+
+
+ 2020-06-16
+ 50000708
+
+
+
+```