# Quickstart

This pages demonstrate how to make API calls to QuickBooks Online through QuickBooks V3 PHP SDK. It is assuming developers have obtained OAuth 1.0 or OAuth 2.0 tokens, and have configured their dataService correctly. If not, please refer to the Authorization part and Configuration part

## Create new resources (POST)

QuickBooks Online API has a list API endpoints that support creating new entities. For example, you can create a new Invoice, a new SalesReceipt, or a new Item through QuickBooks Online API. For a list of API endpoints that support `create` operation, please refer to our API Reference: https://developer.intuit.com/docs/api/accounting

We use `Invoice` as our example here (Other API entities have very similar data structure compared with Invoice, developers should have no difficulty replacing Invoice with other entities by using our API reference page).

### Constructing Entities

Last week, we provide a sewing service to our Customer `Alex`. We need to charge him `$150` dollars. How can we record this in QuickBooks Online?

When creating an Invoice, it will need to have at least a target Customer, a documentation Number that identifies the Invoice, and information for goods/services that we provide.

The first thing we need to figure out is how to tell QuickBooks Online that this Invoice is for our Customer `Alex`.

In QuickBooks Online, all Name list resource (like Customer, Employee, Vendor, Item, etc, they are grouped under `Name list resources` in the API Reference page) will be referred by their `Id` in QuickBooks Online. For our example, “Alex” has `Id=1`.

Refer back to Invoice documentation page <https://developer.intuit.com/docs/api/accounting/invoice>, we found `CustomerRef` is exactly what we are looking for:

Document for CustomerRef

we follow the instruction, use the same name `CustomerRef` from our docs as our key, and passing `1` to its `value` attribute. Record them in an array format:

    "CustomerRef" => [
          "value" => "1",
          "name" => "Alex"
    ]

and this will be the part specifying a `Customer` for an Invoice.

Note

1. The key, `CustomerRef` we use in our array has the SAME name displayed on our documentation page. This is not by coincidence. For all the keys we use in the QuickBooks V3 SDK, they all have the same name as our docs.
2. The field `name` is not required here, only `value` is.
3. Refer to the Query and access resources (GET) section for how to find the `Id` of a Name list resources

After the Customer is provided, QuickBooks Online will need information about the goods/services we provide.

QuickBooks Online identifies the goods/services for an entity by extracting information from what it called `Line` Item. An `Line` item represents a transaction. QuickBooks Online has five types of Line Item: `SalesItemLine`, `GroupItemLine` , `DescriptionOnly`, `DiscountLine`, and `SubtotalLine`. Each `Line` type has a specific usage. For our Invoice, we will use `SalesItemLine` to represent the sewing service we provide to `Alex`.

To find out what information we need to provide in the SalesItemLine, we refer back to the API reference page again: <https://developer.intuit.com/docs/api/accounting/invoice>

Document for SalesItemLine

The fields that are labelled as `required` is what we need to provide. We found that we need to specify the `Amount` we charge for `Alex`, which is `$150` dollars; the `DetailType` for the LineItem, which is `SalesItemLineDetail`. Under the `SalesItemLineDetail`, we also need to specify the `Item` we sell as well (The docs said it is optional, however, for `Invoice` creation with Service, it is required).

In QuickBooks Online, an `Item` is a thing that your company buys, sells, or re-sells, such as products and services. An item is shown as a line on an invoice or other sales form. Since `Item` is located under `Name list resources`, it will be a `Reference` type. We find that QuickBooks Online has a default `Item` called `Services`, which has `Id=1`. We use this `Services` item to represent our sewing service. Again, we store these information in an associate array, adding description to make it clear.

    "Line" => [
        [
          "Description" => "Sewing Service for Alex",
          "Amount" => 150.00,
          "DetailType" => "SalesItemLineDetail",
          "SalesItemLineDetail" => [
            "ItemRef" => [
              "value" => 1,
              "name" => "Services"
            ]
          ]
        ]
    ]

Searching the API reference page for documentation number, we have:

    "DocNumber" => "101",

Putting everything together for our Invoice, we have the following associate array:

    [
      "DocNumber" => "101",
      "Line" => [
      [
        "Description" => "Sewing Service for Alex",
        "Amount" => 150.00,
        "DetailType" => "SalesItemLineDetail",
        "SalesItemLineDetail" => [
          "ItemRef" => [
            "value" => 1,
            "name" => "Services"
          ]
        ]
      ]],
      "CustomerRef" => [
         "value" => "1",
         "name" => "Alex"
      ]
    ]

This associate array contains all the information we want to store in the Invoice. To actually use this array to construct an Invoice Object with this information, we will import our Invoice Facade class:

    use QuickBooksOnline\API\Facades\Invoice;

and pass the array to the `create()` static method:

    $invoiceToCreate = Invoice::create([
      "DocNumber" => "101",
      "Line" => [
        [
          "Description" => "Sewing Service for Alex",
          "Amount" => 150.00,
          "DetailType" => "SalesItemLineDetail",
          "SalesItemLineDetail" => [
            "ItemRef" => [
              "value" => 1,
              "name" => "Services"
            ]
          ]
        ]
      ],
      "CustomerRef" => [
          "value" => "1",
          "name" => "Alex"
      ]
    ]);

and the `$invoiceToCreate` object will our final object sent to QuickBooks Online.

### Sending Requests

