stepxmlclient 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. stepxmlclient/__init__.py +22 -0
  2. stepxmlclient/client.py +373 -0
  3. stepxmlclient/creator.py +317 -0
  4. stepxmlclient/model/__init__.py +418 -0
  5. stepxmlclient/model/asset.py +250 -0
  6. stepxmlclient/model/attribute.py +288 -0
  7. stepxmlclient/model/business.py +233 -0
  8. stepxmlclient/model/classification.py +409 -0
  9. stepxmlclient/model/complex_elements.py +260 -0
  10. stepxmlclient/model/deletion.py +87 -0
  11. stepxmlclient/model/entity.py +368 -0
  12. stepxmlclient/model/metadata.py +199 -0
  13. stepxmlclient/model/organization.py +167 -0
  14. stepxmlclient/model/product.py +519 -0
  15. stepxmlclient/model/relations.py +105 -0
  16. stepxmlclient/model/replacement.py +417 -0
  17. stepxmlclient/parser.py +2805 -0
  18. stepxmlclient/rest_client/__init__.py +16 -0
  19. stepxmlclient/rest_client/base.py +388 -0
  20. stepxmlclient/rest_client/client.py +127 -0
  21. stepxmlclient/rest_client/exceptions.py +97 -0
  22. stepxmlclient/rest_client/models/__init__.py +175 -0
  23. stepxmlclient/rest_client/models/asset.py +253 -0
  24. stepxmlclient/rest_client/models/attribute.py +112 -0
  25. stepxmlclient/rest_client/models/background_process.py +23 -0
  26. stepxmlclient/rest_client/models/batch.py +41 -0
  27. stepxmlclient/rest_client/models/classification.py +57 -0
  28. stepxmlclient/rest_client/models/collection.py +26 -0
  29. stepxmlclient/rest_client/models/common.py +192 -0
  30. stepxmlclient/rest_client/models/context.py +16 -0
  31. stepxmlclient/rest_client/models/data_type_group.py +15 -0
  32. stepxmlclient/rest_client/models/entity.py +63 -0
  33. stepxmlclient/rest_client/models/event_processor.py +15 -0
  34. stepxmlclient/rest_client/models/import_export.py +43 -0
  35. stepxmlclient/rest_client/models/integration_endpoint.py +16 -0
  36. stepxmlclient/rest_client/models/product.py +200 -0
  37. stepxmlclient/rest_client/models/relations.py +280 -0
  38. stepxmlclient/rest_client/models/system_information.py +11 -0
  39. stepxmlclient/rest_client/models/user.py +28 -0
  40. stepxmlclient/rest_client/models/workflow.py +30 -0
  41. stepxmlclient/rest_client/operations/__init__.py +54 -0
  42. stepxmlclient/rest_client/operations/assets.py +652 -0
  43. stepxmlclient/rest_client/operations/attributes.py +252 -0
  44. stepxmlclient/rest_client/operations/background_processes.py +84 -0
  45. stepxmlclient/rest_client/operations/batch.py +28 -0
  46. stepxmlclient/rest_client/operations/classifications.py +248 -0
  47. stepxmlclient/rest_client/operations/collection_groups.py +46 -0
  48. stepxmlclient/rest_client/operations/collections.py +67 -0
  49. stepxmlclient/rest_client/operations/contexts.py +88 -0
  50. stepxmlclient/rest_client/operations/data_type_groups.py +68 -0
  51. stepxmlclient/rest_client/operations/entities.py +295 -0
  52. stepxmlclient/rest_client/operations/event_processors.py +109 -0
  53. stepxmlclient/rest_client/operations/exports.py +153 -0
  54. stepxmlclient/rest_client/operations/gateway.py +53 -0
  55. stepxmlclient/rest_client/operations/inbound.py +472 -0
  56. stepxmlclient/rest_client/operations/outbound.py +64 -0
  57. stepxmlclient/rest_client/operations/products.py +489 -0
  58. stepxmlclient/rest_client/operations/relations.py +349 -0
  59. stepxmlclient/rest_client/operations/reports.py +27 -0
  60. stepxmlclient/rest_client/operations/system_information.py +28 -0
  61. stepxmlclient/rest_client/operations/user_groups.py +79 -0
  62. stepxmlclient/rest_client/operations/users.py +119 -0
  63. stepxmlclient/rest_client/operations/workflow_tasks.py +61 -0
  64. stepxmlclient/rest_client/operations/workflows.py +50 -0
  65. stepxmlclient/upload.py +293 -0
  66. stepxmlclient-1.0.0.dist-info/METADATA +666 -0
  67. stepxmlclient-1.0.0.dist-info/RECORD +70 -0
  68. stepxmlclient-1.0.0.dist-info/WHEEL +5 -0
  69. stepxmlclient-1.0.0.dist-info/licenses/LICENSE +21 -0
  70. stepxmlclient-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,167 @@
