diff --git a/migration/iD11/oracle/202404081845_IDEMPIERE-6096.sql b/migration/iD11/oracle/202404081845_IDEMPIERE-6096.sql
new file mode 100644
index 0000000000..0f115e3495
--- /dev/null
+++ b/migration/iD11/oracle/202404081845_IDEMPIERE-6096.sql
@@ -0,0 +1,31 @@
+-- IDEMPIERE-6096 Implement regexp VFormat
+SELECT register_migration_script('202404081845_IDEMPIERE-6096.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Apr 8, 2024, 6:45:20 PM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The entered value does not match the required regular expression: {0}',0,0,'Y',TO_TIMESTAMP('2024-04-08 18:45:20','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-04-08 18:45:20','YYYY-MM-DD HH24:MI:SS'),100,200886,'InvalidFormatRegExp','D','ba94f2c9-ac3e-4efd-a054-fa0b653e7322')
+;
+
+-- Apr 8, 2024, 6:48:11 PM CEST
+UPDATE AD_Element SET Description='Format of the value; Can contain fixed format elements, Variables: "_lLoOaAcCa09", or ~regex', Help='Validation elements:
+
+~regex - Validates a regular expression
+
+ (Space) any character
+_ Space (fixed character)
+l any Letter a..Z NO space
+L any Letter a..Z NO space converted to upper case
+o any Letter a..Z or space
+O any Letter a..Z or space converted to upper case
+a any Letters & Digits NO space
+A any Letters & Digits NO space converted to upper case
+c any Letters & Digits or space
+C any Letters & Digits or space converted to upper case
+0 Digits 0..9 NO space
+9 Digits 0..9 or space
+
+Example of format "(000)_000-0000"',Updated=TO_TIMESTAMP('2024-04-08 18:48:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Element_ID=616
+;
+
diff --git a/migration/iD11/postgresql/202404081845_IDEMPIERE-6096.sql b/migration/iD11/postgresql/202404081845_IDEMPIERE-6096.sql
new file mode 100644
index 0000000000..00111e350c
--- /dev/null
+++ b/migration/iD11/postgresql/202404081845_IDEMPIERE-6096.sql
@@ -0,0 +1,28 @@
+-- IDEMPIERE-6096 Implement regexp VFormat
+SELECT register_migration_script('202404081845_IDEMPIERE-6096.sql') FROM dual;
+
+-- Apr 8, 2024, 6:45:20 PM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The entered value does not match the required regular expression: {0}',0,0,'Y',TO_TIMESTAMP('2024-04-08 18:45:20','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-04-08 18:45:20','YYYY-MM-DD HH24:MI:SS'),100,200886,'InvalidFormatRegExp','D','ba94f2c9-ac3e-4efd-a054-fa0b653e7322')
+;
+
+-- Apr 8, 2024, 6:48:11 PM CEST
+UPDATE AD_Element SET Description='Format of the value; Can contain fixed format elements, Variables: "_lLoOaAcCa09", or ~regex', Help='Validation elements:
+
+~regex - Validates a regular expression
+
+ (Space) any character
+_ Space (fixed character)
+l any Letter a..Z NO space
+L any Letter a..Z NO space converted to upper case
+o any Letter a..Z or space
+O any Letter a..Z or space converted to upper case
+a any Letters & Digits NO space
+A any Letters & Digits NO space converted to upper case
+c any Letters & Digits or space
+C any Letters & Digits or space converted to upper case
+0 Digits 0..9 NO space
+9 Digits 0..9 or space
+
+Example of format "(000)_000-0000"',Updated=TO_TIMESTAMP('2024-04-08 18:48:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Element_ID=616
+;
+
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WStringEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WStringEditor.java
index eadf2ddab2..c8f382cf4c 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WStringEditor.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WStringEditor.java
@@ -35,8 +35,12 @@ import org.adempiere.webui.window.WTextEditorDialog;
import org.compiere.model.GridField;
import org.compiere.model.I_R_MailText;
import org.compiere.util.DisplayType;
+import org.compiere.util.Env;
+import org.compiere.util.Msg;
+import org.compiere.util.Util;
import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
@@ -54,6 +58,7 @@ public class WStringEditor extends WEditor implements ContextMenuListener
private static final String[] LISTENER_EVENTS = {Events.ON_CHANGE, Events.ON_OK};
private String oldValue;
+ private String vFormat = null;
private AbstractADWindowContent adwindowContent;
@@ -93,8 +98,7 @@ public class WStringEditor extends WEditor implements ContextMenuListener
{
super(gridField.isAutocomplete() ? new Combobox() : new Textbox(), gridField, tableEditor, editorConfiguration);
- if (gridField.getVFormat() != null && !gridField.getVFormat().isEmpty())
- getComponent().setWidgetListener("onBind", "jq(this).mask('" + gridField.getVFormat() + "');");
+ vFormat = gridField.getVFormat();
init(gridField.getObscureType());
}
@@ -114,8 +118,7 @@ public class WStringEditor extends WEditor implements ContextMenuListener
{
super(new Textbox(), columnName, null, null, mandatory, isReadOnly,isUpdateable);
- if (wVFormat != null && !wVFormat.isEmpty())
- getComponent().setWidgetListener("onBind", "jq(this).mask('" + wVFormat + "');");
+ vFormat = wVFormat;
init(obscureType);
}
@@ -141,6 +144,9 @@ public class WStringEditor extends WEditor implements ContextMenuListener
*/
private void init(String obscureType)
{
+ if (!Util.isEmpty(vFormat) && !vFormat.startsWith("~"))
+ getComponent().setWidgetListener("onBind", "jq(this).mask('" + vFormat + "');");
+
setChangeEventWhenEditing (true);
if (gridField != null)
{
@@ -211,6 +217,14 @@ public class WStringEditor extends WEditor implements ContextMenuListener
if (!isStartEdit && oldValue == null && newValue == null) {
return;
}
+
+ // Validate VFormat with regular expression
+ if (!Util.isEmpty(vFormat) && vFormat.startsWith("~")) {
+ String regex = gridField.getVFormat().substring(1); // remove the initial ~
+ if (!newValue.matches(regex))
+ throw new WrongValueException(component, Msg.getMsg(Env.getCtx(), "InvalidFormatRegExp", new Object[] {regex}));
+ }
+
ValueChangeEvent changeEvent = new ValueChangeEvent(this, this.getColumnName(), oldValue, newValue);
changeEvent.setIsInitEdit(isStartEdit);