Skip to content

Instantly share code, notes, and snippets.

@ruiwen
Created May 20, 2013 07:23
Show Gist options
  • Save ruiwen/5610841 to your computer and use it in GitHub Desktop.
Save ruiwen/5610841 to your computer and use it in GitHub Desktop.
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;
}
// 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