Created
May 20, 2013 07:23
-
-
Save ruiwen/5610841 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
User.prototype.recordTransaction = function(txn) { | |
// Abort if transaction is already processed | |
if(txn.processed) { return; } | |
// Find out which role the User plays in this transaction | |
// and assume friend is the other | |
var role = txn.role(this); | |
var f; | |
if(role == Activity.CREDITOR) { | |
f = txn.debtor(); | |
} | |
else if(role == Activity.DEBTOR) { | |
f = txn.creditor(); | |
} | |
// Initialise .friends and .friendIndex if they haven't been | |
// <snip>Record friends and Transactions</snip> | |
// Process amount only if transaction has not been voided | |
var friend = this.friends[f.objectId]; // Convenience variable | |
if(!txn['void']) { | |
// Record the origin | |
var origin = friend.balance; | |
// Calculate friend balance | |
if(role == Activity.CREDITOR) { | |
friend.balance += txn.amount; | |
} | |
else if(role == Activity.DEBTOR) { | |
friend.balance -= txn.amount; | |
} | |
friend.balance = parseFloat(friend.balance.toFixed(2)); | |
// Calculate sum totals | |
// Algorithm as follows: | |
// Sum total (sumFriendsOwe/sumIOwe) is the cumulative representation of debt/credit | |
// across all a User's friends. It is wrong to treat sumFriendsOwe/sumIOwe the same | |
// way as a single friend's balance, ie. one friend's credit does not cancel out | |
// another's debt. Each friend's credit/debit must be taken into account individually | |
// first, via the '.balance' field, then its effect factored into the sum totals. | |
// | |
// (1) In calculating the sum total, we first look at the current friend's balance | |
// and if the transaction is a credit or debit against him. | |
// If the balance is in credit, ie. positive, and the transaction is a credit, | |
// then we simply add the transaction amount towards the sum total, sumFriendsOwe. | |
// The same applies in the opposite direction, ie. a debit against a negative balance | |
// is simply added to sumIOwe. | |
// | |
// (2) The tricky bit arises when we cross zero, ie. a debt becomes credit and vice versa | |
// When crossing zero, if the transaction amount is larger than the existing balance, | |
// we need to first eliminate the balance on one side, then add the remainder to the other. | |
// | |
// Eg. | |
// Transaction type: Credit | |
// Transaction amount: 20 | |
// Balance: -10 | |
// | |
// (A) Eliminate the balance: sumIOwe -= 10 (making balance zero) | |
// (B) Add the remainder to the other side: sumFriendsOwe += (20-10) (making balance +10) | |
// | |
if(role == Activity.CREDITOR) { | |
if(origin >= 0) { // See above: (1) | |
this.sumFriendsOwe += txn.amount; | |
} | |
else if(origin < 0) { | |
// Here we take into account the case where the difference provided by | |
// txn.amount will cause a shift from credit to debt or vice versa. | |
// We first eliminate excess credit/debt, then add the remainder to the | |
// other side. | |
if(txn.amount > Math.abs(origin)) { // See above: (2) | |
this.sumFriendsOwe += txn.amount - Math.abs(origin); | |
this.sumIOwe -= Math.abs(origin); | |
} | |
else { | |
this.sumIOwe -= txn.amount; | |
} | |
} | |
} | |
else if(role == Activity.DEBTOR) { | |
if(origin <= 0) { // See above: (1) | |
this.sumIOwe += txn.amount; | |
} | |
else if(origin > 0) { | |
// Here we take into account the case where the difference provided by | |
// txn.amount will cause a shift from credit to debt or vice versa. | |
// We first eliminate excess credit/debt, then add the remainder to the | |
// other side. | |
if(txn.amount > origin) { // See above: (2) | |
this.sumFriendsOwe -= origin; | |
this.sumIOwe += txn.amount - Math.abs(origin); | |
} | |
else { | |
this.sumFriendsOwe -= txn.amount; | |
} | |
} | |
} | |
} | |
// Re-jigger the balances | |
friend.balance = Math.round(friend.balance * 100) / 100; | |
this.sumIOwe = Math.round(this.sumIOwe * 100) / 100; | |
this.sumFriendsOwe = Math.round(this.sumFriendsOwe * 100) / 100; | |
// All done? Mark the transaction as processed | |
txn.processed = true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ActivityTypes | |
Transaction.I_OWE = 0; | |
Transaction.OWES_ME = 1; | |
Transaction.I_PAID = 2; | |
Transaction.PAID_ME = 3; | |
Transaction.VOID = 4; | |
Transaction.WELCOME = 5; | |
// Transaction types as strings | |
Transaction.types = ["I owe", "owes me", "I paid", "paid me"]; | |
// Transaction Roles | |
Transaction.DEBTOR = 16; | |
Transaction.CREDITOR = 32; | |
Transaction.prototype.role = function(user) { | |
// Now we expect that Activity is a Parse object, but this | |
// may not always be true | |
// Is user the sender or receiver and what is his role? | |
var arr = [this.sender, this.receiver]; | |
for(var i in arr) { | |
var s = arr[i]; // Convenience variable | |
if(s.email == user.email) { | |
if(s.email == this.debtor().email) { | |
return Activity.DEBTOR; | |
} | |
else if(s.email == this.creditor().email) { | |
return Activity.CREDITOR | |
} | |
} | |
else { continue; } // No .user no cry | |
} | |
} | |
Transaction.prototype.creditor = function() { | |
var res = {}; | |
if(this.type == Activity.I_OWE) { | |
res = this.receiver; | |
} | |
else if(this.type == Activity.OWES_ME) { | |
res = this.sender; | |
} | |
else if (this.type == Activity.I_PAID) { | |
res = this.sender; | |
} | |
else if(this.type == Activity.PAID_ME) { | |
res = this.receiver; | |
} | |
return angular.copy(res, {}); // Return a copy | |
} | |
Transaction.prototype.debtor = function() { | |
var res = {}; | |
if(this.type == Activity.I_OWE) { | |
res = this.sender; | |
} | |
else if(this.type == Activity.OWES_ME) { | |
res = this.receiver; | |
} | |
else if (this.type == Activity.I_PAID) { | |
res = this.receiver; | |
} | |
else if(this.type == Activity.PAID_ME) { | |
res = this.sender; | |
} | |
return angular.copy(res, {}); // Return a copy | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment