Skip to content

Purchasing

PurchasingClient

Source code in src/spyre/purchasing.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
class PurchasingClient:

    def __init__(self, client: SpireClient):
        self.client = client
        self.endpoint = "purchasing/orders"

    def get_purchase_order(self, id: int = None, PO_number: str = None) -> 'purchaseOrder':
        """
        Retrieve a purchase order by its ID or PO number.

        Args:
            id (int, optional): The ID of the purchase order to retrieve.
            PO_number (str, optional): The purchase order number of the purchase order to retrieve.

        Returns:
            purchaseOrder: A `purchaseOrder` wrapper instance containing the retrieved data.

        Raises:
            ValueError: If neither id nor PO_number is provided, or if no matching order is found.
        """
        if id is not None:
            response = self.client._get(f"/{self.endpoint}/{str(id)}")
            return purchaseOrder.from_json(response, self.client)
        elif PO_number is not None:
            orders = self.query_purchase_order(query=PO_number)
            for order in orders:
                if getattr(order, "number", None) == PO_number:
                    return self.get_purchase_order(order.id)
            raise ValueError(f"No purchase order found for purchase order {PO_number}")
        else:
            raise ValueError("Either 'id' or 'PO_number' must be provided.")

    def create_purchase_order(self, purchase_order: 'PurchaseOrder') -> 'purchaseOrder':
        """
        Create a new purchase order.

        Sends a POST request to the purchase order endpoint.

        Args:
            purchase_order (dict): A PurchaseOrder instance containing the purchase order details.

        Returns:
            purchaseOrder: The create PurchaseOrder instance.

        Raises:
            CreateRequestError: If the creation fails or response is invalid.
        """
        if hasattr(purchase_order, "model_dump_json"):  # Pydantic v2
            payload = json.loads(
                purchase_order.model_dump_json(
                    exclude_unset=True,
                    exclude_none=True,
                    by_alias=True,   # keep camelCase if you use aliases
                )
            )
        else:  # Pydantic v1 fallback
            payload = json.loads(
                purchase_order.json(
                    exclude_unset=True,
                    exclude_none=True,
                    by_alias=True,
                )
            )
        response = self.client._post(f"/{self.endpoint}", json=payload)
        if response.get('status_code') == 201:
            location = response.get('headers').get('location')
            parsed_url = urlparse(location)
            path_segments = parsed_url.path.rstrip("/").split("/")
            id = path_segments[-1]
            return self.get_purchase_order(id)
        else:
            error_message = response.get('content')
            raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

    def update_purchase_order(self, id: int, purchase_order: 'purchaseOrder') -> 'purchaseOrder':
        """
        Update an existing purchase order by ID.

        Sends a PUT request to the purchase order endpoint with the provided sales order data
        to update the existing record. Returns a wrapped `purchaseOrder` object containing
        the updated information.

        Args:
            id (int): The ID of the purchase order to update.
            purchase_order (PurchaseOrder): A PurchaseOrder instance with the purchase order details.

        Returns:
            purchaseOrder: An instance of the purchaseOrder wrapper class initialized with the 
                           updated data and client session.
        """
        if purchase_order.status == "I":
            raise ValueError(f"Cannot update an issued purchase order for {purchase_order.number}")
        else:
            response = self.client._put(f"/{self.endpoint}/{str(id)}", json=purchase_order.model_dump(exclude_none=True, exclude_unset=True))
        return purchaseOrder.from_json(response, self.client)

    def delete_purchase_order(self, id: int) -> bool:
        """
        Delete a purchase order by its ID.

        Sends a DELETE request to the purchase order endpoint to remove the specified
        purchase order from the system.

        Args:
            id (int): The ID of the purchase order to delete.

        Returns:
            bool: True if the sales order was successfully deleted, False otherwise.
        """
        order = self.get_purchase_order(id)
        if order.model.status in ("I", "R"):
            raise ValueError(f"Cannot delete an issued or received purchase order for {order.number}")
        else:
            return self.client._delete(f"/{self.endpoint}/{str(id)}")

    def query_purchase_order(
            self,
            *,
            query: Optional[str] = None,
            sort: Optional[Dict[str,str]] = None,
            filter: Optional[Dict[str, Any]] = None,
            all: bool = False,
            limit: int = 1000,
            start: int = 0,
            **extra_params
    ) -> List["purchaseOrder"]:
        """
        Query purchase orders with optional full-text search, filtering, multi-field sorting, and pagination.

        Args:
            query (str, optional): Full-text search string.
            sort (dict, optional): Dictionary of sorting rules (e.g., {"date": "desc", "number": "asc"}).
            filter (dict, optional): Dictionary of filters to apply (will be JSON-encoded and URL-safe).
            all (bool, optional): If True, retrieves all pages of results.
            limit (int, optional): Number of results per page (max 1000).
            start (int, optional): Starting offset for pagination.
            **extra_params (Any): Any additional parameters to include in the query.

        Returns:
            List[purchaseOrder]: List of wrapped purchase order resources.
        """
        return self.client._query(
            endpoint=self.endpoint,
            resource_cls=purchaseOrder,
            query=query,
            sort=sort,
            filter=filter,
            all=all,
            limit=limit,
            start=start,
            **extra_params
        )

    def issue_purchase_order(self, id:int) -> 'purchaseOrder':
        """
        Issue a purchase order by its ID.

        Sends a request to Spire to change the purchase order's status to
        “Issued”. Issuing an order typically means it has been confirmed
        and is ready to be sent to the vendor.

        Args:
            id (int): The ID of the purchaseOrder to issue.

        Returns:
            purchaseOrder: The updated purchase order object with the status set to "I" (Issued).

        Raises:
            CreateRequestError: If the request fails or the API returns an error status.
        """
        response = self.client._post(f"/{self.endpoint}/{str(id)}/issue")
        if response.get("status_code") != 200:
            raise CreateRequestError(f"Failed to issue purchase order: {id}: {response.text}")
        return purchaseOrder.from_json(response, self.client)

    def receive_purchase_order(self, id: int, receiveAll: bool = None) -> 'purchaseOrder':
        """
        Receive a purchase order by its ID.

        This updates the order in Spire to reflect that items have been received. 
        Typically, this should only be called on purchase orders with status "Issued".

        Args:
            id (int): The ID of the purchaseOrder to receive.
            receiveAll (bool, optional): An optional boolean to recieve all quantites on the purchase order.

        Returns:
            purchaseOrder: The updated purchase order object with the status set to "R" (Received).

        Raises: CreateRequestError: If the request fails or the API returns an error status.
        """
        if receiveAll:
            order = self.get_purchase_order(id)
            for item in order.model.items:
                item.receiveQty = item.orderQty
            order.update()
        response = self.client._post(f"/{self.endpoint}/{str(id)}/receive")
        if response.get("status_code") != 200:
            raise CreateRequestError(f"Failed to receive purchase order: {id}: {response.text}")
        return purchaseOrder.from_json(response, self.client)

create_purchase_order(purchase_order)

Create a new purchase order.

Sends a POST request to the purchase order endpoint.

Parameters:

Name Type Description Default
purchase_order dict

A PurchaseOrder instance containing the purchase order details.

required

Returns:

Name Type Description
purchaseOrder purchaseOrder

The create PurchaseOrder instance.

Raises:

Type Description
CreateRequestError

If the creation fails or response is invalid.

Source code in src/spyre/purchasing.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def create_purchase_order(self, purchase_order: 'PurchaseOrder') -> 'purchaseOrder':
    """
    Create a new purchase order.

    Sends a POST request to the purchase order endpoint.

    Args:
        purchase_order (dict): A PurchaseOrder instance containing the purchase order details.

    Returns:
        purchaseOrder: The create PurchaseOrder instance.

    Raises:
        CreateRequestError: If the creation fails or response is invalid.
    """
    if hasattr(purchase_order, "model_dump_json"):  # Pydantic v2
        payload = json.loads(
            purchase_order.model_dump_json(
                exclude_unset=True,
                exclude_none=True,
                by_alias=True,   # keep camelCase if you use aliases
            )
        )
    else:  # Pydantic v1 fallback
        payload = json.loads(
            purchase_order.json(
                exclude_unset=True,
                exclude_none=True,
                by_alias=True,
            )
        )
    response = self.client._post(f"/{self.endpoint}", json=payload)
    if response.get('status_code') == 201:
        location = response.get('headers').get('location')
        parsed_url = urlparse(location)
        path_segments = parsed_url.path.rstrip("/").split("/")
        id = path_segments[-1]
        return self.get_purchase_order(id)
    else:
        error_message = response.get('content')
        raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