1
+ """Organization and dimensional models for STEP XML Client.
2
+
3
+ Defines organizational and dimensional configuration classes including Contexts,
4
+ Qualifiers, Dimensions, Tags, and Units used to organize and structure STEP MDM data.
5
+ """
6
+
7
+ from typing import List, Optional, Any, Dict, TYPE_CHECKING
8
+ from . import BaseObject
9
+
10
+ if TYPE_CHECKING:
11
+ pass
12
+
13
+
14
+ class Context(BaseObject):
15
+ """Context - multi-language/multi-dimensional context for data.
16
+
17
+ Defines a context in which data can exist with language and dimensional variations.
18
+ Contexts enable organizing data by language, region, or dimensional axes.
19
+
20
+ Supports:
21
+ - Multiple languages via Name elements
22
+ - Dimension point associations for multi-dimensional data
23
+ - Locale specification (language/region codes)
24
+ - Priority management for cross-context resolution
25
+ - Boolean state tracking (Selected, Referenced, Changed, Deleted)
26
+
27
+ Attributes:
28
+ ID: Unique context identifier
29
+ Priority: Priority for context resolution
30
+ Locale: Language/locale code (e.g., 'en_US')
31
+ Name: Context name (localized)
32
+ DimensionPointLink: Links to dimension points
33
+ MetaData: Associated metadata
34
+ Selected: Whether context is selected
35
+ Referenced: Whether context is referenced
36
+ Changed: Whether context was changed
37
+ Deleted: Whether context is marked for deletion
38
+ """
39
+ _GROUP_NAME: str = 'contexts'
40
+ _TAG_NAME: str = 'Context'
41
+ _ATTRIBUTE_NAMES: List[str] = ["ID", "Priority", "Locale", "Selected", "Referenced", "Changed", "Deleted"]
42
+ _TEXT_NAMES: List[str] = ["Name"]
43
+ _CHILDREN_NAMES: List[str] = ["DimensionPointLink", "MetaData"]
44
+
45
+ def __init__(self, data: Dict[str, Any]) -> None:
46
+ super().__init__(data)
47
+ # Initialize link lists if not provided
48
+ if "DimensionPointLink" not in self._data:
49
+ self._data["DimensionPointLink"] = []
50
+
51
+ def add_dimension_point_link(self, dimension_id: str, dimension_point_id: str) -> None:
52
+ """Add a dimension point link to this context.
53
+
54
+ Args:
55
+ dimension_id: ID of the dimension
56
+ dimension_point_id: ID of the dimension point
57
+ """
58
+ link_dict = {
59
+ "DimensionID": dimension_id,
60
+ "DimensionPointID": dimension_point_id
61
+ }
62
+
63
+ if not isinstance(self._data["DimensionPointLink"], list):
64
+ self._data["DimensionPointLink"] = []
65
+ self._data["DimensionPointLink"].append(link_dict)
66
+
67
+
68
+ class Qualifier(BaseObject):
69
+ """Qualifier - represents a dimension qualifier or multi-dimensional data point.
70
+
71
+ Qualifiers enable multi-dimensional attribute management in STEP.
72
+ They combine dimension points to define a specific multi-dimensional position.
73
+
74
+ Supports:
75
+ - Multiple dimension point associations
76
+ - Multi-language names
77
+ - Metadata tracking
78
+ """
79
+ _GROUP_NAME: str = 'qualifiers'
80
+ _TAG_NAME: str = 'Qualifier'
81
+ _ATTRIBUTE_NAMES: List[str] = ["ID"]
82
+ _TEXT_NAMES: List[str] = ["Name"]
83
+ _CHILDREN_NAMES: List[str] = ["DimensionPointLink", "MetaData"]
84
+
85
+ def __init__(self, data: Dict[str, Any]) -> None:
86
+ super().__init__(data)
87
+ # Initialize link lists if not provided
88
+ if "DimensionPointLink" not in self._data:
89
+ self._data["DimensionPointLink"] = []
90
+
91
+ def add_dimension_point_link(self, dimension_id: str, dimension_point_id: str) -> None:
92
+ """Add a dimension point link to this qualifier.
93
+
94
+ Args:
95
+ dimension_id: ID of the dimension
96
+ dimension_point_id: ID of the dimension point
97
+ """
98
+ link_dict = {
99
+ "DimensionID": dimension_id,
100
+ "DimensionPointID": dimension_point_id
101
+ }
102
+
103
+ if not isinstance(self._data["DimensionPointLink"], list):
104
+ self._data["DimensionPointLink"] = []
105
+ self._data["DimensionPointLink"].append(link_dict)
106
+
107
+
108
+ class Dimension(BaseObject):
109
+ """Dimension - defines an axis for multi-dimensional data organization.
110
+
111
+ Dimensions define categorization axes (e.g., Market, Color, Size) along which
112
+ products and attributes can be organized. Each dimension contains dimension points.
113
+
114
+ Supports:
115
+ - Dimension points (values/positions along this axis)
116
+ - Multi-language names
117
+ - Duplicate point control
118
+ - Metadata tracking
119
+ """
120
+ _GROUP_NAME: str = 'dimensions'
121
+ _TAG_NAME: str = 'Dimension'
122
+ _ATTRIBUTE_NAMES: List[str] = ["ID", "AllowDuplicates"]
123
+ _TEXT_NAMES: List[str] = ["Name"]
124
+ _CHILDREN_NAMES: List[str] = ["DimensionPoint", "MetaData"]
125
+
126
+ def __init__(self, data: Dict[str, Any]) -> None:
127
+ super().__init__(data)
128
+ # Initialize dimension points list if not provided
129
+ if "DimensionPoint" not in self._data:
130
+ self._data["DimensionPoint"] = []
131
+
132
+ def add_dimension_point(self, dimension_point: Optional['DimensionPoint'] = None,
133
+ **kwargs) -> None:
134
+ """Add a dimension point to this dimension.
135
+
136
+ Can be called two ways:
137
+ 1. With object: add_dimension_point(dimension_point=point_obj)
138
+ 2. With parameters: add_dimension_point(ID="DP001", Name="Point Name")
139
+ """
140
+ if dimension_point is not None:
141
+ point = dimension_point
142
+ else:
143
+ point = DimensionPoint(kwargs)
144
+
145
+ if not isinstance(self._data["DimensionPoint"], list):
146
+ self._data["DimensionPoint"] = []
147
+ self._data["DimensionPoint"].append(point)
148
+
149
+
150
+ class DimensionPoint(BaseObject):
151
+ """DimensionPoint - represents a point/value within a dimension.
152
+
153
+ Dimension points define the valid positions or values along a dimension axis.
154
+ For example, a "Color" dimension might have points: Red, Blue, Green.
155
+
156
+ Supports:
157
+ - Multi-language names
158
+ - Metadata tracking
159
+ """
160
+ _GROUP_NAME: Optional[str] = None # Used as child element only
161
+ _TAG_NAME: str = 'DimensionPoint'
162
+ _ATTRIBUTE_NAMES: List[str] = ["ID"]
163
+ _TEXT_NAMES: List[str] = ["Name"]
164
+ _CHILDREN_NAMES: List[str] = ["MetaData"]
165
+
166
+ def __init__(self, data: Dict[str, Any]) -> None:
167
+ super().__init__(data)
@@ -0,0 +1,519 @@
1
+ from typing import List, Optional, Any, Dict, TYPE_CHECKING
2
+ from . import BaseObject
3
+
4
+ if TYPE_CHECKING:
5
+ from .relations import (
6
+ AssetCrossReference,
7
+ ProductCrossReference,
8
+ ClassificationReference,
9
+ ClassificationCrossReference,
10
+ EntityCrossReference,
11
+ )
12
+ from .deletion import DeleteAssetCrossReference, DeleteProductCrossReference
13
+ from .complex_elements import (
14
+ STEPWorkflowStateBind,
15
+ SequenceProduct,
16
+ OverrideSubProduct,
17
+ PackagingReference,
18
+ )
19
+
20
+
21
+ class Product(BaseObject):
22
+ """Product entity in STEP MDM.
23
+
24
+ This model serializes to STEP ``Product`` and supports both direct dictionary
25
+ payloads and helper methods.
26
+
27
+ Common top-level ``data`` keys:
28
+ - ``ID`` (str): Product identifier
29
+ - ``ParentId`` (str): Parent product ID
30
+ - ``UserTypeID`` (str): Product object type
31
+ - ``Name`` (str): Product name
32
+ - ``KeyValue`` (dict/list): external keys, e.g. ``{"KeyID": "ExternalKey", "value": "P-1"}``
33
+ - ``Values`` (dict): attribute value map
34
+
35
+ Frequently used child collections:
36
+ - ``AssetCrossReference``
37
+ - ``ProductCrossReference``
38
+ - ``ClassificationReference``
39
+ - ``ClassificationCrossReference``
40
+ - ``EntityCrossReference``
41
+
42
+ Usage examples:
43
+ Dict-first style::
44
+
45
+ product = Product({"ID": "P100", "UserTypeID": "UT_PRODUCT", "Name": "Bike"})
46
+ product.set_workflow_state_bind(workflow_id="WF_APPROVAL", state_id="STATE_DRAFT")
47
+ product.add_packaging_reference(product_id="BOX_01", quantity="1")
48
+
49
+ Pre-built object style::
50
+
51
+ from stepxmlclient.model import STEPWorkflowStateBind, SequenceProduct
52
+ product = Product({"ID": "P200", "UserTypeID": "UT_PRODUCT"})
53
+ product.set_workflow_state_bind(bind_object=STEPWorkflowStateBind({
54
+ "WorkflowID": "WF_APPROVAL", "StateID": "STATE_READY"
55
+ }))
56
+ product.add_sequence_product(sequence_object=SequenceProduct({"ID": "P201"}))
57
+ """
58
+
59
+ _GROUP_NAME: str = "products"
60
+ _TAG_NAME: str = "Product"
61
+ _ATTRIBUTE_NAMES: List[str] = [
62
+ "ID",
63
+ "ParentId",
64
+ "UserTypeID",
65
+ "QualifierID",
66
+ "Changed",
67
+ "Republished",
68
+ "Selected",
69
+ "Referenced",
70
+ ]
71
+ _TEXT_NAMES: List[str] = ["Name"]
72
+ _CHILDREN_NAMES: List[str] = [
73
+ "KeyValue",
74
+ "ParentKeyValue",
75
+ "STEPWorkflowStateBind",
76
+ "Values",
77
+ "DeleteSuppressedAssetCrossReference",
78
+ "SuppressedAssetCrossReference",
79
+ "AssetCrossReference",
80
+ "DeleteSuppressedProductCrossReference",
81
+ "SuppressedProductCrossReference",
82
+ "ProductCrossReference",
83
+ "DeleteSuppressedClassificationReference",
84
+ "DeleteClassificationReference",
85
+ "SuppressedClassificationReference",
86
+ "ClassificationReference",
87
+ "DeleteSuppressedClassificationCrossReference",
88
+ "DeleteClassificationCrossReference",
89
+ "SuppressedClassificationCrossReference",
90
+ "ClassificationCrossReference",
91
+ "DeleteSuppressedEntityCrossReference",
92
+ "DeleteEntityCrossReference",
93
+ "SuppressedEntityCrossReference",
94
+ "EntityCrossReference",
95
+ "DeleteAttributeLink",
96
+ "AttributeLink",
97
+ "DataContainerTypeLink",
98
+ "DeleteDataContainerTypeLink",
99
+ "DataContainers",
100
+ "DeleteAssetCrossReference",
101
+ "DeleteProductCrossReference",
102
+ "SequenceProduct",
103
+ "CurrentTasks",
104
+ "Terms",
105
+ "Tables",
106
+ "OverrideSubProduct",
107
+ "PackagingReference",
108
+ ]
109
+
110
+ def __init__(self, data: Dict[str, Any]) -> None:
111
+ super().__init__(data)
112
+ for rel_name in [
113
+ "DeleteSuppressedAssetCrossReference",
114
+ "SuppressedAssetCrossReference",
115
+ "AssetCrossReference",
116
+ "DeleteSuppressedProductCrossReference",
117
+ "SuppressedProductCrossReference",
118
+ "ProductCrossReference",
119
+ "DeleteSuppressedClassificationReference",
120
+ "DeleteClassificationReference",
121
+ "SuppressedClassificationReference",
122
+ "ClassificationReference",
123
+ "DeleteSuppressedClassificationCrossReference",
124
+ "DeleteClassificationCrossReference",
125
+ "SuppressedClassificationCrossReference",
126
+ "ClassificationCrossReference",
127
+ "DeleteSuppressedEntityCrossReference",
128
+ "DeleteEntityCrossReference",
129
+ "SuppressedEntityCrossReference",
130
+ "EntityCrossReference",
131
+ "DeleteAttributeLink",
132
+ "AttributeLink",
133
+ "DataContainerTypeLink",
134
+ "DeleteDataContainerTypeLink",
135
+ "DeleteAssetCrossReference",
136
+ "DeleteProductCrossReference",
137
+ "SequenceProduct",
138
+ "CurrentTasks",
139
+ "Terms",
140
+ "Tables",
141
+ "OverrideSubProduct",
142
+ "PackagingReference",
143
+ ]:
144
+ if rel_name not in self._data:
145
+ self._data[rel_name] = []
146
+
147
+ def add_asset_reference(
148
+ self,
149
+ asset_id: Optional[str] = None,
150
+ type_: Optional[str] = None,
151
+ qualifier_id: Optional[str] = None,
152
+ ref_object: Optional["AssetCrossReference"] = None,
153
+ ) -> None:
154
+ """Add an ``AssetCrossReference``.
155
+
156
+ Fields:
157
+ - ``AssetID``
158
+ - ``Type``
159
+ - ``QualifierID``
160
+ """
161
+ from .relations import AssetCrossReference
162
+
163
+ ref = ref_object or AssetCrossReference(
164
+ {"AssetID": asset_id, "Type": type_, "QualifierID": qualifier_id}
165
+ )
166
+ if not isinstance(self._data["AssetCrossReference"], list):
167
+ self._data["AssetCrossReference"] = []
168
+ self._data["AssetCrossReference"].append(ref)
169
+
170
+ def delete_asset_reference(
171
+ self,
172
+ asset_id: Optional[str] = None,
173
+ type_: Optional[str] = None,
174
+ qualifier_id: Optional[str] = None,
175
+ delete_ref_object: Optional["DeleteAssetCrossReference"] = None,
176
+ ) -> None:
177
+ """Add ``DeleteAssetCrossReference`` to remove an asset link.
178
+
179
+ Fields:
180
+ - ``AssetID``
181
+ - ``Type``
182
+ - ``QualifierID``
183
+ """
184
+ from .deletion import DeleteAssetCrossReference
185
+
186
+ delete_ref = delete_ref_object or DeleteAssetCrossReference(
187
+ {"AssetID": asset_id, "Type": type_, "QualifierID": qualifier_id}
188
+ )
189
+ if not isinstance(self._data["DeleteAssetCrossReference"], list):
190
+ self._data["DeleteAssetCrossReference"] = []
191
+ self._data["DeleteAssetCrossReference"].append(delete_ref)
192
+
193
+ def add_product_reference(
194
+ self,
195
+ product_id: Optional[str] = None,
196
+ type_: Optional[str] = None,
197
+ qualifier_id: Optional[str] = None,
198
+ mount_location: Optional[str] = None,
199
+ ref_object: Optional["ProductCrossReference"] = None,
200
+ ) -> None:
201
+ """Add a ``ProductCrossReference``.
202
+
203
+ Fields:
204
+ - ``ProductID``
205
+ - ``Type``
206
+ - ``QualifierID``
207
+ - ``MountLocation``
208
+ """
209
+ from .relations import ProductCrossReference
210
+
211
+ ref = ref_object or ProductCrossReference(
212
+ {
213
+ "ProductID": product_id,
214
+ "Type": type_,
215
+ "QualifierID": qualifier_id,
216
+ "MountLocation": mount_location,
217
+ }
218
+ )
219
+ if not isinstance(self._data["ProductCrossReference"], list):
220
+ self._data["ProductCrossReference"] = []
221
+ self._data["ProductCrossReference"].append(ref)
222
+
223
+ def delete_product_reference(
224
+ self,
225
+ product_id: Optional[str] = None,
226
+ type_: Optional[str] = None,
227
+ qualifier_id: Optional[str] = None,
228
+ mount_location: Optional[str] = None,
229
+ delete_ref_object: Optional["DeleteProductCrossReference"] = None,
230
+ ) -> None:
231
+ """Add ``DeleteProductCrossReference`` to remove a product link.
232
+
233
+ Fields:
234
+ - ``ProductID``
235
+ - ``Type``
236
+ - ``QualifierID``
237
+ - ``MountLocation``
238
+ """
239
+ from .deletion import DeleteProductCrossReference
240
+
241
+ delete_ref = delete_ref_object or DeleteProductCrossReference(
242
+ {
243
+ "ProductID": product_id,
244
+ "Type": type_,
245
+ "QualifierID": qualifier_id,
246
+ "MountLocation": mount_location,
247
+ }
248
+ )
249
+ if not isinstance(self._data["DeleteProductCrossReference"], list):
250
+ self._data["DeleteProductCrossReference"] = []
251
+ self._data["DeleteProductCrossReference"].append(delete_ref)
252
+
253
+ def add_classification_reference(
254
+ self,
255
+ classification_id: Optional[str] = None,
256
+ type_: Optional[str] = None,
257
+ qualifier_id: Optional[str] = None,
258
+ mount_location: Optional[str] = None,
259
+ ref_object: Optional["ClassificationReference"] = None,
260
+ ) -> None:
261
+ """Add ``ClassificationReference`` (not CrossReference).
262
+
263
+ Fields:
264
+ - ``ClassificationID``
265
+ - ``Type``
266
+ - ``QualifierID``
267
+ - ``MountLocation``
268
+ """
269
+ from .relations import ClassificationReference, ClassificationCrossReference
270
+
271
+ if ref_object is not None:
272
+ ref = ref_object
273
+ if isinstance(ref, ClassificationCrossReference):
274
+ ref = ClassificationReference(dict(ref._data))
275
+ else:
276
+ ref = ClassificationReference(
277
+ {
278
+ "ClassificationID": classification_id,
279
+ "Type": type_,
280
+ "QualifierID": qualifier_id,
281
+ "MountLocation": mount_location,
282
+ }
283
+ )
284
+
285
+ if not isinstance(self._data["ClassificationReference"], list):
286
+ self._data["ClassificationReference"] = []
287
+ self._data["ClassificationReference"].append(ref)
288
+
289
+ def add_classification_cross_reference(
290
+ self,
291
+ classification_id: Optional[str] = None,
292
+ type_: Optional[str] = None,
293
+ qualifier_id: Optional[str] = None,
294
+ mount_location: Optional[str] = None,
295
+ ref_object: Optional["ClassificationCrossReference"] = None,
296
+ ) -> None:
297
+ """Add explicit ``ClassificationCrossReference``.
298
+
299
+ Fields:
300
+ - ``ClassificationID``
301
+ - ``Type``
302
+ - ``QualifierID``
303
+ - ``MountLocation``
304
+ """
305
+ from .relations import ClassificationCrossReference
306
+
307
+ ref = ref_object or ClassificationCrossReference(
308
+ {
309
+ "ClassificationID": classification_id,
310
+ "Type": type_,
311
+ "QualifierID": qualifier_id,
312
+ "MountLocation": mount_location,
313
+ }
314
+ )
315
+ if not isinstance(self._data["ClassificationCrossReference"], list):
316
+ self._data["ClassificationCrossReference"] = []
317
+ self._data["ClassificationCrossReference"].append(ref)
318
+
319
+ def add_entity_reference(
320
+ self,
321
+ entity_id: Optional[str] = None,
322
+ type_: Optional[str] = None,
323
+ qualifier_id: Optional[str] = None,
324
+ mount_location: Optional[str] = None,
325
+ ref_object: Optional["EntityCrossReference"] = None,
326
+ ) -> None:
327
+ """Add ``EntityCrossReference``.
328
+
329
+ Fields:
330
+ - ``EntityID``
331
+ - ``Type``
332
+ - ``QualifierID``
333
+ - ``MountLocation``
334
+ """
335
+ from .relations import EntityCrossReference
336
+
337
+ ref = ref_object or EntityCrossReference(
338
+ {
339
+ "EntityID": entity_id,
340
+ "Type": type_,
341
+ "QualifierID": qualifier_id,
342
+ "MountLocation": mount_location,
343
+ }
344
+ )
345
+ if not isinstance(self._data["EntityCrossReference"], list):
346
+ self._data["EntityCrossReference"] = []
347
+ self._data["EntityCrossReference"].append(ref)
348
+
349
+ def set_workflow_state_bind(
350
+ self,
351
+ workflow_id: Optional[str] = None,
352
+ state_id: Optional[str] = None,
353
+ bind_data: Optional[Dict[str, Any]] = None,
354
+ bind_object: Optional["STEPWorkflowStateBind"] = None,
355
+ ) -> None:
356
+ """Set ``STEPWorkflowStateBind``.
357
+
358
+ Required XSD keys:
359
+ - ``WorkflowID``
360
+ - ``StateID``
361
+
362
+ Compatibility:
363
+ - legacy dict keys ``ID`` and ``CurrentStateID`` are normalized.
364
+ """
365
+ if bind_object is not None:
366
+ self._data["STEPWorkflowStateBind"] = bind_object
367
+ return
368
+
369
+ if bind_data is not None:
370
+ normalized = dict(bind_data)
371
+ if "WorkflowID" not in normalized and "ID" in normalized:
372
+ normalized["WorkflowID"] = normalized.pop("ID")
373
+ if "StateID" not in normalized and "CurrentStateID" in normalized:
374
+ normalized["StateID"] = normalized.pop("CurrentStateID")
375
+ self._data["STEPWorkflowStateBind"] = normalized
376
+ return
377
+
378
+ self._data["STEPWorkflowStateBind"] = {
379
+ "WorkflowID": workflow_id,
380
+ "StateID": state_id,
381
+ }
382
+
383
+ def add_sequence_product(
384
+ self,
385
+ product_id: Optional[str] = None,
386
+ changed: Optional[bool] = None,
387
+ key_values: Optional[List[Dict[str, Any]]] = None,
388
+ sequence_data: Optional[Dict[str, Any]] = None,
389
+ sequence_object: Optional["SequenceProduct"] = None,
390
+ ) -> None:
391
+ """Add ``SequenceProduct``.
392
+
393
+ XSD fields:
394
+ - ``ID`` (required)
395
+ - ``Changed`` (optional)
396
+ - ``KeyValue`` (optional, list)
397
+ """
398
+ if sequence_object is not None:
399
+ entry: Any = sequence_object
400
+ elif sequence_data is not None:
401
+ entry = sequence_data
402
+ else:
403
+ entry = {"ID": product_id, "Changed": changed, "KeyValue": key_values}
404
+
405
+ if not isinstance(self._data.get("SequenceProduct"), list):
406
+ self._data["SequenceProduct"] = []
407
+ self._data["SequenceProduct"].append(entry)
408
+
409
+ def add_current_task(self, task_data: Optional[Dict[str, Any]] = None) -> None:
410
+ """Add ``CurrentTasks``/``Task`` data.
411
+
412
+ Typical task keys:
413
+ - ``STEPWorkflowID``
414
+ - ``StateID``
415
+ - ``AssigneeID``
416
+ - ``AssignedToGroup``
417
+ - ``EntryTime``
418
+ - optional: ``Deadline``, ``Priority``, ``StatusID``
419
+ """
420
+ if not isinstance(self._data.get("CurrentTasks"), list):
421
+ self._data["CurrentTasks"] = []
422
+ self._data["CurrentTasks"].append(task_data or {})
423
+
424
+ def add_term(self, term_data: Optional[Dict[str, Any]] = None) -> None:
425
+ """Add ``Terms``/``Term`` data.
426
+
427
+ Common term keys:
428
+ - ``TermsListID``
429
+ - ``MinimumQuantity`` / ``MaximumQuantity``
430
+ - ``StartDate`` / ``EndDate``
431
+ - ``LeadTime``
432
+ - ``UnitID``
433
+ - ``LotSize``
434
+ - optional text value: ``value``
435
+ """
436
+ if not isinstance(self._data.get("Terms"), list):
437
+ self._data["Terms"] = []
438
+ self._data["Terms"].append(term_data or {})
439
+
440
+ def add_table(self, table_data: Optional[Dict[str, Any]] = None) -> None:
441
+ """Add ``Tables`` data.
442
+
443
+ Supported container forms:
444
+ - ``{"Table": {...}}``
445
+ - ``{"TableDefinition": {...}}``
446
+
447
+ Common ``Table`` attributes:
448
+ - ``ID``
449
+ - ``ContextID``
450
+ - ``TableTypeID``
451
+ """
452
+ if not isinstance(self._data.get("Tables"), list):
453
+ self._data["Tables"] = []
454
+ self._data["Tables"].append(table_data or {})
455
+
456
+ def add_override_sub_product(
457
+ self,
458
+ product_id: Optional[str] = None,
459
+ analyzer_result: Optional[str] = None,
460
+ override_data: Optional[Dict[str, Any]] = None,
461
+ override_object: Optional["OverrideSubProduct"] = None,
462
+ ) -> None:
463
+ """Add ``OverrideSubProduct``.
464
+
465
+ XSD fields:
466
+ - ``ProductID`` (required)
467
+ - ``AnalyzerResult`` (optional, deprecated)
468
+ """
469
+ if override_object is not None:
470
+ entry: Any = override_object
471
+ elif override_data is not None:
472
+ entry = override_data
473
+ else:
474
+ entry = {"ProductID": product_id, "AnalyzerResult": analyzer_result}
475
+
476
+ if not isinstance(self._data.get("OverrideSubProduct"), list):
477
+ self._data["OverrideSubProduct"] = []
478
+ self._data["OverrideSubProduct"].append(entry)
479
+
480
+ def add_packaging_reference(
481
+ self,
482
+ product_id: Optional[str] = None,
483
+ quantity: Optional[str] = None,
484
+ packaging_data: Optional[Dict[str, Any]] = None,
485
+ packaging_object: Optional["PackagingReference"] = None,
486
+ ) -> None:
487
+ """Add ``PackagingReference``.
488
+
489
+ Required XSD fields:
490
+ - ``ProductID``
491
+ - ``Quantity``
492
+ """
493
+ if packaging_object is not None:
494
+ entry: Any = packaging_object
495
+ elif packaging_data is not None:
496
+ entry = packaging_data
497
+ else:
498
+ entry = {"ProductID": product_id, "Quantity": quantity}
499
+
500
+ if not isinstance(self._data.get("PackagingReference"), list):
501
+ self._data["PackagingReference"] = []
502
+ self._data["PackagingReference"].append(entry)
503
+
504
+
505
+ class ProductReplacementRule(BaseObject):
506
+ _GROUP_NAME: str = "product_replacement_rules"
507
+ _TAG_NAME: str = "Product"
508
+ _ATTRIBUTE_NAMES: List[str] = [
509
+ "ID",
510
+ "ParentId",
511
+ "UserTypeID",
512
+ "QualifierID",
513
+ "Changed",
514
+ "Republished",
515
+ "Selected",
516
+ "Referenced",
517
+ ]
518
+ _TEXT_NAMES: List[str] = ["Name"]
519
+ _CHILDREN_NAMES: List[str] = ["KeyValue", "ParentKeyValue", "Values"]