python-terminusgps 1.8.0__tar.gz → 1.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of python-terminusgps might be problematic. Click here for more details.

Files changed (34) hide show
  1. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/PKG-INFO +3 -1
  2. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/pyproject.toml +3 -1
  3. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/authorizenet/profiles/addresses.py +42 -19
  4. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/authorizenet/profiles/base.py +2 -2
  5. python_terminusgps-1.9.0/terminusgps/authorizenet/profiles/customers.py +109 -0
  6. python_terminusgps-1.9.0/terminusgps/authorizenet/profiles/payments.py +118 -0
  7. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/settings.py +2 -0
  8. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/uv.lock +167 -7
  9. python_terminusgps-1.8.0/terminusgps/authorizenet/profiles/customers.py +0 -72
  10. python_terminusgps-1.8.0/terminusgps/authorizenet/profiles/payments.py +0 -90
  11. python_terminusgps-1.8.0/terminusgps/test.py +0 -55
  12. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/.gitignore +0 -0
  13. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/COPYING +0 -0
  14. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/README.md +0 -0
  15. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/requirements.txt +0 -0
  16. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/__init__.py +0 -0
  17. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/authorizenet/__init__.py +0 -0
  18. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/authorizenet/auth.py +0 -0
  19. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/aws/__init__.py +0 -0
  20. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/aws/secrets.py +0 -0
  21. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/__init__.py +0 -0
  22. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/constants.py +0 -0
  23. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/errors.py +0 -0
  24. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/flags.py +0 -0
  25. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/__init__.py +0 -0
  26. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/base.py +0 -0
  27. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/resource.py +0 -0
  28. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/retranslator.py +0 -0
  29. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/route.py +0 -0
  30. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/unit.py +0 -0
  31. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/unit_group.py +0 -0
  32. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/items/user.py +0 -0
  33. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/session.py +0 -0
  34. {python_terminusgps-1.8.0 → python_terminusgps-1.9.0}/terminusgps/wialon/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 1.8.0
3
+ Version: 1.9.0
4
4
  Summary: Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more.
5
5
  Project-URL: Documentation, https://app.terminusgps.com/docs/apps/python-terminusgps/index.html
6
6
  Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
@@ -16,4 +16,6 @@ Requires-Dist: argparse>=1.4.0
16
16
  Requires-Dist: authorizenet>=1.1.5
17
17
  Requires-Dist: boto3>=1.34.144
18
18
  Requires-Dist: django>=5.1.5
19
+ Requires-Dist: intuit-oauth>=1.2.6
20
+ Requires-Dist: python-quickbooks>=0.9.10
19
21
  Requires-Dist: python-wialon>=1.2.4
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-terminusgps"
3
- version = "1.8.0"
3
+ version = "1.9.0"
4
4
  description = "Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more."
5
5
  readme = "README.md"
6
6
  authors = [ {name = "Blake Nall", email = "blake@terminusgps.com" } ]
@@ -17,6 +17,8 @@ dependencies = [
17
17
  "authorizenet>=1.1.5",
18
18
  "boto3>=1.34.144",
19
19
  "django>=5.1.5",
20
+ "intuit-oauth>=1.2.6",
21
+ "python-quickbooks>=0.9.10",
20
22
  "python-wialon>=1.2.4",
21
23
  ]
22
24
 
@@ -17,44 +17,67 @@ from terminusgps.authorizenet.profiles.base import AuthorizenetCustomerProfileBa
17
17
 
18
18
  class AddressProfile(AuthorizenetCustomerProfileBase):
19
19
  def create(self, **kwargs) -> int:
20
- if not kwargs.get("address"):
21
- raise ValueError("'address' is required on creation.")
20
+ shipping_addr = kwargs.get("shipping_addr")
22
21
 
22
+ if not shipping_addr:
23
+ raise ValueError("'shipping_addr' is required on creation.")
24
+
25
+ if not isinstance(shipping_addr, customerAddressType):
26
+ raise TypeError(
27
+ f"'shipping_addr' must be customerAddressType, got '{type(shipping_addr)}'."
28
+ )
29
+
30
+ return self._authorizenet_create_shipping_address(shipping_addr)
31
+
32
+ def delete(self) -> None:
33
+ self._authorizenet_delete_shipping_address()
34
+
35
+ def update(self, shipping_addr: customerAddressType) -> None:
36
+ self._authorizenet_update_shipping_address(shipping_addr)
37
+
38
+ def get_details(self) -> dict:
39
+ return self._authorizenet_get_shipping_address()
40
+
41
+ def _authorizenet_create_shipping_address(self, addr: customerAddressType) -> int:
23
42
  request = createCustomerShippingAddressRequest(
24
43
  merchantAuthentication=self.merchantAuthentication,
25
44
  customerProfileId=self.customerProfileId,
26
- address=kwargs["address"],
27
- defaultShippingAddress=self.default,
45
+ address=addr,
46
+ default=self.default,
28
47
  )
29
48
  controller = createCustomerShippingAddressController(request)
30
49
  response = self.execute_controller(controller)
31
50
  return int(response.get("customerAddressId"))
32
51
 
33
- def delete(self) -> None:
34
- request = deleteCustomerShippingAddressRequest(
35
- merchantAuthentication=self.merchantAuthentication,
36
- customerProfileId=self.customerProfileId,
37
- customerAddressId=self.id,
38
- )
39
- controller = deleteCustomerShippingAddressController(request)
40
- self.execute_controller(controller)
41
-
42
- def update(self, address: customerAddressType, default: bool = False) -> None:
52
+ def _authorizenet_update_shipping_address(self, addr: customerAddressType) -> None:
53
+ assert self.id, "'id' was not set."
54
+ addr.customerAddressId = self.id
43
55
  request = updateCustomerShippingAddressRequest(
44
56
  merchantAuthentication=self.merchantAuthentication,
45
57
  customerProfileId=self.customerProfileId,
46
- address=address,
47
- defaultShippingAddress=default,
58
+ address=addr,
59
+ default=self.default,
48
60
  )