delete_purchase_order(id)

Delete a purchase order by its ID.

Sends a DELETE request to the purchase order endpoint to remove the specified purchase order from the system.

Parameters:

Name Type Description Default
id int

The ID of the purchase order to delete.

required

Returns:

Name Type Description
bool bool

True if the sales order was successfully deleted, False otherwise.

Source code in src/spyre/purchasing.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
def delete_purchase_order(self, id: int) -> bool:
    """
    Delete a purchase order by its ID.

    Sends a DELETE request to the purchase order endpoint to remove the specified
    purchase order from the system.

    Args:
        id (int): The ID of the purchase order to delete.

    Returns:
        bool: True if the sales order was successfully deleted, False otherwise.
    """
    order = self.get_purchase_order(id)
    if order.model.status in ("I", "R"):
        raise ValueError(f"Cannot delete an issued or received purchase order for {order.number}")
    else:
        return self.client._delete(f"/{self.endpoint}/{str(id)}")

get_purchase_order(id=None, PO_number=None)

Retrieve a purchase order by its ID or PO number.

Parameters:

Name Type Description Default
id int

The ID of the purchase order to retrieve.

None
PO_number str

The purchase order number of the purchase order to retrieve.

None

Returns:

Name Type Description
purchaseOrder purchaseOrder

A purchaseOrder wrapper instance containing the retrieved data.

Raises:

Type Description
ValueError

If neither id nor PO_number is provided, or if no matching order is found.

Source code in src/spyre/purchasing.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def get_purchase_order(self, id: int = None, PO_number: str = None) -> 'purchaseOrder':
    """
    Retrieve a purchase order by its ID or PO number.

    Args:
        id (int, optional): The ID of the purchase order to retrieve.
        PO_number (str, optional): The purchase order number of the purchase order to retrieve.

    Returns:
        purchaseOrder: A `purchaseOrder` wrapper instance containing the retrieved data.

    Raises:
        ValueError: If neither id nor PO_number is provided, or if no matching order is found.
    """
    if id is not None:
        response = self.client._get(f"/{self.endpoint}/{str(id)}")
        return purchaseOrder.from_json(response, self.client)
    elif PO_number is not None:
        orders = self.query_purchase_order(query=PO_number)
        for order in orders:
            if getattr(order, "number", None) == PO_number:
                return self.get_purchase_order(order.id)
        raise ValueError(f"No purchase order found for purchase order {PO_number}")
    else:
        raise ValueError("Either 'id' or 'PO_number' must be provided.")

issue_purchase_order(id)

Issue a purchase order by its ID.

Sends a request to Spire to change the purchase order's status to “Issued”. Issuing an order typically means it has been confirmed and is ready to be sent to the vendor.

Parameters:

Name Type Description Default
id int

The ID of the purchaseOrder to issue.

required

Returns:

Name Type Description
purchaseOrder purchaseOrder

The updated purchase order object with the status set to "I" (Issued).

Raises:

Type Description
CreateRequestError

If the request fails or the API returns an error status.

Source code in src/spyre/purchasing.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
def issue_purchase_order(self, id:int) -> 'purchaseOrder':
    """
    Issue a purchase order by its ID.

    Sends a request to Spire to change the purchase order's status to
    “Issued”. Issuing an order typically means it has been confirmed
    and is ready to be sent to the vendor.

    Args:
        id (int): The ID of the purchaseOrder to issue.

    Returns:
        purchaseOrder: The updated purchase order object with the status set to "I" (Issued).

    Raises:
        CreateRequestError: If the request fails or the API returns an error status.
    """
    response = self.client._post(f"/{self.endpoint}/{str(id)}/issue")
    if response.get("status_code") != 200:
        raise CreateRequestError(f"Failed to issue purchase order: {id}: {response.text}")
    return purchaseOrder.from_json(response, self.client)

query_purchase_order(*, query=None, sort=None, filter=None, all=False, limit=1000, start=0, **extra_params)

