1 package fr.ifremer.dali.dao.system.rule;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import com.google.common.collect.Lists;
27 import com.google.common.collect.Sets;
28 import fr.ifremer.dali.config.DaliConfiguration;
29 import fr.ifremer.dali.dao.administration.user.DaliDepartmentDao;
30 import fr.ifremer.dali.dao.referential.DaliReferentialDao;
31 import fr.ifremer.dali.dao.technical.Daos;
32 import fr.ifremer.dali.dto.DaliBeanFactory;
33 import fr.ifremer.dali.dto.DaliBeans;
34 import fr.ifremer.dali.dto.configuration.control.PreconditionRuleDTO;
35 import fr.ifremer.dali.dto.configuration.control.RuleListDTO;
36 import fr.ifremer.dali.dto.referential.DepartmentDTO;
37 import fr.ifremer.dali.service.administration.program.ProgramStrategyService;
38 import fr.ifremer.dali.service.system.SystemService;
39 import fr.ifremer.quadrige3.core.dao.administration.program.ProgramImpl;
40 import fr.ifremer.quadrige3.core.dao.administration.user.DepartmentImpl;
41 import fr.ifremer.quadrige3.core.dao.administration.user.Quser;
42 import fr.ifremer.quadrige3.core.dao.administration.user.QuserImpl;
43 import fr.ifremer.quadrige3.core.dao.referential.StatusCode;
44 import fr.ifremer.quadrige3.core.dao.referential.StatusImpl;
45 import fr.ifremer.quadrige3.core.dao.system.rule.RuleList;
46 import fr.ifremer.quadrige3.core.dao.system.rule.RuleListDaoImpl;
47 import fr.ifremer.quadrige3.core.dao.technical.Assert;
48 import fr.ifremer.quadrige3.core.dao.technical.Beans;
49 import fr.ifremer.quadrige3.core.dao.technical.Dates;
50 import fr.ifremer.quadrige3.core.security.SecurityContextHelper;
51 import org.apache.commons.collections4.CollectionUtils;
52 import org.apache.commons.lang3.mutable.MutableBoolean;
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55 import org.hibernate.SessionFactory;
56 import org.hibernate.type.StringType;
57 import org.springframework.beans.factory.annotation.Autowired;
58 import org.springframework.dao.DataRetrievalFailureException;
59 import org.springframework.stereotype.Repository;
60
61 import javax.annotation.Resource;
62 import java.time.LocalDate;
63 import java.util.*;
64
65
66
67
68
69
70 @Repository("daliRuleListDao")
71 public class DaliRuleListDaoImpl extends RuleListDaoImpl implements DaliRuleListDao {
72
73 private static final Log log = LogFactory.getLog(DaliRuleListDaoImpl.class);
74
75 @Resource
76 protected DaliConfiguration config;
77
78 @Resource(name = "daliRuleDao")
79 private DaliRuleDao ruleDao;
80
81 @Resource(name = "daliDepartmentDao")
82 protected DaliDepartmentDao departmentDao;
83
84 @Resource(name = "daliProgramStrategyService")
85 private ProgramStrategyService programStrategyService;
86
87 @Resource(name = "daliReferentialDao")
88 protected DaliReferentialDao referentialDao;
89
90 @Resource(name = "daliSystemService")
91 protected SystemService systemService;
92
93
94
95
96
97
98 @Autowired
99 public DaliRuleListDaoImpl(SessionFactory sessionFactory) {
100 super(sessionFactory);
101 }
102
103
104
105
106 @Override
107 public List<RuleListDTO> getAllRuleLists() {
108
109 Iterator<Object[]> it = queryIterator("allRuleList");
110 return toRuleListDTOs(it);
111 }
112
113 @Override
114 public List<RuleListDTO> getRuleListsForProgram(String programCode) {
115
116 Iterator<Object[]> it = queryIterator("allRuleListWithProgramCode",
117 "programCode", StringType.INSTANCE, programCode);
118 return toRuleListDTOs(it);
119 }
120
121 @Override
122 public boolean ruleListExists(String ruleListCode) {
123 Assert.notBlank(ruleListCode);
124
125 return queryUnique("ruleListByCode", "ruleListCode", StringType.INSTANCE, ruleListCode) != null;
126 }
127
128
129
130
131 @Override
132 public RuleListDTO getRuleList(String ruleListCode) {
133 Assert.notBlank(ruleListCode);
134
135 Object[] source = queryUnique("ruleListByCode", "ruleListCode", StringType.INSTANCE, ruleListCode);
136
137 if (source == null) {
138 throw new DataRetrievalFailureException("can't load rule list with code = " + ruleListCode);
139 }
140 TimeZone dbTimezone = config.getDbTimezone();
141 RuleListDTO result = toRuleListDTO(Arrays.asList(source).iterator(), dbTimezone);
142
143 if (fillAndValidRuleList(result,
144 programStrategyService.getManagedProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId()),
145 programStrategyService.getWritableProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId())))
146
147 return result;
148
149 return null;
150 }
151
152
153
154
155 @Override
156 public void saveRuleList(RuleListDTO source, Integer quserId) {
157 Assert.notNull(source);
158 Assert.notNull(source.getCode());
159 Assert.notNull(quserId);
160
161 RuleList target = get(source.getCode());
162 boolean isNew = false;
163 if (target == null) {
164 target = RuleList.Factory.newInstance();
165 target.setRuleListCd(source.getCode());
166 isNew = true;
167 }
168
169
170 beanToEntity(source, target, quserId, config.getDbTimezone());
171
172
173 if (isNew) {
174 getSession().save(target);
175 } else {
176 getSession().update(target);
177 }
178
179
180 final List<String> rulesCdsToRemove = DaliBeans.collectProperties(target.getRules(), "ruleCd");
181
182
183 if (CollectionUtils.isNotEmpty(source.getControlRules())) {
184 source.getControlRules().forEach(controlRule -> {
185 ruleDao.save(controlRule, source.getCode());
186 rulesCdsToRemove.remove(controlRule.getCode());
187 if (!controlRule.isPreconditionsEmpty()) {
188 for (PreconditionRuleDTO preconditionRule : controlRule.getPreconditions()) {
189 rulesCdsToRemove.remove(preconditionRule.getBaseRule().getCode());
190 rulesCdsToRemove.remove(preconditionRule.getUsedRule().getCode());
191 }
192 }
193 });
194 }
195
196 getSession().flush();
197 getSession().clear();
198
199
200 if (CollectionUtils.isNotEmpty(rulesCdsToRemove)) {
201 ruleDao.removeByCds(Beans.asStringArray(rulesCdsToRemove));
202
203 getSession().flush();
204 getSession().clear();
205 }
206 }
207
208 private boolean fillAndValidRuleList(RuleListDTO ruleList, Set<String> managedProgramCodes, Set<String> writableProgramCodes) {
209
210
211 List<String> programCodes = getProgramCodesByRuleListCode(ruleList.getCode());
212
213 if (CollectionUtils.isEmpty(programCodes)) {
214
215 return false;
216 }
217
218
219 boolean canRead = programCodes.stream().anyMatch(writableProgramCodes::contains);
220 if (!canRead) {
221 if (log.isWarnEnabled()) {
222 log.warn(String.format("The rule list '%s' is not compatible (writable program list is empty for the current user)", ruleList.getCode()));
223 }
224
225 return false;
226 }
227
228
229 boolean canWrite = managedProgramCodes.containsAll(programCodes);
230 ruleList.setReadOnly(!canWrite);
231
232
233 ruleList.setPrograms(programStrategyService.getProgramsByCodes(programCodes));
234
235
236 ruleList.setDepartments(getControlledDepartmentsByRuleListCode(ruleList.getCode()));
237
238
239
240
241
242
243
244
245
246
247
248 MutableBoolean incompatibleRule = new MutableBoolean(false);
249 ruleList.addAllControlRules(ruleDao.getRulesByRuleListCode(ruleList.getCode(), false , incompatibleRule));
250
251
252 ruleList.addAllControlRules(ruleDao.getPreconditionedRulesByRuleListCode(ruleList.getCode(), false, incompatibleRule));
253
254
255 if (ruleList.isControlRulesEmpty()) {
256 if (log.isWarnEnabled()) {
257 log.warn(String.format("The rule list '%s' is not compatible (rule list is empty)", ruleList.getCode()));
258 }
259
260 return false;
261 }
262
263 if (incompatibleRule.booleanValue()) ruleList.setReadOnly(true);
264
265 return true;
266 }
267
268 private List<String> getProgramCodesByRuleListCode(String ruleListCode) {
269 Assert.notBlank(ruleListCode);
270
271 return queryListTyped("programCodesByRuleListCode",
272 "ruleListCode", StringType.INSTANCE, ruleListCode);
273 }
274
275
276
277
278
279
280
281 private List<DepartmentDTO> getControlledDepartmentsByRuleListCode(String ruleListCode) {
282 Assert.notBlank(ruleListCode);
283
284 List<Integer> depIds = queryListTyped("departmentIdsByRuleListCode",
285 "ruleListCode", StringType.INSTANCE, ruleListCode);
286
287 List<DepartmentDTO> result = Lists.newArrayList();
288 if (CollectionUtils.isNotEmpty(depIds)) {
289 for (Integer depId : new HashSet<>(depIds)) {
290 result.add(departmentDao.getDepartmentById(depId));
291 }
292 }
293 return result;
294 }
295
296
297
298 private List<RuleListDTO> toRuleListDTOs(Iterator<Object[]> it) {
299
300 Set<String> managedProgramCodes = programStrategyService.getManagedProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId());
301 Set<String> writableProgramCodes = programStrategyService.getWritableProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId());
302
303 TimeZone dbTimezone = config.getDbTimezone();
304 List<RuleListDTO> result = Lists.newArrayList();
305 while (it.hasNext()) {
306 Object[] source = it.next();
307 RuleListDTO ruleList = toRuleListDTO(Arrays.asList(source).iterator(), dbTimezone);
308
309 if (fillAndValidRuleList(ruleList, managedProgramCodes, writableProgramCodes))
310
311 result.add(ruleList);
312 }
313
314 return result;
315 }
316
317 private RuleListDTO toRuleListDTO(Iterator<Object> source, TimeZone dbTimezone) {
318 RuleListDTO result = DaliBeanFactory.newRuleListDTO();
319 result.setCode((String) source.next());
320 result.setActive(Daos.safeConvertToBoolean(source.next()));
321
322 LocalDate startDate = Dates.convertToLocalDate(Daos.convertToDate(source.next()), dbTimezone);
323 result.setStartMonth(
324 Optional.ofNullable(startDate)
325 .map(LocalDate::getMonthValue)
326 .flatMap(month -> systemService.getMonths().stream().filter(monthDTO -> monthDTO.getId().equals(month)).findFirst())
327 .orElse(null)
328 );
329
330 LocalDate endDate = Dates.convertToLocalDate(Daos.convertToDate(source.next()), dbTimezone);
331 result.setEndMonth(
332 Optional.ofNullable(endDate)
333 .map(LocalDate::getMonthValue)
334 .flatMap(month -> systemService.getMonths().stream().filter(monthDTO -> monthDTO.getId().equals(month)).findFirst())
335 .orElse(null)
336 );
337
338 result.setDescription((String) source.next());
339 result.setStatus(referentialDao.getStatusByCode((String) source.next()));
340 result.setCreationDate(Daos.convertToDate(source.next()));
341 result.setUpdateDate(Daos.convertToDate(source.next()));
342
343 return result;
344 }
345
346 private void beanToEntity(RuleListDTO source, RuleList target, int quserId, TimeZone dbTimezone) {
347
348 target.setRuleListDc(source.getDescription());
349 target.setRuleListIsActive(Daos.convertToString(source.isActive()));
350
351
352 LocalDate startDate = Optional.ofNullable(source.getStartMonth())
353 .map(monthDTO -> LocalDate.of(LocalDate.now().getYear(), monthDTO.getId(), 1))
354 .orElse(null);
355 target.setRuleListFirstMonth(Dates.convertToDate(startDate, dbTimezone));
356
357 LocalDate endDate = Optional.ofNullable(source.getEndMonth())
358 .map(monthDTO -> LocalDate.of(LocalDate.now().getYear(), monthDTO.getId(), 1).plusMonths(1).minusDays(1))
359 .orElse(null);
360 target.setRuleListLastMonth(Dates.convertToDate(endDate, dbTimezone));
361
362
363
364
365
366 if (target.getRuleListCreationDt() == null) {
367 target.setRuleListCreationDt(newCreateDate());
368 }
369
370
371 if (CollectionUtils.isEmpty(target.getQusers())) {
372
373 Quser quser = load(QuserImpl.class, quserId);
374 target.setQusers(Sets.newHashSet(quser));
375 } else if (DaliBeans.findByProperty(target.getQusers(), "quserId", quserId) == null) {
376
377 Quser quser = load(QuserImpl.class, quserId);
378 target.getQusers().add(quser);
379 }
380
381
382 if (CollectionUtils.isEmpty(target.getRespDepartments())) {
383
384 Quser quser = load(QuserImpl.class, quserId);
385 target.setRespDepartments(Sets.newHashSet(quser.getDepartment()));
386 } else {
387
388 Quser quser = load(QuserImpl.class, quserId);
389 if (DaliBeans.findByProperty(target.getRespDepartments(), "depId", quser.getDepartment().getDepId()) == null) {
390 target.getRespDepartments().add(quser.getDepartment());
391 }
392 }
393
394
395 if (source.getPrograms() == null) {
396 target.getPrograms().clear();
397 } else {
398 Daos.replaceEntities(target.getPrograms(),
399 source.getPrograms(),
400 vo -> load(ProgramImpl.class, Objects.requireNonNull(vo).getCode()));
401 }
402
403
404
405
406 if (source.getDepartments() == null) {
407 target.getControledDepartments().clear();
408 } else {
409 Daos.replaceEntities(target.getControledDepartments(),
410 source.getDepartments(),
411 vo -> load(DepartmentImpl.class, Objects.requireNonNull(vo).getId()));
412 }
413
414
415 target.setStatus(load(StatusImpl.class, StatusCode.ENABLE.getValue()));
416
417 }
418
419 }