diff --git a/src-ui/src/app/data/custom-field-query.ts b/src-ui/src/app/data/custom-field-query.ts index 8559a5fbb..226a10605 100644 --- a/src-ui/src/app/data/custom-field-query.ts +++ b/src-ui/src/app/data/custom-field-query.ts @@ -93,6 +93,7 @@ export const CUSTOM_FIELD_QUERY_OPERATOR_GROUPS_BY_TYPE = { [CustomFieldDataType.Monetary]: [ CustomFieldQueryOperatorGroups.Basic, CustomFieldQueryOperatorGroups.String, + CustomFieldQueryOperatorGroups.Arithmetic, ], [CustomFieldDataType.DocumentLink]: [ CustomFieldQueryOperatorGroups.Basic, diff --git a/src/documents/filters.py b/src/documents/filters.py index 255e7d3d7..c471f8df6 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -261,7 +261,7 @@ class CustomFieldQueryParser: CustomField.FieldDataType.BOOL: ("basic",), CustomField.FieldDataType.INT: ("basic", "arithmetic"), CustomField.FieldDataType.FLOAT: ("basic", "arithmetic"), - CustomField.FieldDataType.MONETARY: ("basic", "string"), + CustomField.FieldDataType.MONETARY: ("basic", "string", "arithmetic"), CustomField.FieldDataType.DOCUMENTLINK: ("basic", "containment"), CustomField.FieldDataType.SELECT: ("basic",), } @@ -417,6 +417,13 @@ class CustomFieldQueryParser: value_field_name = CustomFieldInstance.get_value_field_name( custom_field.data_type, ) + if custom_field.data_type == CustomField.FieldDataType.MONETARY and op in ( + "gt", + "gte", + "lt", + "lte", + ): + value_field_name = "value_monetary_amount" has_field = Q(custom_fields__field=custom_field) # Our special exists operator. diff --git a/src/documents/migrations/1054_alter_savedviewfilterrule_rule_type.py b/src/documents/migrations/1054_customfieldinstance_value_monetary_amount_and_more.py similarity index 83% rename from src/documents/migrations/1054_alter_savedviewfilterrule_rule_type.py rename to src/documents/migrations/1054_customfieldinstance_value_monetary_amount_and_more.py index acc31153e..210df44fe 100644 --- a/src/documents/migrations/1054_alter_savedviewfilterrule_rule_type.py +++ b/src/documents/migrations/1054_customfieldinstance_value_monetary_amount_and_more.py @@ -1,5 +1,6 @@ -# Generated by Django 5.1.1 on 2024-09-28 04:48 +# Generated by Django 5.1.1 on 2024-09-29 00:39 +import django.db.models.functions.text from django.db import migrations from django.db import models @@ -10,6 +11,15 @@ class Migration(migrations.Migration): ] operations = [ + migrations.AddField( + model_name="customfieldinstance", + name="value_monetary_amount", + field=models.GeneratedField( + db_persist=True, + expression=django.db.models.functions.text.Substr("value_monetary", 4), + output_field=models.DecimalField(decimal_places=2, max_digits=125), + ), + ), migrations.AlterField( model_name="savedviewfilterrule", name="rule_type", diff --git a/src/documents/models.py b/src/documents/models.py index b0a6bdd61..71622b31b 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -22,6 +22,7 @@ from multiselectfield import MultiSelectField if settings.AUDIT_LOG_ENABLED: from auditlog.registry import auditlog +from django.db.models.functions import Substr from django_softdelete.models import SoftDeleteModel from documents.data_models import DocumentSource @@ -922,6 +923,12 @@ class CustomFieldInstance(models.Model): value_monetary = models.CharField(null=True, max_length=128) + value_monetary_amount = models.GeneratedField( + expression=Substr("value_monetary", 4), + output_field=models.DecimalField(decimal_places=2, max_digits=125), + db_persist=True, + ) + value_document_ids = models.JSONField(null=True) value_select = models.PositiveSmallIntegerField(null=True) diff --git a/src/documents/tests/test_api_filter_by_custom_fields.py b/src/documents/tests/test_api_filter_by_custom_fields.py index 327855c4c..3dabf896b 100644 --- a/src/documents/tests/test_api_filter_by_custom_fields.py +++ b/src/documents/tests/test_api_filter_by_custom_fields.py @@ -393,6 +393,14 @@ class TestCustomFieldsSearch(DirectoriesMixin, APITestCase): and document["date_field"].year >= 2024, ) + def test_gt_monetary(self): + self._assert_query_match_predicate( + ["monetary_field", "gt", "99"], + lambda document: "monetary_field" in document + and document["monetary_field"] is not None + and document["monetary_field"] == "USD100.00", + ) + # ==========================================================# # Subset check (document link field only) # # ==========================================================#