diff --git a/dev_requirements.txt b/dev_requirements.txt index 74c3c14b..05c1b035 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,4 +1,3 @@ coverage==7.3.0 ipdb==0.13.13 -mock==5.1.0 nose==1.3.7 \ No newline at end of file diff --git a/quickbooks/client.py b/quickbooks/client.py index d83a62d0..5575dbe1 100644 --- a/quickbooks/client.py +++ b/quickbooks/client.py @@ -248,7 +248,7 @@ def process_request(self, request_type, url, headers="", params="", data=""): request_type, url, headers=headers, params=params, data=data) def get_single_object(self, qbbo, pk, params=None): - url = "{0}/company/{1}/{2}/{3}/".format(self.api_url, self.company_id, qbbo.lower(), pk) + url = "{0}/company/{1}/{2}/{3}".format(self.api_url, self.company_id, qbbo.lower(), pk) result = self.get(url, {}, params=params) return result diff --git a/quickbooks/mixins.py b/quickbooks/mixins.py index 49db76fa..60140599 100644 --- a/quickbooks/mixins.py +++ b/quickbooks/mixins.py @@ -255,14 +255,26 @@ class ListMixin(object): @classmethod def all(cls, order_by="", start_position="", max_results=100, qb=None): - """ - :param start_position: - :param max_results: The max number of entities that can be returned in a response is 1000. - :param qb: - :return: Returns list - """ - return cls.where("", order_by=order_by, start_position=start_position, - max_results=max_results, qb=qb) + """Returns list of objects containing all objects in the QuickBooks database""" + if qb is None: + qb = QuickBooks() + + # For Item objects, we need to explicitly request the SKU field + if cls.qbo_object_name == "Item": + select = "SELECT *, Sku FROM {0}".format(cls.qbo_object_name) + else: + select = "SELECT * FROM {0}".format(cls.qbo_object_name) + + if order_by: + select += " ORDER BY {0}".format(order_by) + + if start_position: + select += " STARTPOSITION {0}".format(start_position) + + if max_results: + select += " MAXRESULTS {0}".format(max_results) + + return cls.query(select, qb=qb) @classmethod def filter(cls, order_by="", start_position="", max_results="", qb=None, **kwargs): diff --git a/tests/integration/test_account.py b/tests/integration/test_account.py index b90c953d..40383711 100644 --- a/tests/integration/test_account.py +++ b/tests/integration/test_account.py @@ -13,32 +13,77 @@ def setUp(self): def test_create(self): account = Account() - account.AcctNum = self.account_number - account.Name = self.name + # Use shorter timestamp for uniqueness (within 20 char limit) + timestamp = datetime.now().strftime('%m%d%H%M%S') + unique_number = f"T{timestamp}" # T for Test + unique_name = f"Test Account {timestamp}" + + account.AcctNum = unique_number + account.Name = unique_name + account.AccountType = "Bank" # Required field account.AccountSubType = "CashOnHand" - account.save(qb=self.qb_client) - self.id = account.Id - query_account = Account.get(account.Id, qb=self.qb_client) + created_account = account.save(qb=self.qb_client) + + # Verify the save was successful + self.assertIsNotNone(created_account) + self.assertIsNotNone(created_account.Id) + self.assertTrue(int(created_account.Id) > 0) - self.assertEqual(account.Id, query_account.Id) - self.assertEqual(query_account.Name, self.name) - self.assertEqual(query_account.AcctNum, self.account_number) + query_account = Account.get(created_account.Id, qb=self.qb_client) + + self.assertEqual(created_account.Id, query_account.Id) + self.assertEqual(query_account.Name, unique_name) + self.assertEqual(query_account.AcctNum, unique_number) + self.assertEqual(query_account.AccountType, "Bank") + self.assertEqual(query_account.AccountSubType, "CashOnHand") def test_update(self): - account = Account.filter(Name=self.name, qb=self.qb_client)[0] + # First create an account with a unique name and number + timestamp = datetime.now().strftime('%m%d%H%M%S') + unique_number = f"T{timestamp}" + unique_name = f"Test Account {timestamp}" + + account = Account() + account.AcctNum = unique_number + account.Name = unique_name + account.AccountType = "Bank" + account.AccountSubType = "CashOnHand" - account.Name = "Updated Name {0}".format(self.account_number) - account.save(qb=self.qb_client) + created_account = account.save(qb=self.qb_client) + + # Verify the save was successful + self.assertIsNotNone(created_account) + self.assertIsNotNone(created_account.Id) - query_account = Account.get(account.Id, qb=self.qb_client) - self.assertEqual(query_account.Name, "Updated Name {0}".format(self.account_number)) + # Change the name + updated_name = f"{unique_name}_updated" + created_account.Name = updated_name + updated_account = created_account.save(qb=self.qb_client) + + # Query the account and make sure it has changed + query_account = Account.get(updated_account.Id, qb=self.qb_client) + self.assertEqual(query_account.Name, updated_name) + self.assertEqual(query_account.AcctNum, unique_number) # Account number should not change def test_create_using_from_json(self): + timestamp = datetime.now().strftime('%m%d%H%M%S') + unique_number = f"T{timestamp}" + unique_name = f"Test JSON {timestamp}" + account = Account.from_json({ - "AcctNum": datetime.now().strftime('%d%H%M%S'), - "Name": "{} {}".format(self.name, self.time.strftime("%Y-%m-%d %H:%M:%S")), + "AcctNum": unique_number, + "Name": unique_name, + "AccountType": "Bank", "AccountSubType": "CashOnHand" }) - account.save(qb=self.qb_client) + created_account = account.save(qb=self.qb_client) + self.assertIsNotNone(created_account) + self.assertIsNotNone(created_account.Id) + + # Verify we can get the account + query_account = Account.get(created_account.Id, qb=self.qb_client) + self.assertEqual(query_account.Name, unique_name) + self.assertEqual(query_account.AccountType, "Bank") + self.assertEqual(query_account.AccountSubType, "CashOnHand") diff --git a/tests/integration/test_base.py b/tests/integration/test_base.py index 404d6a4f..3372b2ab 100644 --- a/tests/integration/test_base.py +++ b/tests/integration/test_base.py @@ -17,7 +17,6 @@ def setUp(self): ) self.qb_client = QuickBooks( - minorversion=73, auth_client=self.auth_client, refresh_token=os.environ.get('REFRESH_TOKEN'), company_id=os.environ.get('COMPANY_ID'), diff --git a/tests/integration/test_item.py b/tests/integration/test_item.py index ee12c686..f8ae4dcd 100644 --- a/tests/integration/test_item.py +++ b/tests/integration/test_item.py @@ -45,3 +45,22 @@ def test_create(self): self.assertEqual(query_item.IncomeAccountRef.value, self.income_account.Id) self.assertEqual(query_item.ExpenseAccountRef.value, self.expense_account.Id) self.assertEqual(query_item.AssetAccountRef.value, self.asset_account.Id) + + def test_sku_in_all(self): + """Test that SKU is properly returned when using Item.all()""" + # First create an item with a SKU + unique_name = "Test SKU Item {0}".format(datetime.now().strftime('%d%H%M%S')) + item = Item() + item.Name = unique_name + item.Type = "Service" + item.Sku = "TEST_SKU_" + self.account_number + item.IncomeAccountRef = self.income_account.to_ref() + item.ExpenseAccountRef = self.expense_account.to_ref() + item.save(qb=self.qb_client) + + # Now fetch all items and verify the SKU is present + items = Item.all(max_results=100, qb=self.qb_client) + found_item = next((i for i in items if i.Id == item.Id), None) + + self.assertIsNotNone(found_item, "Created item not found in Item.all() results") + self.assertEqual(found_item.Sku, "TEST_SKU_" + self.account_number) diff --git a/tests/unit/test_batch.py b/tests/unit/test_batch.py index e3bacd8f..9bc71f74 100644 --- a/tests/unit/test_batch.py +++ b/tests/unit/test_batch.py @@ -1,8 +1,5 @@ import unittest -try: - from mock import patch -except ImportError: - from unittest.mock import patch +from unittest.mock import patch from quickbooks import batch, client from quickbooks.objects.customer import Customer from quickbooks.exceptions import QuickbooksException diff --git a/tests/unit/test_cdc.py b/tests/unit/test_cdc.py index d115177a..da9ae0be 100644 --- a/tests/unit/test_cdc.py +++ b/tests/unit/test_cdc.py @@ -1,8 +1,5 @@ import unittest -try: - from mock import patch -except ImportError: - from unittest.mock import patch +from unittest.mock import patch from quickbooks.cdc import change_data_capture from quickbooks.objects import Invoice, Customer from quickbooks import QuickBooks diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 23c20632..37ea1d20 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -1,11 +1,7 @@ import json import warnings from tests.integration.test_base import QuickbooksUnitTestCase - -try: - from mock import patch, mock_open -except ImportError: - from unittest.mock import patch, mock_open +from unittest.mock import patch, mock_open from quickbooks.exceptions import QuickbooksException, SevereException, AuthorizationException from quickbooks import client, mixins @@ -32,12 +28,10 @@ def test_client_new(self): self.qb_client = client.QuickBooks( company_id="company_id", verbose=True, - minorversion=75, verifier_token=TEST_VERIFIER_TOKEN, ) self.assertEqual(self.qb_client.company_id, "company_id") - self.assertEqual(self.qb_client.minorversion, 75) def test_client_with_deprecated_minor_version(self): with warnings.catch_warnings(record=True) as w: @@ -154,7 +148,7 @@ def test_get_single_object(self, make_req): qb_client.company_id = "1234" qb_client.get_single_object("test", 1) - url = "https://sandbox-quickbooks.api.intuit.com/v3/company/1234/test/1/" + url = "https://sandbox-quickbooks.api.intuit.com/v3/company/1234/test/1" make_req.assert_called_with("GET", url, {}, params=None) @patch('quickbooks.client.QuickBooks.make_request') @@ -163,7 +157,7 @@ def test_get_single_object_with_params(self, make_req): qb_client.company_id = "1234" qb_client.get_single_object("test", 1, params={'param':'value'}) - url = "https://sandbox-quickbooks.api.intuit.com/v3/company/1234/test/1/" + url = "https://sandbox-quickbooks.api.intuit.com/v3/company/1234/test/1" make_req.assert_called_with("GET", url, {}, params={'param':'value'}) @patch('quickbooks.client.QuickBooks.process_request') @@ -264,7 +258,7 @@ def test_make_request_file_closed(self, process_request): class MockResponse(object): @property def text(self): - return "oauth_token_secret=secretvalue&oauth_callback_confirmed=true&oauth_token=tokenvalue" + return '{"QueryResponse": {"Department": []}}' @property def status_code(self): @@ -275,10 +269,8 @@ def status_code(self): return httplib.OK def json(self): - return "{}" + return json.loads(self.text) - def content(self): - return '' class MockResponseJson: def __init__(self, json_data=None, status_code=200): @@ -327,5 +319,8 @@ def get_session(self): class MockSession(object): - def request(self, request_type, url, no_idea, company_id, **kwargs): + def __init__(self): + self.access_token = "test_access_token" + + def request(self, request_type, url, headers=None, params=None, data=None, **kwargs): return MockResponse() diff --git a/tests/unit/test_mixins.py b/tests/unit/test_mixins.py index 0c413bf8..d2d6fc31 100644 --- a/tests/unit/test_mixins.py +++ b/tests/unit/test_mixins.py @@ -1,14 +1,13 @@ import unittest from urllib.parse import quote +from unittest import TestCase +from datetime import datetime +from unittest.mock import patch, ANY from quickbooks.objects import Bill, Invoice, Payment, BillPayment from tests.integration.test_base import QuickbooksUnitTestCase - -try: - from mock import patch -except ImportError: - from unittest.mock import patch +from tests.unit.test_client import MockSession from quickbooks.objects.base import PhoneNumber, QuickbooksBaseObject from quickbooks.objects.department import Department @@ -130,15 +129,17 @@ def test_to_dict(self): class ListMixinTest(QuickbooksUnitTestCase): - @patch('quickbooks.mixins.ListMixin.where') - def test_all(self, where): + @patch('quickbooks.mixins.ListMixin.query') + def test_all(self, query): + query.return_value = [] Department.all() - where.assert_called_once_with('', order_by='', max_results=100, start_position='', qb=None) + query.assert_called_once_with("SELECT * FROM Department MAXRESULTS 100", qb=ANY) def test_all_with_qb(self): + self.qb_client.session = MockSession() # Add a mock session with patch.object(self.qb_client, 'query') as query: Department.all(qb=self.qb_client) - self.assertTrue(query.called) + query.assert_called_once() @patch('quickbooks.mixins.ListMixin.where') def test_filter(self, where):