️ This Gitlab will be shut down at 2021-12-31 23:59:59.
Students and staff can migrate to gitlab.utwente.nl.
SNT members can migrate to gitlab.snt.utwente.nl.
Contact bestuur@snt.utwente.nl for more information.

Migrate your projects today!
Export your project using the webinterface or use a script.

Commit 60dfb4e2 authored by TJHeeringa's avatar TJHeeringa

Progress with debt_collections

parent 49b7ed7b
import Checkbox from "@material-ui/core/Checkbox";
import red from "@material-ui/core/colors/red";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormHelperText from "@material-ui/core/FormHelperText";
import React from "react";
import { ValidatorComponent } from "react-material-ui-form-validator";
......@@ -13,7 +14,7 @@ const style = {
class CheckboxField extends ValidatorComponent {
renderValidatorComponent() {
const { errorMessages, validators, validatorListener, requiredError, label, labelPlacement, ...rest } = this.props;
const { errorMessages, validators, validatorListener, requiredError, label, labelPlacement, helperText, ...rest } = this.props;
return (
<div>
......@@ -22,6 +23,7 @@ class CheckboxField extends ValidatorComponent {
label={label}
labelPlacement={labelPlacement}
/>
<FormHelperText>{ helperText }</FormHelperText>
{ this.errorText() }
</div>
);
......
import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import ExtremeTable from "App/Components/Tables/ExtremeTable";
import { snakeCase } from "lodash";
import moment from "moment";
import PropTypes from "prop-types";
import React, {useState} from "react";
import CardGrid from "../../Components/Card/CardGrid";
import Wrapper from "../../Components/Fields/Wrapper";
import Block from "../../Components/PageLayout/Content/Block";
const useStyles = makeStyles(theme => ({
root: {}
}));
const headers = ["Who", "Amount", "Identifier", "Bank Account", "Email", "Cancellation"];
const editingStateColumnExtensions = [
{ columnName: "who", editingEnabled: false },
{ columnName: "amount", editingEnabled: false },
{ columnName: "identifier", editingEnabled: false },
{ columnName: "bank_account", editingEnabled: false },
{ columnName: "email", editingEnabled: false },
{ columnName: "stornering", editingEnabled: true },
];
const DebtCollectionDetail = ({debtCollection, ...props}) => {
const classes = useStyles();
const headers = ["Who", "Amount", "Identifier", "Bank Account", "Email", "Stornering"];
const editingStateColumnExtensions = [
{ columnName: "who", editingEnabled: false },
{ columnName: "amount", editingEnabled: false },
{ columnName: "identifier", editingEnabled: false },
{ columnName: "bank_account", editingEnabled: false },
{ columnName: "email", editingEnabled: false },
{ columnName: "stornering", editingEnabled: true },
];
const morphDataToDebtEntry = (data) => {
return {
who: data.profile.surname,
amount: data.total_price,
identifier: undefined,
bank_account: data.profile.iban,
email: data.profile.email,
};
};
const extremeHeaders = headers.map(header=>{return {name: snakeCase(header), title: header};});
const rows = debtCollection.debt_entries ? debtCollection.debt_entries.map(entry=>morphDataToDebtEntry(entry)) : [];
const [selection, setSelection] = useState([]);
return (
<ExtremeTable
headers={extremeHeaders}
rows={debtCollection.debt_entries || []}
booleanColumns={["stornering"]}
showExporter={true}
showGrouping={false}
showSelect={false}
editingStateColumnExtensions={editingStateColumnExtensions}
showEditing={true}
selection={{selection: selection, setSelection: setSelection}}
{...props}
/>
<Block>
<Wrapper>
<Typography variant={"h4"}>
Debt Collection { debtCollection.debt_collection_date && "of " + moment(debtCollection.debt_collection_date).format("LLLL") }
</Typography>
<div>
<Button variant={"outlined"} color={"secondary"}>Delete Debt Collection</Button>
&nbsp;
<Button variant={"outlined"}>Lock</Button>
</div>
</Wrapper>
<hr className={"box-title-separator"}/>
<ExtremeTable
headers={extremeHeaders}
rows={rows || []}
booleanColumns={["stornering"]}
showExporter={true}
showGrouping={false}
showSelect={false}
editingStateColumnExtensions={editingStateColumnExtensions}
showEditing={true}
selection={{selection: selection, setSelection: setSelection}}
{...props}
/>
</Block>
);
};
DebtCollectionDetail.propTypes = {
......
import {Checkbox, Input, MenuItem, Select} from "@material-ui/core";
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import CalendarToday from "@material-ui/icons/CalendarToday";
import FormModal from "App/Components/Modals/FormModal";
import { useAlertHandler } from "App/Contexts/AlertHandler";
import PropTypes from "prop-types";
import React from "react";
import Block from "../../Components/PageLayout/Content/Block";
import {useAPI} from "../../Contexts/API";
import DebtCollectionNew from "./DebtCollectionNew";
import useModalState from "../../Components/Hooks/useModalState";
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(2),
flexGrow: 1,
},
paper: {
padding: theme.spacing(1),
color: theme.palette.text.secondary,
},
addButton: {
width: "100%",
},
wrapper: {
display: "flex",
justifyContent: "space-between",
padding: theme.spacing(1, 0),
},
icon: {
margin: theme.spacing(2, 0),
marginRight: theme.spacing(2),
},
textField: {
width: "100%",
},
}));
const DebtCollectionList = ({ debtCollectionApi, eventsApi, association, setSelectedDebtCollection }) => {
const classes = useStyles();
const alertHandler = useAlertHandler();
const API = useAPI();
const debtCollections = debtCollectionApi.collection;
const events = eventsApi.collection;
const [modalOpen, toggleModal] = useModalState(false);
const onCreate = (debtCollection) => {
debtCollectionApi.add(debtCollection);
toggleModal();
};
const handleDebtCollectionSelection = (debt_collection) => {
API.callv4({
url: debt_collection.url,
queryParams: {expand: "debt_entries"},
method: "GET",
on_succes: (data) => setSelectedDebtCollection(data),
on_failure: () => {
alertHandler.handleAlertHandler("red", "Could not retrieve debt collection");
}
});
};
return (
<div className={classes.root}>
<DebtCollectionNew
association={association}
open={modalOpen}
onCreate={onCreate}
onCancel={toggleModal}
events={events}
/>
<Block>
<Typography variant={"h4"}>Debt Collections</Typography>
<List>
<ListItem>
<Button onClick={toggleModal} style={{width:"100%"}} variant={"outlined"} color={"primary"}>
New
</Button>
</ListItem>
<hr className={"box-title-separator"}/>
{ debtCollections && debtCollections.map((debtCollection,d)=>
<ListItem button key={d} onClick={()=>handleDebtCollectionSelection(debtCollection)}>
<ListItemText primary={debtCollection.debt_collection_date} secondary={""+debtCollection.amount}/>
</ListItem>
) }
</List>
</Block>
</div>
);
};
DebtCollectionList.propTypes = {
association: PropTypes.object.isRequired,
debtCollectionApi: PropTypes.object.isRequired,
eventsApi: PropTypes.object.isRequired,
setSelectedDebtCollection: PropTypes.func.isRequired
};
export default DebtCollectionList;
import {Checkbox, MenuItem} from "@material-ui/core";
import ListItemText from "@material-ui/core/ListItemText";
import CalendarToday from "@material-ui/icons/CalendarToday";
import FormModal from "App/Components/Modals/FormModal";
import { useAlertHandler } from "App/Contexts/AlertHandler";
import PropTypes from "prop-types";
import React, {useCallback, useState} from "react";
import CheckboxField from "../../Components/Fields/CheckboxField";
import DateTimeField from "../../Components/Fields/DateTimeField";
import SelectField from "../../Components/Fields/SelectField";
import Wrapper from "../../Components/Fields/Wrapper";
import {useAPI} from "../../Contexts/API";
import IconHolder from "../../Components/Fields/IconHolder";
import moment from "moment";
const DebtCollectionNew = ({ debtCollection: propDebtCollection, open, onCreate, onCancel, association, events }) => {
const alertHandler = useAlertHandler();
const API = useAPI();
const [debtCollectionChanges, setDebtCollectionChanges] = useState({association: association.url});
const debtCollection = {...propDebtCollection, ...debtCollectionChanges};
const handleDebtCollectionChange = (field, value) => {
setDebtCollectionChanges(prevState=>({ ...prevState, [field]: value }));
};
const addDebtCollection = () => {
console.log(debtCollection);
API.callv4({
url: "/debt_collections",
method: "POST",
object: debtCollection,
on_succes: (data) => {
onCreate(data);
setDebtCollectionChanges({association: association.url});
alertHandler.success("Created debt collection");
},
on_failure: () => {
alertHandler.failure("Creation of debt collection failed");
}
});
};
const renderMultipleSelect = useCallback((selected)=> {
return events.filter(event=>selected.includes(event.url)).map(event=>event.name).join(", ");
}, [events]);
return (
<FormModal
title={"New Debt Collection"}
open={open}
onCancel={onCancel}
onSubmit={addDebtCollection}
size={"lg"}
submitButtonText={"Create"}
>
<Wrapper>
<IconHolder Icon={CalendarToday}/>
<DateTimeField
name={"Debt Collection Date"}
value={debtCollection.debt_collection_date}
onChange={date => handleDebtCollectionChange("debt_collection_date", date)}
helperText={"This is the date you are planning on collection the debt collection."}
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={CalendarToday}/>
<SelectField
name={"events"}
value={debtCollection.include_events}
onChange={event=>handleDebtCollectionChange("include_events", event.target.value)}
labelPlacement={"end"}
SelectProps={{
multiple: true,
renderValue: renderMultipleSelect
}}
helperText={"Events to include in the debt collection"}
>
{ events && events.map((event,e)=>(
<MenuItem key={e} value={event.url}>
<Checkbox checked={debtCollection.include_events.indexOf(event.url) > -1} />
<ListItemText primary={event.name} secondary={event.start_date}/>
</MenuItem>
)) }
</SelectField>
</Wrapper>
<Wrapper>
<CheckboxField
label={"Include Membership fees"}
value={debtCollection.include_membership_fee}
checked={debtCollection.include_membership_fee}
onChange={event=>handleDebtCollectionChange("include_membership_fee", event.target.checked)}
labelPlacement={"end"}
/>
</Wrapper>
<Wrapper>
<CheckboxField
label={"Include invoices"}
value={debtCollection.include_invoices}
checked={debtCollection.include_invoices}
onChange={event=>handleDebtCollectionChange("include_invoices", event.target.checked)}
labelPlacement={"end"}
/>
</Wrapper>
<Wrapper>
<CheckboxField
label={"Final debt collection of the year"}
value={debtCollection.ignore_threshold}
checked={debtCollection.ignore_threshold}
onChange={event=>handleDebtCollectionChange("ignore_threshold", event.target.checked)}
labelPlacement={"end"}
helperText={"Ticking this will mean that the debt collection threshold will be ignored."}
/>
</Wrapper>
</FormModal>
);
};
DebtCollectionNew.propTypes = {
association: PropTypes.object.isRequired,
debtCollection: PropTypes.object,
open: PropTypes.object.isRequired,
onCreate: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
events: PropTypes.array.isRequired
};
DebtCollectionNew.defaultProps = {
debtCollection: {
include_events: [],
include_membership_fee: false,
debt_collection_date: moment().add(1,"days").utc(true),
include_invoices: false,
ignore_threshold: false
},
};
export default DebtCollectionNew;
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import CalendarToday from "@material-ui/icons/CalendarToday";
import FormModal from "App/Components/Modals/FormModal";
import { useAlertHandler } from "App/Contexts/AlertHandler";
import moment from "moment";
import PropTypes from "prop-types";
import React, {useEffect, useState} from "react";
import { useGet } from "restful-react";
import CheckboxField from "../../Components/Fields/CheckboxField";
import DateTimeField from "../../Components/Fields/DateTimeField";
import Wrapper from "../../Components/Fields/Wrapper";
import Block from "../../Components/PageLayout/Content/Block";
import {useAPI} from "../../Contexts/API";
import DebtCollectionDetail from "./DebtCollectionDetail";
import SelectField from "../../Components/Fields/SelectField";
import {MenuItem, Select} from "@material-ui/core";
const useStyles = makeStyles((theme) => ({
root: {
margin: theme.spacing(2),
flexGrow: 1,
},
paper: {
padding: theme.spacing(1),
// textAlign: 'center',
color: theme.palette.text.secondary,
},
addButton: {
width: "100%",
},
wrapper: {
display: "flex",
justifyContent: "space-between",
padding: theme.spacing(1, 0),
},
icon: {
margin: theme.spacing(2, 0),
marginRight: theme.spacing(2),
},
textField: {
width: "100%",
},
}));
const DebtCollections = (props) => {
const classes = useStyles();
const alertHandler = useAlertHandler();
const API = useAPI();
const [checkboxes, setCheckboxes] = useState([]);
const default_new_debt_collection = {
association: props.association.url,
debt_collection_date: moment().add(1,"days")
};
let { data: debtCollections, loading: loadingdebtCollections } = useGet({
path: "/debt_collections",
queryParams: {limit: 10000, association__slug: props.association.slug, ordering: "-debt_collection_date"},
resolve: data => data && data.results.map(morphDataToDebtCollection)
});
let { data: events } = useGet({
path: "/events",
queryParams: {limit: 10000, association__slug: props.association.slug},
resolve: data => data && data.results.map(morphDataToDebtCollection)
});
useEffect(()=>{
if (events !== null) {
setCheckboxes( events.reduce(
(options, option) => ({
...options,
[option.slug]: false
}),
{}
));
}
}, [events]);
const [newDebtCollection, setNewDebtCollection] = React.useState(default_new_debt_collection);
const [selectedDebtCollection, setSelectedDebtCollection] = React.useState({});
const [modalOpen, setModalOpen] = React.useState(false);
const [membershipFeeCheckbox, setMembershipFeeCheckbox] = React.useState(false);
const [finalDebtCollectionCheckbox, setFinalDebtCollectionCheckbox] = React.useState(false);
const handleCheckboxChange = (field, value) => {
console.log(events);
console.log(field, value);
setCheckboxes(prevState=>({
...prevState,
[field]: value
}));
};
const morphDataToDebtCollection = (data) => {
data.debt_collection_date = moment(data.debt_collection_date);
if (data.debt_entries !== undefined) {
data.debt_entries = data.debt_entries.map(morphDataToDebtEntry);
}
return data;
};
const morphNewDebtCollectionToApiData = (debt_collection) => {
debt_collection.debt_collection_date = debt_collection.debt_collection_date.toDate();
return debt_collection;
};
const morphDataToDebtEntry = (data) => {
return {
who: data.profile.surname,
amount: data.total_price,
identifier: undefined,
bank_account: data.profile.iban,
email: data.profile.email,
};
};
const handleDebtCollectionSelection = (debt_collection) => {
API.callv3({
url: debt_collection.url,
method: "GET",
on_succes: (data) => {
setSelectedDebtCollection(morphDataToDebtCollection(data));
},
on_failure: () => {
alertHandler.handleAlertHandler("red", "Could not retrieve debt collection");
}
});
};
const handleNewDebtCollectionChange = (date) => {
console.log("date", date);
setNewDebtCollection(prevState=>{
prevState.debt_collection_date = moment(date);
return prevState;
});
};
const addDebtCollection = () => {
API.callv4({
url: "/debt_collections",
method: "POST",
object: morphNewDebtCollectionToApiData(newDebtCollection),
on_succes: (data) => {
debtCollections.splice(0, 0, data);
setNewDebtCollection(morphDataToDebtCollection(default_new_debt_collection));
setModalOpen(false);
},
on_failure: () => {
alertHandler.handleAlertHandler("red", "Creation of debt collection failed");
setModalOpen(false);
}
});
};
console.log("debtCollections", loadingdebtCollections, debtCollections);
console.log("selectedDebtCollection", selectedDebtCollection);
console.log(checkboxes);
return (
<div className={classes.root}>
<FormModal
title={"New Debt Collection"}
open={modalOpen}
onCancel={()=>setModalOpen(false)}
onSubmit={addDebtCollection}
size={"lg"}
submitButtonText={"Create"}
>
<Wrapper>
<CalendarToday className={classes.icon} color={"action"} />
<DateTimeField
name={"Debt Collection Date"}
value={newDebtCollection.debt_collection_date}
onChange={date => handleNewDebtCollectionChange(date)}
helperText={"This is the date you are planning on collection the debt collection."}
/>
</Wrapper>
<Typography>Please select the events that should be included in the debt collection</Typography>
<Wrapper>
<SelectField
name={"events"}
value={newDebtCollection.events}
onChange={checkEvent=>setNewDebtCollection(prevState => ({...prevState, event: checkEvent.target.value}))}
labelPlacement={"end"}
multiple
>
{ events && events.map((event,e)=>(
<MenuItem key={e} value={event.slug}>{ event.name }</MenuItem>
)) }
</SelectField>
</Wrapper>
<Wrapper>
<Select