diff --git a/Layout/default/Preorder/include/preorder-detail.php b/Layout/default/Preorder/include/preorder-detail.php index 30aef601a..1d271623c 100644 --- a/Layout/default/Preorder/include/preorder-detail.php +++ b/Layout/default/Preorder/include/preorder-detail.php @@ -484,7 +484,7 @@ Neuer Status history as $history): ?> - key != "preorderstatus_id") continue; ?> + key != "status_id") continue; ?> create)?> creator->name?> diff --git a/Layout/default/User/Form.php b/Layout/default/User/Form.php index 3bc72f040..48e1d99c5 100644 --- a/Layout/default/User/Form.php +++ b/Layout/default/User/Form.php @@ -225,6 +225,31 @@ + +

Lager

+ +
+
+
+ can("WarehouseAdmin")) ? "checked='checked'" : ""?> /> + +
+
+ +
+
+ can("WarehouseUser")) ? "checked='checked'" : ""?> /> + +
+
+ +
+
+ can("WarehouseEShop")) ? "checked='checked'" : ""?> /> + +
+
+

Zusatzberechtigungen

diff --git a/Layout/default/menu.php b/Layout/default/menu.php index d717c8a77..12e8c53c7 100644 --- a/Layout/default/menu.php +++ b/Layout/default/menu.php @@ -130,23 +130,28 @@ - is(["Admin"])&& isset($_GET['warehouse'])): ?> + can(["WarehouseAdmin", "WarehouseUser", "WarehouseEShop"])): ?>
  • - Lager
    + can("WarehouseEShop") && !($me->can("WarehouseAdmin") || $me->can("WarehouseUser"))): ?> + E-Shop
    + can("WarehouseAdmin") || $me->can("WarehouseUser")): ?> + Lager
    +
  • diff --git a/application/ADBHausnummer/ADBHausnummer.php b/application/ADBHausnummer/ADBHausnummer.php index 976c8d3c6..737d5b3be 100644 --- a/application/ADBHausnummer/ADBHausnummer.php +++ b/application/ADBHausnummer/ADBHausnummer.php @@ -16,9 +16,47 @@ class ADBHausnummer extends mfBaseModel { } public function afterSave() { - if($this->netzgebiet_id && !$this->gps_long && !$this->gps_lat) { - $this->getGpsCoords(); - } + // prevent potential infinite loop + $nesting_level = mfValuecache::singleton()->get("adbhausnummer-save-nesting-level-".$this->id); + if(!$nesting_level) { + $nesting_level = 1; + } else { + $nesting_level++; + } + mfValuecache::singleton()->set("adbhausnummer-save-nesting-level-".$this->id, $nesting_level); + + if($nesting_level > 1) { + return true; + } + + if($this->netzgebiet_id && !$this->gps_long && !$this->gps_lat) { + $this->getGpsCoords(); + } + + + // Statuschange from Rimo statuschange for all units + foreach(ADBWohneinheitModel::search(["hausnummer_id" => $this->id]) as $wohneinheit) { + AddressDB::handleRimoStatusUpdate($wohneinheit->id); + } + + + } + + public function setNewStatusCode($new_status_code) { + if(!$new_status_code) return false; + + $this->log->debug(__METHOD__.": Want new Hausnummer (".$this->id.") Status ".$new_status_code); + + $new_status = ADBStatusModel::getFirst(["code" => $new_status_code]); + if(!$new_status) return false; + + $old_status = $this->getProperty("status"); + if($old_status->code < $new_status->code) { + $this->log->debug(__METHOD__.": Setting Hausnummer (".$this->id.") Status from ".$old_status->code." to ".$new_status->code); + $this->status_id = $new_status->id; + } + + return true; } private function getGpsCoords() { diff --git a/application/ADBWohneinheit/ADBWohneinheit.php b/application/ADBWohneinheit/ADBWohneinheit.php index d8d9befba..f609ba569 100644 --- a/application/ADBWohneinheit/ADBWohneinheit.php +++ b/application/ADBWohneinheit/ADBWohneinheit.php @@ -14,7 +14,23 @@ class ADBWohneinheit extends mfBaseModel { protected function afterSave() { if(!$this->id) return true; if(!$this->hausnummer_id) return true; - + + // prevent potential infinite loop + $nesting_level = mfValuecache::singleton()->get("adbwohneinheit-save-nesting-level-".$this->id); + if(!$nesting_level) { + $nesting_level = 1; + } else { + $nesting_level++; + } + mfValuecache::singleton()->set("adbwohneinheit-save-nesting-level-".$this->id, $nesting_level); + + if($nesting_level > 1) { + return true; + } + + // Statuschange from Rimo statuschange + AddressDB::handleRimoStatusUpdate($this->id); + // ADBWohneinheit_onSave_noAutoUnitCount can be defined if doing bulk // operations where unit count is calculated seperately if(!defined("ADBWohneinheit_onSave_noAutoUnitCount") || !ADBWohneinheit_onSave_noAutoUnitCount) { @@ -36,6 +52,20 @@ class ADBWohneinheit extends mfBaseModel { } } + + public function setNewStatusCode($new_status_code) { + if(!$new_status_code) return false; + + $new_status = ADBStatusModel::getFirst(["code" => $new_status_code]); + if(!$new_status) return false; + + $old_status = $this->getProperty("status"); + if($old_status->code < $new_status->code) { + $this->status_id = $new_status->id; + } + + return true; + } public static function parseHausnummerZusatz($text) { diff --git a/application/AddressDB/AddressDB.php b/application/AddressDB/AddressDB.php index 55ce94410..452eaca2d 100644 --- a/application/AddressDB/AddressDB.php +++ b/application/AddressDB/AddressDB.php @@ -70,7 +70,83 @@ class AddressDB { $this->db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); $this->log = mfLoghandler::singleton(); } - + + public static function handleRimoStatusUpdate($wohneinheit_id) { + if(!$wohneinheit_id) return true; + + $log = mfLoghandler::singleton(); + //echo "in handleRimoStatusUpdate\n"; + + $wohneinheit = new ADBWohneinheit($wohneinheit_id); + if(!$wohneinheit->id) return false; + + $hausnummer = $wohneinheit->hausnummer; + if(!$hausnummer->netzgebiet->getOption("statuschange")) { + return true; + } + + $preorder = PreorderModel::getFirstActive(["adb_wohneinheit_id" => $wohneinheit->id]); + if(!$preorder) { + return true; + } + + $b_ex_state = strtolower($hausnummer->rimo_ex_state); + $b_op_state = strtolower($hausnummer->rimo_op_state); + + $h_ex_state = strtolower($wohneinheit->rimo_ex_state); + $h_op_state = strtolower($wohneinheit->rimo_op_state); + + $wo_state = false; + $workorder = $wohneinheit->rimo_workorder; + if($workorder) { + $wo_state = strtolower($workorder->rimo_status); + } + + + $order_type = $preorder->type; + + foreach(TT_PREORDER_RIMO_STATUS_MATRIX as $matrix) { + //echo "wohneinheit ".$wohneinheit->id."\n"; + //var_dump($matrix); + if($matrix["rbop"] && $matrix["rbop"] != $b_op_state) continue; + if($matrix["rbex"] && $matrix["rbex"] != $b_ex_state) continue; + + if($matrix["rhop"] && $matrix["rhop"] != $h_op_state) continue; + if($matrix["rhex"] && $matrix["rhex"] != $h_ex_state) continue; + + if($matrix["rwo"] && (!$workorder || $matrix["rwo"] != $wo_state)) continue; + + if($matrix["pt"] && $matrix["pt"] != $order_type) continue; + // seems all criteria match => set new status + + $log->debug(__METHOD__.": new Preorder Status: ".$matrix["p"]); + + $preorderstatus = $matrix["p"]; + if(!$preorderstatus) { + continue; + } + + $preorder->setNewStatusCode($preorderstatus); + $preorder->save(); + + $hausnummer_status = $matrix["h"]; + if($hausnummer_status) { + $log->debug(__METHOD__.": new Hausnummer (".$hausnummer->id.") status: ".$matrix["h"]); + $hausnummer->setNewStatusCode($hausnummer_status); + $hausnummer->save(); + } + + $wohneinheit_status = $matrix["w"]; + if($wohneinheit_status) { + $wohneinheit->setNewStatusCode($wohneinheit_status); + $wohneinheit->save(); + } + + + } + return true; + } + public function import($input) { $path = __DIR__."/Importer/"; $dir = opendir($path); diff --git a/application/IpNetwork/IpNetworkController.php b/application/IpNetwork/IpNetworkController.php index 5477a943d..2c66a2edd 100644 --- a/application/IpNetwork/IpNetworkController.php +++ b/application/IpNetwork/IpNetworkController.php @@ -180,5 +180,39 @@ class IpNetworkController extends mfBaseController { } } + private function update(): array { + $json = json_decode(file_get_contents('php://input'), true); + + try { + IpNetworkModel::updateIpNetwork($json); + return [ + "status" => "success", + "message" => "IP Network updated." + ]; + } catch (Exception $e) { + return [ + "status" => "error", + "message" => $e->getMessage() + ]; + } + } + + private function delete(): array { + $json = json_decode(file_get_contents('php://input'), true); + + try { + IpNetworkModel::deleteIpNetwork($json['id']); + return [ + "status" => "success", + "message" => "IP Network deleted." + ]; + } catch (Exception $e) { + return [ + "status" => "error", + "message" => $e->getMessage() + ]; + } + } + } \ No newline at end of file diff --git a/application/IpNetwork/IpNetworkModel.php b/application/IpNetwork/IpNetworkModel.php index ee19afbfb..6f5811861 100644 --- a/application/IpNetwork/IpNetworkModel.php +++ b/application/IpNetwork/IpNetworkModel.php @@ -200,6 +200,24 @@ class IpNetworkModel { } } + public static function updateIpNetwork($data): void { + $db = FronkDB::singleton(); + + $sqlSetStr = ""; + $sqlSetStr .= isset($data['status']) ? "`status` = '" . $data['status'] . "', " : ""; + $sqlSetStr .= isset($data['name']) ? "`name` = '" . $data['name'] . "', " : ""; + $sqlSetStr .= isset($data['description']) ? "`description` = '" . $data['description'] . "', " : ""; + $sqlSetStr .= isset($data['location']) ? "`location` = '" . $data['location'] . "', " : ""; + $sqlSetStr .= "`edit` = UNIX_TIMESTAMP()"; + + $sql = "UPDATE `IpNetwork` SET $sqlSetStr WHERE `id` = " . $data['id']; + $result = $db->query($sql); + + if (!$result) { + throw new Exception("Failed to update network"); + } + } + public static function getById($id) { $db = FronkDB::singleton(); @@ -209,4 +227,22 @@ class IpNetworkModel { return $row ? new IpNetworkModel($row) : null; } + public static function deleteIpNetwork($id) { + // delete this id and all children and children of children until no more children + $db = FronkDB::singleton(); + $sql = "SELECT `id` FROM `IpNetwork` WHERE `parent_network_id` = $id"; + $result = $db->query($sql); + + while ($row = $result->fetch_assoc()) { + self::deleteIpNetwork($row['id']); + } + + $sql = "DELETE FROM `IpNetwork` WHERE `id` = $id"; + $result = $db->query($sql); + if (!$result) { + throw new Exception("Failed to delete network"); + } + + } + } \ No newline at end of file diff --git a/application/Preorder/Preorder.php b/application/Preorder/Preorder.php index 2eadcf591..33dd5df87 100644 --- a/application/Preorder/Preorder.php +++ b/application/Preorder/Preorder.php @@ -235,7 +235,7 @@ class Preorder extends mfBaseModel { } - private function cascadeStatusToPreorders() { + public function cascadeStatusToPreorders() { $status = new Preorderstatus($this->status_id); if(!$status->id) { return false; @@ -427,6 +427,18 @@ class Preorder extends mfBaseModel { } } } + + public function setNewStatusCode($new_status_code) { + if(!$new_status_code) return false; + + $new_status = PreorderstatusModel::getFirst(["code" => $new_status_code]); + if(!$new_status) return false; + + $status = $this->getProperty("status"); + if($status->code < $new_status->code) { + $this->status_id = $new_status->id; + } + } public function createUcode() { $ucode = $this->generateNewUcode(); diff --git a/application/RimoWorkorder/RimoWorkorder.php b/application/RimoWorkorder/RimoWorkorder.php index 2956f18d3..e915dacd8 100644 --- a/application/RimoWorkorder/RimoWorkorder.php +++ b/application/RimoWorkorder/RimoWorkorder.php @@ -3,7 +3,27 @@ class RimoWorkorder extends mfBaseModel { private $adb_wohneinheit; private $termination; - + + public function afterSave() { + // prevent potential infinite loop + $nesting_level = mfValuecache::singleton()->get("rimoworkorder-save-nesting-level-".$this->id); + if(!$nesting_level) { + $nesting_level = 1; + } else { + $nesting_level++; + } + mfValuecache::singleton()->set("rimoworkorder-save-nesting-level-".$this->id, $nesting_level); + + if($nesting_level > 1) { + return true; + } + + // Statuschange from Rimo statuschange for all units + $wohneinheit = $this->getProperty("adb_wohneinheit"); + if($wohneinheit) { + AddressDB::handleRimoStatusUpdate($wohneinheit->id); + } + } public function getProperty($name) { if($this->$name == null) { diff --git a/application/User/User.php b/application/User/User.php index b4cdc27e1..2cb02d3ca 100644 --- a/application/User/User.php +++ b/application/User/User.php @@ -261,10 +261,9 @@ class User extends mfBaseModel { if(!is_array($what)) { $what = [$what]; } - - //ob_end_clean();var_dump($what, $this->permissions);exit; + foreach($what as $w) { - $perm = ucfirst(strtolower($w)); + $perm = ucfirst(($w)); if(is_object($this->permissions) && property_exists($this->permissions->data, "can$perm")) { if($this->permissions->{"can$perm"} === "true") { return true; diff --git a/application/User/UserController.php b/application/User/UserController.php index d239ce342..bc65ba9bb 100644 --- a/application/User/UserController.php +++ b/application/User/UserController.php @@ -235,6 +235,9 @@ class UserController extends mfBaseController $user->permissions->canBilling = "false"; $user->permissions->canFibu = "false"; $user->permissions->canStatistics = "false"; + $user->permissions->canWarehouseAdmin = "false"; + $user->permissions->canWarehouseEShop = "false"; + $user->permissions->canWarehouseUser = "false"; if($r->get("can") && is_array($r->can)) { foreach($r->can as $key => $can) { diff --git a/application/WarehouseArticle/WarehouseArticleController.php b/application/WarehouseArticle/WarehouseArticleController.php index 73d2734bf..643fe6f8b 100644 --- a/application/WarehouseArticle/WarehouseArticleController.php +++ b/application/WarehouseArticle/WarehouseArticleController.php @@ -9,11 +9,16 @@ class WarehouseArticleController extends TTCrud { ['key' => 'title', 'text' => 'Titel', 'required' => true, 'table' => ['priority' => 9]], ['key' => 'description', 'text' => 'Beschreibung', 'required' => true, 'table' => false], ['key' => 'category', 'text' => 'Kategorie', 'required' => true], + ['key' => 'unit', 'text' => 'Einheit', 'required' => true,'table' => false], // Boolean value + ['key' => 'defaultSellMultiplier', 'text' => 'Standard Multiplikator','regex' => '/^[0-9]*$/' , 'required' => true,'modal' => ['type' => 'number'], 'table' => false], // Boolean value + ['key' => 'revenueAccount', 'text' => 'Erlöskonto', 'required' => true,'modal' => ['type' => 'select'], 'table' => false], // Boolean value ['key' => 'cheapestPurchasePrice', 'text' => 'Einkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']], ['key' => 'cheapestSellPrice', 'text' => 'Verkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']], - ['key' => 'warningAmount', 'text' => 'Warnmenge', 'modal' => ['type' => 'number'], 'table' => ['class' => 'text-center']], // Stock/inventory related - ['key' => 'criticalAmount', 'text' => 'Kritische Menge', 'modal' => ['type' => 'number'], 'table' => ['class' => 'text-center']], // Stock/inventory related - ['key' => 'isEShop', 'text' => 'Ist E-Shop', 'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value + ['key' => 'warningAmount', 'text' => 'Warnmenge', 'required' => true,'modal' => ['type' => 'number'], 'table' => ['class' => 'text-center']], // Stock/inventory related + ['key' => 'criticalAmount', 'text' => 'Kritische Menge', 'required' => true,'modal' => ['type' => 'number'], 'table' => ['class' => 'text-center']], // Stock/inventory related + ['key' => 'isSerialDocumentation', 'text' => 'Seriennummern', 'required' => true,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value + ['key' => 'isEShop', 'text' => 'Ist E-Shop', 'required' => true,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value + ['key' => 'isEShopHide', 'text' => 'E-Shop Versteckt', 'required' => true,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value ['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center', 'priority' => 8]] ]; @@ -30,6 +35,15 @@ class WarehouseArticleController extends TTCrud { 'delete' => 'Artikel wurde gelöscht', 'noChanges' => 'Keine Änderungen',]; + public function prepareCrudConfig() { + $revenueAccounts = WarehouseRevenueAccountModel::getAll(); + $revenueAccounts = array_map(function ($revenueAccount) { + return ['value' => $revenueAccount->id, 'text' => $revenueAccount->title]; + }, $revenueAccounts); + + $this->columns[5]['modal']['items'] = $revenueAccounts; + } + protected function beforeUpdate($postData): bool { (new WarehouseHistoryController)->create($postData, $this->mod); return true; @@ -39,33 +53,75 @@ class WarehouseArticleController extends TTCrud { self::updateCheapestPurchasePrice($postData['id']); } - public static function updateCheapestPurchasePrice($articleId) { - $cheapestPurchasePrice = WarehouseArticleDistributorModel::getAll(['articleId' => $articleId], 1, 0, - ['key' => 'purchasePrice', 'order' => 'ASC'])[0]->purchasePrice; - + /** + * Updates the cheapest purchase price for a given article from WarehouseArticleDistributorModel prices. + * + * @param int $articleId The ID of the article to update. + * @return void + * @throws Exception If the article is not an instance of WarehouseArticleModel. + */ + public static function updateCheapestPurchasePrice(int $articleId): void { $article = WarehouseArticleModel::get($articleId); if (!$article instanceof WarehouseArticleModel) { throw new Exception("Article is not an instance of WarehouseArticleModel"); } - //TODO: think of a new way as we have multiple sell prices now and article sellPriceOverride and sellPriceMultiplier do not exist anymore - // $cheapestSellPrice = $article->sellPriceOverride ?? $article->sellPriceMultiplier * $cheapestPurchasePrice; + $order = ['key' => 'purchasePrice', 'order' => 'ASC']; + $cheapestDistributorEntry = WarehouseArticleDistributorModel::getAll(['articleId' => $articleId], 1, 0, $order); - if ($article->cheapestPurchasePrice != $cheapestPurchasePrice) { - WarehouseArticleModel::update([...get_object_vars($article), // Unpack properties into an array - 'cheapestPurchasePrice' => $cheapestPurchasePrice]); - } + if (empty($cheapestDistributorEntry)) return; + + $cheapestPurchasePrice = $cheapestDistributorEntry[0]->purchasePrice; + if ($article->cheapestPurchasePrice == $cheapestPurchasePrice) return; + WarehouseArticleModel::update(array_merge(get_object_vars($article), ['cheapestPurchasePrice' => $cheapestPurchasePrice])); } + /** + * Updates the sell prices for a given article. + * + * @param int $articleId The ID of the article to update. + * @return void + * @throws Exception If the article is not an instance of WarehouseArticleModel. + */ + public static function updateSellPrices(int $articleId) { + $article = WarehouseArticleModel::get($articleId); + if (!$article instanceof WarehouseArticleModel) { + throw new Exception("Article is not an instance of WarehouseArticleModel"); + } + + $priceTypes = WarehouseArticlePriceTypeModel::getAll(); + $articlePriceTypes = WarehouseArticlePriceModel::getAll(['articleId' => $articleId]); + + $cheapestSellPrices = []; + // Calculate sell prices for each price type, use default sell multiplier if no specific price is set + foreach ($priceTypes as $priceType) { + $articlePriceType = array_filter($articlePriceTypes, function ($apt) use ($priceType) { + return $apt->articlePriceTypeId == $priceType->id; + }); + + $sellPrice = $article->defaultSellMultiplier * $article->cheapestPurchasePrice; + if (!empty($articlePriceType)) { + $articlePriceType = $articlePriceType[0]; + $sellPrice = $articlePriceType->priceOverride ?: $articlePriceType->priceMultiplier * $article->cheapestPurchasePrice; + } + $cheapestSellPrices[$priceType->id] = ['title' => $priceType->title, 'price' => $sellPrice]; + } + + $article->cheapestSellPrice = json_encode($cheapestSellPrices); + WarehouseArticleModel::update(get_object_vars($article)); + } + + protected function afterCreate($postData) { self::updateCheapestPurchasePrice($postData['id']); + self::updateSellPrices($postData['id']); } - protected function updateAllCheapestPurchasePricesAction() { - $articles = WarehouseArticleModel::getAll(); - foreach ($articles as $article) { + protected function updatePricesAction() { + foreach (WarehouseArticleModel::getAll() as $article) { self::updateCheapestPurchasePrice($article->id); + self::updateSellPrices($article->id); } } @@ -175,12 +231,12 @@ class WarehouseArticleController extends TTCrud { // Check if ArticleDistributor exists $articleDistributor = WarehouseArticleDistributorModel::getAll(['articleId' => $articleId, - 'distributorId' => $distributorId]); + 'distributorId' => $distributorId]); if (empty($articleDistributor)) { WarehouseArticleDistributorModel::create(['articleId' => $articleId, - 'distributorId' => $distributorId, - 'purchasePrice' => $purchasePrice, - 'externalArticleNumber' => $item['Lieferant/ Hersteller Artikelnr:'],]); + 'distributorId' => $distributorId, + 'purchasePrice' => $purchasePrice, + 'externalArticleNumber' => $item['Lieferant/ Hersteller Artikelnr:'],]); } } diff --git a/application/WarehouseArticle/WarehouseArticleModel.php b/application/WarehouseArticle/WarehouseArticleModel.php index 30634f526..926920fac 100644 --- a/application/WarehouseArticle/WarehouseArticleModel.php +++ b/application/WarehouseArticle/WarehouseArticleModel.php @@ -6,10 +6,15 @@ class WarehouseArticleModel extends TTCrudBaseModel { public string $description; public string $category; public ?float $cheapestPurchasePrice; - public ?float $cheapestSellPrice; + public ?string $cheapestSellPrice; public int $warningAmount; public int $criticalAmount; public int $isEShop; + public int $isEShopHide; + public float $defaultSellMultiplier; + public string $unit; + public int $isSerialDocumentation; + public int $revenueAccount; } \ No newline at end of file diff --git a/application/WarehouseArticleDistributor/WarehouseArticleDistributorController.php b/application/WarehouseArticleDistributor/WarehouseArticleDistributorController.php index db8fa6e84..73aa7ca14 100644 --- a/application/WarehouseArticleDistributor/WarehouseArticleDistributorController.php +++ b/application/WarehouseArticleDistributor/WarehouseArticleDistributorController.php @@ -20,29 +20,37 @@ class WarehouseArticleDistributorController extends TTCrud { 'delete' => 'Lieferanteintrag wurde gelöscht', 'noChanges' => 'Keine Änderungen',]; - protected function checkExistingThresholdEntry($postData): bool { - $count = WarehouseLocationThresholdOverrideModel::count(['articleId' => $postData['articleId'], - 'distributorId' => $postData['distributorId']]); - - if ($count > 0) { - self::returnJson(['success' => false, - 'message' => 'Es existiert bereits ein Eintrag mit dieser Artikelnummer und diesem Lieferanten.']); - return false; - } - - return true; + protected function beforeCreate($postData): bool { + return $this->checkExistingDistributorEntry($postData); } - protected function beforeCreate($postData): bool { - return $this->checkExistingThresholdEntry($postData); + protected function checkExistingDistributorEntry($postData): bool { + if (isset($postData['id'])) { + $count = WarehouseArticleDistributorModel::count(['articleId' => $postData['articleId'], + 'distributorId' => $postData['articlePriceTypeId'], + 'id' => $postData['id']]); + + if ($count > 0) return true; + } else { + $count = WarehouseArticleDistributorModel::count(['articleId' => $postData['articleId'], + 'distributorId' => $postData['distributorId']]); + if ($count > 0) { + self::returnJson(['success' => false, + 'message' => 'Es existiert bereits ein Eintrag mit dieser Artikelnummer und diesem Lieferanten.']); + return false; + + } + } + return true; } protected function afterCreate($postData) { WarehouseArticleController::updateCheapestPurchasePrice($postData['articleId']); + WarehouseArticleController::updateSellPrices($postData['articleId']); } protected function beforeUpdate($postData): bool { - $existing = $this->checkExistingThresholdEntry($postData); + $existing = $this->checkExistingDistributorEntry($postData); if (!$existing) { return false; @@ -55,6 +63,7 @@ class WarehouseArticleDistributorController extends TTCrud { protected function afterUpdate($postData) { WarehouseArticleController::updateCheapestPurchasePrice($postData['articleId']); + WarehouseArticleController::updateSellPrices($postData['articleId']); } protected function getHistoryAction() { diff --git a/application/WarehouseArticlePacket/WarehouseArticlePacket.php b/application/WarehouseArticlePacket/WarehouseArticlePacket.php new file mode 100644 index 000000000..7b4d60ff1 --- /dev/null +++ b/application/WarehouseArticlePacket/WarehouseArticlePacket.php @@ -0,0 +1,9 @@ + 'title', 'text' => 'Titel', 'required' => true], + ['key' => 'description', 'text' => 'Beschreibung', 'required' => true], + ['key' => 'category', 'text' => 'Kategorie', 'required' => true], + ['key' => 'overrideSellPrice', 'text' => 'Überschriebener Verkaufspreis', 'required' => false, 'modal' => ['type' => 'number'], 'table' => false], + ['key' => 'calculatedSellPrice', 'text' => 'Verkaufspreis', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false]], + ['key' => 'subItems', 'text' => 'Unterartikel', 'required' => true], + ['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center', 'priority' => 10]], + ]; + // @formatter:on + + protected array $infoMessages = ['create' => 'Artikel-Paket wurde erstellt', + 'update' => 'Artikel-Paket wurde aktualisiert', + 'delete' => 'Artikel-Paket wurde gelöscht', + 'noChanges' => 'Keine Änderungen']; + + protected function prepareCrudConfig() { + $articles = array_map(function ($article) { + return ['value' => $article->id, 'text' => $article->title]; + }, WarehouseArticleModel::getAll( + ['isEShop' => 1], + )); + + $this->columns[5]['modal']['items'] = $articles; + } + + //TODO: make this so it does not update all packets at the same time + protected function updatePacketPricesAction() { + $packets = WarehouseArticlePacketModel::getAll(); + $articles = WarehouseArticleModel::getAll(['isEShop' => 1]); + + // packet has $calculatedSellPrice for this but when overrideSellPrice is set, it should be used + + foreach ($packets as $packet) { + if ($packet->overrideSellPrice) { + $calculatedSellPrice = $packet->overrideSellPrice; + } else { + $subItems = json_decode($packet->subItems); + $calculatedSellPrice = 0; + + foreach ($subItems as $subItem) { + $article = WarehouseArticleModel::get($subItem->id); + $cheapestSellPrices = json_decode($article->cheapestSellPrice); + // find in array cheapestSellPrices by title === 'Energie Steiermark' and get the price + $articlePrice = array_values(array_filter($cheapestSellPrices, function ($cheapestSellPrice) { + return $cheapestSellPrice->title === 'Energie Steiermark'; + }))[0]->price; + + $calculatedSellPrice += $subItem->amount * $articlePrice; + } + } + + WarehouseArticlePacketModel::update([...get_object_vars($packet), // Unpack properties into an array + 'calculatedSellPrice' => $calculatedSellPrice]); + } + + return true; + } + + protected function afterUpdate(): bool { + return $this->updatePacketPricesAction(); + } + + protected function afterCreate(): bool { + return $this->updatePacketPricesAction(); + } + + protected function beforeUpdate($postData): bool { + (new WarehouseHistoryController)->create($postData, $this->mod); + + return true; + } + + protected function getHistoryAction() { + $history = WarehouseHistoryModel::getByRowId($this->request->id, $this->mod); + + $history = array_map(function ($item) { + $item = (array) $item; + + $item['columnHeader'] = $this->columns[array_search($item['key'], array_column($this->columns, 'key'))]['text']; + return $item; + }, $history); + + self::returnJson($history); + } + +} \ No newline at end of file diff --git a/application/WarehouseArticlePacket/WarehouseArticlePacketModel.php b/application/WarehouseArticlePacket/WarehouseArticlePacketModel.php new file mode 100644 index 000000000..fce43e62f --- /dev/null +++ b/application/WarehouseArticlePacket/WarehouseArticlePacketModel.php @@ -0,0 +1,11 @@ +validate($postData); } + //TODO: phpdoc and simplify protected function validate($postData): bool { + // if either priceOverride or priceMultiplier is empty set it to null + if (isset($postData['priceOverride']) && $postData['priceOverride'] === '') { + $postData['priceOverride'] = null; + } + if (isset($postData['priceMultiplier']) && $postData['priceMultiplier'] === '') { + $postData['priceMultiplier'] = null; + } + // check if postData priceOverride or priceMultiplier is set but only one of them if (isset($postData['priceOverride']) && isset($postData['priceMultiplier'])) { self::returnJson(['success' => false, @@ -34,11 +43,11 @@ class WarehouseArticlePriceController extends TTCrud { return false; } - if (isset($postData['id'])) { $count = WarehouseArticlePriceModel::count(['articleId' => $postData['articleId'], 'articlePriceTypeId' => $postData['articlePriceTypeId'], 'id' => $postData['id']]); + if ($count > 0) return true; } else { $count = WarehouseArticlePriceModel::count(['articleId' => $postData['articleId'], @@ -78,4 +87,12 @@ class WarehouseArticlePriceController extends TTCrud { self::returnJson($history); } + public function afterCreate($postData) { + WarehouseArticleController::updateSellPrices($postData['articleId']); + } + + public function afterUpdate($postData) { + WarehouseArticleController::updateSellPrices($postData['articleId']); + } + } \ No newline at end of file diff --git a/application/WarehouseArticlePriceType/WarehouseArticlePriceTypeController.php b/application/WarehouseArticlePriceType/WarehouseArticlePriceTypeController.php index b8cfbd12f..e9cc254fb 100644 --- a/application/WarehouseArticlePriceType/WarehouseArticlePriceTypeController.php +++ b/application/WarehouseArticlePriceType/WarehouseArticlePriceTypeController.php @@ -51,6 +51,14 @@ class WarehouseArticlePriceTypeController extends TTCrud { return true; } + public function afterCreate($postData) { + WarehouseArticleController::updateSellPrices($postData['articleId']); + } + + public function afterUpdate($postData) { + WarehouseArticleController::updateSellPrices($postData['articleId']); + } + protected function getHistoryAction() { $history = WarehouseHistoryModel::getByRowId($this->request->id, $this->mod); diff --git a/application/WarehouseEShop/WarehouseEShopController.php b/application/WarehouseEShop/WarehouseEShopController.php index 7f23a26a9..f57b0afc9 100644 --- a/application/WarehouseEShop/WarehouseEShopController.php +++ b/application/WarehouseEShop/WarehouseEShopController.php @@ -1,12 +1,18 @@ 'title', 'text' => 'Titel'], - ['key' => 'category', 'text' => 'Kategorie'], + ['key' => 'title', 'text' => 'Artikel'], + ['key' => 'category', 'text' => 'Kategorie', 'table' => false], + ['key' => 'price', 'text' => 'Preis', 'table' => ['filter' => false,'sortable' => false,'class' => 'text-right']], ['key' => 'amount', 'text' => 'Menge', 'table' => ['filter' => false,'sortable' => false,'class' => 'p-0 width-80']], ['key' => 'add', 'text' => 'Hinzufügen', 'table' => ['filter' => false,'sortable' => false, 'class' => 'width-120 text-center']] ]; @@ -18,24 +24,36 @@ class WarehouseEShopController extends TTCrud { 'noChanges' => 'Keine Änderungen', ]; + public function permissionCheck(): bool { + return $this->user->can(["WarehouseEShop"]); + } + public function getAction() { $filter = $this->postData['filters'] ?? []; $order = $this->postData['order'] ?? ['key' => null, 'order' => 'ASC']; $page = $this->postData['pagination']['page'] ?? 1; $perPage = $this->postData['pagination']['per_page'] ?? 10; - $filter['isEShop'] = 1; + $warehouseArticleFilter = $filter; + $warehouseArticleFilter['isEShop'] = 1; + $warehouseArticleFilter['isEShopHide'] = 0; - $rows = WarehouseArticleModel::getAll($filter, $perPage, ($page - 1) * $perPage, $order); - $filteredAvailable = WarehouseArticleModel::count($filter); - $totalRows = WarehouseArticleModel::count(['isEShop' => 1]); + $warehouseArticles = WarehouseArticleModel::getAll($warehouseArticleFilter, $perPage, ($page - 1) * $perPage, $order); + $warehouseArticlesTotal = WarehouseArticleModel::count(['isEShop' => 1, 'isEShopHide' => 0]); + $warehouseArticlesAvailable = WarehouseArticleModel::count($warehouseArticleFilter); + + $warehousePackets = WarehouseArticlePacketModel::getAll(); + $warehousePacketsTotal = WarehouseArticlePacketModel::count(); + $warehousePacketsAvailable = WarehouseArticlePacketModel::count($filter); + + $filteredAvailable = $warehouseArticlesAvailable + $warehousePacketsAvailable; + $totalRows = $warehouseArticlesTotal + $warehousePacketsTotal; + $rows = [...$warehouseArticles, ...$warehousePackets]; self::returnJson(["rows" => $rows, "pagination" => ["page" => $page, "total_pages" => ceil($filteredAvailable / $perPage), "per_page" => $perPage, - "filtered_available" => intval($filteredAvailable), - "total_rows" => intval($totalRows)]]); } - - + "filtered_available" => $filteredAvailable, + "total_rows" => $totalRows]]); } } \ No newline at end of file diff --git a/application/WarehouseEShopOrder/WarehouseEShopOrderController.php b/application/WarehouseEShopOrder/WarehouseEShopOrderController.php index 6a5702b05..e767dbea2 100644 --- a/application/WarehouseEShopOrder/WarehouseEShopOrderController.php +++ b/application/WarehouseEShopOrder/WarehouseEShopOrderController.php @@ -9,7 +9,7 @@ class WarehouseEShopOrderController extends TTCrud { ['key' => 'status', 'text' => 'Status', 'required' => true], ['key' => 'deliveryMode', 'text' => 'Liefermodus', 'required' => true, 'modal' => ['type' => 'select', 'items' => [ ['value' => 'singleAddress', 'text' => 'Einzelne Adresse'], - ['value' => 'multipleAddresses', 'text' => 'Mehrere Adressen'], +// ['value' => 'multipleAddresses', 'text' => 'Mehrere Adressen'], ]]], ['key' => 'deliveryAddressName', 'text' => 'Lieferadresse Name', 'required' => true, 'table' => false], ['key' => 'deliveryAddressLine', 'text' => 'Lieferadresse', 'required' => true, 'required_length' => 4], @@ -31,6 +31,10 @@ class WarehouseEShopOrderController extends TTCrud { 'noChanges' => 'Keine Änderungen', ]; + public function permissionCheck(): bool { + return $this->user->can(["WarehouseEShop"]); + } + protected function prepareCrudConfig() { $users = array_map(function($user) { return ['value' => intval($user->id), 'text' => $user->name]; @@ -67,11 +71,24 @@ class WarehouseEShopOrderController extends TTCrud { // now create WarehouseEShopOrderItems for each item in the shopping cart foreach ($shoppingCart as $item) { - WarehouseEShopOrderItemModel::create([ - 'orderId' => $id, - 'articleId' => $item['itemId'], - 'quantity' => intval($item['amount']), - ]); + // itemId can either be P-[PACKETID] or I-[ARTICLEID] + // parse this and either fill articleId or articlePacketId for warehouseEShopOrderItem + if (strpos($item['itemId'], 'P-') === 0) { + WarehouseEShopOrderItemModel::create([ + 'orderId' => $id, + 'articlePacketId' => intval(substr($item['itemId'], 2)), + 'quantity' => intval($item['amount']), + ]); + } else if (strpos($item['itemId'], 'I-') === 0) { + WarehouseEShopOrderItemModel::create([ + 'orderId' => $id, + 'articleId' => intval(substr($item['itemId'], 2)), + 'quantity' => intval($item['amount']), + ]); + } else { + self::returnJson(['success' => false, 'message' => 'Invalid item id']); + die(); + } } self::returnJson(['success' => true, diff --git a/application/WarehouseEShopOrderItem/WarehouseEShopOrderItemModel.php b/application/WarehouseEShopOrderItem/WarehouseEShopOrderItemModel.php index 9fcc6627e..6bbf0cd56 100644 --- a/application/WarehouseEShopOrderItem/WarehouseEShopOrderItemModel.php +++ b/application/WarehouseEShopOrderItem/WarehouseEShopOrderItemModel.php @@ -9,6 +9,7 @@ class WarehouseEShopOrderItemModel extends TTCrudBaseModel { public int $id; public int $orderId; - public int $articleId; + public ?int $articleId; + public ?int $articlePacketId; public int $quantity; } \ No newline at end of file diff --git a/application/WarehouseHistory/WarehouseHistoryModel.php b/application/WarehouseHistory/WarehouseHistoryModel.php index 5e92ee25b..4c90decf5 100644 --- a/application/WarehouseHistory/WarehouseHistoryModel.php +++ b/application/WarehouseHistory/WarehouseHistoryModel.php @@ -24,9 +24,8 @@ class WarehouseHistoryModel { public static function create($data) { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; - $sql = /** @lang text */ "INSERT INTO `WarehouseHistory` (`table`, `row_id`, `key`, `old_value`, `new_value`, `note`, `user_id`, `create`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; - $stmt = $db->prepare($sql); - $stmt->execute([ + + $dataArr = [ $data["table"], $data["row_id"], $data["key"], @@ -35,9 +34,16 @@ class WarehouseHistoryModel { $data["note"], $data["user_id"], $data["create"] - ]); + ]; - return $stmt->insert_id; + $sqlValueStr = "(" . implode(", ", array_map(function ($item) use ($db) { + return "'" . $db->real_escape_string($item) . "'"; + }, $dataArr)) . ")"; + + $sql = /** @lang text */ "INSERT INTO `WarehouseHistory` (`table`, `row_id`, `key`, `old_value`, `new_value`, `note`, `user_id`, `create`) VALUES $sqlValueStr"; + $db->query($sql) or die($db->error); + + return $db->insert_id; } /** * Retrieves an array of WarehouseHistoryModel objects by row ID. diff --git a/application/WarehouseRevenueAccount/WarehouseRevenueAccount.php b/application/WarehouseRevenueAccount/WarehouseRevenueAccount.php new file mode 100644 index 000000000..8077d5496 --- /dev/null +++ b/application/WarehouseRevenueAccount/WarehouseRevenueAccount.php @@ -0,0 +1,9 @@ + 'title', 'text' => 'Titel', 'required' => true], + ['key' => 'revenueAccountNumber', 'text' => 'Erlöskonto Nummer', 'required' => true, 'modal' => ['type' => 'number']], + ['key' => 'actions', 'text' => 'Aktionen', 'required' => false, 'modal' => false, 'table' => ['filter' => false, 'sortable' => false, 'class' => 'text-center', 'priority' => 10]], + ]; + // @formatter:on + + protected array $infoMessages = ['create' => 'Erlöskonto wurde erstellt', + 'update' => 'Erlöskonto wurde aktualisiert', + 'delete' => 'Erlöskonto wurde gelöscht', + 'noChanges' => 'Keine Änderungen']; + + protected function beforeUpdate($postData): bool { + (new WarehouseHistoryController)->create($postData, $this->mod); + + return true; + } + + protected function getHistoryAction() { + $history = WarehouseHistoryModel::getByRowId($this->request->id, $this->mod); + + $history = array_map(function ($item) { + $item = (array) $item; + + $item['columnHeader'] = $this->columns[array_search($item['key'], array_column($this->columns, 'key'))]['text']; + return $item; + }, $history); + + self::returnJson($history); + } + +} \ No newline at end of file diff --git a/application/WarehouseRevenueAccount/WarehouseRevenueAccountModel.php b/application/WarehouseRevenueAccount/WarehouseRevenueAccountModel.php new file mode 100644 index 000000000..2113d4b9f --- /dev/null +++ b/application/WarehouseRevenueAccount/WarehouseRevenueAccountModel.php @@ -0,0 +1,7 @@ +getEnvironment() == "thetool") { + $table = $this->table("WorkerPermission"); + $table->addColumn("canWarehouseAdmin", "enum", ["null" => false, "values" => 'false,true', "default" => "false", "after" => "canSuperexpert"]); + $table->addColumn("canWarehouseUser", "enum", ["null" => false, "values" => 'false,true', "default" => "false", "after" => "canSuperexpert"]); + $table->addColumn("canWarehouseEShop", "enum", ["null" => false, "values" => 'false,true', "default" => "false", "after" => "canSuperexpert"]); + $table->update(); + } + + if ($this->getEnvironment() == "addressdb") { + + } + } + + public function down(): void { + if ($this->getEnvironment() == "thetool") { + $table = $this->table("WorkerPermission"); + $table->removeColumn("canWarehouseAdmin"); + $table->removeColumn("canWarehouseUser"); + $table->removeColumn("canWarehouseEShop"); + $table->save(); + } + + if ($this->getEnvironment() == "addressdb") { + + } + } +} diff --git a/db/migrations/20240724143300_update_warehouse_tables.php b/db/migrations/20240724143300_update_warehouse_tables.php new file mode 100644 index 000000000..39c824e41 --- /dev/null +++ b/db/migrations/20240724143300_update_warehouse_tables.php @@ -0,0 +1,62 @@ +getEnvironment() == "thetool") { + $WarehouseArticle = $this->table("WarehouseArticle"); + $WarehouseArticle->changeColumn("cheapestSellPrice", "text", ["null" => false]); + $WarehouseArticle->addColumn("defaultSellMultiplier", "float", ["null" => false, "default" => 1]); + $WarehouseArticle->addColumn("unit", "string", ["null" => false]); + $WarehouseArticle->addColumn("isSerialDocumentation", "integer", ["null" => false]); + $WarehouseArticle->addColumn("revenueAccount", "integer", ["null" => false]); + $WarehouseArticle->update(); + + $WarehouseArticlePacket = $this->table("WarehouseArticlePacket", ["signed" => true]); + $WarehouseArticlePacket->addColumn("title", "string", ["null" => false]); + $WarehouseArticlePacket->addColumn("description", "text", ["null" => false]); + $WarehouseArticlePacket->addColumn("category", "string", ["null" => false]); + $WarehouseArticlePacket->addColumn("overrideSellPrice", "float", ["null" => true]); + $WarehouseArticlePacket->addColumn("calculatedSellPrice", "float", ["null" => true]); + $WarehouseArticlePacket->addColumn("subItems", "text", ["null" => false]); + $WarehouseArticlePacket->create(); + + $WarehouseEShopOrderItem = $this->table("WarehouseEShopOrderItem"); + $WarehouseEShopOrderItem->changeColumn("articleId", "integer", ["null" => true]); + $WarehouseEShopOrderItem->addColumn("articlePacketId", "integer", ["null" => true]); + $WarehouseEShopOrderItem->update(); + + $WarehouseRevenueAccount = $this->table("WarehouseRevenueAccount", ["signed" => true]); + $WarehouseRevenueAccount->addColumn("revenueAccountNumber", "integer", ["null" => false]); + $WarehouseRevenueAccount->addColumn("title", "string", ["null" => false]); + $WarehouseRevenueAccount->create(); + + } + } + + public function down(): void { + if ($this->getEnvironment() == "thetool") { + $WarehouseArticle = $this->table("WarehouseArticle"); + $WarehouseArticle->changeColumn("cheapestSellPrice", "float", ["null" => false]); + $WarehouseArticle->removeColumn("defaultSellMultiplier"); + $WarehouseArticle->removeColumn("unit"); + $WarehouseArticle->removeColumn("isSerialDocumentation"); + $WarehouseArticle->removeColumn("revenueAccount"); + $WarehouseArticle->update(); + + $WarehouseArticlePacket = $this->table("WarehouseArticlePacket"); + $WarehouseArticlePacket->drop()->save(); + + $WarehouseEShopOrderItem = $this->table("WarehouseEShopOrderItem"); + $WarehouseEShopOrderItem->changeColumn("articleId", "integer", ["null" => false]); + $WarehouseEShopOrderItem->removeColumn("articlePacketId"); + $WarehouseEShopOrderItem->update(); + + $WarehouseRevenueAccount = $this->table("WarehouseRevenueAccount"); + $WarehouseRevenueAccount->drop()->save(); + + } + } +} diff --git a/db/migrations/20240724155700_warehouse_add_e_shop_hide.php b/db/migrations/20240724155700_warehouse_add_e_shop_hide.php new file mode 100644 index 000000000..40d60e27d --- /dev/null +++ b/db/migrations/20240724155700_warehouse_add_e_shop_hide.php @@ -0,0 +1,22 @@ +getEnvironment() == "thetool") { + $WarehouseArticle = $this->table("WarehouseArticle"); + $WarehouseArticle->addColumn("isEShopHide", "integer", ["null" => false]); + $WarehouseArticle->update(); + } + } + + public function down(): void { + if ($this->getEnvironment() == "thetool") { + $WarehouseArticle = $this->table("WarehouseArticle"); + $WarehouseArticle->removeColumn("isEShopHide"); + $WarehouseArticle->update(); + } + } +} diff --git a/lib/Helper/Helper.php b/lib/Helper/Helper.php index 2feba1cbb..b63e05c81 100644 --- a/lib/Helper/Helper.php +++ b/lib/Helper/Helper.php @@ -34,6 +34,8 @@ class Helper { $sql .= " AND `$columnName` LIKE '%" . $item . "%'"; } } + } else if ($filterValue === 0) { + $sql .= " AND `$columnName` = 0"; } return $sql; @@ -61,6 +63,11 @@ class Helper { $value = $data[$key] ?? null; $title = $rules['title'] ?? $key; + //TODO: fix this, skip arrays for now + if (is_array($value)) { + continue; + } + // Apply default values for missing rules $rules = array_merge([ diff --git a/lib/TTCrud/TTCrud.php b/lib/TTCrud/TTCrud.php index a53f0aa3d..fde700436 100644 --- a/lib/TTCrud/TTCrud.php +++ b/lib/TTCrud/TTCrud.php @@ -23,7 +23,12 @@ class TTCrud extends mfBaseController { $this->user = $me; $this->layout()->set('me', $me); - if (!$me->is(["Admin"])) { + if (method_exists($this, 'permissionCheck')) { + $allowed = $this->permissionCheck(); + if (!$allowed) { + $this->redirect("Dashboard"); + } + } else if (!$me->is(["Admin"])) { $this->redirect("Dashboard"); } @@ -118,7 +123,7 @@ class TTCrud extends mfBaseController { $id = $this->model::create($this->postData); if (method_exists($this, 'afterCreate')) { - $this->afterCreate($this->postData); + $this->afterCreate(array_merge($this->postData, ['id' => $id])); } self::returnJson(['success' => true, diff --git a/lib/TTCrudBaseModel/TTCrudBaseModel.php b/lib/TTCrudBaseModel/TTCrudBaseModel.php index 9f7039f32..c296fd258 100644 --- a/lib/TTCrudBaseModel/TTCrudBaseModel.php +++ b/lib/TTCrudBaseModel/TTCrudBaseModel.php @@ -68,12 +68,17 @@ class TTCrudBaseModel { } - public static function get($id): TTCrudBaseModel { + public static function get($id, $die= false): TTCrudBaseModel { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $id = $db->real_escape_string($id); $table = self::getTable(); $sql = "SELECT * FROM `$table` WHERE `id` = $id"; + + if($die) { + die($sql); + } + $result = $db->query($sql); // as TTCRudBaseModel is abstract, we need to get the class name of the child class $class = get_called_class(); @@ -148,6 +153,13 @@ class TTCrudBaseModel { if ($field === "id") { continue; } + // TODO: make this cleaner + if ($value === "" && (new ReflectionProperty(get_called_class(), $field))->getType()->getName() === "float") { + $value = null; + } + if ($value === "" && (new ReflectionProperty(get_called_class(), $field))->getType()->getName() === "int") { + $value = null; + } $values[] = $value === null ? "`$field` = NULL" : "`$field` = '" . $db->real_escape_string($value) . "'"; } diff --git a/public/js/pages/IpNetwork/IpNetwork.js b/public/js/pages/IpNetwork/IpNetwork.js index cd9ec1205..dee1a0f00 100644 --- a/public/js/pages/IpNetwork/IpNetwork.js +++ b/public/js/pages/IpNetwork/IpNetwork.js @@ -17,7 +17,7 @@ Vue.component('IpNetwork', { Go Back - @@ -28,6 +28,13 @@ Vue.component('IpNetwork', { No description + + @@ -47,11 +54,12 @@ Vue.component('IpNetwork', {
    - +
    @@ -83,6 +91,7 @@ Vue.component('IpNetwork', {
    @@ -112,6 +121,7 @@ Vue.component('IpNetwork', { {value: 'reserved', text: 'Reserved', icon: 'fas fa-lock text-warning'}] }, {text: 'Children', key: 'children', filter: 'numberRange'}, + {text: 'Aktionen', key: 'actions', sortable: false}, ], tableHeader: 'IPAM', key: 'IpNetwork' @@ -140,6 +150,34 @@ Vue.component('IpNetwork', { }, methods: { + openModal(row = false) { + if (row) { + const data = JSON.parse(JSON.stringify(row)); + + + this.addModalData = { + id: data.id, + network_address: data.network_address_str.split('/')[0], + cidr: data.cidr, + parent_network_id: this.currentNetworkData ? this.currentNetworkData.id : '', + status: data.status, + name: data.name, + description: data.description, + location: data.location, + } + } else { + this.addModalData = { + network_address: '', + cidr: '', + parent_network_id: this.currentNetworkData ? this.currentNetworkData.id : '', + status: 'active', + name: '', + description: '', + location: '', + }; + } + this.addModal = true; + }, async switchCurrentNetwork(networkId = null) { if (!networkId) { this.$refs.table.$set(this.$refs.table.filters, 'parent_network_id', undefined); @@ -159,18 +197,30 @@ Vue.component('IpNetwork', { await this.$refs.table.fetchData(); }, async addSubmit() { - const response = await axios.post(`${this.apiUrl}?do=create`, + const response = await axios.post(`${this.apiUrl}?do=${this.addModalData.id ? 'update' : 'create'}`, { ...this.addModalData, parent_network_id: this.currentNetworkData ? this.currentNetworkData.id : null }); if (response.data.status === 'success') { this.addModal = false; + this.addModalData = {}; window.notify('success', 'Network space created successfully'); await this.$refs.table.fetchData(); } else { window.notify('error', response.data.message); } }, + async deleteSubmit() { + const response = await axios.post(`${this.apiUrl}?do=delete`, {id: this.addModalData.id}); + if (response.data.status === 'success') { + this.addModal = false; + this.addModalData = {}; + window.notify('success', 'Network space deleted successfully'); + await this.$refs.table.fetchData(); + } else { + window.notify('error', response.data.message); + } + } }, }) diff --git a/public/js/pages/WarehouseArticle/WarehouseArticle.js b/public/js/pages/WarehouseArticle/WarehouseArticle.js index 96f2a781f..0d9b6d676 100644 --- a/public/js/pages/WarehouseArticle/WarehouseArticle.js +++ b/public/js/pages/WarehouseArticle/WarehouseArticle.js @@ -37,7 +37,7 @@ Vue.component('warehouse-distributor-modal', { this.rows = response.data.rows }, addRow() { - this.rows.push({distributor: null, price: null, externalArticleNumber: null}); + this.rows.push({distributorId: undefined, price: null, externalArticleNumber: null}); }, async saveRow(index) { // post to /WarehouseArticleDistributor/save with rows data and articleId const row = this.rows[index]; @@ -256,6 +256,12 @@ Vue.component('warehouse-article', { @editPricesEntries="priceModal = true; priceModalId = $event.id" @editThresholdEntries="thresholdModal = true; thresholdModalId = $event.id"> + + diff --git a/public/js/pages/WarehouseArticlePacket/WarehouseArticlePacket.js b/public/js/pages/WarehouseArticlePacket/WarehouseArticlePacket.js new file mode 100644 index 000000000..1ef35a8d7 --- /dev/null +++ b/public/js/pages/WarehouseArticlePacket/WarehouseArticlePacket.js @@ -0,0 +1,90 @@ +Vue.component('WarehouseArticlePacket', { + //language=Vue + template: ` + + + + + + + + + + + `, data() { + return { + window: window, + historyModal: false, + historyModalId: null, + subItemsAutocomplete: '', + articles: [], + updateCounter: 0, + newSubItemAmount: 1, + subItems: [] + } + }, beforeMount() { + this.articles = window['TT_CONFIG']['CRUD_CONFIG'].columns.find(column => column.key === 'subItems').modal.items; + }, methods: { + updateCrudModalData() { + const ref = this.$refs.WarehouseArticlePacketCrud; + ref.$set(ref.crudModalData, 'subItems', JSON.stringify(this.subItems)); + this.updateCounter++; + }, removeSubItem(item) { + this.subItems = this.subItems.filter(subItem => subItem !== item); + this.updateCrudModalData(); + }, addSubItem() { + // only continue if id and amount are set else use window.notify('error', 'Bitte Artikel und Menge auswählen'); + if (this.subItemsAutocomplete === '' || this.newSubItemAmount === '') { + window.notify('error', 'Bitte Artikel und Menge auswählen'); + return; + } + + this.subItems.push({id: this.subItemsAutocomplete, amount: this.newSubItemAmount}); + this.updateCrudModalData(); + this.$refs.subItemsAutocomplete.clear(); + this.newSubItemAmount = 1; + } + }, mounted() { + this.$watch(() => { + return this.$refs.WarehouseArticlePacketCrud.crudModal + }, () => { + // check if this.$refs.WarehouseArticlePacketCrud.crudModalData.subItems is defined + if (typeof this.$refs.WarehouseArticlePacketCrud.crudModalData.subItems === 'undefined') { + this.$set(this.$refs.WarehouseArticlePacketCrud.crudModalData, 'subItems', JSON.stringify([])); + } + this.subItems = JSON.parse(this.$refs.WarehouseArticlePacketCrud.crudModalData.subItems); + }) + + } +}) diff --git a/public/js/pages/WarehouseEShop/WarehouseEShop.js b/public/js/pages/WarehouseEShop/WarehouseEShop.js index e27989598..2b43f7568 100644 --- a/public/js/pages/WarehouseEShop/WarehouseEShop.js +++ b/public/js/pages/WarehouseEShop/WarehouseEShop.js @@ -25,9 +25,12 @@ Vue.component('tt-expandable-shopping-cart', {

    Einkaufswagen

    @@ -52,7 +55,7 @@ Vue.component('warehouse-e-shop', { @@ -63,13 +66,18 @@ Vue.component('warehouse-e-shop', { + + @@ -110,12 +118,16 @@ Vue.component('warehouse-e-shop', { 'Ein Fehler ist aufgetreten'); } }, - addToCart(row) { + addToCart(row, type) { + row = JSON.parse(JSON.stringify(row)); + row.id = `${type}-${row.id}`; if (!this.itemAmounts[row.id] || this.itemAmounts[row.id] === 0) { window.notify('error', 'Bitte geben Sie eine Menge ein.'); return; } + console.log(this.shoppingCart, row) + // Check if Article is already in the shopping cart if (this.shoppingCart.some(item => item.itemId === row.id)) { window.notify('warning', `${row.title} ist bereits im Warenkorb.`); diff --git a/public/plugins/vue/tt-components/css/tt-table.css b/public/plugins/vue/tt-components/css/tt-table.css index 76d83e178..a5f2cfe63 100644 --- a/public/plugins/vue/tt-components/css/tt-table.css +++ b/public/plugins/vue/tt-components/css/tt-table.css @@ -323,8 +323,8 @@ td { .tt-expandable-shopping-cart.expanded .cart-count { position: unset !important; - width: 21px; - height: 21px; + width: 18px; + height: 18px; grid-column-start: 3; } diff --git a/public/plugins/vue/tt-components/tt-autocomplete.js b/public/plugins/vue/tt-components/tt-autocomplete.js index 84712f01c..9cd962e4f 100644 --- a/public/plugins/vue/tt-components/tt-autocomplete.js +++ b/public/plugins/vue/tt-components/tt-autocomplete.js @@ -11,17 +11,26 @@ Vue.component('tt-autocomplete', { class="form-control" :class="{'form-control-sm': sm}" v-model="displayValue" + :placeholder="placeholder" @input="onInput" @focus="onFocus" @blur="onBlur" :style="{'padding-right': $slots.append ? '30px' : '0'}" /> +