pybrightree 0.1.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.
Files changed (32) hide show
  1. pybrightree-0.1.0/PKG-INFO +34 -0
  2. pybrightree-0.1.0/README.md +24 -0
  3. pybrightree-0.1.0/pyproject.toml +26 -0
  4. pybrightree-0.1.0/setup.cfg +4 -0
  5. pybrightree-0.1.0/src/brightree/__init__.py +4 -0
  6. pybrightree-0.1.0/src/brightree/client.py +78 -0
  7. pybrightree-0.1.0/src/brightree/config.py +15 -0
  8. pybrightree-0.1.0/src/brightree/exceptions.py +47 -0
  9. pybrightree-0.1.0/src/brightree/helpers.py +36 -0
  10. pybrightree-0.1.0/src/brightree/services/__init__.py +29 -0
  11. pybrightree-0.1.0/src/brightree/services/base.py +94 -0
  12. pybrightree-0.1.0/src/brightree/services/custom_field.py +29 -0
  13. pybrightree-0.1.0/src/brightree/services/doctor.py +50 -0
  14. pybrightree-0.1.0/src/brightree/services/document.py +25 -0
  15. pybrightree-0.1.0/src/brightree/services/documentation.py +79 -0
  16. pybrightree-0.1.0/src/brightree/services/insurance.py +93 -0
  17. pybrightree-0.1.0/src/brightree/services/inventory.py +44 -0
  18. pybrightree-0.1.0/src/brightree/services/invoice.py +34 -0
  19. pybrightree-0.1.0/src/brightree/services/patient.py +79 -0
  20. pybrightree-0.1.0/src/brightree/services/pickup.py +40 -0
  21. pybrightree-0.1.0/src/brightree/services/pricing.py +32 -0
  22. pybrightree-0.1.0/src/brightree/services/reference.py +78 -0
  23. pybrightree-0.1.0/src/brightree/services/sales_order.py +100 -0
  24. pybrightree-0.1.0/src/brightree/services/security.py +32 -0
  25. pybrightree-0.1.0/src/brightree/soap.py +28 -0
  26. pybrightree-0.1.0/src/pybrightree.egg-info/PKG-INFO +34 -0
  27. pybrightree-0.1.0/src/pybrightree.egg-info/SOURCES.txt +30 -0
  28. pybrightree-0.1.0/src/pybrightree.egg-info/dependency_links.txt +1 -0
  29. pybrightree-0.1.0/src/pybrightree.egg-info/requires.txt +1 -0
  30. pybrightree-0.1.0/src/pybrightree.egg-info/top_level.txt +1 -0
  31. pybrightree-0.1.0/tests/test_client.py +78 -0
  32. pybrightree-0.1.0/tests/test_services.py +144 -0
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: pybrightree
3
+ Version: 0.1.0
4
+ Summary: Python SOAP wrapper for Brightree
5
+ Author: Nick
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: zeep>=4.3.1
10
+
11
+ # pybrightree
12
+
13
+ `pybrightree` is a Python SOAP wrapper for Brightree.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install -e .
19
+ ```
20
+
21
+ The package installs as `pybrightree` and is imported as `brightree`.
22
+
23
+ ## Usage
24
+
25
+ ```python
26
+ from brightree import Brightree
27
+
28
+ bt = Brightree("username", "password")
29
+ note = bt.Patient().PatientNoteFetchByKey(141508)
30
+ ```
31
+
32
+ The package preserves the PHP wrapper's service accessor and SOAP method names so existing examples map cleanly.
33
+
34
+ Examples are available in [examples/basic_usage.py](/Users/nicholascheek/code/Brightree-python/examples/basic_usage.py) and [examples/custom_config.py](/Users/nicholascheek/code/Brightree-python/examples/custom_config.py).
@@ -0,0 +1,24 @@
1
+ # pybrightree
2
+
3
+ `pybrightree` is a Python SOAP wrapper for Brightree.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install -e .
9
+ ```
10
+
11
+ The package installs as `pybrightree` and is imported as `brightree`.
12
+
13
+ ## Usage
14
+
15
+ ```python
16
+ from brightree import Brightree
17
+
18
+ bt = Brightree("username", "password")
19
+ note = bt.Patient().PatientNoteFetchByKey(141508)
20
+ ```
21
+
22
+ The package preserves the PHP wrapper's service accessor and SOAP method names so existing examples map cleanly.
23
+
24
+ Examples are available in [examples/basic_usage.py](/Users/nicholascheek/code/Brightree-python/examples/basic_usage.py) and [examples/custom_config.py](/Users/nicholascheek/code/Brightree-python/examples/custom_config.py).
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pybrightree"
7
+ version = "0.1.0"
8
+ description = "Python SOAP wrapper for Brightree"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Nick" }
14
+ ]
15
+ dependencies = [
16
+ "zeep>=4.3.1"
17
+ ]
18
+
19
+ [tool.setuptools]
20
+ package-dir = { "" = "src" }
21
+
22
+ [tool.setuptools.packages.find]
23
+ where = ["src"]
24
+
25
+ [tool.pytest.ini_options]
26
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .client import Brightree
2
+ from .exceptions import BrightreeException
3
+
4
+ __all__ = ["Brightree", "BrightreeException"]
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from .config import DEFAULT_SERVICES
4
+ from .helpers import ArrayHelper
5
+ from .services import (
6
+ CustomField,
7
+ Doctor,
8
+ Document,
9
+ Documentation,
10
+ Insurance,
11
+ Inventory,
12
+ Invoice,
13
+ Patient,
14
+ Pickup,
15
+ Pricing,
16
+ Reference,
17
+ SalesOrder,
18
+ Security,
19
+ )
20
+
21
+
22
+ class Brightree(ArrayHelper):
23
+ def __init__(self, username: str, password: str, config: dict | None = None, client_factory=None) -> None:
24
+ super().__init__()
25
+ merged_config = {"service": dict(DEFAULT_SERVICES)}
26
+ if config:
27
+ merged_config.update({key: value for key, value in config.items() if key != "service"})
28
+ if "service" in config:
29
+ merged_config["service"].update(config["service"])
30
+
31
+ self.info = {
32
+ "username": username,
33
+ "password": password,
34
+ "config": merged_config,
35
+ }
36
+ self._client_factory = client_factory
37
+
38
+ def _service(self, cls):
39
+ return cls(self.info, client_factory=self._client_factory)
40
+
41
+ def Patient(self):
42
+ return self._service(Patient)
43
+
44
+ def Doctor(self):
45
+ return self._service(Doctor)
46
+
47
+ def Document(self):
48
+ return self._service(Document)
49
+
50
+ def Documentation(self):
51
+ return self._service(Documentation)
52
+
53
+ def CustomField(self):
54
+ return self._service(CustomField)
55
+
56
+ def Insurance(self):
57
+ return self._service(Insurance)
58
+
59
+ def Inventory(self):
60
+ return self._service(Inventory)
61
+
62
+ def Pickup(self):
63
+ return self._service(Pickup)
64
+
65
+ def Reference(self):
66
+ return self._service(Reference)
67
+
68
+ def SalesOrder(self):
69
+ return self._service(SalesOrder)
70
+
71
+ def Pricing(self):
72
+ return self._service(Pricing)
73
+
74
+ def Security(self):
75
+ return self._service(Security)
76
+
77
+ def Invoice(self):
78
+ return self._service(Invoice)
@@ -0,0 +1,15 @@
1
+ DEFAULT_SERVICES = {
2
+ "document": "https://webservices.brightree.net/v0100-2108/DocumentationService/DocumentManagementService.svc",
3
+ "patient": "https://webservices.brightree.net/v0100-2101/OrderEntryService/patientservice.svc",
4
+ "documentation": "https://webservices.brightree.net/v0100-2004/DocumentationService/DocumentationService.svc",
5
+ "custom": "https://webservices.brightree.net/v0100-1610/CustomFieldService/CustomFieldService.svc",
6
+ "insurance": "https://webservices.brightree.net/v0100-2310/OrderEntryService/InsuranceService.svc",
7
+ "reference": "https://webservices.brightree.net/v0100-2049/ReferenceDataService/ReferenceDataService.svc",
8
+ "doctor": "https://webservices.brightree.net/v0100-2406/DoctorService/DoctorService.svc",
9
+ "inventory": "https://webservices.brightree.net/v0100-2309/InventoryService/InventoryService.svc",
10
+ "pickup": "https://webservices.brightree.net/v0100-2007/OrderEntryService/PickupExchangeService.svc",
11
+ "salesorder": "https://webservices.brightree.net/v0100-2408/OrderEntryService/SalesOrderService.svc",
12
+ "invoice": "https://webservices.brightree.net/v0100-2409/InvoiceService/InvoiceService.svc",
13
+ "security": "https://webservices.brightree.net/v0100-2407/SecurityService/UserSecurityService.svc",
14
+ "pricing": "https://webservices.brightree.net/v0100-1908/InventoryService/PricingService.svc",
15
+ }
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class BrightreeException(Exception):
5
+ def __init__(
6
+ self,
7
+ message: str,
8
+ code: int = 0,
9
+ *,
10
+ error_data=None,
11
+ request_data=None,
12
+ previous: Exception | None = None,
13
+ ) -> None:
14
+ super().__init__(message)
15
+ self.code = code
16
+ self.error_data = error_data
17
+ self.request_data = request_data
18
+ self.previous = previous
19
+
20
+ @classmethod
21
+ def from_soap_fault(cls, fault: Exception, request_data=None) -> "BrightreeException":
22
+ message = getattr(fault, "message", str(fault))
23
+ code = getattr(fault, "code", 0) or 0
24
+ detail = getattr(fault, "detail", None)
25
+ return cls(message, code, error_data=detail, request_data=request_data, previous=fault)
26
+
27
+ @classmethod
28
+ def from_api_response(cls, response, request_data=None) -> "BrightreeException":
29
+ message = "API Error"
30
+ code = 0
31
+ error = getattr(response, "Error", None)
32
+ if error is not None:
33
+ message = getattr(error, "Message", message)
34
+ code = getattr(error, "Code", code) or 0
35
+ return cls(message, code, error_data=response, request_data=request_data)
36
+
37
+ @classmethod
38
+ def config_error(cls, message: str) -> "BrightreeException":
39
+ return cls(f"Configuration Error: {message}", 1000)
40
+
41
+ @classmethod
42
+ def auth_error(cls, message: str = "Authentication failed") -> "BrightreeException":
43
+ return cls(f"Authentication Error: {message}", 1001)
44
+
45
+ @classmethod
46
+ def param_error(cls, method: str, param: str) -> "BrightreeException":
47
+ return cls(f"Parameter Error: Required parameter '{param}' missing for method '{method}'", 1002)
@@ -0,0 +1,36 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class ArrayHelper:
5
+ def __init__(self) -> None:
6
+ self._search = {}
7
+ self._sort = {}
8
+ self._pages = {"page": 1}
9
+ self._page_size = {"pageSize": 10}
10
+
11
+ def search(self, search: dict) -> "ArrayHelper":
12
+ self._search = search
13
+ return self
14
+
15
+ def sort(self, sort: dict | None = None) -> "ArrayHelper":
16
+ self._sort = sort or {"SortParams": []}
17
+ return self
18
+
19
+ def pageSize(self, page_size: int = 10) -> "ArrayHelper":
20
+ self._page_size = {"pageSize": page_size}
21
+ return self
22
+
23
+ def page_size(self, page_size: int = 10) -> "ArrayHelper":
24
+ return self.pageSize(page_size)
25
+
26
+ def pages(self, pages: int = 1) -> "ArrayHelper":
27
+ self._pages = {"page": pages}
28
+ return self
29
+
30
+ def page(self, page_number: int = 1) -> "ArrayHelper":
31
+ return self.pages(page_number)
32
+
33
+ def build(self) -> dict:
34
+ if not self._sort:
35
+ self._sort = {"SortParams": []}
36
+ return {**self._search, **self._sort, **self._pages, **self._page_size}
@@ -0,0 +1,29 @@
1
+ from .custom_field import CustomField
2
+ from .doctor import Doctor
3
+ from .document import Document
4
+ from .documentation import Documentation
5
+ from .insurance import Insurance
6
+ from .inventory import Inventory
7
+ from .invoice import Invoice
8
+ from .patient import Patient
9
+ from .pickup import Pickup
10
+ from .pricing import Pricing
11
+ from .reference import Reference
12
+ from .sales_order import SalesOrder
13
+ from .security import Security
14
+
15
+ __all__ = [
16
+ "CustomField",
17
+ "Doctor",
18
+ "Document",
19
+ "Documentation",
20
+ "Insurance",
21
+ "Inventory",
22
+ "Invoice",
23
+ "Patient",
24
+ "Pickup",
25
+ "Pricing",
26
+ "Reference",
27
+ "SalesOrder",
28
+ "Security",
29
+ ]
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable, Mapping
4
+ from typing import Any
5
+
6
+ from ..config import DEFAULT_SERVICES
7
+ from ..exceptions import BrightreeException
8
+ from ..soap import ZeepSoapClient
9
+
10
+
11
+ class BaseService:
12
+ service_name: str | None = None
13
+ methods: dict[str, bool] = {}
14
+ special_methods: dict[str, tuple[str, ...]] = {}
15
+
16
+ def __init__(self, info: dict[str, Any], client_factory=None) -> None:
17
+ self.info = info
18
+ self._client_factory = client_factory or ZeepSoapClient
19
+
20
+ service_name = self.get_service_name()
21
+ services = info.get("config", {}).get("service", {})
22
+ location = services.get(service_name)
23
+ if not location:
24
+ raise BrightreeException.config_error(f"{service_name.capitalize()} service URL not configured")
25
+
26
+ username = info.get("username", "")
27
+ password = info.get("password", "")
28
+ if not username or not password:
29
+ raise BrightreeException.auth_error("Authentication credentials not provided")
30
+
31
+ self.wsdl = f"{location}?singleWsdl"
32
+ self.location = location
33
+ self.username = username
34
+ self.password = password
35
+
36
+ def get_service_name(self) -> str:
37
+ if self.service_name:
38
+ return self.service_name
39
+ return self.__class__.__name__.lower()
40
+
41
+ def _create_client(self):
42
+ return self._client_factory(
43
+ wsdl=self.wsdl,
44
+ username=self.username,
45
+ password=self.password,
46
+ location=self.location,
47
+ )
48
+
49
+ def _api_call(self, method: str, params: dict[str, Any]) -> Any:
50
+ try:
51
+ client = self._create_client()
52
+ return client.call(method, params)
53
+ except BrightreeException:
54
+ raise
55
+ except Exception as exc:
56
+ raise BrightreeException.from_soap_fault(exc, {"method": method, "params": params}) from exc
57
+
58
+ @staticmethod
59
+ def _is_iterable(value: Any) -> bool:
60
+ return isinstance(value, Mapping) or (
61
+ isinstance(value, Iterable) and not isinstance(value, (str, bytes, bytearray))
62
+ )
63
+
64
+ @staticmethod
65
+ def _ensure_iterable(method: str, query: Any) -> None:
66
+ if not BaseService._is_iterable(query):
67
+ raise BrightreeException(f"Method {method} requires an iterable parameter", 1002)
68
+
69
+ def _call_with_query(self, method: str, query: Any) -> Any:
70
+ self._ensure_iterable(method, query)
71
+ return self._api_call(method, dict(query))
72
+
73
+ def _call_with_special(self, method: str, param_names: tuple[str, ...], *args: Any) -> Any:
74
+ params = {}
75
+ for index, param_name in enumerate(param_names):
76
+ if index >= len(args):
77
+ raise BrightreeException.param_error(method, param_name)
78
+ params[param_name] = args[index]
79
+ return self._api_call(method, params)
80
+
81
+ def __getattr__(self, name: str):
82
+ if name in self.methods:
83
+ if self.methods[name]:
84
+ return lambda query=None, _name=name: self._call_with_query(_name, query)
85
+ return lambda _name=name: self._api_call(_name, {})
86
+
87
+ if name in self.special_methods:
88
+ return lambda *args, _name=name: self._call_with_special(_name, self.special_methods[_name], *args)
89
+
90
+ raise AttributeError(name)
91
+
92
+
93
+ def default_config() -> dict[str, dict[str, str]]:
94
+ return {"service": dict(DEFAULT_SERVICES)}
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from .base import BaseService
4
+
5
+
6
+ class CustomField(BaseService):
7
+ service_name = "custom"
8
+ methods = {
9
+ "CustomFieldValueSaveMultiple": True,
10
+ }
11
+ special_methods = {
12
+ "CustomFieldFetchAllByCategory": ("category", "includeInactive"),
13
+ "CustomFieldValueFetchAllByBrightreeID": ("brightreeID", "category"),
14
+ }
15
+
16
+ def CustomFieldFetchAllByCategory(self, category: str, includeInactive: int = 0):
17
+ return self._api_call(
18
+ "CustomFieldFetchAllByCategory",
19
+ {"category": category, "includeInactive": includeInactive},
20
+ )
21
+
22
+ def CustomFieldValueFetchAllByBrightreeID(self, brightree_id: int, category: str):
23
+ return self._api_call(
24
+ "CustomFieldValueFetchAllByBrightreeID",
25
+ {"brightreeID": brightree_id, "category": category},
26
+ )
27
+
28
+ def CustomFieldValueSaveMultiple(self, query):
29
+ return self._call_with_query("CustomFieldValueSaveMultiple", query)
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from .base import BaseService
4
+
5
+
6
+ class Doctor(BaseService):
7
+ methods = {
8
+ "DoctorCreate": True,
9
+ "DoctorNoteCreate": True,
10
+ "DoctorNoteUpdate": True,
11
+ "DoctorSearch": True,
12
+ "DoctorUpdate": True,
13
+ "DoctorGroupFetchAll": False,
14
+ "FacilityFetchAll": False,
15
+ "FacilityGroupFetchAll": False,
16
+ "MarketingRepFetchAll": False,
17
+ }
18
+ special_methods = {
19
+ "AddDoctorReferralContact": ("DoctorBrightreeID", "ReferralContactBrightreeID"),
20
+ "DoctorFetchByBrightreeID": ("BrightreeID",),
21
+ "DoctorFetchByExternalID": ("ExternalID",),
22
+ "DoctorNoteFetchByKey": ("brightreeID",),
23
+ "DoctorNoteFetchByDoctor": ("brightreeID",),
24
+ "DoctorReferralContactsFetchByDoctorKey": ("DoctorBrightreeID",),
25
+ "RemoveDoctorReferralContact": ("DoctorBrightreeID", "ReferralContactBrightreeID"),
26
+ }
27
+
28
+ def AddDoctorReferralContact(self, doctor_brightree_id: int, referral_contact_brightree_id: int):
29
+ return self._api_call(
30
+ "AddDoctorReferralContact",
31
+ {
32
+ "DoctorBrightreeID": doctor_brightree_id,
33
+ "ReferralContactBrightreeID": referral_contact_brightree_id,
34
+ },
35
+ )
36
+
37
+ def DoctorCreate(self, query):
38
+ return self._call_with_query("DoctorCreate", query)
39
+
40
+ def DoctorFetchByBrightreeID(self, brightree_id: int):
41
+ return self._api_call("DoctorFetchByBrightreeID", {"BrightreeID": brightree_id})
42
+
43
+ def RemoveDoctorReferralContact(self, doctor_brightree_id: int, referral_contact_brightree_id: int):
44
+ return self._api_call(
45
+ "RemoveDoctorReferralContact",
46
+ {
47
+ "DoctorBrightreeID": doctor_brightree_id,
48
+ "ReferralContactBrightreeID": referral_contact_brightree_id,
49
+ },
50
+ )
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from ..exceptions import BrightreeException
4
+ from .base import BaseService
5
+
6
+
7
+ class Document(BaseService):
8
+ methods = {
9
+ "DocumentTypesFetchAll": False,
10
+ "DocumentBatchCreate": True,
11
+ "DocumentBatchSearch": True,
12
+ "DocumentPropertyUpdate": True,
13
+ "DocumentReviewUpdate": True,
14
+ "DocumentSearch": True,
15
+ "GenerateDocumentID": True,
16
+ "StoreDocument": True,
17
+ }
18
+ special_methods = {
19
+ "FetchDocumentContent": ("documentKey",),
20
+ }
21
+
22
+ def FetchDocumentContent(self, key: int = 12345):
23
+ if key <= 0:
24
+ raise BrightreeException(f"Invalid document key: {key}", 1003)
25
+ return self._api_call("FetchDocumentContent", {"documentKey": key})
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from ..exceptions import BrightreeException
4
+ from .base import BaseService
5
+
6
+
7
+ class Documentation(BaseService):
8
+ methods = {
9
+ "CMNCreateFromPatient": True,
10
+ "CMNDetailCreate": True,
11
+ "CMNDetailDelete": True,
12
+ "CMNDetailUpdate": True,
13
+ "CMNFetchByBrightreeID": True,
14
+ "CMNFetchByExternalID": True,
15
+ "CMNFetchByPatientBrightreeID": True,
16
+ "CMNFetchBySalesOrderBrightreeID": True,
17
+ "CMNFrequencyUpdate": True,
18
+ "CMNLog": True,
19
+ "CMNPreview": True,
20
+ "CMNPrint": True,
21
+ "CMNQuestionAnswerConfiguration": True,
22
+ "CMNReasonFetchAll": True,
23
+ "CMNRenew": True,
24
+ "CMNRevise": True,
25
+ "CMNSearch": True,
26
+ "CMNTaskCreate": True,
27
+ "CMNTaskUpdate": True,
28
+ "CMNUpdate": True,
29
+ "PARAddPurchaseLimit": True,
30
+ "PARCreateFromPatient": True,
31
+ "PARDelete": True,
32
+ "PARFetchByBrightreeID": True,
33
+ "PARFetchByExternalID": True,
34
+ "PARFetchByPatientBrightreeID": True,
35
+ "PARFetchBySalesOrderBrightreeID": True,
36
+ "PARFetchBySalesOrderTemplateBrightreeID": True,
37
+ "PARLog": True,
38
+ "PARRenew": True,
39
+ "PARSearch": True,
40
+ "PARTaskCreate": True,
41
+ "PARTaskUpdate": True,
42
+ "PARUpdate": True,
43
+ "PARUpdatePurchaseLimit": True,
44
+ "SalesOrderItemLinkCMN": True,
45
+ "SalesOrderItemLinkNewCMN": True,
46
+ "SalesOrderItemLinkToNewPAR": True,
47
+ "SalesOrderItemLinkToPAR": True,
48
+ "SalesOrderItemsLinkCMN": True,
49
+ "SalesOrderItemsLinkNewCMN": True,
50
+ "SalesOrderItemsLinkToNewPAR": True,
51
+ "SalesOrderItemsLinkToPAR": True,
52
+ "SalesOrderItemsUnlinkCMN": True,
53
+ "SalesOrderItemsUnlinkPAR": True,
54
+ "SalesOrderItemUnlinkCMN": True,
55
+ "SalesOrderItemUnlinkPAR": True,
56
+ "SalesOrderTemplateItemLinkToPAR": True,
57
+ "SalesOrderTemplateItemsLinkToPAR": True,
58
+ "SalesOrderTemplateItemsUnlinkPAR": True,
59
+ "SalesOrderTemplateItemUnlinkPAR": True,
60
+ "SetParticipantComplianceDate": True,
61
+ "PARTaskReasonFetchAll": False,
62
+ }
63
+ special_methods = {
64
+ "CMNFrequencyFetchbyBrightreeID": ("BrightreeID",),
65
+ "PARTaskFetchByPARBrightreeID": ("BrightreeID",),
66
+ }
67
+
68
+ def CMNFrequencyFetchbyBrightreeID(self, brightree_id: int):
69
+ if brightree_id <= 0:
70
+ raise BrightreeException(f"Invalid Brightree ID: {brightree_id}", 1003)
71
+ return self._api_call("CMNFrequencyFetchbyBrightreeID", {"BrightreeID": brightree_id})
72
+
73
+ def PARTaskFetchByPARBrightreeID(self, par_brightree_id: int):
74
+ if par_brightree_id <= 0:
75
+ raise BrightreeException(f"Invalid PAR Brightree ID: {par_brightree_id}", 1003)
76
+ return self._api_call("PARTaskFetchByPARBrightreeID", {"BrightreeID": par_brightree_id})
77
+
78
+ def PARTaskReasonFetchAll(self):
79
+ return self._api_call("PARTaskReasonFetchAll", {})