commit 6155a9e3389933653f711a3ba141647a7eb7bd21
parent 804fb81e618d34f4970b279d70152319d2afe372
Author: Felicitus <felicitus@felicitus.org>
Date: Tue, 28 Jun 2011 15:51:01 +0200
Added editing of the price+amounts for stock entries
Diffstat:
5 files changed, 147 insertions(+), 22 deletions(-)
diff --git a/frontend/js/Components/Part/PartStockHistory.js b/frontend/js/Components/Part/PartStockHistory.js
@@ -17,11 +17,19 @@ Ext.define('PartKeepr.PartStockHistory', {
},
width: 20
},
- {header: i18n("User"), dataIndex: 'username', width: 80 },
- {header: i18n("Amount"), dataIndex: 'amount', width: 50},
+ {header: i18n("User"), dataIndex: 'username', flex: 0.4, minWidth: 80 },
+ {header: i18n("Amount"), dataIndex: 'amount', width: 50,
+ editor: {
+ xtype:'numberfield',
+ allowBlank:false
+ }},
{header: i18n("Date"), dataIndex: 'datetime', width: 120},
{
header: i18n("Price"),
+ editor: {
+ xtype:'numberfield',
+ allowBlank:false
+ },
dataIndex: 'price',
width: 60,
renderer: function (val, p, rec) {
@@ -34,10 +42,13 @@ Ext.define('PartKeepr.PartStockHistory', {
}
],
model: 'PartKeepr.StockEntry',
+ /**
+ * Initializes the stock history grid.
+ */
initComponent: function () {
var config = {
autoLoad: false,
- autoSync: false, // Do not change. If true, new (empty) records would be immediately commited to the database.
+ autoSync: true,
remoteFilter: false,
remoteSort: false,
model: 'PartKeepr.StockEntry',
@@ -45,9 +56,50 @@ Ext.define('PartKeepr.PartStockHistory', {
this.store = Ext.create('Ext.data.Store', config);
+ this.editing = Ext.create('Ext.grid.plugin.CellEditing', {
+ clicksToEdit: 1
+ });
+
+ this.plugins = [ this.editing ];
+
+ this.editing.on("beforeedit", this.onBeforeEdit, this);
+
this.on("activate", this.onActivate, this);
this.callParent();
},
+ /**
+ * Called before editing a cell. Checks if the user may actually make the requested changes.
+ *
+ * @param e Passed from ExtJS
+ * @returns {Boolean}
+ */
+ onBeforeEdit: function (e) {
+
+ // Checks if the usernames match
+ var sameUser = e.record.get("username") == PartKeepr.getApplication().getUsername();
+
+ switch (e.field) {
+ case "price":
+ // Check the direction is "out". If yes, editing the price field is not allowed
+ if (e.record.get("direction") == "out") {
+ return false;
+ }
+
+ // If it's not the same user or an admin, editing is not allowed
+ if ( !sameUser && !PartKeepr.getApplication().isAdmin()) {
+ return false;
+ }
+ break;
+ case "amount":
+ // Only an admin may edit the amount. Regular users must put the stock back in manually.
+ if (!PartKeepr.getApplication().isAdmin()) {
+ return false;
+ }
+ }
+ },
+ /**
+ * Called when the view is activated.
+ */
onActivate: function () {
var proxy = this.store.getProxy();
proxy.extraParams.item = this.part;
diff --git a/frontend/js/Dialogs/Auth/LoginDialog.js b/frontend/js/Dialogs/Auth/LoginDialog.js
@@ -74,19 +74,33 @@ Ext.define('PartKeepr.LoginDialog', {
call.setHandler(Ext.bind(this.onLogin, this));
call.doCall();
},
+ /**
+ * Callback after the login call was completed.
+ *
+ * @param obj The response object from the server
+ */
onLogin: function (obj) {
+ // Set session + username
PartKeepr.getApplication().setSession(obj.sessionid);
PartKeepr.getApplication().setUsername(obj.username);
+ // @todo Disable the "edit users" menu somehow else
if (!obj.admin) {
Ext.getCmp("edit-users").hide();
} else {
Ext.getCmp("edit-users").show();
}
+ // Set the admin flag
+ PartKeepr.getApplication().setAdmin(obj.admin);
+
+ // Call the "login" method, which initializes the system with the user
PartKeepr.getApplication().login();
+ // Write out a logging message
PartKeepr.log(i18n("Logged in as")+" "+obj.username);
+
+ // Close the window
this.close();
}
diff --git a/frontend/js/PartKeepr.js b/frontend/js/PartKeepr.js
@@ -88,6 +88,12 @@ Ext.application({
autoLoad: false
});
},
+ setAdmin: function (admin) {
+ this.admin = admin;
+ },
+ isAdmin: function () {
+ return this.admin;
+ },
getUnitStore: function () {
return this.unitStore;
},
@@ -249,10 +255,23 @@ Ext.application({
}
},
+ /**
+ * Sets the username. This should only be called from the login dialog.
+ *
+ * Also updates the statusbar to reflect the username.
+ *
+ * @param {string} username The username to set
+ */
setUsername: function (username) {
this.username = username;
-
this.getStatusbar().setCurrentUser(username);
+ },
+ /**
+ * Returns the current username
+ * @returns {string}
+ */
+ getUsername: function () {
+ return this.username;
}
});
diff --git a/src/de/RaumZeitLabor/PartKeepr/Stock/StockEntry.php b/src/de/RaumZeitLabor/PartKeepr/Stock/StockEntry.php
@@ -5,17 +5,11 @@ declare(encoding = 'UTF-8');
use de\RaumZeitLabor\PartKeepr\Part\Part;
use de\RaumZeitLabor\PartKeepr\User\User;
use de\RaumZeitLabor\PartKeepr\PartKeepr;
+use de\RaumZeitLabor\PartKeepr\Util\BaseEntity;
+use de\RaumZeitLabor\PartKeepr\Util\Serializable;
/** @Entity @HasLifecycleCallbacks **/
-class StockEntry {
-
- /**
- * @Id @Column(type="integer")
- * @GeneratedValue(strategy="AUTO")
- * @var int
- */
- private $id;
-
+class StockEntry extends BaseEntity implements Serializable {
/**
* @Column(type="integer")
*/
@@ -163,14 +157,6 @@ class StockEntry {
}
/**
- * Returns the ID for this entity.
- * @return int The ID
- */
- public function getId () {
- return $this->id;
- }
-
- /**
* If the stock level is negative, we can't have a price.
* @PrePersist
*/
@@ -189,6 +175,32 @@ class StockEntry {
$this->updatePrice();
}
+ /**
+ * Returns if the current stock entry is a removal.
+ * @return boolean True if the entry is a removal, false otherwise
+ */
+ public function isRemoval () {
+ if ($this->getStockLevel() < 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see de\RaumZeitLabor\PartKeepr\Util.Serializable::serialize()
+ */
+ public function serialize () {
+ return array(
+ "id" => $this->getId(),
+ "username" => is_object($this->getUser()) ? $this->getUser()->getUsername() : PartKeepr::i18n("Unknown User"),
+ "amount" => abs($this->getStockLevel()),
+ "datetime" => $this->getDateTime()->format("Y-m-d H:i:s"),
+ "direction" => ($this->getStockLevel() < 0) ? "out" : "in",
+ "price" => $this->getPrice()
+ );
+ }
public function updateStockLevel () {
$query = PartKeepr::getEM()->createQuery("SELECT SUM(se.stockLevel) FROM de\RaumZeitLabor\PartKeepr\Stock\StockEntry se WHERE se.part = :part");
diff --git a/src/de/RaumZeitLabor/PartKeepr/Stock/StockService.php b/src/de/RaumZeitLabor/PartKeepr/Stock/StockService.php
@@ -5,6 +5,7 @@ declare(encoding = 'UTF-8');
use de\RaumZeitLabor\PartKeepr\Stock\StockEntry,
de\RaumZeitLabor\PartKeepr\PartKeepr,
+ de\RaumZeitLabor\PartKeepr\Session\SessionManager,
de\RaumZeitLabor\PartKeepr\Service\RestfulService,
de\RaumZeitLabor\PartKeepr\Service\Service;;
@@ -41,8 +42,35 @@ class StockService extends Service implements RestfulService {
throw new \Exception("Not yet implemented");
}
+ /**
+ * (non-PHPdoc)
+ * @see de\RaumZeitLabor\PartKeepr\Service.RestfulService::update()
+ */
public function update () {
- throw new \Exception("Not yet implemented");
+ $this->requireParameter("id");
+
+ $stockEntry = StockEntry::loadById($this->getParameter("id"));
+
+ if (!SessionManager::getCurrentSession()->getUser()->isAdmin() ||
+ (SessionManager::getCurrentSession()->getUser() && $stockEntry->getUser() && SessionManager::getCurrentSession()->getUser()->getId() != $stockEntry->getUser()->getId() )) {
+ throw new \Exception("Permission denied");
+ }
+
+ /* It's not allowed to edit a price for a removal */
+ if (!$stockEntry->isRemoval()) {
+ $stockEntry->setPrice(abs($this->getParameter("price")));
+ }
+
+ /**
+ * Only an admin user may correct the in&out stock levels
+ */
+ if (SessionManager::getCurrentSession()->getUser()->isAdmin()) {
+ $stockEntry->setStockLevel($this->getParameter("amount"));
+ }
+
+ PartKeepr::getEM()->flush();
+
+ return array("data" => $stockEntry->serialize());
}
public function destroy () {