Query purchase orders with optional full-text search, filtering, multi-field sorting, and pagination.

Parameters:

Name Type Description Default
query str

Full-text search string.

None
sort dict

Dictionary of sorting rules (e.g., {"date": "desc", "number": "asc"}).

None
filter dict

Dictionary of filters to apply (will be JSON-encoded and URL-safe).

None
all bool

If True, retrieves all pages of results.

False
limit int

Number of results per page (max 1000).

1000
start int

Starting offset for pagination.

0
**extra_params Any

Any additional parameters to include in the query.

{}

Returns:

Type Description
List[purchaseOrder]

List[purchaseOrder]: List of wrapped purchase order resources.

Source code in src/spyre/purchasing.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def query_purchase_order(
        self,
        *,
        query: Optional[str] = None,
        sort: Optional[Dict[str,str]] = None,
        filter: Optional[Dict[str, Any]] = None,
        all: bool = False,
        limit: int = 1000,
        start: int = 0,
        **extra_params
) -> List["purchaseOrder"]:
    """
    Query purchase orders with optional full-text search, filtering, multi-field sorting, and pagination.

    Args:
        query (str, optional): Full-text search string.
        sort (dict, optional): Dictionary of sorting rules (e.g., {"date": "desc", "number": "asc"}).
        filter (dict, optional): Dictionary of filters to apply (will be JSON-encoded and URL-safe).
        all (bool, optional): If True, retrieves all pages of results.
        limit (int, optional): Number of results per page (max 1000).
        start (int, optional): Starting offset for pagination.
        **extra_params (Any): Any additional parameters to include in the query.

    Returns:
        List[purchaseOrder]: List of wrapped purchase order resources.
    """
    return self.client._query(
        endpoint=self.endpoint,
        resource_cls=purchaseOrder,
        query=query,
        sort=sort,
        filter=filter,
        all=all,
        limit=limit,
        start=start,
        **extra_params
    )

receive_purchase_order(id, receiveAll=None)

Receive a purchase order by its ID.

This updates the order in Spire to reflect that items have been received. Typically, this should only be called on purchase orders with status "Issued".

Parameters:

Name Type Description Default
id int

The ID of the purchaseOrder to receive.

required
receiveAll bool

An optional boolean to recieve all quantites on the purchase order.

None

Returns:

Name Type Description
purchaseOrder purchaseOrder

The updated purchase order object with the status set to "R" (Received).

Raises: CreateRequestError: If the request fails or the API returns an error status.

Source code in src/spyre/purchasing.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def receive_purchase_order(self, id: int, receiveAll: bool = None) -> 'purchaseOrder':
    """
    Receive a purchase order by its ID.

    This updates the order in Spire to reflect that items have been received. 
    Typically, this should only be called on purchase orders with status "Issued".

    Args:
        id (int): The ID of the purchaseOrder to receive.
        receiveAll (bool, optional): An optional boolean to recieve all quantites on the purchase order.

    Returns:
        purchaseOrder: The updated purchase order object with the status set to "R" (Received).

    Raises: CreateRequestError: If the request fails or the API returns an error status.
    """
    if receiveAll:
        order = self.get_purchase_order(id)
        for item in order.model.items:
            item.receiveQty = item.orderQty
        order.update()
    response = self.client._post(f"/{self.endpoint}/{str(id)}/receive")
    if response.get("status_code") != 200:
        raise CreateRequestError(f"Failed to receive purchase order: {id}: {response.text}")
    return purchaseOrder.from_json(response, self.client)

update_purchase_order(id, purchase_order)

Update an existing purchase order by ID.

Sends a PUT request to the purchase order endpoint with the provided sales order data to update the existing record. Returns a wrapped purchaseOrder object containing the updated information.

Parameters:

Name Type Description Default
id int

The ID of the purchase order to update.

required
purchase_order PurchaseOrder

A PurchaseOrder instance with the purchase order details.

required

Returns:

Name Type Description
purchaseOrder purchaseOrder

An instance of the purchaseOrder wrapper class initialized with the updated data and client session.