After the object is constructed, sending it to QuickBooks Online will be very straightforward.

We will use the `$dataService->Add()` method to complete our task:

    $resultObj = $dataService->Add($invoiceToCreate);

### Handling Response

If everything works as expected, the `$resultObj` will be a copy of the `Invoice` we just created, you can directly access all its public fields. However, if there is any error occurred, we need to use `$error = $dataService->getLastError();` to track for the error:

    $error = $dataService->getLastError();
    if ($error) {
        echo "The Status code is: " . $error->getHttpStatusCode() . "\n";
        echo "The Helper message is: " . $error->getOAuthHelperError() . "\n";
        echo "The Response message is: " . $error->getResponseBody() . "\n";
    }else {
        echo "Created Id={$resultObj->Id}. Reconstructed response body:\n\n";
        $xmlBody = XmlObjectSerializer::getPostXmlFromArbitraryEntity($resultingObj, $urlResource);
        echo $xmlBody . "\n";
    }

### Example and Result

Here is the Complete example and what the result looks like in QuickBooks Online:

    use QuickBooksOnline\API\Core\ServiceContext;
    use QuickBooksOnline\API\DataService\DataService;
    use QuickBooksOnline\API\Core\Http\Serialization\XmlObjectSerializer;
    use QuickBooksOnline\API\Facades\Invoice;
    // Prep Data Services
    $dataService = DataService::Configure(array(
        'auth_mode' => 'oauth1',
        'consumerKey' => "qyprdUSoVpIHrtBp0eDMTHGz8UXuSz",
        'consumerSecret' => "TKKBfdlU1I1GEqB9P3AZlybdC8YxW5qFSbuShkG7",
        'accessTokenKey' => "qyprdxccscoNl7KRbUJoaJQIhUvyXRzD9tNOlXn4DhRDoj4g",
        'accessTokenSecret' => "JqkHSBKzNHbqjMq0Njbcq8fjgJSpfjMvqHVWnDOW",
        'QBORealmID' => "193514464689044",
        'baseUrl' => "Development"
    ));
    $dataService->setLogLocation("/Users/hlu2/Desktop/newFolderForLog");
    $dataService->throwExceptionOnError(true);
    //Add a new Invoice
    $invoiceToCreate = Invoice::create([
      "DocNumber" => "101",
      "Line" => [
        [
          "Description" => "Sewing Service for Alex",
          "Amount" => 150.00,
          "DetailType" => "SalesItemLineDetail",
          "SalesItemLineDetail" => [
            "ItemRef" => [
              "value" => 1,
              "name" => "Services"
            ]
          ]
        ]
      ],
      "CustomerRef" => [
          "value" => "1",
          "name" => "Alex"
      ]
    ]);

    $resultObj = $dataService->Add($invoiceToCreate);
    $error = $dataService->getLastError();
    if ($error) {
        echo "The Status code is: " . $error->getHttpStatusCode() . "\n";
        echo "The Helper message is: " . $error->getOAuthHelperError() . "\n";
        echo "The Response message is: " . $error->getResponseBody() . "\n";
    }else {
        echo "Created Id={$resultObj->Id}. Reconstructed response body:\n\n";
        $xmlBody = XmlObjectSerializer::getPostXmlFromArbitraryEntity($resultingObj, $urlResource);
        echo $xmlBody . "\n";
    }

Result shown on QuickBooks Online

### Constructing Entities with Tax

Many QuickBooks Online users need to create entities with Tax. It is achieved on V3 PHP SDK by specifying the `value` on `SalesItemLineDetail.TaxCodeRef` when creating entities. Below is an example with creating Invoice with Tax on a Canadian QuickBooks Online Company:

    use QuickBooksOnline\API\Core\ServiceContext;
    use QuickBooksOnline\API\DataService\DataService;
    use QuickBooksOnline\API\Core\Http\Serialization\XmlObjectSerializer;
    use QuickBooksOnline\API\Facades\Invoice;
    // Prep Data Services
    $dataService = DataService::Configure(array(
      'auth_mode' => 'oauth2',
      'ClientID' => "Q094uKi203zS1V1ZxH6dyff236cHa2CQhFiXs3JZFjRq1Gmu9f",
      'ClientSecret' => "NacL2Q92jmFEKjycARHEw8qrGGD1fv89OMxbjjbq",
      'accessTokenKey' =>
      'eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..-JhZXI7fxx9AnbF8z6vHtQ. .IdURYPB5ZiD_FwTnqZpz7A',
      'refreshTokenKey' => "L011529104721Bb4GIrEKlXQ03Zw8pyGAHSsVxUt3prTKiNoVO",
      'QBORealmID' => "193514708967874",
      'baseUrl' => "Production"
    ));
    $dataService->setLogLocation("/Users/hlu2/Desktop/newFolderForLog");
    $dataService->throwExceptionOnError(true);
    //Add a new Invoice
    $invoiceToCreate = Invoice::create([
      "DocNumber" => "101",
      "Line" => [
        [
          "Description" => "Sewing Service for Jimmy in Canada",
          "Amount" => 150.00,
          "DetailType" => "SalesItemLineDetail",
          "SalesItemLineDetail" => [
            "ItemRef" => [
              "value" => 1,
              "name" => "Services"
            ],
            "TaxCodeRef" => [
              "value" => 2
            ]
          ]
        ]
      ],
      "CustomerRef" => [
          "value" => "2",
          "name" => "Jimmy"
      ]
    ]);

    $resultObj = $dataService->Add($invoiceToCreate);
    $error = $dataService->getLastError();
    if ($error) {
        echo "The Status code is: " . $error->getHttpStatusCode() . "\n";
        echo "The Helper message is: " . $error->getOAuthHelperError() . "\n";
        echo "The Response message is: " . $error->getResponseBody() . "\n";
    }else {
        echo "Created Id={$resultObj->Id}. Reconstructed response body:\n\n";
        $xmlBody = XmlObjectSerializer::getPostXmlFromArbitraryEntity($resultingObj, $urlResource);
        echo $xmlBody . "\n";
    }

