From 10ec398a128b4f27a98040edc89ab676446357ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leo=20Nikkil=C3=A4?= Date: Mon, 13 Mar 2017 05:28:58 +0200 Subject: [PATCH] Add preferences API operations --- lib/exquickbooks/oauth.ex | 2 +- lib/exquickbooks/preferences.ex | 39 ++++ test/exquickbooks/preferences_test.exs | 46 ++++ .../preferences/read_preferences.json | 197 ++++++++++++++++++ .../preferences/update_preferences.json | 197 ++++++++++++++++++ .../preferences/update_preferences_error.json | 14 ++ 6 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 lib/exquickbooks/preferences.ex create mode 100644 test/exquickbooks/preferences_test.exs create mode 100644 test/fixtures/preferences/read_preferences.json create mode 100644 test/fixtures/preferences/update_preferences.json create mode 100644 test/fixtures/preferences/update_preferences_error.json diff --git a/lib/exquickbooks/oauth.ex b/lib/exquickbooks/oauth.ex index 57b7465..d5fc7c0 100644 --- a/lib/exquickbooks/oauth.ex +++ b/lib/exquickbooks/oauth.ex @@ -1,6 +1,6 @@ defmodule ExQuickBooks.OAuth do @moduledoc """ - Authentication functions for OAuth 1.0a. + Functions for interacting with the OAuth API. QuickBooks uses the three-legged OAuth 1.0a flow. For a human-readable overview of the whole flow and how to implement it, see e.g. diff --git a/lib/exquickbooks/preferences.ex b/lib/exquickbooks/preferences.ex new file mode 100644 index 0000000..ef89e3f --- /dev/null +++ b/lib/exquickbooks/preferences.ex @@ -0,0 +1,39 @@ +defmodule ExQuickBooks.Preferences do + @moduledoc """ + Functions for interacting with the Preferences API. + + This module directly implements operations from the official API: + + """ + + use ExQuickBooks.Endpoint, base_url: ExQuickBooks.accounting_api + use ExQuickBooks.JSONEndpoint + + alias ExQuickBooks.Token + + @doc """ + Retrieves preferences for the given realm. + """ + @spec read_preferences(Token.t, String.t) :: + {:ok, json_map} | {:error, any} + def read_preferences(token, realm_id) do + request(:get, "company/#{realm_id}/preferences") + |> sign_request(token) + |> send_json_request + end + + @doc """ + Updates and retrieves preferences for the given realm. + + This operation performs a full update. The preferences map must define all of + the keys in the full preferences map returned by `read_preferences/2`, + otherwise the omitted values are set to their default values or NULL. + """ + @spec update_preferences(Token.t, String.t, json_map) :: + {:ok, json_map} | {:error, any} + def update_preferences(token, realm_id, preferences) do + request(:post, "company/#{realm_id}/preferences", preferences) + |> sign_request(token) + |> send_json_request + end +end diff --git a/test/exquickbooks/preferences_test.exs b/test/exquickbooks/preferences_test.exs new file mode 100644 index 0000000..a1c75dd --- /dev/null +++ b/test/exquickbooks/preferences_test.exs @@ -0,0 +1,46 @@ +defmodule ExQuickBooks.PreferencesTest do + use ExUnit.Case, async: false + use ExQuickBooks.APICase + + alias ExQuickBooks.Preferences + alias ExQuickBooks.Token + + doctest Preferences + + @token %Token{ + token: "token", + token_secret: "secret" + } + + @realm_id "realm_id" + + test "read_preferences/2 retrieves preferences" do + load_response("preferences/read_preferences.json") |> send_response + + assert {:ok, %{"Preferences" => _}} = + Preferences.read_preferences(@token, @realm_id) + + assert %{url: url} = take_request() + assert String.contains?(url, "/realm_id/") + end + + test "update_preferences/3 updates and retrieves preferences" do + load_response("preferences/update_preferences.json") |> send_response + + assert {:ok, %{"Preferences" => _}} = + Preferences.update_preferences(@token, @realm_id, %{"foo" => true}) + + assert %{url: url, body: body} = take_request() + assert String.contains?(url, "/realm_id/") + assert String.contains?(to_string(body), "foo") + end + + test "update_preferences/3 recovers from an error" do + load_response("preferences/update_preferences_error.json") + |> Map.put(:status_code, 400) + |> send_response + + assert {:error, %{"Fault" => _}} = + Preferences.update_preferences(@token, @realm_id, %{"foo" => true}) + end +end diff --git a/test/fixtures/preferences/read_preferences.json b/test/fixtures/preferences/read_preferences.json new file mode 100644 index 0000000..0354cfd --- /dev/null +++ b/test/fixtures/preferences/read_preferences.json @@ -0,0 +1,197 @@ +{ + "Preferences": { + "AccountingInfoPrefs": { + "TrackDepartments": false, + "ClassTrackingPerTxn": false, + "ClassTrackingPerTxnLine": false, + "CustomerTerminology": "Customers" + }, + "ProductAndServicesPrefs": { + "ForSales": true, + "ForPurchase": true, + "QuantityWithPriceAndRate": true, + "QuantityOnHand": true + }, + "SalesFormsPrefs": { + "CustomField": [ + { + "CustomField": [ + { + "Name": "SalesFormsPrefs.UseSalesCustom3", + "Type": "BooleanType", + "BooleanValue": false + }, + { + "Name": "SalesFormsPrefs.UseSalesCustom2", + "Type": "BooleanType", + "BooleanValue": false + }, + { + "Name": "SalesFormsPrefs.UseSalesCustom1", + "Type": "BooleanType", + "BooleanValue": true + } + ] + }, + { + "CustomField": [ + { + "Name": "SalesFormsPrefs.SalesCustomName1", + "Type": "StringType", + "StringValue": "Crew #" + } + ] + } + ], + "CustomTxnNumbers": false, + "AllowDeposit": false, + "AllowDiscount": true, + "DefaultDiscountAccount": "86", + "AllowEstimates": true, + "ETransactionEnabledStatus": "NotApplicable", + "ETransactionAttachPDF": false, + "ETransactionPaymentEnabled": false, + "IPNSupportEnabled": false, + "AllowServiceDate": false, + "AllowShipping": false, + "DefaultTerms": { + "value": "3" + }, + "UsingPriceLevels": false, + "DefaultCustomerMessage": "Thank you for your business and have a great day!" + }, + "EmailMessagesPrefs": { + "InvoiceMessage": { + "Subject": "Invoice from Craig's Design and Landscaping Services", + "Message": "Your invoice is attached. Please remit payment at your earliest convenience.\nThank you for your business - we appreciate it very much.\n\nSincerely,\nCraig's Design and Landscaping Services" + }, + "EstimateMessage": { + "Subject": "Estimate from Craig's Design and Landscaping Services", + "Message": "Please review the estimate below. Feel free to contact us if you have any questions.\nWe look forward to working with you.\n\nSincerely,\nCraig's Design and Landscaping Services" + }, + "SalesReceiptMessage": { + "Subject": "Sales Receipt from Craig's Design and Landscaping Services", + "Message": "Your sales receipt is attached.\nThank you for your business - we appreciate it very much.\n\nSincerely,\nCraig's Design and Landscaping Services" + }, + "StatementMessage": { + "Subject": "Statement from Craig's Design and Landscaping Services", + "Message": "Your statement is attached. Please remit payment at your earliest convenience.\nThank you for your business - we appreciate it very much.\n\nSincerely,\nCraig's Design and Landscaping Services" + } + }, + "VendorAndPurchasesPrefs": { + "TrackingByCustomer": true, + "BillableExpenseTracking": true, + "POCustomField": [ + { + "CustomField": [ + { + "Name": "PurchasePrefs.UsePurchaseCustom3", + "Type": "BooleanType", + "BooleanValue": false + }, + { + "Name": "PurchasePrefs.UsePurchaseCustom2", + "Type": "BooleanType", + "BooleanValue": true + }, + { + "Name": "PurchasePrefs.UsePurchaseCustom1", + "Type": "BooleanType", + "BooleanValue": true + } + ] + }, + { + "CustomField": [ + { + "Name": "PurchasePrefs.PurchaseCustomName2", + "Type": "StringType", + "StringValue": "Sales Rep" + }, + { + "Name": "PurchasePrefs.PurchaseCustomName1", + "Type": "StringType", + "StringValue": "Crew #" + } + ] + } + ] + }, + "TimeTrackingPrefs": { + "UseServices": true, + "BillCustomers": true, + "ShowBillRateToAll": false, + "WorkWeekStartDate": "Monday", + "MarkTimeEntriesBillable": true + }, + "TaxPrefs": { + "UsingSalesTax": true, + "TaxGroupCodeRef": { + "value": "2" + } + }, + "CurrencyPrefs": { + "MultiCurrencyEnabled": false, + "HomeCurrency": { + "value": "USD" + } + }, + "ReportPrefs": { + "ReportBasis": "Accrual", + "CalcAgingReportFromTxnDate": false + }, + "OtherPrefs": { + "NameValue": [ + { + "Name": "SalesFormsPrefs.DefaultCustomerMessage", + "Value": "Thank you for your business and have a great day!" + }, + { + "Name": "SalesFormsPrefs.DefaultItem", + "Value": "1" + }, + { + "Name": "DTXCopyMemo", + "Value": "true" + }, + { + "Name": "UncategorizedAssetAccountId", + "Value": "32" + }, + { + "Name": "UncategorizedIncomeAccountId", + "Value": "30" + }, + { + "Name": "UncategorizedExpenseAccountId", + "Value": "31" + }, + { + "Name": "SFCEnabled", + "Value": "true" + }, + { + "Name": "DataPartner", + "Value": "false" + }, + { + "Name": "Vendor1099Enabled", + "Value": "true" + }, + { + "Name": "TimeTrackingFeatureEnabled", + "Value": "true" + } + ] + }, + "domain": "QBO", + "sparse": false, + "Id": "1", + "SyncToken": "4", + "MetaData": { + "CreateTime": "2017-02-13T01:08:44-08:00", + "LastUpdatedTime": "2017-03-12T16:13:23-07:00" + } + }, + "time": "2017-03-12T20:05:48.317-07:00" +} diff --git a/test/fixtures/preferences/update_preferences.json b/test/fixtures/preferences/update_preferences.json new file mode 100644 index 0000000..cccab77 --- /dev/null +++ b/test/fixtures/preferences/update_preferences.json @@ -0,0 +1,197 @@ +{ + "Preferences": { + "AccountingInfoPrefs": { + "TrackDepartments": false, + "ClassTrackingPerTxn": false, + "ClassTrackingPerTxnLine": false, + "CustomerTerminology": "Customers" + }, + "ProductAndServicesPrefs": { + "ForSales": true, + "ForPurchase": true, + "QuantityWithPriceAndRate": true, + "QuantityOnHand": true + }, + "SalesFormsPrefs": { + "CustomField": [ + { + "CustomField": [ + { + "Name": "SalesFormsPrefs.UseSalesCustom3", + "Type": "BooleanType", + "BooleanValue": false + }, + { + "Name": "SalesFormsPrefs.UseSalesCustom2", + "Type": "BooleanType", + "BooleanValue": false + }, + { + "Name": "SalesFormsPrefs.UseSalesCustom1", + "Type": "BooleanType", + "BooleanValue": true + } + ] + }, + { + "CustomField": [ + { + "Name": "SalesFormsPrefs.SalesCustomName1", + "Type": "StringType", + "StringValue": "Crew #" + } + ] + } + ], + "CustomTxnNumbers": false, + "AllowDeposit": false, + "AllowDiscount": true, + "DefaultDiscountAccount": "86", + "AllowEstimates": true, + "ETransactionEnabledStatus": "NotApplicable", + "ETransactionAttachPDF": false, + "ETransactionPaymentEnabled": false, + "IPNSupportEnabled": false, + "AllowServiceDate": false, + "AllowShipping": false, + "DefaultTerms": { + "value": "3" + }, + "UsingPriceLevels": false, + "DefaultCustomerMessage": "Thank you for your business and have a great day!" + }, + "EmailMessagesPrefs": { + "InvoiceMessage": { + "Subject": "Invoice from Craig's Design and Landscaping Services", + "Message": "Your invoice is attached. Please remit payment at your earliest convenience.\nThank you for your business - we appreciate it very much.\n\nSincerely,\nCraig's Design and Landscaping Services" + }, + "EstimateMessage": { + "Subject": "Estimate from Craig's Design and Landscaping Services", + "Message": "Please review the estimate below. Feel free to contact us if you have any questions.\nWe look forward to working with you.\n\nSincerely,\nCraig's Design and Landscaping Services" + }, + "SalesReceiptMessage": { + "Subject": "Sales Receipt from Craig's Design and Landscaping Services", + "Message": "Your sales receipt is attached.\nThank you for your business - we appreciate it very much.\n\nSincerely,\nCraig's Design and Landscaping Services" + }, + "StatementMessage": { + "Subject": "Statement from Craig's Design and Landscaping Services", + "Message": "Your statement is attached. Please remit payment at your earliest convenience.\nThank you for your business - we appreciate it very much.\n\nSincerely,\nCraig's Design and Landscaping Services" + } + }, + "VendorAndPurchasesPrefs": { + "TrackingByCustomer": true, + "BillableExpenseTracking": true, + "POCustomField": [ + { + "CustomField": [ + { + "Name": "PurchasePrefs.UsePurchaseCustom3", + "Type": "BooleanType", + "BooleanValue": false + }, + { + "Name": "PurchasePrefs.UsePurchaseCustom2", + "Type": "BooleanType", + "BooleanValue": true + }, + { + "Name": "PurchasePrefs.UsePurchaseCustom1", + "Type": "BooleanType", + "BooleanValue": true + } + ] + }, + { + "CustomField": [ + { + "Name": "PurchasePrefs.PurchaseCustomName2", + "Type": "StringType", + "StringValue": "Sales Rep" + }, + { + "Name": "PurchasePrefs.PurchaseCustomName1", + "Type": "StringType", + "StringValue": "Crew #" + } + ] + } + ] + }, + "TimeTrackingPrefs": { + "UseServices": true, + "BillCustomers": true, + "ShowBillRateToAll": false, + "WorkWeekStartDate": "Monday", + "MarkTimeEntriesBillable": true + }, + "TaxPrefs": { + "UsingSalesTax": true, + "TaxGroupCodeRef": { + "value": "2" + } + }, + "CurrencyPrefs": { + "MultiCurrencyEnabled": false, + "HomeCurrency": { + "value": "USD" + } + }, + "ReportPrefs": { + "ReportBasis": "Accrual", + "CalcAgingReportFromTxnDate": false + }, + "OtherPrefs": { + "NameValue": [ + { + "Name": "SalesFormsPrefs.DefaultCustomerMessage", + "Value": "Thank you for your business and have a great day!" + }, + { + "Name": "SalesFormsPrefs.DefaultItem", + "Value": "1" + }, + { + "Name": "DTXCopyMemo", + "Value": "true" + }, + { + "Name": "UncategorizedAssetAccountId", + "Value": "32" + }, + { + "Name": "UncategorizedIncomeAccountId", + "Value": "30" + }, + { + "Name": "UncategorizedExpenseAccountId", + "Value": "31" + }, + { + "Name": "SFCEnabled", + "Value": "true" + }, + { + "Name": "DataPartner", + "Value": "false" + }, + { + "Name": "Vendor1099Enabled", + "Value": "true" + }, + { + "Name": "TimeTrackingFeatureEnabled", + "Value": "true" + } + ] + }, + "domain": "QBO", + "sparse": false, + "Id": "1", + "SyncToken": "4", + "MetaData": { + "CreateTime": "2017-02-13T01:08:44-08:00", + "LastUpdatedTime": "2017-03-12T16:13:23-07:00" + } + }, + "time": "2017-03-12T20:13:29.531-07:00" +} diff --git a/test/fixtures/preferences/update_preferences_error.json b/test/fixtures/preferences/update_preferences_error.json new file mode 100644 index 0000000..8ca9c94 --- /dev/null +++ b/test/fixtures/preferences/update_preferences_error.json @@ -0,0 +1,14 @@ +{ + "Fault": { + "Error": [ + { + "Message": "Stale Object Error", + "Detail": "Stale Object Error : You and Leo Nikkilä were working on this at the same time. Leo Nikkilä finished before you did, so your work was not saved.", + "code": "5010", + "element": "" + } + ], + "type": "ValidationFault" + }, + "time": "2017-03-12T20:26:12.521-07:00" +}