Skip to content

Package: DefaultIBizProjectImporter

DefaultIBizProjectImporter

nameinstructionbranchcomplexitylinemethod
getHourlyRate(ConfigurationDecorator, List)
M: 42 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
importJobEvent(ConfigurationDecorator)
M: 46 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
importJobEvents(Stream)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
importProject(ConfigurationDecorator)
M: 83 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 20 C: 0
0%
M: 1 C: 0
0%
importProject(Path)
M: 12 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
importProjects()
M: 14 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * blueHour: open source accounting
5: * http://tidalwave.it/projects/bluehour
6: *
7: * Copyright (C) 2013 - 2024 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/bluehour-src
22: * git clone https://github.com/tidalwave-it/bluehour-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.accounting.importer.ibiz.impl;
27:
28: import javax.annotation.Nonnull;
29: import java.util.List;
30: import java.util.stream.Stream;
31: import java.io.IOException;
32: import java.nio.file.FileVisitResult;
33: import java.nio.file.Files;
34: import java.nio.file.Path;
35: import java.nio.file.SimpleFileVisitor;
36: import java.nio.file.attribute.BasicFileAttributes;
37: import it.tidalwave.accounting.importer.ibiz.spi.IBizProjectImporter;
38: import it.tidalwave.accounting.model.CustomerRegistry;
39: import it.tidalwave.accounting.model.JobEvent;
40: import it.tidalwave.accounting.model.JobEventGroup;
41: import it.tidalwave.accounting.model.ProjectRegistry;
42: import it.tidalwave.accounting.model.spi.TimedJobEventSpi;
43: import it.tidalwave.accounting.model.types.Money;
44: import it.tidalwave.util.NotFoundException;
45: import lombok.RequiredArgsConstructor;
46: import lombok.extern.slf4j.Slf4j;
47: import static java.util.stream.Collectors.*;
48:
49: /***************************************************************************************************************************************************************
50: *
51: * @author Fabrizio Giudici
52: *
53: **************************************************************************************************************************************************************/
54: @Slf4j @RequiredArgsConstructor
55: public class DefaultIBizProjectImporter implements IBizProjectImporter
56: {
57: @Nonnull
58: private final CustomerRegistry customerRegistry;
59:
60: @Nonnull
61: private final ProjectRegistry projectRegistry;
62:
63: @Nonnull
64: private final Path path;
65:
66: /***********************************************************************************************************************************************************
67: * Imports the projects.
68: **********************************************************************************************************************************************************/
69: @Override
70: public void importProjects()
71: throws IOException
72: {
73: log.debug("importProjects()");
74: Files.walkFileTree(path.resolve("Projects"), new SimpleFileVisitor<>()
75: {
76: @Override
77: public FileVisitResult visitFile (@Nonnull final Path file, @Nonnull final BasicFileAttributes attrs)
78: throws IOException
79: {
80: if (!".DS_Store".equals(file.toFile().getName()))
81: {
82: importProject(file);
83: }
84:
85: return FileVisitResult.CONTINUE;
86: }
87: });
88: }
89:
90: /***********************************************************************************************************************************************************
91: * @throws IOException in case of error
92: **********************************************************************************************************************************************************/
93: private void importProject (@Nonnull final Path file)
94: throws IOException
95: {
96: try
97: {
98: importProject(IBizUtils.loadConfiguration(file));
99: }
100: catch (NotFoundException e)
101: {
102: throw new IOException(e);
103: }
104: }
105:
106: /***********************************************************************************************************************************************************
107: * Imports a project from the given configuration object.
108: *
109: * @param projectConfig the configuration object
110: **********************************************************************************************************************************************************/
111: private void importProject (@Nonnull final ConfigurationDecorator projectConfig)
112: throws NotFoundException
113: {
114: final var customerId = projectConfig.getId("clientIdentifier");
115: final var customer =
116: customerRegistry.findCustomers().withId(customerId).optionalResult().orElseThrow(NotFoundException::new);
117: final var status = IBizProjectStatus.values()[projectConfig.getInt("projectStatus")];
118:
119:• if (status.getMappedStatus() == null)
120: {
121: log.warn("IGNORING PROJECT {} with status {}", projectConfig.getString("projectName"), status);
122: }
123: else
124: {
125: final var jobEvents = importJobEvents(projectConfig.getStream("jobEvents"));
126: projectRegistry.addProject().withId(projectConfig.getId("uniqueIdentifier"))
127: .withBudget(projectConfig.getMoney("projectEstimate"))
128: .withCustomer(customer)
129: .withName(projectConfig.getString("projectName"))
130: // .withDescription("description of project 1")
131: .withStartDate(projectConfig.getDate("projectStartDate"))
132: .withEndDate(projectConfig.getDate("projectDueDate"))
133: .withNotes(projectConfig.getString("projectNotes"))
134: .withNumber(projectConfig.getString("projectNumber"))
135: .withStatus(status.getMappedStatus())
136: .withHourlyRate(getHourlyRate(projectConfig, jobEvents))
137: .withEvents(jobEvents)
138: .create();
139: }
140: }
141:
142: /***********************************************************************************************************************************************************
143: * Retrieves the hourly rates - if missing from the project description, tries to recover it from the first
144: * meaningful job event.
145: **********************************************************************************************************************************************************/
146: @Nonnull
147: private Money getHourlyRate(final ConfigurationDecorator projectConfig, final List<? extends JobEvent> jobEvents)
148: throws NotFoundException
149: {
150: var hourlyRate = projectConfig.getMoney("projectRate");
151:
152:• if ((hourlyRate.compareTo(Money.ZERO) == 0) && !jobEvents.isEmpty())
153: // don't use equals() - see http://stackoverflow.com/questions/6787142/bigdecimal-equals-versus-compareto
154: {
155: var event = jobEvents.get(0);
156:
157:• while ((event instanceof JobEventGroup) && ((JobEventGroup)event).findChildren().count() > 0)
158: {
159: event = ((JobEventGroup)event).findChildren().optionalFirstResult().orElseThrow(NotFoundException::new);
160: }
161:
162:• if (event instanceof TimedJobEventSpi)
163: {
164: hourlyRate = ((TimedJobEventSpi)event).getHourlyRate();
165: }
166: }
167: return hourlyRate;
168: }
169:
170: /***********************************************************************************************************************************************************
171: * Imports the job events.
172: **********************************************************************************************************************************************************/
173: @Nonnull
174: private List<JobEvent> importJobEvents (@Nonnull final Stream<? extends ConfigurationDecorator> jobEventsConfig)
175: {
176: return jobEventsConfig.map(this::importJobEvent).collect(toList());
177: }
178:
179: /***********************************************************************************************************************************************************
180: * Imports a single job event.
181: **********************************************************************************************************************************************************/
182: @Nonnull
183: private JobEvent importJobEvent (@Nonnull final ConfigurationDecorator jobEventConfig)
184: {
185: // log.debug(">>>> properties: {}", toList(jobEvent.getKeys()));
186:
187: // final boolean checkedOut = jobEvent.getBoolean("checkedout");
188: // final boolean isExpense = jobEvent.getBoolean("isExpense");
189: // final boolean nonBillable = jobEvent.getBoolean("nonBillable");
190: // final int taxable = jobEvent.getInt("taxable");
191: // final DateTime lastModifiedDate = jobEvent.getDateTime("lastModifiedDate");
192: // final int paid = jobEvent.getInt("jobEventPaid");
193: final var type = IBizJobEventType.values()[jobEventConfig.getInt("jobEventType")];
194:
195: return JobEvent.builder().withId(jobEventConfig.getId("uniqueIdentifier"))
196: .withType(type.getMappedType())
197: .withStartDateTime(jobEventConfig.getDateTime("jobEventStartDate"))
198: .withEndDateTime(jobEventConfig.getDateTime("jobEventEndDate"))
199: .withName(jobEventConfig.getString("jobEventName"))
200: .withDescription(jobEventConfig.getString("jobEventNotes"))
201: .withHourlyRate(jobEventConfig.getMoney("jobEventRate"))
202: .withEarnings(jobEventConfig.getMoney("jobEventEarnings"))
203: .withEvents(importJobEvents(jobEventConfig.getStream("children")))
204: .create();
205: /*
206: <key>tax1</key>
207: <real>22</real>
208:
209: */
210: }
211: }