Add validation of transactions in UI

This commit is contained in:
Aaron Lindsay 2015-08-29 09:50:16 -04:00
parent 6708edb00c
commit fcea2d380b
4 changed files with 96 additions and 5 deletions

View File

@ -1,5 +1,6 @@
// Import all the objects we want to use from ReactBootstrap
var Alert = ReactBootstrap.Alert;
var Modal = ReactBootstrap.Modal;
var Pagination = ReactBootstrap.Pagination;
@ -127,12 +128,16 @@ const AmountInput = React.createClass({
var symbol = "?";
if (this.props.security)
symbol = this.props.security.Symbol;
var bsStyle = "";
if (this.props.bsStyle)
bsStyle = this.props.bsStyle;
return (
<Input type="text"
value={this.state.Amount}
onChange={this.onChange}
addonBefore={symbol}
bsStyle={bsStyle}
ref="amount"/>
);
}
@ -142,7 +147,10 @@ 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};
return {
errorAlert: [],
transaction: t
};
},
getInitialState: function() {
return this._getInitialState(this.props);
@ -232,6 +240,24 @@ const AddEditTransactionModal = React.createClass({
});
},
handleSubmit: function() {
var errorString = ""
var imbalancedSecurityList = this.state.transaction.imbalancedSplitSecurities(this.props.account_map);
if (imbalancedSecurityList.length > 0)
errorString = "Transaction must balance"
for (var i = 0; i < this.state.transaction.Splits.length; i++) {
var s = this.state.transaction.Splits[i];
if (!(s.AccountId in this.props.account_map)) {
errorString = "All accounts must be valid"
}
}
if (errorString.length > 0) {
this.setState({
errorAlert: (<Alert className='saving-transaction-alert' bsStyle='danger'><strong>Error Saving Transaction:</strong> {errorString}</Alert>)
});
return;
}
if (this.props.onSubmit != null)
this.props.onSubmit(this.state.transaction);
},
@ -250,13 +276,25 @@ const AddEditTransactionModal = React.createClass({
);
}
var imbalancedSecurityList = this.state.transaction.imbalancedSplitSecurities(this.props.account_map);
var imbalancedSecurityMap = {};
for (i = 0; i < imbalancedSecurityList.length; i++)
imbalancedSecurityMap[imbalancedSecurityList[i]] = i;
splits = [];
for (var i = 0; i < this.state.transaction.Splits.length; i++) {
var self = this;
var s = this.state.transaction.Splits[i];
var security = null;
if (this.props.account_map[s.AccountId])
var amountValidation = "";
var accountValidation = "";
if (s.AccountId in this.props.account_map) {
security = this.props.security_map[this.props.account_map[s.AccountId].SecurityId];
if (security.SecurityId in imbalancedSecurityMap)
amountValidation = "error";
} else {
accountValidation = "has-error";
}
// Define all closures for calling split-updating functions
var deleteSplitFn = (function() {
@ -307,12 +345,14 @@ const AddEditTransactionModal = React.createClass({
value={s.AccountId}
includeRoot={false}
onSelect={updateAccountFn}
ref={"account-"+i} /></Col>
ref={"account-"+i}
className={accountValidation}/></Col>
<Col xs={2}><AmountInput type="text"
value={s.Amount}
security={security}
onChange={updateAmountFn}
ref={"amount-"+i} /></Col>
ref={"amount-"+i}
bsStyle={amountValidation}/></Col>
{deleteSplitButton}
</Row>
));
@ -367,6 +407,7 @@ const AddEditTransactionModal = React.createClass({
bsStyle="success">
<Glyphicon glyph='plus-sign' /></Button></Col>
</Row>
<Row>{this.state.errorAlert}</Row>
</Grid>
</form>
</Modal.Body>

View File

@ -31,6 +31,9 @@ const AccountCombobox = React.createClass({
},
render: function() {
var accounts = getAccountDisplayList(this.props.accounts, this.props.includeRoot, this.props.rootName);
var className = "";
if (this.props.className)
className = this.props.className;
return (
<Combobox
data={accounts}
@ -38,7 +41,8 @@ const AccountCombobox = React.createClass({
textField='Name'
value={this.props.value}
onSelect={this.handleAccountChange}
ref="account" />
ref="account"
className={className} />
);
}
});

View File

@ -323,6 +323,33 @@ Transaction.prototype.deepCopy = function() {
return t;
}
Transaction.prototype.imbalancedSplitSecurities = function(account_map) {
// Return a list of SecurityIDs for those securities that aren't balanced
// in this transaction's splits. If a split's AccountId is invalid, that
// split is ignored, so those must be checked elsewhere
var splitBalances = {};
const emptySplit = new Split();
for (var i = 0; i < this.Splits.length; i++) {
split = this.Splits[i];
if (split.AccountId == emptySplit.AccountId) {
continue;
}
var securityId = account_map[split.AccountId].SecurityId;
if (securityId in splitBalances) {
splitBalances[securityId] = split.Amount.plus(splitBalances[securityId]);
} else {
splitBalances[securityId] = split.Amount.plus(0);
}
}
var imbalancedIDs = [];
for (var id in splitBalances) {
if (!splitBalances[id].eq(0)) {
imbalancedIDs.push(id);
}
}
return imbalancedIDs;
}
function Error() {
this.ErrorId = -1;
this.ErrorString = "";

View File

@ -115,3 +115,22 @@ div.accounttree-root div {
.skinny-pagination {
margin: 0px;
}
/* Make Combobox support .has-error class */
.has-error.rw-widget {
border-color: #843534;
}
.has-error.rw-widget.rw-state-focus {
border-color: #843534;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset, 0px 0px 6px #CE8483;
}
.has-error.rw-widget > .rw-select {
border-left: 1px solid #843534;
color: #A94442;
background-color: #F2DEDE;
}
/* Fix Alert Spacing inside */
.alert.saving-transaction-alert {
margin: 20px 0 0 0;
}