Projects:Reference Extension/Technical Documentation
Contents |
Data Reference Interface
Openbravo ERP is composed by different systems, each of them makes use of data references in different ways. Below it is specified for each system the usage that is done for references, therefore which is the interface extensible references should implement to be able to interact with these systems.
DAL
- Java type: DAL generates a property for each column, the type for these properties is taken from the reference.
- List values: the DAL uses the ref list values to determine what the allowed values are for a certain type.
- Foreign Key: If the reference is a foreign key, it defines which is the referred table and column.
The DAL has a class: Reference and other Ref* classes in the same package which are used to feed the runtime model with type information.
Interface
public abstract class ReferenceLogic { public enum BaseType { PRIMITIVE, FOREIGN_KEY, ENUMERATION } Class getPrimitiveType(String idRef); Column getFK (String idRef, Column col); Set<Object> getEnumerateValues(); void setReferenceId(String refId); String getReferenceId(); BaseType getBaseType(); }
- getPrimitiveType: Used for primitive objects. It returns the java class that is used by the property. For example Date reference (id 15) would return java.util.Date.class. Non primitive references would return null.
- getFK: For non primitive references it would return the FK column that the main column (col) points to. For example in TableDir reference it would implement the logic to look for the primary of the table named like the column name removing the _ID.
- getEnumerateValues. For enumeration references it returns a list of allowed values. Currently applicable to List reference.
- setReferenceId/getReferenceId. Setter and getter for data type reference. This contains the more specific value in the reference definition (main reference or reference).
- getBaseType. Returns the base type for the reference.
Refactoring
- Add to AD_Reference table the class implementing ReferenceLogic interface for each data type reference.
- In Reference class and Reference.hbm.xml.
- Remove the HashMap<String, Class> primitiveTypes
- Assuming that a classname column is added to the ad_reference table, add the mapping of this column to the Reference.hbm.xml and add a classname member in the Reference class.
- Add the getReferenceLogicInstance() method to the Reference class, this method returns the instance of the class identified by the classname column.
- Change the uses of the static getPrimitiveType to use the getReferenceLogicInstance().getPrimitiveType() method.
- In ModelProvider class.
- In getColumnByReference method (which sets the FK referenced column), remove all logic and call the getFK method in the class that implements the ReferenceLogic. (possibly move the logic from getColumnByReference to the getFK method).
Open discussion items
- MTA: formatting: this topic maybe already covered somewhere else, but should we not consider also formatting definition at reference level, displayformat/editformat. This is a fairly big topic (taking into account localization etc.) so this discussion topic is just to validate if we need to do something here.
Closed discussion items
- MTA: Extra field mandatory rules should be enforced in the main reference tab:
- a base reference should always have the Model Implementation field filled
- a non-base reference always needs to have a parent reference (which must be a base reference) (has been implemented)
ALO: getFK might need read from tables that are not defined in the bootstrap to obtain the referred. Is this feasible?-
MT>> the bootstrap also contains the ad_table/ad_column table, (see how the ModelProvider reads the tables), so afaics all information is available in the bootstrap. -
MT2>> after chatting with Asier, this is the same topic as that the reference adds extra tables to contain reference information.
-
- Allowed values, currently it is only used for List reference. Should we maintain as it is now, or allow new references to implement it in a different way.
- MT>> Yes, in other type systems the 'enum' type is also part of the type system and it is possible to define allowable types. So I would add a getEnumerateValues() to the ReferenceLogic interface.
- MTA: A question is how to actually implement the ReferenceLogic: as an interface or as an abstract class. If we do this as an interface then we can't add new methods without being binary incompatible right away. If we implement it as an abstract class then we have some flexibility in adding new methods (for example isMany: to support types with multiple values), as we can support a default implementation, although even then we can introduce binary incompatibility....
- ALO: abstract class: In this way we can provide default behavior implementation. We might need to add some naming conventions for the methods in classes extending this class to avoid possible clashes with new methods provided by the abstract class.
- MTA: Also the referenceLogic instance would need to know the reference it is defined for (so that it can read its own related tables which refer to the reference). So a method setReferenceId or setReference(Reference reference) is needed I think.
- ALO: A different instance of referenceLogic will be created per each of the records in AD_Reference table that specify an implementation for this class. Each of these instances will have information about its reference and sub reference (reference value). An special case will be the TableDir references, which even it is defined as a single reference, it will create one instance per column that is is used in, the reason of doing so is that it implicitly defines a new subreference per column.
- ICI:I think we should be more exhaustive declaring the interface: I miss getBaseType method (to know if it is a primitive type, enumeration, reference, etc.) or boolean methods with same objective (isPrimitiveType, isEnumeration, isReference)
- ALO: Added this one, other ones could be added during implementation process.
- MTA: Why does the reference instance have a getSubReferenceId/setSubReferenceId method? The subreference will have a foreign key to the ad_reference table, so the implementation can find the subreference using the ad_reference id, isn't it?
- ALO:now only have getter and setter for reference
- MTA: To make it easier/faster for the DAL to initialize the ReferenceLogic instances it can make sense to have an environment/context which reads all tables/columns in memory (as it is now in the ModelProvider) and use that to initialize the ReferenceLogic. This would mean that the ReferenceLogic interface also would need a setModelContext(ModelContext context) (or setReferenceContext(ReferenceContext context)). The ModelContext/ReferenceContext would then provide access to all tables/columns in the system. The getReferenceLogicInstance() would then become getReferenceLogicInstance(ReferenceContext context).
- MTA: I think we don't need to discuss this remark further, let's see what we need in the implementation.
- MTA: A related 'issue' is that a ReferenceLogic implementation would maybe need access to specific tables which its module adds. These tables would need a hbm mapping also. This means that a module should be able to add new hbm mappings to the bootstrap. An idea to support this can be to search in the classpath for new added mapping files. For example say that a module adds a new reference and ReferenceLogic class: org.mygreatorg.newreference.MyReferenceLogic then the system would search for the mapping file: org.mygreatorg.newreference.MyReferenceLogic.hbm.xml in the classpath, and if it is found add it to the bootstrap. This would also mean that there are two bootstraps: one to read all references and referencelogic values and collects the hbm files and the second bootstrap to really initialize the model layer.
- ICI:I think that model loading in memory and bootstrap will finally need to be modified by extensions for sure. IMO both capabilities should be supported from the begining.
- MTA:This topic can be handled in the implementation phase because then we are sure what we need. As we have a good example right away (selector reference) we will get a clear picture what we need during implementing this selector reference.
- ALO: When instanciating class for TableDir reference, it would be enough an instance per column name rather than per column. Is this doable?
- MTA:I think internally (in memory) we have to maybe instantiate one tabledir instance per record in ad_table. So implement a referenceprovider which has a getReference method with a referenceId and an optional Column parameter.
- ICI: I don't like the name ReferenceLogic. Instead I would prefer DALDomainType. This way we start changing our naming (we plan to replace reference by DomainType long time ago) and we state a clearer name.
- [Done] DomainType has been used
- MTA: currently properties validate if a value is correct taking the property type into account. See the method Property.checkIsValidValue(Object value) and the implementations of the PropertyValidator interface in the package org.openbravo.base.validation. Imo the reference should also support this. To support it the interface should be extended with the following methods:
- checkIsValidValue(Property property, Object value) throws ValidationException (ValidationException already exists, note the property parameter is there to know the context in which the value is set).
- checkObjectIsValid(BaseOBObject obObject, Property property). This method is called when an object is saved to the database, so when all the data has been set.
- [Done]
WAD
General WAD Refactoring
The following refactoring is applicable to all components in WAD.
- Add to AD_Reference table the WAD implementer for data references. This implementer must be a subclass of WADControl.
- Change the way WADControl is obtained for different references in WadUtility class (getControl method), currently it is taken regarding the reference's name and looking for a WADControl class in the controls package matching this name. After refactoring it will be obtained using the WAD implementer class defined in AD_Reference.
Action Button
- Combo Reloads See Open Discussion items.
- Parameters visualization. The way parameters are displayed depends on the references. Currently it is using WADControl classes.
Refactoring
In case we decide not to include, by the moment, combo realoads for new references no refactoring would be needed.
Tab Java
- Data type. Used to obtain in a different way parameters from html in the getEditVariables method. (WadActionButton.isNumericType)
- Show actual value. For references storing in DB a value different that the displayed one, 2 fields are generated. This is used to retrieve this info from HTML. (WadActionButton.isSearchType WadActionButton.isSelectType)
- WADControl. The rest of Java elements are obtained from the WADControl implementation for the reference.
Interface
... boolean isNumericType(); boolean has2UIFields(); boolean is2ndUIFieldCalculated();
- isNumericType. Returns true for numeric types, used to retrieve numeric parameters from UI.
- has2UIFields. This would merge current WadActionButton.isSearchType and WadActionButton.isSelectType methods for this case. It would return true for references that store a key in database but this key is not shown to the user. For these cases the reference is composed by 2 fields: the actual value in DB and the displayed one.
- is2ndUIFieldCalculated. Search fields currently have 2 UI fields, the particularity is that the value displayed to user must be calculated in the back-end, not like in combos that as the UI has the id-value association it is enough with passing the id because the displayed value is calculated by the browser.
Refactoring
- Add these 2 new methods to WADControl class and implement them for existent references.
- In Wad class, processTabJava method.
- Replace calls to WadUtility.isNumericType, WadUtility.isSearchType and WadUtility.isSelectType by the new ones in the control implementation.
- When calculating default values for search references, use is2ndUIFieldCalculated method.
Currently there's some hardcoded logic for YesNo reference (id 20), this would be kept as it is now.
Tab XSQL
The following cases are used for generating the SQL queries.
- Identifier. For references that do not show their actual value identifier is calculated as another field in the query. It is needed to know which fields, tables and parameters take part for the SQL. It is calculated by WadUtility.columnIdentifier method.
- SQL Data type. In some cases casting is used (dates...)
- Show actual value. Some references (text, number, dates...) don't require additional queries to be shown, but other ones (FKs, List...) store a value in DB that is a reference, this must be known to compose the query.
Interface
... String columnIdentifier(ConnectionProvider conn, String tableName, boolean required, FieldsData fields, Vector<String> vecCounters, boolean translated, Vector<String> vecFields, Vector<String> vecTable, Vector<Object> vecWhere, Vector<String> vecParameters, Vector<String> vecTableParameters, String sqlDateFormat); String getDefaultValueXSQL();
- columnIdentifier. Adds to the vec* parameters the required information to compose the SQL clause.
- getDefaultValueXSQL. Returns a complete XSQL clause (including parameter) for default values of is2ndUIFieldCalculated references. For this kind of references this method is mandatory.
Refactoring
- Add the described interface to WADControl.
- Remove WadUtility.columnIdentifier method because it is implemented in the WADControl for each reference.
- In Wad.processTabXSQL method.
- Calculating parent info call the columnIdentifier methods in the control.
- Calculating default values for is2ndUIFieldCalculated references use the getDefaultValueXSQL method.
- For actDef use the getDefaultValueXSQL method. ActDef are default values for search type of generated action button parameter pages.
Tab Edition HTML
- WADControl. Used to:
- Show the edition and new modes in html.
- Import JavaScript libraries.
- Import CSSs.
- Is Combo. In case the reference is represented as a combo the display length for the control is multiplied by 7.5 (?).
- Show actual value. When calculating first field focused.
Interface
... String getDisplayedValueFieldName();
- getDisplayedValueFieldName. Obtains the name of the UI field that is displayed to user. It is useful for has2UIFields elements.
Refactoring
Use the getActualValueFieldName method to obtain the first field focused.
Tab Edition XML
- WADControl Only WADControl implementation is used.
- Obtains xml for field.
- Obtains xml for label.
Refactoring
No new interface nor refactoring would be needed in this case.
Grid View
DataGrid class:
- Numeric Display Format. For each of the numeric references a different formatting is applied.
- Image. Old image reference is displayed in the grid.
The following reference uses are done by TableSQLData and ModelSQLGeneration classes. They are used as utility classes by the grid view, but they are also used from WAD generated windows.
- Identifier. For some references (FKs and List) the displayed information is different from the actual value. Used in TableSQLData class.
- SQL Query. When generating the SQL query, each depending on the reference the fields to be retrieved as well as the tables to query are different. This is used in TableSQLData class.
- Filter SQL generation. When there is a filter applied to the grid, ModelSQLGenartion class creates the SQL to apply this filter.
- Number of fields. Some references, as date and numeric ones, define a range in filter rather than a single value.
- SQL type. Each reference defines a different SQL data type which must be casted in a different way when generating the SQL statement.
Interface
boolean isNumericType(); DecimalFormat numericTypeFormat(); boolean has2UIFields(); void setReferenceQuery(); String formatField(); String getGridType(); String formatFilter(String tablename, String columnname, boolean first); boolean filterDefinesRange();
- isNumericType. Determines whether a reference is a numeric value.
- numericTypeFormat. Returns the format to represent numeric values.
- has2UIFields. It is the same as the one defined in the WAD interface.
- setReferenceQuery. Adds the TableSQL properties the needed info to compose the query. This would replace all TableSQLData.set*Query methods used in TableSQLData.identifier method.
- formatField. It does the SQL casting (TO_NUMBER...).
- getGridType. Returns the type of visualization in grid. Current values are string (by default), dynamicEnum, url, img, integer, and float.
- formatFilter. Returns the SQL clause for filtering the reference.
- filterDefinesRange. Returns true in case the reference is a range when it is filtered. Currently numeric values and dates define range, this means in the filter pop up appears 2 fields (one from and the other to).
Refactoring
- In DataGrid class.
- In PrintPageData method, for isNumericType references obtain the numericTypeFormat to do the conversion, replacing in this way the current harcoded implementation.
- In TableSQLData class.
- set*Query methods should be moved from this class to the implementing classes per reference
- identifier method. Call the new setReferenceQuery instead of the set*Query current ones.
- Replace formatField in TableSQLData class by the ones in the reference classes.
- generateSQL method. Use has2UIFields to decide whether an extra field is added to the query.
- getHeaders. Use getGridType.
- In ModelSQLGeneration class.
- getFilter method. Use filterDefinesRange and filterDefinesRange to create the filter query.
Filter pop-up
It is the pop-up that is opened when the filter icon is clicked in the toolbar, it retrieves the fields that are used when the filter is generated by ModelSQLGenartion class. It is implemented by the Buscador class.
- Default value. When the poup-up is opened for a tab for first time, it each of the displayed fields is populated with a default value which depends on its reference.
- Visualization. Which is the HTML representation for each reference and whether it defines a single field or a range.
- Combo reload generation. Combo reloads are generated depending on the referenece. See open discussion items.
Interface
String getFilterDefaultValue(); String getFilterHTML();
- getFilterDefaultValue. Obtains the default value to be used in filter popup.
- getFilterHTML. Composes all the HTML and javascript needed for visualizing the fields in the popup to filter using this reference.
Refactoring
In Buscador class.
- Use getFilterDefaultValue in doPost method.
- In generataHTML method, replace current hardcoded HTML code by calls to the getFilterHTML method.
Linked items
Implemented by UsedByLink class.
- Foreign key. Which is the FK defined by the reference. In UsedByLink_data.xsql a single SQL query obtain all the columns that are foreign keys pointing to the current one.
Open discussion items
- Would be possible to refactor this completely to implement it using DAL? If it is possible to query dal to know the columns that point to a determinate column it would easy it a lot. Otherwise, it would be required to compose the FieldProvider with all the FKs by adding a partial FieldProvider for each reference that define this FK.
Implemented by ReferencedLink and ReferencedTables classes. WAD generates the link which invokes ReferencedLink servlet, in this link it is included the table to navigate to.
In ReferencedLink reference is used just for special cases (Debt Payments).
- Foreing key. Used to determine whether there are different sales/purchases destination windows and in which of these categories is the current record (if aplicacable). In ReferencedTables.
Others
PrintJR
- Data type. It distinguishes numeric parameters to retrieve them differently.
Refacoring
Use numericTypeFormat method defined in grid.
Implementation general details
Java classes
There are 3 different interfaces (abstract classes) to implement:
- Model. Defines data model. It is used by DAL system.
- WAD-BuildTime. Defines the needed interface to generate WAD windows. Implemented by WADControl class.
- UI-RunTime. Defines UI runtime components for current systems such as grid, pop-up filter etc.
Model and UI-RunTime can be implemented as regular classes within src module's directory. But WAD-BuildTime, because of the current dependencies cannot be under this directory. For this case it is needed to allow modules to extend src-wad directory, these classes will be compiled and packaged into openbravo-wad.jar by the wad.lib ant task.
AD Model
AD_Reference table is where references are defined, it will require the addition of the following columns:
- Model, WAD-BuildTime and UI-Runtime. These 3 columns will contain the implementer of each of the defined interfaces. For data references not requiring subreference these 3 columns are mandatory. For references with subreferences, this columns can be set at reference and/or at subreference level. In case the subreference has definition of implementer this will be used, if not it will be used the one in its super reference.
- IsBaseReference. Boolean value, base reference means it is a data reference, when it is not base reference it is possible to define sub references.
- ParentReference. For non base references it is possible to define which is the parent reference. (These two columns will deprecate current validationType column).
General Open Discussion Items
Combo Reloads
Combo reloads are used to automatically reload combo options for combos depending on other fields. They are used in WAD window, in WAD generated parameter windows for report and processes and in filter pop-up. Combo reloads are generated for fields with combo references (TableDir, Table and List) that have a Validation Rule. And are triggered whenever the value of one of the fields participating in the validation rule is changed. Because of this definition validation rules can only be associated to columns using one of the 3 references mentioned above.
Now the question is whether we should allow new references to make use of validation rules, and therefore force them to implement an interface to generate combo reloads; or if combo reloads cannot be used within new references limiting them to the pre-defined ones.
Standalone references
Now it is possible to call directly from menu to the pop-up defined in the Search references. This makes search references usable without associating it to a column in a table. Currently this is the only reference that can be used in this way.
Should we allow to create new references that are callable directly from menu?
- MTA: References should also be usable outside of columns, for example for computed fields in the ui