Source code in src/spyre/purchasing.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def update_purchase_order(self, id: int, purchase_order: 'purchaseOrder') -> 'purchaseOrder':
    """
    Update an existing purchase order by ID.

    Sends a PUT request to the purchase order endpoint with the provided sales order data
    to update the existing record. Returns a wrapped `purchaseOrder` object containing
    the updated information.

    Args:
        id (int): The ID of the purchase order to update.
        purchase_order (PurchaseOrder): A PurchaseOrder instance with the purchase order details.

    Returns:
        purchaseOrder: An instance of the purchaseOrder wrapper class initialized with the 
                       updated data and client session.
    """
    if purchase_order.status == "I":
        raise ValueError(f"Cannot update an issued purchase order for {purchase_order.number}")
    else:
        response = self.client._put(f"/{self.endpoint}/{str(id)}", json=purchase_order.model_dump(exclude_none=True, exclude_unset=True))
    return purchaseOrder.from_json(response, self.client)

PurchasingHistoryClient

Source code in src/spyre/purchasing.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
class PurchasingHistoryClient:

    def __init__(self, client: SpireClient):
        self.client = client
        self.endpoint = "purchasing/history"

    def get_purchase_history_order(self, id: int = None, PO_number: str = None) -> 'purchaseOrder':
        """
        Retrieve an archived purchase order by its ID or PO number.

        Args:
            id (int, optional): The ID of the purchase order to retrieve.
            PO_number (str, optional): The purchase order number of the purchase order to retrieve.

        Returns:
            purchaseOrder: A `purchaseOrder` wrapper instance containing the retrieved data.

        Raises:
            ValueError: If neither id nor PO_number is provieded, or if no matching purchase order is found.
        """
        if id is not None:
            response = self.client._get(f"/{self.endpoint}/{str(id)}")
            return purchaseOrder.from_json(response, self.client)
        elif PO_number is not None:
            orders = self.query_purchase_history_order(query=PO_number)
            for order in orders:
                if getattr(order, "number", None) == PO_number:
                    return self.get_purchase_history_order(order.id)
            raise ValueError(f"No purchase order found for purchase order {PO_number}")
        else:
            raise ValueError("Either 'id' or 'PO_number' must be provided.")

    def query_purchase_history_order(
            self,
            *,
            query: Optional[str] = None,
            sort: Optional[Dict[str,str]] = None,
            filter: Optional[Dict[str, Any]] = None,
            all: bool = False,
            limit: int = 1000,
            start: int = 0,
            **extra_params
    ) -> List["purchaseOrder"]:
        """
        Query archived purchase orders with optional full-text search, filtering, multi-field sorting, and pagination.

        Args:
            query (str, optional): Full-text search string.
            sort (dict, optional): Dictionary of sorting rules (e.g., {"date": "desc", "number": "asc"}).
            filter (dict, optional): Dictionary of filters to apply (will be JSON-encoded and URL-safe).
            all (bool, optional): If True, retrieves all pages of results.
            limit (int, optional): Number of results per page (max 1000).
            start (int, optional): Starting offset for pagination.
            **extra_params (Any): Any additional parameters to include in the query.

        Returns:
            List[purchaseOrder]: List of wrapped purchase order resources.
        """
        return self.client._query(
            endpoint=self.endpoint,
            resource_cls=purchaseOrder,
            query=query,
            sort=sort,
            filter=filter,
            all=all,
            limit=limit,
            start=start,
            **extra_params
        )

get_purchase_history_order(id=None, PO_number=None)

Retrieve an archived purchase order by its ID or PO number.

Parameters:

Name Type Description Default
id int

The ID of the purchase order to retrieve.

None
PO_number str

The purchase order number of the purchase order to retrieve.

None

Returns:

Name Type Description
purchaseOrder purchaseOrder

A purchaseOrder wrapper instance containing the retrieved data.

Raises:

Type Description
ValueError

If neither id nor PO_number is provieded, or if no matching purchase order is found.