Note

US Company do not have a numeric value for TaxCode. It is either `TAX` or `NON`.

Notice we didn’t provide `TxnTaxDetail` when creating sales transactions, however, it is returned as part of READ response. In QuickBooks Online, it automatically calculates tax and returns the amount in the TxnTaxDetail.TotalTax attribute of the transaction based on the TaxCode assigned to each line.

For more information, please refer to this docs: https://developer.intuit.com/docs/00_quickbooks_online/2_build/60_tutorials/0060_manage_sales_tax_for_non-us_locales

## Query and access resources (GET)

Most API requests sent to QuickBooks Online are either `Query` or `GetById`. For example, creating Invoice would require retrieving the `Id` stored for a specific `Customer` in QuickBooks Online. QuickBooks V3 PHP SDK provides easier way to perform these operations.

### Get by Entity Id

If you already possess the object contains the `Id`, you can use the `FindById()` method directly:

    //The $invoice object which has an Id
    $dataService->FindById($invoice);

or if you know the `Id` and which `API endpoint` it belongs, you can use a variation of the `FindById()` method by passing the string name of the API endpoint and the Id:

    //We are looking for Invoice with id=1. You can use
    //Bill, Customer, Purchase, etc
    $dataService->FindById("Invoice", 1);

Depends if the first parameter is a string or an object, the method will perform differently.

### Get Company Info or Preference

For getting Company Info, please use `getCompanyInfo()` directly from `DataService`:

    $CompanyInfo = $dataService->getCompanyInfo();

Similarly, for getting CompanyPreference, you can use `getCompanyPreferences()`:

    $CompanyPrefs = $dataService->getCompanyPreferences();

The `getCompanyInfo()` and `getCompanyPreferences()` are also regarded as sample methods to test OAuth 1&2 settings.

### Query resources

The query operation is the method for creating a guided query against an entity. The select statement in the request enables you to specify the selection criteria, entity properties, sort order, and pagination. To perform a query in QuickBooks Online, developers will use `$dataServices->Query()` method. It is similar to a pared down SQL query select statement with constraints in place to ensure the request doesn’t overload server-side resources. For example, to find all invoices from a QuickBooks Online Company, the query method would look like this:

    $allInvoices = $dataServices->Query("SELECT * FROM Invoice");

In QuickBooks Online, not all SQL statement is supported. The following are query operation limitations:

- The response set always returns all properties for each object. That is, projections are not supported.
- Only those properties with values are returned in the response set.
- Wild card character support with LIKE clauses is limited to “%” (wildcard that substitutes for 0 or more characters).
- The OR operation is not supported in WHERE clauses.
- The GROUP BY clause is not supported.
- The JOIN clause is not supported.

When using QuickBooks PHP V3 SDK to perform a query, it is suggested to construct the SQL statement OUTSIDE OF the `Query()` method, as developers may need to use backslash to escape special characters like apostrophe. For query operation, the returned result would always be `array`.

#### Maximum number of entities in a response

The maximum number of entities that can be returned in a response is 1000. If the result size is not specified, the default number is 100. If a query returns many entities, fetch the entities in chunks, as described in Pagination. To determine the number of entities that a particular query returns, probe by using the COUNT keyword in the query. See Count for details.

#### Pagination

To page through the results, specify STARTPOSITION (position of the entity in the query results) and MAXRESULTS (maximum number of entities in the result).

For example, suppose you have 25 invoices. The following query gets invoices 1 - 10:

    $firstTenInvoices = $dataServices->Query("SELECT * FROM Invoice STARTPOSITION 1 MAXRESULTS 10");

Starting from v4.0.5 release, developers can pass pagination as additional query parameters:

    $firstTenInvoices = $dataServices->Query("SELECT * FROM Invoice", 1, 10);

The above two code snippets perform the same query operation.

#### Count

To find out how many object will be returned by a query, specify the COUNT keyword. The number of entities is returned in the totalCount attribute of the <QueryResponse> element. The QuickBooks Online data objects, such as Customer, are not in the response.

The following query returns the total number of customers that belong to the QuickBooks company:

    $statement = "SELECT COUNT" . "(" . "*" . ")" . "FROM CUSTOMER";
    $numberOfCustomers = $dataServices->Query($statement);

#### Filters

The filter in the WHERE clause determines which entities are returned by a query. The filter criteria is based on the value of a property. For example, the following query returns all Invoice entities with a TotalAmt property greater than 1000.0:

    $invoicesGreaterThenThousand = $dataServices->Query("SELECT * FROM Invoice WHERE TotalAmt > '1000.0'");

