Task
Add the following Actions:
- Context Car:
- Reserve a selected car (PreviewView)
- Context CarDriver:
- Reserve a car for a selected driver (PreviewView)
- Context CarReservation:
- Return selected car(s) (set ENDDATE as current date) (FilterView)
Here you can find a generic description of Actions and ActionGroups.
Solution
Reserve a selected car
In Car_entity, create an Action "reserveCar", titled "Reserve this car", and add the following onActionProcess (leaving all other properties in default value):
import { neon, neonFilter, vars } from "@aditosoftware/jdito-types";
var params = {};
var carId = vars.get("$field.CARID");
if (carId) {
params = {
"CarId_param": carId
};
}
var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters(params).toString();
neon.openContextWithRecipe("CarReservation", null, recipe, neon.OPERATINGSTATE_NEW);
Car_entity.reserveCar.onActionProcess
In CarReservation_entity, create a Parameter named "CarId_param" and set its property "exposed" to "true".
Furthermore, create a valueProcess for EntityField CAR_ID as follows:
import { neon, result, vars } from "@aditosoftware/jdito-types";
if (vars.get("$this.value") == null && neon.OPERATINGSTATE_NEW == vars.get("$sys.recordstate"))
{
let carId = vars.get("$param.CarId_param");
if (carId)
{
result.string(carId);
}
}
CarReservation_entity.CAR_ID.valueProcess
If you execute Action "reserveCar" (via the three-dotted button in CarPreview_view), CarReservationEdit_view is opened, with the CAR_ID UID preselected. We can now optimize this by adding a displayValueProcess for CAR_ID:
import { result, vars } from "@aditosoftware/jdito-types";
import { newSelect } from "SqlBuilder_lib";
let carId = vars.get("$field.CAR_ID");
if (carId) {
var res = newSelect("CONCAT(CAR.MANUFACTURER, ' ', CAR.TYPE)")
.from("CAR")
.whereIfSet("CAR.CARID", carId)
.cell();
result.string(res);
}
CarReservation_entity.CAR_ID.displayValueProcess
Now, after executing the Action, CarReservationEdit_view is opened, with manufacturer and type preselected.


