Enhancement: better monetary field with currency code (#5858)

This commit is contained in:
shamoon
2024-02-27 08:26:06 -08:00
committed by GitHub
parent 84721b001f
commit bf11dc8d1b
13 changed files with 283 additions and 38 deletions

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.10 on 2024-02-22 03:52
from django.db import migrations
from django.db import models
class Migration(migrations.Migration):
dependencies = [
("documents", "1044_workflow_workflowaction_workflowtrigger_and_more"),
]
operations = [
migrations.AlterField(
model_name="customfieldinstance",
name="value_monetary",
field=models.CharField(max_length=128, null=True),
),
]

View File

@@ -838,7 +838,7 @@ class CustomFieldInstance(models.Model):
value_float = models.FloatField(null=True)
value_monetary = models.DecimalField(null=True, decimal_places=2, max_digits=12)
value_monetary = models.CharField(null=True, max_length=128)
value_document_ids = models.JSONField(null=True)

View File

@@ -12,6 +12,7 @@ from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.validators import DecimalValidator
from django.core.validators import MaxLengthValidator
from django.core.validators import RegexValidator
from django.core.validators import integer_validator
from django.utils import timezone
from django.utils.crypto import get_random_string
@@ -528,9 +529,17 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer):
elif field.data_type == CustomField.FieldDataType.INT:
integer_validator(data["value"])
elif field.data_type == CustomField.FieldDataType.MONETARY:
DecimalValidator(max_digits=12, decimal_places=2)(
Decimal(str(data["value"])),
)
try:
# First try to validate as a number from legacy format
DecimalValidator(max_digits=12, decimal_places=2)(
Decimal(str(data["value"])),
)
except Exception:
# If that fails, try to validate as a monetary string
RegexValidator(
regex=r"^[A-Z][A-Z][A-Z]\d+(\.\d{2,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:
MaxLengthValidator(limit_value=128)(data["value"])

View File

@@ -10,7 +10,7 @@ from documents.models import Document
from documents.tests.utils import DirectoriesMixin
class TestCustomField(DirectoriesMixin, APITestCase):
class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
ENDPOINT = "/api/custom_fields/"
def setUp(self):
@@ -127,6 +127,10 @@ class TestCustomField(DirectoriesMixin, APITestCase):
name="Test Custom Field Monetary",
data_type=CustomField.FieldDataType.MONETARY,
)
custom_field_monetary2 = CustomField.objects.create(
name="Test Custom Field Monetary 2",
data_type=CustomField.FieldDataType.MONETARY,
)
custom_field_documentlink = CustomField.objects.create(
name="Test Custom Field Doc Link",
data_type=CustomField.FieldDataType.DOCUMENTLINK,
@@ -164,7 +168,11 @@ class TestCustomField(DirectoriesMixin, APITestCase):
},
{
"field": custom_field_monetary.id,
"value": 11.10,
"value": "EUR11.10",
},
{
"field": custom_field_monetary2.id,
"value": 11.10, # Legacy format
},
{
"field": custom_field_documentlink.id,
@@ -188,13 +196,14 @@ class TestCustomField(DirectoriesMixin, APITestCase):
{"field": custom_field_boolean.id, "value": True},
{"field": custom_field_url.id, "value": "https://example.com"},
{"field": custom_field_float.id, "value": 12.3456},
{"field": custom_field_monetary.id, "value": 11.10},
{"field": custom_field_monetary.id, "value": "EUR11.10"},
{"field": custom_field_monetary2.id, "value": "11.1"},
{"field": custom_field_documentlink.id, "value": [doc2.id]},
],
)
doc.refresh_from_db()
self.assertEqual(len(doc.custom_fields.all()), 8)
self.assertEqual(len(doc.custom_fields.all()), 9)
def test_change_custom_field_instance_value(self):
"""
@@ -458,7 +467,7 @@ class TestCustomField(DirectoriesMixin, APITestCase):
GIVEN:
- Document & custom field exist
WHEN:
- API request to set a field value to something not a valid monetary decimal
- API request to set a field value to something not a valid monetary decimal (legacy) or not a new monetary format e.g. USD12.34
THEN:
- HTTP 400 is returned
- No field instance is created or attached to the document
@@ -488,6 +497,54 @@ class TestCustomField(DirectoriesMixin, APITestCase):
format="json",
)
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={
"custom_fields": [
{
"field": custom_field_money.id,
# Too many places past decimal
"value": "GBP12.123",
},
],
},
format="json",
)
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,
# Not a 3-letter currency code
"value": "G12.12",
},
],
},
format="json",
)
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(CustomFieldInstance.objects.count(), 0)
self.assertEqual(len(doc.custom_fields.all()), 0)

View File

@@ -777,12 +777,12 @@ msgstr ""
msgid "Invalid color."
msgstr ""
#: documents/serialisers.py:1061
#: documents/serialisers.py:1073
#, python-format
msgid "File type %(type)s not supported"
msgstr ""
#: documents/serialisers.py:1164
#: documents/serialisers.py:1176
msgid "Invalid variable detected."
msgstr ""