ordercloud-python 2026.4.1__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 (114) hide show
  1. ordercloud/__init__.py +37 -0
  2. ordercloud/auth.py +136 -0
  3. ordercloud/client.py +211 -0
  4. ordercloud/config.py +42 -0
  5. ordercloud/errors.py +47 -0
  6. ordercloud/http.py +218 -0
  7. ordercloud/middleware.py +66 -0
  8. ordercloud/models/__init__.py +271 -0
  9. ordercloud/models/address.py +47 -0
  10. ordercloud/models/api_client.py +116 -0
  11. ordercloud/models/approval.py +73 -0
  12. ordercloud/models/assignments.py +402 -0
  13. ordercloud/models/auth_models.py +114 -0
  14. ordercloud/models/bundle.py +31 -0
  15. ordercloud/models/buyer.py +271 -0
  16. ordercloud/models/catalog.py +33 -0
  17. ordercloud/models/category.py +35 -0
  18. ordercloud/models/cost_center.py +27 -0
  19. ordercloud/models/credit_card.py +35 -0
  20. ordercloud/models/delivery.py +277 -0
  21. ordercloud/models/discount.py +63 -0
  22. ordercloud/models/integration.py +76 -0
  23. ordercloud/models/inventory_record.py +53 -0
  24. ordercloud/models/line_item.py +95 -0
  25. ordercloud/models/line_item_types.py +89 -0
  26. ordercloud/models/message_sender.py +80 -0
  27. ordercloud/models/misc.py +280 -0
  28. ordercloud/models/open_id_connect.py +47 -0
  29. ordercloud/models/order.py +477 -0
  30. ordercloud/models/order_return.py +92 -0
  31. ordercloud/models/payment.py +77 -0
  32. ordercloud/models/price_schedule.py +76 -0
  33. ordercloud/models/product.py +227 -0
  34. ordercloud/models/product_collection.py +186 -0
  35. ordercloud/models/promotion.py +297 -0
  36. ordercloud/models/security.py +89 -0
  37. ordercloud/models/shared.py +131 -0
  38. ordercloud/models/shipment.py +150 -0
  39. ordercloud/models/spec.py +67 -0
  40. ordercloud/models/spending_account.py +33 -0
  41. ordercloud/models/subscription.py +125 -0
  42. ordercloud/models/supplier.py +43 -0
  43. ordercloud/models/sync.py +172 -0
  44. ordercloud/models/user.py +207 -0
  45. ordercloud/models/user_group.py +27 -0
  46. ordercloud/models/webhook.py +58 -0
  47. ordercloud/py.typed +0 -0
  48. ordercloud/resources/__init__.py +65 -0
  49. ordercloud/resources/addresses.py +228 -0
  50. ordercloud/resources/admin_addresses.py +128 -0
  51. ordercloud/resources/admin_user_groups.py +185 -0
  52. ordercloud/resources/admin_users.py +150 -0
  53. ordercloud/resources/api_clients.py +308 -0
  54. ordercloud/resources/approval_rules.py +144 -0
  55. ordercloud/resources/base.py +145 -0
  56. ordercloud/resources/bundle_line_items.py +59 -0
  57. ordercloud/resources/bundle_subscription_items.py +54 -0
  58. ordercloud/resources/bundles.py +278 -0
  59. ordercloud/resources/buyer_groups.py +128 -0
  60. ordercloud/resources/buyers.py +164 -0
  61. ordercloud/resources/cart.py +613 -0
  62. ordercloud/resources/catalogs.py +311 -0
  63. ordercloud/resources/categories.py +392 -0
  64. ordercloud/resources/cost_centers.py +222 -0
  65. ordercloud/resources/credit_cards.py +227 -0
  66. ordercloud/resources/delivery_configurations.py +132 -0
  67. ordercloud/resources/discounts.py +201 -0
  68. ordercloud/resources/entity_syncs.py +534 -0
  69. ordercloud/resources/error_configs.py +71 -0
  70. ordercloud/resources/forgotten_credentials.py +74 -0
  71. ordercloud/resources/group_orders.py +28 -0
  72. ordercloud/resources/impersonation_configs.py +132 -0
  73. ordercloud/resources/incrementors.py +128 -0
  74. ordercloud/resources/integration_events.py +203 -0
  75. ordercloud/resources/inventory_integrations.py +65 -0
  76. ordercloud/resources/inventory_records.py +484 -0
  77. ordercloud/resources/line_items.py +262 -0
  78. ordercloud/resources/locales.py +203 -0
  79. ordercloud/resources/me.py +1882 -0
  80. ordercloud/resources/message_senders.py +261 -0
  81. ordercloud/resources/open_id_connects.py +128 -0
  82. ordercloud/resources/order_returns.py +306 -0
  83. ordercloud/resources/order_syncs.py +65 -0
  84. ordercloud/resources/orders.py +689 -0
  85. ordercloud/resources/payments.py +176 -0
  86. ordercloud/resources/price_schedules.py +164 -0
  87. ordercloud/resources/product_collections.py +116 -0
  88. ordercloud/resources/product_facets.py +128 -0
  89. ordercloud/resources/product_syncs.py +76 -0
  90. ordercloud/resources/products.py +454 -0
  91. ordercloud/resources/promotion_integrations.py +65 -0
  92. ordercloud/resources/promotions.py +203 -0
  93. ordercloud/resources/security_profiles.py +222 -0
  94. ordercloud/resources/seller_approval_rules.py +128 -0
  95. ordercloud/resources/shipments.py +256 -0
  96. ordercloud/resources/specs.py +313 -0
  97. ordercloud/resources/spending_accounts.py +227 -0
  98. ordercloud/resources/subscription_integrations.py +65 -0
  99. ordercloud/resources/subscription_items.py +146 -0
  100. ordercloud/resources/subscriptions.py +128 -0
  101. ordercloud/resources/supplier_addresses.py +144 -0
  102. ordercloud/resources/supplier_user_groups.py +210 -0
  103. ordercloud/resources/supplier_users.py +170 -0
  104. ordercloud/resources/suppliers.py +190 -0
  105. ordercloud/resources/tracking_events.py +130 -0
  106. ordercloud/resources/user_groups.py +210 -0
  107. ordercloud/resources/users.py +254 -0
  108. ordercloud/resources/webhooks.py +128 -0
  109. ordercloud/resources/xp_indices.py +77 -0
  110. ordercloud/sync_client.py +170 -0
  111. ordercloud_python-2026.4.1.dist-info/METADATA +552 -0
  112. ordercloud_python-2026.4.1.dist-info/RECORD +114 -0
  113. ordercloud_python-2026.4.1.dist-info/WHEEL +4 -0
  114. ordercloud_python-2026.4.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,256 @@
