1 package org.example.customer;
2
3 /*
4 * This is free and unencumbered software released into the public domain.
5 * Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software,
6 * either in source code form or as a compiled binary, for any purpose, commercial or
7 * non-commercial, and by any means.
8 *
9 * In jurisdictions that recognize copyright laws, the author or authors of this
10 * software dedicate any and all copyright interest in the software to the public domain.
11 * We make this dedication for the benefit of the public at large and to the detriment of
12 * our heirs and successors. We intend this dedication to be an overt act of relinquishment in
13 * perpetuity of all present and future rights to this software under copyright law.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES
18 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * For more information, please refer to: https://unlicense.org/
22 */
23
24 import java.io.Serializable;
25 import java.time.LocalDate;
26 import java.time.format.DateTimeFormatter;
27 import java.util.ArrayList;
28 import java.util.List;
29
30 import org.apache.commons.lang3.builder.EqualsBuilder;
31 import org.apache.commons.lang3.builder.HashCodeBuilder;
32 import org.apache.logging.log4j.LogManager;
33 import org.apache.logging.log4j.Logger;
34 import org.example.customer.utility.CustomerEntity;
35 import org.example.websecurity.XssSanitizer;
36 import org.example.websecurity.XssSanitizerImpl;
37
38 /**
39 * The Order Entity for the Customer application.
40 * <br>
41 * <br>
42 * This class represents the following DB Table:
43 *
44 * <pre>
45 * CREATE TABLE ORDERS (
46 * ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
47 * ORDER_DATE DATE NOT NULL,
48 * CUSTOMER_ID INTEGER,
49 * TOTAL_AMOUNT DECIMAL(12,2),
50 * ORDER_NUMBER VARCHAR(10),
51 * CONSTRAINT PK_ORDER PRIMARY KEY (ID)
52 * );
53 * </pre>
54 *
55 * @author Jonathan Earl
56 * @version 1.0
57 *
58 */
59 public final class Order extends CustomerEntity
60 implements Serializable, Comparable<Order>
61 {
62 private static final long serialVersionUID = 1L;
63
64 private static final Logger LOG = LogManager.getLogger();
65
66 private LocalDate myOrderDate;
67 private int myCustomerId;
68 private double myTotalAmount;
69 private String myOrderNumber;
70
71 private XssSanitizer mySanitizer;
72
73 private List<OrderItem> myOrderItems;
74
75 /**
76 * The default constructor for the Order class.
77 * <br>
78 * <br>
79 * The initial values are:
80 * <ul>
81 * <li>id: 1</li>
82 * <li>orderDate: Current Date</li>
83 * <li>customerId: 1</li>
84 * <li>totalAmount: 0.00</li>
85 * <li>orderNumber: null</li>
86 * <li>orderItems: empty</li>
87 * </ul>
88 */
89 public Order()
90 {
91 this(new XssSanitizerImpl());
92 LOG.debug("Finishing the default Constructor");
93 }
94
95 /**
96 * The overloaded constructor for the order class that takes an XssSanitizer as input.
97 * <p>
98 * The initial values are:
99 * <ul>
100 * <li>id: 1</li>
101 * <li>orderDate: Current Date</li>
102 * <li>customerId: 1</li>
103 * <li>totalAmount: 0.00</li>
104 * <li>orderNumber: null</li>
105 * <li>orderItems: empty</li>
106 * </ul>
107 *
108 * @param sanitizer the XssSanitizer used by this instance
109 */
110 public Order(final XssSanitizer sanitizer)
111 {
112 LOG.debug("Starting the overloaded Constructor");
113 final int initialId = 1;
114 final LocalDate initialOrderDate = LocalDate.now();
115 final int initialCustomerId = 1;
116 final String initialOrderNumber = null;
117 final List<OrderItem> initalOrderItems = new ArrayList<OrderItem>();
118
119 mySanitizer = sanitizer;
120
121 setId(initialId);
122 setOrderDate(initialOrderDate);
123 setCustomerId(initialCustomerId);
124 setOrderNumber(initialOrderNumber);
125 setOrderItems(initalOrderItems);
126 }
127
128 /**
129 * This will enable sorting of Order by Order number concatenated with Order Date.
130 * <br>
131 * <br>
132 * @param other the Order object to compare with
133 * @return the sort value of negative/zero/positive
134 */
135 @Override
136 public int compareTo(Order other)
137 {
138 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy");
139 String thisSortValue = this.getOrderNumber() + " "
140 + this.getOrderDate().format(formatter);
141 String otherSortValue = other.getOrderNumber() + " "
142 + other.getOrderDate().format(formatter);
143 return thisSortValue.compareToIgnoreCase(otherSortValue);
144 }
145
146 /**
147 * Returns the orderDate value for the Order.
148 *
149 * @return the orderDate value for the order
150 */
151 public LocalDate getOrderDate()
152 {
153 LOG.debug("returning the Oder Date: " + myOrderDate);
154 return myOrderDate;
155 }
156
157 /**
158 * Sets the order date value for the Customer.
159 * <p>
160 * The business rules are:
161 * <ul>
162 * <li>the order date must <strong>not</strong> be null</li>
163 * <li>the order date must <strong>today or earlier</strong></li>
164 * </ul>
165 *
166 * @param orderDate the value to set into the orderDate field
167 * @throws IllegalArgumentException if the first name is invalid
168 */
169 public void setOrderDate(final LocalDate orderDate)
170 {
171 LOG.debug("setting the order Date");
172
173 if (orderDate == null)
174 {
175 LOG.error("Order Date must not be null");
176 throw new IllegalArgumentException("Order Date must not be null");
177 }
178 LocalDate tomorrow = LocalDate.now().plusDays(1);
179 if (orderDate.isBefore(tomorrow))
180 {
181 this.myOrderDate = orderDate;
182 }
183 else
184 {
185 LOG.error("OrderDate must be today or earlier");
186 throw new IllegalArgumentException("OrderDate must be today or earlier");
187 }
188 }
189
190 /**
191 * Returns the id value for the Customer Id.
192 *
193 * @return the id value for the customer id
194 */
195 public int getCustomerId()
196 {
197 LOG.debug("returning the CustomerId: " + myCustomerId);
198 return myCustomerId;
199 }
200
201 /**
202 * Sets the id value for the Customer ID.
203 * <p>
204 * The business rules are:
205 * <ul>
206 * <li>the id must be 1 or greater</li>
207 * </ul>
208 *
209 * @param customerId the value to set into the customer id field
210 * @throws IllegalArgumentException if the customerId is invalid
211 */
212 public void setCustomerId(final int customerId)
213 {
214 LOG.debug("setting the CustomerId: " + customerId);
215 final int min = 1;
216
217 if (customerId < min)
218 {
219 LOG.error("CustomerId must be greater then zero");
220 throw new IllegalArgumentException("CustomerId must be greater then zero");
221 }
222 this.myCustomerId = customerId;
223 }
224
225
226 /**
227 * Returns the total amount for the order.
228 *
229 * @return the totalAmount value for the order
230 */
231 public double getTotalAmount()
232 {
233 LOG.debug("returning the totalAmount: " + myTotalAmount);
234 return myTotalAmount;
235 }
236
237 /**
238 * Sets the total amount value for the Orde.
239 * <p>
240 * The business rules are:
241 * <ul>
242 * <li>the TotalAmount must be 0.01 or greater</li>
243 * </ul>
244 *
245 * @param totalAmount the value to set into the totalAmount field
246 * @throws IllegalArgumentException if the totalAmount is invalid
247 */
248 public void setTotalAmount(final double totalAmount)
249 {
250 LOG.debug("setting the totalAmount: " + totalAmount);
251 final double min = 0.01;
252
253 if (totalAmount < min)
254 {
255 LOG.error("totalAmount must be greater then zero");
256 throw new IllegalArgumentException("totalAmount must be greater then zero");
257 }
258 this.myTotalAmount = totalAmount;
259 }
260
261 /**
262 * Returns the orderNumber value for the Order.
263 *
264 * @return the orderNumber value for the order
265 */
266 public String getOrderNumber()
267 {
268 LOG.debug("returning the OrderNumber: " + myOrderNumber);
269 return myOrderNumber;
270 }
271
272 /**
273 * Sets the orderNumber value for the Order.
274 * <p>
275 * The business rules are:
276 * <ul>
277 * <li>the orderNumber <strong>may</strong> be null</li>
278 * <li>the orderNumber must <strong>not</strong> be empty</li>
279 * <li>the orderNumber must min length of 2 chars</li>
280 * <li>the orderNumber must max length of 10 chars</li>
281 * <li>XSS strings within the orderNumber will be removed</li>
282 * </ul>
283 *
284 * @param orderNumber the value to set into the Order Number field
285 * @throws IllegalArgumentException if the orderNumber is invalid
286 */
287 public void setOrderNumber(final String orderNumber)
288 {
289 LOG.debug("setting the OrderNumber");
290 final int max = 10;
291 final int min = 2;
292
293 if (orderNumber == null)
294 {
295 LOG.debug("orderNumber is set to null");
296 this.myOrderNumber = null;
297 return;
298 }
299
300 String safeOrderNumber = mySanitizer.sanitizeInput(orderNumber);
301 if (safeOrderNumber.isEmpty())
302 {
303 LOG.error("OrderNumber must not be empty");
304 throw new IllegalArgumentException("OrderNumber must not be empty");
305 }
306 if (safeOrderNumber.length() > max || safeOrderNumber.length() < min)
307 {
308 LOG.error("OrderNumber must be between 2 and 10 chars in length");
309 throw new IllegalArgumentException("OrderNumber must be between 2 and 10 chars in length");
310 }
311 LOG.debug("setting the OrderNumber to: " + safeOrderNumber);
312 this.myOrderNumber = safeOrderNumber;
313 }
314
315 /**
316 * Returns the orderItem list value for the Order.
317 *
318 * @return the myOrderItems for the order
319 */
320 public List<OrderItem> getOrderItems()
321 {
322 LOG.debug("returning the OrderItems: " + myOrderItems);
323 return myOrderItems;
324 }
325
326 /**
327 * Sets the orderItem list value for the Order.
328 * <p>
329 * The business rules are:
330 * <ul>
331 * <li>the orderItems must <strong>not</strong> be null</li>
332 * <li>the orderItems may be empty</li>
333 * </ul>
334 *
335 * @param orderItems the list to set into the orderItems
336 * @throws IllegalArgumentException if the orderItems is invalid
337 */
338 public void setOrderItems(final List<OrderItem> orderItems)
339 {
340 LOG.debug("setting the Orderitems");
341 if (orderItems == null)
342 {
343 LOG.error("Orderitems must not be null");
344 throw new IllegalArgumentException("Orderitems must not be null");
345 }
346 double total = 0.0;
347 for (OrderItem current : orderItems)
348 {
349 total += current.getSubTotal();
350 }
351 this.myTotalAmount = total;
352 this.myOrderItems = orderItems;
353 }
354
355 /**
356 * The hashCode() method of the Order class.
357 * <p>
358 * <strong>This method uses:</strong>
359 * <ul>
360 * <li>id</li>
361 * <li>order date</li>
362 * <li>customer id</li>
363 * <li>order number</li>
364 * </ul>
365 *
366 * @see java.lang.Object#hashCode()
367 * @return the hashCode value for this Order object
368 */
369 @Override
370 public int hashCode()
371 {
372 LOG.debug("building HashCode");
373 return new HashCodeBuilder()
374 .append(getId())
375 .append(myOrderDate)
376 .append(myCustomerId)
377 .append(myOrderNumber)
378 .toHashCode();
379 }
380
381 /**
382 * The equals() method of the order class.
383 * <p>
384 * <strong>This method uses:</strong>
385 * <ul>
386 * <li>id</li>
387 * <li>order date</li>
388 * <li>customer id</li>
389 * <li>order number</li>
390 * </ul>
391 *
392 * @see java.lang.Object#equals(Object obj)
393 * @param obj the incoming object to compare against
394 * @return true if the fields being compared are equal
395 */
396 @Override
397 public boolean equals(final Object obj)
398 {
399 LOG.debug("checking equals");
400 if (obj instanceof Order)
401 {
402 final Order other = (Order) obj;
403 return new EqualsBuilder()
404 .append(getId(), other.getId())
405 .append(myOrderDate, other.myOrderDate)
406 .append(myCustomerId, other.myCustomerId)
407 .append(myOrderNumber, other.myOrderNumber)
408 .isEquals();
409 }
410 else
411 {
412 return false;
413 }
414 }
415
416
417
418 /**
419 * The toString method for the Order class.
420 *
421 * this method will return:<br>
422 * Order [Id=xxx, OrderDate=xx/xx/xx, CustomerId=xxx,
423 * TotalAmount=xxx, OrderNumber=xxx]
424 */
425 @Override
426 public String toString()
427 {
428 return "Order [Id=" + getId() + ", OrderDate=" + myOrderDate + ", CustomerId=" + myCustomerId
429 + ", TotalAmount=" + myTotalAmount + ", OrderNumber=" + myOrderNumber
430 + ", OrderItems=" + myOrderItems.size() + "]";
431 }
432 }