add and remove work times

This commit is contained in:
Jörg Henke
2022-05-05 18:02:03 +02:00
parent 25b0288f3e
commit 707707f7b9
7 changed files with 303 additions and 15 deletions

View File

@ -32,6 +32,10 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
private TModuleRecord module;
private TJobRecord activity;
private TBillingRecord billing;
private Integer fkProject;
private Integer fkModule;
private Integer fkJob;
private Integer fkBilling;
public DoneBean() {
}
@ -45,8 +49,12 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
this.module = moduleMap.get(r.getFkModule());
this.activity = jobMap.get(r.getFkJob());
this.billing = billingMap.get(r.getFkBilling());
this.fkProject = project.getPk();
this.fkModule = module.getPk();
this.fkJob = activity.getPk();
this.fkBilling = billing.getPk();
}
@Override
public final String toString() {
StringBuilder buf = new StringBuilder("DoneBean{");
@ -105,6 +113,22 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
return String.format("%02d:%02d", diff.toHours(), diff.toMinutes() % 60);
}
/**
* try to find out what date this entry is for; if not found, use the current
* date
*
* @return a local date
*/
public LocalDate getLocalDate() {
if (timeFrom != null) {
return timeFrom.toLocalDate();
} else if (timeUntil != null) {
return timeUntil.toLocalDate();
} else {
return LocalDate.now();
}
}
/**
* get local date time from s
*
@ -146,15 +170,15 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
public String getJobName() {
return activity == null ? "" : activity.getName();
}
public String getBillingName() {
return billing == null ? "" : billing.getName();
}
public String getBillingShortcut() {
return billing == null ? "" : billing.getShortcut();
}
public String getBillingCsskey() {
return billing == null ? "" : billing.getCsskey();
}
@ -205,6 +229,7 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
public void setProject(TProjectRecord project) {
this.project = project;
this.fkProject = project != null ? project.getPk() : null;
}
public TModuleRecord getModule() {
@ -213,6 +238,7 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
public void setModule(TModuleRecord module) {
this.module = module;
this.fkModule = module != null ? module.getPk() : null;
}
public TJobRecord getActivity() {
@ -221,6 +247,7 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
public void setActivity(TJobRecord activity) {
this.activity = activity;
this.fkJob = activity != null ? activity.getPk() : null;
}
/**
@ -235,5 +262,62 @@ public class DoneBean implements Serializable, Comparable<DoneBean> {
*/
public void setBilling(TBillingRecord billing) {
this.billing = billing;
this.fkBilling = billing != null ? billing.getPk() : null;
}
/**
* @return the fkProject
*/
public Integer getFkProject() {
return fkProject;
}
/**
* @param fkProject the fkProject to set
*/
public void setFkProject(Integer fkProject) {
this.fkProject = fkProject;
}
/**
* @return the fkModule
*/
public Integer getFkModule() {
return fkModule;
}
/**
* @param fkModule the fkModule to set
*/
public void setFkModule(Integer fkModule) {
this.fkModule = fkModule;
}
/**
* @return the fkJob
*/
public Integer getFkJob() {
return fkJob;
}
/**
* @param fkJob the fkJob to set
*/
public void setFkJob(Integer fkJob) {
this.fkJob = fkJob;
}
/**
* @return the fkBilling
*/
public Integer getFkBilling() {
return fkBilling;
}
/**
* @param fkBilling the fkBilling to set
*/
public void setFkBilling(Integer fkBilling) {
this.fkBilling = fkBilling;
}
}

View File

@ -63,8 +63,30 @@ public class DoneController {
bean = new DoneBean(); // the add case
}
model.addAttribute("doneBean", bean);
// model.addAttribute("types", Arrays.asList(EnumNotetype.values()));
// model.addAttribute("categories", Arrays.asList(EnumCategory.values()));
model.addAttribute("projectList", doneService.getProjects());
model.addAttribute("moduleList", doneService.getModules());
model.addAttribute("jobList", doneService.getJobs());
model.addAttribute("billingList", doneService.getBillings());
return "done/item";
}
@RolesAllowed("timetrack_user")
@RequestMapping(value = "/done/upsert", method = RequestMethod.POST)
public String doUpsert(Model model, @ModelAttribute DoneBean bean) {
String username = doneService.getCurrentUser(request);
Integer amount = doneService.doUpsert(bean, username);
DoneModel doneModel = new DoneModel();
doneModel.setDay(bean.getLocalDate());
return amount.equals(1) ? getList(doneModel, model) : toItem(bean.getPk(), model);
}
@RolesAllowed("timetrack_user")
@GetMapping(value = "/done/delete/{id}")
public String doDelete(@PathVariable Integer id, Model model) {
DoneBean bean = doneService.getBean(id);
Integer amount = doneService.doDelete(id);
DoneModel doneModel = new DoneModel();
doneModel.setDay(bean.getLocalDate());
return amount.equals(1) ? getList(doneModel, model) : toItem(id, model);
}
}

