Merge branch 'allow-incident-price-modification-frontend' into 'master'
Allow incident price modification frontend+backend See merge request saburly/psihologija!81
This commit was merged in pull request #81.
This commit is contained in:
@@ -12,16 +12,21 @@ import {
|
||||
incidentDescriptions,
|
||||
incidentLevelDescriptions,
|
||||
UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION, UNLOCKED_INCIDENT_STANDALONE, UNSCHEDULED_INCIDENT_AFTER_RESERVATION,
|
||||
UNSCHEDULED_INCIDENT_BEFORE_RESERVATION, UNSCHEDULED_INCIDENT_STANDALONE
|
||||
UNSCHEDULED_INCIDENT_BEFORE_RESERVATION, UNSCHEDULED_INCIDENT_STANDALONE, BOOKING_CANCELED_LATE, BOOKING_MOVED_TO_ANOTHER_DAY,
|
||||
BOOKING_SHORTENED
|
||||
} from '../../../constants/enums';
|
||||
import { doorLockRelatedWithReservationIncidentHeaders, standaloneDoorLockIncidentHeaders, bookingChangeIncidentHeaders } from '../../../constants/constants';
|
||||
import { deleteIncidents } from "../../../store/actions";
|
||||
import { deleteIncidents, updateIncidentFees } from "../../../store/actions";
|
||||
|
||||
class SingleIncidentsTable extends Component {
|
||||
state = {
|
||||
selectedUnlockedIncidentIds: [],
|
||||
selectedUnscheduledIncidentIds: [],
|
||||
selectedBookingChangeIncidentIds: []
|
||||
selectedBookingChangeIncidentIds: [],
|
||||
changedUnlockedIncidentIds: {},
|
||||
changedUnscheduledIncidentIds: {},
|
||||
changedBookingChangeIncidentIds: {},
|
||||
inputIdToFocus: null,
|
||||
};
|
||||
|
||||
onSelectChange = (selectedIncidents) => {
|
||||
@@ -82,6 +87,24 @@ class SingleIncidentsTable extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
updateChangedFees = () => {
|
||||
const { dateRange, updateIncidentsById, memberId } = this.props;
|
||||
const { changedUnlockedIncidentIds, changedUnscheduledIncidentIds, changedBookingChangeIncidentIds } = this.state;
|
||||
|
||||
const incidentFeesToUpdate = {
|
||||
unlockedIncidentFees: changedUnlockedIncidentIds,
|
||||
unscheduledIncidentFees: changedUnscheduledIncidentIds,
|
||||
bookingChangeIncidentFees: changedBookingChangeIncidentIds
|
||||
};
|
||||
|
||||
updateIncidentsById(dateRange, incidentFeesToUpdate, memberId);
|
||||
this.setState({
|
||||
changedUnlockedIncidentIds: {},
|
||||
changedUnscheduledIncidentIds: {},
|
||||
changedBookingChangeIncidentIds: {}
|
||||
});
|
||||
};
|
||||
|
||||
render(){
|
||||
const {
|
||||
loading,
|
||||
@@ -94,12 +117,22 @@ class SingleIncidentsTable extends Component {
|
||||
const {
|
||||
selectedUnlockedIncidentIds,
|
||||
selectedUnscheduledIncidentIds,
|
||||
selectedBookingChangeIncidentIds
|
||||
selectedBookingChangeIncidentIds,
|
||||
changedUnlockedIncidentIds,
|
||||
changedUnscheduledIncidentIds,
|
||||
changedBookingChangeIncidentIds,
|
||||
inputIdToFocus
|
||||
} = this.state;
|
||||
|
||||
const totalSelected = selectedUnlockedIncidentIds.length + selectedUnscheduledIncidentIds.length + selectedBookingChangeIncidentIds.length;
|
||||
const numberOfSelectedText = totalSelected > 0 ? ` (${totalSelected})` : '';
|
||||
|
||||
const totalChanged =
|
||||
Object.keys(changedUnlockedIncidentIds).length +
|
||||
Object.keys(changedUnscheduledIncidentIds).length +
|
||||
Object.keys(changedBookingChangeIncidentIds).length;
|
||||
const numberOfChangedText = totalChanged > 0 ? ` (${totalChanged})` : '';
|
||||
|
||||
const incidents = this.props.incidents ? this.props.incidents : [];
|
||||
incidents.forEach(incident => {
|
||||
incident.id = `${incident.incidentType}-${incident.incidentId}`;
|
||||
@@ -122,6 +155,44 @@ class SingleIncidentsTable extends Component {
|
||||
break;
|
||||
}
|
||||
|
||||
const priceChangeHandler = (element) => {
|
||||
if (element && element.target){
|
||||
const {value: newValue, id: incidentDescriptionID} = element.target;
|
||||
const newValueFloat = parseFloat(newValue);
|
||||
const incidentData = incidentDescriptionID.split('-');
|
||||
const incidentType = parseInt(incidentData[0]) || null;
|
||||
const incidentID = parseInt(incidentData[1]) || null;
|
||||
|
||||
if ((newValueFloat || (newValueFloat === 0) || (isNaN(newValueFloat))) && incidentType && incidentID){
|
||||
const changedUnlockedIncidentIdsCopy = Object.assign({}, changedUnlockedIncidentIds);
|
||||
const changedUnscheduledIncidentIdsCopy = Object.assign({}, changedUnscheduledIncidentIds);
|
||||
const changedBookingChangeIncidentIdsCopy = Object.assign({}, changedBookingChangeIncidentIds);
|
||||
|
||||
switch (incidentType) {
|
||||
case UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
|
||||
case UNLOCKED_INCIDENT_STANDALONE:
|
||||
changedUnlockedIncidentIdsCopy[incidentID] = newValueFloat;
|
||||
this.setState({changedUnlockedIncidentIds: changedUnlockedIncidentIdsCopy, inputIdToFocus: incidentDescriptionID});
|
||||
break;
|
||||
|
||||
case UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
|
||||
case UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
|
||||
case UNSCHEDULED_INCIDENT_STANDALONE:
|
||||
changedUnscheduledIncidentIdsCopy[incidentID] = newValueFloat;
|
||||
this.setState({changedUnscheduledIncidentIds: changedUnscheduledIncidentIdsCopy, inputIdToFocus: incidentDescriptionID});
|
||||
break;
|
||||
|
||||
case BOOKING_MOVED_TO_ANOTHER_DAY:
|
||||
case BOOKING_SHORTENED:
|
||||
case BOOKING_CANCELED_LATE:
|
||||
changedBookingChangeIncidentIdsCopy[incidentID] = newValueFloat;
|
||||
this.setState({changedBookingChangeIncidentIds: changedBookingChangeIncidentIdsCopy, inputIdToFocus: incidentDescriptionID});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tableHeaders.forEach((header) => {
|
||||
const columnTitle = incidentsReportHeaderTitles[header];
|
||||
|
||||
@@ -143,6 +214,10 @@ class SingleIncidentsTable extends Component {
|
||||
Cell: props => {
|
||||
let cellValue = '';
|
||||
let urlValue = undefined;
|
||||
let clickablePrice = undefined;
|
||||
let priceAsNumber = undefined;
|
||||
let priceInputID = undefined;
|
||||
let priceFontColor = 'black';
|
||||
|
||||
switch (props.column.id) {
|
||||
case 'memberName':
|
||||
@@ -198,9 +273,44 @@ class SingleIncidentsTable extends Component {
|
||||
}
|
||||
break;
|
||||
case 'totalChargeFee':
|
||||
const totalFee = (props.row['_original'].incidentPrice || props.value) || 0;
|
||||
const totalFeeFormatted = parseFloat(totalFee).toFixed(2);
|
||||
cellValue = `$ ${totalFeeFormatted}`;
|
||||
clickablePrice = true;
|
||||
let totalFee = 0;
|
||||
|
||||
let changedIncidentsProxy;
|
||||
switch (props.row['_original'].incidentType) {
|
||||
case UNLOCKED_INCIDENT_STANDALONE:
|
||||
case UNLOCKED_INCIDENT_RELATED_WITH_RESERVATION:
|
||||
changedIncidentsProxy = changedUnlockedIncidentIds;
|
||||
break;
|
||||
case UNSCHEDULED_INCIDENT_STANDALONE:
|
||||
case UNSCHEDULED_INCIDENT_BEFORE_RESERVATION:
|
||||
case UNSCHEDULED_INCIDENT_AFTER_RESERVATION:
|
||||
changedIncidentsProxy = changedUnscheduledIncidentIds;
|
||||
break;
|
||||
case BOOKING_CANCELED_LATE:
|
||||
case BOOKING_MOVED_TO_ANOTHER_DAY:
|
||||
case BOOKING_SHORTENED:
|
||||
changedIncidentsProxy = changedBookingChangeIncidentIds;
|
||||
break;
|
||||
}
|
||||
const changedFee = changedIncidentsProxy[props.row['_original'].incidentId];
|
||||
|
||||
if (typeof changedFee === 'number'){
|
||||
// Not undefined, maybe 0 or NaN
|
||||
if (isNaN(changedFee)){
|
||||
totalFee = '';
|
||||
}else{
|
||||
totalFee = changedFee;
|
||||
priceFontColor = 'red';
|
||||
}
|
||||
}else{
|
||||
//Not defined, in this context means it is not changed
|
||||
totalFee = parseFloat((props.row['_original'].incidentPrice || props.value) || 0).toFixed(2);
|
||||
}
|
||||
// const totalFeeFormatted = parseFloat(totalFee).toFixed(2);
|
||||
priceAsNumber = totalFee;
|
||||
priceInputID = `${props.row['_original'].incidentType || ''}-${props.row['_original'].incidentId || ''}`;
|
||||
// cellValue = `$ ${totalFeeFormatted}`;
|
||||
columnContentsAlignment = columnAlignments.right;
|
||||
break;
|
||||
case 'oldReservation':
|
||||
@@ -217,10 +327,22 @@ class SingleIncidentsTable extends Component {
|
||||
cellValue = props.value;
|
||||
}
|
||||
|
||||
if (openMemberSummaryOnMemberClick && urlValue){
|
||||
return <NavLink to={urlValue}>{cellValue}</NavLink>
|
||||
if (clickablePrice){
|
||||
return <p>$ <input
|
||||
id={priceInputID}
|
||||
style={{ color: priceFontColor }}
|
||||
type={'number'}
|
||||
onChange={priceChangeHandler}
|
||||
value={priceAsNumber}
|
||||
autoFocus={priceInputID === inputIdToFocus}
|
||||
/>
|
||||
</p>
|
||||
}else{
|
||||
return <div style={{ textAlign: columnContentsAlignment, whiteSpace: 'pre' }}>{cellValue}</div>
|
||||
if (openMemberSummaryOnMemberClick && urlValue){
|
||||
return <NavLink to={urlValue}>{cellValue}</NavLink>
|
||||
}else{
|
||||
return <div style={{ textAlign: columnContentsAlignment, whiteSpace: 'pre' }}>{cellValue}</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -235,6 +357,9 @@ class SingleIncidentsTable extends Component {
|
||||
{
|
||||
<Button disabled={loading || totalSelected === 0} onClick={this.deleteSelectedFees}>{`Delete selected ${numberOfSelectedText}`}</Button>
|
||||
}
|
||||
{
|
||||
<Button disabled={loading || totalChanged === 0} onClick={this.updateChangedFees}>{`Save changed ${numberOfChangedText}`}</Button>
|
||||
}
|
||||
<br/><br/>
|
||||
{
|
||||
!loading && incidents &&
|
||||
@@ -252,7 +377,8 @@ class SingleIncidentsTable extends Component {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
deleteIncidentsById: (dateRange, incidentsToDelete, memberId) => deleteIncidents(dispatch, {dateRange, incidentsToDelete, memberId})
|
||||
deleteIncidentsById: (dateRange, incidentsToDelete, memberId) => deleteIncidents(dispatch, {dateRange, incidentsToDelete, memberId}),
|
||||
updateIncidentsById: (dateRange, updatedIncidentsData, memberId) => updateIncidentFees(dispatch, {dateRange, updatedIncidentsData, memberId})
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SingleIncidentsTable);
|
||||
|
||||
@@ -103,6 +103,21 @@ export const deleteIncidents = (dispatch, deleteData) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const updateIncidentFees = (dispatch, updateData) => {
|
||||
const pendingAction = updateData.memberId ? FETCH_MEMBER_INCIDENTS_PENDING : FETCH_INCIDENTS_PENDING;
|
||||
const successAction = updateData.memberId ? FETCH_MEMBER_INCIDENTS_SUCCESS : FETCH_INCIDENTS_SUCCESS;
|
||||
const failedAction = updateData.memberId ? FETCH_MEMBER_INCIDENTS_FAILED : FETCH_INCIDENTS_FAILED;
|
||||
|
||||
dispatch({type: pendingAction});
|
||||
API.patch(`/integration/fees`, updateData)
|
||||
.then(response => {
|
||||
dispatch({type: successAction, payload: response.data});
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({type: failedAction, payload: error.response});
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchMembersList = (dispatch) => {
|
||||
dispatch({type: FETCH_MEMBERS_PENDING});
|
||||
API.get('officeRnD/membersList')
|
||||
|
||||
@@ -2,15 +2,25 @@
|
||||
|
||||
const moment = require('moment-timezone');
|
||||
|
||||
const { getMappingsFromDatabase, fetchOffices, fetchResources, saveNewMappingToDatabase, deleteMappingById, updateMappingById } = require('../services/officeRnD/resources');
|
||||
const {
|
||||
getMappingsFromDatabase,
|
||||
fetchOffices,
|
||||
fetchResources,
|
||||
saveNewMappingToDatabase,
|
||||
deleteMappingById,
|
||||
updateMappingById } = require('../services/officeRnD/resources');
|
||||
const { getAllIncidents, getMemberPracticeSummaryReport } = require('../services/integration/reports');
|
||||
const { getMembersFeesForDateRange } = require('../services/integration/invoiceIntegration');
|
||||
const { deleteFeesFromORD, addFeesToORD } = require('../services/officeRnD/fees');
|
||||
const { reformatMembershipsName } = require('../services/officeRnD/memberships');
|
||||
const { checkBookingChanges } = require('../services/integration/checkBookingChange');
|
||||
const { checkIfProcessing } = require('../services/integration/processingStatus');
|
||||
const { deleteUnlockedIncidentsById, deleteUnscheduledIncidentsById } = require('../services/integration/doorLockCharges');
|
||||
const { deleteBookingChangeIncidentsById } = require('../services/integration/bookingChangeCharges');
|
||||
const {
|
||||
deleteUnlockedIncidentsById,
|
||||
deleteUnscheduledIncidentsById,
|
||||
updateUnscheduledIncidentsById,
|
||||
updateUnlockedIncidentsById } = require('../services/integration/doorLockCharges');
|
||||
const { deleteBookingChangeIncidentsById, updateBookingChangeIncidentsById } = require('../services/integration/bookingChangeCharges');
|
||||
|
||||
const { UI_TIMEZONE, DEFAULT_DATE_FORMAT, ALLOW_SENDING_FEES, integrationServiceErrors } = require('../constants/constants');
|
||||
|
||||
@@ -177,7 +187,7 @@ const addFees = (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
const deleteFees= (req, res) => {
|
||||
const deleteFees = (req, res) => {
|
||||
const deleteData = req.body;
|
||||
const dateRange = deleteData.dateRange ? deleteData.dateRange : null;
|
||||
const incidents = deleteData.incidentsToDelete ? deleteData.incidentsToDelete : null;
|
||||
@@ -220,6 +230,49 @@ const deleteFees= (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
const updateFees = (req, res) => {
|
||||
const updateData = req.body;
|
||||
const dateRange = updateData.dateRange ? updateData.dateRange : null;
|
||||
const incidents = updateData.updatedIncidentsData ? updateData.updatedIncidentsData : null;
|
||||
const memberId = updateData.memberId ? updateData.memberId : null;
|
||||
|
||||
const unlockedIncidentFees = incidents.unlockedIncidentFees ? incidents.unlockedIncidentFees : {};
|
||||
const unscheduledIncidentFees = incidents.unscheduledIncidentFees ? incidents.unscheduledIncidentFees : {};
|
||||
const bookingChangeIncidentFees = incidents.bookingChangeIncidentFees ? incidents.bookingChangeIncidentFees : {};
|
||||
|
||||
req.params.startDate = dateRange.startDate ? dateRange.startDate : null;
|
||||
req.params.endDate = dateRange.endDate ? dateRange.endDate : null;
|
||||
|
||||
if (unlockedIncidentFees && unscheduledIncidentFees && bookingChangeIncidentFees){
|
||||
const asyncUpdateActions = [
|
||||
updateUnlockedIncidentsById(unlockedIncidentFees),
|
||||
updateUnscheduledIncidentsById(unscheduledIncidentFees),
|
||||
updateBookingChangeIncidentsById(bookingChangeIncidentFees)
|
||||
];
|
||||
|
||||
Promise.all(asyncUpdateActions)
|
||||
.then(() => {
|
||||
if (memberId){
|
||||
req.params.memberId = memberId;
|
||||
getMemberIncidents(req, res);
|
||||
}else{
|
||||
getAllIncidentsController(req, res);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('Error updating incidents : ', error);
|
||||
res.status(500).send();
|
||||
});
|
||||
}else{
|
||||
if (memberId){
|
||||
req.params.memberId = memberId;
|
||||
getMemberIncidents(req, res);
|
||||
}else{
|
||||
getAllIncidentsController(req, res);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const checkProcessingStatus = (req, res) => {
|
||||
checkIfProcessing()
|
||||
.then((processing) => {
|
||||
@@ -272,5 +325,6 @@ module.exports = {
|
||||
addFees,
|
||||
checkProcessingStatus,
|
||||
getPracticeSummaryReport,
|
||||
deleteFees
|
||||
deleteFees,
|
||||
updateFees
|
||||
};
|
||||
|
||||
@@ -13,7 +13,8 @@ const {
|
||||
addFees,
|
||||
checkProcessingStatus,
|
||||
getPracticeSummaryReport,
|
||||
deleteFees
|
||||
deleteFees,
|
||||
updateFees
|
||||
} = require('../controllers/integration');
|
||||
|
||||
const { calculateDoorLockCharges } = require('../services/integration/doorLockCharges');
|
||||
@@ -36,6 +37,7 @@ router.get('/officeRnD/membersList', fetchMembersList);
|
||||
|
||||
router.post('/integration/addFees', addFees);
|
||||
router.delete('/integration/fees', deleteFees);
|
||||
router.patch('/integration/fees', updateFees);
|
||||
|
||||
router.get('/integration/processing', checkProcessingStatus);
|
||||
|
||||
@@ -44,6 +46,6 @@ router.get('/integration/report/practiceSummary/:year', getPracticeSummaryReport
|
||||
|
||||
|
||||
// temporary route, manually trigger door lock charge calculations
|
||||
router.get('/calculate', (req, res) => { calculateDoorLockCharges(); res.send();});
|
||||
router.get('/calculate', (req, res) => { calculateDoorLockCharges().then(() => res.send('Done')).catch((err) => res.send('Error \r\n', err));});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -283,6 +283,22 @@ const deleteBookingChangeIncidentsById = (incidentIds) => {
|
||||
return db.bookingChangeIncident.update({deleted: true},{where: filters});
|
||||
};
|
||||
|
||||
const updateBookingChangeIncidentsById = (incidentFees) => {
|
||||
const incidentIds = Object.keys(incidentFees);
|
||||
|
||||
const asyncUpdateActions = [];
|
||||
|
||||
incidentIds.forEach((incidentId) => {
|
||||
const newFeeCharge = parseFloat(incidentFees[incidentId]);
|
||||
|
||||
if (newFeeCharge || (newFeeCharge === 0)){
|
||||
asyncUpdateActions.push(db.bookingChangeIncident.update({chargeFee: newFeeCharge}, {where: {id: incidentId}}));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(asyncUpdateActions);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getChargedCanceledReservations,
|
||||
getIncidentsFromChanges,
|
||||
@@ -290,5 +306,6 @@ module.exports = {
|
||||
getShorteningIncidentsForReservationId,
|
||||
getReservationsIncidentsForRemoval,
|
||||
deleteBookingChangeIncidents,
|
||||
deleteBookingChangeIncidentsById
|
||||
deleteBookingChangeIncidentsById,
|
||||
updateBookingChangeIncidentsById
|
||||
};
|
||||
|
||||
@@ -80,6 +80,26 @@ const deleteUnscheduledIncidentsById = (incidentIds) => {
|
||||
return db.unscheduledIncident.update({deleted: true},{where: filters});
|
||||
};
|
||||
|
||||
const updateUnscheduledIncidentsById = (incidentFees) => {
|
||||
const incidentIds = Object.keys(incidentFees);
|
||||
|
||||
const asyncUpdateActions = [];
|
||||
|
||||
incidentIds.forEach((incidentId) => {
|
||||
const newFeeCharge = parseFloat(incidentFees[incidentId]);
|
||||
|
||||
if (newFeeCharge || (newFeeCharge === 0)){
|
||||
asyncUpdateActions.push(db.unscheduledIncident.update({
|
||||
totalChargeFee: newFeeCharge,
|
||||
chargePrice: newFeeCharge,
|
||||
timeIntervalsToCharge: 1
|
||||
}, {where: {id: incidentId}}));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(asyncUpdateActions);
|
||||
};
|
||||
|
||||
const insertUnlockedIncidents = (incidents) => {
|
||||
const asyncJobs = [];
|
||||
incidents.forEach((incident) => {
|
||||
@@ -124,6 +144,22 @@ const deleteUnlockedIncidentsById = (incidentIds) => {
|
||||
return db.unlockedIncident.update({deleted: true},{where: filters});
|
||||
};
|
||||
|
||||
const updateUnlockedIncidentsById = (incidentFees) => {
|
||||
const incidentIds = Object.keys(incidentFees);
|
||||
|
||||
const asyncUpdateActions = [];
|
||||
|
||||
incidentIds.forEach((incidentId) => {
|
||||
const newFeeCharge = parseFloat(incidentFees[incidentId]);
|
||||
|
||||
if (newFeeCharge || (newFeeCharge === 0)){
|
||||
asyncUpdateActions.push(db.unlockedIncident.update({incidentLevelPrice: newFeeCharge}, {where: {id: incidentId}}));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(asyncUpdateActions);
|
||||
};
|
||||
|
||||
const setUnlockedIncidentsLevel = (incidents) => {
|
||||
return new Promise ((resolve, reject) => {
|
||||
const sortingFunction = (incidentA, incidentB) => {
|
||||
@@ -850,5 +886,7 @@ const calculateDoorLockCharges = () => {
|
||||
module.exports = {
|
||||
calculateDoorLockCharges,
|
||||
deleteUnlockedIncidentsById,
|
||||
deleteUnscheduledIncidentsById
|
||||
deleteUnscheduledIncidentsById,
|
||||
updateUnlockedIncidentsById,
|
||||
updateUnscheduledIncidentsById
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user