Skip to main content

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)
tip

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.

Selection PreviewView

Car 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: MULTI
  • onActionProcess as 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.

Action Group

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

Selection FilterView

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 stateProcess again) 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)