49
61
  controller = updateCustomerShippingAddressController(request)
50
62
  self.execute_controller(controller)
51
- self._default = default
52
63
 
53
- def get_details(self) -> getCustomerShippingAddressResponse:
64
+ def _authorizenet_get_shipping_address(self) -> dict:
65
+ assert self.id, "'id' was not set."
54
66
  request = getCustomerShippingAddressRequest(
55
67
  merchantAuthentication=self.merchantAuthentication,
56
68
  customerProfileId=self.customerProfileId,
57
69
  customerAddressId=self.id,
58
70
  )
59
71
  controller = getCustomerShippingAddressController(request)
60
- return self.execute_controller(controller)
72
+ response = self.execute_controller(controller)
73
+ return response
74
+
75
+ def _authorizenet_delete_shipping_address(self) -> None:
76
+ assert self.id, "'id' was not set."
77
+ request = deleteCustomerShippingAddressRequest(
78
+ merchantAuthentication=self.merchantAuthentication,
79
+ customerProfileId=self.customerProfileId,
80
+ customerAddressId=self.id,
81
+ )
82
+ controller = deleteCustomerShippingAddressController(request)
83
+ self.execute_controller(controller)
@@ -7,7 +7,7 @@ class AuthorizenetProfileBase:
7
7
  self, merchant_id: int | str, id: int | str | None = None, **kwargs
8
8
  ) -> None:
9
9
  self.merchant_id = merchant_id
10
- self.id = id if id else str(self.create(**kwargs))
10
+ self.id = str(id) if id else str(self.create(**kwargs))
11
11
 
12
12
  def __str__(self) -> str:
13
13
  return f"#{self.id}"
@@ -59,4 +59,4 @@ class AuthorizenetCustomerProfileBase(AuthorizenetProfileBase):
59
59
 
60
60
  @property
61
61
  def default(self) -> str:
