pythonxero-api

Unable to construct a DueDate String for the Invoice API call


I am trying to make an API call using python and httpx to the Xero Invoice Api. I use the following api method:

async def create_invoice(
        invoice_detail: InvoiceCreateRequest,
        token_manager=Depends(oauth_manager)
) -> InvoiceCreateResponse:
    base_url = 'https://api.xero.com/api.xro/2.0/Invoices'
    headers = {
        'Xero-Tenant-Id': get_settings().TENANT_ID,
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {await token_manager.access_token}'
    }
    data = invoice_request_to_dict(invoice_detail)

    async with httpx.AsyncClient() as client:
        response = await client.request(
            method='POST',
            url=base_url,
            headers=headers,
            data=data
        )

The data object looks like this:

{
        "Type": "ACCREC",
        "Contact": {
            "ContactID": "3ed357da-0988-4ea1-b1b7-96829e0dde69"
        },
        "DueDate": r"\/Date(1518685950940+0000)\/",
        "LineItems": [
            {
                "Description": "Services as agreed",
                "Quantity": "4",
                "UnitAmount": "100.00",
                "AccountCode": "200"
            }
        ],
        "Status": "AUTHORISED"
    }

This leaves me with the following error:

b'{\r\n  "ErrorNumber": 14,\r\n  "Type": "PostDataInvalidException",\r\n  "Message": "Invalid Json data"\r\n}'

As far as I can figure out, it has to do with the way the DueDate is passed to the Xero API. They use the MS .NET date format but python adds escape characters before the \ to give this:

'\\/Date(1518685950940+0000)\\/'

I have set up a native API call via Postman to the same Xero endpoint with the same payload and it works fine. Changing the DueDate object to look like the one above generates the same type of error:

JSON for post data was invalid,Could not convert string to DateTime: \/Date(1518685950940+0000)\/. Path 'DueDate', line 6, position 45.</Message>

I have not managed to find a way to reformat the string to rid of the additional escape characters. Is there a specfic way this is to be done? I have not been able to find anything regarding this in the dev docs.


Solution

  • Turns out it is an issue of encoding. For httpx, if data is a simple Dict object like:

    {
            "Type": "ACCREC",
            "Contact": {
                "ContactID": "3ed357da-0988-4ea1-b1b7-96829e0dde69"
            },
            "DueDate": r"\/Date(1518685950940+0000)\/",
            "LineItems": [
                {
                    "Description": "Services as agreed",
                    "Quantity": "4",
                    "UnitAmount": "100.00",
                    "AccountCode": "200"
                }
            ],
            "Status": "AUTHORISED"
        }
    

    it should first be serialized with json.dumps(data).

    Updated code:

        data = json.dumps(invoice_request_to_dict(invoice_detail))
    
        async with httpx.AsyncClient() as client:
            response = await client.request(
                method='POST',
                url=base_url,
                headers=headers,
                data=data
            )