diff --git a/incl/api.inc.php b/incl/api.inc.php
index 45b1c56..e800137 100755
--- a/incl/api.inc.php
+++ b/incl/api.inc.php
@@ -25,6 +25,7 @@
 
 const API_O_BARCODES       = 'objects/product_barcodes';
 const API_O_PRODUCTS       = 'objects/products';
+const API_O_QU_CONV_R      = 'objects/quantity_unit_conversions_resolved';
 const API_STOCK_PRODUCTS   = 'stock/products';
 const API_ALL_PRODUCTS     = 'stock';
 const API_SHOPPINGLIST     = 'stock/shoppinglist/';
@@ -44,52 +45,303 @@
 
 
 class GrocyProduct {
-    public $id;
-    public $name;
-    public $barcodes = null;
-    public $unit = null;
-    public $stockAmount = "0";
-    public $isTare;
-    public $tareWeight;
-    public $quFactor;
-    public $defaultBestBeforeDays;
-    public $creationDate;
+    public int $id;
+    public string $name;
+    public ?array $barcodes = null;
+    public float $stockAmount = 0;
+    public bool $isTare = false;
+    public string $tareWeight;
+    public GrocyQuantityUnit $unit;
+    public GrocyQuantityUnit $purchaseUnit;
+    public GrocyQuantityUnit $consumeUnit;
+    public ?GrocyQuantityUnit $priceUnit = null;
+    public ?float $purchaseUnitFactor = null;
+    public ?float $priceUnitFactor = null;
+    public string $defaultBestBeforeDays;
+    public string $creationDate;
+    protected ?array $productInfo = null;
+    protected ?array $stockInfo = null;
 
     public static function parseProductInfoStock(array $infoArray): GrocyProduct {
-        checkIfNumeric($infoArray["product"]["id"]);
-
-        $result                        = new GrocyProduct();
-        $result->id                    = $infoArray["product"]["id"];
-        $result->name                  = sanitizeString($infoArray["product"]["name"]);
-        $result->isTare                = ($infoArray["product"]["enable_tare_weight_handling"] == "1");
-        $result->tareWeight            = sanitizeString($infoArray["product"]["tare_weight"]);
-        $result->defaultBestBeforeDays = $infoArray["product"]["default_best_before_days"];
-        $result->creationDate          = $infoArray["product"]["row_created_timestamp"];
-        $result->unit                  = sanitizeString($infoArray["quantity_unit_stock"]["name"]);
-        $result->barcodes              = $infoArray["product_barcodes"];
-
-        if (isset($infoArray["product"]["qu_conversion_factor_purchase_to_stock"]))
-            $result->quFactor = sanitizeString($infoArray["product"]["qu_conversion_factor_purchase_to_stock"]);
+        $result = new GrocyProduct();
+        $result->parseFromInfo($infoArray["product"]);
+        $result->parseFromStock($infoArray);
+        return $result;
+    }
+
+    public function parseFromStock(array $infoArray): void {
+        $this->stockInfo   = $infoArray;
+        $this->stockAmount = checkIfNumeric($infoArray["stock_amount"]);
+        $this->barcodes    = GrocyProductBarcode::parseProductBarcodes($infoArray["product_barcodes"]);
+
+        if (isset($infoArray["quantity_unit_stock"]))
+            $this->unit = GrocyQuantityUnit::parseQuantityUnit($infoArray["quantity_unit_stock"]);
+
+        if (isset($infoArray["default_quantity_unit_purchase"]))
+            $this->purchaseUnit = GrocyQuantityUnit::parseQuantityUnit($infoArray["default_quantity_unit_purchase"]);
+
+        if (isset($infoArray["default_quantity_unit_consume"]))
+            $this->consumeUnit = GrocyQuantityUnit::parseQuantityUnit($infoArray["default_quantity_unit_consume"]);
+
+        if (isset($infoArray["quantity_unit_price"]))
+            $this->priceUnit = GrocyQuantityUnit::parseQuantityUnit($infoArray["quantity_unit_price"]);
+
+        if (isset($infoArray["qu_conversion_factor_purchase_to_stock"]))
+            $this->purchaseUnitFactor = checkIfFloat($infoArray["qu_conversion_factor_purchase_to_stock"]);
+
+        if (isset($infoArray["qu_conversion_factor_price_to_stock"]))
+            $this->priceUnitFactor = checkIfFloat($infoArray["qu_conversion_factor_price_to_stock"]);
+    }
+
+    public static function parseProductInfo(array $infoArray): GrocyProduct {
+        $result = new GrocyProduct();
+        $result->parseFromInfo($infoArray);
+        return $result;
+    }
+
+    public function parseFromInfo(array $infoArray): void {
+        $this->productInfo           = $infoArray;
+        $this->id                    = checkIfNumeric($infoArray["id"]);
+        $this->name                  = sanitizeString($infoArray["name"]);
+        $this->isTare                = ($infoArray["enable_tare_weight_handling"] == "1");
+        $this->tareWeight            = sanitizeString($infoArray["tare_weight"]);
+        $this->defaultBestBeforeDays = $infoArray["default_best_before_days"];
+        $this->creationDate          = $infoArray["row_created_timestamp"];
+
+        $this->unit = new GrocyQuantityUnit($infoArray["qu_id_stock"]);
+        $this->purchaseUnit = new GrocyQuantityUnit($infoArray["qu_id_purchase"]);
+        $this->consumeUnit = new GrocyQuantityUnit($infoArray["qu_id_consume"]);
+        $this->priceUnit = new GrocyQuantityUnit($infoArray["qu_id_price"]);
+
+        if (isset($infoArray["qu_conversion_factor_purchase_to_stock"]))
+            $this->purchaseUnitFactor = checkIfFloat($infoArray["qu_conversion_factor_purchase_to_stock"]);
+
+        if (isset($infoArray["qu_conversion_factor_price_to_stock"]))
+            $this->priceUnitFactor = checkIfFloat($infoArray["qu_conversion_factor_price_to_stock"]);
+    }
+
+    public function updateStock(bool $ignoreCache = false): bool {
+        $productInfo = getProductInfo($this->id);
+        if ($productInfo == null)
+            return false;
+
+        if ($ignoreCache || $this->productInfo == null)
+            $this->parseFromInfo($productInfo->productInfo);
+        $this->parseFromStock($productInfo->stockInfo);
+
+        return true;
+    }
+
+    public function updateBarcodes(bool $ignoreCache = false): bool {
+        $allBarcodes = getAllBarcodes($ignoreCache);
+        $this->barcodes = array();
+        foreach ($allBarcodes as $barcodeInfo) {
+            if (!($barcodeInfo instanceof GrocyProductBarcode) || $barcodeInfo->productId != $this->id)
+                continue;
+            $this->barcodes[$barcodeInfo->barcode] = $barcodeInfo;
+        }
+        $foundBarcodes = sizeof($this->barcodes) > 0;
+        if ($ignoreCache && !$foundBarcodes)
+            return $this->updateStock();
+
+        return $foundBarcodes;
+    }
+
+    public function getBarcodeInfo(string $barcode): ?GrocyProductBarcode {
+        $hasBarcodes = $this->barcodes != null && sizeof($this->barcodes) > 0;
+        if (!$hasBarcodes) {
+            if (!$this->updateBarcodes($this->barcodes != null))
+                return null;
+        }
+      return $this->barcodes[$barcode] ?? null;
+    }
+
+    public function getQuantityUnitByBarcode(?GrocyProductBarcode $barcode, bool $isConsume = false): GrocyQuantityUnit {
+        if ($barcode != null)
+            return new GrocyQuantityUnit($barcode->quId);
+
+        $qu = $isConsume ? $this->consumeUnit : $this->purchaseUnit;
+        return $qu ?? $this->unit;
+    }
+
+    public function getAmountByBarcode(string $barcode, bool $isConsume = false, ?GrocyQuantityUnit $qu = null, ?float $quantity = null): ?float {
+        $barcodeInfo = $this->getBarcodeInfo($barcode);
+        $qu = $qu ?? $this->getQuantityUnitByBarcode($barcodeInfo, $isConsume);
+        $quantity = $quantity ?? $barcodeInfo->amount;
+        $quc = $this->getQuantityConversionFrom($qu->id);
+        if ($quc == null)
+            return null;
+        return $quc->factor * $quantity;
+    }
+
+    public function getQuantityConversionFrom(int $quId): ?GrocyQuantityConversion {
+        $factor = null;
+        if ($quId == $this->unit->id)
+            $factor = 1;
+        else if ($quId == $this->purchaseUnit->id)
+            $factor = $this->purchaseUnitFactor;
+        else if ($quId == $this->priceUnit->id)
+            $factor = $this->priceUnitFactor;
+
+        if ($factor != null) {
+            $quc = new GrocyQuantityConversion();
+            $quc->productId = $this->id;
+            $quc->fromQuId  = $quId;
+            $quc->toQuId    = $this->unit->id;
+            $quc->factor    = $factor;
+            return $quc;
+        }
+
+        return API::getQuantityConversion($this->id, $quId, $this->unit->id);
+    }
+
+    public static function parseQuantityConversion(array $convArray): GrocyProduct {
+        $result            = new GrocyQuantityConversion();
+        $result->id        = checkIfNumeric($convArray["id"]);
+        $result->productId = checkIfNumeric($convArray["product_id"]);
+        $result->fromQuId  = checkIfNumeric($convArray["from_qu_id"]);
+        $result->toQuId    = checkIfNumeric($convArray["to_qu_id"]);
+        $result->factor    = checkIfFloat($convArray["factor"]);
+
+        return $result;
+    }
+
+    public function getQuantityConversionByBarcode(string $barcode, bool $isConsume = false): ?GrocyQuantityConversion {
+        $qu = $this->getQuantityUnitByBarcode($isConsume);
+        return $this->getQuantityConversionFrom($qu->id);
+    }
+}
+
+class GrocyBarcode {
+    public int $id;
+    public string $barcode;
+    public ?string $note = null;
+    protected ?array $barcodeInfo = null;
+
+    public function __construct(string|int $id = -1) {
+        $this->id = checkIfNumeric($id);
+    }
+
+    public static function parseBarcode(array $barcodeArray): GrocyBarcode {
+        $result = new GrocyBarcode($barcodeArray["id"]);
+        $result->parseFromBarcode($barcodeArray);
+        return $result;
+    }
+
+    public static function parseAnyBarcode(array $barcodeArray): GrocyBarcode {
+        if (isset($barcodeArray["product_id"]) && isset($barcodeArray["amount"]))
+            return GrocyProductBarcode::parseProductBarcode($barcodeArray);
         else
-            $result->quFactor = 1;
+            return GrocyBarcode::parseBarcode($barcodeArray);
+    }
+
+    public function parseFromBarcode(array $barcodeArray): void {
+        $this->barcodeInfo = $barcodeArray;
+        $this->id          = checkIfNumeric($barcodeArray["id"]);
+        $this->barcode     = sanitizeString($barcodeArray["barcode"]);
+        $this->note        = sanitizeString($barcodeArray["note"]);
+    }
+
+    public static function parseBarcodes(array $barcodes): array {
+        $result = array();
+        foreach ($barcodes as $barcodeArray) {
+            $barcodeInfo = GrocyBarcode::parseBarcode($barcodeArray);
+            $result[$barcodeInfo->barcode] = $barcodeInfo;
+        }
+        return $result;
+    }
+
+    public static function parseAnyBarcodes(array $barcodes): array {
+        $result = array();
+        foreach ($barcodes as $barcodeArray) {
+            $barcodeInfo = GrocyBarcode::parseAnyBarcode($barcodeArray);
+            $result[$barcodeInfo->barcode] = $barcodeInfo;
+        }
+        return $result;
+    }
+}
 