62
- return "true" if self._default else "false"
62
+ return str(self._default).lower()
@@ -0,0 +1,109 @@
1
+ from authorizenet.apicontrollers import (
2
+ createCustomerProfileController,
3
+ deleteCustomerProfileController,
4
+ getCustomerProfileController,
5
+ updateCustomerProfileController,
6
+ )
7
+ from authorizenet.apicontractsv1 import (
8
+ customerProfileType,
9
+ createCustomerProfileRequest,
10
+ deleteCustomerProfileRequest,
11
+ getCustomerProfileRequest,
12
+ updateCustomerProfileRequest,
13
+ )
14
+
15
+ from terminusgps.authorizenet.profiles.base import AuthorizenetProfileBase
16
+
17
+
18
+ class CustomerProfile(AuthorizenetProfileBase):
19
+ def create(self, **kwargs) -> int:
20
+ """
21
+ Creates the customer profile.
22
+
23
+ :param email: An email address for the customer profile.
24
+ :type email: :py:obj:`str`
25
+ :param desc: A description describing the customer profile. Optional.
26
+ :type desc: :py:obj:`str` | :py:obj:`None`
27
+ :returns: The new customer profile id.
28
+ :rtype: :py:obj:`int`
29
+
30
+ """
31
+ if not kwargs.get("email"):
32
+ raise ValueError("'email' is required for creation.")
33
+
34
+ return self._authorizenet_create_customer_profile(
35
+ kwargs["email"], kwargs.get("desc", "")
36
+ )
37
+
38
+ def update(self, email: str, desc: str = "") -> None:
39
+ """Updates the customer profile."""
40
+ return self._authorizenet_update_customer_profile(email, desc)
41
+
42
+ def delete(self) -> None:
43
+ """Deletes the customer profile."""
44
+ return self._authorizenet_delete_customer_profile()
45
+
46
+ def get_payment_profiles(self) -> list[dict] | None:
47
+ """Retrieves the customer profile's payment profiles list."""
48
+ profiles = self._authorizenet_get_customer_profile(issuer_info=False).get(
49
+ "paymentProfiles"
50
+ )
51
+ return profiles if profiles else None
52
+
53
+ def get_shipping_addresses(self) -> list[dict] | None:
54
+ """Retrieves the customer profile's shipping address list."""
55
+ addresses = self._authorizenet_get_customer_profile(issuer_info=False).get(
56
+ "shipToList"
57
+ )
58
+ return addresses if addresses else None
59
+
60
+ def _authorizenet_create_customer_profile(self, email: str, desc: str = "") -> int:
61
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerProfileRequest` using the Authorize.NET API."""
62
+ request = createCustomerProfileRequest(
63
+ merchantAuthentication=self.merchantAuthentication,
64
+ profile=customerProfileType(
65
+ merchantCustomerId=self.merchantCustomerId,
66
+ email=email,
67
+ description=desc,
68
+ ),
69
+ )
70
+ controller = createCustomerProfileController(request)
71
+ response = self.execute_controller(controller)
72
+ return int(response.customerProfileId)
73
+
74
+ def _authorizenet_delete_customer_profile(self) -> None:
75
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerProfileRequest` using the Authorize.NET API."""
76
+ assert self.id, "'id' was not set."
77
+ request = deleteCustomerProfileRequest(
78
+ merchantAuthentication=self.merchantAuthentication,
79
+ customerProfileId=str(self.id),
80
+ )
81
+ controller = deleteCustomerProfileController(request)
82
+ self.execute_controller(controller)
83
+
84
+ def _authorizenet_get_customer_profile(self, issuer_info: bool = True) -> dict:
85
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerProfileRequest` using the Authorize.NET API."""
86
+ assert self.id, "'id' was not set."
87
+ request = getCustomerProfileRequest(
88
+ merchantAuthentication=self.merchantAuthentication,
89
+ customerProfileId=self.id,
90
+ includeIssuerInfo=str(issuer_info).lower(),
91
+ )
92
+ controller = getCustomerProfileController(request)
93
+ response = self.execute_controller(controller)
94
+ return response
95
+
96
+ def _authorizenet_update_customer_profile(self, email: str, desc: str = "") -> None:
97
+ """Executes an :py:obj:`~authorizenet.apicontractsv1.updateCustomerProfileRequest` using the Authorize.NET API."""
98
+ assert self.id, "'id' was not set."
99
+ request = updateCustomerProfileRequest(
100
+ merchantAuthentication=self.merchantAuthentication,
101
+ profile=customerProfileType(
102
+ merchantCustomerId=self.merchantCustomerId,
103
+ email=email,
104
+ description=desc,
105
+ customerProfileId=self.id,
106
+ ),
107
+ )
108
+ controller = updateCustomerProfileController(request)
109
+ self.execute_controller(controller)
@@ -0,0 +1,118 @@
1
+ from authorizenet.apicontractsv1 import (
2
+ createCustomerPaymentProfileRequest,
3
+ customerAddressType,
4
+ deleteCustomerPaymentProfileRequest,
5
+ getCustomerPaymentProfileRequest,
6
+ paymentProfile,
7
+ paymentType,
8
+ validateCustomerPaymentProfileRequest,
9
+ updateCustomerPaymentProfileRequest,
10
+ )
11
+ from authorizenet.apicontrollers import (
12
+ createCustomerPaymentProfileController,
13
+ deleteCustomerPaymentProfileController,
14
+ getCustomerPaymentProfileController,
15
+ updateCustomerPaymentProfileController,
16
+ validateCustomerPaymentProfileController,
17
+ )
18
+ from terminusgps.authorizenet.profiles.base import AuthorizenetCustomerProfileBase
19
+
20
+
21
+ class PaymentProfile(AuthorizenetCustomerProfileBase):
22
+ def create(self, **kwargs) -> int:
23
+ billing_addr = kwargs.get("billing_addr")
24
+ payment = kwargs.get("payment")
25
+ if not billing_addr:
26
+ raise ValueError("'billing_addr' is required on creation.")
27
+ if not payment:
28
+ raise ValueError("'payment' is required on creation.")
29
+
30
+ if not isinstance(billing_addr, customerAddressType):
31
+ raise TypeError(
32
+ f"'billing_addr' must be customerAddressType, got '{type(billing_addr)}'"
33
+ )
34
+ if not isinstance(payment, paymentType):
35
+ raise TypeError(
36
+ f"'payment' must be customerAddressType, got '{type(payment)}'"
37
+ )
38
+
39
+ return self._authorizenet_create_payment_profile(billing_addr, payment)
40
+
41
+ def update(self, billing_addr: customerAddressType, payment: paymentType) -> None:
42
+ self._authorizenet_update_payment_profile(billing_addr, payment)
43
+
44
+ def delete(self) -> None:
45
+ self._authorizenet_delete_payment_profile()
46
+
47
+ def get_details(self, issuer_info: bool = False) -> dict:
48
+ return self._authorizenet_get_payment_profile(issuer_info)
49
+
50
+ def _authorizenet_create_payment_profile(
51
+ self, billing_addr: customerAddressType, payment: paymentType
52
+ ) -> int:
53
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerPaymentProfileRequest` using the Authorize.NET API."""
54
+ request = createCustomerPaymentProfileRequest(
55
+ merchantAuthentication=self.merchantAuthentication,
56
+ customerProfileId=self.customerProfileId,
57
+ paymentProfile=paymentProfile(
58
+ billTo=billing_addr, payment=payment, default=self.default
59
+ ),
60
+ validationMode=self.validationMode,
61
+ )
62
+ controller = createCustomerPaymentProfileController(request)
63
+ response = self.execute_controller(controller)
64
+ return int(response.get("customerPaymentProfileId"))
65
+
66
+ def _authorizenet_update_payment_profile(
67
+ self, billing_addr: customerAddressType, payment: paymentType
68
+ ) -> None:
69
+ """Executes an :py:obj:`~authorizenet.apicontractsv1.updateCustomerPaymentProfileRequest` using the Authorize.NET API."""
70
+ assert self.id, "'id' was not set."
71
+ request = updateCustomerPaymentProfileRequest(
72
+ merchantAuthentication=self.merchantAuthentication,
73
+ customerProfileId=self.customerProfileId,
74
+ paymentProfile=paymentProfile(
75
+ billTo=billing_addr,
76
+ payment=payment,
77
+ defaultPaymentProfile=self.default,
78
+ customerPaymentProfileId=self.id,
79
+ ),
80
+ validationMode=self.validationMode,
81
+ )
82
+ controller = updateCustomerPaymentProfileController(request)
83
+ self.execute_controller(controller)
84
+
85
+ def _authorizenet_get_payment_profile(self, issuer_info: bool = False) -> dict:
86
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerPaymentProfileRequest` using the Authorize.NET API."""
87
+ assert self.id, "'id' was not set."
88
+ request = getCustomerPaymentProfileRequest(
89
+ merchantAuthentication=self.merchantAuthentication,
90
+ customerProfileId=self.customerProfileId,
91
+ customerPaymentProfileId=self.id,
92
+ includeIssuerInfo=str(issuer_info).lower(),
93
+ )
94
+ controller = getCustomerPaymentProfileController(request)
95
+ return self.execute_controller(controller)
96
+
97
+ def _authorizenet_validate_payment_profile(self) -> None:
98
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.validateCustomerPaymentProfileRequest` using the Authorize.NET API."""
99
+ assert self.id, "'id' was not set."
100
+ request = validateCustomerPaymentProfileRequest(
101
+ merchantAuthentication=self.merchantAuthentication,
102
+ customerProfileId=self.customerProfileId,
103
+ customerPaymentProfileId=self.id,
104
+ validationMode=self.validationMode,
105
+ )
106
+ controller = validateCustomerPaymentProfileController(request)
107
+ self.execute_controller(controller)
108
+
109
+ def _authorizenet_delete_payment_profile(self) -> None:
110
+ """Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerPaymentProfileRequest` using the Authorize.NET API."""
111
+ assert self.id, "'id' was not set."
112
+ request = deleteCustomerPaymentProfileRequest(
113
+ merchantAuthentication=self.merchantAuthentication,
114
+ customerProfileId=self.customerProfileId,
115
+ customerPaymentProfileId=self.id,
116
+ )
117
+ controller = deleteCustomerPaymentProfileController(request)
118
+ self.execute_controller(controller)
@@ -6,3 +6,5 @@ WIALON_ADMIN_ID = getenv("WIALON_ADMIN_ID")
6
6
  WIALON_TOKEN = getenv("WIALON_TOKEN")