Source code in src/spyre/purchasing.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
def get_purchase_history_order(self, id: int = None, PO_number: str = None) -> 'purchaseOrder':
    """
    Retrieve an archived purchase order by its ID or PO number.

    Args:
        id (int, optional): The ID of the purchase order to retrieve.
        PO_number (str, optional): The purchase order number of the purchase order to retrieve.

    Returns:
        purchaseOrder: A `purchaseOrder` wrapper instance containing the retrieved data.

    Raises:
        ValueError: If neither id nor PO_number is provieded, or if no matching purchase order is found.
    """
    if id is not None:
        response = self.client._get(f"/{self.endpoint}/{str(id)}")
        return purchaseOrder.from_json(response, self.client)
    elif PO_number is not None:
        orders = self.query_purchase_history_order(query=PO_number)
        for order in orders:
            if getattr(order, "number", None) == PO_number:
                return self.get_purchase_history_order(order.id)
        raise ValueError(f"No purchase order found for purchase order {PO_number}")
    else:
        raise ValueError("Either 'id' or 'PO_number' must be provided.")

query_purchase_history_order(*, query=None, sort=None, filter=None, all=False, limit=1000, start=0, **extra_params)

Query archived purchase orders with optional full-text search, filtering, multi-field sorting, and pagination.

Parameters:

Name Type Description Default
query str

Full-text search string.

None
sort dict

Dictionary of sorting rules (e.g., {"date": "desc", "number": "asc"}).

None
filter dict

Dictionary of filters to apply (will be JSON-encoded and URL-safe).

None
all bool

If True, retrieves all pages of results.

False
limit int

Number of results per page (max 1000).

1000
start int

Starting offset for pagination.

0
**extra_params Any

Any additional parameters to include in the query.

{}

Returns:

Type Description
List[purchaseOrder]

List[purchaseOrder]: List of wrapped purchase order resources.

Source code in src/spyre/purchasing.py
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
def query_purchase_history_order(
        self,
        *,
        query: Optional[str] = None,
        sort: Optional[Dict[str,str]] = None,
        filter: Optional[Dict[str, Any]] = None,
        all: bool = False,
        limit: int = 1000,
        start: int = 0,
        **extra_params
) -> List["purchaseOrder"]:
    """
    Query archived purchase orders with optional full-text search, filtering, multi-field sorting, and pagination.

    Args:
        query (str, optional): Full-text search string.
        sort (dict, optional): Dictionary of sorting rules (e.g., {"date": "desc", "number": "asc"}).
        filter (dict, optional): Dictionary of filters to apply (will be JSON-encoded and URL-safe).
        all (bool, optional): If True, retrieves all pages of results.
        limit (int, optional): Number of results per page (max 1000).
        start (int, optional): Starting offset for pagination.
        **extra_params (Any): Any additional parameters to include in the query.

    Returns:
        List[purchaseOrder]: List of wrapped purchase order resources.
    """
    return self.client._query(
        endpoint=self.endpoint,
        resource_cls=purchaseOrder,
        query=query,
        sort=sort,
        filter=filter,
        all=all,
        limit=limit,
        start=start,
        **extra_params
    )

purchaseOrder

Bases: APIResource[PurchaseOrder]

Source code in src/spyre/purchasing.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
class purchaseOrder(APIResource[PurchaseOrder]):
    endpoint = "purchasing/orders"
    Model = PurchaseOrder

    def issue(self) -> 'purchaseOrder':
        """
        Issue this purchase order.

        Sends a POST request to Spire to change the purchase order's status to
        “I” (Issued). Issuing an order typically means it has been confirmed
        and is ready to be sent to the vendor.

        Returns:
            purchaseOrder: The updated purchase order object with the status set to "I" (Issued).

        Raise:
            CreateRequestError: If the request fails or the API returns an error status.
        """
        response = self._client._post(f"/{self.endpoint}/{str(self.id)}/issue")
        if response.get("status_code") != 200:
            raise CreateRequestError(f"Failed to issue purchase order: {self.id}: {response.text}")
        return purchaseOrder.from_json(response, self._client)

    def delete(self) -> bool:
        """
        Cancels and deletes this purchase order.

        Sends a DELETE request to the API to remove the purchase order with the current ID.

        Returns:
            bool: True if the order was successfully deleted (HTTP 204 or 200), False otherwise.
        """
        return self._client._delete(f"/{self.endpoint}/{str(self.id)}")

    def update(self, order: "purchaseOrder" = None) -> 'purchaseOrder':
        """
        Update this sales order.

        If no purchase order object is provided, updates the current instance on the server.
        If a purchase order object is provided, updates the purchase order using the given data.

        Args:
            order (purchaseOrder, optional): An optional purchaseOrder instance to use for the update.

        Returns: 
            purchaseOrder: The updated purchaseOrder object reflecting the new status.
        """
        data = order.model_dump(exclude_unset=True, exclude_none=True) if order else self.model_dump(exclude_unset=True, exclude_none=True)
        response = self._client._put(f"/{self.endpoint}/{str(self.id)}", json=data)
        return purchaseOrder.from_json(response, self._client)

    def receive(self, receiveAll: bool = None) -> 'purchaseOrder':
        """
        Receive this purchase order.

        Sends a POST request to Spire to change the purchase order's status to
        "R" (Received). Receiving an order typically means the received quantites have been
        entered and the order is ready to be invoiced.

        Args:
            receiveAll (bool, optional): An optional boolean to recieve all quantites on the purchase order.

        Returns:
            purchaseOrder: The updated purchase order object with the status set to "R" (Received).

        Raise: 
            CreateRequestError: If the request fails or the API returns an error status.
        """
        if receiveAll:
            for item in self.model.items:
                item.receiveQty = item.orderQty
            self.update()
        response = self._client._post(f"/{self.endpoint}/{str(self.id)}/receive")
        if response.get('status_code') != 200:
            raise CreateRequestError(f"Failed to receive purchase order: {self.id}. (Check Order Quantites): {response.text}")
        return purchaseOrder.from_json(response, self._client)