-        if (sanitizeString($infoArray["stock_amount"]) != null)
-            $result->stockAmount = sanitizeString($infoArray["stock_amount"]);
+class GrocyProductBarcode extends GrocyBarcode {
+    public int $productId;
+    public int $quId;
+    public float $amount;
+    public ?int $shoppingLocationId = null;
+    public ?float $lastPrice = null;
+
+    public function __construct(string|int $id = -1) {
+        parent::__construct($id);
+    }
+
+    public static function parseProductBarcode(array $barcodeArray): GrocyProductBarcode {
+        $result = new GrocyProductBarcode($barcodeArray["id"]);
+        $result->parseFromProductBarcode($barcodeArray);
         return $result;
     }
 
+    public function parseFromProductBarcode(array $barcodeArray): void {
+        $this->parseFromBarcode($barcodeArray);
+        $this->productId = checkIfNumeric($barcodeArray["product_id"]);
+        $this->quId      = checkIfNumeric($barcodeArray["qu_id"]);
+        $this->amount    = checkIfFloat($barcodeArray["amount"]);
 
-    public static function parseProductInfoObjects(array $infoArray): GrocyProduct {
-        checkIfNumeric($infoArray["id"]);
+        if (isset($barcodeArray["shopping_location_id"]))
+            $this->shoppingLocationId = checkIfNumeric($barcodeArray["shopping_location_id"]);
+        if (isset($barcodeArray["last_price"]))
+            $this->lastPrice = checkIfFloat($barcodeArray["last_price"]);
+    }
+
+    public static function parseProductBarcodes(array $barcodes): array {
+        $result = array();
+        foreach ($barcodes as $barcodeArray) {
+            $barcodeInfo = GrocyProductBarcode::parseProductBarcode($barcodeArray);
+            $result[$barcodeInfo->barcode] = $barcodeInfo;
+        }
+        return $result;
+    }
+}
+
+class GrocyQuantityUnit {
+    public int $id;
+    public string $name;
+    public ?string $description;
+    public ?string $namePlural;
+    public ?array $pluralForms;
+    public bool $isActive;
+
+    public function __construct(string|int $id) {
+        $this->id = checkIfNumeric($id);
+    }
+
+    public static function parseQuantityUnit(array $convArray): GrocyQuantityUnit {
+        $result              = new GrocyQuantityUnit($convArray["id"]);
+        $result->name        = sanitizeString($convArray["name"]);
+        $result->namePlural  = sanitizeString($convArray["name_plural"] ?? null);
+        $result->description = sanitizeString($convArray["description"]);
+        $result->pluralForms = $convArray["plural_forms"];
+        $result->isActive    = ($convArray["active"] == 1);
+
+        return $result;
+    }
+}
+
+class GrocyQuantityConversion {
+    public int $id;
+    public int $productId;
+    public int $fromQuId;
+    public int $toQuId;
+    public float $factor;
+
+    public function __construct(string|int $id = -1) {
+        $this->id = checkIfNumeric($id);
+    }
+
+    public static function parseQuantityConversion(array $convArray): GrocyQuantityConversion {
+        $result            = new GrocyQuantityConversion($convArray["id"]);
+        $result->productId = checkIfNumeric($convArray["product_id"]);
+        $result->fromQuId  = checkIfNumeric($convArray["from_qu_id"]);
+        $result->toQuId    = checkIfNumeric($convArray["to_qu_id"]);
+        $result->factor    = checkIfFloat($convArray["factor"]);
 
-        $result                        = new GrocyProduct();
-        $result->id                    = $infoArray["id"];
-        $result->name                  = sanitizeString($infoArray["name"]);
-        $result->isTare                = ($infoArray["enable_tare_weight_handling"] == "1");
-        $result->tareWeight            = sanitizeString($infoArray["tare_weight"]);
-        $result->quFactor              = 1; //FIXME qu_conversion_factor_purchase_to_stock was removed, might break QU conversion
-        $result->defaultBestBeforeDays = $infoArray["default_best_before_days"];
-        $result->creationDate          = $infoArray["row_created_timestamp"];
         return $result;
     }
 }
@@ -127,7 +379,7 @@ public static function getAllProductsInfo(bool $ignoreCache = false): ?array {
         if ($result != null) {
             $products = array();
             foreach ($result as $product) {
-                $grocyProduct                = GrocyProduct::parseProductInfoObjects($product);
+                $grocyProduct                = GrocyProduct::parseProductInfo($product);
                 $products[$grocyProduct->id] = $grocyProduct;
             }
             if ($updateRedisCache) {
@@ -164,6 +416,26 @@ public static function getProductInfo(int $productId): ?GrocyProduct {
         return null;
     }
 
+    public static function getQuantityConversion(int $productId, int $fromQuId, int $toQuId): ?GrocyQuantityConversion {
+        $url = API_O_QU_CONV_R . "?query[]=product_id=" . $productId
+            . "&query[]=from_qu_id=" . $fromQuId
+            . "&query[]=to_qu_id=" . $toQuId;
+        $result = null;  // Assure assignment in event curl throws exception.
+        $curl   = new CurlGenerator($url);
+        try {
+            $result = $curl->execute(true);
+        } catch (Exception $e) {
+            self::processError($e, "Could not lookup Grocy quantity conversion");
+            return null;
+        }
+        if ($result == null || sizeof($result) == 0) {
+            $log = new LogOutput("Failed to look up quantity conversion for product_id=$productId from qu=$fromQuId to qu=$toQuId", EVENT_TYPE_ERROR);
+            $log->setVerbose()->dontSendWebsocket()->createLog();
+            return null;
+        }
+        return GrocyQuantityConversion::parseQuantityConversion($result[0]);
+    }
+
     /**
      * Gets the last created product
      * @param int $timeframeInMinutes Specify how many old the product can be max (in minutes) or pass 0 for unlimited age
@@ -507,7 +779,7 @@ public static function addBarcodeQuantity(string $barcode, float $quantity, bool
                 return;
             }
         }
-        $barcodeId = $barcodes[$barcode]["barcode_id"];
+        $barcodeId = $barcodes[$barcode]->id;
         $url       = API_O_BARCODES . "/" . $barcodeId;
 
         $curl = new CurlGenerator($url, METHOD_PUT, $data);
@@ -586,10 +858,11 @@ public static function getProductByBarcode(string $barcode, bool $ignoreCache =
             return self::getProductInfo(checkIfNumeric($id));
         }
         $allBarcodes = self::getAllBarcodes($ignoreCache);
-        if (!isset($allBarcodes[$barcode])) {
+        if (!isset($allBarcodes[$barcode]) || !($allBarcodes[$barcode] instanceof GrocyProductBarcode)) {
             return null;
         } else {
-            return self::getProductInfo($allBarcodes[$barcode]["id"]);
+            $productId = $allBarcodes[$barcode]->productId;
+            return self::getProductInfo($productId);
         }
     }
 
@@ -619,7 +892,7 @@ public static function getAllBarcodes(bool $ignoreCache = false): ?array {
             if (!$ignoreCache && RedisConnection::isCacheAvailable()) {
                 $cachedBarcodes = RedisConnection::getAllBarcodes();
                 if ($cachedBarcodes != null)
-                    return $cachedBarcodes;
+                    return GrocyBarcode::parseAnyBarcodes($cachedBarcodes);
                 else
                     $updateRedis = true;
             } else {
@@ -634,18 +907,9 @@ public static function getAllBarcodes(bool $ignoreCache = false): ?array {
             self::processError($e, "Could not lookup Grocy barcodes");
             return null;
         }
-        $result = array();
-        foreach ($curlResult as $item) {
-            if (!isset($item["barcode"]) || !isset($item["product_id"]))
-                continue;
-            $barcode                        = strtoupper(strval($item["barcode"]));
-            $result[$barcode]["id"]         = $item["product_id"];
-            $result[$barcode]["factor"]     = $item["amount"];
-            $result[$barcode]["barcode_id"] = $item["id"];
-            $result[$barcode]["barcode"]    = $barcode;
-        }
+        $result = GrocyBarcode::parseAnyBarcodes($curlResult);
         if ($updateRedis)
-            RedisConnection::cacheAllBarcodes($result);
+            RedisConnection::cacheAllBarcodes($curlResult);
         return $result;
     }
 
diff --git a/incl/modules/barcodeFederation.php b/incl/modules/barcodeFederation.php
index 39dd578..8f64951 100644
--- a/incl/modules/barcodeFederation.php
+++ b/incl/modules/barcodeFederation.php
@@ -68,9 +68,11 @@ private static function syncBarcodes() {
         $items = array();
 
         foreach ($barcodes as $barcode) {
-            $name = $products[$barcode["id"]]->name;
-            if (strlen($name) > 1 && strlen($barcode["barcode"]) > 4)
-                array_push($items, new ServerBarcode($barcode, $name));
+            if (!($barcode instanceof GrocyProductBarcode))
+                continue;
+            $name = $products[$barcode->productId]->name;
+            if (strlen($name) > 1 && strlen($barcode->barcode) > 4)
+                array_push($items, new ServerBarcode($barcode->barcode, $name));
         }
         $json = json_encode(array("ServerBarcodes" => $items));
         try {
@@ -253,16 +255,16 @@ public static function chooseOtherBarcodeName(string $barcode, string $newName):
 }
 
 class ServerBarcode {
-    public $name;
-    public $barcode;
+    public string $name;
+    public string $barcode;
 
     /**
      * BarcodeServerItem constructor.
      * @param array $barcode
      * @param string $name
      */
-    public function __construct(array $barcode, string $name) {
+    public function __construct(string $barcode, string $name) {
         $this->name    = $name;
-        $this->barcode = $barcode["barcode"];
+        $this->barcode = $barcode;
     }
-}
\ No newline at end of file
+}
diff --git a/incl/modules/quantityManager.php b/incl/modules/quantityManager.php
index 7fa5505..d723901 100755
--- a/incl/modules/quantityManager.php
+++ b/incl/modules/quantityManager.php
@@ -11,8 +11,8 @@ public static function getQuantities(): array {
         $storedBarcodes = API::getAllBarcodes();
         $products       = API::getAllProductsInfo();
         foreach ($storedBarcodes as $barcode) {
-            if ($barcode["factor"] != null) {
-                array_push($barcodes, new ApiQuantity($barcode, $products));
+            if ($barcode instanceof GrocyProductBarcode) {
+                array_push($barcodes, new ApiQuantity($barcode, $products[$barcode->productId]));
             }
         }
         return $barcodes;
@@ -28,7 +28,7 @@ public static function getQuantities(): array {
      * @return float quantity or 1 if not found
      * @throws DbConnectionDuringEstablishException
      */
-    public static function getStoredQuantityForBarcode(string $barcode, bool $deleteAfterCompletion = false, SQLite3 $db = null): float {
+    public static function getStoredQuantityForBarcode(string $barcode, bool $deleteAfterCompletion = false, SQLite3 $db = null): ?float {
         if ($db == null)
             $db = DatabaseConnection::getInstance()->getDatabaseReference();
         $res = $db->query("SELECT * FROM Quantities WHERE barcode='$barcode'");
@@ -38,7 +38,7 @@ public static function getStoredQuantityForBarcode(string $barcode, bool $delete
                 self::delete($entry->id, $db);
             return $entry->quantity;
         } else {
-            return 1;
+            return null;
         }
     }
 
@@ -54,7 +54,7 @@ public static function getStoredQuantityForBarcode(string $barcode, bool $delete
      */
     public static function syncBarcodeToGrocy(string $barcode, SQLite3 $db = null): void {
         $storedAmount = self::getStoredQuantityForBarcode($barcode, true, $db);
-        if ($storedAmount != 1) {
+        if ($storedAmount != null) {
             API::addBarcodeQuantity($barcode, $storedAmount);
             //Only store log if not currently upgrading db
             if ($db == null) {
@@ -73,15 +73,15 @@ public static function syncBarcodeToGrocy(string $barcode, SQLite3 $db = null):
      */
     public static function getQuantityForBarcode(string $barcode, bool $isConsume, GrocyProduct $productInfo): float {
         $config = BBConfig::getInstance();
-        if ($isConsume && !$config["CONSUME_SAVED_QUANTITY"])
-            return 1;
-        $amountSavedInProduct = floatval($productInfo->quFactor);
-        $barcodes             = API::getAllBarcodes();
-        if (isset($barcodes[$barcode]) && $barcodes[$barcode]["factor"] != null)
-            return $barcodes[$barcode]["factor"];
-        if ($config["USE_GROCY_QU_FACTOR"] && $amountSavedInProduct > 1)
-            return $amountSavedInProduct;
-        return $amount = QuantityManager::getStoredQuantityForBarcode($barcode);
+        $quantity = ($isConsume && $config["CONSUME_SAVED_QUANTITY"]) ? QuantityManager::getStoredQuantityForBarcode($barcode) : null;
+        $qu = !$config["USE_GROCY_QU_FACTOR"] ? $productInfo->unit : null;
+        $amount = $productInfo->getAmountByBarcode($barcode, $isConsume, $qu, $quantity);
+        if ($amount == null) {
+            $log = new LogOutput("Failed to find amount for barcode $barcode of " . $productInfo->name, EVENT_TYPE_ERROR);
+            $log->setVerbose()->dontSendWebsocket()->createLog();
+            return $quantity ?? 1;
+        }
+        return $amount;
     }
 
 
@@ -150,21 +150,21 @@ private function isValidRow(array $dbRow): bool {
 
 class ApiQuantity {
 
-    public $id;
-    public $barcode;
-    public $quantity;
-    public $product;
+    public int $id;
+    public string $barcode;
+    public float $quantity;
+    public string $product;
 
     /**
      * ApiQuantity constructor.
-     * @param array $barcodeArrayItem
-     * @param GrocyProduct[] $productList
+     * @param GrocyProductBarcode $barcodeInfo
+     * @param GrocyProduct $productInfo
      */
-    public function __construct(array $barcodeArrayItem, array $productList) {
-        $this->id       = $barcodeArrayItem['barcode_id'];
-        $this->barcode  = $barcodeArrayItem['barcode'];
-        $this->quantity = $barcodeArrayItem['factor'];
-        $this->product  = $productList[$barcodeArrayItem['id']]->name;
+    public function __construct(GrocyProductBarcode $barcodeInfo, GrocyProduct $productInfo) {
+        $this->id       = $barcodeInfo->id;
+        $this->barcode  = $barcodeInfo->barcode;
+        $this->quantity = $barcodeInfo->amount;
+        $this->product  = $productInfo->name;
     }
 
 }
diff --git a/incl/processing.inc.php b/incl/processing.inc.php
index 393e174..c3015e9 100755
--- a/incl/processing.inc.php
+++ b/incl/processing.inc.php
@@ -196,7 +196,7 @@ function processUnknownBarcode(string $barcode, bool $websocketEnabled, LockGene
     $db     = DatabaseConnection::getInstance();
     $amount = 1;
     if ($db->getTransactionState() == STATE_PURCHASE) {
-        $amount = QuantityManager::getStoredQuantityForBarcode($barcode);
+        $amount = QuantityManager::getStoredQuantityForBarcode($barcode) ?? 1;
     }
     if ($db->isUnknownBarcodeAlreadyStored($barcode)) {
         //Unknown barcode already in local database
@@ -362,9 +362,9 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
             $amountToConsume = QuantityManager::getQuantityForBarcode($barcode, true, $productInfo);
 
             if ($productInfo->stockAmount > 0) {
-                if ($productInfo->stockAmount < $amountToConsume)
+                if ($amountToConsume && $productInfo->stockAmount < $amountToConsume)
                     $amountToConsume = $productInfo->stockAmount;
-                $log    = new LogOutput("Consuming " . $amountToConsume . " " . $productInfo->unit . " of " . $productInfo->name, EVENT_TYPE_CONSUME_PRODUCT, $barcode);
+                $log    = new LogOutput("Consuming " . $amountToConsume . " " . $productInfo->unit->name . " of " . $productInfo->name, EVENT_TYPE_CONSUME_PRODUCT, $barcode);
                 $output = $log
                     ->addStockToText($productInfo->stockAmount - $amountToConsume)
                     ->setWebsocketResultCode(WS_RESULT_PRODUCT_FOUND)
@@ -384,7 +384,7 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
         case STATE_CONSUME_ALL:
             $amountToConsume = $productInfo->stockAmount;
             if ($productInfo->stockAmount > 0) {
-                $log    = new LogOutput("Consuming all" . $amountToConsume . " " . $productInfo->unit . " of " . $productInfo->name, EVENT_TYPE_CONSUME_ALL_PRODUCT, $barcode);
+                $log    = new LogOutput("Consuming all" . $amountToConsume . " " . $productInfo->unit->name . " of " . $productInfo->name, EVENT_TYPE_CONSUME_ALL_PRODUCT, $barcode);
                 $output = $log
                     ->setWebsocketResultCode(WS_RESULT_PRODUCT_FOUND)
                     ->addProductFoundText()
@@ -408,7 +408,7 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
             $amountToSpoil = QuantityManager::getQuantityForBarcode($barcode, true, $productInfo);
 
             if ($productInfo->stockAmount > 0) {
-                $log    = new LogOutput("Consuming " . $amountToSpoil . " spoiled " . $productInfo->unit . " of " . $productInfo->name, EVENT_TYPE_CONSUME_S_PRODUCT, $barcode);
+                $log    = new LogOutput("Consuming " . $amountToSpoil . " spoiled " . $productInfo->unit->name . " of " . $productInfo->name, EVENT_TYPE_CONSUME_S_PRODUCT, $barcode);
                 $output = $log
                     ->addStockToText($productInfo->stockAmount - $amountToSpoil)
                     ->setWebsocketResultCode(WS_RESULT_PRODUCT_FOUND)
@@ -437,7 +437,7 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
             } else {
                 $additionalLog = "";
             }
-            $log    = new LogOutput("Adding  $amount " . $productInfo->unit . " of " . $productInfo->name . $additionalLog, EVENT_TYPE_PURCHASE_PRODUCT, $barcode, $isWarning);
+            $log    = new LogOutput("Adding " . "$amount " . $productInfo->unit->name . " of " . $productInfo->name . $additionalLog, EVENT_TYPE_PURCHASE_PRODUCT, $barcode, $isWarning);
             $output = $log
                 ->addStockToText($productInfo->stockAmount + $amount)
                 ->setWebsocketResultCode(WS_RESULT_PRODUCT_FOUND)
@@ -449,7 +449,7 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
         case STATE_OPEN:
             $amount    = QuantityManager::getQuantityForBarcode($barcode, false, $productInfo);
             if ($productInfo->stockAmount > 0) {
-                $log    = new LogOutput("Opening " . $amount . " " . $productInfo->unit . " of " . $productInfo->name, EVENT_TYPE_OPEN_PRODUCT, $barcode);
+                $log    = new LogOutput("Opening " . $amount . " " . $productInfo->unit->name . " of " . $productInfo->name, EVENT_TYPE_OPEN_PRODUCT, $barcode);
                 $output = $log
                     ->addStockToText($productInfo->stockAmount)
                     ->setWebsocketResultCode(WS_RESULT_PRODUCT_FOUND)
@@ -471,19 +471,19 @@ function processKnownBarcode(GrocyProduct $productInfo, string $barcode, bool $w
             return $output;
         case STATE_GETSTOCK:
             $fileLock->removeLock();
-            $log = "Currently in stock: " . $productInfo->stockAmount . " " . $productInfo->unit . " of " . $productInfo->name;
+            $log = "Currently in stock: " . $productInfo->stockAmount . " " . $productInfo->unit->name . " of " . $productInfo->name;
             if ($productInfo->stockAmount > 0) {
                 $locationInfo = API::getProductLocations($productInfo->id);
                 foreach ($locationInfo as $location) {
-                    $log = $log . '\nLocation ' . $location["location_name"] . ": " . $location["amount"] . " " . $productInfo->unit;
+                    $log = $log . '\nLocation ' . $location["location_name"] . ": " . $location["amount"] . " " . $productInfo->unit->name;
                 }
             }
             return (new LogOutput($log, EVENT_TYPE_GET_STOCK_PRODUCT))->createLog();
         case STATE_ADD_SL:
             $amount    = QuantityManager::getQuantityForBarcode($barcode, false, $productInfo);
             $fileLock->removeLock();
-			$log = "Added to shopping list: " . $amount . " " . $productInfo->unit . " of " . $productInfo->name;
-            API::addToShoppinglist($productInfo->id, 1);
+            $log = "Added to shopping list: " . $amount . " " . $productInfo->unit->name . " of " . $productInfo->name;
+            API::addToShoppinglist($productInfo->id, $amount);
             return (new LogOutput($log, EVENT_TYPE_ADD_TO_SHOPPINGLIST))->createLog();
         default:
             throw new Exception("Unknown state");
@@ -539,7 +539,7 @@ function sanitizeString(?string $input, bool $strongFilter = false): ?string {
  * @param string $input
  * @return int Returns value as int if valid
  */
-function checkIfNumeric(string $input): int {
+function checkIfNumeric(string|int $input): int {
     if (!is_numeric($input) && $input != "") {
         die("Illegal input! " . sanitizeString($input) . " needs to be a number");
     }
diff --git a/index.php b/index.php
index fcaa05a..ec1da91 100755
--- a/index.php
+++ b/index.php
@@ -255,7 +255,7 @@ function processButtons(): void {
                             $amount = $product->stockAmount;
                         if ($amount > 0) {
                             API::consumeProduct($gidSelected, $amount);
-                            $log = new LogOutput("Consuming $amount " . $product->unit . " of " . $product->name, EVENT_TYPE_ADD_KNOWN_BARCODE);
+                            $log = new LogOutput("Consuming $amount " . $product->unit->name . " of " . $product->name, EVENT_TYPE_ADD_KNOWN_BARCODE);
                         } else {
                             $log = new LogOutput("None in stock, not consuming: " . $product->name, EVENT_TYPE_ADD_KNOWN_BARCODE);
                         }
@@ -265,7 +265,7 @@ function processButtons(): void {
                         if (!API::purchaseProduct($gidSelected, $amount, $row["bestBeforeInDays"], $row["price"])) {
                             $additionalLog = " [WARNING]: No default best before date set!";
                         }
-                        $log = new LogOutput("Adding $amount " . $product->unit . " of " . $product->name . $additionalLog, EVENT_TYPE_ADD_KNOWN_BARCODE);
+                        $log = new LogOutput("Adding $amount " . $product->unit->name . " of " . $product->name . $additionalLog, EVENT_TYPE_ADD_KNOWN_BARCODE);
                         $log->setVerbose()->dontSendWebsocket()->createLog();
                     }
                 }