1
+ # GENERATED by tools/codegen — DO NOT EDIT
2
+ # Source: ordercloud-openapi-v3.json
3
+ """OrderCloud Shipments API resource."""
4
+
5
+ from __future__ import annotations
6
+ from typing import Any, Optional, Union
7
+
8
+ from ..models.address import Address
9
+ from ..models.shipment import Shipment, ShipmentItem
10
+ from ..models.shared import ListPage
11
+ from .base import BaseResource
12
+
13
+ __all__ = ["ShipmentsResource"]
14
+
15
+
16
+ class ShipmentsResource(BaseResource):
17
+ """Operations on OrderCloud Shipments."""
18
+
19
+ async def list(
20
+ self,
21
+ *,
22
+ order_id: Optional[str] = None,
23
+ search: Optional[str] = None,
24
+ search_on: Optional[str] = None,
25
+ sort_by: Optional[str] = None,
26
+ page: Optional[int] = None,
27
+ page_size: Optional[int] = None,
28
+ filters: Optional[dict[str, Any]] = None,
29
+ ) -> ListPage[Shipment]:
30
+ """List shipments
31
+
32
+ Args:
33
+ order_id: ID of the order.
34
+ search: Word or phrase to search for.
35
+ search_on: Comma-delimited list of fields to search on.
36
+ sort_by: Comma-delimited list of fields to sort by.
37
+ page: Page of results to return. When paginating through many items (> page 30), we recommend the "Last ID" method, as outlined in the Advanced Querying documentation.
38
+ page_size: Number of results to return per page.
39
+ filters: An object or dictionary representing key/value pairs to apply as filters. Valid keys are top-level properties of the returned model or 'xp.???'
40
+
41
+ Returns:
42
+ A paginated list of Shipment objects.
43
+ """
44
+ params = self._build_list_params(
45
+ search=search,
46
+ search_on=search_on,
47
+ sort_by=sort_by,
48
+ page=page,
49
+ page_size=page_size,
50
+ filters=filters,
51
+ )
52
+ if order_id is not None:
53
+ params["orderID"] = order_id
54
+ resp = await self._http.get("/shipments", **params)
55
+ return self._parse_list(resp.json(), Shipment)
56
+
57
+ async def create(
58
+ self,
59
+ shipment: Union[Shipment, dict[str, Any]],
60
+ ) -> Shipment:
61
+ """Create a shipment
62
+
63
+ Args:
64
+ shipment: A ``Shipment`` model or dict.
65
+
66
+ Returns:
67
+ The Shipment object.
68
+ """
69
+ resp = await self._http.post("/shipments", json=self._serialize(shipment))
70
+ return Shipment(**resp.json())
71
+
72
+ async def get(
73
+ self,
74
+ shipment_id: str,
75
+ ) -> Shipment:
76
+ """Retrieve a shipment
77
+
78
+ Args:
79
+ shipment_id: ID of the shipment.
80
+
81
+ Returns:
82
+ The Shipment object.
83
+ """
84
+ resp = await self._http.get(f"/shipments/{shipment_id}")
85
+ return Shipment(**resp.json())
86
+
87
+ async def save(
88
+ self,
89
+ shipment_id: str,
90
+ shipment: Union[Shipment, dict[str, Any]],
91
+ ) -> Shipment:
92
+ """Create or update a shipment
93
+
94
+ Args:
95
+ shipment_id: ID of the shipment.
96
+ shipment: A ``Shipment`` model or dict.
97
+
98
+ Returns:
99
+ The Shipment object.
100
+ """
101
+ resp = await self._http.put(
102
+ f"/shipments/{shipment_id}",
103
+ json=self._serialize(shipment),
104
+ )
105
+ return Shipment(**resp.json())
106
+
107
+ async def delete(
108
+ self,
109
+ shipment_id: str,
110
+ ) -> None:
111
+ """Delete a shipment
112
+
113
+ Args:
114
+ shipment_id: ID of the shipment.
115
+ """
116
+ await self._http.delete(f"/shipments/{shipment_id}")
117
+
118
+ async def patch(
119
+ self,
120
+ shipment_id: str,
121
+ partial: dict[str, Any],
122
+ ) -> Shipment:
123
+ """Partially update a shipment
124
+
125
+ Args:
126
+ shipment_id: ID of the shipment.
127
+ partial: A dict of fields to update.
128
+
129
+ Returns:
130
+ The Shipment object.
131
+ """
132
+ resp = await self._http.patch(f"/shipments/{shipment_id}", json=partial)
133
+ return Shipment(**resp.json())
134
+
135
+ async def list_items(
136
+ self,
137
+ shipment_id: str,
138
+ *,
139
+ sort_by: Optional[str] = None,
140
+ page: Optional[int] = None,
141
+ page_size: Optional[int] = None,
142
+ filters: Optional[dict[str, Any]] = None,
143
+ ) -> ListPage[ShipmentItem]:
144
+ """List shipment items
145
+
146
+ Args:
147
+ shipment_id: ID of the shipment.
148
+ sort_by: Comma-delimited list of fields to sort by.
149
+ page: Page of results to return. When paginating through many items (> page 30), we recommend the "Last ID" method, as outlined in the Advanced Querying documentation.
150
+ page_size: Number of results to return per page.
151
+ filters: An object or dictionary representing key/value pairs to apply as filters. Valid keys are top-level properties of the returned model or 'xp.???'
152
+
153
+ Returns:
154
+ A paginated list of ShipmentItem objects.
155
+ """
156
+ params = self._build_list_params(
157
+ sort_by=sort_by,
158
+ page=page,
159
+ page_size=page_size,
160
+ filters=filters,
161
+ )
162
+ resp = await self._http.get(f"/shipments/{shipment_id}/items", **params)
163
+ return self._parse_list(resp.json(), ShipmentItem)
164
+
165
+ async def save_item(
166
+ self,
167
+ shipment_id: str,
168
+ shipment_item: Union[ShipmentItem, dict[str, Any]],
169
+ ) -> ShipmentItem:
170
+ """Create or update a shipment item
171
+
172
+ Args:
173
+ shipment_id: ID of the shipment.
174
+ shipment_item: A ``ShipmentItem`` model or dict. Required fields: OrderID, LineItemID, QuantityShipped.
175
+
176
+ Returns:
177
+ The ShipmentItem object.
178
+ """
179
+ resp = await self._http.post(
180
+ f"/shipments/{shipment_id}/items", json=self._serialize(shipment_item)
181
+ )
182
+ return ShipmentItem(**resp.json())
183
+
184
+ async def get_item(
185
+ self,
186
+ shipment_id: str,
187
+ order_id: str,
188
+ line_item_id: str,
189
+ ) -> ShipmentItem:
190
+ """Retrieve a shipment item
191
+
192
+ Args:
193
+ shipment_id: ID of the shipment.
194
+ order_id: ID of the order.
195
+ line_item_id: ID of the line item.
196
+
197
+ Returns:
198
+ The ShipmentItem object.
199
+ """
200
+ resp = await self._http.get(f"/shipments/{shipment_id}/items/{order_id}/{line_item_id}")
201
+ return ShipmentItem(**resp.json())
202
+
203
+ async def delete_item(
204
+ self,
205
+ shipment_id: str,
206
+ order_id: str,
207
+ line_item_id: str,
208
+ ) -> None:
209
+ """Delete a shipment item
210
+
211
+ Args:
212
+ shipment_id: ID of the shipment.
213
+ order_id: ID of the order.
214
+ line_item_id: ID of the line item.
215
+ """
216
+ await self._http.delete(f"/shipments/{shipment_id}/items/{order_id}/{line_item_id}")
217
+
218
+ async def set_ship_from_address(
219
+ self,
220
+ shipment_id: str,
221
+ address: Union[Address, dict[str, Any]],
222
+ ) -> Shipment:
223
+ """Set a ship from address
224
+
225
+ Args:
226
+ shipment_id: ID of the shipment.
227
+ address: A ``Address`` model or dict. Required fields: Street1, City, Country.
228
+
229
+ Returns:
230
+ The Shipment object.
231
+ """
232
+ resp = await self._http.put(
233
+ f"/shipments/{shipment_id}/shipfrom",
234
+ json=self._serialize(address),
235
+ )
236
+ return Shipment(**resp.json())
237
+
238
+ async def set_ship_to_address(
239
+ self,
240
+ shipment_id: str,
241
+ address: Union[Address, dict[str, Any]],
242
+ ) -> Shipment:
243
+ """Set a ship to address
244
+
245
+ Args:
246
+ shipment_id: ID of the shipment.
247
+ address: A ``Address`` model or dict. Required fields: Street1, City, Country.
248
+
249
+ Returns:
250
+ The Shipment object.
251
+ """
252
+ resp = await self._http.put(
253
+ f"/shipments/{shipment_id}/shipto",
254
+ json=self._serialize(address),
255
+ )
256
+ return Shipment(**resp.json())
@@ -0,0 +1,313 @@
1
+ # GENERATED by tools/codegen — DO NOT EDIT
2
+ # Source: ordercloud-openapi-v3.json
3
+ """OrderCloud Specs API resource."""
4
+
5
+ from __future__ import annotations
6
+ from typing import Any, Optional, Union
7
+
8
+ from ..models.assignments import SpecProductAssignment
9
+ from ..models.spec import Spec, SpecOption
10
+ from ..models.shared import ListPage
11
+ from .base import BaseResource
12
+
13
+ __all__ = ["SpecsResource"]
14
+
15
+
16
+ class SpecsResource(BaseResource):
17
+ """Operations on OrderCloud Specs."""
18
+
19
+ async def list(
20
+ self,
21
+ *,
22
+ search: Optional[str] = None,
23
+ search_on: Optional[str] = None,
24
+ sort_by: Optional[str] = None,
25
+ page: Optional[int] = None,
26
+ page_size: Optional[int] = None,
27
+ filters: Optional[dict[str, Any]] = None,
28
+ ) -> ListPage[Spec]:
29
+ """List specs
30
+
31
+ Args:
32
+ search: Word or phrase to search for.
33
+ search_on: Comma-delimited list of fields to search on.
34
+ sort_by: Comma-delimited list of fields to sort by.
35
+ page: Page of results to return. When paginating through many items (> page 30), we recommend the "Last ID" method, as outlined in the Advanced Querying documentation.
36
+ page_size: Number of results to return per page.
37
+ filters: An object or dictionary representing key/value pairs to apply as filters. Valid keys are top-level properties of the returned model or 'xp.???'
38
+
39
+ Returns:
40
+ A paginated list of Spec objects.
41
+ """
42
+ params = self._build_list_params(
43
+ search=search,
44
+ search_on=search_on,
45
+ sort_by=sort_by,
46
+ page=page,
47
+ page_size=page_size,
48
+ filters=filters,
49
+ )
50
+ resp = await self._http.get("/specs", **params)
51
+ return self._parse_list(resp.json(), Spec)
52
+
53
+ async def create(
54
+ self,
55
+ spec: Union[Spec, dict[str, Any]],
56
+ ) -> Spec:
57
+ """Create a spec
58
+
59
+ Args:
60
+ spec: A ``Spec`` model or dict. Required fields: Name.
61
+
62
+ Returns:
63
+ The Spec object.
64
+ """
65
+ resp = await self._http.post("/specs", json=self._serialize(spec))
66
+ return Spec(**resp.json())
67
+
68
+ async def get(
69
+ self,
70
+ spec_id: str,
71
+ ) -> Spec:
72
+ """Retrieve a spec
73
+
74
+ Args:
75
+ spec_id: ID of the spec.
76
+
77
+ Returns:
78
+ The Spec object.
79
+ """
80
+ resp = await self._http.get(f"/specs/{spec_id}")
81
+ return Spec(**resp.json())
82
+
83
+ async def save(
84
+ self,
85
+ spec_id: str,
86
+ spec: Union[Spec, dict[str, Any]],
87
+ ) -> Spec:
88
+ """Create or update a spec
89
+
90
+ Args:
91
+ spec_id: ID of the spec.
92
+ spec: A ``Spec`` model or dict. Required fields: Name.
93
+
94
+ Returns:
95
+ The Spec object.
96
+ """
97
+ resp = await self._http.put(
98
+ f"/specs/{spec_id}",
99
+ json=self._serialize(spec),
100
+ )
101
+ return Spec(**resp.json())
102
+
103
+ async def delete(
104
+ self,
105
+ spec_id: str,
106
+ ) -> None:
107
+ """Delete a spec
108
+
109
+ Args:
110
+ spec_id: ID of the spec.
111
+ """
112
+ await self._http.delete(f"/specs/{spec_id}")
113
+
114
+ async def patch(
115
+ self,
116
+ spec_id: str,
117
+ partial: dict[str, Any],
118
+ ) -> Spec:
119
+ """Partially update a spec
120
+
121
+ Args:
122
+ spec_id: ID of the spec.
123
+ partial: A dict of fields to update.
124
+
125
+ Returns:
126
+ The Spec object.
127
+ """
128
+ resp = await self._http.patch(f"/specs/{spec_id}", json=partial)
129
+ return Spec(**resp.json())
130
+
131
+ async def list_options(
132
+ self,
133
+ spec_id: str,
134
+ *,
135
+ search: Optional[str] = None,
136
+ search_on: Optional[str] = None,
137
+ sort_by: Optional[str] = None,
138
+ page: Optional[int] = None,
139
+ page_size: Optional[int] = None,
140
+ filters: Optional[dict[str, Any]] = None,
141
+ ) -> ListPage[SpecOption]:
142
+ """List spec options
143
+
144
+ Args:
145
+ spec_id: ID of the spec.
146
+ search: Word or phrase to search for.
147
+ search_on: Comma-delimited list of fields to search on.
148
+ sort_by: Comma-delimited list of fields to sort by.
149
+ page: Page of results to return. When paginating through many items (> page 30), we recommend the "Last ID" method, as outlined in the Advanced Querying documentation.
150
+ page_size: Number of results to return per page.
151
+ filters: An object or dictionary representing key/value pairs to apply as filters. Valid keys are top-level properties of the returned model or 'xp.???'
152
+
153
+ Returns:
154
+ A paginated list of SpecOption objects.
155
+ """
156
+ params = self._build_list_params(
157
+ search=search,
158
+ search_on=search_on,
159
+ sort_by=sort_by,
160
+ page=page,
161
+ page_size=page_size,
162
+ filters=filters,
163
+ )
164
+ resp = await self._http.get(f"/specs/{spec_id}/options", **params)
165
+ return self._parse_list(resp.json(), SpecOption)
166
+
167
+ async def create_option(
168
+ self,
169
+ spec_id: str,
170
+ spec_option: Union[SpecOption, dict[str, Any]],
171
+ ) -> SpecOption:
172
+ """Create a spec option
173
+
174
+ Args:
175
+ spec_id: ID of the spec.
176
+ spec_option: A ``SpecOption`` model or dict. Required fields: Value.
177
+
178
+ Returns:
179
+ The SpecOption object.
180
+ """
181
+ resp = await self._http.post(f"/specs/{spec_id}/options", json=self._serialize(spec_option))
182
+ return SpecOption(**resp.json())
183
+
184
+ async def get_option(
185
+ self,
186
+ spec_id: str,
187
+ option_id: str,
188
+ ) -> SpecOption:
189
+ """Retrieve a spec option
190
+
191
+ Args:
192
+ spec_id: ID of the spec.
193
+ option_id: ID of the option.
194
+
195
+ Returns:
196
+ The SpecOption object.
197
+ """
198
+ resp = await self._http.get(f"/specs/{spec_id}/options/{option_id}")
199
+ return SpecOption(**resp.json())
200
+
201
+ async def save_option(
202
+ self,
203
+ spec_id: str,
204
+ option_id: str,
205
+ spec_option: Union[SpecOption, dict[str, Any]],
206
+ ) -> SpecOption:
207
+ """Create or update a spec option
208
+
209
+ Args:
210
+ spec_id: ID of the spec.
211
+ option_id: ID of the option.
212
+ spec_option: A ``SpecOption`` model or dict. Required fields: Value.
213
+
214
+ Returns:
215
+ The SpecOption object.
216
+ """
217
+ resp = await self._http.put(
218
+ f"/specs/{spec_id}/options/{option_id}",
219
+ json=self._serialize(spec_option),
220
+ )
221
+ return SpecOption(**resp.json())
222
+
223
+ async def delete_option(
224
+ self,
225
+ spec_id: str,
226
+ option_id: str,
227
+ ) -> None:
228
+ """Delete a spec option
229
+
230
+ Args:
231
+ spec_id: ID of the spec.
232
+ option_id: ID of the option.
233
+ """
234
+ await self._http.delete(f"/specs/{spec_id}/options/{option_id}")
235
+
236
+ async def patch_option(
237
+ self,
238
+ spec_id: str,
239
+ option_id: str,
240
+ partial: dict[str, Any],
241
+ ) -> SpecOption:
242
+ """Partially update a spec option
243
+
244
+ Args:
245
+ spec_id: ID of the spec.
246
+ option_id: ID of the option.
247
+ partial: A dict of fields to update.
248
+
249
+ Returns:
250
+ The SpecOption object.
251
+ """
252
+ resp = await self._http.patch(f"/specs/{spec_id}/options/{option_id}", json=partial)
253
+ return SpecOption(**resp.json())
254
+
255
+ async def delete_product_assignment(
256
+ self,
257
+ spec_id: str,
258
+ product_id: str,
259
+ ) -> None:
260
+ """Delete a spec product assignment
261
+
262
+ Args:
263
+ spec_id: ID of the spec.
264
+ product_id: ID of the product.
265
+ """
266
+ await self._http.delete(f"/specs/{spec_id}/productassignments/{product_id}")
267
+
268
+ async def list_product_assignments(
269
+ self,
270
+ *,
271
+ search: Optional[str] = None,
272
+ search_on: Optional[str] = None,
273
+ sort_by: Optional[str] = None,
274
+ page: Optional[int] = None,
275
+ page_size: Optional[int] = None,
276
+ filters: Optional[dict[str, Any]] = None,
277
+ ) -> ListPage[SpecProductAssignment]:
278
+ """List spec product assignments
279
+
280
+ Args:
281
+ search: Word or phrase to search for.
282
+ search_on: Comma-delimited list of fields to search on.
283
+ sort_by: Comma-delimited list of fields to sort by.
284
+ page: Page of results to return. When paginating through many items (> page 30), we recommend the "Last ID" method, as outlined in the Advanced Querying documentation.
285
+ page_size: Number of results to return per page.
286
+ filters: An object or dictionary representing key/value pairs to apply as filters. Valid keys are top-level properties of the returned model or 'xp.???'
287
+
288
+ Returns:
289
+ A paginated list of SpecProductAssignment objects.
290
+ """
291
+ params = self._build_list_params(
292
+ search=search,
293
+ search_on=search_on,
294
+ sort_by=sort_by,
295
+ page=page,
296
+ page_size=page_size,
297
+ filters=filters,
298
+ )
299
+ resp = await self._http.get("/specs/productassignments", **params)
300
+ return self._parse_list(resp.json(), SpecProductAssignment)
301
+
302
+ async def save_product_assignment(
303
+ self,
304
+ spec_product_assignment: Union[SpecProductAssignment, dict[str, Any]],
305
+ ) -> None:
306
+ """Create or update a spec product assignment
307
+
308
+ Args:
309
+ spec_product_assignment: A ``SpecProductAssignment`` model or dict. Required fields: SpecID, ProductID.
310
+ """
311
+ await self._http.post(
312
+ "/specs/productassignments", json=self._serialize(spec_product_assignment)
313
+ )