Skip to content

Sales

InvoiceClient

Source code in src/spyre/sales.py
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
211
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
class InvoiceClient():

    def __init__(self, client : SpireClient):
        self.client = client
        self.endpoint = "sales/invoices"

    def get_invoice(self, id: int) -> 'invoice':
        """
        Retrieve a sales invoice by its ID.

        Sends a GET request to the invoices endpoint to fetch the invoice data.

        Args:
            id (int): The ID of the invoice to retrieve.

        Returns:
            invoice: An invoice instance created from the response data.
        """
        response = self.client._get(f"/{self.endpoint}/{id}")
        return invoice.from_json(response, self.client)

    def update_invoice(self, id: int, invoice : Invoice) -> 'invoice':
        """
        Update an existing invoice by ID.

        Sends a PUT request with updated invoice data to the invoices endpoint.

        Args:
            id (int): The ID of the invoice to update.
            invoice (Invoice): The Invoice model instance containing updated data.

        Returns:
            invoice: The updated invoice instance created from the response data.
        """
        response = self.client._put(f"/{self.endpoint}/{id}", json=invoice.model_dump(exclude_none=True, exclude_unset=True))
        return invoice.from_json(response, self.client)

    def query_invoices(
        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["salesOrder"]:
        """
        Query invoices 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., {"orderDate": "desc", "orderNo": "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[invoice]: List of wrapped invoice resources.
        """
        return self.client._query(
            endpoint=self.endpoint,
            resource_cls=invoice,
            query=query,
            sort=sort,
            filter=filter,
            all=all,
            limit=limit,
            start=start,
            **extra_params
        )

get_invoice(id)

Retrieve a sales invoice by its ID.

Sends a GET request to the invoices endpoint to fetch the invoice data.

Parameters:

Name Type Description Default
id int

The ID of the invoice to retrieve.

required

Returns:

Name Type Description
invoice invoice

An invoice instance created from the response data.

Source code in src/spyre/sales.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def get_invoice(self, id: int) -> 'invoice':
    """
    Retrieve a sales invoice by its ID.

    Sends a GET request to the invoices endpoint to fetch the invoice data.

    Args:
        id (int): The ID of the invoice to retrieve.

    Returns:
        invoice: An invoice instance created from the response data.
    """
    response = self.client._get(f"/{self.endpoint}/{id}")
    return invoice.from_json(response, self.client)

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

Query invoices 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., {"orderDate": "desc", "orderNo": "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[salesOrder]

List[invoice]: List of wrapped invoice resources.

Source code in src/spyre/sales.py
206
207
208
209
210
211
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
def query_invoices(
    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["salesOrder"]:
    """
    Query invoices 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., {"orderDate": "desc", "orderNo": "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[invoice]: List of wrapped invoice resources.
    """
    return self.client._query(
        endpoint=self.endpoint,
        resource_cls=invoice,
        query=query,
        sort=sort,
        filter=filter,
        all=all,
        limit=limit,
        start=start,
        **extra_params
    )

update_invoice(id, invoice)

Update an existing invoice by ID.

Sends a PUT request with updated invoice data to the invoices endpoint.

Parameters:

Name Type Description Default
id int

The ID of the invoice to update.

required
invoice Invoice

The Invoice model instance containing updated data.

required

Returns:

Name Type Description
invoice invoice

The updated invoice instance created from the response data.

Source code in src/spyre/sales.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def update_invoice(self, id: int, invoice : Invoice) -> 'invoice':
    """
    Update an existing invoice by ID.

    Sends a PUT request with updated invoice data to the invoices endpoint.

    Args:
        id (int): The ID of the invoice to update.
        invoice (Invoice): The Invoice model instance containing updated data.

    Returns:
        invoice: The updated invoice instance created from the response data.
    """
    response = self.client._put(f"/{self.endpoint}/{id}", json=invoice.model_dump(exclude_none=True, exclude_unset=True))
    return invoice.from_json(response, self.client)

OrdersClient

Source code in src/spyre/sales.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
class OrdersClient():

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

    def get_sales_order(self, id: int = None, order_number: str = None) -> "salesOrder":
        """
        Retrieve a sales order by its ID or order number.

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

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

        Raises:
            ValueError: If neither id nor order_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 salesOrder.from_json(response, self.client)
        elif order_number is not None:
            orders = self.query_sales_orders(query=order_number)
            for order in orders:
                if getattr(order, "orderNo", None) == order_number:
                    return order
            raise ValueError(f"No order found for order number {order_number}")
        else:
            raise ValueError("Either 'id' or 'order_number' must be provided.")

    def create_sales_order(self, sales_order : 'SalesOrder') -> 'salesOrder':
        """
        Create a new sales order.

        Sends a POST request to the sales order endpoint .

        Args:
            sales_order (dict): A SalesOrder instance containing the sales order details.

        Returns:
            salesOrder: The created SalesOrder instance.

        Raises:
            CreateRequestError: If the creation fails or response is invalid.
        """

        response =  self.client._post(f"/{self.endpoint}", json=sales_order.model_dump(exclude_unset=True, exclude_none=True))
        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_sales_order(id)
        else:
            error_message = response.get('content')
            raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

    def update_sales_order(self, id: int, sales_order : 'SalesOrder') -> 'salesOrder':
        """
        Update an existing sales order by ID.

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

        Args:
            id (int): The ID of the sales order to update.
            sales_order (SaleOrder): A SalesOrder instance with the sales order details.

        Returns:
            salesOrder: An instance of the salesOrder wrapper class initialized with 
                        the updated data and client session.
        """
        response = self.client._put(f"/{self.endpoint}/{str(id)}", json=sales_order.model_dump(exclude_none=True, exclude_unset=True))
        return salesOrder.from_json(response, self.client)

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

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

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

        Returns:
            bool: True if the sales order was successfully deleted, False otherwise.
        """
        return self.client._delete(f"/{self.endpoint}/{str(id)}")

    def query_sales_orders(
        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["salesOrder"]:
        """
        Query sales 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., {"orderDate": "desc", "orderNo": "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[salesOrder]: List of wrapped sales order resources.
        """
        return self.client._query(
            endpoint=self.endpoint,
            resource_cls=salesOrder,
            query=query,
            sort=sort,
            filter=filter,
            all=all,
            limit=limit,
            start=start,
            **extra_params
        )

    def create_sales_order_note(self, id: int , note_body : str, note_subject : str = "Note") -> note:
        """
        Create a new note on this sales order.

        Args:
            id (int): The id of the salesOrder to create a note on.
            note_body (str): The body of the note. 
            note_subject (str): The subject of the note.
        Returns:
            note: The created note

        Raises:
            CreateRequestError: If the creation fails or response is invalid.
        """

        note_model = Note(body=note_body, subject=note_subject)
        response =  self.client._post(f"/{self.endpoint}/{id}/notes/", json=note_model.model_dump(exclude_unset=True, exclude_none=True))
        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 CRMClient(client=self.client).get_note(id)
        else:
            error_message = response.get('content')
            raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

create_sales_order(sales_order)

Create a new sales order.

Sends a POST request to the sales order endpoint .

Parameters:

Name Type Description Default
sales_order dict

A SalesOrder instance containing the sales order details.

required

Returns:

Name Type Description
salesOrder salesOrder

The created SalesOrder instance.

Raises:

Type Description
CreateRequestError

If the creation fails or response is invalid.

Source code in src/spyre/sales.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
def create_sales_order(self, sales_order : 'SalesOrder') -> 'salesOrder':
    """
    Create a new sales order.

    Sends a POST request to the sales order endpoint .

    Args:
        sales_order (dict): A SalesOrder instance containing the sales order details.

    Returns:
        salesOrder: The created SalesOrder instance.

    Raises:
        CreateRequestError: If the creation fails or response is invalid.
    """

    response =  self.client._post(f"/{self.endpoint}", json=sales_order.model_dump(exclude_unset=True, exclude_none=True))
    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_sales_order(id)
    else:
        error_message = response.get('content')
        raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

create_sales_order_note(id, note_body, note_subject='Note')

Create a new note on this sales order.

Parameters:

Name Type Description Default
id int

The id of the salesOrder to create a note on.

required
note_body str

The body of the note.

required
note_subject str

The subject of the note.

'Note'

Returns: note: The created note

Raises:

Type Description
CreateRequestError

If the creation fails or response is invalid.

Source code in src/spyre/sales.py
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
def create_sales_order_note(self, id: int , note_body : str, note_subject : str = "Note") -> note:
    """
    Create a new note on this sales order.

    Args:
        id (int): The id of the salesOrder to create a note on.
        note_body (str): The body of the note. 
        note_subject (str): The subject of the note.
    Returns:
        note: The created note

    Raises:
        CreateRequestError: If the creation fails or response is invalid.
    """

    note_model = Note(body=note_body, subject=note_subject)
    response =  self.client._post(f"/{self.endpoint}/{id}/notes/", json=note_model.model_dump(exclude_unset=True, exclude_none=True))
    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 CRMClient(client=self.client).get_note(id)
    else:
        error_message = response.get('content')
        raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

delete_sales_order(id)

Delete a sales order by its ID.

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

Parameters:

Name Type Description Default
id int

The ID of the sales 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/sales.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def delete_sales_order(self, id: int) -> bool:
    """
    Delete a sales order by its ID.

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

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

    Returns:
        bool: True if the sales order was successfully deleted, False otherwise.
    """
    return self.client._delete(f"/{self.endpoint}/{str(id)}")

get_sales_order(id=None, order_number=None)

Retrieve a sales order by its ID or order number.

Parameters:

Name Type Description Default
id int

The ID of the sales order to retrieve.

None
order_number str

The order number of the sales order to retrieve.

None

Returns:

Name Type Description
salesOrder salesOrder

A salesOrder wrapper instance containing the retrieved data.

Raises:

Type Description
ValueError

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

Source code in src/spyre/sales.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_sales_order(self, id: int = None, order_number: str = None) -> "salesOrder":
    """
    Retrieve a sales order by its ID or order number.

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

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

    Raises:
        ValueError: If neither id nor order_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 salesOrder.from_json(response, self.client)
    elif order_number is not None:
        orders = self.query_sales_orders(query=order_number)
        for order in orders:
            if getattr(order, "orderNo", None) == order_number:
                return order
        raise ValueError(f"No order found for order number {order_number}")
    else:
        raise ValueError("Either 'id' or 'order_number' must be provided.")

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

Query sales 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., {"orderDate": "desc", "orderNo": "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[salesOrder]

List[salesOrder]: List of wrapped sales order resources.

Source code in src/spyre/sales.py
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
def query_sales_orders(
    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["salesOrder"]:
    """
    Query sales 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., {"orderDate": "desc", "orderNo": "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[salesOrder]: List of wrapped sales order resources.
    """
    return self.client._query(
        endpoint=self.endpoint,
        resource_cls=salesOrder,
        query=query,
        sort=sort,
        filter=filter,
        all=all,
        limit=limit,
        start=start,
        **extra_params
    )

update_sales_order(id, sales_order)

Update an existing sales order by ID.

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

Parameters:

Name Type Description Default
id int

The ID of the sales order to update.

required
sales_order SaleOrder

A SalesOrder instance with the sales order details.

required

Returns:

Name Type Description
salesOrder salesOrder

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

Source code in src/spyre/sales.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def update_sales_order(self, id: int, sales_order : 'SalesOrder') -> 'salesOrder':
    """
    Update an existing sales order by ID.

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

    Args:
        id (int): The ID of the sales order to update.
        sales_order (SaleOrder): A SalesOrder instance with the sales order details.

    Returns:
        salesOrder: An instance of the salesOrder wrapper class initialized with 
                    the updated data and client session.
    """
    response = self.client._put(f"/{self.endpoint}/{str(id)}", json=sales_order.model_dump(exclude_none=True, exclude_unset=True))
    return salesOrder.from_json(response, self.client)

invoice

Bases: APIResource[Invoice]

Source code in src/spyre/sales.py
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
class invoice(APIResource[Invoice]):
    endpoint = "sales/invoices/"
    Model = Invoice

    def reverse(self) -> salesOrder:
        """
        Convert this invoice into a sales order.

        This method uses the internal model of the invoice to create a new sales order
        using the `create_sales_order_from_invoice` utility function. It then submits the
        order through the OrdersClient.

        Returns:
            salesOrder: The created sales order instance returned by the API.
        """
        order_converted = create_sales_order_from_invoice(self._model)
        return OrdersClient(self._client).create_sales_order(order_converted)

    def update(self , invoice_: "Invoice" = None) -> 'invoice':
        """
        Update this invoice.

        Sends a PUT request with updated invoice data to the invoices endpoint.
        If no order object is provided, updates the current instance on the server.

        Args:
            invoice_ (Invoice): The Invoice model instance containing updated data.

        Returns:
            invoice: The updated invoice instance created from the response data.
        """
        data = invoice_.model_dump(exclude_unset=True, exclude_none=True) if invoice_ else self.model_dump(exclude_unset=True, exclude_none=True)
        response = self._client._put(f"/{self.endpoint}/{str(self.id)}", json=data)
        return invoice.from_json(response, self._client)   

reverse()

Convert this invoice into a sales order.

This method uses the internal model of the invoice to create a new sales order using the create_sales_order_from_invoice utility function. It then submits the order through the OrdersClient.

Returns:

Name Type Description
salesOrder salesOrder

The created sales order instance returned by the API.

Source code in src/spyre/sales.py
343
344
345
346
347
348
349
350
351
352
353
354
355
def reverse(self) -> salesOrder:
    """
    Convert this invoice into a sales order.

    This method uses the internal model of the invoice to create a new sales order
    using the `create_sales_order_from_invoice` utility function. It then submits the
    order through the OrdersClient.

    Returns:
        salesOrder: The created sales order instance returned by the API.
    """
    order_converted = create_sales_order_from_invoice(self._model)
    return OrdersClient(self._client).create_sales_order(order_converted)

update(invoice_=None)

Update this invoice.

Sends a PUT request with updated invoice data to the invoices endpoint. If no order object is provided, updates the current instance on the server.

Parameters:

Name Type Description Default
invoice_ Invoice

The Invoice model instance containing updated data.

None

Returns:

Name Type Description
invoice invoice

The updated invoice instance created from the response data.

Source code in src/spyre/sales.py
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
def update(self , invoice_: "Invoice" = None) -> 'invoice':
    """
    Update this invoice.

    Sends a PUT request with updated invoice data to the invoices endpoint.
    If no order object is provided, updates the current instance on the server.

    Args:
        invoice_ (Invoice): The Invoice model instance containing updated data.

    Returns:
        invoice: The updated invoice instance created from the response data.
    """
    data = invoice_.model_dump(exclude_unset=True, exclude_none=True) if invoice_ else self.model_dump(exclude_unset=True, exclude_none=True)
    response = self._client._put(f"/{self.endpoint}/{str(self.id)}", json=data)
    return invoice.from_json(response, self._client)   

salesOrder

Bases: APIResource[SalesOrder]

Source code in src/spyre/sales.py
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
281
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
class salesOrder(APIResource[SalesOrder]):
    endpoint = "sales/orders/"
    Model = SalesOrder 

    def invoice(self) -> "invoice":
        """
        Invoice the current sales order.

        Sends a POST request to create an invoice for this sales order.
        Note that quotes (salesOrder with type "Q") cannot be invoiced.

        Returns:
            invoice (invoice): The created invoice object if successful.

        Raises:
            CreateRequestError: If invoice creation failed.

        """
        response = self._client._post(f"/{self.endpoint}/{str(self.id)}/invoice")
        if response.get('status_code') == 200:
            data = response.get('content').get('invoice')
            return salesOrder.from_json(json_data=data, client= self._client)
        else:
            error_message = response.get('content')
            raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

    def process(self) -> 'salesOrder':
        """
        Set the status of the salesOrder to 'Processed' (status = 'P').

        Sends a PUT request to update the status field of this Sales Oder.

        Returns:
            salesOrder: The updated salesOrder object reflecting the new status.
        """
        response = self._client._put(
            f"/{self.endpoint}/{str(self.id)}",
            json={"status": "P"}    
        )
        return salesOrder.from_json(response, self._client)

    def delete(self) -> bool:
        """
        Cancels or deletes the sales order.

        Sends a DELETE request to the API to remove the sales 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: "salesOrder" = None) -> 'salesOrder':
        """
        Update the sales order.

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

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

        Returns:
            salesOrder: The updated salesOrder 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 salesOrder.from_json(response, self._client)    

    def add_note(self, note_body : "str" , note_subject : "str" = "Note") -> note:
        """
        Add a note to this sales order

        Args:
            note_body (str): the body of the note.
            note_subject (str, "Note"): the subject of the note. the defualt value is just "Note"

        Returns:
            note: The note created.
        """

        note_model = Note(body=note_body, subject=note_subject)
        response =  self._client._post(f"/{self.endpoint}/{str(self.id)}/notes/", json=note_model.model_dump(exclude_unset=True, exclude_none=True))
        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 CRMClient(client=self._client).get_note(id)
        else:
            error_message = response.get('content')
            raise CreateRequestError(f"/{self.endpoint}/{str(self.id)}/notes/", status_code=response.get('status_code'), error_message=error_message)

add_note(note_body, note_subject='Note')

Add a note to this sales order

Parameters:

Name Type Description Default
note_body str

the body of the note.

required
note_subject (str, Note)

the subject of the note. the defualt value is just "Note"

'Note'

Returns:

Name Type Description
note note

The note created.

Source code in src/spyre/sales.py
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
def add_note(self, note_body : "str" , note_subject : "str" = "Note") -> note:
    """
    Add a note to this sales order

    Args:
        note_body (str): the body of the note.
        note_subject (str, "Note"): the subject of the note. the defualt value is just "Note"

    Returns:
        note: The note created.
    """

    note_model = Note(body=note_body, subject=note_subject)
    response =  self._client._post(f"/{self.endpoint}/{str(self.id)}/notes/", json=note_model.model_dump(exclude_unset=True, exclude_none=True))
    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 CRMClient(client=self._client).get_note(id)
    else:
        error_message = response.get('content')
        raise CreateRequestError(f"/{self.endpoint}/{str(self.id)}/notes/", status_code=response.get('status_code'), error_message=error_message)

delete()

Cancels or deletes the sales order.

Sends a DELETE request to the API to remove the sales 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/sales.py
286
287
288
289
290
291
292
293
294
295
296
def delete(self) -> bool:
    """
    Cancels or deletes the sales order.

    Sends a DELETE request to the API to remove the sales 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)}")

invoice()

Invoice the current sales order.

Sends a POST request to create an invoice for this sales order. Note that quotes (salesOrder with type "Q") cannot be invoiced.

Returns:

Name Type Description
invoice invoice

The created invoice object if successful.

Raises:

Type Description
CreateRequestError

If invoice creation failed.

Source code in src/spyre/sales.py
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
def invoice(self) -> "invoice":
    """
    Invoice the current sales order.

    Sends a POST request to create an invoice for this sales order.
    Note that quotes (salesOrder with type "Q") cannot be invoiced.

    Returns:
        invoice (invoice): The created invoice object if successful.

    Raises:
        CreateRequestError: If invoice creation failed.

    """
    response = self._client._post(f"/{self.endpoint}/{str(self.id)}/invoice")
    if response.get('status_code') == 200:
        data = response.get('content').get('invoice')
        return salesOrder.from_json(json_data=data, client= self._client)
    else:
        error_message = response.get('content')
        raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)

process()

Set the status of the salesOrder to 'Processed' (status = 'P').

Sends a PUT request to update the status field of this Sales Oder.

Returns:

Name Type Description
salesOrder salesOrder

The updated salesOrder object reflecting the new status.

Source code in src/spyre/sales.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
def process(self) -> 'salesOrder':
    """
    Set the status of the salesOrder to 'Processed' (status = 'P').

    Sends a PUT request to update the status field of this Sales Oder.

    Returns:
        salesOrder: The updated salesOrder object reflecting the new status.
    """
    response = self._client._put(
        f"/{self.endpoint}/{str(self.id)}",
        json={"status": "P"}    
    )
    return salesOrder.from_json(response, self._client)

update(order=None)

Update the sales order.

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

Parameters:

Name Type Description Default
order salesOrder

An optional salesOrder instance to use for the update.

None

Returns:

Name Type Description
salesOrder salesOrder

The updated salesOrder object reflecting the new status.

Source code in src/spyre/sales.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
def update(self, order: "salesOrder" = None) -> 'salesOrder':
    """
    Update the sales order.

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

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

    Returns:
        salesOrder: The updated salesOrder 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 salesOrder.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),
    )