Skip to content

Xero API incorrectly updating discount fields for invoice when updating the SentToContact field #713

Open
@JoeCMorgan

Description

@JoeCMorgan

A quick summary and/or background
As mentioned in the title, when I'm hitting the Invoices endpoint to update an invoice, through either Postman, the API explorer, or the Python SDK (updateInvoice endpoint), the discount fields are being modified, even though I'm only marking the invoice as being sent.

Steps to reproduce
This is tricky as my invoices are being create via the Parex bridge for Shopify. However, the invoice is created with a discount, as shown below:

{
    "Id": "e91927c5-546a-40c0-8655-5daf048ed0b5",
    "Status": "OK",
    "ProviderName": "Send Invoice Emails",
    "DateTimeUTC": "/Date(1744023335310)/",
    "Invoices": [
        {
            "Type": "ACCREC",
            "InvoiceID": "21fbe4ca-8e63-45fa-a513-431976c648a7",
            "InvoiceNumber": "SHP_#1347",
            "Reference": "pos",
            "Prepayments": [],
            "Overpayments": [],
            "AmountDue": 45.55,
            "AmountPaid": 0.00,
            "SentToContact": false,
            "CurrencyRate": 1.0000000000,
            "TotalDiscount": 2.39,
            "IsDiscounted": true,
            "HasAttachments": false,
            "HasErrors": false,
            "Attachments": [],
            "InvoicePaymentServices": [],
            "Contact": {...},
            "DateString": "2025-04-07T00:00:00",
            "Date": "/Date(1743984000000+0000)/",
            "DueDateString": "2025-05-30T00:00:00",
            "DueDate": "/Date(1748563200000+0000)/",
            "BrandingThemeID": "d45ca3b8-18e1-4cca-9e79-d1f50a790575",
            "Status": "AUTHORISED",
            "LineAmountTypes": "Inclusive",
            "LineItems": [
                {
                    "ItemCode": "SOL-STH-MID-MC070-1L",
                    "Description": "Premium Standard Thinner, 1L",
                    "UnitAmount": 1.92,
                    "TaxType": "OUTPUT2",
                    "TaxAmount": 5.78,
                    "LineAmount": 34.66,
                    "AccountCode": "200",
                    "Item": {
                        "ItemID": "f688ec16-99dc-4efd-99ca-e97c56a7ba3b",
                        "Name": "Premium Standard Thinner, 1L",
                        "Code": "SOL-STH-MID-MC070-1L"
                    },
                    "Tracking": [],
                    "Quantity": 19.0000,
                    "DiscountRate": 4.99,
                    "LineItemID": "c522edc5-e632-4e0a-9ab0-147642cdd7c0",
                    "AccountID": "e15ef38f-0423-4883-82b0-fba43e00ceba",
                    "DiscountAmount": 1.8200,
                    "ValidationErrors": []
                },
                {
                    "ItemCode": "SOL-STH-MID-MC070-1L",
                    "Description": "Premium Standard Thinner, 1L",
                    "UnitAmount": 1.91,
                    "TaxType": "OUTPUT2",
                    "TaxAmount": 1.81,
                    "LineAmount": 10.89,
                    "AccountCode": "200",
                    "Item": {
                        "ItemID": "f688ec16-99dc-4efd-99ca-e97c56a7ba3b",
                        "Name": "Premium Standard Thinner, 1L",
                        "Code": "SOL-STH-MID-MC070-1L"
                    },
                    "Tracking": [],
                    "Quantity": 6.0000,
                    "DiscountRate": 4.97,
                    "LineItemID": "7520ff63-791b-41f1-bea7-94b34c062915",
                    "AccountID": "e15ef38f-0423-4883-82b0-fba43e00ceba",
                    "DiscountAmount": 0.5700,
                    "ValidationErrors": []
                }
            ],
            "SubTotal": 37.96,
            "TotalTax": 7.59,
            "Total": 45.55,
            "UpdatedDateUTC": "/Date(1744023317490+0000)/",
            "CurrencyCode": "GBP"
        }
    ]
}

The discount fields above match what is entered into Shopify, therefore the sync is correct.

