1
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 package it.tidalwave.accounting.model.impl;
27
28 import javax.annotation.Nonnull;
29 import java.time.Duration;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.io.PrintWriter;
33 import java.io.StringWriter;
34 import java.io.Writer;
35 import it.tidalwave.accounting.model.HourlyReport;
36 import it.tidalwave.accounting.model.HourlyReportGenerator;
37 import it.tidalwave.accounting.model.JobEvent;
38 import it.tidalwave.accounting.model.JobEventGroup;
39 import it.tidalwave.accounting.model.spi.JobEventSpi;
40 import it.tidalwave.accounting.model.spi.ProjectSpi;
41 import it.tidalwave.dci.annotation.DciRole;
42 import lombok.RequiredArgsConstructor;
43 import static it.tidalwave.accounting.model.spi.util.Formatters.*;
44 import static java.util.Comparator.*;
45
46
47
48
49
50
51 @DciRole(datumType = ProjectSpi.class) @RequiredArgsConstructor
52 public class DefaultHourlyReportGenerator implements HourlyReportGenerator
53 {
54 private static final String SEPARATOR = "===========================================================================";
55
56 private static final String PATTERN = "| %-12s | %-30s | %-8s | %-12s |%n";
57 private static final String PATTERN2 = "| %12s | %-30s | %8s | %12s |%n";
58 private static final String PATTERN3 = " %12s %-30s %8s %12s %n";
59
60 @Nonnull
61 private final ProjectSpi project;
62
63 @Override @Nonnull
64 public HourlyReport createReport()
65 {
66 final var sw = new StringWriter();
67 makeReport(sw);
68 return new HourlyReport(sw.toString());
69 }
70
71 private void makeReport (@Nonnull final Writer w)
72 {
73 final var pw = new PrintWriter(w);
74 System.err.println("CREATE REPORT " + project);
75
76 pw.print(SEPARATOR + "\n");
77 pw.printf(PATTERN, "Date", "Description", "Time", "Cost");
78 pw.print(SEPARATOR + "\n");
79
80
81 final List<JobEventSpi> jobEvents = new ArrayList<>();
82 addAll(jobEvents, project.findChildren().results());
83 jobEvents.stream().sorted(comparing(JobEventSpi::getDateTime))
84 .forEach(event -> pw.printf(PATTERN2, DATE_FORMATTER.format(event.getDateTime()),
85 event.getName(),
86 DURATION_FORMATTER.format(event.getDuration()),
87 MONEY_FORMATTER.format(event.getEarnings())));
88 pw.print(SEPARATOR + "\n");
89 pw.printf(PATTERN3, "", "", DURATION_FORMATTER.format(project.getDuration()),
90 MONEY_FORMATTER.format(project.getEarnings()));
91
92
93
94 final var duration = Duration.ofHours((long)project.getBudget().divided(project.getHourlyRate()));
95 pw.printf("BUDGET: %s%n", MONEY_FORMATTER.format(project.getBudget()));
96 pw.printf("HOURLY RATE: %s%n", MONEY_FORMATTER.format(project.getHourlyRate()));
97 pw.printf("DURATION: %s%n", DURATION_FORMATTER.format(duration));
98 pw.printf("REMAINING BUDGET: %s%n",
99 MONEY_FORMATTER.format(project.getBudget().subtract(project.getEarnings())));
100 pw.printf("REMAINING TIME: %s%n", DURATION_FORMATTER.format(duration.minus(project.getDuration())));
101 pw.flush();
102 }
103
104 private void addAll (@Nonnull final List<? super JobEventSpi> results,
105 @Nonnull final List<? extends JobEvent> jobEvents)
106 {
107 for (final var jobEvent : jobEvents)
108 {
109 if (jobEvent instanceof JobEventGroup)
110 {
111 addAll(results, ((JobEventGroup)jobEvent).findChildren().results());
112 }
113 else
114 {
115 results.add((JobEventSpi)jobEvent);
116 }
117 }
118 }
119 }