Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] Currency input parsing issue with point and/or comma
### Description

In Switzerland, currency amounts are written with the decimal point. Since the update to 2.8.1, I cannot use a point as a decimal separator for a Swiss franc amount. The input parser deletes the input `444.50` after the point. However, if I enter the amount of money with a comma like `444,50`, the API refuses to save it. 

![Bildschirmaufnahme2024-05-07um20 02 51-ezgif com-video-to-gif-converter](https://github.com/paperless-ngx/paperless-ngx/assets/2379437/b6b0580b-21fe-4740-8d69-69ad8837d1e3)




### Steps to reproduce

1. Use browser locale de-CH and write `444.50` as Swiss francs `CHF` amount.
2. Write `444,50` and save it.

### Webserver logs

```bash
No errors in log.
```


### Browser logs

```bash
{"headers":{"normalisedNames":{}, "lazyUpdate":null}, "status":400, "statusText": "Bad Request", "url": "http://****/api/documents/2752/", "ok":false, "name": "HttpErrorResponse", "message": "Http failure response for http://****/api/documents/2752/: 400 Bad Request", "error":{"custom_fields":[{},{},{},{},{"non_field_errors":["Must be a two-decimal number with optional currency code e.g. GBP123.45"]}]}}
```


### Paperless-ngx version

2.8.1

### Host OS

x86_64

### Installation method

Docker - official image

### Browser

Chrome

### Configuration changes

docker-compose.yml

### Other

window.navigator.language is 'de-CH'

### Please confirm the following

- [X] I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
- [X] I have already searched for relevant existing issues and discussions before opening this report.
- [X] I have updated the title field above with a concise description.


Discussion:
Looks like the last zero in `444,50` is not being registred by an event handler; PUT request sends `{field: 14, value: "CHF444.5"}` which does not match `^[A-Z]{3}-?\d+(\.\d{2,2})$`.
Testing this kinda stuff is pretty challenging, but I cant reproduce. Using the `,` saves OK as the value is parsed to a float. See video:


https://github.com/paperless-ngx/paperless-ngx/assets/4887959/815cbbbf-e91e-43fd-98f9-ea8fe136b4d1


Looks like `input[type=number]` causes the issue: I tracked in Chrome's console the keyups (aka: the real changes) for the currency field.

```
document.querySelector("[step='.01']").addEventListener("keyup", (event) => {
  console.log(event.target.value)
});
```

My input was `123,45` and then `123.45`. Console output is:

```
# with comma
> 1
> 12
> 123
> 123.4
> 123.45

# with point
> 1
> 12
> 123
> 
> 4
> 45 
```

With `input[type=text]` there is no such behaviour.
But it is a number and there are pros to it being a number so as opposed to just changing it to text. The browser controls what decimal separator is allowed based on locale, because we dont.

Resolving patch:
diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py
--- a/src/documents/serialisers.py
+++ b/src/documents/serialisers.py
@@ -547,7 +547,7 @@ def validate(self, data):
                 except Exception:
                     # If that fails, try to validate as a monetary string
                     RegexValidator(
-                        regex=r"^[A-Z]{3}-?\d+(\.\d{2,2})$",
+                        regex=r"^[A-Z]{3}-?\d+(\.\d{1,2})$",
                         message="Must be a two-decimal number with optional currency code e.g. GBP123.45",
                     )(data["value"])
             elif field.data_type == CustomField.FieldDataType.STRING:


Test patch:
diff --git a/src/documents/tests/test_api_custom_fields.py b/src/documents/tests/test_api_custom_fields.py
--- a/src/documents/tests/test_api_custom_fields.py
+++ b/src/documents/tests/test_api_custom_fields.py
@@ -499,22 +499,6 @@ def test_custom_field_value_monetary_validation(self):
 
         self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
 
-        resp = self.client.patch(
-            f"/api/documents/{doc.id}/",
-            data={
-                "custom_fields": [
-                    {
-                        "field": custom_field_money.id,
-                        # Too few places past decimal
-                        "value": "GBP12.1",
-                    },
-                ],
-            },
-            format="json",
-        )
-
-        self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
-
         resp = self.client.patch(
             f"/api/documents/{doc.id}/",
             data={