AppendixNotificationPlugin.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. using GreenTree.Nachtragsmanagement.Core.Plugins;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Web;
  6. using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
  7. using GreenTree.Nachtragsmanagement.Services.User;
  8. using GreenTree.Nachtragsmanagement.Services.Configuration;
  9. using GreenTree.Nachtragsmanagement.Services.Appendix;
  10. using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
  11. using System.Net.Mail;
  12. using System.Net;
  13. using System.Globalization;
  14. using Quartz;
  15. using GreenTree.Nachtragsmanagement.Services.Logging;
  16. using Autofac;
  17. using GreenTree.Nachtragsmanagement.Core;
  18. namespace GreenTree.Nachtragsmanagement.Web.Implementations
  19. {
  20. public class AppendixNotificationPlugin : INotificationPlugin, IJob
  21. {
  22. #region Services
  23. private readonly IUserService _userService;
  24. private readonly IConfigurationService _configurationService;
  25. private readonly IAppendixService _appendixService;
  26. private readonly IServiceLogger _logger;
  27. #endregion
  28. #region Properties
  29. /// <summary>
  30. /// Id
  31. /// </summary>
  32. public Guid Id
  33. {
  34. get
  35. {
  36. return Guid.Parse("E99CA4A1-B3A9-4AA6-BBAD-9254CEED0A45");
  37. }
  38. }
  39. /// <summary>
  40. /// System name
  41. /// </summary>
  42. public string SystemName
  43. {
  44. get
  45. {
  46. return "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin";
  47. }
  48. }
  49. /// <summary>
  50. /// List of available notification jobs
  51. /// </summary>
  52. public List<NotificationJob> AvailableNotificationJobs
  53. {
  54. get
  55. {
  56. return new List<NotificationJob>
  57. {
  58. new NotificationJob
  59. (
  60. Guid.Parse("2F3642E0-259D-466D-8629-CB279F740313"),
  61. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate",
  62. "Verhandlungstermine überprüfen",
  63. "Erstellt automatisch Benachrichtigungen für Nachträge, die nach 8 Wochen nach Einreichung noch keinen " +
  64. "Verhandlungstermin gesetzt haben und ändert den entsprechenden Status ab"
  65. ),
  66. new NotificationJob
  67. (
  68. Guid.Parse("2E46B32A-1912-47F9-951E-C8188AA9BA50"),
  69. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationProtocol",
  70. "Verhandlungsprotokolle überprüfen",
  71. "Erstellt automatisch Benachrichtigungen für Nachträge, die nach 2 Wochen zwar verhandelt sind, " +
  72. "jedoch noch kein Protokoll aufweisen."
  73. ),
  74. new NotificationJob
  75. (
  76. Guid.Parse("B39FB44A-6E57-458C-A403-2C4FA7581F15"),
  77. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationOrder",
  78. "Bestellnummern überprüfen",
  79. "Erstellt automatisch Benachrichtigungen für Nachträge, die nach 2 Wochen zwar verhandelt sind, " +
  80. "jedoch noch keine Bestellnummer aufweisen."
  81. )
  82. };
  83. }
  84. }
  85. /// <summary>
  86. /// Displayed name
  87. /// </summary>
  88. public string Name
  89. {
  90. get
  91. {
  92. return "Nachtragsbenachrichtigung";
  93. }
  94. }
  95. /// <summary>
  96. /// Further description on how this plugin works
  97. /// </summary>
  98. public string Description
  99. {
  100. get
  101. {
  102. return
  103. "Erstellt automatisch Benachrichtigungen für Nachträge, die nach 8 Wochen nach Einreichung noch keinen " +
  104. "Verhandlungstermin gesetzt haben und ändert den entsprechenden Status ab. Außerdem werden alle 2 Wochen " +
  105. "Benachrichtigungen für Nachträge erstellt, die zwar verhandelt sind, jedoch noch kein Protokoll aufweisen oder " +
  106. "keine Bestellnummer aufweisen.";
  107. }
  108. }
  109. #endregion
  110. /// <summary>
  111. /// Initializes a new instance of the AppendixNotificationPlugin class
  112. /// </summary>
  113. public AppendixNotificationPlugin() { }
  114. /// <summary>
  115. /// Initializes a new instance of the AppendixNotificationPlugin class
  116. /// </summary>
  117. public AppendixNotificationPlugin(
  118. IUserService userService,
  119. IConfigurationService configurationService,
  120. IAppendixService appendixService,
  121. IServiceLogger logger)
  122. {
  123. _userService = userService;
  124. _configurationService = configurationService;
  125. _appendixService = appendixService;
  126. _logger = logger;
  127. }
  128. #region Quartz implmentation
  129. /// <summary>
  130. /// Executes the current job
  131. /// </summary>
  132. /// <param name="context"></param>
  133. public void Execute(IJobExecutionContext context)
  134. {
  135. if (!context.JobDetail.JobDataMap.ContainsKey("MailNotifications"))
  136. return;
  137. var mailNotifications = context.JobDetail.JobDataMap.Get("MailNotifications") as IEnumerable<MailNotification>;
  138. ProcessNotifications(mailNotifications);
  139. }
  140. #endregion
  141. #region Processing
  142. /// <summary>
  143. /// Process all mail notifications registered for that plugin
  144. /// </summary>
  145. /// <param name="mailNotifications">The notifications which shall be generated.</param>
  146. public void ProcessNotifications(IEnumerable<MailNotification> mailNotifications)
  147. {
  148. if (mailNotifications == null || (mailNotifications != null && !mailNotifications.Any())) return;
  149. _logger.Information(
  150. String.Format(
  151. "Starte Verarbeitung Mail-Benachrichtigung in \"{0}\" für \"{1} ...",
  152. SystemName, mailNotifications.First().NotificationJobSystemName));
  153. foreach (var notification in mailNotifications)
  154. {
  155. try
  156. {
  157. switch (notification.NotificationJobSystemName)
  158. {
  159. case "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate":
  160. {
  161. ProcessNegotiationDateNotification(notification);
  162. }
  163. break;
  164. case "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationProtocol":
  165. {
  166. ProcessNegotiationProtocolNotification(notification);
  167. }
  168. break;
  169. case "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationOrder":
  170. {
  171. ProcessNegotiationOrderNotification(notification);
  172. }
  173. break;
  174. default:
  175. continue;
  176. }
  177. }
  178. catch (Exception ex)
  179. {
  180. _logger.Error(
  181. String.Format(
  182. "Fehler bei Mail-Benachrichtigung in \"{0}\" für \"{1}", SystemName, notification.NotificationJobSystemName), ex);
  183. continue;
  184. }
  185. }
  186. _logger.Information(
  187. String.Format(
  188. "Verarbeitung Mail-Benachrichtigung in \"{0}\" für \"{1} erfolgreich abgeschlossen!",
  189. SystemName, mailNotifications.First().NotificationJobSystemName));
  190. }
  191. /// <summary>
  192. /// Sets the corresponding status for appendices which offering date is older than N weeks and notifies
  193. /// the correspondig recipients
  194. /// </summary>
  195. /// <param name="mailNotification">The notification which shall be generated.</param>
  196. private void ProcessNegotiationDateNotification(MailNotification mailNotification)
  197. {
  198. var ageDays = _configurationService.TryGetConfigItemValue<int>(
  199. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.AgeDays", 56);
  200. var stateSetId = _configurationService.TryGetConfigItemValue<int[]>(
  201. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.StateSet", new[] { 13 })[0];
  202. var stateConditionIds = _configurationService.TryGetConfigItemValue<int[]>(
  203. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.StateCondition", new[] { 1, 5, 6, 12 });
  204. var interval = _configurationService.TryGetConfigItemValue<int>(
  205. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.Interval", 2);
  206. interval = interval == 0
  207. ? 1
  208. : interval;
  209. var finishStateId = _appendixService.GetFinishState().Id;
  210. var appendices = _appendixService.GetAllAppendices()
  211. .Where(a => a.OfferingDate.HasValue &&
  212. (DateTime.Now - a.OfferingDate).Value.Days >= ageDays &&
  213. a.StateId != stateSetId && a.StateId != finishStateId &&
  214. stateConditionIds.Contains(a.StateId.Value) && a.NegotiationDate == null)
  215. .ToList();
  216. var currentCalendarWeek = GetCalendarWeek(DateTime.Now);
  217. appendices = appendices
  218. .Where(a => (currentCalendarWeek - GetCalendarWeek(a.OfferingDate.Value) % interval == 0) ||
  219. (currentCalendarWeek - GetCalendarWeek(a.OfferingDate.Value) < 0))
  220. .OrderBy(a => a.Site == null
  221. ? String.Empty
  222. : a.Site.CustomNumber)
  223. .ThenBy(a => a.CustomNumber)
  224. .ToList();
  225. if (appendices.Any())
  226. {
  227. var mailBody = GenerateNegotiationDateMailBody(appendices);
  228. foreach (var appendix in appendices)
  229. {
  230. appendix.StateId = stateSetId;
  231. _appendixService.UpdateAppendix(appendix);
  232. }
  233. SendNotification(mailNotification, "Autom. Übersicht nicht verhandelte Nachträge", mailBody);
  234. }
  235. }
  236. /// <summary>
  237. /// Sets the corresponding status for appendices which negotiation date is older than N weeks and notifies
  238. /// the correspondig recipients when the protocol does not exist
  239. /// </summary>
  240. /// <param name="mailNotification">The notification which shall be generated.</param>
  241. private void ProcessNegotiationProtocolNotification(MailNotification mailNotification)
  242. {
  243. var ageDays = _configurationService.TryGetConfigItemValue<int>(
  244. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationProtocol.AgeDays", 14);
  245. var interval = _configurationService.TryGetConfigItemValue<int>(
  246. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationProtocol.Interval", 2);
  247. interval = interval == 0
  248. ? 1
  249. : interval;
  250. var finishStateId = _appendixService.GetFinishState().Id;
  251. var appendices = _appendixService.GetAllAppendices()
  252. .Where(a => a.NegotiationDate.HasValue &&
  253. (DateTime.Now - a.NegotiationDate).Value.Days >= ageDays &&
  254. !a.ProtocolExists && a.StateId != finishStateId)
  255. .ToList();
  256. var currentCalendarWeek = GetCalendarWeek(DateTime.Now);
  257. appendices = appendices
  258. .Where(a => (currentCalendarWeek - GetCalendarWeek(a.OfferingDate.Value) % interval == 0) ||
  259. (currentCalendarWeek - GetCalendarWeek(a.OfferingDate.Value) < 0))
  260. .OrderBy(a => a.Site == null
  261. ? String.Empty
  262. : a.Site.CustomNumber)
  263. .ThenBy(a => a.CustomNumber)
  264. .ToList();
  265. if (appendices.Any())
  266. {
  267. var mailBody = GenerateNegotiationProtocolMailBody(appendices);
  268. SendNotification(mailNotification, "Autom. Übersicht verhandelte Nachträge o. Protokoll", mailBody);
  269. }
  270. }
  271. /// <summary>
  272. /// Sets the corresponding status for appendices which negotiation date is older than N weeks and notifies
  273. /// the correspondig recipients when the order number is still missing
  274. /// </summary>
  275. /// <param name="mailNotification">The notification which shall be generated.</param>
  276. private void ProcessNegotiationOrderNotification(MailNotification mailNotification)
  277. {
  278. var ageDays = _configurationService.TryGetConfigItemValue<int>(
  279. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationOrder.AgeDays", 14);
  280. var interval = _configurationService.TryGetConfigItemValue<int>(
  281. "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationOrder.Interval", 2);
  282. interval = interval == 0
  283. ? 1
  284. : interval;
  285. var finishStateId = _appendixService.GetFinishState().Id;
  286. var appendices = _appendixService.GetAllAppendices()
  287. .Where(a => a.NegotiationDate.HasValue &&
  288. (DateTime.Now - a.NegotiationDate).Value.Days >= ageDays &&
  289. String.IsNullOrEmpty(a.OrderNumber) && a.StateId != finishStateId)
  290. .ToList();
  291. var currentCalendarWeek = GetCalendarWeek(DateTime.Now);
  292. appendices = appendices
  293. .Where(a => (currentCalendarWeek - GetCalendarWeek(a.OfferingDate.Value) % interval == 0) ||
  294. (currentCalendarWeek - GetCalendarWeek(a.OfferingDate.Value) < 0))
  295. .OrderBy(a => a.Site == null
  296. ? String.Empty
  297. : a.Site.CustomNumber)
  298. .ThenBy(a => a.CustomNumber)
  299. .ToList();
  300. if (appendices.Any())
  301. {
  302. var mailBody = GenerateNegotiationOrderMailBody(appendices);
  303. SendNotification(mailNotification, "Autom. Übersicht verhandelte Nachträge o. Bestellnummer", mailBody);
  304. }
  305. }
  306. #endregion
  307. #region Mail body generation
  308. /// <summary>
  309. /// Generates a mail body with a list of all appendices with a offering date later than N weeks
  310. /// </summary>
  311. /// <param name="appendices">Appendices matching that criteria.</param>
  312. public string GenerateNegotiationDateMailBody(IEnumerable<Appendix> appendices)
  313. {
  314. var template =
  315. "<html>" +
  316. " <body>" +
  317. " <h3>Übersicht \"Nicht verhandelte Nachträge\"</h3>" +
  318. " <p>Folgende Nachträge haben ein Einreichdatum älter als 8 Wochen, jedoch noch kein Verhandlungstermin" +
  319. " gesetzt:</p>" +
  320. " <ul>" +
  321. " {0}" +
  322. " </ul>" +
  323. " </body>" +
  324. "</html>";
  325. if (!appendices.Any()) return String.Format(template, String.Empty);
  326. var appendicesList = String.Empty;
  327. foreach (var appendix in appendices)
  328. {
  329. appendicesList += String.Format(
  330. "<li>Baustelle <b>\"{0}\"</b> - Nachtrag <b>\"{1}\"</b> - Einreichdatum: <b>{2:dd.MM.yyyy}</b></i>",
  331. appendix.Site == null
  332. ? String.Empty
  333. : appendix.Site.CustomNumber,
  334. appendix.CustomNumber,
  335. appendix.OfferingDate);
  336. }
  337. return String.Format(template, appendicesList);
  338. }
  339. /// <summary>
  340. /// Generates a mail body with a list of all appendices with a negotiation date later than N weeks and not existing protocol
  341. /// </summary>
  342. /// <param name="appendices">Appendices matching that criteria.</param>
  343. public string GenerateNegotiationProtocolMailBody(IEnumerable<Appendix> appendices)
  344. {
  345. var template =
  346. "<html>" +
  347. " <body>" +
  348. " <h3>Übersicht \"Verhandelte Nachträge ohne Protokoll\"</h3>" +
  349. " <p>Folgende Nachträge haben ein Verhandlungstermin älter als zwei Wochen, jedoch noch kein Protokoll:" +
  350. " <ul>" +
  351. " {0}" +
  352. " </ul>" +
  353. " </body>" +
  354. "</html>";
  355. if (!appendices.Any()) return String.Format(template, String.Empty);
  356. var appendicesList = String.Empty;
  357. foreach (var appendix in appendices)
  358. {
  359. appendicesList += String.Format(
  360. "<li>Baustelle <b>\"{0}\"</b> - Nachtrag <b>\"{1}\"</b> - Verhandlungsdatum: <b>{2:dd.MM.yyyy}</b></i>",
  361. appendix.Site == null
  362. ? String.Empty
  363. : appendix.Site.CustomNumber,
  364. appendix.CustomNumber,
  365. appendix.NegotiationDate);
  366. }
  367. return String.Format(template, appendicesList);
  368. }
  369. /// <summary>
  370. /// Generates a mail body with a list of all appendices with a negotiation date later than N weeks and not existing order number
  371. /// </summary>
  372. /// <param name="appendices">Appendices matching that criteria.</param>
  373. public string GenerateNegotiationOrderMailBody(IEnumerable<Appendix> appendices)
  374. {
  375. var template =
  376. "<html>" +
  377. " <body>" +
  378. " <h3>Übersicht \"Verhandelte Nachträge ohne Bestellnummer\"</h3>" +
  379. " <p>Folgende Nachträge haben ein Verhandlungstermin älter als zwei Wochen, jedoch noch keine Bestellnummer:" +
  380. " <ul>" +
  381. " {0}" +
  382. " </ul>" +
  383. " </body>" +
  384. "</html>";
  385. if (!appendices.Any()) return String.Format(template, String.Empty);
  386. var appendicesList = String.Empty;
  387. foreach (var appendix in appendices)
  388. {
  389. appendicesList += String.Format(
  390. "<li> Baustelle <b>\"{1}\"</b> - Nachtrag <b>\"{0}\"</b> - Verhandlungsdatum: <b>{2:dd.MM.yyyy}</b></i>",
  391. appendix.Site == null
  392. ? String.Empty
  393. : appendix.Site.CustomNumber,
  394. appendix.CustomNumber,
  395. appendix.NegotiationDate);
  396. }
  397. return String.Format(template, appendicesList);
  398. }
  399. #endregion
  400. #region Mail sending
  401. /// <summary>
  402. /// Sends a generated mail body to the specified recipients in the mail notification
  403. /// </summary>
  404. /// <param name="mailNotification">The mail notification.</param>
  405. /// <param name="subject">The mail subject.</param>
  406. /// <param name="body">The mail body.</param>
  407. public void SendNotification(MailNotification mailNotification, string subject, string body)
  408. {
  409. if (mailNotification == null) return;
  410. var mailServerConfig = _configurationService.GetCurrentConfiguration().MailServerElement;
  411. var smptClient = new SmtpClient(mailServerConfig.SmtpServer, mailServerConfig.Port)
  412. {
  413. EnableSsl = mailServerConfig.UseSsl,
  414. Credentials = new NetworkCredential(
  415. mailServerConfig.Username,
  416. mailServerConfig.Password,
  417. mailServerConfig.Domain)
  418. };
  419. var recipients =
  420. mailNotification.Users
  421. .Select(u => u.MailAddress);
  422. var mailMessage = new MailMessage
  423. {
  424. IsBodyHtml = true,
  425. Subject = subject,
  426. Body = body,
  427. From = new MailAddress("Nachtragsbenachrichtigung@schweerbau.de")
  428. };
  429. foreach (var recipient in recipients)
  430. mailMessage.To.Add(recipient);
  431. smptClient.Send(mailMessage);
  432. }
  433. #endregion
  434. #region Helper
  435. /// <summary>
  436. /// Determines the calendar week of a specific datetime
  437. /// </summary>
  438. /// <param name="date">The datetime which calendarweek should be calculated.</param>
  439. public static int GetCalendarWeek(DateTime date)
  440. {
  441. var currentCulture = CultureInfo.CurrentCulture;
  442. var calendar = currentCulture.Calendar;
  443. var calendarWeek = calendar.GetWeekOfYear(
  444. date,
  445. currentCulture.DateTimeFormat.CalendarWeekRule,
  446. currentCulture.DateTimeFormat.FirstDayOfWeek);
  447. if (calendarWeek > 52)
  448. {
  449. date = date.AddDays(7);
  450. var testCalendarWeek = calendar.GetWeekOfYear(date,
  451. currentCulture.DateTimeFormat.CalendarWeekRule,
  452. currentCulture.DateTimeFormat.FirstDayOfWeek);
  453. if (testCalendarWeek == 2)
  454. calendarWeek = 1;
  455. }
  456. return calendarWeek;
  457. }
  458. #endregion
  459. }
  460. }