Decimals in textfield do not match BigDecimal scale

Hi,
I have several text fields with decimal value.
In the database I have some of them with precision= 19 scale=2 and others with precision=19 scale =3.
I need to present some text fields with 2 decimals and other with 3.
I have read past threads and I see I cannot use message bundle but I should use formatters.
I cannot find formatters in new textfields.
Can you please help me?
Thanks,
Mario

To explain better:

  • field “foo” with scale = 2 must show the value as 123.45
  • field “bar” with scale = 2 must show the value as 123.456 and obviously the user can input 3 decimal digits.

Mario

Hi Mario!

Let me clarify what you mean by “new textfields”?
I’d venture to guess you mean Jmix 2.0, but I’m still not sure.

Regards,
Dmitriy

Sorry sure jmix 2.0.
Actually the only way to put three decimal digits is to use:

decimalFormat = #,##0.000

But then all textfields use that format and I must input three digits even in textfields that needs two.

There is no formatter functionality like in the ClassicUI.

The global formatting parameter for a specific datatype, as you have already written, can be determined using the localization property.
Formatting is applied to the datatype in the following method: io.jmix.flowui.component.textfield.TypedTextField#convertToPresentation.

Currently the only way to add a new format is to define your own datatype and then assigne it to the textField.
You can also use valuePicker component with your own formatter.

Also, you can try using bigDecimalField component. It saves the value to the database with the required format, but the presentation value is not formatted.

I hope some method works for you.

Regards,
Dmitriy

This is a showstopper for me: the customer will refuse the software!
I now try with custom datatype hoping I can solve the situation in some way.

I was able to create a Datatype with formatters as you suggested to me.
But now I have an even worse problem: liquibase wants to delete and recreate all columns marked with:

@PropertyDataType("mycustomdatatype")

And this is not acceptable because I lose customer data in this way.
Please note I have not changed anything on the type definition (it is the same BigDecimal with precision= 19 and scale = 3)

It’s strange that liquidbase created some scripts.
The datatype does not oblige you to have a different datatype in the database.

Can you ignore these scripts and run the application? Will the formatting work as intended?

Yes if I ignore them I can run the application and it works.
The problem is now that I build a docker to install it in production and I need to be 200% sure it will not run any migration that may destroy database.
I can run a test locally but I do not feel safe, I prefer that liquibase do not try to drop columns.

Liquibase does indeed do some odd things sometimes. I can’t reproduce any of them, but I wanted to chime in and mention that LB just does not feel safe or trustworthy. Sometimes it generates some rather odd changelogs. For example recreating indexes that already exist, which of course causes an error when it tries to run.

You are right something strange it also happened to me in the past.
But this time the problem is serious because I risk to lose customer’s data.
I need two things:

  • a quick and 100% workaround to block any migration in production
  • a real solution because next time I need a migration in the project I cannot do it

And, unfortunately, customer has just started using new software, cannot go back and is impacted because I cannot submit fixes without risking to delete data.
Since I have support I now ask to escalate.

1 Like

Can you provide the source code for your custom datatype?
Perhaps you did not specify the @DLL annotation. Therefore the studio is trying to generate a changelog.

1 Like

You can add the @NumberFormat annotation to any attribute to specify a format different from the global one defined in messages.properties.

In Studio:
image

The annotation on the attribute:

@NumberFormat(pattern = "#.000")
@Column(name = "DEC193", precision = 19, scale = 3)
private BigDecimal dec193;

The application view:
image

1 Like

Sure,
here it is (code is not optimized):

package it.giammar.ricoco;

import com.google.common.base.Strings;
import io.jmix.core.metamodel.annotation.DatatypeDef;
import io.jmix.core.metamodel.annotation.Ddl;
import io.jmix.core.metamodel.datatype.Datatype;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;

@DatatypeDef(
        id = "tredecimali",
        javaClass = BigDecimal.class
)
@Ddl("bigDecimal")
public class TreDecimali implements Datatype<BigDecimal> {

    private static final String PATTERN = "#,##0.000";

    @Override
    public String format(@Nullable Object value) {
        if (value == null)
            return "";
        DecimalFormatSymbols symbols = new DecimalFormatSymbols();
        symbols.setGroupingSeparator('.');
        symbols.setDecimalSeparator(',');
        DecimalFormat decimalFormat = new DecimalFormat(PATTERN,symbols);
        decimalFormat.setParseBigDecimal(true);
        return decimalFormat.format(value);
    }

    @Override
    public String format(@Nullable Object value, Locale locale) {
        return format(value);
    }

    @Nullable
    @Override
    public BigDecimal parse(@Nullable String value) throws ParseException {
        if (Strings.isNullOrEmpty(value))
            return null;
        DecimalFormatSymbols symbols = new DecimalFormatSymbols();
        symbols.setGroupingSeparator('.');
        symbols.setDecimalSeparator(',');
        DecimalFormat decimalFormat = new DecimalFormat(PATTERN,symbols);
        decimalFormat.setParseBigDecimal(true);

// parse the string value
        return (BigDecimal) decimalFormat.parse(value);

    }

    @Nullable
    @Override
    public BigDecimal parse(@Nullable String value, Locale locale) throws ParseException {
        return parse(value);
    }
}

This is the good one thanks!
I do not know why but I have tried to use the @Digits annotation that does a completely different thing, when the good one exists and is @NumberFormat.
Thanks,
Mario