From e32f457a3c725d8d4fdecf329d532229b515b81e Mon Sep 17 00:00:00 2001
From: BEAUVAIS ANTOINE <antoine.beauvais@etu.unistra.fr>
Date: Thu, 23 Sep 2021 23:01:40 +0200
Subject: [PATCH] Added simple stock manipulation through transaction.

---
 .../unistra/sil/erp/back/DatabaseSystem.java  |  2 +-
 .../back/controller/api/ApiErrorHandler.java  | 15 +++
 .../api/ApiResourceNotFoundException.java     | 28 ++++++
 .../api/ApiSubmitTransactionController.java   | 47 +++++++--
 .../back/db/DatabaseConnectionException.java  |  9 ++
 .../sil/erp/back/db/DatabaseInterface.java    | 18 ++++
 .../db/DatabaseResourceNotFoundException.java | 26 +++++
 .../sil/erp/back/db/DatabaseSQLiteImpl.java   | 98 +++++++++++++++++++
 .../erp/back/db/DatabaseUpdateException.java  | 27 +++++
 9 files changed, 263 insertions(+), 7 deletions(-)
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java
 create mode 100644 src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java

diff --git a/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java b/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java
index 4cdb1f3..e32e6a8 100644
--- a/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java
+++ b/src/main/java/fr/unistra/sil/erp/back/DatabaseSystem.java
@@ -33,7 +33,7 @@ public class DatabaseSystem {
             DatabaseSystem.instance = new DatabaseSQLiteImpl();
         
         if(DatabaseSystem.instance == null)
-            throw new DatabaseConnectionException();
+            throw new DatabaseConnectionException("Failed to create DB.");
         
         return DatabaseSystem.instance;
     }
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java
index fbd17bf..e3114b4 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiErrorHandler.java
@@ -53,4 +53,19 @@ public class ApiErrorHandler {
                 HttpStatus.BAD_REQUEST);
     }
     
+    /**
+     * Handler for HTTP 404 Not Found.
+     * @param ex the thrown Exception.
+     * @param request the HTTP Servlet request.
+     * @param response the HTTP Servlet response.
+     * @return the response to the client.
+     */
+    @ExceptionHandler(ApiResourceNotFoundException.class)
+    public ResponseEntity<Object> handleResourceNotFound(Exception ex,
+            HttpServletRequest request, HttpServletResponse response)
+    {
+        return new ResponseEntity<>(new ErrorMessage(ex.getMessage()),
+                HttpStatus.NOT_FOUND);
+    }
+    
 }
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java
new file mode 100644
index 0000000..0676cef
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiResourceNotFoundException.java
@@ -0,0 +1,28 @@
+/*
+ * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
+ * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
+ */
+package fr.unistra.sil.erp.back.controller.api;
+
+/**
+ * API Exception for HTTP 404 Not Found.
+ * 
+ * If a resource requested by the user does not exist, this exception
+ * is thrown and handled through ApiErrorHandler.class.
+ * 
+ * @see ApiErrorHandler
+ * 
+ * @author BEAUVAIS ANTOINE
+ */
+public class ApiResourceNotFoundException extends Exception {
+    
+    /**
+     * Class constructor.
+     * @param errMsg the error message.
+     */
+    public ApiResourceNotFoundException(String errMsg)
+    {
+        super(errMsg);
+    }
+    
+}
diff --git a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java
index 33f6b5c..b39c303 100644
--- a/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java
+++ b/src/main/java/fr/unistra/sil/erp/back/controller/api/ApiSubmitTransactionController.java
@@ -1,13 +1,19 @@
 /*
- * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
- * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
+ * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
  */
 package fr.unistra.sil.erp.back.controller.api;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonParseException;
 import static fr.unistra.sil.erp.back.Config.MAPPING_SUBTRANSAC;
+import fr.unistra.sil.erp.back.DatabaseSystem;
 import fr.unistra.sil.erp.back.controller.ISubmitTransactionController;
+import fr.unistra.sil.erp.back.db.DatabaseConnectionException;
+import fr.unistra.sil.erp.back.db.DatabaseInterface;
+import fr.unistra.sil.erp.back.db.DatabaseResourceNotFoundException;
+import fr.unistra.sil.erp.back.db.DatabaseUpdateException;
+import fr.unistra.sil.erp.back.model.Stock;
 import fr.unistra.sil.erp.back.model.Transaction;
 import java.io.IOException;
 import java.util.logging.Level;