View File

@ -29,4 +29,8 @@ public interface IDoneService {
public List<TJobRecord> getJobs();
public List<TBillingRecord> getBillings();
public Integer doUpsert(DoneBean bean, String username);
public Integer doDelete(Integer id);
}

View File

@ -19,9 +19,11 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.InsertValuesStep7;
import org.jooq.Record7;
import org.jooq.Result;
import org.jooq.SelectConditionStep;
import org.jooq.UpdateConditionStep;
import org.jooq.exception.DataAccessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@ -51,7 +53,7 @@ public class DoneGateway {
public DSLContext getJooq() {
return this.jooq;
}
/**
* get the user id of the user with username
*
@ -248,4 +250,82 @@ public class DoneGateway {
LOGGER.debug("{}", sql.toString());
return sql.execute();
}
/**
* get the done bean of the pk
*
* @param pk the ID of the data set
* @return the bean if found; null otherwise
* @throws SQLException
* @throws ClassNotFoundException
* @throws DataAccessException
*/
public DoneBean getBean(Integer pk) throws DataAccessException, ClassNotFoundException, SQLException {
SelectConditionStep<Record7<Integer, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer>> sql = getJooq()
// @formatter:off
.select(T_DONE.PK,
T_DONE.TIME_FROM,
T_DONE.TIME_UNTIL,
T_DONE.FK_PROJECT,
T_DONE.FK_MODULE,
T_DONE.FK_JOB,
T_DONE.FK_BILLING)
.from(T_DONE)
.where(T_DONE.PK.eq(pk));
// @formatter:on
LOGGER.debug("{}", sql.toString());
Map<Integer, TProjectRecord> projectMap = getProjectMap();
Map<Integer, TModuleRecord> moduleMap = getModuleMap();
Map<Integer, TJobRecord> jobMap = getJobMap();
Map<Integer, TBillingRecord> billingMap = getBillingMap();
for (Record7<Integer, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer> r : sql.fetch()) {
DoneBean bean = new DoneBean();
bean.setPk(r.get(T_DONE.PK));
bean.setTimeFrom(r.get(T_DONE.TIME_FROM));
bean.setTimeUntil(r.get(T_DONE.TIME_UNTIL));
bean.setProject(projectMap.get(r.get(T_DONE.FK_PROJECT)));
bean.setModule(moduleMap.get(r.get(T_DONE.FK_MODULE)));
bean.setActivity(jobMap.get(r.get(T_DONE.FK_JOB)));
bean.setBilling(billingMap.get(r.get(T_DONE.FK_BILLING)));
return (bean);
}
return (null);
}
public Integer upsert(DoneBean bean, Integer userId) {
return bean.getPk() != null ? update(bean) : insert(bean, userId);
}
private Integer insert(DoneBean bean, Integer userId) {
InsertValuesStep7<TDoneRecord, LocalDateTime, LocalDateTime, Integer, Integer, Integer, Integer, Integer> sql = getJooq()
// @formatter:off
.insertInto(T_DONE,
T_DONE.TIME_FROM,
T_DONE.TIME_UNTIL,
T_DONE.FK_PROJECT,
T_DONE.FK_MODULE,
T_DONE.FK_JOB,
T_DONE.FK_BILLING,
T_DONE.FK_LOGIN)
.values(bean.getTimeFrom(), bean.getTimeUntil(), bean.getFkProject(), bean.getFkModule(), bean.getFkJob(), bean.getFkBilling(), userId);
// @formatter:on
LOGGER.debug(sql.toString());
return sql.execute();
}
private Integer update(DoneBean bean) {
UpdateConditionStep<TDoneRecord> sql = getJooq()
// @formatter:off
.update(T_DONE)
.set(T_DONE.TIME_FROM, bean.getTimeFrom())
.set(T_DONE.TIME_UNTIL, bean.getTimeUntil())
.set(T_DONE.FK_PROJECT, bean.getFkProject())
.set(T_DONE.FK_MODULE, bean.getFkModule())
.set(T_DONE.FK_JOB, bean.getFkJob())
.set(T_DONE.FK_BILLING, bean.getFkBilling())
.where(T_DONE.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug(sql.toString());
return sql.execute();
}
}

View File

@ -59,12 +59,16 @@ public class DoneService implements IDoneService {
@Override
public DoneBean getBean(Integer id) {
// TODO Auto-generated method stub
return null;
try {
return new DoneGateway(dsl).getBean(id);
} catch (Exception e) {
LOGGER.error(e);
return null;
}
}
@Override
public List<TProjectRecord> getProjects() {
public List<TProjectRecord> getProjects() {
try {
return new DoneGateway(dsl).getAllProjects();
} catch (Exception e) {
@ -103,4 +107,26 @@ public class DoneService implements IDoneService {
}
}
@Override
public Integer doUpsert(DoneBean bean, String username) {
try {
DoneGateway gw = new DoneGateway(dsl);
Integer userId = gw.getUserId(username);
return gw.upsert(bean, userId);
} catch (Exception e) {
LOGGER.error(e);
return -1;
}
}
@Override
public Integer doDelete(Integer id) {
try {
return new DoneGateway(dsl).delete(id);
} catch (Exception e) {
LOGGER.error(e);
return -1;
}
}
}

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
<head>
<title>Arbeitszeit aktualisieren</title>
</head>
<body>
<ul layout:fragment="menu">
</ul>
<main layout:fragment="content">
<div class="container formpane">
<form th:action="@{/done/upsert}" th:object="${doneBean}" method="post">
<div class="row mb-3">
<label for="inputPk" class="col-sm-2 col-form-label">Inhalt von Eintrag</label>
<div class="col-sm-10">
<input id="inputPk" type="text" th:field="*{pk}" class="form-control" readonly="readonly" />
</div>
</div>
<div class="row mb-3">
<label for="inputTimefrom" class="col-sm-2 col-form-label">von</label>
<div class="col-sm-10">
<input id="inputTimefrom" type="text" th:field="*{timeFromString}" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label for="inputTimeuntil" class="col-sm-2 col-form-label">bis</label>
<div class="col-sm-10">
<input id="inputTimeuntil" type="text" th:field="*{timeUntilString}" class="form-control" />
</div>
</div>
<div class="row mb-3">
<label for="inputProject" class="col-sm-2 col-form-label">Projekt</label>
<select id="inputProject" class="form-control select2-single" th:field="*{fkProject}">
<option th:each="i : ${projectList}" th:value="${i.pk}" th:text="${i.name}"></option>
</select>
</div>
<div class="row mb-3">
<label for="inputModule" class="col-sm-2 col-form-label">Modul</label>
<select id="inputModule" class="form-control select2-single" th:field="*{fkModule}">
<option th:each="i : ${moduleList}" th:value="${i.pk}" th:text="${i.name}"></option>
</select>
</div>
<div class="row mb-3">
<label for="inputJob" class="col-sm-2 col-form-label">Aufgabe</label>
<select id="inputJob" class="form-control select2-single" th:field="*{fkJob}">
<option th:each="i : ${jobList}" th:value="${i.pk}" th:text="${i.name}"></option>
</select>
</div>
<div class="row mb-3">
<label for="inputBilling" class="col-sm-2 col-form-label">Abrechnung</label>
<select id="inputBilling" class="form-control select2-single" th:field="*{fkBilling}">
<option th:each="i : ${billingList}" th:value="${i.pk}" th:text="${i.name}"></option>
</select>
</div>
<div class="row mb-3">
<div class="col-sm-2">Änderung</div>
<div class="col-sm-10">
<button type="submit" class="btn btn-success">speichern</button>
<button type="submit" class="btn btn-secondary" th:formaction="@{/done/list}">abbrechen</button>
<div class="dropdown float-right" th:if="${doneBean.pk != null}" sec:authorize="hasRole('timetrack_user')">
<button class="btn btn-danger dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">Eintrag löschen</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" th:href="@{/done/delete/{id}(id=${doneBean.pk})}">endgültig löschen</a></li>
</ul>
</div>
</div>
</div>
</form>
</div>
</main>
</body>
</html>

View File

@ -48,11 +48,11 @@
</thead>
<tbody>
<tr th:each="done : ${doneList}">
<td><span th:text="${#temporals.format(done.timeFrom, 'HH:mm')}"></span></td>
<td><span th:text="${#temporals.format(done.timeUntil, 'HH:mm')}"></span></td>
<td><span th:text="${done.project.name}"></span></td>
<td><span th:text="${done.module.name}"></span></td>
<td><span th:text="${done.activity.name}"></span></td>
<td><a th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${#temporals.format(done.timeFrom, 'HH:mm')}"></span></a></td>
<td><a th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${#temporals.format(done.timeUntil, 'HH:mm')}"></span></a></td>
<td><a th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${done.project.name}"></span></a></td>
<td><a th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${done.module.name}"></span></a></td>
<td><a th:href="@{/done/edit/{id}(id=${done.pk})}"><span th:text="${done.activity.name}"></span></a></td>
<td><span th:text="${done.billing.shortcut}" th:class="'billing ' + ${done.billing.csskey}"
th:if="${done.billing != null}"></span></td>
</tr>