7
7
  MERCHANT_AUTH_LOGIN_ID = getenv("MERCHANT_AUTH_LOGIN_ID")
8
8
  MERCHANT_AUTH_TRANSACTION_KEY = getenv("MERCHANT_AUTH_TRANSACTION_KEY")
9
+ QB_CLIENT_ID = getenv("QB_CLIENT_ID")
10
+ QB_CLIENT_SECRET = getenv("QB_CLIENT_ID")
@@ -50,30 +50,30 @@ wheels = [
50
50
 
51
51
  [[package]]
52
52
  name = "boto3"
53
- version = "1.36.11"
53
+ version = "1.36.12"
54
54
  source = { registry = "https://pypi.org/simple" }
55
55
  dependencies = [
56
56
  { name = "botocore" },
57
57
  { name = "jmespath" },
58
58
  { name = "s3transfer" },
59
59
  ]
60
- sdist = { url = "https://files.pythonhosted.org/packages/ee/ac/2b4346474bd3ae501a2fc0e2b5b4f12f412dc89c05bf321a8108d3a95b5c/boto3-1.36.11.tar.gz", hash = "sha256:b40fbf2c0f22e55b67df95475a68bb72be5169097180a875726b6b884339ac8b", size = 111010 }
60
+ sdist = { url = "https://files.pythonhosted.org/packages/04/2b/7365aab02b6768a9694ff43e0e5f1f83f205fc7fe253a410cda990425048/boto3-1.36.12.tar.gz", hash = "sha256:287d84f49bba3255a17b374578127d42b6251e72f55914a62e0ad9ca78c0954b", size = 111048 }
61
61
  wheels = [
62
- { url = "https://files.pythonhosted.org/packages/4e/a5/e6f90b0a768560a0c44cb9076a313ee6d669ec98fd2747a8451832403ffe/boto3-1.36.11-py3-none-any.whl", hash = "sha256:641dd772eac111d9443258f0f5491c57c2af47bddae94a8d32de19edb5bf7b1c", size = 139177 },
62
+ { url = "https://files.pythonhosted.org/packages/d0/0b/eb4d244ea84b0fd9e255286a30c98f37f8f28bbf6ea85f7f4b734bf929a9/boto3-1.36.12-py3-none-any.whl", hash = "sha256:32cdf0967287f3ec25a9dc09df0d29cb86b8900c3e0546a63d672775d8127abf", size = 139178 },
63
63
  ]
64
64
 
65
65
  [[package]]
66
66
  name = "botocore"
67
- version = "1.36.11"
67
+ version = "1.36.12"
68
68
  source = { registry = "https://pypi.org/simple" }
69
69
  dependencies = [
70
70
  { name = "jmespath" },
71
71
  { name = "python-dateutil" },
72
72
  { name = "urllib3" },
73
73
  ]
74
- sdist = { url = "https://files.pythonhosted.org/packages/a5/0f/6b92050154ad0e286b82ca36de5f87a466723e1cdc525df53270bcc36f60/botocore-1.36.11.tar.gz", hash = "sha256:c919be883f95b9e0c3021429a365d40cd7944b8345a07af30dc8d891ceefe07a", size = 13497505 }
74
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/54/cc9a665f92534aa105a49dd867f18d4eff4687835994a70a32a103fa11ae/botocore-1.36.12.tar.gz", hash = "sha256:86ed88beb4f244c96529435c868d3940073c2774116f0023fb7691f6e7053bd9", size = 13498321 }
75
75
  wheels = [
76
- { url = "https://files.pythonhosted.org/packages/2c/ce/e97be00389d51a010c0680ea688a073737ca3b2de6f924800fc61bf68e41/botocore-1.36.11-py3-none-any.whl", hash = "sha256:82c5660027f696608d0e55feb08c146c11c7ebeba7615961c7765dcf6009a00d", size = 13327743 },
76
+ { url = "https://files.pythonhosted.org/packages/92/3a/5e19d1d1896e86bb9c25f31f5f05ff2e8f40727aa66ee6149b545d3e28b0/botocore-1.36.12-py3-none-any.whl", hash = "sha256:5ae1ed362c8ed908a6ced8cdd12b21e2196c100bc79f9e95c9c1fc7f9ea74f5a", size = 13328293 },
77
77
  ]
78
78
 
79
79
  [[package]]
@@ -85,6 +85,39 @@ wheels = [
85
85
  { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
86
86
  ]
87
87
 
88
+ [[package]]
89
+ name = "cffi"
90
+ version = "1.17.1"
91
+ source = { registry = "https://pypi.org/simple" }
92
+ dependencies = [
93
+ { name = "pycparser" },
94
+ ]
95
+ sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
96
+ wheels = [
97
+ { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
98
+ { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
99
+ { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
100
+ { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
101
+ { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
102
+ { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 },
103
+ { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
104
+ { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
105
+ { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
106
+ { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
107
+ { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
108
+ { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
109
+ { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
110
+ { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
111
+ { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
112
+ { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
113
+ { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
114
+ { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
115
+ { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
116
+ { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
117
+ { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
118
+ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
119
+ ]
120
+
88
121
  [[package]]
89
122
  name = "charset-normalizer"
90
123
  version = "3.4.1"
@@ -129,6 +162,37 @@ wheels = [
129
162
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
130
163
  ]
131
164
 
165
+ [[package]]
166
+ name = "cryptography"
167
+ version = "44.0.0"
168
+ source = { registry = "https://pypi.org/simple" }
169
+ dependencies = [
170
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
171
+ ]
172
+ sdist = { url = "https://files.pythonhosted.org/packages/91/4c/45dfa6829acffa344e3967d6006ee4ae8be57af746ae2eba1c431949b32c/cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", size = 710657 }
173
+ wheels = [
174
+ { url = "https://files.pythonhosted.org/packages/55/09/8cc67f9b84730ad330b3b72cf867150744bf07ff113cda21a15a1c6d2c7c/cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", size = 6541833 },
175
+ { url = "https://files.pythonhosted.org/packages/7e/5b/3759e30a103144e29632e7cb72aec28cedc79e514b2ea8896bb17163c19b/cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", size = 3922710 },
176
+ { url = "https://files.pythonhosted.org/packages/5f/58/3b14bf39f1a0cfd679e753e8647ada56cddbf5acebffe7db90e184c76168/cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", size = 4137546 },
177
+ { url = "https://files.pythonhosted.org/packages/98/65/13d9e76ca19b0ba5603d71ac8424b5694415b348e719db277b5edc985ff5/cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", size = 3915420 },
178
+ { url = "https://files.pythonhosted.org/packages/b1/07/40fe09ce96b91fc9276a9ad272832ead0fddedcba87f1190372af8e3039c/cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", size = 4154498 },
179
+ { url = "https://files.pythonhosted.org/packages/75/ea/af65619c800ec0a7e4034207aec543acdf248d9bffba0533342d1bd435e1/cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", size = 3932569 },
180
+ { url = "https://files.pythonhosted.org/packages/c7/af/d1deb0c04d59612e3d5e54203159e284d3e7a6921e565bb0eeb6269bdd8a/cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", size = 4016721 },
181
+ { url = "https://files.pythonhosted.org/packages/bd/69/7ca326c55698d0688db867795134bdfac87136b80ef373aaa42b225d6dd5/cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", size = 4240915 },
182
+ { url = "https://files.pythonhosted.org/packages/ef/d4/cae11bf68c0f981e0413906c6dd03ae7fa864347ed5fac40021df1ef467c/cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", size = 2757925 },
183
+ { url = "https://files.pythonhosted.org/packages/64/b1/50d7739254d2002acae64eed4fc43b24ac0cc44bf0a0d388d1ca06ec5bb1/cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", size = 3202055 },
184
+ { url = "https://files.pythonhosted.org/packages/11/18/61e52a3d28fc1514a43b0ac291177acd1b4de00e9301aaf7ef867076ff8a/cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", size = 6542801 },
185
+ { url = "https://files.pythonhosted.org/packages/1a/07/5f165b6c65696ef75601b781a280fc3b33f1e0cd6aa5a92d9fb96c410e97/cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", size = 3922613 },
186
+ { url = "https://files.pythonhosted.org/packages/28/34/6b3ac1d80fc174812486561cf25194338151780f27e438526f9c64e16869/cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", size = 4137925 },
187
+ { url = "https://files.pythonhosted.org/packages/d0/c7/c656eb08fd22255d21bc3129625ed9cd5ee305f33752ef2278711b3fa98b/cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", size = 3915417 },
188
+ { url = "https://files.pythonhosted.org/packages/ef/82/72403624f197af0db6bac4e58153bc9ac0e6020e57234115db9596eee85d/cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", size = 4155160 },
189
+ { url = "https://files.pythonhosted.org/packages/a2/cd/2f3c440913d4329ade49b146d74f2e9766422e1732613f57097fea61f344/cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", size = 3932331 },
190
+ { url = "https://files.pythonhosted.org/packages/7f/df/8be88797f0a1cca6e255189a57bb49237402b1880d6e8721690c5603ac23/cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", size = 4017372 },
191
+ { url = "https://files.pythonhosted.org/packages/af/36/5ccc376f025a834e72b8e52e18746b927f34e4520487098e283a719c205e/cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", size = 4239657 },
192
+ { url = "https://files.pythonhosted.org/packages/46/b0/f4f7d0d0bcfbc8dd6296c1449be326d04217c57afb8b2594f017eed95533/cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", size = 2758672 },
193
+ { url = "https://files.pythonhosted.org/packages/97/9b/443270b9210f13f6ef240eff73fd32e02d381e7103969dc66ce8e89ee901/cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", size = 3202071 },
194
+ ]
195
+
132
196
  [[package]]
133
197
  name = "django"
134
198
  version = "5.1.5"
@@ -152,6 +216,15 @@ wheels = [
152
216
  { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
153
217
  ]
154
218
 
219
+ [[package]]
220
+ name = "enum-compat"
221
+ version = "0.0.3"
222
+ source = { registry = "https://pypi.org/simple" }
223
+ sdist = { url = "https://files.pythonhosted.org/packages/41/46/8ed2368976d934652d8f33f1fdd86f5580fab45e890c90a848e83097a093/enum-compat-0.0.3.tar.gz", hash = "sha256:3677daabed56a6f724451d585662253d8fb4e5569845aafa8bb0da36b1a8751e", size = 1389 }
224
+ wheels = [
225
+ { url = "https://files.pythonhosted.org/packages/55/ae/467bc4509246283bb59746e21a1a2f5a8aecbef56b1fa6eaca78cd438c8b/enum_compat-0.0.3-py3-none-any.whl", hash = "sha256:88091b617c7fc3bbbceae50db5958023c48dc40b50520005aa3bf27f8f7ea157", size = 1322 },
226
+ ]
227
+
155
228
  [[package]]
156
229
  name = "future"
157
230
  version = "1.0.0"
@@ -179,6 +252,22 @@ wheels = [
179
252
  { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 },
180
253
  ]
181
254
 
255
+ [[package]]
256
+ name = "intuit-oauth"
257
+ version = "1.2.6"
258
+ source = { registry = "https://pypi.org/simple" }
259
+ dependencies = [
260
+ { name = "enum-compat" },
261
+ { name = "pyjwt", extra = ["crypto"] },
262
+ { name = "requests" },
263
+ { name = "requests-oauthlib" },
264
+ { name = "six" },
265
+ ]
266
+ sdist = { url = "https://files.pythonhosted.org/packages/c4/e0/a292a5eb847e6ee9ef707443f0d85124d633cfecf31fa4867d67cc404a04/intuit_oauth-1.2.6.tar.gz", hash = "sha256:b93439e8135d536acdbe53cf9842930ade2205410c6ab3530fb1dbea12eee5d0", size = 13813 }
267
+ wheels = [
268
+ { url = "https://files.pythonhosted.org/packages/d6/15/e98ee874c3a82fd0f030b62865b4c7e8c608950386aaac71673cb118ce07/intuit_oauth-1.2.6-py3-none-any.whl", hash = "sha256:4c25b3fcbdb5aaaa65dcc8f0f71e8f8400dcaa4dcdac58b8333d5f1b11a8f82d", size = 15890 },
269
+ ]
270
+
182
271
  [[package]]
183
272
  name = "jinja2"
184
273
  version = "3.1.5"
@@ -253,6 +342,15 @@ wheels = [
253
342
  { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
254
343
  ]
255
344
 
345
+ [[package]]
346
+ name = "oauthlib"
347
+ version = "3.2.2"
348
+ source = { registry = "https://pypi.org/simple" }
349
+ sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 }
350
+ wheels = [
351
+ { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 },
352
+ ]
353
+
256
354
  [[package]]
257
355
  name = "packaging"
258
356
  version = "24.2"
@@ -262,6 +360,15 @@ wheels = [
262
360
  { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
263
361
  ]
264
362
 
363
+ [[package]]
364
+ name = "pycparser"
365
+ version = "2.22"
366
+ source = { registry = "https://pypi.org/simple" }
367
+ sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
368
+ wheels = [
369
+ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
370
+ ]
371
+
265
372
  [[package]]
266
373
  name = "pygments"
267
374
  version = "2.19.1"
@@ -271,6 +378,20 @@ wheels = [
271
378
  { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
272
379
  ]
273
380
 
381
+ [[package]]
382
+ name = "pyjwt"
383
+ version = "2.10.1"
384
+ source = { registry = "https://pypi.org/simple" }
385
+ sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 }
386
+ wheels = [
387
+ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 },
388
+ ]
389
+
390
+ [package.optional-dependencies]
391
+ crypto = [
392
+ { name = "cryptography" },
393
+ ]
394
+
274
395
  [[package]]
275
396
  name = "python-dateutil"
276
397
  version = "2.9.0.post0"
@@ -283,15 +404,30 @@ wheels = [
283
404
  { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
284
405
  ]
285
406
 
407
+ [[package]]
408
+ name = "python-quickbooks"
409
+ version = "0.9.10"
410
+ source = { registry = "https://pypi.org/simple" }
411
+ dependencies = [
412
+ { name = "intuit-oauth" },
413
+ { name = "python-dateutil" },
414
+ { name = "requests" },
415
+ { name = "requests-oauthlib" },
416
+ { name = "setuptools" },
417
+ ]
418
+ sdist = { url = "https://files.pythonhosted.org/packages/f7/a5/aa030ca351063e4881373935e9f880e4692583c76c71fc727ff940f389e7/python_quickbooks-0.9.10.tar.gz", hash = "sha256:54c9271222514cde85994581fa7c0754785891ddedb5db7db3808dcb2430848a", size = 34218 }
419
+
286
420
  [[package]]
287
421
  name = "python-terminusgps"
288
- version = "1.8.0"
422
+ version = "1.9.0"
289
423
  source = { editable = "." }
290
424
  dependencies = [
291
425
  { name = "argparse" },
292
426
  { name = "authorizenet" },
293
427
  { name = "boto3" },
294
428
  { name = "django" },
429
+ { name = "intuit-oauth" },
430
+ { name = "python-quickbooks" },
295
431
  { name = "python-wialon" },
296
432
  ]
297
433
 
@@ -307,6 +443,8 @@ requires-dist = [
307
443
  { name = "authorizenet", specifier = ">=1.1.5" },
308
444
  { name = "boto3", specifier = ">=1.34.144" },
309
445
  { name = "django", specifier = ">=5.1.5" },
446
+ { name = "intuit-oauth", specifier = ">=1.2.6" },
447
+ { name = "python-quickbooks", specifier = ">=0.9.10" },
310
448
  { name = "python-wialon", specifier = ">=1.2.4" },
311
449
  ]
312
450
 
@@ -353,6 +491,19 @@ wheels = [
353
491
  { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
354
492
  ]
355
493
 
494
+ [[package]]
495
+ name = "requests-oauthlib"
496
+ version = "2.0.0"
497
+ source = { registry = "https://pypi.org/simple" }
498
+ dependencies = [
499
+ { name = "oauthlib" },
500
+ { name = "requests" },
501
+ ]
502
+ sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 }
503
+ wheels = [
504
+ { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 },
505
+ ]
506
+
356
507
  [[package]]
357
508
  name = "s3transfer"
358
509
  version = "0.11.2"
@@ -365,6 +516,15 @@ wheels = [
365
516
  { url = "https://files.pythonhosted.org/packages/1b/ac/e7dc469e49048dc57f62e0c555d2ee3117fa30813d2a1a2962cce3a2a82a/s3transfer-0.11.2-py3-none-any.whl", hash = "sha256:be6ecb39fadd986ef1701097771f87e4d2f821f27f6071c872143884d2950fbc", size = 84151 },
366
517
  ]
367
518
 
519
+ [[package]]
520
+ name = "setuptools"
521
+ version = "75.8.0"
522
+ source = { registry = "https://pypi.org/simple" }
523
+ sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 }
524
+ wheels = [
525
+ { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 },
526
+ ]
527
+
368
528
  [[package]]
369
529
  name = "simplejson"
370
530
  version = "3.19.3"
@@ -1,72 +0,0 @@
1
- from authorizenet.apicontrollers import (
2
- createCustomerProfileController,
3
- deleteCustomerProfileController,
4
- getCustomerProfileController,
5
- updateCustomerProfileController,
6
- )
7
- from authorizenet.apicontractsv1 import (
8
- customerProfileType,
9
- createCustomerProfileRequest,
10
- deleteCustomerProfileRequest,
11
- getCustomerProfileRequest,
12
- updateCustomerProfileRequest,
13
- )
14
-
15
- from terminusgps.authorizenet.profiles.base import AuthorizenetProfileBase
16
-
17
-
18
- class CustomerProfile(AuthorizenetProfileBase):
19
- def create(self, **kwargs) -> int:
20
- if not kwargs.get("email"):
21
- raise ValueError("'email' is required for creation.")
22
-
23
- request = createCustomerProfileRequest(
24
- merchantAuthentication=self.merchantAuthentication,
25
- profile=customerProfileType(
26
- merchantCustomerId=self.merchantCustomerId,
27
- email=kwargs["email"],
28
- description=kwargs.get("desc", ""),
29
- ),
30
- )
31
- controller = createCustomerProfileController(request)
32
- response = self.execute_controller(controller)
33
- return int(response.customerProfileId)
34
-
35
- def update(self, merchant_id: str, email: str, desc: str = "") -> None:
36
- request = updateCustomerProfileRequest(
37
- merchantAuthentication=self.merchantAuthentication,
38
- profile=CustomerProfile(
39
- self.id, merchantCustomerId=merchant_id, email=email, description=desc
40
- ),
41
- )
42
- controller = updateCustomerProfileController(request)
43
- self.execute_controller(controller)
44
-
45
- def delete(self) -> None:
46
- request = deleteCustomerProfileRequest(
47
- merchantAuthentication=self.merchantAuthentication,
48
- customerProfileId=str(self.id),
49
- )
50
- controller = deleteCustomerProfileController(request)
51
- self.execute_controller(controller)
52
-
53
- @property
54
- def payment_profiles(self) -> list[dict]:
55
- request = getCustomerProfileRequest(
56
- merchantAuthentication=self.merchantAuthentication,
57
- customerProfileId=self.id,
58
- includeIssuerInfo="true",
59
- )
60
- controller = getCustomerProfileController(request)
61
- response = self.execute_controller(controller)
62
- return response
63
-
64
-
65
- def main() -> None:
66
- profile = CustomerProfile(merchant_id=55, id=522226003)
67
- profile.delete()
68
- return
69
-
70
-
71
- if __name__ == "__main__":
72
- main()
@@ -1,90 +0,0 @@
1
- from authorizenet.apicontractsv1 import (
2
- createCustomerPaymentProfileRequest,
3
- getCustomerPaymentProfileRequest,
4
- deleteCustomerPaymentProfileRequest,
5
- paymentProfile,
6
- validateCustomerPaymentProfileRequest,
7
- )
8
- from authorizenet.apicontrollers import (
9
- createCustomerPaymentProfileController,
10
- deleteCustomerPaymentProfileController,
11
- getCustomerPaymentProfileController,
12
- validateCustomerPaymentProfileController,
13
- )
14
- from terminusgps.authorizenet.profiles.base import AuthorizenetCustomerProfileBase
15
-
16
-
17
- class PaymentProfile(AuthorizenetCustomerProfileBase):
18
- def create(self, **kwargs) -> int:
19
- """
20
- Creates an Authorize.NET payment profile.
21
-
22
- :param billing_address: A billing address for the payment profile.
23
- :type billing_address: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
24
- :param payment: An Authorize.NET API payment object. Usually a credit card, but could be a bank account.
25
- :type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
26
- :param default: Whether or not the payment method should be marked as default. Default is :py:obj:`False`.
27
- :type default: :py:obj:`bool`
28
- :returns: The new payment profile id.
29
- :rtype: :py:obj:`int`
30
-
31
- """
32
- if not kwargs.get("billing_address"):
33
- raise ValueError("'billing_address' is required on creation.")
34
- if not kwargs.get("payment"):
35
- raise ValueError("'payment' is required on creation.")
36
-
37
- request = createCustomerPaymentProfileRequest(
38
- merchantAuthentication=self.merchantAuthentication,
39
- customerProfileId=self.customerProfileId,
40
- paymentProfile=paymentProfile(
41
- billTo=kwargs["billing_address"],
42
- payment=kwargs["payment"],
43
- defaultPaymentProfile=self.default,
44
- ),
45
- validationMode=self.validationMode,
46
- )
47
- controller = createCustomerPaymentProfileController(request)
48
- response = self.execute_controller(controller)
49
- return int(response.get("customerPaymentProfileId"))
50
-
51
- def delete(self) -> None:
52
- """Deletes the Authorize.NET payment profile."""
53
- request = deleteCustomerPaymentProfileRequest(
54
- merchantAuthentication=self.merchantAuthentication,
55
- customerProfileId=self.customerProfileId,
56
- customerPaymentProfileId=self.id,
57
- )
58
- controller = deleteCustomerPaymentProfileController(request)
59
- self.execute_controller(controller)
60
-
61
- def validate(self) -> None:
62
- """Validates the Authorize.NET payment profile."""
63
- request = validateCustomerPaymentProfileRequest(
64
- merchantAuthentication=self.merchantAuthentication,
65
- customerProfileId=self.customerProfileId,
66
- customerPaymentProfileId=self.id,
67
- validationMode=self.validationMode,
68
- )
69
- controller = validateCustomerPaymentProfileController(request)
70
- self.execute_controller(controller)
71
-
72
- def get_details(self, issuer_info: bool = False) -> dict:
73
- """
74
- Calls the Authorize.NET API and retrieves the payment profile.
75
-
76
- :param issuer_info: Whether or not Authorize.NET should return issuer info for the payment profile.
77
- :type issuer_info: :py:obj:`bool`
78
- :returns: The Authorize.NET API payment profile.
79
- :rtype: :py:obj:`~authorizenet.apicontractsv1.paymentProfile`
80
-
81
- """
82
- request = getCustomerPaymentProfileRequest(
83
- merchantAuthentication=self.merchantAuthentication,
84
- customerProfileId=self.customerProfileId,
85
- customerPaymentProfileId=self.id,
86
- includeIssuerInfo="true" if issuer_info else "false",
87
- )
88
- controller = getCustomerPaymentProfileController(request)
89
- response = self.execute_controller(controller)
90
- return response
@@ -1,55 +0,0 @@
1
- from django.utils import timezone
2
- from terminusgps.wialon.session import WialonSession
3
- from terminusgps.wialon.utils import get_hw_type_id, gen_wialon_password
4
- from terminusgps.wialon.items import WialonUser, WialonResource, WialonUnit
5
- from terminusgps.wialon import constants
6
-
7
-
8
- def registration_test() -> None:
9
- timestamp = f"{timezone.now():%y-%m-%d-%H:%M:%S}"
10
-
11
- with WialonSession() as session:
12
- print(f"Running test '{registration_test.__name__}' as '{session.username}'...")
13
- hw_type_id = get_hw_type_id(name="Test HW", session=session)
14
-
15
- super_user = WialonUser(
16
- creator_id=session.uid,
17
- name=f"super_user_{timestamp}",
18
- password=gen_wialon_password(length=32),
19
- session=session,
20
- )
21
- resource = WialonResource(
22
- creator_id=super_user.id, name=f"resource_{timestamp}", session=session
23
- )
24
- unit = WialonUnit(
25
- creator_id=session.uid,
26
- name=f"test_unit_{timestamp}",
27
- hw_type_id=hw_type_id,
28
- session=session,
29
- )
30
- super_user.grant_access(unit, access_mask=constants.ACCESSMASK_UNIT_MIGRATION)
31
- resource.create_account("terminusgps_ext_hist")
32
- resource.migrate_unit(unit)
33
- end_user = WialonUser(
34
- creator_id=super_user.id,
35
- name=f"end_user_{timestamp}",
36
- password=gen_wialon_password(length=32),
37
- session=session,
38
- )
39
- end_user.grant_access(unit, access_mask=constants.ACCESSMASK_UNIT_BASIC)
40
-
41
-
42
- def account_days_test() -> None:
43
- timestamp = f"{timezone.now():%y-%m-%d-%H:%M:%S}"
44
-
45
- with WialonSession() as session:
46
- print(f"Running test '{account_days_test.__name__}' as '{session.username}'...")
47
- account = WialonResource(id="28990259", session=session)
48
- account.set_settings_flags(0x20)
49
- account.set_minimum_days(0)
50
- account.add_days(30)
51
-
52
-
53
- if __name__ == "__main__":
54
- registration_test()
55
- account_days_test()