delete()

Cancels and deletes this purchase order.

Sends a DELETE request to the API to remove the purchase order with the current ID.

Returns:

Name Type Description
bool bool

True if the order was successfully deleted (HTTP 204 or 200), False otherwise.

Source code in src/spyre/purchasing.py
305
306
307
308
309
310
311
312
313
314
def delete(self) -> bool:
    """
    Cancels and deletes this purchase order.

    Sends a DELETE request to the API to remove the purchase order with the current ID.

    Returns:
        bool: True if the order was successfully deleted (HTTP 204 or 200), False otherwise.
    """
    return self._client._delete(f"/{self.endpoint}/{str(self.id)}")

issue()

Issue this purchase order.

Sends a POST request to Spire to change the purchase order's status to “I” (Issued). Issuing an order typically means it has been confirmed and is ready to be sent to the vendor.

Returns:

Name Type Description
purchaseOrder purchaseOrder

The updated purchase order object with the status set to "I" (Issued).

Raise

CreateRequestError: If the request fails or the API returns an error status.

Source code in src/spyre/purchasing.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
def issue(self) -> 'purchaseOrder':
    """
    Issue this purchase order.

    Sends a POST request to Spire to change the purchase order's status to
    “I” (Issued). Issuing an order typically means it has been confirmed
    and is ready to be sent to the vendor.

    Returns:
        purchaseOrder: The updated purchase order object with the status set to "I" (Issued).

    Raise:
        CreateRequestError: If the request fails or the API returns an error status.
    """
    response = self._client._post(f"/{self.endpoint}/{str(self.id)}/issue")
    if response.get("status_code") != 200:
        raise CreateRequestError(f"Failed to issue purchase order: {self.id}: {response.text}")
    return purchaseOrder.from_json(response, self._client)

receive(receiveAll=None)

Receive this purchase order.

Sends a POST request to Spire to change the purchase order's status to "R" (Received). Receiving an order typically means the received quantites have been entered and the order is ready to be invoiced.

Parameters:

Name Type Description Default
receiveAll bool

An optional boolean to recieve all quantites on the purchase order.

None

Returns:

Name Type Description
purchaseOrder purchaseOrder

The updated purchase order object with the status set to "R" (Received).

Raise

CreateRequestError: If the request fails or the API returns an error status.

Source code in src/spyre/purchasing.py
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
def receive(self, receiveAll: bool = None) -> 'purchaseOrder':
    """
    Receive this purchase order.

    Sends a POST request to Spire to change the purchase order's status to
    "R" (Received). Receiving an order typically means the received quantites have been
    entered and the order is ready to be invoiced.

    Args:
        receiveAll (bool, optional): An optional boolean to recieve all quantites on the purchase order.

    Returns:
        purchaseOrder: The updated purchase order object with the status set to "R" (Received).

    Raise: 
        CreateRequestError: If the request fails or the API returns an error status.
    """
    if receiveAll:
        for item in self.model.items:
            item.receiveQty = item.orderQty
        self.update()
    response = self._client._post(f"/{self.endpoint}/{str(self.id)}/receive")
    if response.get('status_code') != 200:
        raise CreateRequestError(f"Failed to receive purchase order: {self.id}. (Check Order Quantites): {response.text}")
    return purchaseOrder.from_json(response, self._client)

