mirror of
				https://github.com/aclindsa/moneygo.git
				synced 2025-10-30 01:23:26 -04:00 
			
		
		
		
	Add basic transaction register support
This commit is contained in:
		| @@ -31,7 +31,7 @@ type Account struct { | ||||
| 	// monotonically-increasing account transaction version number. Used for | ||||
| 	// allowing a client to ensure they have a consistent version when paging | ||||
| 	// through transactions. | ||||
| 	Version int64 | ||||
| 	AccountVersion int64 `json:"Version"` | ||||
| } | ||||
|  | ||||
| type AccountList struct { | ||||
| @@ -127,7 +127,7 @@ func insertUpdateAccount(a *Account, insert bool) error { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		a.Version = oldacct.Version + 1 | ||||
| 		a.AccountVersion = oldacct.AccountVersion + 1 | ||||
|  | ||||
| 		count, err := transaction.Update(a) | ||||
| 		if err != nil { | ||||
| @@ -227,7 +227,7 @@ func AccountHandler(w http.ResponseWriter, r *http.Request) { | ||||
| 		} | ||||
| 		account.AccountId = -1 | ||||
| 		account.UserId = user.UserId | ||||
| 		account.Version = 0 | ||||
| 		account.AccountVersion = 0 | ||||
|  | ||||
| 		if GetSecurity(account.SecurityId) == nil { | ||||
| 			WriteError(w, 3 /*Invalid Request*/) | ||||
|   | ||||
							
								
								
									
										489
									
								
								static/account_register.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										489
									
								
								static/account_register.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,489 @@ | ||||
| // Import all the objects we want to use from ReactBootstrap | ||||
|  | ||||
| var Modal = ReactBootstrap.Modal; | ||||
|  | ||||
| var Label = ReactBootstrap.Label; | ||||
| var Table = ReactBootstrap.Table; | ||||
| var Grid = ReactBootstrap.Grid; | ||||
| var Row = ReactBootstrap.Row; | ||||
| var Col = ReactBootstrap.Col; | ||||
|  | ||||
| var DateTimePicker = ReactWidgets.DateTimePicker; | ||||
|  | ||||
| const TransactionRow = React.createClass({ | ||||
| 	handleClick: function(e) { | ||||
| 		const refs = ["date", "number", "description", "account", "status", "amount"]; | ||||
| 		for (var ref in refs) { | ||||
| 			if (this.refs[refs[ref]].getDOMNode() == e.target) { | ||||
| 				this.props.onEdit(this.props.transaction, refs[ref]); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 	render: function() { | ||||
| 		var date = this.props.transaction.Date; | ||||
| 		var dateString = date.getFullYear() + "/" + (date.getMonth()+1) + "/" + date.getDate(); | ||||
| 		var number = "" | ||||
| 		var accountName = ""; | ||||
| 		var status = ""; | ||||
| 		var security = this.props.security_map[this.props.account.SecurityId]; | ||||
|  | ||||
| 		if (this.props.transaction.isTransaction()) { | ||||
| 			var thisAccountSplit; | ||||
| 			for (var i = 0; i < this.props.transaction.Splits.length; i++) { | ||||
| 				if (this.props.transaction.Splits[i].AccountId == this.props.account.AccountId) { | ||||
| 					thisAccountSplit = this.props.transaction.Splits[i]; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (this.props.transaction.Splits.length == 2) { | ||||
| 				var otherSplit = this.props.transaction.Splits[0]; | ||||
| 				if (otherSplit.AccountId == this.props.account.AccountId) | ||||
| 					var otherSplit = this.props.transaction.Splits[1]; | ||||
| 				var accountName = getAccountDisplayName(this.props.account_map[otherSplit.AccountId], this.props.account_map); | ||||
| 			} else { | ||||
| 				accountName = "--Split Transaction--"; | ||||
| 			} | ||||
|  | ||||
| 			var amount = "$" + thisAccountSplit.Amount.toFixed(security.Precision); | ||||
| 			status = TransactionStatusMap[this.props.transaction.Status]; | ||||
| 			number = thisAccountSplit.Number; | ||||
| 		} else { | ||||
| 			var amount = "$" + (new Big(0.0)).toFixed(security.Precision); | ||||
| 		} | ||||
|  | ||||
| 		return ( | ||||
| 			<tr> | ||||
| 				<td ref="date" onClick={this.handleClick}>{dateString}</td> | ||||
| 				<td ref="number" onClick={this.handleClick}>{number}</td> | ||||
| 				<td ref="description" onClick={this.handleClick}>{this.props.transaction.Description}</td> | ||||
| 				<td ref="account" onClick={this.handleClick}>{accountName}</td> | ||||
| 				<td ref="status" onClick={this.handleClick}>{status}</td> | ||||
| 				<td ref="amount" onClick={this.handleClick}>{amount}</td> | ||||
| 				<td>$??.??</td> | ||||
| 			</tr>); | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| const AddEditTransactionModal = React.createClass({ | ||||
| 	_getInitialState: function(props) { | ||||
| 		// Ensure we can edit this without screwing up other copies of it | ||||
| 		var t = props.transaction.deepCopy(); | ||||
| 		return {transaction: t}; | ||||
| 	}, | ||||
| 	getInitialState: function() { | ||||
| 		 return this._getInitialState(this.props); | ||||
| 	}, | ||||
| 	handleCancel: function() { | ||||
| 		if (this.props.onCancel != null) | ||||
| 			this.props.onCancel(); | ||||
| 	}, | ||||
| 	handleDescriptionChange: function() { | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Description = this.refs.description.getValue(); | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleDateChange: function(date, string) { | ||||
| 		if (date == null) | ||||
| 			return; | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Date = date; | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleStatusChange: function(status) { | ||||
| 		if (status.hasOwnProperty('StatusId')) { | ||||
| 			var transaction = this.state.transaction.deepCopy(); | ||||
| 			transaction.Status = status.StatusId; | ||||
| 			this.setState({ | ||||
| 				transaction: transaction | ||||
| 			}); | ||||
| 		} | ||||
| 	}, | ||||
| 	handleDeleteSplit: function(split) { | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Splits.splice(split, 1); | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleUpdateNumber: function(split) { | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Splits[split].Number = this.refs['number-'+split].getValue(); | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleUpdateMemo: function(split) { | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Splits[split].Memo = this.refs['memo-'+split].getValue(); | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleUpdateAccount: function(account, split) { | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Splits[split].AccountId = account.AccountId; | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleUpdateAmount: function(split) { | ||||
| 		var transaction = this.state.transaction.deepCopy(); | ||||
| 		transaction.Splits[split].Amount = new Big(this.refs['amount-'+split].getValue()); | ||||
| 		this.setState({ | ||||
| 			transaction: transaction | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleSubmit: function() { | ||||
| 		if (this.props.onSubmit != null) | ||||
| 			this.props.onSubmit(this.state.transaction); | ||||
| 	}, | ||||
| 	handleDelete: function() { | ||||
| 		if (this.props.onDelete != null) | ||||
| 			this.props.onDelete(this.state.transaction); | ||||
| 	}, | ||||
| 	componentWillReceiveProps: function(nextProps) { | ||||
| 		if (nextProps.show && !this.props.show) { | ||||
| 			this.setState(this._getInitialState(nextProps)); | ||||
| 		} | ||||
| 	}, | ||||
| 	render: function() { | ||||
| 		var editing = this.props.transaction != null && this.props.transaction.isTransaction(); | ||||
| 		var headerText = editing ? "Edit" : "Create New"; | ||||
| 		var buttonText = editing ? "Save Changes" : "Create Transaction"; | ||||
| 		var deleteButton = []; | ||||
| 		if (editing) { | ||||
| 			deleteButton = ( | ||||
| 				<Button onClick={this.handleDelete} bsStyle="danger">Delete Transaction</Button> | ||||
| 		   ); | ||||
| 		} | ||||
|  | ||||
| 		splits = []; | ||||
| 		for (var i = 0; i < this.state.transaction.Splits.length; i++) { | ||||
| 			var self = this; | ||||
| 			var s = this.state.transaction.Splits[i]; | ||||
|  | ||||
| 			// Define all closures for calling split-updating functions | ||||
| 			var deleteSplitFn = (function() { | ||||
| 				var j = i; | ||||
| 				return function() {self.handleDeleteSplit(j);}; | ||||
| 			})(); | ||||
| 			var updateNumberFn = (function() { | ||||
| 				var j = i; | ||||
| 				return function() {self.handleUpdateNumber(j);}; | ||||
| 			})(); | ||||
| 			var updateMemoFn = (function() { | ||||
| 				var j = i; | ||||
| 				return function() {self.handleUpdateMemo(j);}; | ||||
| 			})(); | ||||
| 			var updateAccountFn = (function() { | ||||
| 				var j = i; | ||||
| 				return function(account) {self.handleUpdateAccount(account, j);}; | ||||
| 			})(); | ||||
| 			var updateAmountFn = (function() { | ||||
| 				var j = i; | ||||
| 				return function() {self.handleUpdateAmount(j);}; | ||||
| 			})(); | ||||
|  | ||||
| 			var deleteSplitButton = []; | ||||
| 			if (this.state.transaction.Splits.length > 2) { | ||||
| 				deleteSplitButton = ( | ||||
| 					<Col xs={1}><Button onClick={deleteSplitFn} | ||||
| 							bsStyle="danger"> | ||||
| 							<Glyphicon glyph='trash' /></Button></Col> | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			splits.push(( | ||||
| 				<Row> | ||||
| 				<Col xs={1}><Input | ||||
| 					type="text" | ||||
| 					value={s.Number} | ||||
| 					onChange={updateNumberFn} | ||||
| 					ref={"number-"+i} /></Col> | ||||
| 				<Col xs={5}><Input | ||||
| 					type="text" | ||||
| 					value={s.Memo} | ||||
| 					onChange={updateMemoFn} | ||||
| 					ref={"memo-"+i} /></Col> | ||||
| 				<Col xs={3}><AccountCombobox | ||||
| 					accounts={this.props.accounts} | ||||
| 					account_map={this.props.account_map} | ||||
| 					value={s.AccountId} | ||||
| 					includeRoot={false} | ||||
| 					onSelect={updateAccountFn} | ||||
| 					ref={"account-"+i} /></Col> | ||||
| 				<Col xs={2}><Input type="text" | ||||
| 					value={s.Amount} | ||||
| 					onChange={updateAmountFn} | ||||
| 					ref={"amount-"+i} /></Col> | ||||
| 				{deleteSplitButton} | ||||
| 				</Row> | ||||
| 			)); | ||||
| 		} | ||||
|  | ||||
| 		return ( | ||||
| 			<Modal show={this.props.show} onHide={this.handleCancel} bsSize="large"> | ||||
| 				<Modal.Header closeButton> | ||||
| 					<Modal.Title>{headerText} Transaction</Modal.Title> | ||||
| 				</Modal.Header> | ||||
| 				<Modal.Body> | ||||
| 				<form onSubmit={this.handleSubmit} | ||||
| 						className="form-horizontal"> | ||||
| 					<Input wrapperClassName="wrapper" | ||||
| 						label="Date" | ||||
| 						labelClassName="col-xs-2" | ||||
| 						wrapperClassName="col-xs-10"> | ||||
| 					<DateTimePicker | ||||
| 						time={false} | ||||
| 						defaultValue={this.state.transaction.Date} | ||||
| 						onChange={this.handleDateChange} /> | ||||
| 					</Input> | ||||
| 					<Input type="text" | ||||
| 						label="Description" | ||||
| 						value={this.state.transaction.Description} | ||||
| 						onChange={this.handleDescriptionChange} | ||||
| 						ref="description" | ||||
| 						labelClassName="col-xs-2" | ||||
| 						wrapperClassName="col-xs-10"/> | ||||
| 					<Input wrapperClassName="wrapper" | ||||
| 						label="Status" | ||||
| 						labelClassName="col-xs-2" | ||||
| 						wrapperClassName="col-xs-10"> | ||||
| 					<Combobox | ||||
| 						data={TransactionStatusList} | ||||
| 						valueField='StatusId' | ||||
| 						textField='Name' | ||||
| 						value={this.state.transaction.Status} | ||||
| 						onSelect={this.handleStatusChange} | ||||
| 						ref="status" /> | ||||
| 					</Input> | ||||
| 					<Grid fluid={true}><Row> | ||||
| 					<span className="split-header col-xs-1">#</span> | ||||
| 					<span className="split-header col-xs-5">Memo</span> | ||||
| 					<span className="split-header col-xs-3">Account</span> | ||||
| 					<span className="split-header col-xs-2">Amount</span> | ||||
| 					</Row> | ||||
| 					{splits} | ||||
| 					</Grid> | ||||
| 				</form> | ||||
| 				</Modal.Body> | ||||
| 				<Modal.Footer> | ||||
| 					<ButtonGroup className="pull-right"> | ||||
| 						<Button onClick={this.handleCancel} bsStyle="warning">Cancel</Button> | ||||
| 						{deleteButton} | ||||
| 						<Button onClick={this.handleSubmit} bsStyle="success">{buttonText}</Button> | ||||
| 					</ButtonGroup> | ||||
| 				</Modal.Footer> | ||||
| 			</Modal> | ||||
| 		); | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| const AccountRegister = React.createClass({ | ||||
| 	getInitialState: function() { | ||||
| 		return { | ||||
| 			editingTransaction: false, | ||||
| 			selectedTransaction: new Transaction(), | ||||
| 			transactions: [] | ||||
| 		}; | ||||
| 	}, | ||||
| 	handleEditTransaction: function(transaction, fieldName) { | ||||
| 		//TODO select fieldName first when editing | ||||
| 		this.setState({ | ||||
| 			selectedTransaction: transaction, | ||||
| 			editingTransaction: true | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleEditingCancel: function() { | ||||
| 		this.setState({ | ||||
| 			editingTransaction: false | ||||
| 		}); | ||||
| 	}, | ||||
| 	ajaxError: function(jqXHR, status, error) { | ||||
| 		var e = new Error(); | ||||
| 		e.ErrorId = 5; | ||||
| 		e.ErrorString = "Request Failed: " + status + error; | ||||
| 		this.setState({error: e}); | ||||
| 	}, | ||||
| 	getTransactionPage: function(account, page) { | ||||
| 		$.ajax({ | ||||
| 			type: "GET", | ||||
| 			dataType: "json", | ||||
| 			url: "account/"+account.AccountId+"/transactions?sort=date-desc&limit=50&page="+page, | ||||
| 			success: function(data, status, jqXHR) { | ||||
| 				var e = new Error(); | ||||
| 				var transactions = []; | ||||
| 				e.fromJSON(data); | ||||
| 				if (e.isError()) { | ||||
| 					this.setState({error: e}); | ||||
| 				} else { | ||||
| 					for (var i = 0; i < data.transactions.length; i++) { | ||||
| 						var t = new Transaction(); | ||||
| 						t.fromJSON(data.transactions[i]); | ||||
| 						transactions.push(t); | ||||
| 					} | ||||
| 				} | ||||
| 				var a = new Account(); | ||||
| 				a.fromJSON(data.account); | ||||
|  | ||||
| 				this.setState({transactions: transactions}); | ||||
| 			}.bind(this), | ||||
| 			error: this.ajaxError | ||||
| 		}); | ||||
| 	}, | ||||
| 	onNewTransaction: function() { | ||||
| 		this.getTransactionPage(this.props.selectedAccount, 0); | ||||
| 	}, | ||||
| 	onUpdatedTransaction: function() { | ||||
| 		this.getTransactionPage(this.props.selectedAccount, 0); | ||||
| 	}, | ||||
| 	onDeletedTransaction: function() { | ||||
| 		this.getTransactionPage(this.props.selectedAccount, 0); | ||||
| 	}, | ||||
| 	createNewTransaction: function(transaction) { | ||||
| 		$.ajax({ | ||||
| 			type: "POST", | ||||
| 			dataType: "json", | ||||
| 			url: "transaction/", | ||||
| 			data: {transaction: transaction.toJSON()}, | ||||
| 			success: function(data, status, jqXHR) { | ||||
| 				var e = new Error(); | ||||
| 				e.fromJSON(data); | ||||
| 				if (e.isError()) { | ||||
| 					this.setState({error: e}); | ||||
| 				} else { | ||||
| 					this.onNewTransaction(); | ||||
| 				} | ||||
| 			}.bind(this), | ||||
| 			error: this.ajaxError | ||||
| 		}); | ||||
| 	}, | ||||
| 	updateTransaction: function(transaction) { | ||||
| 		$.ajax({ | ||||
| 			type: "PUT", | ||||
| 			dataType: "json", | ||||
| 			url: "transaction/"+transaction.TransactionId+"/", | ||||
| 			data: {transaction: transaction.toJSON()}, | ||||
| 			success: function(data, status, jqXHR) { | ||||
| 				var e = new Error(); | ||||
| 				e.fromJSON(data); | ||||
| 				if (e.isError()) { | ||||
| 					this.setState({error: e}); | ||||
| 				} else { | ||||
| 					this.onUpdatedTransaction(); | ||||
| 				} | ||||
| 			}.bind(this), | ||||
| 			error: this.ajaxError | ||||
| 		}); | ||||
| 	}, | ||||
| 	deleteTransaction: function(transaction) { | ||||
| 		console.log("handleDeleteTransaction", transaction); | ||||
| 		$.ajax({ | ||||
| 			type: "DELETE", | ||||
| 			dataType: "json", | ||||
| 			url: "transaction/"+transaction.TransactionId+"/", | ||||
| 			success: function(data, status, jqXHR) { | ||||
| 				var e = new Error(); | ||||
| 				e.fromJSON(data); | ||||
| 				if (e.isError()) { | ||||
| 					this.setState({error: e}); | ||||
| 				} else { | ||||
| 					this.onDeletedTransaction(); | ||||
| 				} | ||||
| 			}.bind(this), | ||||
| 			error: this.ajaxError | ||||
| 		}); | ||||
| 	}, | ||||
| 	handleDeleteTransaction: function(transaction) { | ||||
| 		this.setState({ | ||||
| 			editingTransaction: false | ||||
| 		}); | ||||
| 		this.deleteTransaction(transaction); | ||||
| 	}, | ||||
| 	handleUpdateTransaction: function(transaction) { | ||||
| 		this.setState({ | ||||
| 			editingTransaction: false | ||||
| 		}); | ||||
| 		if (transaction.TransactionId != -1) { | ||||
| 			this.updateTransaction(transaction); | ||||
| 		} else { | ||||
| 			this.createNewTransaction(transaction); | ||||
| 		} | ||||
| 	}, | ||||
| 	componentWillReceiveProps: function(nextProps) { | ||||
| 		if (nextProps.selectedAccount != this.props.selectedAccount) { | ||||
| 			this.getTransactionPage(nextProps.selectedAccount, 0); | ||||
| 			console.log("TODO begin fetching transactions for new account"); | ||||
| 		} | ||||
| 	}, | ||||
| 	render: function() { | ||||
| 		var name = "Please select an account"; | ||||
| 		if (this.props.selectedAccount != null) | ||||
| 			name = this.props.selectedAccount.Name; | ||||
|  | ||||
| 		register = []; | ||||
| 		if (this.props.selectedAccount != null) { | ||||
| 			var newTransaction = new Transaction(); | ||||
| 			newTransaction.Description = "Create New Transaction..."; | ||||
| 			newTransaction.Status = TransactionStatus.Entered; | ||||
| 			newTransaction.Date = new Date(); | ||||
| 			newTransaction.Splits.push(new Split()); | ||||
| 			newTransaction.Splits.push(new Split()); | ||||
| 			newTransaction.Splits[0].AccountId = this.props.selectedAccount.AccountId; | ||||
|  | ||||
| 			var transactionRows = []; | ||||
| 			var allTransactions = [newTransaction].concat(this.state.transactions); | ||||
| 			for (var i = 0; i < allTransactions.length; i++) { | ||||
| 				var t = allTransactions[i]; | ||||
| 				transactionRows.push(( | ||||
| 					<TransactionRow | ||||
| 						transaction={t} | ||||
| 						account={this.props.selectedAccount} | ||||
| 						accounts={this.props.accounts} | ||||
| 						account_map={this.props.account_map} | ||||
| 						securities={this.props.securities} | ||||
| 						security_map={this.props.security_map} | ||||
| 						onEdit={this.handleEditTransaction}/> | ||||
| 				)); | ||||
| 			} | ||||
|  | ||||
| 			register = ( | ||||
| 				<Table bordered striped condensed hover> | ||||
| 					<thead><tr> | ||||
| 						<th>Date</th> | ||||
| 						<th>#</th> | ||||
| 						<th>Description</th> | ||||
| 						<th>Account</th> | ||||
| 						<th>Status</th> | ||||
| 						<th>Amount</th> | ||||
| 						<th>Balance</th> | ||||
| 					</tr></thead> | ||||
| 					<tbody> | ||||
| 						{transactionRows} | ||||
| 					</tbody> | ||||
| 					</Table> | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		return ( | ||||
| 			<div> | ||||
| 				<AddEditTransactionModal | ||||
| 					show={this.state.editingTransaction} | ||||
| 					transaction={this.state.selectedTransaction} | ||||
| 					accounts={this.props.accounts} | ||||
| 					account_map={this.props.account_map} | ||||
| 					onCancel={this.handleEditingCancel} | ||||
| 					onSubmit={this.handleUpdateTransaction} | ||||
| 					onDelete={this.handleDeleteTransaction} | ||||
| 					securities={this.props.securities}/> | ||||
| 				{name} | ||||
| 				{register} | ||||
| 			</div> | ||||
| 		); | ||||
| 	} | ||||
| }); | ||||
| @@ -10,30 +10,10 @@ var Button = ReactBootstrap.Button; | ||||
| var ButtonGroup = ReactBootstrap.ButtonGroup; | ||||
| var Glyphicon = ReactBootstrap.Glyphicon; | ||||
|  | ||||
| var Modal = ReactBootstrap.Modal; | ||||
|  | ||||
| var CollapsibleMixin = ReactBootstrap.CollapsibleMixin; | ||||
|  | ||||
| var Combobox = ReactWidgets.Combobox; | ||||
|  | ||||
| const recursiveAccountDisplayInfo = function(account, prefix) { | ||||
| 	var name = prefix + account.Name; | ||||
| 	var accounts = [{AccountId: account.AccountId, Name: name}]; | ||||
| 	for (var i = 0; i < account.Children.length; i++) | ||||
| 		accounts = accounts.concat(recursiveAccountDisplayInfo(account.Children[i], name + "/")); | ||||
| 	return accounts | ||||
| }; | ||||
| const getAccountDisplayList = function(account_list, includeRoot, rootName) { | ||||
| 	var accounts = [] | ||||
| 	if (includeRoot) | ||||
| 		accounts.push({AccountId: -1, Name: rootName}); | ||||
| 	for (var i = 0; i < account_list.length; i++) { | ||||
| 		if (account_list[i].isRootAccount()) | ||||
| 			accounts = accounts.concat(recursiveAccountDisplayInfo(account_list[i], "")); | ||||
| 	} | ||||
| 	return accounts; | ||||
| }; | ||||
|  | ||||
| const AccountCombobox = React.createClass({ | ||||
| 	getDefaultProps: function() { | ||||
| 		return { | ||||
| @@ -494,7 +474,12 @@ const AccountsTab = React.createClass({ | ||||
| 							<Glyphicon glyph='trash' /></Button> | ||||
| 					</ButtonGroup> | ||||
| 				</Col><Col xs={10} className="fullheight transactions-column"> | ||||
| 					blah | ||||
| 					<AccountRegister | ||||
| 						selectedAccount={this.state.selectedAccount} | ||||
| 						accounts={this.props.accounts} | ||||
| 						account_map={this.props.account_map} | ||||
| 						securities={this.props.securities} | ||||
| 						security_map={this.props.security_map} /> | ||||
| 				</Col> | ||||
| 			</Row></Grid> | ||||
| 		); | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| <script type="text/javascript" src="static/models.js"></script> | ||||
|  | ||||
| <script type="text/jsx" src="static/top_bar.js"></script> | ||||
| <script type="text/jsx" src="static/account_register.js"></script> | ||||
| <script type="text/jsx" src="static/accounts.js"></script> | ||||
| <script type="text/jsx" src="static/ui.js"></script> | ||||
|  | ||||
|   | ||||
| @@ -212,8 +212,6 @@ Split.prototype.toJSONobj = function() { | ||||
| } | ||||
|  | ||||
| Split.prototype.fromJSONobj = function(json_obj) { | ||||
| 	var json_obj = getJSONObj(json_input); | ||||
|  | ||||
| 	if (json_obj.hasOwnProperty("SplitId")) | ||||
| 		this.SplitId = json_obj.SplitId; | ||||
| 	if (json_obj.hasOwnProperty("TransactionId")) | ||||
| @@ -243,6 +241,18 @@ const TransactionStatus = { | ||||
| 	Reconciled: 3, | ||||
| 	Voided: 4 | ||||
| } | ||||
| var TransactionStatusList = []; | ||||
| for (var type in TransactionStatus) { | ||||
| 	if (TransactionStatus.hasOwnProperty(type)) { | ||||
| 		TransactionStatusList.push({'StatusId': TransactionStatus[type], 'Name': type}); | ||||
|    } | ||||
| } | ||||
| var TransactionStatusMap = {}; | ||||
| for (var status in TransactionStatus) { | ||||
| 	if (TransactionStatus.hasOwnProperty(status)) { | ||||
| 		TransactionStatusMap[TransactionStatus[status]] = status; | ||||
|    } | ||||
| } | ||||
|  | ||||
| function Transaction() { | ||||
| 	this.TransactionId = -1; | ||||
| @@ -262,8 +272,8 @@ Transaction.prototype.toJSON = function() { | ||||
| 	json_obj.Date = this.Date.toJSON(); | ||||
| 	json_obj.Splits = []; | ||||
| 	for (var i = 0; i < this.Splits.length; i++) | ||||
| 		json_obj.push(this.Splits[i].toJSONobj()); | ||||
| 	return json_obj; | ||||
| 		json_obj.Splits.push(this.Splits[i].toJSONobj()); | ||||
| 	return JSON.stringify(json_obj); | ||||
| } | ||||
|  | ||||
| Transaction.prototype.fromJSON = function(json_input) { | ||||
| @@ -289,8 +299,11 @@ Transaction.prototype.fromJSON = function(json_input) { | ||||
| 			this.Date = new Date(0); | ||||
| 	} | ||||
| 	if (json_obj.hasOwnProperty("Splits")) { | ||||
| 		for (var i = 0; i < json_obj.Splits.length; i++) | ||||
| 			this.Splits.push(this.Splits[i].fromJSON()); | ||||
| 		for (var i = 0; i < json_obj.Splits.length; i++) { | ||||
| 			var s = new Split(); | ||||
| 			s.fromJSONobj(json_obj.Splits[i]); | ||||
| 			this.Splits.push(s); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -300,6 +313,12 @@ Transaction.prototype.isTransaction = function() { | ||||
| 		this.UserId != empty_transaction.UserId; | ||||
| } | ||||
|  | ||||
| Transaction.prototype.deepCopy = function() { | ||||
| 	var t = new Transaction(); | ||||
| 	t.fromJSON(this.toJSON()); | ||||
| 	return t; | ||||
| } | ||||
|  | ||||
| function Error() { | ||||
| 	this.ErrorId = -1; | ||||
| 	this.ErrorString = ""; | ||||
|   | ||||
| @@ -75,3 +75,24 @@ div.accounttree-root div { | ||||
| 	padding: 15px; | ||||
| 	border-right: 1px solid #DDD; | ||||
| } | ||||
|  | ||||
| .register-row-editing { | ||||
| 	background-color: #FFFFE0 !important; | ||||
| } | ||||
| .register-row-editing:hover { | ||||
| 	background-color: #e8e8e8 !important; | ||||
| } | ||||
| .register-row-editing .form-group { | ||||
| 	margin: 0; | ||||
| } | ||||
|  | ||||
| .row > div > .form-group, | ||||
| .row > div > .rw-combobox { | ||||
| 	margin-right: -7px; | ||||
| 	margin-left: -7px; | ||||
| } | ||||
|  | ||||
| .split-header { | ||||
| 	font-weight: 700; | ||||
| 	text-align: center; | ||||
| } | ||||
|   | ||||
							
								
								
									
										27
									
								
								static/utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								static/utils.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| const recursiveAccountDisplayInfo = function(account, prefix) { | ||||
| 	var name = prefix + account.Name; | ||||
| 	var accounts = [{AccountId: account.AccountId, Name: name}]; | ||||
| 	for (var i = 0; i < account.Children.length; i++) | ||||
| 		accounts = accounts.concat(recursiveAccountDisplayInfo(account.Children[i], name + "/")); | ||||
| 	return accounts | ||||
| }; | ||||
|  | ||||
| const getAccountDisplayList = function(account_list, includeRoot, rootName) { | ||||
| 	var accounts = [] | ||||
| 	if (includeRoot) | ||||
| 		accounts.push({AccountId: -1, Name: rootName}); | ||||
| 	for (var i = 0; i < account_list.length; i++) { | ||||
| 		if (account_list[i].isRootAccount()) | ||||
| 			accounts = accounts.concat(recursiveAccountDisplayInfo(account_list[i], "")); | ||||
| 	} | ||||
| 	return accounts; | ||||
| }; | ||||
|  | ||||
| const getAccountDisplayName = function(account, account_map) { | ||||
| 	var name = account.Name; | ||||
| 	while (account.ParentAccountId >= 0) { | ||||
| 		account = account_map[account.ParentAccountId]; | ||||
| 		name = account.Name + "/" + name; | ||||
| 	} | ||||
| 	return name; | ||||
| }; | ||||
| @@ -18,7 +18,7 @@ type Split struct { | ||||
| 	SplitId       int64 | ||||
| 	TransactionId int64 | ||||
| 	AccountId     int64 | ||||
| 	Number        int64 // Check or reference number | ||||
| 	Number        string // Check or reference number | ||||
| 	Memo          string | ||||
| 	Amount        string // String representation of decimal, suitable for passing to big.Rat.SetString() | ||||
| 	Debit         bool | ||||
| @@ -38,10 +38,8 @@ func (s *Split) Valid() bool { | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| type TransactionStatus int64 | ||||
|  | ||||
| const ( | ||||
| 	Entered    TransactionStatus = 1 | ||||
| 	Entered    int64 = 1 | ||||
| 	Cleared                      = 2 | ||||
| 	Reconciled                   = 3 | ||||
| 	Voided                       = 4 | ||||
| @@ -51,7 +49,7 @@ type Transaction struct { | ||||
| 	TransactionId int64 | ||||
| 	UserId        int64 | ||||
| 	Description   string | ||||
| 	Status        TransactionStatus | ||||
| 	Status        int64 | ||||
| 	Date          time.Time | ||||
| 	Splits        []*Split `db:"-"` | ||||
| } | ||||
| @@ -118,7 +116,7 @@ func GetTransaction(transactionid int64, userid int64) (*Transaction, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	err = transaction.SelectOne(&t, "SELECT * from transaction where UserId=? AND TransactionId=?", userid, transactionid) | ||||
| 	err = transaction.SelectOne(&t, "SELECT * from transactions where UserId=? AND TransactionId=?", userid, transactionid) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -172,7 +170,7 @@ func incrementAccountVersions(transaction *gorp.Transaction, user *User, account | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		account.Version++ | ||||
| 		account.AccountVersion++ | ||||
| 		count, err := transaction.Update(account) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
|   | ||||
		Reference in New Issue
	
	Block a user