private void makeReport (final @Nonnull Writer w) { final PrintWriter pw = new PrintWriter(w); System.err.println("CREATE REPORT " + project); pw.printf(SEPARATOR + "\n"); pw.printf(PATTERN, "Date", "Description", "Time", "Cost"); pw.printf(SEPARATOR + "\n"); // TODO: quick and dirty - refactor with visitor, lambdas final List<JobEventSpi> jobEvents = new ArrayList<>(); addAll(jobEvents, project.findChildren().results()); jobEvents.stream().sorted(comparing(JobEventSpi::getDateTime)) .forEach(event -> pw.printf(PATTERN2, DATE_FORMATTER.format(event.getDateTime()), event.getName(), DURATION_FORMATTER.format(event.getDuration()), MONEY_FORMATTER.format(event.getEarnings()))); pw.printf(SEPARATOR + "\n"); pw.printf(PATTERN3, "", "", DURATION_FORMATTER.format(project.getDuration()), MONEY_FORMATTER.format(project.getEarnings())); // FIXME: rename getAmount() -> getBudget() // FIXME: introduce getBudgetDuration() final Duration duration = Duration.ofHours((long)project.getBudget().divided(project.getHourlyRate())); pw.printf("BUDGET: %s\n", MONEY_FORMATTER.format(project.getBudget())); pw.printf("HOURLY RATE: %s\n", MONEY_FORMATTER.format(project.getHourlyRate())); pw.printf("DURATION: %s\n", DURATION_FORMATTER.format(duration)); pw.printf("REMAINING BUDGET: %s\n", MONEY_FORMATTER.format(project.getBudget().subtract(project.getEarnings()))); pw.printf("REMAINING TIME: %s\n", DURATION_FORMATTER.format(duration.minus(project.getDuration()))); pw.flush(); }
@Override @Nonnull protected AggregatePresentationModelBuilder aggregateBuilder() { return super.aggregateBuilder() .with(DATE, (Displayable) () -> DATETIME_FORMATTER.format(jobEvent.getStartDateTime())) .with(TIME, (Displayable) () -> DURATION_FORMATTER.format(computeDuration()), STYLE_RIGHT_ALIGNED) .with(HOURLY_RATE, (Displayable) () -> MONEY_FORMATTER.format(jobEvent.getHourlyRate()), STYLE_RIGHT_ALIGNED); }
@Nonnull protected AggregatePresentationModelBuilder aggregateBuilder() { // FIXME: uses the column header names, should be an internal id instead return AggregatePresentationModelBuilder.newInstance() .with(JOB_EVENT, (Displayable) () -> jobEvent.getName()) .with(NOTES, (Displayable) () -> jobEvent.getDescription()) // FIXME: this is dynamically computed, can be slow - should be also cached .with(AMOUNT, (Displayable) () -> MONEY_FORMATTER.format(jobEvent.getEarnings()), STYLE_RIGHT_ALIGNED); }
@Override @Nonnull protected AggregatePresentationModelBuilder aggregateBuilder() { return super.aggregateBuilder() .with(DATE, (Displayable) () -> DATE_FORMATTER.format(jobEvent.getDate())) .with(TIME, new DefaultDisplayable("")) .with(HOURLY_RATE, new DefaultDisplayable("")) .with(AMOUNT , (Displayable) () -> MONEY_FORMATTER.format(jobEvent.getEarnings()), new DefaultStyleable("right-aligned"), new RedStyleForNegativeMoney(jobEvent::getEarnings)); }
new DefaultStyleable("right-aligned")) .with("Notes", (Displayable) () -> project.getNotes()) .with("Budget", (Displayable) () -> MONEY_FORMATTER.format(budget), new DefaultStyleable("right-aligned", budget.isEqualTo(Money.ZERO) ? "alerted" : "")) .with("Earnings", (Displayable) () -> MONEY_FORMATTER.format(earnings), new DefaultStyleable("right-aligned", earnings.greaterThan(budget) ? "alerted" : "", .with("Time", (Displayable) () -> DURATION_FORMATTER.format(project.getDuration()), new DefaultStyleable("right-aligned")) .with("Invoiced", (Displayable) () -> MONEY_FORMATTER.format(invoicedEarnings), new DefaultStyleable("right-aligned", invoicedEarnings.greaterThan(earnings) ? "alerted" : "",