update(order=None)

Update this sales order.

If no purchase order object is provided, updates the current instance on the server. If a purchase order object is provided, updates the purchase order using the given data.

Parameters:

Name Type Description Default
order purchaseOrder

An optional purchaseOrder instance to use for the update.

None

Returns:

Name Type Description
purchaseOrder purchaseOrder

The updated purchaseOrder object reflecting the new status.

Source code in src/spyre/purchasing.py
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
def update(self, order: "purchaseOrder" = None) -> 'purchaseOrder':
    """
    Update this sales order.

    If no purchase order object is provided, updates the current instance on the server.
    If a purchase order object is provided, updates the purchase order using the given data.

    Args:
        order (purchaseOrder, optional): An optional purchaseOrder instance to use for the update.

    Returns: 
        purchaseOrder: The updated purchaseOrder object reflecting the new status.
    """
    data = order.model_dump(exclude_unset=True, exclude_none=True) if order else self.model_dump(exclude_unset=True, exclude_none=True)
    response = self._client._put(f"/{self.endpoint}/{str(self.id)}", json=data)
    return purchaseOrder.from_json(response, self._client)

create_sales_order_from_invoice(invoice)

Creates a Sales Order from an Invoice by copying relevant fields and adjusting quantities and freight charges for order processing.

Parameters:

Name Type Description Default
invoice Invoice

The invoice object to convert.

required

Returns:

Name Type Description
SalesOrder SalesOrder

A new Sales Order instance populated with data from the invoice, including negative freight and order quantities, and duplicated address and item records.

Notes
  • Freight amount is negated.
  • Order and committed quantities are negated unless zero.
  • Shipping carrier and tracking number fields are reset to None.
  • Duplicate records are created for address, shipping address, and items to avoid mutating the original invoice data.
Source code in src/spyre/utils.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def create_sales_order_from_invoice(invoice: Invoice) -> SalesOrder:
    """
    Creates a Sales Order from an Invoice by copying relevant fields 
    and adjusting quantities and freight charges for order processing.

    Args:
        invoice (Invoice): The invoice object to convert.

    Returns:
        SalesOrder: A new Sales Order instance populated with data from the invoice,
            including negative freight and order quantities, and duplicated
            address and item records.

    Notes:
        - Freight amount is negated.
        - Order and committed quantities are negated unless zero.
        - Shipping carrier and tracking number fields are reset to None.
        - Duplicate records are created for address, shipping address, and items 
          to avoid mutating the original invoice data.
    """


    return SalesOrder(
        orderNo=invoice.orderNo,
        division=invoice.division,
        location=invoice.location,
        profitCenter=invoice.profitCenter,
        customer=deepcopy(invoice.customer),
        currency=deepcopy(invoice.currency),
        status="O",
        type="O",
        hold=False,
        orderDate=invoice.orderDate,
        address=create_duplicate_record(invoice.address, exclude_fields=["contacts.contact_type"]),
        shippingAddress=create_duplicate_record(invoice.shippingAddress),
        customerPO=invoice.customerPO,
        fob=invoice.fob,
        incoterms=invoice.incoterms,
        incotermsPlace=invoice.incotermsPlace,
        referenceNo=None,
        shippingCarrier=None,
        shipDate=invoice.shipDate,
        trackingNo=None,
        termsCode=invoice.termsCode,
        termsText=invoice.termsText,
        freight=f"-{invoice.freight}",
        subtotal=invoice.subtotal,
        total=invoice.total,
        items = [
            create_duplicate_record(item).model_copy(update={
                "orderQty": f"-{item.orderQty}" if item.orderQty != "0" else item.orderQty ,
                "committedQty": f"-{item.committedQty}" if item.committedQty != "0" else item.committedQty
            })
            for item in invoice.items or []
        ],
        udf=deepcopy(invoice.udf),
    )