preparing for adding positions to document
This commit is contained in:
@@ -20,6 +20,7 @@ public class Constants {
|
|||||||
public static final String DESCRIPTION = "description";
|
public static final String DESCRIPTION = "description";
|
||||||
public static final String DOMAIN = "domain";
|
public static final String DOMAIN = "domain";
|
||||||
public static final String DUE_DATE = "due_date";
|
public static final String DUE_DATE = "due_date";
|
||||||
|
public static final String DURATION = "duration";
|
||||||
public static final String EMAIL = "email";
|
public static final String EMAIL = "email";
|
||||||
public static final String END_TIME = "end_time";
|
public static final String END_TIME = "end_time";
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import java.util.Collection;
|
|||||||
public interface TaskService {
|
public interface TaskService {
|
||||||
CompanyService companyService();
|
CompanyService companyService();
|
||||||
Collection<Task> listCompanyTasks(long companyId) throws UmbrellaException;
|
Collection<Task> listCompanyTasks(long companyId) throws UmbrellaException;
|
||||||
|
Collection<Task> listProjectTasks(long projectId) throws UmbrellaException;
|
||||||
|
|
||||||
ProjectService projectService();
|
ProjectService projectService();
|
||||||
|
|
||||||
UserService userService();
|
UserService userService();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
/* © SRSoftware 2025 */
|
/* © SRSoftware 2025 */
|
||||||
package de.srsoftware.umbrella.core.model;
|
package de.srsoftware.umbrella.core.model;
|
||||||
|
|
||||||
import de.srsoftware.tools.Mappable;
|
|
||||||
|
|
||||||
import static de.srsoftware.umbrella.core.Constants.*;
|
import static de.srsoftware.umbrella.core.Constants.*;
|
||||||
import static de.srsoftware.umbrella.core.Util.dateTimeOf;
|
import static de.srsoftware.umbrella.core.Util.dateTimeOf;
|
||||||
|
import static de.srsoftware.umbrella.core.Util.markdown;
|
||||||
|
|
||||||
|
import de.srsoftware.tools.Mappable;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -59,18 +60,15 @@ public class Time implements Mappable{
|
|||||||
this.taskIds = taskIds;
|
this.taskIds = taskIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long id(){
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isClosed() {
|
||||||
public Map<String, Object> toMap() {
|
return switch (state){
|
||||||
var map = new HashMap<String,Object>();
|
case Complete, Cancelled -> true;
|
||||||
map.put(ID,id);
|
case null, default -> false;
|
||||||
map.put(USER_ID,userId);
|
};
|
||||||
map.put(SUBJECT,subject);
|
|
||||||
map.put(DESCRIPTION,description);
|
|
||||||
map.put(START_TIME,start);
|
|
||||||
map.put(END_TIME,end);
|
|
||||||
map.put(STATE,Map.of(STATUS_CODE,state.code,NAME,state.name()));
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Time of(ResultSet rs) throws SQLException {
|
public static Time of(ResultSet rs) throws SQLException {
|
||||||
@@ -90,4 +88,26 @@ public class Time implements Mappable{
|
|||||||
new HashSet<>()
|
new HashSet<>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDateTime start(){
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Long> taskIds(){
|
||||||
|
return taskIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
var map = new HashMap<String,Object>();
|
||||||
|
map.put(ID,id);
|
||||||
|
map.put(USER_ID,userId);
|
||||||
|
map.put(SUBJECT,subject);
|
||||||
|
map.put(DESCRIPTION,Map.of(SOURCE,description,RENDERED,markdown(description)));
|
||||||
|
map.put(START_TIME,start.toString().replace("T"," "));
|
||||||
|
map.put(END_TIME,end.toString().replace("T"," "));
|
||||||
|
map.put(STATE,Map.of(STATUS_CODE,state.code,NAME,state.name()));
|
||||||
|
map.put(DURATION,Duration.between(start,end).toMinutes()/60d);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public class Constants {
|
|||||||
public static final String PATH_POSITIONS = "positions";
|
public static final String PATH_POSITIONS = "positions";
|
||||||
public static final String PATH_SEND = "send";
|
public static final String PATH_SEND = "send";
|
||||||
public static final String PATH_TYPES = "types";
|
public static final String PATH_TYPES = "types";
|
||||||
|
public static final String POSITION = "position";
|
||||||
public static final String PROJECT_ID = "project_id";
|
public static final String PROJECT_ID = "project_id";
|
||||||
|
|
||||||
public static final String STATES = "states";
|
public static final String STATES = "states";
|
||||||
|
|||||||
@@ -141,7 +141,15 @@ public class DocumentApi extends BaseHandler {
|
|||||||
case LIST -> listCompaniesDocuments(ex,user.get(),token.orElse(null));
|
case LIST -> listCompaniesDocuments(ex,user.get(),token.orElse(null));
|
||||||
case TEMPLATES -> postTemplateList(ex,user.get());
|
case TEMPLATES -> postTemplateList(ex,user.get());
|
||||||
case null -> postDocument(ex,user.get());
|
case null -> postDocument(ex,user.get());
|
||||||
default -> super.doPost(path,ex);
|
default -> {
|
||||||
|
var docId = 0L;
|
||||||
|
try {
|
||||||
|
docId = Long.parseLong(head);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
yield super.doPost(path,ex);
|
||||||
|
}
|
||||||
|
yield postToDocument(ex,path,user.get(),docId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (UmbrellaException e) {
|
} catch (UmbrellaException e) {
|
||||||
return send(ex,e);
|
return send(ex,e);
|
||||||
@@ -247,6 +255,10 @@ public class DocumentApi extends BaseHandler {
|
|||||||
return sendContent(ex,saved.toMap());
|
return sendContent(ex,saved.toMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean postDocumentPosition(long docId, HttpExchange ex, UmbrellaUser user) throws IOException {
|
||||||
|
return notImplemented(ex,"postDocumentPosition",this);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean postTemplateList(HttpExchange ex, UmbrellaUser user) throws UmbrellaException, IOException {
|
private boolean postTemplateList(HttpExchange ex, UmbrellaUser user) throws UmbrellaException, IOException {
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
if (!(json.has(COMPANY) && json.get(COMPANY) instanceof Number companyId)) throw missingFieldException(COMPANY);
|
if (!(json.has(COMPANY) && json.get(COMPANY) instanceof Number companyId)) throw missingFieldException(COMPANY);
|
||||||
@@ -255,4 +267,13 @@ public class DocumentApi extends BaseHandler {
|
|||||||
var templates = db.getCompanyTemplates(companyId.longValue());
|
var templates = db.getCompanyTemplates(companyId.longValue());
|
||||||
return sendContent(ex,templates.stream().map(Template::toMap));
|
return sendContent(ex,templates.stream().map(Template::toMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean postToDocument(HttpExchange ex, Path path, UmbrellaUser user, long docId) throws IOException {
|
||||||
|
var head = path.pop();
|
||||||
|
return switch (head){
|
||||||
|
case POSITION -> postDocumentPosition(docId,ex,user);
|
||||||
|
case null, default -> super.doPost(path,ex);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,43 @@
|
|||||||
|
|
||||||
let { close = () => {}, doc = $bindable({}), onSelect = (item) => {} } = $props();
|
let { close = () => {}, doc = $bindable({}), onSelect = (item) => {} } = $props();
|
||||||
|
|
||||||
let select = $state(0);
|
let source = $state(0);
|
||||||
|
|
||||||
|
function select(position){
|
||||||
|
close();
|
||||||
|
onSelect(position);
|
||||||
|
}
|
||||||
|
|
||||||
function estimateSelected(estimate){
|
function estimateSelected(estimate){
|
||||||
onSelect({task:estimate});
|
select({
|
||||||
close();
|
code:t('estimated_time'),
|
||||||
|
subject:estimate.name,
|
||||||
|
description:estimate.description.source,
|
||||||
|
amount:estimate.estimated_time,
|
||||||
|
unit:doc.currency+"/h"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemSelected(item){
|
function itemSelected(item){
|
||||||
onSelect({item:item});
|
select({
|
||||||
close();
|
code:item.code,
|
||||||
|
subject:item.name,
|
||||||
|
description:item.description.source,
|
||||||
|
amount:1,
|
||||||
|
unit:item.unit,
|
||||||
|
unit_price:item.unit_price,
|
||||||
|
tax:item.tax
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeSelected(time){
|
function timeSelected(time){
|
||||||
console.log({timeSelected:time});
|
select({
|
||||||
|
code:t('document.timetrack'),
|
||||||
|
title:time.subject,
|
||||||
|
description:time.description.source,
|
||||||
|
amount:time.duration,
|
||||||
|
unit:doc.currency+"/h"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -42,14 +65,14 @@
|
|||||||
|
|
||||||
<div class="position_selector">
|
<div class="position_selector">
|
||||||
<span class="tabs">
|
<span class="tabs">
|
||||||
<button onclick={() => select=0}>{t('document.items')}</button>
|
<button onclick={() => source=0}>{t('document.items')}</button>
|
||||||
<button onclick={() => select=1}>{t('document.estimated_times')}</button>
|
<button onclick={() => source=1}>{t('document.estimated_times')}</button>
|
||||||
<button onclick={() => select=2}>{t('document.timetrack')}</button>
|
<button onclick={() => source=2}>{t('document.timetrack')}</button>
|
||||||
<button onclick={close}>{t('document.abort')}</button>
|
<button onclick={close}>{t('document.abort')}</button>
|
||||||
</span>
|
</span>
|
||||||
{#if select == 0}
|
{#if source == 0}
|
||||||
<ItemList company_id={doc.company.id} onSelect={itemSelected} />
|
<ItemList company_id={doc.company.id} onSelect={itemSelected} />
|
||||||
{:else if select == 1}
|
{:else if source == 1}
|
||||||
<EstimateList company_id={doc.company.id} onSelect={estimateSelected} />
|
<EstimateList company_id={doc.company.id} onSelect={estimateSelected} />
|
||||||
{:else}
|
{:else}
|
||||||
<TimeList company_id={doc.company.id} onSelect={timeSelected} />
|
<TimeList company_id={doc.company.id} onSelect={timeSelected} />
|
||||||
|
|||||||
@@ -4,17 +4,33 @@
|
|||||||
|
|
||||||
let { company_id, onSelect = (time) => {} } = $props();
|
let { company_id, onSelect = (time) => {} } = $props();
|
||||||
|
|
||||||
|
let projects = $state(null);
|
||||||
let times = $state(null);
|
let times = $state(null);
|
||||||
let error = $state(null);
|
let error = $state(null);
|
||||||
|
|
||||||
async function loadTimes(){
|
async function loadProjects(){
|
||||||
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/times/list`;
|
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/project/list`;
|
||||||
let data = { company_id: company_id };
|
let data = { company_id: company_id };
|
||||||
const resp = await fetch(url,{
|
const resp = await fetch(url,{
|
||||||
credentials:'include',
|
credentials:'include',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
});
|
});
|
||||||
|
if (resp.ok){
|
||||||
|
projects = await resp.json();
|
||||||
|
} else {
|
||||||
|
error = await resp.body();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadTimes(projectId){
|
||||||
|
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/times/list`;
|
||||||
|
let data = { company_id: company_id, project_id: projectId };
|
||||||
|
const resp = await fetch(url,{
|
||||||
|
credentials:'include',
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
times = await resp.json();
|
times = await resp.json();
|
||||||
} else {
|
} else {
|
||||||
@@ -22,11 +38,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(loadTimes);
|
onMount(loadProjects);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1>Times</h1>
|
<h1>Times</h1>
|
||||||
|
{#if projects}
|
||||||
|
{#each projects as project,idx1}
|
||||||
|
<button onclick={() => loadTimes(project.id)}>{project.name}</button>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
{#if times}
|
{#if times}
|
||||||
|
{#each times as time,idx2}
|
||||||
|
<div class="time" onclick={() => onSelect(time)}>
|
||||||
|
<span class="duration">{(time.duration).toFixed(3)} {t('hours')}</span>
|
||||||
|
<span class="subject">{time.subject}</span>
|
||||||
|
<span class="start_time">{time.start_time}</span>
|
||||||
|
<span class="description">{@html time.description.rendered}</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
import StateSelector from './StateSelector.svelte';
|
import StateSelector from './StateSelector.svelte';
|
||||||
import TemplateSelector from './TemplateSelector.svelte';
|
import TemplateSelector from './TemplateSelector.svelte';
|
||||||
let { id } = $props();
|
let { id } = $props();
|
||||||
let error = null;
|
let error = $state(null);
|
||||||
let doc = $state(null);
|
let doc = $state(null);
|
||||||
let position_select = $state(false);
|
let position_select = $state(false);
|
||||||
|
|
||||||
@@ -63,12 +63,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPosition(selected){
|
async function addPosition(selected){
|
||||||
console.log(selected);
|
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/document/${doc.id}/position`;
|
||||||
let newPos = {};
|
const resp = await fetch(url,{
|
||||||
if (selected.item) newPos['item']=selected.item.id;
|
method: 'POST',
|
||||||
if (selected.task) newPos['task']=selected.task.id;
|
credentials:'include',
|
||||||
console.log(JSON.stringify({newPos:newPos}));
|
body:JSON.stringify(selected)
|
||||||
|
});
|
||||||
|
if (resp.ok){
|
||||||
|
} else {
|
||||||
|
error = await resp.text();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(loadDoc);
|
onMount(loadDoc);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import static de.srsoftware.umbrella.core.Paths.LIST;
|
|||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden;
|
||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
||||||
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
||||||
|
import static java.util.Comparator.comparing;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.configuration.Configuration;
|
import de.srsoftware.configuration.Configuration;
|
||||||
@@ -61,7 +62,7 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Project> listProjects(long companyId, boolean includeClosed) throws UmbrellaException {
|
public Collection<Project> listProjects(long companyId, boolean includeClosed) throws UmbrellaException {
|
||||||
return projectDb.list(companyId, includeClosed);
|
return projectDb.list(companyId, includeClosed).stream().sorted(comparing(Project::name)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean listItems(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
private boolean listItems(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ public class TaskModule extends BaseHandler implements TaskService {
|
|||||||
return taskDb.listTasks(projectList.stream().map(Project::id).toList());
|
return taskDb.listTasks(projectList.stream().map(Project::id).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> listProjectTasks(long projectId) throws UmbrellaException {
|
||||||
|
return taskDb.listTasks(List.of(projectId));
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String,Object> placeInTree(Task task, HashMap<Long, Map<String,Object>> tree, Map<Long, Task> map) {
|
private Map<String,Object> placeInTree(Task task, HashMap<Long, Map<String,Object>> tree, Map<Long, Task> map) {
|
||||||
var taskMap = task.toMap();
|
var taskMap = task.toMap();
|
||||||
if (task.parentTaskId() != null){
|
if (task.parentTaskId() != null){
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.sql.SQLException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SqliteDb implements TimeDb {
|
public class SqliteDb implements TimeDb {
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package de.srsoftware.umbrella.time;
|
|||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.Time;
|
import de.srsoftware.umbrella.core.model.Time;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface TimeDb {
|
public interface TimeDb {
|
||||||
Collection<Time> listTimes(Collection<Long> taskIds) throws UmbrellaException;
|
Collection<Time> listTimes(Collection<Long> taskIds) throws UmbrellaException;
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ package de.srsoftware.umbrella.time;
|
|||||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
||||||
import static de.srsoftware.umbrella.core.Constants.*;
|
import static de.srsoftware.umbrella.core.Constants.*;
|
||||||
import static de.srsoftware.umbrella.core.Paths.LIST;
|
import static de.srsoftware.umbrella.core.Paths.LIST;
|
||||||
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden;
|
||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
||||||
import static de.srsoftware.umbrella.time.Constants.*;
|
import static de.srsoftware.umbrella.time.Constants.*;
|
||||||
import static java.util.stream.Collectors.toMap;
|
import static java.util.function.Predicate.not;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.configuration.Configuration;
|
import de.srsoftware.configuration.Configuration;
|
||||||
@@ -16,7 +17,6 @@ import de.srsoftware.umbrella.core.BaseHandler;
|
|||||||
import de.srsoftware.umbrella.core.api.*;
|
import de.srsoftware.umbrella.core.api.*;
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.*;
|
import de.srsoftware.umbrella.core.model.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -66,15 +66,68 @@ public class TimeModule extends BaseHandler implements TimeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
1 : {
|
||||||
|
name: Projekt 1
|
||||||
|
id: 1
|
||||||
|
times: {
|
||||||
|
3:{
|
||||||
|
name: time 3
|
||||||
|
start: 123456
|
||||||
|
end: 78901
|
||||||
|
},
|
||||||
|
4:{
|
||||||
|
name: time 4
|
||||||
|
start: 234567
|
||||||
|
end: 890123
|
||||||
|
tasks:{
|
||||||
|
5:{
|
||||||
|
name: task5
|
||||||
|
},
|
||||||
|
6:{
|
||||||
|
name: task6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
name: Projekt 2
|
||||||
|
id: 2
|
||||||
|
times: {
|
||||||
|
7:{
|
||||||
|
name: time 7
|
||||||
|
start: 456789
|
||||||
|
end: 012345
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
private boolean listTimes(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
private boolean listTimes(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID);
|
if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID);
|
||||||
long companyId = cid.longValue();
|
var companyId = cid.longValue();
|
||||||
if (!companies.membership(companyId,user.id())) throw UmbrellaException.forbidden("You are not a member of compayn {0}",companyId);
|
var company = companies.get(companyId);
|
||||||
var projectMap = projects.listProjects(companyId,false).stream().collect(toMap(Project::id, p -> p));
|
if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name());
|
||||||
var taskMap = tasks.listCompanyTasks(companyId).stream().collect(Collectors.toMap(Task::id,t->t));
|
if (!(json.has(PROJECT_ID) && json.get(PROJECT_ID) instanceof Number pid)) throw missingFieldException(PROJECT_ID);
|
||||||
var taskIds = taskMap.keySet();
|
long projectId = pid.longValue();
|
||||||
var timesList = timeDb.listTimes(taskIds).stream().map();
|
Map<Long,Task> tasksOfProject = tasks.listProjectTasks(projectId).stream().collect(Collectors.toMap(Task::id,t->t));
|
||||||
return sendContent(ex,tree);
|
|
||||||
|
List<Map<String, Object>> times = timeDb.listTimes(tasksOfProject.keySet())
|
||||||
|
.stream().filter(not(Time::isClosed))
|
||||||
|
.sorted(Comparator.comparing(Time::start))
|
||||||
|
.map(time -> {
|
||||||
|
var map = time.toMap();
|
||||||
|
var timeTasks = time.taskIds().stream().map(tasksOfProject::get).map(Task::toMap).toList();
|
||||||
|
map.put(TASKS,timeTasks);
|
||||||
|
return map;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
return sendContent(ex,times);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"loading": "lade…"
|
"loading": "lade…"
|
||||||
},
|
},
|
||||||
"document": {
|
"document": {
|
||||||
|
"abort": "abbrechen",
|
||||||
"actions": "Aktionen",
|
"actions": "Aktionen",
|
||||||
"add_new": "{0} anlegen",
|
"add_new": "{0} anlegen",
|
||||||
"add_position": "hinzufügen",
|
"add_position": "hinzufügen",
|
||||||
@@ -17,9 +18,12 @@
|
|||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"delete": "löschen",
|
"delete": "löschen",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
|
"estimated_time": "geschätzte Zeit",
|
||||||
|
"estimated_times": "geschätzte Zeiten",
|
||||||
"footer": "Fuß-Text",
|
"footer": "Fuß-Text",
|
||||||
"gross_sum": "Brutto-Summe",
|
"gross_sum": "Brutto-Summe",
|
||||||
"head": "Kopf-Text",
|
"head": "Kopf-Text",
|
||||||
|
"items": "Artikel",
|
||||||
"list": "Dokumente",
|
"list": "Dokumente",
|
||||||
"list_of": "Dokumente von {0}",
|
"list_of": "Dokumente von {0}",
|
||||||
"net_price": "Nettopreis",
|
"net_price": "Nettopreis",
|
||||||
@@ -42,6 +46,7 @@
|
|||||||
"state_sent": "versendet",
|
"state_sent": "versendet",
|
||||||
"tax_id": "Steuernummer",
|
"tax_id": "Steuernummer",
|
||||||
"tax_rate": "Steuersatz",
|
"tax_rate": "Steuersatz",
|
||||||
|
"timetrack": "Zeiterfassung",
|
||||||
"title_or_desc": "Titel/Beschreibung",
|
"title_or_desc": "Titel/Beschreibung",
|
||||||
"type": "Dokumententyp",
|
"type": "Dokumententyp",
|
||||||
"type_confirmation": "Bestätigung",
|
"type_confirmation": "Bestätigung",
|
||||||
|
|||||||
Reference in New Issue
Block a user