However, when I attempt the mark the invoice as paid in postman, the discount values change. I'm hitting the Invoices/GUID endpoint, with the following payload:

{
    "InvoiceID": "21fbe4ca-8e63-45fa-a513-431976c648a7",
    "SentToContact": "true"
}

I get the following response:

{
    "Id": "d24a32b4-d84c-444f-9b6f-7d6078f68473",
    "Status": "OK",
    "ProviderName": "Send Invoice Emails",
    "DateTimeUTC": "/Date(1744023257300)/",
    "Invoices": [
        {
            "Type": "ACCREC",
            "InvoiceID": "21fbe4ca-8e63-45fa-a513-431976c648a7",
            "InvoiceNumber": "SHP_#1347",
            "Reference": "pos",
            "Prepayments": [],
            "Overpayments": [],
            "AmountDue": 45.55,
            "AmountPaid": 0.00,
            "SentToContact": true,
            "CurrencyRate": 1.0000000000,
            "TotalDiscount": 9.96,
            "IsDiscounted": true,
            "HasErrors": false,
            "InvoicePaymentServices": [],
            "Contact": {...},
            "DateString": "2025-04-07T00:00:00",
            "Date": "/Date(1743984000000+0000)/",
            "DueDateString": "2025-05-30T00:00:00",
            "DueDate": "/Date(1748563200000+0000)/",
            "BrandingThemeID": "d45ca3b8-18e1-4cca-9e79-d1f50a790575",
            "Status": "AUTHORISED",
            "LineAmountTypes": "Inclusive",
            "LineItems": [
                {
                    "ItemCode": "SOL-STH-MID-MC070-1L",
                    "Description": "Premium Standard Thinner, 1L",
                    "UnitAmount": 1.92,
                    "TaxType": "OUTPUT2",
                    "TaxAmount": 5.78,
                    "LineAmount": 34.66,
                    "AccountCode": "200",
                    "Item": {
                        "ItemID": "f688ec16-99dc-4efd-99ca-e97c56a7ba3b",
                        "Name": "Premium Standard Thinner, 1L",
                        "Code": "SOL-STH-MID-MC070-1L"
                    },
                    "Tracking": [],
                    "Quantity": 19.0000,
                    "DiscountRate": 13.68,
                    "LineItemID": "c522edc5-e632-4e0a-9ab0-147642cdd7c0",
                    "AccountID": "e15ef38f-0423-4883-82b0-fba43e00ceba",
                    "DiscountEnteredAsPercent": false,
                    "ValidationErrors": []
                },
                {
                    "ItemCode": "SOL-STH-MID-MC070-1L",
                    "Description": "Premium Standard Thinner, 1L",
                    "UnitAmount": 1.91,
                    "TaxType": "OUTPUT2",
                    "TaxAmount": 1.81,
                    "LineAmount": 10.89,
                    "AccountCode": "200",
                    "Item": {
                        "ItemID": "f688ec16-99dc-4efd-99ca-e97c56a7ba3b",
                        "Name": "Premium Standard Thinner, 1L",
                        "Code": "SOL-STH-MID-MC070-1L"
                    },
                    "Tracking": [],
                    "Quantity": 6.0000,
                    "DiscountRate": 43.37,
                    "LineItemID": "7520ff63-791b-41f1-bea7-94b34c062915",
                    "AccountID": "e15ef38f-0423-4883-82b0-fba43e00ceba",
                    "DiscountEnteredAsPercent": false,
                    "ValidationErrors": []
                }
            ],
            "SubTotal": 37.96,
            "TotalTax": 7.59,
            "Total": 45.55,
            "UpdatedDateUTC": "/Date(1744023257207+0000)/",
            "CurrencyCode": "GBP"
        }
    ]
}

Notice how the DiscountRate has changed, and the DiscountAmount has disappeared? Why is this happening when all I'm updating is the SentToContact field?

What you expected would happen
I would expect only the SentToContact field for that specific invoice to be modified, and no other fields.

What actually happens
Shown above.

Notes
As mentioned, I first noticed that something similar was happening in the Python SDK - an error message was returned saying Discount must be between 0.00 and 100.00 or empty.. So I tested it in the API explorer, and postman, and I get the above results. Therefore I believe this is an issue with the API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions