Skip to content

Commit

Permalink
Merge pull request #17 from dimmik/family-relations
Browse files Browse the repository at this point in the history
Family relations
  • Loading branch information
dimmik authored Oct 14, 2019
2 parents a3dd8e9 + 6bd9c48 commit 08db3f1
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 33 deletions.
2 changes: 2 additions & 0 deletions TCalcCore/Domain/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class Person : AbstractItem
public List<SpendingInfo> SpentSendingInfo { get; set; } = new List<SpendingInfo>();
public List<SpendingInfo> ReceivedSendingInfo { get; set; } = new List<SpendingInfo>();

public string ParentId { get; set; }

public override string ToString()
{
return $"{Name} - {GUID} -- {Debt()}";
Expand Down
30 changes: 30 additions & 0 deletions TCalcCore/Logic/TourCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,37 @@ private void DealWithRoundErrors()
public Tour SuggestFinalPayments()
{
Calculate(includePlanned: true);
// deal with families
// first, find all guys with parents
var descedants = CurrentTour.Persons
.Where(p => !string.IsNullOrWhiteSpace(p.ParentId))
.Where(p => p.Debt() != 0)
.Where(
p => CurrentTour.Persons.Any(
pp => (pp.GUID == p.ParentId)
)
).ToArray();
// remove cycles
descedants = descedants.Where(p => !descedants.Any(d => d.GUID == p.ParentId)).ToArray();
// nullify all descendants' debt
foreach (var d in descedants)
{
var parent = CurrentTour.Persons.First(p => p.GUID == d.ParentId);
var debt = d.Debt();
var spending = new Spending()
{
Planned = true,
FromGuid = d.GUID,
ToGuid = new[] { parent.GUID }.ToList(),
ToAll = false,
AmountInCents = debt,
GUID = Guid.NewGuid().ToString(),
Description = $"Family '{d.Name}' -> '{parent.Name}'"
};
CurrentTour.Spendings.Add(spending);
}
// find ones who owes min (will receive max)
Calculate(includePlanned: true);
var creditors = CurrentTour.Persons.Where(p => p.Debt() < 0).OrderBy(p => p.Debt()).ToArray();
var debtors = CurrentTour.Persons.Where(p => p.Debt() > 0).OrderBy(p => -p.Debt()).ToArray();
int maxIterations = 500;
Expand Down
17 changes: 8 additions & 9 deletions TourCalcWebApp/App/containers/person-show-spendings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ export default class SpendingsDetail extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogOpen: props.open
dialogOpen: props.open,
spendingInfo: props.spendingInfo,
person: props.person
}
this.spendingInfo = props.spendingInfo
this.person = props.person
}
spendingInfo = []
person = null
/*{
from: Person 3,
receivedAmountInCents: 250,
Expand All @@ -40,22 +38,23 @@ export default class SpendingsDetail extends React.Component {
render() {
return (
<span>
<span onClick={() => this.setState({ dialogOpen: true })} style={{ cursor: "pointer" }}>
<span onClick={() => this.setState({ dialogOpen: true, spendingInfo: this.props.spendingInfo })} style={{ cursor: "pointer" }}>
{this.props.showText}
</span>
<Dialog aria-labelledby="customized-dialog-title" open={this.state.dialogOpen}>
<DialogTitle id="customized-dialog-title">{this.props.received ? 'Received' : 'Spent' } for {this.person.name}</DialogTitle>
<DialogTitle id="customized-dialog-title">{this.props.received ? 'Received' : 'Spent' } for {this.state.person.name}</DialogTitle>
<DialogContent>

<Table>
<TableBody>
{this.spendingInfo.map((si, idx) => {
{this.state.spendingInfo.map((si, idx) => {
return this.props.received ? (
<TableRow key={idx} hover>
<TableCell component="th" scope="row">
{si.from}
</TableCell>
<TableCell component="th" scope="row">
<TableCell component="th" scope="row">
{/*<!--pre>{JSON.stringify(si, null, 2)}</pre>*/}
{si.receivedAmountInCents} ({(si.receivedAmountInCents * 100 / si.totalSpendingAmountInCents).toFixed(0)}% of {si.totalSpendingAmountInCents})
</TableCell>
<TableCell component="th" scope="row">
Expand Down
33 changes: 21 additions & 12 deletions TourCalcWebApp/App/containers/tour-person-edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,11 @@ export default class PersonForm extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogOpen: props.open
dialogOpen: props.open,
person: props.person == null ? { name: "", weight: 100, parentId: "" } : props.person,
tour: props.tour
}
if (props.name != null) this.name = props.name
if (props.weight != null) this.weight = props.weight
//alert('open: ' + props.open)
}
name = ""
weight = 100
render() {
return (
<span>
Expand All @@ -51,21 +48,33 @@ export default class PersonForm extends React.Component {
id="name"
required
label="Name"
defaultValue={this.name}
defaultValue={this.state.person.name}
autoFocus
onChange={(e) => this.name = event.target.value}
onChange={(e) => { this.state.person.name = e.target.value; this.setState({ person: this.state.person }) }}
margin="normal"
/>
<TextField
id="weight"
required
label="Weight"
type="number"
defaultValue={this.weight}
onChange={(e) => this.weight = event.target.value}
defaultValue={this.state.person.weight}
onChange={(e) => { this.state.person.weight = e.target.value; this.setState({ person: this.state.person }) }}
margin="normal"
/>
<br />
<InputLabel htmlFor="select-relative">Paying Relative</InputLabel>
<Select
input={<Input id="select-relative" />}
value={this.state.person.parentId == null ? 'none' : this.state.person.parentId}
onChange={(e) => { this.state.person.parentId = e.target.value; this.setState({ person: this.state.person }) }}
>
<MenuItem value="none" key='None'>None</MenuItem>
{
this.state.tour.persons.map(p => (<MenuItem value={p.guid} key={p.guid}>{p.name}</MenuItem>))
}
</Select>
<br />
</FormGroup>

</form>
Expand All @@ -75,8 +84,8 @@ export default class PersonForm extends React.Component {
color="primary" size='large' variant='outlined'
onClick={() => {
( this.props.mode === "add"
? AppState.addPerson(this.props.app, this.props.tourid, { name: this.name, weight: this.weight })
: AppState.editPerson(this.props.app, this.props.tourid, { guid: this.props.guid, name: this.name, weight: this.weight })
? AppState.addPerson(this.props.app, this.props.tourid, this.state.person)
: AppState.editPerson(this.props.app, this.props.tourid, this.state.person)
)
.then(this.setState({ dialogOpen: false }))
.then(() => { AppState.loadTour(this.props.app, this.props.tourid) })
Expand Down
2 changes: 1 addition & 1 deletion TourCalcWebApp/App/containers/tour-spending-edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default class SpendingsForm extends React.Component {
guid: ""
}
validate() {
if (this.state.spending != null && this.state.spending.amountInCents > 0 && this.state.spending.fromGuid != null && (this.spending.toAll || this.spending.toGuid.length > 0))
if (this.state.spending != null && this.state.spending.amountInCents != 0 && this.state.spending.fromGuid != null && (this.spending.toAll || this.spending.toGuid.length > 0))
return true;
return false;
}
Expand Down
47 changes: 36 additions & 11 deletions TourCalcWebApp/App/containers/tour-ui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ class TourTable extends React.Component {
app={this}
><Button color='primary' variant='outlined'>Add</Button></SpendingForm>
&nbsp;
show suggested <input type='checkbox' defaultChecked={this.state.showSuggested}
onClick={() => { this.setState({ showSuggested: !this.state.showSuggested}) }}
/>
<Button color='secondary' variant='outlined' onClick={() => { this.setState({ showSuggested: !this.state.showSuggested }) }}>
{this.state.showSuggested ? 'Hide' : 'Show'} suggested
</Button>

</TableCell>
<TableCell align="right">From</TableCell>
Expand Down Expand Up @@ -244,11 +244,13 @@ class TourTable extends React.Component {
<TableCell>Person Name
<PersonForm mode="add"
tourid={this.props.tourid}
open={false}
tour={this.state.tour}
open={false}
app={this}
buttonText="Add" actionButtonText="Add Person" ><Button color='primary' variant='outlined'>Add</Button>
</PersonForm>
</TableCell>
</TableCell>
<TableCell align="right">#</TableCell>
<TableCell align="right">Weight %</TableCell>
<TableCell align="right">Spent</TableCell>
<TableCell align="right">Received</TableCell>
Expand All @@ -267,17 +269,40 @@ class TourTable extends React.Component {
}}>X</span>
&nbsp;
{(idx + 1) + '.'}
<PersonForm mode="edit"
<PersonForm mode="edit"
tourid={this.props.tourid}
tour={this.state.tour}
open={false}
app={this}
buttonText={p.name} actionButtonText="Save Person"
name={p.name}
weight={p.weight}
person={p}
guid={p.guid}
><span style={{ cursor: 'pointer', textDecoration: 'underline' }}>{p.name}</span></PersonForm>


><span style={{ cursor: 'pointer', textDecoration: 'underline' }}>{p.name}

</span></PersonForm>
<span style={{ fontSize: 'xx-small' }}>
{this.state.tour.persons.find((pp) => pp.guid == p.parentId) == null
? ''
: ' > ' + this.state.tour.persons.find((pp) => pp.guid == p.parentId).name}
</span>
</TableCell>
<TableCell align="right">
<SpendingForm
tour={this.state.tour}
buttonText="Add"
actionButtonText="Save Spending"
open={false}
mode="add"
app={this}
spending={{
description: "",
amountInCents: 0,
fromGuid: p.guid,
toGuid: [],
toAll: false,
guid: ""}}
>&nbsp;&nbsp;<span style={{ cursor: 'pointer', borderStyle: 'ridge', fontSize: 'small' }}
>Spend</span></SpendingForm>
</TableCell>
<TableCell align="right">{p.weight}</TableCell>
<TableCell align="right">
Expand Down

0 comments on commit 08db3f1

Please sign in to comment.