@@ -35,11 +41,15 @@ public class ApiSubmitTransactionController
      * @param response the HTTP Servlet Response provided by Spring.
      * @return the response for the user.
      * @throws ApiBadRequestException if the query failed.
+     * @throws ApiServerErrorException Server error.
+     * @throws ApiResourceNotFoundException Stock not found.
      */
     @RequestMapping(value=MAPPING_SUBTRANSAC, method = RequestMethod.POST)
     @Override
     public ResponseEntity<Object> submitTransaction(HttpServletRequest request,
-            HttpServletResponse response) throws ApiBadRequestException
+            HttpServletResponse response)
+            throws ApiBadRequestException, ApiServerErrorException,
+                   ApiResourceNotFoundException
     {
         Gson gson = new Gson();
         String body;
@@ -70,9 +80,34 @@ public class ApiSubmitTransactionController
         if(!t.checkIfValid())
             throw new ApiBadRequestException("Invalid JSON schema.");
         
-        System.out.println("Transaction : " + t.getItem() +
-                t.getType() + " " + t.getAmount());
-        
+        DatabaseInterface db;
+        Stock s;
+        try {
+            db = DatabaseSystem.getInstance();
+            s = db.getStockForItem(t.getItem());
+            if(s == null)
+                throw new ApiServerErrorException("Database failure.");
+            
+            int newQuantity = s.getQuantity() + t.getQuantity();
+            if(newQuantity < 0)
+                newQuantity = 0;
+            
+            db.updateStock(s.getId(), newQuantity);
+        } catch (DatabaseConnectionException ex) {
+            Logger.getLogger(ApiSubmitTransactionController.class.getName())
+                    .log(Level.SEVERE, ex.getMessage(), ex);
+            throw new ApiServerErrorException("Database failure.");
+        } catch (DatabaseResourceNotFoundException ex) {
+            //Logger.getLogger(ApiSubmitTransactionController.class.getName())
+            //        .log(Level.SEVERE, null, ex);
+            throw new ApiResourceNotFoundException("No stock found for item " + 
+                    t.getItem());
+        } catch (DatabaseUpdateException ex) {
+            Logger.getLogger(ApiSubmitTransactionController.class.getName())
+                    .log(Level.SEVERE, ex.getMessage(), ex);
+            throw new ApiServerErrorException("Database update failure.");
+        }
+
         return new ResponseEntity<>(t, HttpStatus.CREATED);
     }
     
diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java
index fcff9c1..0ace8fd 100644
--- a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java
+++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseConnectionException.java
@@ -16,4 +16,13 @@ package fr.unistra.sil.erp.back.db;
  */
 public class DatabaseConnectionException extends Exception {
     
+    /**
+     * Class constructor.
+     * @param errMsg the error message.
+     */
+    public DatabaseConnectionException(String errMsg)
+    {
+        super(errMsg);
+    }
+    
 }
diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java
index 988daf0..c71e083 100644
--- a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java
+++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseInterface.java
@@ -46,4 +46,22 @@ public interface DatabaseInterface {
      */
     public List<Stock> getStocks();
     
+    /**
+     * Returns the stock entry of an item.
+     * @param id the item's ID.
+     * @return the stock line.
+     * @throws DatabaseResourceNotFoundException when the query fails.
+     */
+    public Stock getStockForItem(int itemId)
+            throws DatabaseResourceNotFoundException;
+    
+    /**
+     * Updates the specified stock's quantity.
+     * @param id the stock entry's ID.
+     * @param quantity the new quantity.
+     * @throws DatabaseUpdateException when the query fails.
+     */
+    public void updateStock(int id, int quantity)
+            throws DatabaseConnectionException, DatabaseUpdateException;
+    
 }
diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java
new file mode 100644
index 0000000..9a34919
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseResourceNotFoundException.java
@@ -0,0 +1,26 @@
+/*
+ * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
+ * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
+ */
+package fr.unistra.sil.erp.back.db;
+
+/**
+ * Exception for non-existent database resources.
+ * 
+ * If the information requested in a database doesn't exist, this Exception
+ * is thrown.
+ * 
+ * @author BEAUVAIS ANTOINE
+ */
+public class DatabaseResourceNotFoundException extends Exception {
+    
+    /**
+     * Class constructor.
+     * @param errMsg the error message.
+     */
+    public DatabaseResourceNotFoundException(String errMsg)
+    {
+        super(errMsg);
+    }
+    
+}
diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java
index dd162f3..5d94b89 100644
--- a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java
+++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseSQLiteImpl.java
@@ -60,6 +60,12 @@ public class DatabaseSQLiteImpl implements DatabaseInterface {
             "i.name AS name, s.quantity AS quantity FROM stocks s " +
             "INNER JOIN items i ON s.item = i.ref";
     
+    private static final String SQL_GETSTOCKFORITEM = SQL_GETSTOCKS +
+            " WHERE item = ?";
+    
+    private static final String SQL_UPDATESTOCK =
+            "UPDATE stocks SET quantity = ? WHERE item = ?";
+    
     /**
      * SQL Connection object.
      */
@@ -177,6 +183,7 @@ public class DatabaseSQLiteImpl implements DatabaseInterface {
         } catch (SQLException ex) {
             Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
                     Level.SEVERE, "Failed to parse results.", ex);
+            return null;
         }
         
         return res;
