diff --git a/docker/objects-apis/fixtures/objecttypes_api_fixtures.json b/docker/objects-apis/fixtures/objecttypes_api_fixtures.json index 371db4a1f2..fa62a65f87 100644 --- a/docker/objects-apis/fixtures/objecttypes_api_fixtures.json +++ b/docker/objects-apis/fixtures/objecttypes_api_fixtures.json @@ -172,8 +172,17 @@ "submission_payment_completed": { "type": "boolean" }, - "submission_payment_amount": { - "type": "string" + "nested": { + "type": "object", + "properties": { + "unrelated": { + "type": "string" + }, + "submission_payment_amount": { + "type": "number", + "multipleOf": 0.01 + } + } }, "submission_payment_public_ids": { "type": "array" diff --git a/src/openforms/registrations/contrib/objects_api/plugin.py b/src/openforms/registrations/contrib/objects_api/plugin.py index 2a3ba23e81..ae1bc5d259 100644 --- a/src/openforms/registrations/contrib/objects_api/plugin.py +++ b/src/openforms/registrations/contrib/objects_api/plugin.py @@ -122,11 +122,7 @@ def update_payment_status( object_url = submission.registration_result["url"] with get_objects_client() as objects_client: - operation = ( - objects_client.patch if options["version"] == 1 else objects_client.put - ) - - response = operation( + response = objects_client.patch( url=object_url, json=updated_object_data, headers={"Content-Crs": "EPSG:4326"}, diff --git a/src/openforms/registrations/contrib/objects_api/registration_variables.py b/src/openforms/registrations/contrib/objects_api/registration_variables.py index 641b1945a0..f2456d9e1f 100644 --- a/src/openforms/registrations/contrib/objects_api/registration_variables.py +++ b/src/openforms/registrations/contrib/objects_api/registration_variables.py @@ -65,7 +65,7 @@ class PaymentAmount(BaseStaticVariable): def get_initial_value(self, submission: Submission | None = None): if submission is None: return None - return str(submission.payments.sum_amount()) + return float(submission.payments.sum_amount()) @register("payment_public_order_ids") diff --git a/src/openforms/registrations/contrib/objects_api/submission_registration.py b/src/openforms/registrations/contrib/objects_api/submission_registration.py index 94fc379c57..7c4e45e27e 100644 --- a/src/openforms/registrations/contrib/objects_api/submission_registration.py +++ b/src/openforms/registrations/contrib/objects_api/submission_registration.py @@ -26,7 +26,7 @@ SubmissionFileAttachment, SubmissionReport, ) -from openforms.typing import JSONValue +from openforms.typing import JSONObject from openforms.variables.service import get_static_variables from openforms.variables.utils import get_variables_for_context @@ -36,6 +36,7 @@ from .registration_variables import register as variables_registry from .typing import ( ConfigVersion, + ObjecttypeVariableMapping, RegistrationOptions, RegistrationOptionsV1, RegistrationOptionsV2, @@ -357,6 +358,31 @@ def get_update_payment_status_data( class ObjectsAPIV2Handler(ObjectsAPIRegistrationHandler[RegistrationOptionsV2]): + @staticmethod + def _get_record_data( + variables_values: FormioData, variables_mapping: list[ObjecttypeVariableMapping] + ) -> JSONObject: + record_data: JSONObject = {} + + for mapping in variables_mapping: + variable_key = mapping["variable_key"] + if variable_key not in variables_values: + # This should only happen for payment status update, + # where only a subset of the variables are updated. + continue + target_path = mapping["target_path"] + + # Type hint is wrong: currently some static variables are of type date/datetime + value = cast(Any, variables_values[variable_key]) + + # Comply with JSON Schema "format" specs: + if isinstance(value, (datetime, date)): + value = value.isoformat() + + glom.assign(record_data, glom.Path(*target_path), value, missing=dict) + + return record_data + @override def get_object_data( self, submission: Submission, options: RegistrationOptionsV2 @@ -401,22 +427,8 @@ def get_object_data( ) variables_values = FormioData({**dynamic_values, **static_values}) - variables_mapping = options["variables_mapping"] - record_data: dict[str, JSONValue] = {} - - for mapping in variables_mapping: - variable_key = mapping["variable_key"] - target_path = mapping["target_path"] - - # Type hint is wrong: currently some static variables are of type date/datetime - value = cast(Any, variables_values[variable_key]) - - # Comply with JSON Schema "format" specs: - if isinstance(value, (datetime, date)): - value = value.isoformat() - - glom.assign(record_data, glom.Path(*target_path), value, missing=dict) + record_data = self._get_record_data(variables_values, variables_mapping) object_data = prepare_data_for_registration( record_data=record_data, @@ -435,10 +447,28 @@ def get_object_data( def get_update_payment_status_data( self, submission: Submission, options: RegistrationOptionsV2 ) -> dict[str, Any]: - # In V2, a PUT request is made, so we essentially return the same - # payload from the initial registration. Payment related variables - # will have their value updated. - return self.get_object_data(submission, options) + + values = { + variable.key: variable.initial_value + for variable in get_static_variables( + submission=submission, + variables_registry=variables_registry, + ) + if variable.key + in ["payment_completed", "payment_amount", "payment_public_order_ids"] + } + + variables_values = FormioData(values) + variables_mapping = options["variables_mapping"] + record_data = self._get_record_data(variables_values, variables_mapping) + + object_data = prepare_data_for_registration( + record_data=record_data, + objecttype=options["objecttype"], + objecttype_version=options["objecttype_version"], + ) + + return object_data HANDLER_MAPPING: dict[ConfigVersion, ObjectsAPIRegistrationHandler[Any]] = { diff --git a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIPaymentStatusUpdateV2Tests/ObjectsAPIPaymentStatusUpdateV2Tests.test_update_payment_status.yaml b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIPaymentStatusUpdateV2Tests/ObjectsAPIPaymentStatusUpdateV2Tests.test_update_payment_status.yaml index 865d837e8c..b0bbd0d09e 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIPaymentStatusUpdateV2Tests/ObjectsAPIPaymentStatusUpdateV2Tests.test_update_payment_status.yaml +++ b/src/openforms/registrations/contrib/objects_api/tests/files/vcr_cassettes/ObjectsAPIPaymentStatusUpdateV2Tests/ObjectsAPIPaymentStatusUpdateV2Tests.test_update_payment_status.yaml @@ -3,8 +3,9 @@ interactions: body: '{"type": "http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48", "record": {"typeVersion": 3, "data": {"age": 20, "name": {"last.name": "My last name"}, "submission_pdf_url": "http://example.com", "submission_csv_url": "http://example.com", - "submission_payment_completed": false, "submission_payment_amount": "0", "submission_payment_public_ids": - [], "submission_date": "2020-02-02T00:00:00+00:00"}, "startAt": "2020-02-02"}}' + "submission_payment_completed": false, "nested": {"unrelated": "some_value", + "submission_payment_amount": 0}, "submission_payment_public_ids": [], "submission_date": + "2020-02-02T00:00:00+00:00"}, "startAt": "2020-02-02"}}' headers: Accept: - '*/*' @@ -17,7 +18,7 @@ interactions: Content-Crs: - EPSG:4326 Content-Length: - - '456' + - '493' Content-Type: - application/json User-Agent: @@ -26,21 +27,21 @@ interactions: uri: http://localhost:8002/api/v2/objects response: body: - string: '{"url":"http://localhost:8002/api/v2/objects/2c287cd4-7737-473d-84de-8b0a018a46bb","uuid":"2c287cd4-7737-473d-84de-8b0a018a46bb","type":"http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","record":{"index":1,"typeVersion":3,"data":{"age":20,"name":{"last.name":"My - last name"},"submission_pdf_url":"http://example.com","submission_csv_url":"http://example.com","submission_payment_completed":false,"submission_payment_amount":"0","submission_payment_public_ids":[],"submission_date":"2020-02-02T00:00:00+00:00"},"geometry":null,"startAt":"2020-02-02","endAt":null,"registrationAt":"2024-03-19","correctionFor":null,"correctedBy":null}}' + string: '{"url":"http://localhost:8002/api/v2/objects/302d84d5-b428-4e6b-8fb5-9127a893fd5e","uuid":"302d84d5-b428-4e6b-8fb5-9127a893fd5e","type":"http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","record":{"index":1,"typeVersion":3,"data":{"age":20,"name":{"last.name":"My + last name"},"submission_pdf_url":"http://example.com","submission_csv_url":"http://example.com","submission_payment_completed":false,"nested":{"unrelated":"some_value","submission_payment_amount":0},"submission_payment_public_ids":[],"submission_date":"2020-02-02T00:00:00+00:00"},"geometry":null,"startAt":"2020-02-02","endAt":null,"registrationAt":"2024-03-22","correctionFor":null,"correctedBy":null}}' headers: Allow: - GET, POST, HEAD, OPTIONS Content-Crs: - EPSG:4326 Content-Length: - - '669' + - '703' Content-Type: - application/json Cross-Origin-Opener-Policy: - same-origin Location: - - http://localhost:8002/api/v2/objects/2c287cd4-7737-473d-84de-8b0a018a46bb + - http://localhost:8002/api/v2/objects/302d84d5-b428-4e6b-8fb5-9127a893fd5e Referrer-Policy: - same-origin X-Content-Type-Options: @@ -52,11 +53,9 @@ interactions: message: Created - request: body: '{"type": "http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48", - "record": {"typeVersion": 3, "data": {"age": 20, "name": {"last.name": "My last - name"}, "submission_date": "2020-02-02T00:00:00+00:00", "submission_pdf_url": - "", "submission_csv_url": "", "submission_payment_completed": true, "submission_payment_amount": - "10.01", "submission_payment_public_ids": ["TEST-123"]}, "startAt": "2020-02-02", - "geometry": {"type": "Point", "coordinates": [52.36673378967122, 4.893164274470299]}}}' + "record": {"typeVersion": 3, "data": {"submission_payment_completed": true, + "nested": {"submission_payment_amount": 10.01}, "submission_payment_public_ids": + ["TEST-123"]}, "startAt": "2020-02-02"}}' headers: Accept: - '*/*' @@ -69,24 +68,24 @@ interactions: Content-Crs: - EPSG:4326 Content-Length: - - '519' + - '293' Content-Type: - application/json User-Agent: - python-requests/2.31.0 - method: PUT - uri: http://localhost:8002/api/v2/objects/2c287cd4-7737-473d-84de-8b0a018a46bb + method: PATCH + uri: http://localhost:8002/api/v2/objects/302d84d5-b428-4e6b-8fb5-9127a893fd5e response: body: - string: '{"url":"http://localhost:8002/api/v2/objects/2c287cd4-7737-473d-84de-8b0a018a46bb","uuid":"2c287cd4-7737-473d-84de-8b0a018a46bb","type":"http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","record":{"index":2,"typeVersion":3,"data":{"age":20,"name":{"last.name":"My - last name"},"submission_date":"2020-02-02T00:00:00+00:00","submission_pdf_url":"","submission_csv_url":"","submission_payment_completed":true,"submission_payment_amount":"10.01","submission_payment_public_ids":["TEST-123"]},"geometry":{"type":"Point","coordinates":[52.36673378967122,4.893164274470299]},"startAt":"2020-02-02","endAt":null,"registrationAt":"2024-03-19","correctionFor":null,"correctedBy":null}}' + string: '{"url":"http://localhost:8002/api/v2/objects/302d84d5-b428-4e6b-8fb5-9127a893fd5e","uuid":"302d84d5-b428-4e6b-8fb5-9127a893fd5e","type":"http://objecttypes-web:8000/api/v2/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48","record":{"index":2,"typeVersion":3,"data":{"age":20,"name":{"last.name":"My + last name"},"nested":{"unrelated":"some_value","submission_payment_amount":10.01},"submission_date":"2020-02-02T00:00:00+00:00","submission_csv_url":"http://example.com","submission_pdf_url":"http://example.com","submission_payment_completed":true,"submission_payment_public_ids":["TEST-123"]},"geometry":null,"startAt":"2020-02-02","endAt":null,"registrationAt":"2024-03-22","correctionFor":null,"correctedBy":null}}' headers: Allow: - GET, PUT, PATCH, DELETE, HEAD, OPTIONS Content-Crs: - EPSG:4326 Content-Length: - - '710' + - '716' Content-Type: - application/json Cross-Origin-Opener-Policy: diff --git a/src/openforms/registrations/contrib/objects_api/tests/test_update_payment_status_v2.py b/src/openforms/registrations/contrib/objects_api/tests/test_update_payment_status_v2.py index fded17a157..c18303064f 100644 --- a/src/openforms/registrations/contrib/objects_api/tests/test_update_payment_status_v2.py +++ b/src/openforms/registrations/contrib/objects_api/tests/test_update_payment_status_v2.py @@ -61,7 +61,10 @@ def test_update_payment_status(self): "submission_pdf_url": "http://example.com", "submission_csv_url": "http://example.com", "submission_payment_completed": False, - "submission_payment_amount": "0", + "nested": { + "unrelated": "some_value", + "submission_payment_amount": 0, + }, "submission_payment_public_ids": [], "submission_date": timezone.now().isoformat(), }, @@ -137,7 +140,7 @@ def test_update_payment_status(self): }, { "variable_key": "payment_amount", - "target_path": ["submission_payment_amount"], + "target_path": ["nested", "submission_payment_amount"], }, { "variable_key": "payment_public_order_ids", @@ -164,5 +167,11 @@ def test_update_payment_status(self): result_data = result["record"]["data"] self.assertTrue(result_data["submission_payment_completed"]) - self.assertEqual(result_data["submission_payment_amount"], "10.01") + self.assertEqual( + result_data["nested"], + { + "unrelated": "some_value", + "submission_payment_amount": 10.01, + }, + ) self.assertEqual(result_data["submission_payment_public_ids"], ["TEST-123"])