View Javadoc
1   /*
2    * Copyright (c) Patrick Magauran 2018.
3    *   Licensed under the AGPLv3. All conditions of said license apply.
4    *       This file is part of ABOS.
5    *
6    *       ABOS is free software: you can redistribute it and/or modify
7    *       it under the terms of the GNU Affero General Public License as published by
8    *       the Free Software Foundation, either version 3 of the License, or
9    *       (at your option) any later version.
10   *
11   *       ABOS is distributed in the hope that it will be useful,
12   *       but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   *       GNU Affero General Public License for more details.
15   *
16   *       You should have received a copy of the GNU Affero General Public License
17   *       along with ABOS.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package Controllers;
21  
22  import Exceptions.addressException;
23  import Utilities.*;
24  import Workers.AddCustomerWorker;
25  import javafx.application.Platform;
26  import javafx.beans.value.ObservableValue;
27  import javafx.collections.FXCollections;
28  import javafx.collections.ObservableList;
29  import javafx.event.ActionEvent;
30  import javafx.event.Event;
31  import javafx.fxml.FXML;
32  import javafx.fxml.FXMLLoader;
33  import javafx.geometry.Pos;
34  import javafx.scene.control.*;
35  import javafx.scene.control.cell.PropertyValueFactory;
36  import javafx.scene.input.KeyCode;
37  import javafx.scene.input.KeyEvent;
38  import javafx.scene.layout.Pane;
39  import javafx.stage.Stage;
40  import javafx.util.Callback;
41  import javafx.util.Pair;
42  
43  import java.io.IOException;
44  import java.math.BigDecimal;
45  import java.sql.SQLException;
46  import java.text.DecimalFormat;
47  import java.text.ParsePosition;
48  import java.util.Objects;
49  import java.util.Optional;
50  
51  //import java.awt.*;
52  
53  /**
54   * A dialog that allows the user to add a new customer or edit an existing customer.
55   *
56   * @author patrick
57   * @version 1.0
58   */
59  
60  @SuppressWarnings("WeakerAccess")
61  public class AddCustomerController {
62      private boolean edit = false; //States whether this is an edit or creation of a customer.
63      private Year yearInfo;
64      //private final JPanel contentPanel = new JPanel();
65      //Editable Field for user to input customer info.
66      @FXML
67      private CheckBox Delivered;
68      @FXML
69      private TextField Paid;
70      @FXML
71      private TableView ProductTable;
72      @FXML
73      private TextField Name;
74      @FXML
75      private TextField Address;
76      @FXML
77      private TextField ZipCode;
78      @FXML
79      private TextField Town;
80      @FXML
81      private TextField State;
82      @FXML
83      private TextField Phone;
84      @FXML
85      private TextField Email;
86      @FXML
87      private TextField DonationsT;
88      @FXML
89      private ComboBox<String> userCmbx;
90      @FXML
91      private Button okButton;
92      @FXML
93      private Label runningTotalLabel;
94      private Tab parentTab = null;
95      private Stage parentStage = null;
96      private Pane tPane;
97  
98      //Variables used to store regularly accessed info.
99      private String year = null;
100     private BigDecimal totalCostFinal = BigDecimal.ZERO;
101     //Variables used to calculate difference of orders when in edit mode.
102     private String NameEditCustomer = null;
103     private BigDecimal preEditOrderCost = BigDecimal.ZERO;
104     private Customer customerInfo = new Customer();
105     private Boolean columnsFilled = false;
106     private ObservableList<formattedProductProps> data;
107     private MainController mainCont;
108     private TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent;
109 
110     private String lastKey = null;
111 
112 
113     /**
114      * Used to open dialog with already existing customer information from year as specified in Utilities.Customer Report.
115      *
116      */
117     public void initAddCust(Customer customer, MainController mainController, Tab parent, TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent) {
118         initAddCust(customer, mainController, parent, customer.getUser(), treeParent);
119     }
120 
121     public void initAddCust(Customer customer, MainController mainController, Tab parent, String user, TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent) {
122         this.treeParent = treeParent;
123         customer.refreshData();
124         mainCont = mainController;
125         parentTab = parent;
126         year = customer.getYear();
127         yearInfo = new Year(year);
128         customerInfo = customer;
129         edit = true;
130         User curUser = new User(year);
131         curUser.getuManage().forEach(uMan -> {
132             userCmbx.getItems().add(uMan);
133         });
134         userCmbx.getSelectionModel().select(user);
135         userCmbx.setDisable(true);
136         //Set the address
137         String[] addr = customerInfo.getCustAddressFrmName();
138         String city = addr[0];
139         String state = addr[1];
140         String zip = addr[2];
141         String streetAdd = addr[3];
142         //Fill in Utilities.Customer info fields.
143         Address.setText(streetAdd);
144         Town.setText(city);
145         State.setText(state);
146         ZipCode.setText(zip);
147         Phone.setText(customerInfo.getPhone());
148         Paid.setText(customerInfo.getPaid().toPlainString());
149         Delivered.setSelected(customerInfo.getDelivered());
150         Email.setText(customerInfo.getEmail());
151         Name.setText(customerInfo.getName());
152         DecimalFormat format = new DecimalFormat("#.0");
153 
154         DonationsT.setTextFormatter(new TextFormatter<>(c ->
155         {
156             if (c.getControlNewText().isEmpty()) {
157                 return c;
158             }
159 
160             ParsePosition parsePosition = new ParsePosition(0);
161             Object object = format.parse(c.getControlNewText(), parsePosition);
162 
163             if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
164                 return null;
165             } else {
166                 return c;
167             }
168         }));
169         Paid.setTextFormatter(new TextFormatter<>(c ->
170         {
171             if (c.getControlNewText().isEmpty()) {
172                 return c;
173             }
174 
175             ParsePosition parsePosition = new ParsePosition(0);
176             Object object = format.parse(c.getControlNewText(), parsePosition);
177 
178             if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
179                 return null;
180             } else {
181                 return c;
182             }
183         }));
184         DonationsT.setText(customerInfo.getDontation().toPlainString());
185         BigDecimal preEditDonations = customerInfo.getDontation();
186         //Fill the table with their previous order info on record.
187         fillOrderedTable();
188 
189         NameEditCustomer = customerInfo.getName();
190         edit = true;
191         ProductTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
192         Platform.runLater(() -> {
193             Name.requestFocus();
194 
195         });
196 
197         //Add a Event to occur if a cell is changed in the table
198 
199     }
200 
201     public void initAddCust(String aYear, MainController mainController, Stage parent, TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent) {
202         parentStage = parent;
203         initAddCust(aYear, mainController, (Tab) null, treeParent);
204     }
205 
206     public void initAddCust(String aYear, MainController mainController, Stage parent, String User, TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent) {
207         parentStage = parent;
208         initAddCust(aYear, mainController, (Tab) null, User, treeParent);
209     }
210 
211     public void initAddCust(String aYear, MainController mainController, Tab parent, TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent) {
212         initAddCust(aYear, mainController, parent, DbInt.getUserName(), treeParent);
213 
214     }
215 
216     public void initAddCust(String aYear, MainController mainController, Tab parent, String user, TreeItem<TreeItemPair<String, Pair<String, Object>>> treeParent) {
217         this.treeParent = treeParent;
218 
219         mainCont = mainController;
220         parentTab = parent;
221         NameEditCustomer = "";
222         year = aYear;
223         yearInfo = new Year(year);
224         User curUser = new User(year);
225         curUser.getuManage().forEach(uMan -> {
226             userCmbx.getItems().add(uMan);
227         });
228         userCmbx.getSelectionModel().select(user);
229         Name.setText(Config.getProp("CustomerName"));
230         //Name.requestFocus();
231         Address.setText(Config.getProp("CustomerAddress"));
232         Town.setText(Config.getProp("CustomerTown"));
233         State.setText(Config.getProp("CustomerState"));
234         ZipCode.setText(Config.getProp("CustomerZipCode"));
235         ZipCode.setOnKeyTyped(keyEvent -> {
236             if (ZipCode.getCharacters().length() >= 4) {
237                 String zip = ZipCode.getText() + keyEvent.getCharacter();
238 
239                 String cityAndState = "";
240                 try {
241                     cityAndState = Geolocation.getCityState(zip);
242                 } catch (IOException e1) {
243                     LogToFile.log(e1, Severity.WARNING, "Couldn't contact geolocation service. Please try again or enter the adress manually and contact suport.");
244                 }
245                 String[] StateTown = cityAndState.split("&");
246                 String state = StateTown[1];
247                 String town = StateTown[0];
248                 Town.setText(town);
249                 State.setText(state);
250             }
251         });
252         Phone.setText(Config.getProp("CustomerPhone"));
253         Email.setText(Config.getProp("CustomerEmail"));
254 
255         Paid.setText(!Config.getProp("CustomerPaid").isEmpty() ? Config.getProp("CustomerPaid") : "0.0");
256         Delivered.setSelected(Boolean.valueOf(Config.getProp("CustomerDelivered")));
257 
258         DonationsT.setText(!Config.getProp("CustomerDonation").isEmpty() ? Config.getProp("CustomerDonation") : "0.0");
259         DecimalFormat format = new DecimalFormat("#.0");
260 
261         DonationsT.setTextFormatter(new TextFormatter<>(c ->
262         {
263             if (c.getControlNewText().isEmpty()) {
264                 return c;
265             }
266 
267             ParsePosition parsePosition = new ParsePosition(0);
268             Object object = format.parse(c.getControlNewText(), parsePosition);
269 
270             if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
271                 return null;
272             } else {
273 
274                 return c;
275             }
276         }));
277         Paid.setTextFormatter(new TextFormatter<>(c ->
278         {
279             if (c.getControlNewText().isEmpty()) {
280                 return c;
281             }
282 
283             ParsePosition parsePosition = new ParsePosition(0);
284             Object object = format.parse(c.getControlNewText(), parsePosition);
285 
286             if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
287                 return null;
288             } else {
289 
290                 return c;
291             }
292         }));
293 
294         fillTable();
295         ProductTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
296         Platform.runLater(() -> {
297 
298             Name.requestFocus();
299         });
300     }
301 
302     public void initialize() {
303 
304     }
305 
306     @FXML
307     public void submit(ActionEvent event) {
308 
309         if (infoEntered()) {
310             if (!edit && yearInfo.addressExists(Address.getText(), ZipCode.getText())) {
311                 Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
312                 alert.setTitle("Duplicate");
313                 alert.setHeaderText("The address you have entered appears to be a duplicate");
314                 alert.setContentText("Would you like to continue?");
315 
316 
317                 Optional<ButtonType> result = alert.showAndWait();
318                 if (result.get() == ButtonType.OK) {
319                     commitChanges();
320                     //updateTots();
321                     //close
322                     // ... user chose OK
323                 }
324             } else if (new BigDecimal(Paid.getText()).subtract(totalCostFinal).compareTo(BigDecimal.ZERO) > 0) {
325                 Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
326                 alert.setTitle("Overpaid");
327                 alert.setHeaderText("You entered more paid than total order.");
328                 alert.setContentText("Would you like to place the extra as a donation?");
329 
330 
331                 Optional<ButtonType> result = alert.showAndWait();
332                 if (result.get() == ButtonType.OK) {
333                     DonationsT.setText(new BigDecimal(Paid.getText()).subtract(totalCostFinal).toPlainString());
334                     commitChanges();
335                     //updateTots();
336                     //close
337                     // ... user chose OK
338                 }
339             } else {
340                 commitChanges();
341 
342             }
343 
344         } else {
345             //javafx.scene.control.Dialog dialog = new Dialog();
346             Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
347             alert.setTitle("");
348             alert.setHeaderText("It appears you have not entered any data");
349             alert.setContentText("Would you like to re-enter the data?");
350 
351 
352             Optional<ButtonType> result = alert.showAndWait();
353             if (result.get() == ButtonType.CANCEL) {
354                 if (parentTab != null) {
355                     mainCont.closeTab(parentTab);
356                 } else {
357                     parentStage.close();
358                 }
359                 //close
360                 // ... user chose OK
361             }
362         }
363     }
364 
365     @FXML
366     public void cancel(ActionEvent event) {
367         if (parentTab != null) {
368             mainCont.closeTab(parentTab);
369         } else {
370             parentStage.close();
371         }
372         //close
373         // ... user chose OK
374 
375     }
376 
377     /**
378      * Fills the table with quantitys set to 0.
379      */
380     private void fillTable() {
381 
382         formattedProduct[] productArray = yearInfo.getAllProducts();
383         Object[][] rows = new Object[productArray.length][6];
384         data = FXCollections.observableArrayList();
385 
386         int i = 0;
387         for (formattedProduct productOrder : productArray) {
388             //String productID, String productName, String productSize, String productUnitPrice, String productCategory, int orderedQuantity, BigDecimal extendedCost
389             formattedProductProps prodProps = new formattedProductProps(productOrder.productKey, productOrder.productID, productOrder.productName, productOrder.productSize, productOrder.productUnitPrice, productOrder.productCategory, productOrder.orderedQuantity, productOrder.extendedCost);
390             data.add(prodProps);
391             i++;
392         }
393 
394         // ProductTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
395         //ProductTable.getSelectionModel().setCellSelectionEnabled(true);
396         // ProductTable.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
397 
398         ProductTable.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
399             if (ProductTable.getEditingCell() == null && t.getCode() == KeyCode.ENTER) {
400                 if (t.isShiftDown()) {
401                     ProductTable.getSelectionModel().selectAboveCell();
402                 } else {
403                     ProductTable.getSelectionModel().selectBelowCell();
404                 }
405                 t.consume();
406             } else {
407                 TablePosition tp;
408                 if (!t.isControlDown() &&
409                         (t.getCode().isDigitKey())) {
410                     lastKey = t.getText();
411                     tp = ProductTable.getFocusModel().getFocusedCell();
412                     ProductTable.edit(tp.getRow(), tp.getTableColumn());
413                     lastKey = null;
414                 }
415             }
416 
417             /*//I decided not to override the default tab behavior
418             //using ctrl tab for cell traversal, but arrow keys are better
419             if (t.isControlDown() && t.getCode() == KeyCode.TAB) {
420                 if (t.isShiftDown()) {
421                     tv.getSelectionModel().selectLeftCell();
422                 } else {
423                     tv.getSelectionModel().selectRightCell();
424                 }
425                 t.consume();
426             }*/
427         });
428 
429         /*ProductTable.setOnKeyPressed((KeyEvent t) -> {
430             TablePosition tp;
431             if (!t.isControlDown() &&
432                     (t.getCode().isLetterKey() || t.getCode().isDigitKey())) {
433                 lastKey = t.getText();
434                 tp = ProductTable.getFocusModel().getFocusedCell();
435                 ProductTable.edit(tp.getRow(),tp.getTableColumn());
436                 lastKey = null;
437             }
438         });*/
439         Callback<TableColumn<formattedProductProps, String>, TableCell<formattedProductProps, String>> txtCellFactory =
440                 (TableColumn<formattedProductProps, String> p) -> {return new EditingCell();};
441 
442         if (!columnsFilled) {
443             String[][] columnNames = {{"ID", "productID"}, {"Item", "productName"}, {"Size", "productSize"}, {"Price/Item", "productUnitPrice"}};
444             for (String[] column : columnNames) {
445                 TableColumn<formattedProductProps, String> tbCol = new TableColumn<>(column[0]);
446                 tbCol.setCellValueFactory(new PropertyValueFactory<>(column[1]));
447                 ProductTable.getColumns().add(tbCol);
448             }
449         }
450 
451 
452         //{"Quantity", "orderedQuantity"}, {"Price", "extendedCost"}
453         TableColumn<formattedProductProps, String> quantityCol = new TableColumn<>("Quantity");
454         TableColumn<formattedProductProps, String> priceCol = new TableColumn<>("Price");
455         quantityCol.setCellValueFactory(new PropertyValueFactory<>("orderedQuantityString"));
456 
457         quantityCol.setCellFactory(txtCellFactory);
458 
459         quantityCol.setOnEditCommit(t -> {
460             //t.getTableView().getItems().get(t.getTablePosition().getRow()).orderedQuantity.set(Integer.valueOf(t.getNewValue()));
461             try {
462                 int quantity = Integer.valueOf(t.getNewValue());
463 
464                 BigDecimal unitCost = t.getTableView().getItems().get(t.getTablePosition().getRow()).productUnitPrice.get();
465                 //Removes $ from cost and multiplies to get the total cost for that item
466                 BigDecimal ItemTotalCost = unitCost.multiply(new BigDecimal(quantity));
467                 t.getRowValue().extendedCost.set(ItemTotalCost);
468                 t.getRowValue().orderedQuantity.set(quantity);
469                 t.getRowValue().orderedQuantityString.set(String.valueOf(quantity));
470 
471                 data.get(t.getTablePosition().getRow()).orderedQuantity.set(quantity);
472                 data.get(t.getTablePosition().getRow()).extendedCost.set(ItemTotalCost);
473                 t.getTableView().refresh();
474                 totalCostFinal = new BigDecimal(DonationsT.getText());
475                 t.getTableView().getItems().forEach(item -> {
476                     totalCostFinal = totalCostFinal.add(item.getExtendedCost());//Recalculate Utilities.Order total
477 
478                 });
479                 runningTotalLabel.setText("Total: " + totalCostFinal.toPlainString());
480             } catch (NumberFormatException e) {
481                 Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid number");
482                 alert.setHeaderText("You have entered an invalid number.");
483                 alert.show();
484                 t.getRowValue().productUnitPriceString.set(t.getOldValue());
485                 t.getTableView().getSelectionModel().selectAboveCell();
486 
487                 t.getTableView().refresh();
488             }
489 
490         });
491         priceCol.setCellValueFactory(new PropertyValueFactory<>("extendedCost"));
492 
493 
494         ProductTable.getColumns().addAll(quantityCol, priceCol);
495 
496         columnsFilled = true;
497 
498         ProductTable.setItems(data);
499 
500 
501     }
502 
503     /**
504      * Fills product table with info with quantities set to Amount customer ordered.
505      */
506     private void fillOrderedTable() {
507         Order.orderArray order = Order.createOrderArray(year, customerInfo.getId(), false);
508         data = FXCollections.observableArrayList();
509         Callback<TableColumn<formattedProductProps, String>, TableCell<formattedProductProps, String>> txtCellFactory =
510                 (TableColumn<formattedProductProps, String> p) -> {return new EditingCell();};
511         ProductTable.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
512             if (ProductTable.getEditingCell() == null && t.getCode() == KeyCode.ENTER) {
513                 if (t.isShiftDown()) {
514                     ProductTable.getSelectionModel().selectAboveCell();
515                 } else {
516                     ProductTable.getSelectionModel().selectBelowCell();
517                 }
518                 t.consume();
519             } else {
520                 TablePosition tp;
521                 if (!t.isControlDown() &&
522                         (t.getCode().isDigitKey())) {
523                     lastKey = t.getText();
524                     tp = ProductTable.getFocusModel().getFocusedCell();
525                     ProductTable.edit(tp.getRow(), tp.getTableColumn());
526                     lastKey = null;
527                 }
528             }
529 
530             /*//I decided not to override the default tab behavior
531             //using ctrl tab for cell traversal, but arrow keys are better
532             if (t.isControlDown() && t.getCode() == KeyCode.TAB) {
533                 if (t.isShiftDown()) {
534                     tv.getSelectionModel().selectLeftCell();
535                 } else {
536                     tv.getSelectionModel().selectRightCell();
537                 }
538                 t.consume();
539             }*/
540         });
541         totalCostFinal = new BigDecimal(DonationsT.getText());
542 
543 
544         int i = 0;
545         for (formattedProduct productOrder : order.orderData) {
546             //String productID, String productName, String productSize, String productUnitPrice, String productCategory, int orderedQuantity, BigDecimal extendedCost
547             formattedProductProps prodProps = new formattedProductProps(productOrder.productKey, productOrder.productID, productOrder.productName, productOrder.productSize, productOrder.productUnitPrice, productOrder.productCategory, productOrder.orderedQuantity, productOrder.extendedCost);
548             data.add(prodProps);
549             totalCostFinal = totalCostFinal.add(productOrder.extendedCost);
550             i++;
551         }
552         if (!columnsFilled) {
553             String[][] columnNames = {{"ID", "productID"}, {"Item", "productName"}, {"Size", "productSize"}, {"Price/Item", "productUnitPrice"}};
554             for (String[] column : columnNames) {
555                 TableColumn<formattedProductProps, String> tbCol = new TableColumn<>(column[0]);
556                 tbCol.setCellValueFactory(new PropertyValueFactory<>(column[1]));
557                 ProductTable.getColumns().add(tbCol);
558             }
559         }
560         //{"Quantity", "orderedQuantity"}, {"Price", "extendedCost"}
561         TableColumn<formattedProductProps, String> quantityCol = new TableColumn<>("Quantity");
562         TableColumn<formattedProductProps, String> priceCol = new TableColumn<>("Price");
563         quantityCol.setCellValueFactory(new PropertyValueFactory<>("orderedQuantityString"));
564 
565         quantityCol.setCellFactory(txtCellFactory);
566 
567         quantityCol.setOnEditCommit(t -> {
568             //t.getTableView().getItems().get(t.getTablePosition().getRow()).orderedQuantity.set(Integer.valueOf(t.getNewValue()));
569             try {
570                 int quantity = Integer.valueOf(t.getNewValue());
571 
572                 BigDecimal unitCost = t.getTableView().getItems().get(t.getTablePosition().getRow()).productUnitPrice.get();
573                 //Removes $ from cost and multiplies to get the total cost for that item
574                 BigDecimal ItemTotalCost = unitCost.multiply(new BigDecimal(quantity));
575                 t.getRowValue().extendedCost.set(ItemTotalCost);
576                 t.getRowValue().orderedQuantity.set(quantity);
577                 t.getRowValue().orderedQuantityString.set(String.valueOf(quantity));
578 
579                 data.get(t.getTablePosition().getRow()).orderedQuantity.set(quantity);
580                 data.get(t.getTablePosition().getRow()).extendedCost.set(ItemTotalCost);
581                 t.getTableView().refresh();
582                 totalCostFinal = new BigDecimal(DonationsT.getText());
583                 t.getTableView().getItems().forEach(item -> {
584                     totalCostFinal = totalCostFinal.add(item.getExtendedCost());//Recalculate Utilities.Order total
585 
586                 });
587                 runningTotalLabel.setText("Total: " + totalCostFinal.toPlainString());
588 
589             } catch (NumberFormatException e) {
590                 Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid number");
591                 alert.setHeaderText("You have entered an invalid number.");
592                 alert.show();
593                 t.getRowValue().productUnitPriceString.set(t.getOldValue());
594                 t.getTableView().getSelectionModel().selectAboveCell();
595 
596                 t.getTableView().refresh();
597             }
598 
599         });
600         priceCol.setCellValueFactory(new PropertyValueFactory<>("extendedCost"));
601         ProductTable.getColumns().addAll(quantityCol, priceCol);
602 
603         columnsFilled = true;
604 
605         ProductTable.setItems(data);
606 
607         //Fills original totals to calculate new values to insert in TOTALS table
608         int preEditMulchSales = getNoMulchOrdered();
609         int preEditLawnProductSales = getNoLawnProductsOrdered();
610         int preEditLivePlantSales = getNoLivePlantsOrdered();
611         runningTotalLabel.setText("Total: " + totalCostFinal.toPlainString());
612 
613     }
614 
615     /**
616      * Commits table to the Database
617      */
618     private void commitChanges() {
619 
620         //ProgressDialog progDial = new ProgressDialog();
621         ProgressForm progDial = new ProgressForm();
622 //Do check if new or not, send -1 as ID
623 
624         AddCustomerWorker addCustWork = new AddCustomerWorker(edit ? customerInfo.getId() : -1, Address.getText(),
625                 Town.getText(),
626                 State.getText(),
627                 year,
628                 ProductTable,
629                 Name.getText(),
630                 ZipCode.getText(),
631                 Phone.getText(),
632                 Email.getText(),
633                 DonationsT.getText().isEmpty() ? "0.0" : DonationsT.getText(),
634                 Objects.equals(NameEditCustomer, "") ? Name.getText() : NameEditCustomer,
635                 Paid.getText().isEmpty() ? "0.0" : Paid.getText(),
636                 Delivered.isSelected(),
637                 userCmbx.getSelectionModel().getSelectedItem());
638 
639         /*addCustWork.addPropertyChangeListener(event -> {
640             switch (event.getPropertyName()) {
641                 case "progress":
642                     progDial.progressBar.setIndeterminate(false);
643                     progDial.progressBar.setValue((Integer) event.getNewValue());
644                     break;
645                 case "state":
646                     switch ((SwingWorker.StateValue) event.getNewValue()) {
647                         case DONE:
648                             try {
649                                 int success = addCustWork.get();
650                                 if (success == 1) {
651                                     updateTots();
652                                     dispose();
653                                     setVisible(false);
654                                 }
655                             } catch (CancellationException e) {
656                                 Utilities.LogToFile.log(e, Utilities.Severity.INFO, "The process was cancelled.");
657                             } catch (Exception e) {
658                                 Utilities.LogToFile.log(e, Utilities.Severity.WARNING, "The process Failed.");
659                             }
660                             addCustWork = null;
661                             progDial.dispose();
662                             break;
663                         case STARTED:
664                         case PENDING:
665                             progDial.progressBar.setVisible(true);
666                             progDial.progressBar.setIndeterminate(true);
667                             break;
668                     }
669                     break;
670             }
671         });*/
672         progDial.activateProgressBar(addCustWork);
673         addCustWork.setOnSucceeded(event -> {
674             Pane newPane = null;
675             FXMLLoader loader;
676             progDial.getDialogStage().close();
677 //            updateTots();
678             loader = new FXMLLoader(getClass().getResource("/UI/Year.fxml"));
679             try {
680                 loader.load();
681             } catch (IOException e) {
682                 LogToFile.log(e, Severity.SEVERE, "Error loading window. Please retry then reinstall application. If error persists, contact the developers.");
683             }
684             if (treeParent != null) {
685                 mainCont.addCustomerToTreeView(addCustWork.getValue(), treeParent);
686             }
687             YearController yearCont = loader.getController();
688             yearCont.initYear(year);
689             if (parentTab == null) {
690                 parentStage.close();
691             } else {
692                 mainCont.closeTab(parentTab);
693 
694             }
695             //mainCont.addTab(newPane, "Year View - " + year);
696         });
697         addCustWork.setOnFailed(event -> {
698             progDial.getDialogStage().close();
699 
700             Throwable e = addCustWork.getException();
701             if (e instanceof addressException) {
702                 LogToFile.log(null, Severity.WARNING, "Invalid Address. Please Verify spelling and numbers are correct.");
703 
704             }
705             if (e instanceof SQLException) {
706                 LogToFile.log((SQLException) e, Severity.SEVERE, CommonErrors.returnSqlMessage(((SQLException) addCustWork.getException())));
707 
708             }
709             if (e instanceof InterruptedException) {
710                 if (addCustWork.isCancelled()) {
711                     LogToFile.log((InterruptedException) e, Severity.FINE, "Add Customer process canceled.");
712 
713                 }
714             }
715             if (e instanceof IOException) {
716                 LogToFile.log((IOException) e, Severity.WARNING, "Error contacting geolaction service. Please try again or contasct support.");
717             }
718 
719         });
720 
721 
722         progDial.getDialogStage().show();
723         new Thread(addCustWork).start();
724     }
725 
726     /**
727      * Loops through Table to get total amount of Bulk Mulch ordered.
728      *
729      * @return The amount of Bulk mulch ordered
730      */
731     private int getNoMulchOrdered() {
732         int quantMulchOrdered = 0;
733         for (formattedProductProps aData : data) {
734             if ((aData.getProductName().contains("Mulch")) && (aData.getProductName().contains("Bulk"))) {
735                 quantMulchOrdered += aData.getOrderedQuantity();
736             }
737         }
738         return quantMulchOrdered;
739     }
740 
741     /**
742      * Loops through Table to get total amount of Lawn and Garden Products ordered.
743      *
744      * @return The amount of Lawn and Garden Products ordered
745      */
746     private int getNoLivePlantsOrdered() {
747         int livePlantsOrdered = 0;
748         for (formattedProductProps aData : data) {
749             if (aData.getProductName().contains("-P") || aData.getProductName().contains("-FV")) {
750                 livePlantsOrdered += aData.getOrderedQuantity();
751             }
752         }
753         return livePlantsOrdered;
754     }
755 
756     /**
757      * Loops through Table to get total amount of Lawn Products ordered.
758      *
759      * @return The amount of Live Plants ordered
760      */
761     private int getNoLawnProductsOrdered() {
762         int lawnProductsOrdered = 0;
763         for (formattedProductProps aData : data) {
764             if (aData.getProductName().contains("-L")) {
765                 lawnProductsOrdered += aData.getOrderedQuantity();
766             }
767         }
768         return lawnProductsOrdered;
769     }
770 
771     /**
772      * Calculates the amount of commission to be earned.
773      *
774      * @param totalCost the Sub total for all orders
775      * @return Commission to be earned
776      */
777     private BigDecimal getCommission(BigDecimal totalCost) {
778         BigDecimal commision = BigDecimal.ZERO;
779         if ((totalCost.compareTo(new BigDecimal("299.99")) > 0) && (totalCost.compareTo(new BigDecimal("500.01")) < 0)) {
780             commision = totalCost.multiply(new BigDecimal("0.05"));
781         } else if ((totalCost.compareTo(new BigDecimal("500.01")) > 0) && (totalCost.compareTo(new BigDecimal("1000.99")) < 0)) {
782             commision = totalCost.multiply(new BigDecimal("0.1"));
783         } else if (totalCost.compareTo(new BigDecimal("1000")) >= 0) {
784             commision = totalCost.multiply(new BigDecimal("0.15"));
785         }
786         return commision;
787     }
788 
789     /**
790      * Anaylizes if any text was entered into both the Address and Name field and if both are empty returns false, else true
791      *
792      * @return if required info was entered
793      */
794     private boolean infoEntered() {
795         return !((Name.getText().isEmpty()) && (Address.getText().isEmpty()));
796     }
797 
798     private class EditingCell<S, T> extends TableCell<S, T> {
799 
800         private TextField textField;
801 
802         @Override
803         public void commitEdit(T item) {
804             // This block is necessary to support commit on losing focus, because
805             // the baked-in mechanism sets our editing state to false before we can
806             // intercept the loss of focus. The default commitEdit(...) method
807             // simply bails if we are not editing...
808             if (!isEditing() && !item.equals(getItem())) {
809                 TableView<S> table = getTableView();
810                 if (table != null) {
811                     TableColumn<S, T> column = getTableColumn();
812                     TableColumn.CellEditEvent<S, T> event = new TableColumn.CellEditEvent<>(
813                             table, new TablePosition<S, T>(table, getIndex(), column),
814                             TableColumn.editCommitEvent(), item
815                     );
816                     Event.fireEvent(column, event);
817                 }
818             }
819 
820             super.commitEdit(item);
821         }
822 
823         private void commitEditString(String val) {
824             commitEdit((T) val);
825         }
826         @Override
827         public void startEdit() {
828             if (!isEmpty()) {
829                 super.startEdit();
830                 createTextField();
831                 setText(null);
832                 setGraphic(textField);
833                 //setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
834                 Platform.runLater(() -> {//without this space erases text, f2 doesn't
835                     textField.requestFocus();//also selects
836                 });
837                 if (lastKey != null) {
838                     textField.setText(lastKey);
839                     Platform.runLater(() -> {
840                         textField.deselect();
841                         textField.end();
842                     });
843                 }
844             }
845         }
846 
847         public void commit() {
848             commitEditString(textField.getText());
849         }
850 
851         @Override
852         public void cancelEdit() {
853             super.cancelEdit();
854             try {
855                 setText(getItem().toString());
856             } catch (Exception ignored) {}
857             setGraphic(null);
858         }
859 
860         @Override
861         public void updateItem(T item, boolean empty) {
862             super.updateItem(item, empty);
863 
864             if (empty) {
865                 setText(null);
866                 setGraphic(null);
867             } else if (isEditing()) {
868                 if (textField != null) {
869                     textField.setText(getString());
870                 }
871                 setText(null);
872                 setGraphic(textField);
873             } else {
874                 setText(getString());
875                 setGraphic(null);
876                 if (getTableColumn().getText().equals("amount")) { setAlignment(Pos.CENTER_RIGHT); }
877             }
878         }
879 
880         private void createTextField() {
881             textField = new TextField(getString());
882 
883             //doesn't work if clicking a different cell, only focusing out of table
884             textField.focusedProperty().addListener(
885                     (ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> {
886                         if (!arg2) commitEditString(textField.getText());
887                     });
888 
889             textField.setOnKeyReleased((KeyEvent t) -> {
890                 if (t.getCode() == KeyCode.ENTER) {
891                     commitEditString(textField.getText());
892                     EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
893                     EditingCell.this.getTableView().requestFocus();
894                 } else if (t.getCode() == KeyCode.RIGHT) {
895                     getTableView().getSelectionModel().selectRightCell();
896                     t.consume();
897                 } else if (t.getCode() == KeyCode.LEFT) {
898                     getTableView().getSelectionModel().selectLeftCell();
899                     t.consume();
900                 } else if (t.getCode() == KeyCode.UP) {
901                     getTableView().getSelectionModel().selectAboveCell();
902                     t.consume();
903                 } else if (t.getCode() == KeyCode.DOWN) {
904                     getTableView().getSelectionModel().selectBelowCell();
905                     t.consume();
906                 } else if (t.getCode() == KeyCode.ESCAPE) {
907                     cancelEdit();
908                 }
909             });
910 
911             textField.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent t) -> {
912                 if (t.getCode() == KeyCode.DELETE) {
913                     t.consume();//stop from deleting line in table keyevent
914                 }
915             });
916         }
917 
918         private String getString() {
919             return getItem() == null ? "" : getItem().toString();
920         }
921     }
922 
923 }