Note that not all properties support filters. Refer back to the API reference page (https://developer.intuit.com/docs/api/accounting/), only fields that contain keywords “filterable” are filterable.

Note

1. For QuickBooks Online, a SQL comparison value is required to use SINGLE QUOTATION MARKS. SQL statements that use double quotes or only an integer value for comparison WILL NOT WORK:

    //You will get a 400 from QBO for error parsing string
    $theInvoice = $dataServices->Query("select * from Invoice where docNumber=1038");

2. For Query ReferenceType (ending with keyword Ref), DO NOT USE TypeRef.value. Just use TypeRef. For example, to query an invoice from customer ID 59, you will use:

    //You will get a 400 from QBO for error parsing string
    $theInvoice = $dataServices->Query("SELECT * FROM Invoice WHERE CustomerRef='58'");

#### Sorting

To sort the results of a query, include the ORDERBY clause, specifying a property as the sort field. Similar to filters, only fields that contain keywords “sortable” in the API reference page are sortable.

For example, the following query sorts Customer objects by the FamilyName property:

    $customers = $dataServices->Query("SELECT * FROM Customer ORDERBY FamilyName");

Please refer to our API Data Query page for more information: https://developer.intuit.com/docs/00_quickbooks_online/2_build/20_explore_the_quickbooks_online_api/50_data_queries

## Update resources (PUT)

### Update resources

Update resources often require a `GET` operation first. Similar to Create new resources (POST), we use an associate array to pass our updated data.

For example, we want to change the `Invoice #101` we just created (see example created at Create new resources (POST)) for `Alex` with an additional line item. Besides the sewing service, we also want to give `Alex` a discount, `10 dollars`.

After refer back to the API reference page, we add our discount Line with the sewing service(If you don’t have a Service called `Discount`, you will need to create it first, refer to Item API reference Page: https://developer.intuit.com/docs/api/accounting/item for instruction):

    "Line" => [
        [
          "Description" => "Sewing Service for Alex",
          "Amount" => 150.00,
          "DetailType" => "SalesItemLineDetail",
          "SalesItemLineDetail" => [
            "ItemRef" => [
              "value" => 1,
              "name" => "Services"
            ]
          ]
        ],
        [
          "Description" => "Discount for Alex",
          "Amount" => -10.00,
          "DetailType" => "SalesItemLineDetail",
          "SalesItemLineDetail" => [
            "ItemRef" => [
              "value" => 21,
              "name" => "Discount"
            ]
          ]
        ]
      ]

This associate array will be our updated Content.

Next step is finding our target invoice. We already know that the Invoice has `DocNumber = 101`, so we use query operation to find the Invoice:

    $targetInvoiceArray = $dataService->Query("select * from Invoice where DocNumber='101'");

The $targetInvoiceArray should only contain one element, and it will be our target to update:

    if(!empty($targetInvoiceArray) && sizeof($targetInvoiceArray) == 1){
        $theInvoice = current($targetInvoiceArray);
    }

At this point, we have both the target `Invoice`, and the updated content, we can use `Invoice::update()` method to update the Invoice with our new line item, and using `DataService->Update()` method to send the request:

    $updatedInvoice = Invoice::update($theInvoice, [
        "sparse" => true,
        "Line" => [
          [
            "Description" => "Sewing Service for Alex",
            "Amount" => 150.00,
            "DetailType" => "SalesItemLineDetail",
            "SalesItemLineDetail" => [
              "ItemRef" => [
                "value" => 1,
                "name" => "Services"
              ]
            ]
          ],
          [
            "Description" => "Discount for Alex",
            "Amount" => -10.00,
            "DetailType" => "SalesItemLineDetail",
            "SalesItemLineDetail" => [
              "ItemRef" => [
                "value" => 21,
                "name" => "Discount"
              ]
            ]
          ]
         ]
    ]);
    $updatedResult = dataService->Update($updatedInvoice);

and our `update` operation is complete.

For `Line` item update, QuickBooks Online will replace current `Line` with the updated `Line`, therefore, it requires us to provide the FULL `Line` details each time when we update `Line` item.

In QuickBooks Online PHP V3 SDK, you can use helper class `Line` to create the additional `Line` item, and merge this `Line` with existed `Line` from target object:

    $LineObj = Line::create([
        "Description" => "Discount for Alex",
        "Amount" => -10.00,
        "DetailType" => "SalesItemLineDetail",
        "SalesItemLineDetail" => [
          "ItemRef" => [
            "value" => 21,
            "name" => "Discount"
          ]
        ]
    ]);
    //Remove last element of Line Item.
    $lineArray = array_pop($theInvoice->Line);
    $lineArray[] = $LineObj;
    $updatedInvoice = Invoice::update($theInvoice, [
        "sparse" => true,
        "Line" => $lineArray
    ]);
    $updatedResult = dataService->Update($updatedInvoice);

The example remove the last element of Line Item, because the last Line of a read request would always be a `SubTotalLineDetail`. We can rely on QuickBooks Online to re-calculate this line for us.

### Update resources with Tax

QuickBooks Online does allow developers to update Tax on an existed entity. In order to update the tax used for an entity, the developer needs to do the following for the company(this rule applied for non-us Company):

1. Override the TxnTaxDetail.TotalTax with the new total tax amount.
2. Override the TxnTaxDetail.TaxLine.TaxLineDetail.TaxRateRef. You must send all TaxRateRefs of a TaxCode even if you are modifying just one of them.
3. Override the TxnTaxDetail.TaxLine.Amount
4. Override the TxnTaxDetail.TaxLine.TaxLineDetail.TaxPercent
5. Override the TaxCodeRef.value on LineDetail

Pretty much, if a developer wants to update TaxCode used for a LineItem, he/she needs to update everything. QuickBooks Online will not be able to calculate tax anymore, it is the developer’s job to calculate the tax correctly and provide it to QuickBooks Online.

For example, we have a invoice on a Canada Company:

Document for a Canadian Invoice with Tax

When we read this Invoice from API, here is what we have:

    {
      "Deposit": 0,
      "AllowIPNPayment": false,
      "AllowOnlinePayment": false,
      "AllowOnlineCreditCardPayment": false,
      "AllowOnlineACHPayment": false,
      "domain": "QBO",
      "sparse": false,
      "Id": "1",
      "SyncToken": "0",
      "MetaData": {
        "CreateTime": "2018-03-13T16:33:42-07:00",
        "LastUpdatedTime": "2018-03-13T16:33:42-07:00"
      },
      "CustomField": [],
      "DocNumber": "1001",
      "TxnDate": "2018-03-13",
      "CurrencyRef": {
        "value": "CAD",
        "name": "Canadian Dollar"
      },
      "LinkedTxn": [],
      "Line": [
      {
        "Id": "1",
        "LineNum": 1,
        "Description": "Jimmy Service hours",
        "Amount": 200.0,
        "DetailType": "SalesItemLineDetail",
        "SalesItemLineDetail": {
          "ItemRef": {
            "value": "2",
            "name": "Hours"
          },
          "UnitPrice": 200,
          "Qty": 1,
          "TaxCodeRef": {
            "value": "6"
          }
        }
       },
       {
        "Id": "2",
        "LineNum": 2,
        "Description": "Jimmy extra hours",
        "Amount": 100.0,
        "DetailType": "SalesItemLineDetail",
        "SalesItemLineDetail": {
          "ItemRef": {
            "value": "2",
            "name": "Hours"
          },
          "UnitPrice": 100,
          "Qty": 1,
          "TaxCodeRef": {
            "value": "2"
          }
        }
       },
       {
        "Amount": 300.0,
        "DetailType": "SubTotalLineDetail",
        "SubTotalLineDetail": {}
       }
     ],
     "TxnTaxDetail": {
      "TotalTax": 30.0,
      "TaxLine": [
        {
          "Amount": 30.0,
          "DetailType": "TaxLineDetail",
          "TaxLineDetail": {
            "TaxRateRef": {
              "value": "12"
            },
            "PercentBased": true,
            "TaxPercent": 15,
            "NetAmountTaxable": 200.0
          }
        },
        {
          "Amount": 0,
          "DetailType": "TaxLineDetail",
          "TaxLineDetail": {
            "TaxRateRef": {
              "value": "2"
            },
            "PercentBased": true,
            "TaxPercent": 0,
            "NetAmountTaxable": 100.0
          }
        }
       ]
     },
     "CustomerRef": {
      "value": "1",
      "name": "Jimmy"
     },
     "SalesTermRef": {
      "value": "3"
     },
     "DueDate": "2018-04-12",
     "GlobalTaxCalculation": "TaxExcluded",
     "TotalAmt": 330.0,
     "PrintStatus": "NotSet",
     "EmailStatus": "NotSet",
     "Balance": 330.0
    }

from the Invoice, we find out we actually provided wrong TaxCode for the second line, `Jimmy extra hours`, it should use the same TaxCode as first line, `15%`, not `0%`.

We follow the instruction to update the TaxCode:

1. TxnTaxDetail.TotalTax should be 45 (the extra hours now have 15 tax on it, 15 + 30 = 45)
2. TxnTaxDetail.TaxLine.TaxLineDetail.TaxRateRef now should be using 12 now, which is the same as first lineItem.
3. TxnTaxDetail.TaxLine.Amount should be 15(100 * 15% = 15).
4. TxnTaxDetail.TaxLine.TaxLineDetail.TaxPercent should be same 15% as well.
5. The TaxCode used on LineItem now should be 6 instead of 2.

After we changed the value, we found out that there is no longer two `TaxLine`, we should only have one TaxLine. Therefore, we delete the second TaxLine, only left the first one. Here is what our updating request looks like in JSON:

    {
      "Deposit": 0,
      "AllowIPNPayment": false,
      "AllowOnlinePayment": false,
      "AllowOnlineCreditCardPayment": false,
      "AllowOnlineACHPayment": false,
      "domain": "QBO",
      "sparse": false,
      "Id": "1",
      "SyncToken": "4",
      "MetaData": {
        "CreateTime": "2018-03-13T16:33:42-07:00",
        "LastUpdatedTime": "2018-03-13T17:05:39-07:00"
      },
      "CustomField": [],
      "DocNumber": "1001",
      "TxnDate": "2018-03-13",
      "CurrencyRef": {
        "value": "CAD",
        "name": "Canadian Dollar"
      },
      "LinkedTxn": [],
      "Line": [
        {
          "Id": "1",
          "LineNum": 1,
          "Description": "Jimmy Service hours",
          "Amount": 200.0,
          "DetailType": "SalesItemLineDetail",
          "SalesItemLineDetail": {
            "ItemRef": {
              "value": "2",
              "name": "Hours"
            },
            "UnitPrice": 200,
            "Qty": 1,
            "TaxCodeRef": {
              "value": "6"
            }
          }
        },
        {
          "Id": "2",
          "LineNum": 2,
          "Description": "Jimmy extra hours",
          "Amount": 100.0,
          "DetailType": "SalesItemLineDetail",
          "SalesItemLineDetail": {
            "ItemRef": {
              "value": "2",
              "name": "Hours"
            },
            "UnitPrice": 100,
            "Qty": 1,
            "TaxCodeRef": {
              "value": "6"
            }
          }
        },
        {
          "Amount": 300.0,
          "DetailType": "SubTotalLineDetail",
          "SubTotalLineDetail": {}
        }
      ],
      "TxnTaxDetail": {
        "TotalTax": 45.0,
        "TaxLine": [
          {
            "Amount": 45.0,
            "DetailType": "TaxLineDetail",
            "TaxLineDetail": {
              "TaxRateRef": {
                "value": "12"
              },
              "PercentBased": true,
              "TaxPercent": 15,
              "NetAmountTaxable": 200.0
            }
          }
        ]
      },
      "CustomerRef": {
        "value": "1",
        "name": "Jimmy"
      },
      "SalesTermRef": {
        "value": "3"
      },
      "DueDate": "2018-04-12",
      "GlobalTaxCalculation": "TaxExcluded",
      "TotalAmt": 330.0,
      "PrintStatus": "NotSet",
      "EmailStatus": "NotSet",
      "Balance": 330.0
    }

We put this body in our code, and make an update call. Our Invoice has been updated successfully.

Document for a Canadian Invoice after updating the Tax

Due to the complexity of updating TaxCode, Some developers prefer not updating the TaxCode used, just override the Tax amount used. In this case, they only need to override the `TxnTaxDetail.TotalTax` and `TxnTaxDetail.TaxLine.Amount`.

For example, we just want to update the TaxAmount to 45 instead of updating the TaxCode we use, we can provide this JSON body:

    {
      "sparse" : "true",
      "Id": "1",
      "SyncToken": "9",
      "TxnTaxDetail": {
        "TotalTax": 45.0,
        "TaxLine": [
        {
          "Amount": 30.0,
          "DetailType": "TaxLineDetail",
          "TaxLineDetail": {
            "TaxRateRef": {
              "value": "12"
            },
            "PercentBased": true,
            "TaxPercent": 15,
            "NetAmountTaxable": 200.0
          }
        },
        {
          "Amount": 15,
          "DetailType": "TaxLineDetail",
          "TaxLineDetail": {
            "TaxRateRef": {
              "value": "2"
            },
            "PercentBased": true,
            "TaxPercent": 0,
            "NetAmountTaxable": 100.0
            }
         }
        ]
      }
    }

and the UI will have the correct TotalTaxAmount, with wrong taxCode.

Document for a Canadian Invoice after overriding the Tax

### Sparse Update & Full Update

Be careful when you use the update method. The example we present all use `sparse => true` when update the invoice. QuickBooks Online provides two updates: full update and sparse update. The sparse update operation provides the ability to update a subset of attributes for a given object; only those specified in the request are updated. Missing attributes are left untouched. This is in contrast to the full update operation, where elements missing from the request are cleared.

Considerations for using sparse updates include:

> - Prevent unintended overwrites: A client application often does not use all the fields of an entity, so when it sends a full update request with only fields they use, it results in an erroneous blanking out of fields that were not sent.
> - Reduce request payload: Always desired, but is more relevant when the client application is mobile because of lower speeds, spotty connections, and the fact that mobile users are sensitive to amount of the data usage in each billing cycle.
> - Facilitate future field additions: New fields can be added to an entity without past versions of production applications clearing all other existing fields inadvertently, as would happen with a full update operation.

## Delete resources(DELETE)

There are three ways to “delete” an entity in QuickBooks Online: `DELETE`, `VOID`, and `Inactivate`. Please refer to our API reference docs: https://developer.intuit.com/docs/api/ for more details.

To `Delete` an entity, you need to provide the `Id` and `SyncToken` of that entity. If we want to delete the `Invoice` that has `Id=1`, here is how we do it:

    $invoice = Invoice::create([
        "Id" => "1",
        "SyncToken" => "0"
    ]);
    $currentResultObj = $dataService->Delete($invoice);

Developers can also `GET` the entity through a `READ` or `QUERY` operation, then passing the whole object to `Delete` method. (For `void` operation, simply replace `$dataService->Delete()` with `$dataService->Void()`)

For those entities only support `Inactivate`, `Delete` or `Void` will not work. In QuickBooks Online, `Inactivate` is not considered as `Delete` operation, it is considered as `Update`. It is achieved by setting the Active attribute to false in an object update request. Please refer to Update resources part for more details.

## Accessing Entity Fields Directly

So far, all the REST examples we present show how to change an object through an associate array. It is the suggested way to create/update an entity resource.

However, developers are welcomed to use objects fields directly. You can also create an invoice in this way:

    // Add an invoice
    $invoice = new IPPInvoice();
    $invoice->Deposit       = 0;
    $invoice->domain        =  "QBO";
    $invoice->AutoDocNumber = true;
    $invoice->TxnDate = date('Y-m-d', time());
    $invoice->CustomerRef   = "66";
    $invoice->PrivateNote   = "SomeNote";
    $invoice->TxnStatus     = "Payable";

    $line = new IPPLine();
    $line->Id = "0";
    $line->LineNum          = "1";
    $line->Description      = "test";
    $line->Amount           = 1000;
    $line->DetailType = "DescriptionOnly";

    $sub_line = new IPPLine();
    $sub_line->Id           = "0";
    $sub_line->LineNum      = "2";
    $sub_line->Description  = "Sub Total";
    $sub_line->Amount       = 2000;
    $sub_line->DetailType   = "SubtotalLineDetail";

    $invoice->Line          = array($line, $sub_line);
    $invoice->RemitToRef    = "66";
    $invoice->TotalAmt      = 1000;
    $invoice->FinanceCharge = 'false';

    // Add a invoice
    $resultingInvoiceObj = $dataService->Add($invoice);

The type of `value` will need to match type displayed on our Entity Reference page: https://developer-static.intuit.com/SDKDocs/QBV3Doc/IPPPHPDevKitV3/entities/index.html.

For example, if the `Id` type is `String`, then `$sub_line->Id= "0";` will work but `$sub_line->Id= 0;` will not.

Below is a list of entities that support create/update operation through static methods in the SDK:

- Account
- Bill
- BillPayment
- CompanyCurrency
- CreditMemo
- Customer
- Class
- Department
- Deposit
- Employee
- Estimate
- Line — For excessive usage of Line objects, we create easy way of contructing Line items as well. However, Line is not an API endpoint entity
- Invoice
- Item
- JournalEntry
- Payment
- Purchase
- PurchaseOrder
- RefundReceipt
- SalesReceipt
- TimeActivity
- Transfer
- VendorCredit
- Vendor
- TaxSerivce and TaxRate (for Creation Only)

## Other Supported Operations

Below is a list of operations that currently are supported by the SDK.

### Sending E-mail

For those entities that supports `send` operation, you can use `dataService->SendEmail()` method to send an E-mail to user/users:

    $invoice = $dataService->FindById("invoice", 1);
    $result = $dataSerivce->SendEmail($invoice, "abc@xyz.com");

### Batch Request

The SDK also support batch requests.

Developers will need to create a `$batch` object first, then add the operation in the batch request:

    $batch = $dataService->CreateNewBatch();
    $batch->AddQuery("select * from Customer startPosition 0 maxResults 20", "queryCustomer", "uniqueQuery");
    $batch->AddEntity($invoice, "uniqueIdentifier", "create");
    $batch->Execute();

To add a `query` to the batch request, use `AddQuery()` method.

To add an operation to the batch request, `AddEntity()` method will be used.

Both method will have its second parameter as a string to uniquely identify the operation.

The `AddEntity()` method will have its third parameter as `Operation Name`, it is either `create`, `update`, or `delete`.

The result of batch request will be inside its `intuitBatchItemResponses`, use `uniqueIdentifier` to find the specific batch response:

    $batchItemResponse = $batch->intuitBatchItemResponses["uniqueIdentifier"];

### Change Data Capture(CDC)

The change data capture (cdc) operation returns a list of objects that have changed since a specified time. This operation is for an app that periodically polls data services in order to refresh its local copy of object data. The V3 PHP SDK supports this function as well. For example, if I want to capture all the change for Invoice, Item and Bill from `2017803-07T12:28:32`, I will use below code:

    <?php
    require 'vendor/autoload.php';

    use QuickBooksOnline\API\Core\OAuth\OAuth2\OAuth2LoginHelper;
    use QuickBooksOnline\API\DataService\DataService;

    // Prep Data Services
    $dataService = DataService::Configure(array(
      'auth_mode' => 'oauth2',
      'ClientID' => "Q094uKi203zS1V1ZxH6dyff236cHa2CQhFiXs3JZFjRq1Gmu9f",
      'ClientSecret' => "NacL2Q92jmFEKjycARHEw8qrGGD1fv89OMxbjjbq",
      'accessTokenKey' =>
      'eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..-JhZXI7fxx9AnbF8z6vHtQ. .IdURYPB5ZiD_FwTnqZpz7A',
      'refreshTokenKey' => "L011529104721Bb4GIrEKlXQ03Zw8pyGAHSsVxUt3prTKiNoVO",
      'QBORealmID' => "193514708967874",
      'baseUrl' => "Production"
    ));

    // Use CDC APIs
    $entityList = array('invoice','item', 'bill');

    $cdcResponse = $dataService->CDC($entityList, "2017-03-07T12:28:32-08:00");
    $error = $dataService->getLastError();
    if ($error) {
      echo "The Status code is: " . $error->getHttpStatusCode() . "\n";
      echo "The Helper message is: " . $error->getOAuthHelperError() . "\n";
      echo "The Response message is: " . $error->getResponseBody() . "\n";
      exit();
    }

    if ($cdcResponse->entities) {
      foreach ($cdcResponse->entities as $entityName => $entityArray) {
          echo "CDC Says " . count($entityArray) . " Updated Entities of Type = {$entityName}\n";
      }
    }
    ?>

A few points to remember when using the CDC function on V3 SDK:

1. If an entity has no change, the SDK will return NULL for that entity. For example, if no invoice has been changed, then `$cdcResponse->entities['invoice']` will be NULL
2. The response does not distinguish Create vs Delete. Developers must query the correspodning entity again with the `Id` field returned from response.

### Generating Reports

In V3 PHP SDK, the report service is separate from data service. To use the Report Service, developers will need to create the `ReportService` object first, then set the parameters on the report service:

    <?php
    require 'vendor/autoload.php';

    use QuickBooksOnline\API\DataService\DataService;
    use QuickBooksOnline\API\ReportService\ReportService;
    use QuickBooksOnline\API\ReportService\ReportName;
    // Prep Data Services
    $dataService = DataService::Configure(array(
        'auth_mode' => 'oauth2',
        'ClientID' => "Q094uKi203zS1V1ZxH6dyff236cHa2CQhFiXs3JZFjRq1Gmu9f",
        'ClientSecret' => "NacL2Q92jmFEKjycARHEw8qrGGD1fv89OMxbjjbq",
        'accessTokenKey' =>
        'eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..-JhZXI7fxx9AnbF8z6vHtQ.g7RjYe2a2JmQk9ergWwqUoQiSBWVcoOT_rEo9SXpmdh4UdIgpC91HqEIiyFRh9j9XYD48P3E4E9IDN1kZy2kOHIH6FgV4yLeKJVja3jhs9mFvOEy13Z-T0cU9XpnbfvUDWBPpW9sC20G3GOj1Y9DBI9N-DzwKfTMp3PBg_RGPzDOgtAnqZ1on0bcBOpZ89WcL10_31mscfdyrL1Pl49MIdK3pclkqtjbP_jecBklC8xo-jXeOAyNAfuxgndWmIcnkbx5uK3UrqCJxgWUbQxG63r1lPpNif_BEKj8nymkMIUHuAA4dCvmDOrGZQMmWWkj2_mDk3ntRaploccFEn40GXLPNSYq-P8u2McruB74C4nb2SkW1RsxPSBhNWAQk1dVlDjbcd_9LltGE6JbwgeEGRbK4qro9jjRHQvIK -RBPLl1yBavHuOuJZYuHsQvq6eDxlc3_J7rjOfgC8QB1pix93GpPiNcqBK5aTGU37UuPQ05F39Fi9lIinI543p7kmafANyFBOgsxZ6NDg_HH0GEOtrsCgWdlRKWDgGoaklpdd727gvPmK3mTN6rp6ew5gtUrQjTsx9IiTl58SgLb_WuVWMf8a4fdA6tiL8Z12Zf86ymDjzlRrhlei5XFBqwPC55ulvgQ3x491fFj9q6D2ItgMjHb7vz1z1KI8dOVK1e31Q2ez3F4-1EhpJTG77GMHfM.IdURYPB5ZiD_FwTnqZpz7A',
        'refreshTokenKey' => "L011529104721Bb4GIrEKlXQ03Zw8pyGAHSsVxUt3prTKiNoVO",
        'QBORealmID' => "193514708967874",
        'baseUrl' => "Production"
      ));

      $serviceContext = $dataService->getServiceContext();

      // Prep Data Services
      $reportService = new ReportService($serviceContext);
      if (!$reportService) {
        exit("Problem while initializing ReportService.\n");
      }

      $reportService->setStartDate("2015-01-01");
      $reportService->setAccountingMethod("Accrual");
      $profitAndLossReport = $reportService->executeReport(ReportName::PROFITANDLOSS);
      if (!$profitAndLossReport) {
        exit("ProfitAndLossReport Is Null.\n");
      } else {
        $reportName = strtolower($profitAndLossReport->Header->ReportName);
        echo("ReportName: " . $reportName . "\n");
        echo("Profit And Loss Report Execution Successful!" . "\n");
      }

The `ReportService` constructor requires a `ServiceContext` Object, developers can get it from DataService. For a list of accepted query parameters, please refer to our documentation here: https://developer.intuit.com/docs/api/accounting/profit%20and%20loss

### Download As PDF

For a few endpoint entities, developers can get the entity as PDF. Invoice is one good example. To get an invoice from QuickBooks Online as PDF, use the `DownloadPDF` function:

Note

To specify where the PDF is downloaded, developers can manually pass the directory to the function, `DownloadPDF($invoice, $dir)`, or specify it at sdk.config file on the sdk, as `<contentWriter strategy="export" exportDirectory="/path/to/target/folder">`

    <?php
    require 'vendor/autoload.php';

    use QuickBooksOnline\API\DataService\DataService;
    use QuickBooksOnline\API\Facades\Invoice;

    // Prep Data Services
    $dataService = DataService::Configure(array(
        'auth_mode' => 'oauth2',
        'ClientID' => "Q094uKi203zS1V1ZxH6dyff236cHa2CQhFiXs3JZFjRq1Gmu9f",
        'ClientSecret' => "NacL2Q92jmFEKjycARHEw8qrGGD1fv89OMxbjjbq",
        'accessTokenKey' =>
        'eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..-JhZXI7fxx9AnbF8z6vHtQ. .IdURYPB5ZiD_FwTnqZpz7A',
        'refreshTokenKey' => "L011529104721Bb4GIrEKlXQ03Zw8pyGAHSsVxUt3prTKiNoVO",
        'QBORealmID' => "193514708967874",
        'baseUrl' => "Production"
    ));

    $dataService->throwExceptionOnError(true);
    $invoice = Invoice::create([
        "Id" => 2
    ]);

    $directoryForThePDF = $dataService->DownloadPDF($invoice, "/Users/hlu2/Desktop/newFolderForLog");
    echo "PDF is download at: " .$directoryForThePDF;