diff --git a/doc/development/api/datasync.rst b/doc/development/api/datasync.rst index e2697260fa..5b4dffbcdc 100644 --- a/doc/development/api/datasync.rst +++ b/doc/development/api/datasync.rst @@ -124,4 +124,7 @@ and the association between them are transferred. .. autoclass:: pretix.base.datasync.datasync.OutboundSyncProvider - :members: \ No newline at end of file + :members: + +.. autoclass:: pretix.control.forms.mapping.PropertyMappingFormSet + :members: to_property_mappings_json diff --git a/src/pretix/base/datasync/datasync.py b/src/pretix/base/datasync/datasync.py index 547b510355..7ef64e0558 100644 --- a/src/pretix/base/datasync/datasync.py +++ b/src/pretix/base/datasync/datasync.py @@ -88,8 +88,7 @@ def sync_all(): if not target_cls: # sync plugin not found (plugin deactivated or uninstalled) -> drop outstanding jobs - for sq in queued_orders: - sq.delete() + OrderSyncQueue.objects.filter(pk__in=[sq.pk for sq in queued_orders]).delete() with scope(organizer=event.organizer): with target_cls(event=event) as p: @@ -125,7 +124,7 @@ class RecoverableSyncError(BaseSyncError): pass -StaticMapping = namedtuple('StaticMapping', ('id', 'pretix_model', 'external_object_type', 'pretix_id_field', 'external_id_field', 'property_mapping')) +StaticMapping = namedtuple('StaticMapping', ('id', 'pretix_model', 'external_object_type', 'pretix_id_field', 'external_id_field', 'property_mappings')) class OutboundSyncProvider: @@ -216,7 +215,7 @@ class OutboundSyncProvider: - `pretix_id_field`: Which pretix data field should be used to identify the mapped object. Any ``DataFieldInfo.key`` returned by ``sourcefields.get_data_fields()`` for the combination of ``Event`` and ``pretix_model``. - `external_id_field`: Destination identifier field in the target system. - - `property_mapping`: Mapping configuration as generated by ``PropertyMappingFormSet.to_property_mapping_json()``. + - `property_mappings`: Mapping configuration as generated by ``PropertyMappingFormSet.to_property_mappings_json()``. """ raise NotImplementedError @@ -291,11 +290,11 @@ class OutboundSyncProvider: val = ",".join(val) return val - def get_properties(self, inputs: dict, property_mapping: str): - property_mapping = json.loads(property_mapping) + def get_properties(self, inputs: dict, property_mappings: str): + property_mappings = json.loads(property_mappings) return [ (m["external_field"], self.get_field_value(inputs, m), m["overwrite"]) - for m in property_mapping + for m in property_mappings ] def sync_object_with_properties( @@ -315,7 +314,7 @@ class OutboundSyncProvider: :param external_id_field: Identifier field in the external system as provided in ``mapping.external_identifier`` :param id_value: Identifier contents as retrieved from the property specified by ``mapping.pretix_identifier`` of the model specified by ``mapping.pretix_model`` - :param properties: All properties defined in ``mapping.property_mapping``, as list of three-tuples + :param properties: All properties defined in ``mapping.property_mappings``, as list of three-tuples ``(external_field, value, overwrite)`` :param inputs: All pretix model instances from which data can be retrieved for this mapping :param mapping: The mapping object as returned by ``self.mappings`` @@ -356,7 +355,7 @@ class OutboundSyncProvider: mapped_objects: dict, ): logger.debug("Syncing object %r, %r, %r", inputs, mapping, mapped_objects) - properties = self.get_properties(inputs, mapping.property_mapping) + properties = self.get_properties(inputs, mapping.property_mappings) logger.debug("Properties: %r", properties) id_value = self.get_field_value(inputs, {"pretix_field": mapping.pretix_id_field}) diff --git a/src/pretix/base/datasync/sourcefields.py b/src/pretix/base/datasync/sourcefields.py index 47871313ca..d55cb71243 100644 --- a/src/pretix/base/datasync/sourcefields.py +++ b/src/pretix/base/datasync/sourcefields.py @@ -514,8 +514,8 @@ def get_data_fields(event, for_model=None): return src_fields -def translate_property_mappings(property_mapping, checkin_list_map): - mappings = json.loads(property_mapping) +def translate_property_mappings(property_mappings, checkin_list_map): + mappings = json.loads(property_mappings) for mapping in mappings: if mapping["pretix_field"].startswith("checkin_date_"): diff --git a/src/pretix/control/forms/mapping.py b/src/pretix/control/forms/mapping.py index 111708c8d7..eeefdfb14d 100644 --- a/src/pretix/control/forms/mapping.py +++ b/src/pretix/control/forms/mapping.py @@ -74,7 +74,7 @@ class PropertyMappingFormSet(formset_factory( can_delete=True, extra=0, )): - template_name = "pretixcontrol/datasync/property_mapping_formset.html" + template_name = "pretixcontrol/datasync/property_mappings_formset.html" def __init__(self, pretix_fields, external_fields, available_modes, prefix, *args, initial_json=None, **kwargs): if initial_json: @@ -95,7 +95,22 @@ class PropertyMappingFormSet(formset_factory( ctx["external_fields_id"] = self.prefix + "external-fields" return ctx - def to_property_mapping_json(self): + def to_property_mappings_json(self): + """ + Returns a property mapping configuration as a JSON-serialized list of dictionaries. + + Each entry specifies how to transfer data from one pretix field to one field in the external system: + + - `pretix_field`: Name of a pretix data source field as declared in `pretix.base.datasync.sourcefields.get_data_fields`. + - `external_field`: Name of the target field in the external system. Implementation-defined by the sync provider. + - `value_map`: Dictionary mapping pretix value to external value. Only used for enumeration-type fields. + - `overwrite`: Mode of operation if the object already exists in the target system. + - `MODE_OVERWRITE` (`"overwrite"`) to always overwrite existing value. + - `MODE_SET_IF_NEW` (`"if_new"`) to only set the value if object does not exist in target system yet. + - `MODE_SET_IF_EMPTY` (`"if_empty"`) to only set the value if object does not exist in target system, + or the field is currently empty in target system. + - `MODE_APPEND_LIST` (`"append"`) if the field is an array or a multi-select: add the value to the list. + """ mappings = [f.cleaned_data for f in self.ordered_forms] return json.dumps(mappings) diff --git a/src/pretix/control/templates/pretixcontrol/datasync/property_mapping_formset.html b/src/pretix/control/templates/pretixcontrol/datasync/property_mappings_formset.html similarity index 100% rename from src/pretix/control/templates/pretixcontrol/datasync/property_mapping_formset.html rename to src/pretix/control/templates/pretixcontrol/datasync/property_mappings_formset.html diff --git a/src/tests/base/test_datasync.py b/src/tests/base/test_datasync.py index 279a321c96..67c39f2d23 100644 --- a/src/tests/base/test_datasync.py +++ b/src/tests/base/test_datasync.py @@ -217,7 +217,7 @@ class SimpleOrderSync(OutboundSyncProvider): id=1, pretix_model='Order', external_object_type='ticketorders', pretix_id_field='event_order_code', external_id_field='ordernumber', - property_mapping=json.dumps([ + property_mappings=json.dumps([ { "pretix_field": "email", "external_field": "orderemail", @@ -311,7 +311,7 @@ def test_simple_order_sync(event): StaticMappingWithAssociations = namedtuple('StaticMappingWithAssociations', ( - 'id', 'pretix_model', 'external_object_type', 'pretix_id_field', 'external_id_field', 'property_mapping', 'association_mapping' + 'id', 'pretix_model', 'external_object_type', 'pretix_id_field', 'external_id_field', 'property_mappings', 'association_mappings' )) AssociationMapping = namedtuple('AssociationMapping', ( 'via_mapping_id' @@ -329,7 +329,7 @@ class OrderAndTicketAssociationSync(OutboundSyncProvider): id=1, pretix_model='OrderPosition', external_object_type='tickets', pretix_id_field='ticket_id', external_id_field='ticketnumber', - property_mapping=json.dumps([ + property_mappings=json.dumps([ { "pretix_field": "ticket_price", "external_field": "amount", @@ -376,13 +376,13 @@ class OrderAndTicketAssociationSync(OutboundSyncProvider): "overwrite": MODE_OVERWRITE, }, ]), - association_mapping=[], + association_mappings=[], ), StaticMappingWithAssociations( id=2, pretix_model='Order', external_object_type='ticketorders', pretix_id_field='event_order_code', external_id_field='ordernumber', - property_mapping=json.dumps([ + property_mappings=json.dumps([ { "pretix_field": "email", "external_field": "orderemail", @@ -414,7 +414,7 @@ class OrderAndTicketAssociationSync(OutboundSyncProvider): "overwrite": MODE_OVERWRITE, }, ]), - association_mapping=[ + association_mappings=[ AssociationMapping(via_mapping_id=1) ], ), @@ -436,7 +436,7 @@ class OrderAndTicketAssociationSync(OutboundSyncProvider): **update_values, external_id_field: id_value, "_id": pre_existing_object and pre_existing_object.get("_id"), - "links": [f"link:{obj['object_type']}:{obj['my_result']['_id']}" for am in mapping.association_mapping for obj in mapped_objects[am.via_mapping_id]] + "links": [f"link:{obj['object_type']}:{obj['my_result']['_id']}" for am in mapping.association_mappings for obj in mapped_objects[am.via_mapping_id]] }) return {