CE-936 - Add support for managing associations on insert/edit screens, via childRecordList widget

This commit is contained in:
2024-03-18 12:28:23 -05:00
parent 753c224196
commit d1e4091eb4
3 changed files with 120 additions and 45 deletions

View File

@ -137,7 +137,7 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
*******************************************************************************/ *******************************************************************************/
public Builder withCanAddChildRecord(boolean b) public Builder withCanAddChildRecord(boolean b)
{ {
widgetMetaData.withDefaultValue("canAddChildRecord", true); widgetMetaData.withDefaultValue("canAddChildRecord", b);
return (this); return (this);
} }
@ -151,6 +151,17 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
widgetMetaData.withDefaultValue("disabledFieldsForNewChildRecords", new HashSet<>(disabledFieldsForNewChildRecords)); widgetMetaData.withDefaultValue("disabledFieldsForNewChildRecords", new HashSet<>(disabledFieldsForNewChildRecords));
return (this); return (this);
} }
/*******************************************************************************
**
*******************************************************************************/
public Builder withManageAssociationName(String manageAssociationName)
{
widgetMetaData.withDefaultValue("manageAssociationName", manageAssociationName);
return (this);
}
} }
@ -178,17 +189,24 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
maxRows = ValueUtils.getValueAsInteger(input.getWidgetMetaData().getDefaultValues().containsKey("maxRows")); maxRows = ValueUtils.getValueAsInteger(input.getWidgetMetaData().getDefaultValues().containsKey("maxRows"));
} }
//////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// fetch the record that we're getting children for. // // fetch the record that we're getting children for. e.g., the left-side of the join, with the input id //
// e.g., the left-side of the join, with the input id // // but - only try this if we were given an id. note, this widget could be called for on an INSERT screen, where we don't have a record yet //
//////////////////////////////////////////////////////// // but we still want to be able to return all the other data in here that otherwise comes from the widget meta data, join, etc. //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int totalRows = 0;
QRecord primaryRecord = null;
QQueryFilter filter = null;
QueryOutput queryOutput = new QueryOutput(new QueryInput());
if(StringUtils.hasContent(id))
{
GetInput getInput = new GetInput(); GetInput getInput = new GetInput();
getInput.setTableName(join.getLeftTable()); getInput.setTableName(join.getLeftTable());
getInput.setPrimaryKey(id); getInput.setPrimaryKey(id);
GetOutput getOutput = new GetAction().execute(getInput); GetOutput getOutput = new GetAction().execute(getInput);
QRecord record = getOutput.getRecord(); primaryRecord = getOutput.getRecord();
if(record == null) if(primaryRecord == null)
{ {
throw (new QNotFoundException("Could not find " + (leftTable == null ? "" : leftTable.getLabel()) + " with primary key " + id)); throw (new QNotFoundException("Could not find " + (leftTable == null ? "" : leftTable.getLabel()) + " with primary key " + id));
} }
@ -196,10 +214,10 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// set up the query - for the table on the right side of the join // // set up the query - for the table on the right side of the join //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
QQueryFilter filter = new QQueryFilter(); filter = new QQueryFilter();
for(JoinOn joinOn : join.getJoinOns()) for(JoinOn joinOn : join.getJoinOns())
{ {
filter.addCriteria(new QFilterCriteria(joinOn.getRightField(), QCriteriaOperator.EQUALS, List.of(record.getValue(joinOn.getLeftField())))); filter.addCriteria(new QFilterCriteria(joinOn.getRightField(), QCriteriaOperator.EQUALS, List.of(primaryRecord.getValue(joinOn.getLeftField()))));
} }
filter.setOrderBys(join.getOrderBys()); filter.setOrderBys(join.getOrderBys());
filter.setLimit(maxRows); filter.setLimit(maxRows);
@ -209,11 +227,11 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
queryInput.setShouldTranslatePossibleValues(true); queryInput.setShouldTranslatePossibleValues(true);
queryInput.setShouldGenerateDisplayValues(true); queryInput.setShouldGenerateDisplayValues(true);
queryInput.setFilter(filter); queryInput.setFilter(filter);
QueryOutput queryOutput = new QueryAction().execute(queryInput); queryOutput = new QueryAction().execute(queryInput);
QValueFormatter.setBlobValuesToDownloadUrls(rightTable, queryOutput.getRecords()); QValueFormatter.setBlobValuesToDownloadUrls(rightTable, queryOutput.getRecords());
int totalRows = queryOutput.getRecords().size(); totalRows = queryOutput.getRecords().size();
if(maxRows != null && (queryOutput.getRecords().size() == maxRows)) if(maxRows != null && (queryOutput.getRecords().size() == maxRows))
{ {
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
@ -225,6 +243,7 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
countInput.setFilter(filter); countInput.setFilter(filter);
totalRows = new CountAction().execute(countInput).getCount(); totalRows = new CountAction().execute(countInput).getCount();
} }
}
String tablePath = input.getInstance().getTablePath(rightTable.getName()); String tablePath = input.getInstance().getTablePath(rightTable.getName());
String viewAllLink = tablePath == null ? null : (tablePath + "?filter=" + URLEncoder.encode(JsonUtils.toJson(filter), Charset.defaultCharset())); String viewAllLink = tablePath == null ? null : (tablePath + "?filter=" + URLEncoder.encode(JsonUtils.toJson(filter), Charset.defaultCharset()));
@ -239,10 +258,14 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
// new child records must have values from the join-ons // // new child records must have values from the join-ons //
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
Map<String, Serializable> defaultValuesForNewChildRecords = new HashMap<>(); Map<String, Serializable> defaultValuesForNewChildRecords = new HashMap<>();
if(primaryRecord != null)
{
for(JoinOn joinOn : join.getJoinOns()) for(JoinOn joinOn : join.getJoinOns())
{ {
defaultValuesForNewChildRecords.put(joinOn.getRightField(), record.getValue(joinOn.getLeftField())); defaultValuesForNewChildRecords.put(joinOn.getRightField(), primaryRecord.getValue(joinOn.getLeftField()));
} }
}
widgetData.setDefaultValuesForNewChildRecords(defaultValuesForNewChildRecords); widgetData.setDefaultValuesForNewChildRecords(defaultValuesForNewChildRecords);
Map<String, Serializable> widgetValues = input.getWidgetMetaData().getDefaultValues(); Map<String, Serializable> widgetValues = input.getWidgetMetaData().getDefaultValues();
@ -250,6 +273,22 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
{ {
widgetData.setDisabledFieldsForNewChildRecords((Set<String>) widgetValues.get("disabledFieldsForNewChildRecords")); widgetData.setDisabledFieldsForNewChildRecords((Set<String>) widgetValues.get("disabledFieldsForNewChildRecords"));
} }
else
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if there are no disabled fields specified - then normally any fields w/ a default value get implicitly disabled //
// but - if we didn't look-up the primary record, then we'll want to explicit disable fields from joins //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(primaryRecord == null)
{
Set<String> implicitlyDisabledFields = new HashSet<>();
widgetData.setDisabledFieldsForNewChildRecords(implicitlyDisabledFields);
for(JoinOn joinOn : join.getJoinOns())
{
implicitlyDisabledFields.add(joinOn.getRightField());
}
}
}
} }
return (new RenderWidgetOutput(widgetData)); return (new RenderWidgetOutput(widgetData));

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.model.metadata.frontend; package com.kingsrook.qqq.backend.core.model.metadata.frontend;
import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
@ -60,6 +61,7 @@ public class QFrontendWidgetMetaData
protected Map<String, QIcon> icons; protected Map<String, QIcon> icons;
protected Map<String, QHelpContent> helpContent; protected Map<String, QHelpContent> helpContent;
protected Map<String, Serializable> defaultValues;
private final boolean hasPermission; private final boolean hasPermission;
@ -95,6 +97,7 @@ public class QFrontendWidgetMetaData
} }
this.helpContent = widgetMetaData.getHelpContent(); this.helpContent = widgetMetaData.getHelpContent();
this.defaultValues = widgetMetaData.getDefaultValues();
hasPermission = PermissionsHelper.hasWidgetPermission(actionInput, name); hasPermission = PermissionsHelper.hasWidgetPermission(actionInput, name);
} }
@ -274,4 +277,16 @@ public class QFrontendWidgetMetaData
{ {
return helpContent; return helpContent;
} }
/*******************************************************************************
** Getter for defaultValues
**
*******************************************************************************/
public Map<String, Serializable> getDefaultValues()
{
return defaultValues;
}
} }

View File

@ -779,9 +779,30 @@ public class QJavalinImplementation
{ {
String fieldName = formParam.getKey(); String fieldName = formParam.getKey();
List<String> values = formParam.getValue(); List<String> values = formParam.getValue();
String value = values.get(0);
if("associations".equals(fieldName) && StringUtils.hasContent(value))
{
JSONObject associationsJSON = new JSONObject(value);
for(String key : associationsJSON.keySet())
{
JSONArray associatedRecords = associationsJSON.getJSONArray(key);
for(int i = 0; i < associatedRecords.length(); i++)
{
QRecord associatedRecord = new QRecord();
JSONObject recordJSON = associatedRecords.getJSONObject(i);
for(String k : recordJSON.keySet())
{
associatedRecord.withValue(k, ValueUtils.getValueAsString(recordJSON.get(k)));
}
record.withAssociatedRecord(key, associatedRecord);
}
}
continue;
}
if(CollectionUtils.nullSafeHasContents(values)) if(CollectionUtils.nullSafeHasContents(values))
{ {
String value = values.get(0);
if(StringUtils.hasContent(value)) if(StringUtils.hasContent(value))
{ {
record.setValue(fieldName, value); record.setValue(fieldName, value);