Needs work
Project:
User Points
Version:
7.x-2.x-dev
Component:
Code: userpoints
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Reporter:
Created:
23 Aug 2011 at 23:27 UTC
Updated:
26 Mar 2012 at 18:14 UTC
Jump to comment: Most recent file
Comments
Comment #1
BenK commentedSubscribing
Comment #2
guybrush commentedI think the isReadOnly() function in userpoints.transaction.inc needs to be more flexible to meet the differing requirements of various users of this module.
As it currently stands fields are disabled except for new or pending transactions.
What if the functionality here was controllable by a setting in userpoints_admin_settings(), so that editability is configurable?
The same argument could be made for the ability to delete transactions - which is clearly required by some people, but definitely not required by others
Comment #3
berdirThe thing is that I can't guarantee that changing transactions will correctly update the total.
I tried pretty hard in 7.x-1.x and it's working fairly well, but stuff like changing the status and the category of a transaction at the same time can get very complicated to account for and is hard to maintain. In 6.x-1.x, it just doesn't work at all. Changing a transaction after it has been approved doesn't have an effect on the total at all.
Userpoints is quite often used as a virtual currency (And will likely be used a lot more for this, as I'm currently working hard on http://drupal.org/project/commerce_userpoints to get it where I'd like it to), where it is IMHO extremely important that it is always crystal clear to users what happened with their money. And if the different transactions don't result in the actual, current total and show who did what when with the points of a user, things can get really messy.
Sure, you could tell these sites to disable that option, but I'd still have to maintain, test and fix bugs with all the code that tries to update the total.
There are some open issues with making them read only that I'm aware of such as :
- Sometimes you messed up with a transaction, e.g. granting a user 200 instead of 20 points or granting them to the wrong user etc and you don't want to show that to the user. One approach for this would be the ability to undo transactions, by creating a transaction that reverts the points amount and somehow hides the both from the user, making the only visible for administrators (e.g. users with a special permission).
- Active sites with many transactions will have a huge userpoints_txn table, I know of sites in 6.x which regularly delete old transactions. I'm not yet sure how to solve this properly, there is an open issue about this. Some way to archive old transactions including a configuration option what exactly "old" is.
What's your use case?
Comment #4
guybrush commentedMy use case is the first of the 2 you mentioned: 'e.g. granting a user 200 instead of 20 points'. In fact, currently I'm having to get around this by posting a negative transaction:
userpoints_grant_points('xxx', -$data->points, $uid)
...
->setDescription('Cancelled transaction')
->save();
and then deleting both transactions:
db_delete('userpoints_txn')
->...
Not very neat - but it does ensure the totals are correct.
In other situations, I'm also needing to use userpoints as a virtual currency, so would never want to take the above approach, but would choose to stick with just the negative transaction approach.
Comment #5
berdirYes, I think an undo that works by adding a second transaction and allows to hide them is the way to go.
Possibly requires a new column in the transaction table, maybe a hidden column, which can be 0 or 1.
You're welcome to work on that.
Comment #6
guybrush commentedYes, I'm happy to do a patch for this. This solution will mean that anywhere lists of transactions are displayed, these lists will need to exclude hidden transactions. That's fine for the userpoints module itself, but could cause confusion for people writing their own views, for example.
Maybe a mandatory entry in the reason field for such hidden transactions can help mitigate against this.
Comment #7
guybrush commentedThis patch adds the following:
1. Adds a 'hidden' field to the userpoints_txn table
2. Adds a function userpoints_transaction_delete() that can be called to hide an existing transaction, and cancel out its effect by adding a new negative transaction for the same amount of points
3. References a variable 'userpoints_report_displayhidden' via a new constant USERPOINTS_REPORT_DISPLAYHIDDEN, which defaults to FALSE, when displaying transactions. This means that, by default, hidden transactions do not appear in transaction listings displayed by userpoints_list_transactions(). Set this variable to TRUE will make them appear
Comment #8
berdirThanks for working on this, review below, mostly coding style stuff.
Looks like there is a tab here instead of two spaces.
git diff needs a trailing space at the end of a file, not having one makes it very unhappy.
Should be DeleteS and there should be an empty line after the first line in a docblock. And finally, the last line here is over 80 characters.
I don't think there is a reason to return TRUE/FALSE here. The only thing that could go wrong is an error when trying to save and this would result in an exception anyway.
More tabs. There should be no need to use a wrapper here, just use $transaction->setHidden(TRUE) and then $transaction->save().
I'm not sure if we need this variable. The long-term goal is to replace all our custom listings with views and once we have that, you could simply alter those views if you want to show hidden transactions. Additionally, you could do it selectively only for the admin listing for example.
Yay tests :)
Trailing spaces on the empty lines.
This looks like a copy & paste of another docblock, needs to be updated. Also more tabs and trailing spaces :) And last, you should als define the hidden property explicitly on the class, like the other stuff.
Comment #9
guybrush commentedI've made the changes suggested above, and will submit a patch soon. I have one issue with the test case, though. For some reason, if I don't use the entity wrapper approach, the code works in real cases, but the test case fails as follows:
Current points for tid 0 are correct (expected: 3, actual: 7). Other userpoints.test 70 UserpointsBaseTestCase->verifyPoints() Fail
Max points for tid 0 are correct (expected: 7, actual: 11). Other userpoints.test 76 UserpointsBaseTestCase->verifyPoints()
I can't figure out why the test case would work for entity wrapper, but not ->setHidden(). Any ideas? If I just removed the test case, everything would be fine...but test cases are good!
Comment #10
guybrush commentedI've applied the changes mentioned above for this new patch. I did have to remove the test case, as I still can't work out why it was failing, when the actual code was working. For reference, here is the test case I removed:
/**
* Test basic usage of the API to delete transactions.
*/
function testDeletePoints() {
// Most basic usage, with automated saving.
$transaction = userpoints_grant_points('test', 3, $this->non_admin_user->uid);
$transaction->save();
$this->verifyPoints($this->non_admin_user->uid, 3, 3);
// Add points, and store the transaction
$transaction = userpoints_grant_points('test', 4, $this->non_admin_user->uid);
$transaction->save();
$this->verifyPoints($this->non_admin_user->uid, 7, 7);
// Verify that deleting a transaction reduces the points total
userpoints_transaction_delete($transaction);
$this->verifyPoints($this->non_admin_user->uid, 3, 7);
}
Comment #12
guybrush commentedAnd again...with Unix line endings!
Comment #13
guybrush commentedComment #14
berdirSome more code style issues.
Hint: Both git diff on the console (just git diff, no > some_file.patch) and http://drupal.org/project/dreditor can highlight these for you as well.
Some trailing spaces here.
And tabs here.
More trailing spaces :)
Comment #15
guybrush commentedThanks for the advice...fairly new to patching. Will fix when I return to the UK
Comment #16
guybrush commentedHopefully this resolves all the styling issues
Comment #17
berdirClose :)
Don't worry, it takes some time to get used to the strict coding standards used in Drupal. I recommend getting used to them in your own code as well, takes a while butt once you're used to it, you don't even have to think about formatting a comment line correctly, it even hurts if it doesn't ;) (I'm not saying that you need to document all your custom code, that's up to you, just when you do, follow the coding standard :))
Looks like this could be shortened to something like "Instead of actually deleting the transaction, the point amount is reverted using a second transaction and both are hidden."
Note: Remember that you can "delete" a transaction with a negative amount as well, and a negative negative amount is positive, so try to avoid using stuff like reduce/negative transaction.
Also, while not going over 80 characters, try to use as much of those 80 as possible, that is easier to read than lines which are splitted at 40 or so.
Wondering about naming the function differently, maybe _hide() or _revert(), but I'm not sure. Just wondering if it's confusing to have a function named delete and with a docblock that says delete and only in the second sentence we actually mentioned that we don't really delete. Even if it might be a delete in the user interface, we might not want to name it that in the API.
Comments should end with a . (same for those below), the indentation of the second one looks off and also seems to be too long.
Comment #18
guybrush commentedAs discussed, changed userpoints_transaction_delete() to userpoints_transaction_revert(), and corrected some of the issues from #17 above
Comment #19
berdirSorry for finding new stuff all the time, the method calls (->something()) should be indented two spaces.
What would be great here would be a basic test. You can probably just add a testRevert() method to the API test class, and do something like this:
- Create a user.
- Create 2-3 transactions for that user.
- Revert one of them.
- Make sure that the total amount of points for the user is correct.
- (Visit a page that lists userpoints for that user and make sure the reverted one doesn't show up. You should be able to do so be adding a custom operation for each transaction) and then make sure that those are displayed who should be and the reverted one isn't).
The last point is probably a bit more complicated as it's more than just API calls (you need to log in, view pages and so on) but would be great to have.