Reserve a car for a selected driver
In CarDriver_entity, create an Action "reserveCar", titled "Reserve a car for this driver", and add the following onActionProcess (leaving all other properties in default value):
import { neon, neonFilter, vars } from "@aditosoftware/jdito-types";
var params = {};
var carDriverId = vars.get("$field.CARDRIVERID");
if (carDriverId) {
params = {
"CarDriverId_param": carDriverId
};
}
var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters(params).toString();
neon.openContextWithRecipe("CarReservation", null, recipe, neon.OPERATINGSTATE_NEW);
CarDriver_entity.reserveCar.onActionProcess
In CarReservation_entity, create a Parameter named "CarDriverId_param" and set its property "exposed" to "true".
Furthermore, create a valueProcess for EntityField CARDRIVER_ID as follows:
import { neon, result, vars } from "@aditosoftware/jdito-types";
if (vars.get("$this.value") == null && neon.OPERATINGSTATE_NEW == vars.get("$sys.recordstate"))
{
let carDriverId = vars.get("$param.CarDriverId_param");
if (carDriverId)
{
result.string(carDriverId);
}
}
CarReservation_entity.CARDRIVER_ID.valueProcess
If you execute Action "reserveCar" (via the three-dotted button in CarDriverPreview_view), CarReservationEdit_view is opened, with the CARDRIVER_ID UID preselected. We can now optimize this by adding a displayValueProcess for CARDRIVER_ID:
import { result, vars } from "@aditosoftware/jdito-types";
import { newSelect } from "SqlBuilder_lib";
import { PersUtils } from "Person_lib";
let carDriverId = vars.get("$field.CARDRIVER_ID");
if (carDriverId)
{
var contactId = newSelect("CARDRIVER.CONTACT_ID")
.from("CARDRIVER")
.whereIfSet("CARDRIVER.CARDRIVERID", carDriverId)
.cell();
if (contactId)
{
result.string(PersUtils.getResolvingDisplayValue(contactId));
}
}
CarReservation_entity.CAR_ID.displayValueProcess
Now, after executing the Action, CarReservationEdit_view is opened, with the displayValue of CONTACT_ID preselected (salutation, firstname, and lastname).
Return selected cars
There are multiple options of how to react to invalid selections, i.e., to selections of future reservations, where setting the ENDDATE with the current date makes no sense.
Option 1: Show error message and abort
In CarReservation_entity, create an Action named "returnCars" that overwrites the existing value of ENDDATE with the current date. However, if at least 1 future reservation has been selected, the process should be aborted without any changes, and an error message pops up.
Set the Action's properties as follows:
title: "Return selected cars"selectionType: MULTIonActionProcessas follows:
import { datetime, neon, question, vars } from "@aditosoftware/jdito-types";
import { newWhere, newSelect, SqlBuilder } from "SqlBuilder_lib";
var selected = vars.get("$sys.selection");
// first check if future reservation(s) have been selected
var count = newSelect("count(*)")
.from("CARRESERVATION")
.where("CARRESERVATION.CARRESERVATIONID", selected, SqlBuilder.IN())
.and("CARRESERVATION.STARTDATE > CURRENT_DATE")
.cell(true, "0");
if (count > 0)
{
question.showMessage("At least one selected car has a start date in the future.")
} else
{
newWhere("CARRESERVATION.CARRESERVATIONID", selected, SqlBuilder.IN())
.updateFields({ "ENDDATE": datetime.date() });
neon.refreshAll();
}
CarReservation_entity.Actions.returnCars.onActionProcess
As the Action should appear in the CarReservationFilter_view, we need to reference it in its ViewTemplates "Table" and "TreeTable". These ViewTemplates have no properties for single Actions, but only for ActionGroups. Therefore, we create an ActionGroup named "CarHandlingActionGroup" and subsequently drag & drop our Action into this new ActionGroup. Now we can set the ActionGroup in the ViewTemplates' properties favoriteActionGroup1.

In the client, we will now see something like this:

Option 2: Disable the Action button
Alternatively, we could disable the Action button in case the selection includes at least one dataset that has a STARTDATE in the future. For this purpose, we use the Action's stateProcess:
import { datetime, neon, question, result, vars } from "@aditosoftware/jdito-types";
import { newSelect, SqlBuilder } from "SqlBuilder_lib";
var selected = vars.get("$sys.selection");
// check if future reservation(s) have been selected
var count = newSelect("count(*)")
.from("CARRESERVATION")
.where("CARRESERVATION.CARRESERVATIONID", selected, SqlBuilder.IN())
.and("CARRESERVATION.STARTDATE > CURRENT_DATE")
.cell(true, "0");
if (count > 0)
{
result.string(neon.COMPONENTSTATE_DISABLED);
}
CarReservation_entity.Actions.CarHandlingActionGroup.returnCars.stateProcess.js
The disadvantage of this solution is that the user does not get feedback on the reason why the button is disabled.
Option 3: Ignore invalid datasets
A third option, which is quite common in practice, would be
- to leave the Action button always enabled (remove the
stateProcessagain) and - to simply ignore all invalid selections, i.e., to ignore all selected future reservations when setting the ENDDATE.
The onActionProcess would then be like this:
import { datetime, neon, vars } from "@aditosoftware/jdito-types";
import { newWhere, SqlBuilder } from "SqlBuilder_lib";
var selected = vars.get("$sys.selection");
// Ignore all future reservations
newWhere("CARRESERVATION.CARRESERVATIONID", selected, SqlBuilder.IN()
.and("CARRESERVATION.STARTDATE <= CURRENT_DATE"))
.updateFields({ "ENDDATE": datetime.date() });
neon.refreshAll();
CarReservation_entity.Actions.CarHandlingActionGroup.returnCars.onActionProcess (alternative)