1
0
mirror of https://github.com/aclindsa/moneygo.git synced 2024-12-26 15:42:27 -05:00

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 // Import all the objects we want to use from ReactBootstrap
var Alert = ReactBootstrap.Alert;
var Modal = ReactBootstrap.Modal; var Modal = ReactBootstrap.Modal;
var Pagination = ReactBootstrap.Pagination; var Pagination = ReactBootstrap.Pagination;
@ -127,12 +128,16 @@ const AmountInput = React.createClass({
var symbol = "?"; var symbol = "?";
if (this.props.security) if (this.props.security)
symbol = this.props.security.Symbol; symbol = this.props.security.Symbol;
var bsStyle = "";
if (this.props.bsStyle)
bsStyle = this.props.bsStyle;
return ( return (
<Input type="text" <Input type="text"
value={this.state.Amount} value={this.state.Amount}
onChange={this.onChange} onChange={this.onChange}
addonBefore={symbol} addonBefore={symbol}
bsStyle={bsStyle}
ref="amount"/> ref="amount"/>
); );
} }
@ -142,7 +147,10 @@ const AddEditTransactionModal = React.createClass({
_getInitialState: function(props) { _getInitialState: function(props) {
// Ensure we can edit this without screwing up other copies of it // Ensure we can edit this without screwing up other copies of it
var t = props.transaction.deepCopy(); var t = props.transaction.deepCopy();
return {transaction: t}; return {
errorAlert: [],
transaction: t
};
}, },
getInitialState: function() { getInitialState: function() {
return this._getInitialState(this.props); return this._getInitialState(this.props);
@ -232,6 +240,24 @@ const AddEditTransactionModal = React.createClass({
}); });
}, },
handleSubmit: function() { 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) if (this.props.onSubmit != null)
this.props.onSubmit(this.state.transaction); 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 = []; splits = [];
for (var i = 0; i < this.state.transaction.Splits.length; i++) { for (var i = 0; i < this.state.transaction.Splits.length; i++) {
var self = this; var self = this;
var s = this.state.transaction.Splits[i]; var s = this.state.transaction.Splits[i];
var security = null; 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]; 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 // Define all closures for calling split-updating functions
var deleteSplitFn = (function() { var deleteSplitFn = (function() {
@ -307,12 +345,14 @@ const AddEditTransactionModal = React.createClass({
value={s.AccountId} value={s.AccountId}
includeRoot={false} includeRoot={false}
onSelect={updateAccountFn} onSelect={updateAccountFn}
ref={"account-"+i} /></Col> ref={"account-"+i}
className={accountValidation}/></Col>
<Col xs={2}><AmountInput type="text" <Col xs={2}><AmountInput type="text"
value={s.Amount} value={s.Amount}
security={security} security={security}
onChange={updateAmountFn} onChange={updateAmountFn}
ref={"amount-"+i} /></Col> ref={"amount-"+i}
bsStyle={amountValidation}/></Col>
{deleteSplitButton} {deleteSplitButton}
</Row> </Row>
)); ));
@ -367,6 +407,7 @@ const AddEditTransactionModal = React.createClass({
bsStyle="success"> bsStyle="success">
<Glyphicon glyph='plus-sign' /></Button></Col> <Glyphicon glyph='plus-sign' /></Button></Col>
</Row> </Row>
<Row>{this.state.errorAlert}</Row>
</Grid> </Grid>
</form> </form>
</Modal.Body> </Modal.Body>

View File

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

View File

@ -323,6 +323,33 @@ Transaction.prototype.deepCopy = function() {
return t; 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() { function Error() {
this.ErrorId = -1; this.ErrorId = -1;
this.ErrorString = ""; this.ErrorString = "";

View File

@ -115,3 +115,22 @@ div.accounttree-root div {
.skinny-pagination { .skinny-pagination {
margin: 0px; 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;
}