@@ -235,5 +242,96 @@ public class DatabaseSQLiteImpl implements DatabaseInterface {
         
         return res;
     }
+
+    /**
+     * Returns the stock entry for the specified item.
+     * @param itemId the item's ID.
+     * @return the Stock entry.
+     * @throws DatabaseResourceNotFoundException if the stock doesn't exist.
+     */
+    @Override
+    public Stock getStockForItem(int itemId)
+            throws DatabaseResourceNotFoundException {
+        PreparedStatement ps;
+        try {
+            ps = this.conn.prepareStatement(SQL_GETSTOCKFORITEM);
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
+                    Level.SEVERE, "Failed to connect to database.", ex);
+            return null;
+        }
+        
+        try {
+            ps.setInt(1, itemId);
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
+                    Level.SEVERE, "Failed to set query parameter.", ex);
+            return null;
+        }
+        
+        ResultSet rs;
+        try {
+            rs = ps.executeQuery();
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
+                    Level.SEVERE, "Failed to fetch stock for item " +
+                            itemId, ex);
+            return null;
+        }
+        
+        Stock s;
+        try {
+            if(rs.next())
+                s = new Stock(rs.getInt("id"), rs.getInt("item"),
+                                     rs.getString("name"),
+                                     rs.getInt("quantity"));
+            else
+                throw new DatabaseResourceNotFoundException("Stock not found.");
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
+                    Level.SEVERE, "Failed to parse results.", ex);
+            return null;
+        }
+        
+        return s;
+    }
+
+    /**
+     * Updates the specified stock entry with the new quantity.
+     * @param id the stock's ID.
+     * @param quantity the associated quantity.
+     * @throws DatabaseConnectionException when the connection to the DB fails.
+     * @throws DatabaseUpdateException when the update fails.
+     */
+    @Override
+    public void updateStock(int id, int quantity)
+            throws DatabaseConnectionException, DatabaseUpdateException {
+        
+        PreparedStatement ps;
+        try {
+            ps = this.conn.prepareStatement(SQL_UPDATESTOCK);
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
+                    Level.SEVERE, "Failed to connect to database.", ex);
+            throw new DatabaseConnectionException("Failed to connect to DB.");
+        }
+        
+        try {
+            ps.setInt(1, quantity);
+            ps.setInt(2, id);
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName()).log(
+                    Level.SEVERE, "Failed to set query parameter.", ex);
+            throw new DatabaseUpdateException("Failed to set query params.");
+        }
+        
+        try {
+            ps.execute();
+        } catch (SQLException ex) {
+            Logger.getLogger(DatabaseSQLiteImpl.class.getName())
+                    .log(Level.SEVERE, "Failed to execute query.", ex);
+            throw new DatabaseUpdateException("Failed to execute query.");
+        }
+    }
     
 }
diff --git a/src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java
new file mode 100644
index 0000000..465912e
--- /dev/null
+++ b/src/main/java/fr/unistra/sil/erp/back/db/DatabaseUpdateException.java
@@ -0,0 +1,27 @@
+/*
+ * CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
+ * https://cecill.info/licences/Licence_CeCILL-B_V1-fr.html
+ */
+package fr.unistra.sil.erp.back.db;
+
+/**
+ * Exception for database update failures.
+ * 
+ * When a database system fails to update specific rows
+ * from the database, then it throws this Exception so
+ * the application can handle it.
+ * 
+ * @author BEAUVAIS ANTOINE
+ */
+public class DatabaseUpdateException extends Exception {
+    
+    /**
+     * Class constructor.
+     * @param errMsg the error message.
+     */
+    public DatabaseUpdateException(String errMsg)
+    {
+        super(errMsg);
+    }
+    
+}
-- 
GitLab