diff --git a/src/main/java/de/metas/ui/web/window/datatypes/json/JSONDocumentLayout.java b/src/main/java/de/metas/ui/web/window/datatypes/json/JSONDocumentLayout.java index 88ca60e0e..9fcc10d60 100644 --- a/src/main/java/de/metas/ui/web/window/datatypes/json/JSONDocumentLayout.java +++ b/src/main/java/de/metas/ui/web/window/datatypes/json/JSONDocumentLayout.java @@ -35,11 +35,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public - * License along with this program. If not, see + * License along with this program. If not, see * . * #L% */ @@ -66,9 +66,10 @@ public static final JSONDocumentLayout ofDetailTab(final DocumentLayoutDetailDes @JsonInclude(Include.NON_NULL) private final String tabid; - @JsonProperty("documentNoElement") - @JsonInclude(Include.NON_NULL) - private final JSONDocumentLayoutElement documentNoElement; + // NOTE: we are no longer using documentNoElement (see https://github.com/metasfresh/metasfresh-webui-api/issues/291 ). + // @JsonProperty("documentNoElement") + // @JsonInclude(Include.NON_NULL) + // private final JSONDocumentLayoutElement documentNoElement; @JsonProperty("documentSummaryElement") @JsonInclude(Include.NON_NULL) @@ -113,7 +114,6 @@ private JSONDocumentLayout(final DocumentLayoutDescriptor layout, final JSONOpti type = String.valueOf(layout.getAD_Window_ID()); tabid = null; - documentNoElement = JSONDocumentLayoutElement.fromNullable(layout.getDocumentNoElement(), jsonOpts); documentSummaryElement = JSONDocumentLayoutElement.fromNullable(layout.getDocumentSummaryElement(), jsonOpts); docActionElement = JSONDocumentLayoutElement.fromNullable(layout.getDocActionElement(), jsonOpts); @@ -168,7 +168,6 @@ private JSONDocumentLayout(final DocumentLayoutDetailDescriptor detailLayout, fi final DetailId detailId = detailLayout.getDetailId(); tabid = DetailId.toJson(detailId); - documentNoElement = null; documentSummaryElement = null; docActionElement = null; @@ -190,7 +189,6 @@ private JSONDocumentLayout(final DocumentLayoutDetailDescriptor detailLayout, fi private JSONDocumentLayout( @JsonProperty("type") final String type// , @JsonProperty("tabid") final String tabId // - , @JsonProperty("documentNoElement") final JSONDocumentLayoutElement documentNoElement// , @JsonProperty("documentSummaryElement") final JSONDocumentLayoutElement documentSummaryElement // , @JsonProperty("docActionElement") final JSONDocumentLayoutElement docActionElement// , @JsonProperty("sections") final List sections // @@ -199,12 +197,11 @@ private JSONDocumentLayout( , @JsonProperty("emptyResultText") final String emptyResultText // , @JsonProperty("emptyResultHint") final String emptyResultHint // - ) + ) { super(); this.type = type; tabid = Strings.emptyToNull(tabId); - this.documentNoElement = documentNoElement; this.documentSummaryElement = documentSummaryElement; this.docActionElement = docActionElement; this.sections = sections == null ? ImmutableList.of() : ImmutableList.copyOf(sections); @@ -237,11 +234,6 @@ public String getTabid() return tabid; } - public JSONDocumentLayoutElement getDocumentNoElement() - { - return documentNoElement; - } - public JSONDocumentLayoutElement getDocumentSummaryElement() { return documentSummaryElement; diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentEntityDescriptor.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentEntityDescriptor.java index 645244bcb..e35848254 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentEntityDescriptor.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentEntityDescriptor.java @@ -531,6 +531,11 @@ public Collection getFieldBuilders() return _fieldBuilders.values(); } + public boolean hasField(final String fieldName) + { + return getFieldBuilder(fieldName) != null; + } + public int getFieldsCount() { return _fieldBuilders.size(); diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDependencyMap.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDependencyMap.java index fd4d9f693..c4dae5f02 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDependencyMap.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDependencyMap.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.function.BiConsumer; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; @@ -50,10 +49,17 @@ public static final Builder builder() public enum DependencyType { - ReadonlyLogic, DisplayLogic, MandatoryLogic, LookupValues, + ReadonlyLogic, DisplayLogic, MandatoryLogic, LookupValues, FieldValue }; + + @FunctionalInterface + public static interface IDependencyConsumer + { + void consume(String dependentFieldName, DependencyType dependencyType); + } - private final Map> type2name2dependencies; + /** Map: "dependency type" to "depends on field name" to list of "dependent field name" */ + private final ImmutableMap> type2name2dependencies; private DocumentFieldDependencyMap(final Builder builder) { @@ -100,22 +106,28 @@ public String toStringX() return sb.toString(); } - public void consumeForChangedFieldName(final String changedFieldName, final BiConsumer consumer) + public void consumeForChangedFieldName(final String changedFieldName, final IDependencyConsumer consumer) { for (final DependencyType dependencyType : DependencyType.values()) { final Multimap name2dependencies = type2name2dependencies.get(dependencyType); - if (name2dependencies == null) + if (name2dependencies == null || name2dependencies.isEmpty()) { continue; } + for (final String dependentFieldName : name2dependencies.get(changedFieldName)) { - consumer.accept(dependentFieldName, dependencyType); + consumer.consume(dependentFieldName, dependencyType); } } } + // + // + // + // + // public static final class Builder { private final Map> type2name2dependencies = new HashMap<>(); @@ -134,7 +146,7 @@ public DocumentFieldDependencyMap build() return new DocumentFieldDependencyMap(this); } - private Map> getType2Name2DependenciesMap() + private ImmutableMap> getType2Name2DependenciesMap() { final ImmutableMap.Builder> builder = ImmutableMap.builder(); for (final Entry> e : type2name2dependencies.entrySet()) diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDescriptor.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDescriptor.java index 5df03bef2..7de835c79 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDescriptor.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldDescriptor.java @@ -41,9 +41,11 @@ import de.metas.ui.web.window.descriptor.DocumentFieldDependencyMap.DependencyType; import de.metas.ui.web.window.descriptor.DocumentLayoutElementFieldDescriptor.LookupSource; import de.metas.ui.web.window.descriptor.LookupDescriptorProvider.LookupScope; +import de.metas.ui.web.window.model.IDocumentFieldValueProvider; import de.metas.ui.web.window.model.lookup.LookupDataSource; import de.metas.ui.web.window.model.lookup.LookupDataSourceFactory; import de.metas.ui.web.window.model.lookup.LookupValueByIdSupplier; +import lombok.NonNull; /* * #%L @@ -87,7 +89,6 @@ public static final Builder builder(final String fieldName) /** Is this the key field ? */ private final boolean key; private final boolean parentLink; - private final boolean virtualField; private final boolean calculated; private final DocumentFieldWidgetType widgetType; @@ -95,9 +96,12 @@ public static final Builder builder(final String fieldName) private final Class valueClass; private final LookupDescriptorProvider lookupDescriptorProvider; + + private final boolean virtualField; + private final Optional virtualFieldValueProvider; private final Optional> defaultValueExpression; - private final List callouts; + private final ImmutableList callouts; public static enum Characteristic { @@ -110,13 +114,13 @@ public static enum Characteristic , SpecialField_DocumentNo // , SpecialField_DocStatus // , SpecialField_DocAction // - , SpecialField_DocumentSummary // +// , SpecialField_DocumentSummary // ; }; private static final List SPECIALFIELDS_ToExcludeFromLayout = ImmutableList.of( - Characteristic.SpecialField_DocumentNo // - , Characteristic.SpecialField_DocStatus // +// Characteristic.SpecialField_DocumentNo // NOP, don't exclude it (see https://github.com/metasfresh/metasfresh-webui-api/issues/291 ) + Characteristic.SpecialField_DocStatus // , Characteristic.SpecialField_DocAction // // , SpecialField_DocumentSummary // NOP, don't exclude DocumentSummary because if it's layout it shall be editable at least when new (e.g. C_BPartner.Name) ); @@ -142,8 +146,7 @@ private DocumentFieldDescriptor(final Builder builder) key = builder.isKey(); parentLink = builder.parentLink; - virtualField = builder.virtualField; - calculated = builder.calculated; + calculated = builder.isCalculated(); widgetType = builder.getWidgetType(); valueClass = builder.getValueClass(); @@ -151,6 +154,9 @@ private DocumentFieldDescriptor(final Builder builder) lookupDescriptorProvider = builder.getLookupDescriptorProvider(); defaultValueExpression = Preconditions.checkNotNull(builder.defaultValueExpression, "defaultValueExpression not null"); + + virtualField = builder.isVirtualField(); + virtualFieldValueProvider = builder.getVirtualFieldValueProvider(); characteristics = Sets.immutableEnumSet(builder.characteristics); readonlyLogic = builder.getReadonlyLogicEffective(); @@ -162,7 +168,7 @@ private DocumentFieldDescriptor(final Builder builder) dependencies = builder.buildDependencies(); - callouts = ImmutableList.copyOf(builder.getCallouts()); + callouts = builder.buildCallouts(); } @Override @@ -212,6 +218,11 @@ public boolean isVirtualField() { return virtualField; } + + public Optional getVirtualFieldValueProvider() + { + return virtualFieldValueProvider; + } public boolean isCalculated() { @@ -603,6 +614,7 @@ public static final class Builder private boolean key = false; private boolean parentLink = false; private boolean virtualField; + private Optional virtualFieldValueProvider = Optional.empty(); private boolean calculated; private DocumentFieldWidgetType _widgetType; @@ -617,7 +629,7 @@ public static final class Builder private ILogicExpression _entityReadonlyLogic = ILogicExpression.FALSE; private ILogicExpression _readonlyLogic = ILogicExpression.FALSE; private ILogicExpression _readonlyLogicEffective = null; - private boolean alwaysUpdateable; + private boolean alwaysUpdateable = false; private ILogicExpression displayLogic = ILogicExpression.TRUE; private ILogicExpression _mandatoryLogic = ILogicExpression.FALSE; private ILogicExpression _mandatoryLogicEffective = null; @@ -772,6 +784,15 @@ public Builder setVirtualField(final boolean virtualField) { assertNotBuilt(); this.virtualField = virtualField; + this.virtualFieldValueProvider = Optional.empty(); + return this; + } + + public Builder setVirtualField(@NonNull final IDocumentFieldValueProvider virtualFieldValueProvider) + { + assertNotBuilt(); + this.virtualField = true; + this.virtualFieldValueProvider = Optional.of(virtualFieldValueProvider); return this; } @@ -779,6 +800,11 @@ public boolean isVirtualField() { return virtualField; } + + private Optional getVirtualFieldValueProvider() + { + return virtualFieldValueProvider; + } public Builder setCalculated(final boolean calculated) { @@ -786,6 +812,15 @@ public Builder setCalculated(final boolean calculated) this.calculated = calculated; return this; } + + private boolean isCalculated() + { + if(isVirtualField()) + { + return true; + } + return calculated; + } public Builder setWidgetType(final DocumentFieldWidgetType widgetType) { @@ -897,7 +932,7 @@ public Builder removeCharacteristic(final Characteristic c) return this; } - public boolean isSpecialField() + public boolean isSpecialFieldToExcludeFromLayout() { return !Collections.disjoint(characteristics, SPECIALFIELDS_ToExcludeFromLayout); } @@ -963,7 +998,7 @@ private ILogicExpression buildReadonlyLogicEffective() return ILogicExpression.TRUE; } - // Case: DocumentNo special field not be readonly + // Case: DocumentNo/Value special field not be readonly if (hasCharacteristic(Characteristic.SpecialField_DocumentNo)) { return LOGICEXPRESSION_NotActive.or(LOGICEXPRESSION_Processed); @@ -1089,11 +1124,6 @@ public Builder setMandatoryLogic(final boolean mandatory) return this; } - public ILogicExpression getMandatoryLogic() - { - return _mandatoryLogic; - } - private ILogicExpression getMandatoryLogicEffective() { if (_mandatoryLogicEffective == null) @@ -1135,7 +1165,7 @@ private final ILogicExpression buildMandatoryLogicEffective() // => we need to NOT enforce setting it because it's not needed, user cannot change it and it might be no callouts to set it. // Else, we won't be able to save our document. final boolean publicField = hasCharacteristic(Characteristic.PublicField); - final ILogicExpression mandatoryLogic = getMandatoryLogic(); + final ILogicExpression mandatoryLogic = _mandatoryLogic; final boolean mandatory = mandatoryLogic.isConstantTrue(); final DocumentFieldDataBindingDescriptor fieldDataBinding = getDataBinding().orElse(null); final boolean mandatoryDB = fieldDataBinding != null && fieldDataBinding.isMandatory(); @@ -1182,6 +1212,12 @@ private DocumentFieldDependencyMap buildDependencies() { dependencyMapBuilder.add(fieldName, lookupDescriptor.getDependsOnFieldNames(), DependencyType.LookupValues); } + + final IDocumentFieldValueProvider virtualFieldValueProvider = getVirtualFieldValueProvider().orElse(null); + if(virtualFieldValueProvider != null) + { + dependencyMapBuilder.add(fieldName, virtualFieldValueProvider.getDependsOnFieldNames(), DependencyType.FieldValue); + } return dependencyMapBuilder.build(); } @@ -1206,9 +1242,9 @@ public Builder addCallout(final ILambdaDocumentFieldCallout lambdaCallout) return this; } - /* package */List getCallouts() + private ImmutableList buildCallouts() { - return callouts; + return ImmutableList.copyOf(callouts); } public Builder setButtonProcessId(final int buttonProcessId) diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldWidgetType.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldWidgetType.java index acfb9ba24..41f5fc722 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldWidgetType.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentFieldWidgetType.java @@ -119,6 +119,11 @@ public final boolean isDateOrTime() return TYPES_Date.contains(this); } + public final boolean isNumeric() + { + return TYPES_Numeric.contains(this); + } + public final boolean isText() { return this == Text || this == LongText; diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutDescriptor.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutDescriptor.java index 7f64b7cf5..7dffa538c 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutDescriptor.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutDescriptor.java @@ -57,8 +57,6 @@ public static final Builder builder() /** i.e. AD_Window_ID */ private final int AD_Window_ID; - /** Special element: DocumentNo */ - private final DocumentLayoutElementDescriptor documentNoElement; /** Special element: Document summary */ private final DocumentLayoutElementDescriptor documentSummaryElement; /** Special element: DocStatus/DocAction */ @@ -82,7 +80,6 @@ private DocumentLayoutDescriptor(final Builder builder) { super(); AD_Window_ID = builder.AD_Window_ID; - documentNoElement = builder.documentNoElement; documentSummaryElement = builder.documentSummaryElement; docActionElement = builder.docActionElement; @@ -118,11 +115,6 @@ public int getAD_Window_ID() return AD_Window_ID; } - public DocumentLayoutElementDescriptor getDocumentNoElement() - { - return documentNoElement; - } - public DocumentLayoutElementDescriptor getDocumentSummaryElement() { return documentSummaryElement; @@ -195,7 +187,6 @@ public static final class Builder private static final Logger logger = LogManager.getLogger(DocumentLayoutDescriptor.Builder.class); private int AD_Window_ID; - private DocumentLayoutElementDescriptor documentNoElement; private DocumentLayoutElementDescriptor documentSummaryElement; private DocumentLayoutElementDescriptor docActionElement; @@ -285,12 +276,6 @@ public Builder setAD_Window_ID(final int AD_Window_ID) return this; } - public Builder setDocumentNoElement(final DocumentLayoutElementDescriptor documentNoElement) - { - this.documentNoElement = documentNoElement; - return this; - } - public Builder setDocumentSummaryElement(DocumentLayoutElementDescriptor documentSummaryElement) { this.documentSummaryElement = documentSummaryElement; diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementDescriptor.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementDescriptor.java index 954049258..c9b1b7519 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementDescriptor.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementDescriptor.java @@ -258,7 +258,7 @@ private boolean checkValid(final DocumentLayoutElementFieldDescriptor.Builder fi return false; } - if (excludeSpecialFields && fieldBuilder.isSpecialField()) + if (excludeSpecialFields && fieldBuilder.isSpecialFieldToExcludeFromLayout()) { logger.trace("Skip adding {} to {} because it's a special field and we were asked to exclude special fields", fieldBuilder, this); return false; diff --git a/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementFieldDescriptor.java b/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementFieldDescriptor.java index 7a709089c..9191bf414 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementFieldDescriptor.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/DocumentLayoutElementFieldDescriptor.java @@ -296,9 +296,9 @@ public Builder trackField(final DocumentFieldDescriptor.Builder field) return this; } - public boolean isSpecialField() + public boolean isSpecialFieldToExcludeFromLayout() { - return documentFieldBuilder != null && documentFieldBuilder.isSpecialField(); + return documentFieldBuilder != null && documentFieldBuilder.isSpecialFieldToExcludeFromLayout(); } public Builder addDevices(final List devices) diff --git a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/DefaultDocumentDescriptorLoader.java b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/DefaultDocumentDescriptorLoader.java index fefc98e9d..c3553d2e9 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/DefaultDocumentDescriptorLoader.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/DefaultDocumentDescriptorLoader.java @@ -88,7 +88,7 @@ public DocumentDescriptor load() // // Layout: Create UI sections from main tab final GridTabVO mainTabVO = gridWindowVO.getTab(GridTabVO.MAIN_TabNo); - final LayoutFactory rootLayoutFactory = new LayoutFactory(gridWindowVO, mainTabVO, (GridTabVO)null); + final LayoutFactory rootLayoutFactory = LayoutFactory.ofMainTab(gridWindowVO, mainTabVO); { layoutBuilder.addSections(rootLayoutFactory.layoutSectionsList()); layoutBuilder.setGridView(rootLayoutFactory.layoutGridView()); @@ -97,7 +97,6 @@ public DocumentDescriptor load() // Set special field names layoutBuilder - .setDocumentNoElement(rootLayoutFactory.createSpecialElement_DocumentNo()) .setDocumentSummaryElement(rootLayoutFactory.createSpecialElement_DocumentSummary()) .setDocActionElement(rootLayoutFactory.createSpecialElement_DocStatusAndDocAction()); } @@ -112,7 +111,7 @@ public DocumentDescriptor load() continue; } - final LayoutFactory detailLayoutFactory = new LayoutFactory(gridWindowVO, detailTabVO, mainTabVO); + final LayoutFactory detailLayoutFactory = LayoutFactory.ofIncludedTab(gridWindowVO, mainTabVO, detailTabVO); final DocumentLayoutDetailDescriptor.Builder layoutDetail = detailLayoutFactory.layoutDetail(); layoutBuilder.addDetailIfValid(layoutDetail); diff --git a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GenericDocumentSummaryValueProvider.java b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GenericDocumentSummaryValueProvider.java new file mode 100644 index 000000000..9f2f8f96b --- /dev/null +++ b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GenericDocumentSummaryValueProvider.java @@ -0,0 +1,349 @@ +package de.metas.ui.web.window.descriptor.factory.standard; + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.adempiere.ad.service.ILookupDAO; +import org.adempiere.ad.service.ILookupDAO.ILookupDisplayInfo; +import org.adempiere.ad.service.ILookupDAO.ITableRefInfo; +import org.adempiere.util.Services; +import org.compiere.model.ILookupDisplayColumn; +import org.compiere.util.DisplayType; +import org.compiere.util.Env; +import org.compiere.util.Language; +import org.slf4j.Logger; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import de.metas.logging.LogManager; +import de.metas.printing.esb.base.util.Check; +import de.metas.ui.web.window.WindowConstants; +import de.metas.ui.web.window.datatypes.LookupValue; +import de.metas.ui.web.window.descriptor.DocumentEntityDescriptor; +import de.metas.ui.web.window.descriptor.DocumentFieldDescriptor; +import de.metas.ui.web.window.descriptor.DocumentFieldWidgetType; +import de.metas.ui.web.window.model.Document; +import de.metas.ui.web.window.model.IDocumentFieldValueProvider; +import lombok.Data; + +/* + * #%L + * metasfresh-webui-api + * %% + * Copyright (C) 2017 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +public class GenericDocumentSummaryValueProvider implements IDocumentFieldValueProvider +{ + public static GenericDocumentSummaryValueProvider of(final DocumentEntityDescriptor.Builder entityDescriptor) + { + List fieldValuesExtractors = extractFieldNamesFromLookup(entityDescriptor); + if (fieldValuesExtractors != null && !fieldValuesExtractors.isEmpty()) + { + return new GenericDocumentSummaryValueProvider(fieldValuesExtractors); + } + + fieldValuesExtractors = extractFieldNamesFromDocumentNo(entityDescriptor); + if (fieldValuesExtractors != null && !fieldValuesExtractors.isEmpty()) + { + return new GenericDocumentSummaryValueProvider(fieldValuesExtractors); + } + + fieldValuesExtractors = extractFieldNamesFromValueName(entityDescriptor); + if (fieldValuesExtractors != null && !fieldValuesExtractors.isEmpty()) + { + return new GenericDocumentSummaryValueProvider(fieldValuesExtractors); + } + + return EMPTY; + } + + private static final List extractFieldNamesFromLookup(final DocumentEntityDescriptor.Builder entityDescriptor) + { + try + { + final String idFieldName = entityDescriptor.getIdFieldName(); + if (idFieldName == null) + { + return ImmutableList.of(); + } + + final ILookupDAO lookupDAO = Services.get(ILookupDAO.class); + final ITableRefInfo tableRefInfo = lookupDAO.retrieveTableDirectRefInfo(idFieldName); + final ILookupDisplayInfo displayInfo = lookupDAO.retrieveLookupDisplayInfo(tableRefInfo); + + final ImmutableList displayColumnNames = displayInfo.getLookupDisplayColumns() + .stream() + .map(lookupDisplayColumn -> createFieldValueExtractorFromLookupDisplayColumn(lookupDisplayColumn, entityDescriptor)) + .filter(fieldValueExtractor -> fieldValueExtractor != null) + .collect(ImmutableList.toImmutableList()); + + return displayColumnNames; + } + catch (final Exception ex) + { + logger.warn("Failed extracting summary field names for record's lookup for {}", entityDescriptor, ex); + return ImmutableList.of(); + } + } + + private static final FieldValueExtractor createFieldValueExtractorFromLookupDisplayColumn(final ILookupDisplayColumn lookupDisplayColumn, final DocumentEntityDescriptor.Builder entityDescriptor) + { + final String fieldName = lookupDisplayColumn.getColumnName(); + final DocumentFieldDescriptor.Builder field = entityDescriptor.getFieldBuilder(fieldName); + if (field == null) + { + return null; + } + + final DocumentFieldWidgetType widgetType = field.getWidgetType(); + if (widgetType.isDateOrTime()) + { + return new DateFieldValueExtractor(fieldName, widgetType); + } + else if (widgetType.isNumeric()) + { + return new NumericFieldValueExtractor(fieldName, widgetType, lookupDisplayColumn.getFormatPattern()); + } + + return new GenericFieldValueExtractor(fieldName); + } + + private static final List extractFieldNamesFromDocumentNo(final DocumentEntityDescriptor.Builder entityDescriptor) + { + if (entityDescriptor.hasField(WindowConstants.FIELDNAME_DocumentNo)) + { + return ImmutableList.of(new GenericFieldValueExtractor(WindowConstants.FIELDNAME_DocumentNo)); + } + return ImmutableList.of(); + } + + private static final List extractFieldNamesFromValueName(final DocumentEntityDescriptor.Builder entityDescriptor) + { + final ImmutableList.Builder fieldNames = ImmutableList.builder(); + if (entityDescriptor.hasField(WindowConstants.FIELDNAME_Value)) + { + fieldNames.add(new GenericFieldValueExtractor(WindowConstants.FIELDNAME_Value)); + } + if (entityDescriptor.hasField(WindowConstants.FIELDNAME_Name)) + { + fieldNames.add(new GenericFieldValueExtractor(WindowConstants.FIELDNAME_Name)); + } + + return fieldNames.build(); + } + + private static final transient Logger logger = LogManager.getLogger(GenericDocumentSummaryValueProvider.class); + private static final GenericDocumentSummaryValueProvider EMPTY = new GenericDocumentSummaryValueProvider(ImmutableList.of()); + + private final ImmutableList fieldValueExtractors; + private final ImmutableSet fieldNames; + + private GenericDocumentSummaryValueProvider(final List fieldValueExtractors) + { + this.fieldValueExtractors = ImmutableList.copyOf(fieldValueExtractors); + fieldNames = this.fieldValueExtractors.stream().map(FieldValueExtractor::getFieldName).collect(ImmutableSet.toImmutableSet()); + } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .addValue(fieldValueExtractors) + .toString(); + } + + @Override + public Set getDependsOnFieldNames() + { + return fieldNames; + } + + @Override + public Object calculateValue(final Document document) + { + if (fieldValueExtractors.isEmpty()) + { + return null; + } + + final String summary = fieldValueExtractors.stream() + .map(valueExtractor -> valueExtractor.extractFieldValueToString(document)) + .map(fieldValue -> Check.isEmpty(fieldValue, true) ? null : fieldValue.trim()) // convert empty strings to null + .filter(fieldValue -> fieldValue != null) // skip null strings + .collect(Collectors.joining("_")); // join all field values + + return summary; + } + + private static interface FieldValueExtractor + { + String getFieldName(); + + String extractFieldValueToString(final Document document); + } + + @Data + private static final class GenericFieldValueExtractor implements FieldValueExtractor + { + private final String fieldName; + + @Override + public String extractFieldValueToString(final Document document) + { + final Object fieldValue = document.getFieldView(fieldName).getValue(); + if (fieldValue == null) + { + return null; + } + else if (fieldValue instanceof LookupValue) + { + return ((LookupValue)fieldValue).getDisplayName(); + } + else + { + return fieldValue.toString(); + } + } + } + + @Data + private static final class DateFieldValueExtractor implements FieldValueExtractor + { + private final String fieldName; + private final DocumentFieldWidgetType widgetType; + + @Override + public String extractFieldValueToString(final Document document) + { + final Object fieldValue = document.getFieldView(fieldName).getValue(); + if (fieldValue == null) + { + return null; + } + + try + { + return getDateFormat().format(fieldValue); + } + catch (final Exception ex) + { + logger.warn("Failed formatting date field value '{}' using {}. Returning toString().", fieldValue, this, ex); + return fieldValue.toString(); + } + } + + private SimpleDateFormat getDateFormat() + { + if (widgetType == DocumentFieldWidgetType.Date) + { + return DisplayType.getDateFormat(DisplayType.Date); + } + else if (widgetType == DocumentFieldWidgetType.DateTime) + { + return DisplayType.getDateFormat(DisplayType.DateTime); + } + if (widgetType == DocumentFieldWidgetType.Time) + { + return DisplayType.getDateFormat(DisplayType.Time); + } + else + { + // unknown format, shall not happen + return null; + } + } + } + + private static final class NumericFieldValueExtractor implements FieldValueExtractor + { + private final String fieldName; + private final String formatPattern; + private int displayType; + + private NumericFieldValueExtractor(final String fieldName, final DocumentFieldWidgetType widgetType, final String formatPattern) + { + this.fieldName = fieldName; + this.formatPattern = Check.isEmpty(formatPattern, true) ? null : formatPattern; + + if (widgetType == DocumentFieldWidgetType.Integer) + { + displayType = DisplayType.Integer; + } + else if (widgetType == DocumentFieldWidgetType.Number) + { + displayType = DisplayType.Number; + } + else if (widgetType == DocumentFieldWidgetType.Amount) + { + displayType = DisplayType.Amount; + } + else if (widgetType == DocumentFieldWidgetType.Quantity) + { + displayType = DisplayType.Quantity; + } + else if (widgetType == DocumentFieldWidgetType.CostPrice) + { + displayType = DisplayType.CostPrice; + } + else + { + // shall not happen + displayType = DisplayType.Number; + } + } + + @Override + public String getFieldName() + { + return fieldName; + } + + @Override + public String extractFieldValueToString(final Document document) + { + final Object fieldValue = document.getFieldView(fieldName).getValue(); + if (fieldValue == null) + { + return null; + } + + try + { + return getDecimalFormat().format(fieldValue); + } + catch (final Exception ex) + { + logger.warn("Failed formatting date field value '{}' using {}. Returning toString().", fieldValue, this, ex); + return fieldValue.toString(); + } + } + + private DecimalFormat getDecimalFormat() + { + final Language language = Env.getLanguage(Env.getCtx()); + return DisplayType.getNumberFormat(displayType, language, formatPattern); + } + } + +} diff --git a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GridTabVOBasedDocumentEntityDescriptorFactory.java b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GridTabVOBasedDocumentEntityDescriptorFactory.java index e4f0b6ec7..3ce5883f9 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GridTabVOBasedDocumentEntityDescriptorFactory.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/GridTabVOBasedDocumentEntityDescriptorFactory.java @@ -16,7 +16,6 @@ import org.adempiere.util.lang.ImmutablePair; import org.compiere.model.GridFieldVO; import org.compiere.model.GridTabVO; -import org.compiere.model.I_C_Order; import org.slf4j.Logger; import de.metas.logging.LogManager; @@ -34,7 +33,7 @@ import de.metas.ui.web.window.descriptor.sql.SqlDocumentFieldDataBindingDescriptor; import de.metas.ui.web.window.descriptor.sql.SqlLookupDescriptor; import de.metas.ui.web.window.model.DocumentsRepository; -import de.metas.ui.web.window.model.ExpressionDocumentFieldCallout; +import de.metas.ui.web.window.model.IDocumentFieldValueProvider; import de.metas.ui.web.window.model.sql.SqlDocumentsRepository; /* @@ -92,20 +91,28 @@ public GridTabVOBasedDocumentEntityDescriptorFactory(final GridTabVO gridTabVO, _documentEntryBuilder = createDocumentEntityBuilder(gridTabVO, parentTabVO, isSOTrx); // - // FIXME: HARDCODED: C_Order's DocumentSummary - if (rootEntity && _documentEntryBuilder.isTableName(I_C_Order.Table_Name)) + // Document summary + if(rootEntity) { - // final IExpression valueProvider = expressionFactory.compile("@DocumentNo@ @DateOrdered@ @GrandTotal@", IStringExpression.class); - final IExpression valueProvider = HARDCODED_OrderDocumentSummaryExpression.instance; - addInternalVirtualField( - WindowConstants.FIELDNAME_DocumentSummary // fieldName - , DocumentFieldWidgetType.Text // widgetType - , String.class // valueType - , true // publicField - , valueProvider // valueProvider - ); + final IDocumentFieldValueProvider summaryValueProvider; +// // FIXME: HARDCODED: C_Order's DocumentSummary +// if (_documentEntryBuilder.isTableName(I_C_Order.Table_Name)) +// { +// summaryValueProvider = HARDCODED_OrderDocumentSummaryValueProvider.instance; +// } +// else + { + summaryValueProvider = GenericDocumentSummaryValueProvider.of(_documentEntryBuilder); + } + + if(summaryValueProvider != null) + { + addInternalVirtualField(WindowConstants.FIELDNAME_DocumentSummary, DocumentFieldWidgetType.Text, summaryValueProvider); + } } + // + collectSpecialFieldsDone(); } public ILogicExpression getTabDisplayLogic() @@ -255,9 +262,9 @@ private final void createAndAddDocumentField(final DocumentEntityDescriptor.Buil { final int displayType = gridFieldVO.getDisplayType(); widgetType = DescriptorsFactoryHelper.extractWidgetType(sqlColumnName, displayType); - if(widgetType.isButton() && gridFieldVO.AD_Process_ID > 0) + if (widgetType.isButton() && gridFieldVO.AD_Process_ID > 0) { - if(WindowConstants.FIELDNAME_DocAction.equals(fieldName) + if (WindowConstants.FIELDNAME_DocAction.equals(fieldName) || WindowConstants.FIELDNAME_Processing.equals(fieldName)) { // FIXME: hardcoded, exclude field when considering ProcessButton widget @@ -274,7 +281,7 @@ private final void createAndAddDocumentField(final DocumentEntityDescriptor.Buil { buttonProcessId = -1; } - + alwaysUpdateable = extractAlwaysUpdateable(gridFieldVO); lookupDescriptorProvider = SqlLookupDescriptor.builder() @@ -387,47 +394,20 @@ else if (!gridFieldVO.isUpdateable() && !gridFieldVO.isParentLink()) public final DocumentFieldDescriptor.Builder addInternalVirtualField( final String fieldName // , final DocumentFieldWidgetType widgetType // - , final Class valueClass // - , final boolean publicField // - , final IExpression valueProvider // + , final IDocumentFieldValueProvider valueProvider // ) { final DocumentEntityDescriptor.Builder documentEntity = documentEntity(); - final ExpressionDocumentFieldCallout callout = ExpressionDocumentFieldCallout.of(fieldName, valueProvider); final DocumentFieldDescriptor.Builder fieldBuilder = DocumentFieldDescriptor.builder(fieldName) - // - .setKey(false) - .setParentLink(false) - // .setWidgetType(widgetType) - .setValueClass(valueClass) - .setVirtualField(true) - .setCalculated(true) - // - // Default value: use our expression - .setDefaultValueExpression(valueProvider) - // - .addCharacteristicIfTrue(publicField, Characteristic.PublicField) - // - // Logics: - .setReadonlyLogic(ILogicExpression.TRUE) // yes, always readonly for outside - .setAlwaysUpdateable(false) - .setMandatoryLogic(ILogicExpression.FALSE) // not mandatory - .setDisplayLogic(ILogicExpression.FALSE) // never display it - // - .setDataBinding(null) // no databinding ! - // - .addCallout(callout); + .setVirtualField(valueProvider) + .setDisplayLogic(false); // never display it because it's internal // // Add Field builder to document entity documentEntity.addField(fieldBuilder); - // - // Add Field's data binding to entity data binding - // entityBindingsBuilder.addField(dataBinding); // no databinding! - // // Collect special field collectSpecialField(fieldBuilder); @@ -511,9 +491,14 @@ private final void collectSpecialField(final DocumentFieldDescriptor.Builder fie _specialFieldsCollector.collect(field); } - public DocumentFieldDescriptor.Builder getSpecialField_DocumentNo() + private final void collectSpecialFieldsDone() { - return _specialFieldsCollector == null ? null : _specialFieldsCollector.getDocumentNo(); + if (_specialFieldsCollector == null) + { + return; + } + + _specialFieldsCollector.collectFinish(); } public DocumentFieldDescriptor.Builder getSpecialField_DocumentSummary() diff --git a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/HARDCODED_OrderDocumentSummaryValueProvider.java b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/HARDCODED_OrderDocumentSummaryValueProvider.java new file mode 100644 index 000000000..d2b56f66d --- /dev/null +++ b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/HARDCODED_OrderDocumentSummaryValueProvider.java @@ -0,0 +1,95 @@ +package de.metas.ui.web.window.descriptor.factory.standard; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Set; + +import org.adempiere.model.InterfaceWrapperHelper; +import org.compiere.model.I_C_BPartner; +import org.compiere.model.I_C_Order; +import org.compiere.util.DisplayType; + +import com.google.common.collect.ImmutableSet; + +import de.metas.ui.web.window.model.Document; +import de.metas.ui.web.window.model.IDocumentFieldValueProvider; + +/* + * #%L + * metasfresh-webui-api + * %% + * Copyright (C) 2016 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +public final class HARDCODED_OrderDocumentSummaryValueProvider implements IDocumentFieldValueProvider +{ + public static final transient HARDCODED_OrderDocumentSummaryValueProvider instance = new HARDCODED_OrderDocumentSummaryValueProvider(); + + private static final Set PARAMETERS = ImmutableSet.of( + I_C_Order.COLUMNNAME_DocumentNo, + I_C_Order.COLUMNNAME_C_BPartner_ID, + I_C_Order.COLUMNNAME_DateOrdered, + I_C_Order.COLUMNNAME_GrandTotal); + + private HARDCODED_OrderDocumentSummaryValueProvider() + { + super(); + } + + @Override + public Set getDependsOnFieldNames() + { + return PARAMETERS; + } + + @Override + public Object calculateValue(final Document document) + { + final I_C_Order order = InterfaceWrapperHelper.create(document, I_C_Order.class); + + final SimpleDateFormat dateFormat = DisplayType.getDateFormat(DisplayType.Date); + final DecimalFormat numberFormat = DisplayType.getNumberFormat(DisplayType.Amount); + + final StringBuilder result = new StringBuilder(); + + final String documentNo = order.getDocumentNo(); + result.append(documentNo); + + final I_C_BPartner bpartner = order.getC_BPartner(); + if (bpartner != null) + { + result.append(" ").append(bpartner.getName()); + } + + final Date dateOrdered = order.getDateOrdered(); + if (dateOrdered != null) + { + result.append(" ").append(dateFormat.format(dateOrdered)); + } + + final BigDecimal grandTotal = order.getGrandTotal(); + if (grandTotal != null) + { + result.append(" ").append(numberFormat.format(grandTotal)); + } + + return result.toString(); + } +} diff --git a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/LayoutFactory.java b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/LayoutFactory.java index ef319efed..6950ff8ae 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/LayoutFactory.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/LayoutFactory.java @@ -68,6 +68,17 @@ public class LayoutFactory { + public static final LayoutFactory ofMainTab(final GridWindowVO gridWindowVO, final GridTabVO mainTabVO) + { + final GridTabVO parentTab = null; // no parent + return new LayoutFactory(gridWindowVO, mainTabVO, parentTab); + } + + public static final LayoutFactory ofIncludedTab(final GridWindowVO gridWindowVO, final GridTabVO mainTabVO, final GridTabVO detailTabVO) + { + return new LayoutFactory(gridWindowVO, detailTabVO, mainTabVO); + } + // services private static final transient Logger logger = LogManager.getLogger(LayoutFactory.class); @Autowired @@ -97,7 +108,7 @@ public class LayoutFactory private IWindowUIElementsProvider _uiProvider; private final List _uiSections; - public LayoutFactory(final GridWindowVO gridWindowVO, final GridTabVO gridTabVO, final GridTabVO parentTab) + private LayoutFactory(final GridWindowVO gridWindowVO, final GridTabVO gridTabVO, final GridTabVO parentTab) { Adempiere.autowire(this); @@ -633,23 +644,6 @@ public DocumentEntityDescriptor.Builder documentEntity() return descriptorsFactory.documentEntity(); } - public DocumentLayoutElementDescriptor createSpecialElement_DocumentNo() - { - final DocumentFieldDescriptor.Builder field = descriptorsFactory.getSpecialField_DocumentNo(); - if (field == null) - { - return null; - } - - return DocumentLayoutElementDescriptor.builder() - .setCaptionNone() // not relevant - .setDescription(null) // not relevant - .setLayoutTypeNone() // not relevant - .setWidgetType(field.getWidgetType()) - .addField(layoutElementField(field)) - .build(); - } - public DocumentLayoutElementDescriptor createSpecialElement_DocumentSummary() { final DocumentFieldDescriptor.Builder field = descriptorsFactory.getSpecialField_DocumentSummary(); diff --git a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/SpecialDocumentFieldsCollector.java b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/SpecialDocumentFieldsCollector.java index e4127b4a1..21be4c7a9 100644 --- a/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/SpecialDocumentFieldsCollector.java +++ b/src/main/java/de/metas/ui/web/window/descriptor/factory/standard/SpecialDocumentFieldsCollector.java @@ -6,6 +6,7 @@ import org.slf4j.Logger; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -43,6 +44,7 @@ private static final Set COLUMNNAMES_DocumentNos = ImmutableSet.of(WindowConstants.FIELDNAME_DocumentNo, WindowConstants.FIELDNAME_Value, WindowConstants.FIELDNAME_Name); private static final Set COLUMNNAMES_DocumentSummary = ImmutableSet.of(WindowConstants.FIELDNAME_DocumentSummary, WindowConstants.FIELDNAME_Name); + /** All column names which might be special fields */ private static final Set COLUMNNAMES = ImmutableSet. builder() .addAll(COLUMNNAMES_DocumentNos) .addAll(COLUMNNAMES_DocumentSummary) @@ -50,10 +52,13 @@ .add(WindowConstants.FIELDNAME_DocAction) .build(); + private boolean allowCollecting = true; private final Map collectedFields = new HashMap<>(); public void collect(final DocumentFieldDescriptor.Builder field) { + Preconditions.checkState(allowCollecting, "allowCollecting shall be true"); + final String fieldName = field.getFieldName(); if (!COLUMNNAMES.contains(fieldName)) { @@ -69,9 +74,14 @@ public void collect(final DocumentFieldDescriptor.Builder field) collectedFields.put(fieldName, field); } - - public DocumentFieldDescriptor.Builder getDocumentNo() + + public void collectFinish() { + Preconditions.checkState(allowCollecting, "allowCollecting shall be true"); + allowCollecting = false; + + // + // Update DocumentNo field flags for (final String fieldName : COLUMNNAMES_DocumentNos) { final DocumentFieldDescriptor.Builder field = collectedFields.get(fieldName); @@ -82,10 +92,8 @@ public DocumentFieldDescriptor.Builder getDocumentNo() field.addCharacteristic(Characteristic.PublicField); field.addCharacteristic(Characteristic.SpecialField_DocumentNo); - return field; + break; // only first field shall be elected as DocumentNo } - - return null; } public DocumentFieldDescriptor.Builder getDocumentSummary() @@ -99,7 +107,7 @@ public DocumentFieldDescriptor.Builder getDocumentSummary() } field.addCharacteristic(Characteristic.PublicField); - field.addCharacteristic(Characteristic.SpecialField_DocumentSummary); + //field.addCharacteristic(Characteristic.SpecialField_DocumentSummary); return field; } diff --git a/src/main/java/de/metas/ui/web/window/model/Document.java b/src/main/java/de/metas/ui/web/window/model/Document.java index 216d269f5..08b72945f 100644 --- a/src/main/java/de/metas/ui/web/window/model/Document.java +++ b/src/main/java/de/metas/ui/web/window/model/Document.java @@ -360,6 +360,7 @@ private final void initializeFields(final FieldInitializationMode mode, final Do } // + // Initializing a new document if (FieldInitializationMode.NewDocument == mode) { // FIXME: i think it would be better to trigger the callouts when setting the initial value @@ -376,16 +377,22 @@ private final void initializeFields(final FieldInitializationMode mode, final Do updateAllFieldsFlags(Execution.getCurrentDocumentChangesCollector()); } + // + // Initializing an existing loaded document else if (FieldInitializationMode.Load == mode) { updateAllFieldsFlags(NullDocumentChangesCollector.instance); } + // + // Document refresh else if (FieldInitializationMode.Refresh == mode) { documentCallout.onRefresh(asCalloutRecord()); updateAllFieldsFlags(Execution.getCurrentDocumentChangesCollectorOrNull()); } + // + // Unknown initialization mode else { throw new IllegalArgumentException("Unknown mode: " + mode); @@ -426,7 +433,7 @@ private void initializeField(final IDocumentField documentField, final FieldInit if (initialValue != DocumentValuesSupplier.NO_VALUE) { valueOld = documentField.getValue(); - documentField.setInitialValue(initialValue, mode); + documentField.setInitialValue(initialValue); } valueSet = true; @@ -826,7 +833,7 @@ public Document getRootDocument() return parent; } - + public boolean isRootDocument() { return getParentDocument() == null; @@ -1232,12 +1239,13 @@ private void updateFieldFlag( , final IDocumentChangesCollector documentChangesCollector // ) { + final ReasonSupplier reason = () -> "TriggeringField=" + triggeringFieldName + ", DependencyType=" + triggeringDependencyType; + if (DependencyType.ReadonlyLogic == triggeringDependencyType) { final LogicExpressionResult valueOld = documentField.getReadonly(); updateFieldReadOnly(documentField); - final ReasonSupplier reason = () -> "TriggeringField=" + triggeringFieldName + ", DependencyType=" + triggeringDependencyType; documentChangesCollector.collectReadonlyIfChanged(documentField, valueOld, reason); } else if (DependencyType.MandatoryLogic == triggeringDependencyType) @@ -1245,7 +1253,6 @@ else if (DependencyType.MandatoryLogic == triggeringDependencyType) final LogicExpressionResult valueOld = documentField.getMandatory(); updateFieldMandatory(documentField); - final ReasonSupplier reason = () -> "TriggeringField=" + triggeringFieldName + ", DependencyType=" + triggeringDependencyType; documentChangesCollector.collectMandatoryIfChanged(documentField, valueOld, reason); } else if (DependencyType.DisplayLogic == triggeringDependencyType) @@ -1253,12 +1260,10 @@ else if (DependencyType.DisplayLogic == triggeringDependencyType) final LogicExpressionResult valueOld = documentField.getDisplayed(); updateFieldDisplayed(documentField); - final ReasonSupplier reason = () -> "TriggeringField=" + triggeringFieldName + ", DependencyType=" + triggeringDependencyType; documentChangesCollector.collectDisplayedIfChanged(documentField, valueOld, reason); } else if (DependencyType.LookupValues == triggeringDependencyType) { - final ReasonSupplier reason = () -> "TriggeringField=" + triggeringFieldName + ", DependencyType=" + triggeringDependencyType; final boolean lookupValuesStaledOld = documentField.isLookupValuesStale(); final boolean lookupValuesStaled = documentField.setLookupValuesStaled(triggeringFieldName); if (lookupValuesStaled && !lookupValuesStaledOld) @@ -1266,6 +1271,28 @@ else if (DependencyType.LookupValues == triggeringDependencyType) documentChangesCollector.collectLookupValuesStaled(documentField, reason); } } + else if(DependencyType.FieldValue == triggeringDependencyType) + { + final IDocumentFieldValueProvider valueProvider = documentField.getDescriptor().getVirtualFieldValueProvider().orElse(null); + if(valueProvider != null) + { + try + { + final Object valueOld = documentField.getValue(); + final Object valueNew = valueProvider.calculateValue(this); + + documentField.setInitialValue(valueNew); + documentField.setValue(valueNew); + documentField.updateValid(); // make sure is still valid + + documentChangesCollector.collectValueIfChanged(documentField, valueOld, reason); + } + catch (Exception ex) + { + logger.warn("Failed updating virtual field {} for {}", documentField, this, ex); + } + } + } else { new AdempiereException("Unknown dependency type: " + triggeringDependencyType) @@ -1489,6 +1516,12 @@ public DocumentValidStatus checkAndGetValidStatus() // Check document fields for (final IDocumentField documentField : getFields()) { + // skip virtual fields, those does not matter + if(documentField.isVirtualField()) + { + continue; + } + final DocumentValidStatus validState = documentField.getValidStatus(); if (!validState.isValid()) { diff --git a/src/main/java/de/metas/ui/web/window/model/DocumentField.java b/src/main/java/de/metas/ui/web/window/model/DocumentField.java index b557498e6..cc95199ab 100644 --- a/src/main/java/de/metas/ui/web/window/model/DocumentField.java +++ b/src/main/java/de/metas/ui/web/window/model/DocumentField.java @@ -70,7 +70,7 @@ private static final LogicExpressionResult DISPLAYED_InitialValue = LogicExpressionResult.namedConstant("displayed-initial", false); private LogicExpressionResult _displayed = DISPLAYED_InitialValue; - private DocumentValidStatus _validStatus = DocumentValidStatus.fieldInitiallyInvalid(); + private DocumentValidStatus _validStatus; /* package */ DocumentField(final DocumentFieldDescriptor descriptor, final Document document) { @@ -165,12 +165,12 @@ public Object getInitialValue() * @param initialValue */ @Override - public void setInitialValue(final Object initialValue, final FieldInitializationMode mode) + public void setInitialValue(final Object initialValue) { final Object initialValueConv = convertToValueClassAndCorrect(initialValue); if (logger.isTraceEnabled()) { - logger.trace("setInitialValue: {} = {} ({})", getFieldName(), initialValueConv, mode); + logger.trace("setInitialValue: {} = {}", getFieldName(), initialValueConv); } _initialValue = initialValueConv; diff --git a/src/main/java/de/metas/ui/web/window/model/ExpressionDocumentFieldCallout.java b/src/main/java/de/metas/ui/web/window/model/ExpressionDocumentFieldCallout.java deleted file mode 100644 index 5cc159fb2..000000000 --- a/src/main/java/de/metas/ui/web/window/model/ExpressionDocumentFieldCallout.java +++ /dev/null @@ -1,118 +0,0 @@ -package de.metas.ui.web.window.model; - -import java.util.Set; - -import org.adempiere.ad.callout.api.ICalloutExecutor; -import org.adempiere.ad.callout.api.ICalloutField; -import org.adempiere.ad.expression.api.IExpression; -import org.adempiere.ad.expression.api.IExpressionEvaluator.OnVariableNotFound; -import org.adempiere.util.Check; -import org.compiere.util.Evaluatee; -import org.slf4j.Logger; - -import com.google.common.base.MoreObjects; - -import de.metas.logging.LogManager; -import de.metas.ui.web.window.descriptor.IDocumentFieldCallout; -import de.metas.ui.web.window.model.IDocumentChangesCollector.ReasonSupplier; - -/* - * #%L - * metasfresh-webui-api - * %% - * Copyright (C) 2016 metas GmbH - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * {@link IDocumentFieldCallout} implementation which computes field's value based on a given {@link IExpression}. - * - * @author metas-dev - * - */ -public final class ExpressionDocumentFieldCallout implements IDocumentFieldCallout -{ - public static final ExpressionDocumentFieldCallout of(final String targetFieldName, final IExpression expression) - { - return new ExpressionDocumentFieldCallout(targetFieldName, expression); - } - - private static final Logger logger = LogManager.getLogger(ExpressionDocumentFieldCallout.class); - - private final String id; - private final String targetFieldName; - private final IExpression expression; - - private ExpressionDocumentFieldCallout(final String targetFieldName, final IExpression expression) - { - super(); - - Check.assumeNotEmpty(targetFieldName, "fieldName is not empty"); - this.targetFieldName = targetFieldName; - - Check.assumeNotNull(expression, "Parameter expression is not null"); - this.expression = expression; - - id = targetFieldName + "-" + expression.toString(); - } - - @Override - public String toString() - { - return MoreObjects.toStringHelper(this) - .add("targetFieldName", targetFieldName) - .add("expression", expression) - .add("id", id) - .toString(); - } - - @Override - public String getId() - { - return id; - } - - @Override - public Set getDependsOnFieldNames() - { - return expression.getParameters(); - } - - @Override - public void execute(final ICalloutExecutor calloutExecutor, final ICalloutField calloutField) throws Exception - { - final Document document = extractDocument(calloutField); - final Evaluatee ctx = document.asEvaluatee(); - - final Object value = expression.evaluate(ctx, OnVariableNotFound.ReturnNoResult); - if (expression.isNoResult(value)) - { - logger.trace("Got no value for {}. Skip setting {}", expression, targetFieldName); - return; - } - - final ReasonSupplier reason = () -> "From expression: " + expression; - document.setValue(targetFieldName, value, reason); - } - - private static final Document extractDocument(final ICalloutField calloutField) - { - final IDocumentField documentField = DocumentFieldAsCalloutField.unwrap(calloutField); - return documentField.getDocument(); - } - -} diff --git a/src/main/java/de/metas/ui/web/window/model/IDocumentField.java b/src/main/java/de/metas/ui/web/window/model/IDocumentField.java index a26e6c3ec..730ebb0e6 100644 --- a/src/main/java/de/metas/ui/web/window/model/IDocumentField.java +++ b/src/main/java/de/metas/ui/web/window/model/IDocumentField.java @@ -46,9 +46,8 @@ default DocumentPath getDocumentPath() /** * @param initialValue initial value / last saved value - * @param mode initialization mode */ - void setInitialValue(Object initialValue, FieldInitializationMode mode); + void setInitialValue(Object initialValue); /** * Set field's current value. diff --git a/src/main/java/de/metas/ui/web/window/model/IDocumentFieldValueProvider.java b/src/main/java/de/metas/ui/web/window/model/IDocumentFieldValueProvider.java new file mode 100644 index 000000000..c602ab046 --- /dev/null +++ b/src/main/java/de/metas/ui/web/window/model/IDocumentFieldValueProvider.java @@ -0,0 +1,32 @@ +package de.metas.ui.web.window.model; + +import java.util.Set; + +/* + * #%L + * metasfresh-webui-api + * %% + * Copyright (C) 2017 metas GmbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +public interface IDocumentFieldValueProvider +{ + Set getDependsOnFieldNames(); + + Object calculateValue(final Document document); +}