From 7ed0bdd7bde04ce823054decf252f347170a712c Mon Sep 17 00:00:00 2001 From: Teo Sarca Date: Wed, 3 May 2017 09:46:12 +0300 Subject: [PATCH] more methods for manipulating ViewRowIdsOrderedSelection https://github.com/metasfresh/metasfresh-webui-api/issues/330 --- .../ui/web/handlingunits/HUEditorRow.java | 30 +++++++ .../handlingunits/HUEditorViewRepository.java | 6 +- .../SqlViewRowIdsOrderedSelectionFactory.java | 88 +++++++++++++++++++ .../web/view/ViewRowIdsOrderedSelection.java | 11 +++ .../ViewRowIdsOrderedSelectionFactory.java | 4 + .../web/view/descriptor/SqlViewBinding.java | 12 ++- .../SqlViewSelectionQueryBuilder.java | 67 ++++++++++++-- 7 files changed, 207 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorRow.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorRow.java index a6de09e32..8b4c61b9f 100644 --- a/src/main/java/de/metas/ui/web/handlingunits/HUEditorRow.java +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorRow.java @@ -2,9 +2,11 @@ import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Supplier; import javax.annotation.Nullable; @@ -20,6 +22,7 @@ import de.metas.adempiere.model.I_M_Product; import de.metas.handlingunits.model.I_M_HU; +import de.metas.handlingunits.model.I_M_HU_Storage; import de.metas.handlingunits.model.X_M_HU; import de.metas.handlingunits.storage.IHUProductStorage; import de.metas.ui.web.exceptions.EntityNotFoundException; @@ -71,6 +74,33 @@ public static final HUEditorRow cast(final IViewRow viewRow) { return (HUEditorRow)viewRow; } + + public static DocumentId rowIdFromM_HU_ID(final int huId) + { + return DocumentId.of(huId); + } + + public static Set rowIdsFromM_HU_IDs(final Collection huIds) + { + return DocumentId.ofIntSet(huIds); + } + + + public static DocumentId rowIdFromM_HU_Storage(final int huId, final int productId) + { + return DocumentId.ofString(I_M_HU_Storage.Table_Name + "_HU" + huId + "_P" + productId); + } + + public static int rowIdToM_HU_ID(final DocumentId rowId) + { + return rowId == null ? -1 : rowId.toInt(); + } + + public static Set rowIdsToM_HU_IDs(final Collection rowIds) + { + return DocumentId.toIntSet(rowIds); + } + private final DocumentPath documentPath; private final DocumentId rowId; diff --git a/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java index f00b638a0..cadb8e1ac 100644 --- a/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java +++ b/src/main/java/de/metas/ui/web/handlingunits/HUEditorViewRepository.java @@ -21,7 +21,6 @@ import de.metas.handlingunits.IHandlingUnitsDAO; import de.metas.handlingunits.exceptions.HUException; import de.metas.handlingunits.model.I_M_HU; -import de.metas.handlingunits.model.I_M_HU_Storage; import de.metas.handlingunits.model.X_M_HU; import de.metas.handlingunits.model.X_M_HU_PI_Version; import de.metas.handlingunits.storage.IHUProductStorage; @@ -31,7 +30,6 @@ import de.metas.logging.LogManager; import de.metas.ui.web.handlingunits.util.HUPackingInfoFormatter; import de.metas.ui.web.handlingunits.util.HUPackingInfos; -import de.metas.ui.web.window.datatypes.DocumentId; import de.metas.ui.web.window.datatypes.WindowId; import de.metas.ui.web.window.datatypes.json.JSONLookupValue; import lombok.Builder; @@ -148,7 +146,7 @@ private HUEditorRow createHUEditorRow(final I_M_HU hu) final int huId = hu.getM_HU_ID(); final HUEditorRow.Builder huEditorRow = HUEditorRow.builder(windowId) - .setRowId(DocumentId.of(huId)) + .setRowId(HUEditorRow.rowIdFromM_HU_ID(huId)) .setType(huRecordType) .setAttributesProvider(attributesProvider) .setProcessed(processed) @@ -275,7 +273,7 @@ private HUEditorRow createHUEditorRow(final int parent_HU_ID, final IHUProductSt final HUEditorRowAttributesProvider attributesProviderEffective = huId != parent_HU_ID ? attributesProvider : null; return HUEditorRow.builder(windowId) - .setRowId(DocumentId.ofString(I_M_HU_Storage.Table_Name + "_HU" + huId + "_P" + product.getM_Product_ID())) + .setRowId(HUEditorRow.rowIdFromM_HU_Storage(huId, product.getM_Product_ID())) .setType(HUEditorRowType.HUStorage) .setProcessed(processed) .setAttributesProvider(attributesProviderEffective) diff --git a/src/main/java/de/metas/ui/web/view/SqlViewRowIdsOrderedSelectionFactory.java b/src/main/java/de/metas/ui/web/view/SqlViewRowIdsOrderedSelectionFactory.java index d1e4e15d1..bc772bf88 100644 --- a/src/main/java/de/metas/ui/web/view/SqlViewRowIdsOrderedSelectionFactory.java +++ b/src/main/java/de/metas/ui/web/view/SqlViewRowIdsOrderedSelectionFactory.java @@ -124,4 +124,92 @@ public ViewRowIdsOrderedSelection createOrderedSelectionFromSelection(final View .setQueryLimit(fromSelection.getQueryLimit()) .build(); } + + @Override + public ViewRowIdsOrderedSelection addRowIdsToSelection(final ViewRowIdsOrderedSelection selection, final Collection rowIds) + { + if (rowIds == null || rowIds.isEmpty()) + { + // nothing changed + return selection; + } + + // + // Add + boolean hasChanges = false; + final String selectionId = selection.getSelectionId(); + for(final DocumentId rowId : rowIds) + { + final List sqlParams = new ArrayList<>(); + final String sqlAdd = newSqlViewSelectionQueryBuilder().buildSqlAddRowIdsFromSelection(sqlParams, selectionId, rowId); + final int added = DB.executeUpdateEx(sqlAdd, sqlParams.toArray(), ITrx.TRXNAME_ThreadInherited); + if (added <= 0) + { + continue; + } + + hasChanges = true; + } + if(!hasChanges) + { + // nothing changed + return selection; + } + + // + // Retrieve current size + // NOTE: we are querying it instead of adding how many we added to current "size" because it might be that the size is staled + final int size = retrieveSize(selectionId); + + return selection.toBuilder() + .setSize(size) + .build(); + } + + @Override + public ViewRowIdsOrderedSelection removeRowIdsFromSelection(final ViewRowIdsOrderedSelection selection, final Collection rowIds) + { + if (rowIds == null || rowIds.isEmpty()) + { + // nothing changed + return selection; + } + + // + // Delete + { + final String sqlDelete = newSqlViewSelectionQueryBuilder().buildSqlDeleteRowIdsFromSelection(selection.getSelectionId(), rowIds); + final int deleted = DB.executeUpdateEx(sqlDelete, ITrx.TRXNAME_ThreadInherited); + if (deleted <= 0) + { + // nothing changed + return selection; + } + } + + // + // Retrieve current size + // NOTE: we are querying it instead of subtracting "deleted" from current "size" because it might be that the size is staled + final int size = retrieveSize(selection.getSelectionId()); + + return selection.toBuilder() + .setSize(size) + .build(); + } + + private final int retrieveSize(final String selectionId) + { + final List sqlParams = new ArrayList<>(); + final String sqlCount = newSqlViewSelectionQueryBuilder().buildSqlRetrieveSize(sqlParams, selectionId); + final int size = DB.getSQLValueEx(ITrx.TRXNAME_ThreadInherited, sqlCount, sqlParams); + return size <= 0 ? 0 : size; + } + + public boolean containsAnyOfRowIds(ViewRowIdsOrderedSelection defaultSelection, final Collection rowIds) + { + final List sqlParams = new ArrayList<>(); + final String sqlCount = newSqlViewSelectionQueryBuilder().buildSqlCount(sqlParams, defaultSelection.getSelectionId(), rowIds); + int count = DB.executeUpdateEx(sqlCount, sqlParams.toArray(), ITrx.TRXNAME_ThreadInherited); + return count > 0; + } } diff --git a/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelection.java b/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelection.java index 05a84198b..4ed757487 100644 --- a/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelection.java +++ b/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelection.java @@ -11,6 +11,7 @@ import de.metas.ui.web.window.datatypes.WindowId; import de.metas.ui.web.window.model.DocumentQueryOrderBy; +import lombok.EqualsAndHashCode; /* * #%L @@ -35,6 +36,7 @@ */ @Immutable +@EqualsAndHashCode public final class ViewRowIdsOrderedSelection { public static final Builder builder() @@ -70,6 +72,15 @@ public String toString() .add("orderBys", orderBys.isEmpty() ? null : orderBys) .toString(); } + + public Builder toBuilder() + { + return builder() + .setViewId(viewId) + .setSize(size) + .setOrderBys(orderBys) + .setQueryLimit(queryLimit); + } public ViewId getViewId() { diff --git a/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelectionFactory.java b/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelectionFactory.java index 41da62cec..0e8c1eb02 100644 --- a/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelectionFactory.java +++ b/src/main/java/de/metas/ui/web/view/ViewRowIdsOrderedSelectionFactory.java @@ -37,6 +37,10 @@ public interface ViewRowIdsOrderedSelectionFactory ViewRowIdsOrderedSelection createOrderedSelectionFromSelection(ViewEvaluationCtx viewEvalCtx, ViewRowIdsOrderedSelection fromSelection, List orderBys); String getSqlWhereClause(ViewId viewId, Collection rowIds); + + ViewRowIdsOrderedSelection addRowIdsToSelection(ViewRowIdsOrderedSelection selection, Collection rowIds); + + ViewRowIdsOrderedSelection removeRowIdsFromSelection(ViewRowIdsOrderedSelection selection, Collection rowIds); } diff --git a/src/main/java/de/metas/ui/web/view/descriptor/SqlViewBinding.java b/src/main/java/de/metas/ui/web/view/descriptor/SqlViewBinding.java index ed88f8746..56dbda8a3 100644 --- a/src/main/java/de/metas/ui/web/view/descriptor/SqlViewBinding.java +++ b/src/main/java/de/metas/ui/web/view/descriptor/SqlViewBinding.java @@ -9,6 +9,7 @@ import org.adempiere.ad.expression.api.IExpressionEvaluator.OnVariableNotFound; import org.adempiere.ad.expression.api.IStringExpression; import org.adempiere.ad.expression.api.impl.CompositeStringExpression; +import org.adempiere.ad.expression.api.impl.ConstantStringExpression; import org.adempiere.util.Check; import com.google.common.base.Joiner; @@ -286,7 +287,7 @@ public static final class Builder { private String _sqlTableName; private String _tableAlias; - private IStringExpression sqlWhereClause; + private IStringExpression sqlWhereClause = IStringExpression.NULL; private Collection displayFieldNames; private final Map _fieldsByFieldName = new LinkedHashMap<>(); @@ -333,9 +334,16 @@ private String getTableAlias() public Builder setSqlWhereClause(final IStringExpression sqlWhereClause) { - this.sqlWhereClause = sqlWhereClause; + this.sqlWhereClause = sqlWhereClause == null ? IStringExpression.NULL : sqlWhereClause; return this; } + + public Builder setSqlWhereClause(final String sqlWhereClause) + { + this.sqlWhereClause = ConstantStringExpression.ofNullable(sqlWhereClause); + return this; + } + private IStringExpression getSqlWhereClause() { diff --git a/src/main/java/de/metas/ui/web/view/descriptor/SqlViewSelectionQueryBuilder.java b/src/main/java/de/metas/ui/web/view/descriptor/SqlViewSelectionQueryBuilder.java index d72d2f942..08903e31b 100644 --- a/src/main/java/de/metas/ui/web/view/descriptor/SqlViewSelectionQueryBuilder.java +++ b/src/main/java/de/metas/ui/web/view/descriptor/SqlViewSelectionQueryBuilder.java @@ -53,6 +53,7 @@ * * @see I_T_WEBUI_ViewSelection */ +// TODO: used only in one place.. consider removing it from here public final class SqlViewSelectionQueryBuilder { public static final SqlViewSelectionQueryBuilder newInstance(final SqlEntityBinding entityBinding) @@ -81,7 +82,7 @@ public String getKeyColumnName() { return entityBinding.getKeyColumnName(); } - + public IStringExpression getSqlWhereClause() { return entityBinding.getSqlWhereClause(); @@ -142,7 +143,7 @@ public String buildSqlCreateSelectionFrom( // final List sqlWhereClauseParams = new ArrayList<>(); final IStringExpression sqlWhereClause = buildSqlWhereClause(sqlWhereClauseParams, filters); - if(sqlWhereClause != null && !sqlWhereClause.isNullExpression()) + if (sqlWhereClause != null && !sqlWhereClause.isNullExpression()) { sqlBuilder.append("\n AND (\n").append(sqlWhereClause).append("\n)"); sqlParams.addAll(sqlWhereClauseParams); @@ -162,7 +163,7 @@ public String buildSqlCreateSelectionFrom( // final String sql = sqlBuilder.build().evaluate(viewEvalCtx.toEvaluatee(), OnVariableNotFound.Fail); return sql; } - + private final IStringExpression buildSqlWhereClause(final List sqlParams, final List filters) { final CompositeStringExpression.Builder sqlWhereClauseBuilder = IStringExpression.composer(); @@ -177,14 +178,14 @@ private final IStringExpression buildSqlWhereClause(final List sqlParams sqlWhereClauseBuilder.append(" /* entity where clause */ (").append(entityWhereClauseExpression).append(")"); } } - + // // Document filters { final String sqlFilters = SqlDocumentFiltersBuilder.newInstance(entityBinding) .addFilters(filters) .buildSqlWhereClause(sqlParams); - if(!Check.isEmpty(sqlFilters, true)) + if (!Check.isEmpty(sqlFilters, true)) { sqlWhereClauseBuilder.appendIfNotEmpty("\n AND "); sqlWhereClauseBuilder.append(" /* filters */ (\n").append(sqlFilters).append(")\n"); @@ -254,4 +255,60 @@ public String buildSqlWhereClause(final String selectionId, final Collection rowIds) + { + final Set rowIdsAsInts = DocumentId.toIntSet(rowIds); + if (rowIdsAsInts.isEmpty()) + { + return null; + } + + return "DELETE FROM " + I_T_WEBUI_ViewSelection.Table_Name + + " WHERE " + I_T_WEBUI_ViewSelection.COLUMNNAME_UUID + "=" + DB.TO_STRING(selectionId) + + " AND " + I_T_WEBUI_ViewSelection.COLUMNNAME_Record_ID + " IN " + DB.buildSqlList(rowIdsAsInts); + } + + public String buildSqlAddRowIdsFromSelection(final List sqlParams, final String selectionId, final DocumentId rowId) + { + final int rowIdInt = rowId.toInt(); + final String sql = "INSERT INTO " + I_T_WEBUI_ViewSelection.Table_Name + " (" + + " " + I_T_WEBUI_ViewSelection.COLUMNNAME_UUID + + ", " + I_T_WEBUI_ViewSelection.COLUMNNAME_Line + + ", " + I_T_WEBUI_ViewSelection.COLUMNNAME_Record_ID + + ")" + + " SELECT " + + " ? as UUID " // UUID + + ", (select max(Line) from T_WEBUI_ViewSelection z where z.UUID=?) as Line" // Line + + ", ? as Record_ID" // Record_ID + + " WHERE " + + " NOT EXISTS(select 1 from " + I_T_WEBUI_ViewSelection.Table_Name + " z where z.UUID=? and z.Record_ID=?)"; + // TODO: we should also validate if the rowId is allowed to be part of this selection (e.g. enforce entity binding's SQL where clause) + + sqlParams.add(selectionId); // UUID + sqlParams.add(selectionId); // for Line + sqlParams.add(rowIdInt); // Record_ID + sqlParams.add(selectionId); // for NOT EXISTS + sqlParams.add(rowIdInt); // for NOT EXISTS + + return sql; + } + + public String buildSqlRetrieveSize(final List sqlParams, final String selectionId) + { + Check.assumeNotEmpty(selectionId, "selectionId is not empty"); + sqlParams.add(selectionId); + return "SELECT COUNT(1) FROM " + I_T_WEBUI_ViewSelection.Table_Name + " WHERE " + I_T_WEBUI_ViewSelection.COLUMNNAME_UUID + "=?"; + } + + public String buildSqlCount(final List sqlParams, final String selectionId, final Collection rowIds) + { + Check.assumeNotEmpty(selectionId, "selectionId is not empty"); + final Set recordIds = DocumentId.toIntSet(rowIds); + Check.assumeNotEmpty(recordIds, "recordIds is not empty"); + + sqlParams.add(selectionId); + return "SELECT COUNT(1) FROM " + I_T_WEBUI_ViewSelection.Table_Name + " WHERE " + I_T_WEBUI_ViewSelection.COLUMNNAME_UUID + "=?" + + " AND " + DB.buildSqlList(I_T_WEBUI_ViewSelection.COLUMNNAME_Record_ID, rowIds, sqlParams); + } + }