bb-integrations-library 3.0.11__py3-none-any.whl
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.
- bb_integrations_lib/__init__.py +0 -0
- bb_integrations_lib/converters/__init__.py +0 -0
- bb_integrations_lib/gravitate/__init__.py +0 -0
- bb_integrations_lib/gravitate/base_api.py +20 -0
- bb_integrations_lib/gravitate/model.py +29 -0
- bb_integrations_lib/gravitate/pe_api.py +122 -0
- bb_integrations_lib/gravitate/rita_api.py +552 -0
- bb_integrations_lib/gravitate/sd_api.py +572 -0
- bb_integrations_lib/gravitate/testing/TTE/sd/models.py +1398 -0
- bb_integrations_lib/gravitate/testing/TTE/sd/tests/test_models.py +2987 -0
- bb_integrations_lib/gravitate/testing/__init__.py +0 -0
- bb_integrations_lib/gravitate/testing/builder.py +55 -0
- bb_integrations_lib/gravitate/testing/openapi.py +70 -0
- bb_integrations_lib/gravitate/testing/util.py +274 -0
- bb_integrations_lib/mappers/__init__.py +0 -0
- bb_integrations_lib/mappers/prices/__init__.py +0 -0
- bb_integrations_lib/mappers/prices/model.py +106 -0
- bb_integrations_lib/mappers/prices/price_mapper.py +127 -0
- bb_integrations_lib/mappers/prices/protocol.py +20 -0
- bb_integrations_lib/mappers/prices/util.py +61 -0
- bb_integrations_lib/mappers/rita_mapper.py +523 -0
- bb_integrations_lib/models/__init__.py +0 -0
- bb_integrations_lib/models/dtn_supplier_invoice.py +487 -0
- bb_integrations_lib/models/enums.py +28 -0
- bb_integrations_lib/models/pipeline_structs.py +76 -0
- bb_integrations_lib/models/probe/probe_event.py +20 -0
- bb_integrations_lib/models/probe/request_data.py +431 -0
- bb_integrations_lib/models/probe/resume_token.py +7 -0
- bb_integrations_lib/models/rita/audit.py +113 -0
- bb_integrations_lib/models/rita/auth.py +30 -0
- bb_integrations_lib/models/rita/bucket.py +17 -0
- bb_integrations_lib/models/rita/config.py +188 -0
- bb_integrations_lib/models/rita/constants.py +19 -0
- bb_integrations_lib/models/rita/crossroads_entities.py +293 -0
- bb_integrations_lib/models/rita/crossroads_mapping.py +428 -0
- bb_integrations_lib/models/rita/crossroads_monitoring.py +78 -0
- bb_integrations_lib/models/rita/crossroads_network.py +41 -0
- bb_integrations_lib/models/rita/crossroads_rules.py +80 -0
- bb_integrations_lib/models/rita/email.py +39 -0
- bb_integrations_lib/models/rita/issue.py +63 -0
- bb_integrations_lib/models/rita/mapping.py +227 -0
- bb_integrations_lib/models/rita/probe.py +58 -0
- bb_integrations_lib/models/rita/reference_data.py +110 -0
- bb_integrations_lib/models/rita/source_system.py +9 -0
- bb_integrations_lib/models/rita/workers.py +76 -0
- bb_integrations_lib/models/sd/bols_and_drops.py +241 -0
- bb_integrations_lib/models/sd/get_order.py +301 -0
- bb_integrations_lib/models/sd/orders.py +18 -0
- bb_integrations_lib/models/sd_api.py +115 -0
- bb_integrations_lib/pipelines/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/distribution_report/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/distribution_report/order_by_site_product_parser.py +50 -0
- bb_integrations_lib/pipelines/parsers/distribution_report/tank_configs_parser.py +47 -0
- bb_integrations_lib/pipelines/parsers/dtn/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/dtn/dtn_price_parser.py +102 -0
- bb_integrations_lib/pipelines/parsers/dtn/model.py +79 -0
- bb_integrations_lib/pipelines/parsers/price_engine/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/price_engine/parse_accessorials_prices_parser.py +67 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/__init__.py +0 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/price_merge_parser.py +111 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/price_sync_parser.py +107 -0
- bb_integrations_lib/pipelines/parsers/price_engine/price_file_upload/shared.py +81 -0
- bb_integrations_lib/pipelines/parsers/tank_reading_parser.py +155 -0
- bb_integrations_lib/pipelines/parsers/tank_sales_parser.py +144 -0
- bb_integrations_lib/pipelines/shared/__init__.py +0 -0
- bb_integrations_lib/pipelines/shared/allocation_matching.py +227 -0
- bb_integrations_lib/pipelines/shared/bol_allocation.py +2793 -0
- bb_integrations_lib/pipelines/steps/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/create_accessorials_step.py +80 -0
- bb_integrations_lib/pipelines/steps/distribution_report/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/distribution_report/distribution_report_datafram_to_raw_data.py +33 -0
- bb_integrations_lib/pipelines/steps/distribution_report/get_model_history_step.py +50 -0
- bb_integrations_lib/pipelines/steps/distribution_report/get_order_by_site_product_step.py +62 -0
- bb_integrations_lib/pipelines/steps/distribution_report/get_tank_configs_step.py +40 -0
- bb_integrations_lib/pipelines/steps/distribution_report/join_distribution_order_dos_step.py +85 -0
- bb_integrations_lib/pipelines/steps/distribution_report/upload_distribution_report_datafram_to_big_query.py +47 -0
- bb_integrations_lib/pipelines/steps/echo_step.py +14 -0
- bb_integrations_lib/pipelines/steps/export_dataframe_to_rawdata_step.py +28 -0
- bb_integrations_lib/pipelines/steps/exporting/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/exporting/bbd_export_payroll_file_step.py +107 -0
- bb_integrations_lib/pipelines/steps/exporting/bbd_export_readings_step.py +236 -0
- bb_integrations_lib/pipelines/steps/exporting/cargas_wholesale_bundle_upload_step.py +33 -0
- bb_integrations_lib/pipelines/steps/exporting/dataframe_flat_file_export.py +29 -0
- bb_integrations_lib/pipelines/steps/exporting/gcs_bucket_export_file_step.py +34 -0
- bb_integrations_lib/pipelines/steps/exporting/keyvu_export_step.py +356 -0
- bb_integrations_lib/pipelines/steps/exporting/pe_price_export_step.py +238 -0
- bb_integrations_lib/pipelines/steps/exporting/platform_science_order_sync_step.py +500 -0
- bb_integrations_lib/pipelines/steps/exporting/save_rawdata_to_disk.py +15 -0
- bb_integrations_lib/pipelines/steps/exporting/sftp_export_file_step.py +60 -0
- bb_integrations_lib/pipelines/steps/exporting/sftp_export_many_files_step.py +23 -0
- bb_integrations_lib/pipelines/steps/exporting/update_exported_orders_table_step.py +64 -0
- bb_integrations_lib/pipelines/steps/filter_step.py +22 -0
- bb_integrations_lib/pipelines/steps/get_latest_sync_date.py +34 -0
- bb_integrations_lib/pipelines/steps/importing/bbd_import_payroll_step.py +30 -0
- bb_integrations_lib/pipelines/steps/importing/get_order_numbers_to_export_step.py +138 -0
- bb_integrations_lib/pipelines/steps/importing/load_file_to_dataframe_step.py +46 -0
- bb_integrations_lib/pipelines/steps/importing/load_imap_attachment_step.py +172 -0
- bb_integrations_lib/pipelines/steps/importing/pe_bulk_sync_price_structure_step.py +68 -0
- bb_integrations_lib/pipelines/steps/importing/pe_price_merge_step.py +86 -0
- bb_integrations_lib/pipelines/steps/importing/sftp_file_config_step.py +124 -0
- bb_integrations_lib/pipelines/steps/importing/test_exact_file_match.py +57 -0
- bb_integrations_lib/pipelines/steps/null_step.py +15 -0
- bb_integrations_lib/pipelines/steps/pe_integration_job_step.py +32 -0
- bb_integrations_lib/pipelines/steps/processing/__init__.py +0 -0
- bb_integrations_lib/pipelines/steps/processing/archive_gcs_step.py +76 -0
- bb_integrations_lib/pipelines/steps/processing/archive_sftp_step.py +48 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_format_tank_readings_step.py +492 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_upload_prices_step.py +54 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_upload_tank_sales_step.py +124 -0
- bb_integrations_lib/pipelines/steps/processing/bbd_upload_tankreading_step.py +80 -0
- bb_integrations_lib/pipelines/steps/processing/convert_bbd_order_to_cargas_step.py +226 -0
- bb_integrations_lib/pipelines/steps/processing/delete_sftp_step.py +33 -0
- bb_integrations_lib/pipelines/steps/processing/dtn/__init__.py +2 -0
- bb_integrations_lib/pipelines/steps/processing/dtn/convert_dtn_invoice_to_sd_model.py +145 -0
- bb_integrations_lib/pipelines/steps/processing/dtn/parse_dtn_invoice_step.py +38 -0
- bb_integrations_lib/pipelines/steps/processing/file_config_parser_step.py +720 -0
- bb_integrations_lib/pipelines/steps/processing/file_config_parser_step_v2.py +418 -0
- bb_integrations_lib/pipelines/steps/processing/get_sd_price_price_request.py +105 -0
- bb_integrations_lib/pipelines/steps/processing/keyvu_upload_deliveryplan_step.py +39 -0
- bb_integrations_lib/pipelines/steps/processing/mark_orders_exported_in_bbd_step.py +185 -0
- bb_integrations_lib/pipelines/steps/processing/pe_price_rows_processing_step.py +174 -0
- bb_integrations_lib/pipelines/steps/processing/send_process_report_step.py +47 -0
- bb_integrations_lib/pipelines/steps/processing/sftp_renamer_step.py +61 -0
- bb_integrations_lib/pipelines/steps/processing/tank_reading_touchup_steps.py +75 -0
- bb_integrations_lib/pipelines/steps/processing/upload_supplier_invoice_step.py +16 -0
- bb_integrations_lib/pipelines/steps/send_attached_in_rita_email_step.py +44 -0
- bb_integrations_lib/pipelines/steps/send_rita_email_step.py +34 -0
- bb_integrations_lib/pipelines/steps/sleep_step.py +24 -0
- bb_integrations_lib/pipelines/wrappers/__init__.py +0 -0
- bb_integrations_lib/pipelines/wrappers/accessorials_transformation.py +104 -0
- bb_integrations_lib/pipelines/wrappers/distribution_report.py +191 -0
- bb_integrations_lib/pipelines/wrappers/export_tank_readings.py +237 -0
- bb_integrations_lib/pipelines/wrappers/import_tank_readings.py +192 -0
- bb_integrations_lib/pipelines/wrappers/wrapper.py +81 -0
- bb_integrations_lib/protocols/__init__.py +0 -0
- bb_integrations_lib/protocols/flat_file.py +210 -0
- bb_integrations_lib/protocols/gravitate_client.py +104 -0
- bb_integrations_lib/protocols/pipelines.py +697 -0
- bb_integrations_lib/provider/__init__.py +0 -0
- bb_integrations_lib/provider/api/__init__.py +0 -0
- bb_integrations_lib/provider/api/cargas/__init__.py +0 -0
- bb_integrations_lib/provider/api/cargas/client.py +43 -0
- bb_integrations_lib/provider/api/cargas/model.py +49 -0
- bb_integrations_lib/provider/api/cargas/protocol.py +23 -0
- bb_integrations_lib/provider/api/dtn/__init__.py +0 -0
- bb_integrations_lib/provider/api/dtn/client.py +128 -0
- bb_integrations_lib/provider/api/dtn/protocol.py +9 -0
- bb_integrations_lib/provider/api/keyvu/__init__.py +0 -0
- bb_integrations_lib/provider/api/keyvu/client.py +30 -0
- bb_integrations_lib/provider/api/keyvu/model.py +149 -0
- bb_integrations_lib/provider/api/macropoint/__init__.py +0 -0
- bb_integrations_lib/provider/api/macropoint/client.py +28 -0
- bb_integrations_lib/provider/api/macropoint/model.py +40 -0
- bb_integrations_lib/provider/api/pc_miler/__init__.py +0 -0
- bb_integrations_lib/provider/api/pc_miler/client.py +130 -0
- bb_integrations_lib/provider/api/pc_miler/model.py +6 -0
- bb_integrations_lib/provider/api/pc_miler/web_services_apis.py +131 -0
- bb_integrations_lib/provider/api/platform_science/__init__.py +0 -0
- bb_integrations_lib/provider/api/platform_science/client.py +147 -0
- bb_integrations_lib/provider/api/platform_science/model.py +82 -0
- bb_integrations_lib/provider/api/quicktrip/__init__.py +0 -0
- bb_integrations_lib/provider/api/quicktrip/client.py +52 -0
- bb_integrations_lib/provider/api/telapoint/__init__.py +0 -0
- bb_integrations_lib/provider/api/telapoint/client.py +68 -0
- bb_integrations_lib/provider/api/telapoint/model.py +178 -0
- bb_integrations_lib/provider/api/warren_rogers/__init__.py +0 -0
- bb_integrations_lib/provider/api/warren_rogers/client.py +207 -0
- bb_integrations_lib/provider/aws/__init__.py +0 -0
- bb_integrations_lib/provider/aws/s3/__init__.py +0 -0
- bb_integrations_lib/provider/aws/s3/client.py +126 -0
- bb_integrations_lib/provider/ftp/__init__.py +0 -0
- bb_integrations_lib/provider/ftp/client.py +140 -0
- bb_integrations_lib/provider/ftp/interface.py +273 -0
- bb_integrations_lib/provider/ftp/model.py +76 -0
- bb_integrations_lib/provider/imap/__init__.py +0 -0
- bb_integrations_lib/provider/imap/client.py +228 -0
- bb_integrations_lib/provider/imap/model.py +3 -0
- bb_integrations_lib/provider/sqlserver/__init__.py +0 -0
- bb_integrations_lib/provider/sqlserver/client.py +106 -0
- bb_integrations_lib/secrets/__init__.py +4 -0
- bb_integrations_lib/secrets/adapters.py +98 -0
- bb_integrations_lib/secrets/credential_models.py +222 -0
- bb_integrations_lib/secrets/factory.py +85 -0
- bb_integrations_lib/secrets/providers.py +160 -0
- bb_integrations_lib/shared/__init__.py +0 -0
- bb_integrations_lib/shared/exceptions.py +25 -0
- bb_integrations_lib/shared/model.py +1039 -0
- bb_integrations_lib/shared/shared_enums.py +510 -0
- bb_integrations_lib/storage/README.md +236 -0
- bb_integrations_lib/storage/__init__.py +0 -0
- bb_integrations_lib/storage/aws/__init__.py +0 -0
- bb_integrations_lib/storage/aws/s3.py +8 -0
- bb_integrations_lib/storage/defaults.py +72 -0
- bb_integrations_lib/storage/gcs/__init__.py +0 -0
- bb_integrations_lib/storage/gcs/client.py +8 -0
- bb_integrations_lib/storage/gcsmanager/__init__.py +0 -0
- bb_integrations_lib/storage/gcsmanager/client.py +8 -0
- bb_integrations_lib/storage/setup.py +29 -0
- bb_integrations_lib/util/__init__.py +0 -0
- bb_integrations_lib/util/cache/__init__.py +0 -0
- bb_integrations_lib/util/cache/custom_ttl_cache.py +75 -0
- bb_integrations_lib/util/cache/protocol.py +9 -0
- bb_integrations_lib/util/config/__init__.py +0 -0
- bb_integrations_lib/util/config/manager.py +391 -0
- bb_integrations_lib/util/config/model.py +41 -0
- bb_integrations_lib/util/exception_logger/__init__.py +0 -0
- bb_integrations_lib/util/exception_logger/exception_logger.py +146 -0
- bb_integrations_lib/util/exception_logger/test.py +114 -0
- bb_integrations_lib/util/utils.py +364 -0
- bb_integrations_lib/workers/__init__.py +0 -0
- bb_integrations_lib/workers/groups.py +13 -0
- bb_integrations_lib/workers/rpc_worker.py +50 -0
- bb_integrations_lib/workers/topics.py +20 -0
- bb_integrations_library-3.0.11.dist-info/METADATA +59 -0
- bb_integrations_library-3.0.11.dist-info/RECORD +217 -0
- bb_integrations_library-3.0.11.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from datetime import datetime, UTC, timedelta
|
|
2
|
+
from typing import Dict
|
|
3
|
+
from dateutil.parser import parse
|
|
4
|
+
|
|
5
|
+
import loguru
|
|
6
|
+
|
|
7
|
+
from bb_integrations_lib.provider.api.pc_miler.client import PCMilerClient
|
|
8
|
+
from bb_integrations_lib.provider.api.pc_miler.model import TokenData
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _RoutingAPI:
|
|
12
|
+
def __init__(self, client: PCMilerClient):
|
|
13
|
+
self.client = client
|
|
14
|
+
|
|
15
|
+
def _custom_headers(self):
|
|
16
|
+
return {
|
|
17
|
+
'Authorization': f'{self.client.api_key}',
|
|
18
|
+
'Accept': 'application/json',
|
|
19
|
+
'Content-type': 'application/json'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async def get_route_reports(self, url: str, params: Dict):
|
|
23
|
+
custom_headers = self._custom_headers()
|
|
24
|
+
return await self.client.get(url, params=params, headers=custom_headers)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def service_name(self):
|
|
28
|
+
return """ROUTING_API"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RoutingAPI(_RoutingAPI):
|
|
33
|
+
def __init__(self, client: PCMilerClient):
|
|
34
|
+
super().__init__(client)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _SingleSearchAPI:
|
|
38
|
+
def __init__(self, client: PCMilerClient):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SingleSearchAPI(_SingleSearchAPI):
|
|
43
|
+
def __init__(self, client: PCMilerClient):
|
|
44
|
+
super().__init__(client)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _PlacesAPI:
|
|
48
|
+
def __init__(self, client: PCMilerClient):
|
|
49
|
+
self.client = client
|
|
50
|
+
self.tokendata: TokenData | None = None
|
|
51
|
+
|
|
52
|
+
async def _custom_headers(self):
|
|
53
|
+
if not (self.tokendata and self.tokendata.token and datetime.now(UTC) < parse(self.tokendata.expires)):
|
|
54
|
+
self.tokendata = await self.client.get_token("https://api.trimblemaps.com/places/v1/authenticate")
|
|
55
|
+
return {
|
|
56
|
+
'Authorization': f'Bearer {self.tokendata.token}',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def service_name(self):
|
|
61
|
+
return """PLACES_API"""
|
|
62
|
+
|
|
63
|
+
class PlacesAPI(_PlacesAPI):
|
|
64
|
+
def __init__(self, client: PCMilerClient):
|
|
65
|
+
super().__init__(client)
|
|
66
|
+
|
|
67
|
+
async def get_place_by_id(self, id: str):
|
|
68
|
+
custom_headers = await self._custom_headers()
|
|
69
|
+
result = await self.client.get(
|
|
70
|
+
url=f"https://api.trimblemaps.com/places/v1/place/{id}/details",
|
|
71
|
+
headers=custom_headers
|
|
72
|
+
)
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
async def get_updated_places(self, last_modified_timestamp: str) -> list[dict] | None:
|
|
76
|
+
custom_headers = await self._custom_headers()
|
|
77
|
+
# last_modified_record=None
|
|
78
|
+
all_results = []
|
|
79
|
+
retries = 3
|
|
80
|
+
while True:
|
|
81
|
+
result = await self.client.get(
|
|
82
|
+
url=f"https://api.trimblemaps.com/places/v1/place/updatedPlaces",
|
|
83
|
+
params={
|
|
84
|
+
"lastModifiedDate": last_modified_timestamp,
|
|
85
|
+
# "lastModifiedRecord": last_modified_record,
|
|
86
|
+
"pageSize": 100
|
|
87
|
+
},
|
|
88
|
+
headers=custom_headers
|
|
89
|
+
)
|
|
90
|
+
if result.status_code != 200:
|
|
91
|
+
retries -= 1
|
|
92
|
+
if retries == 0:
|
|
93
|
+
return None
|
|
94
|
+
continue
|
|
95
|
+
data = result.json()
|
|
96
|
+
items = data.get("items", [])
|
|
97
|
+
all_results.extend(items)
|
|
98
|
+
if len(items) < 100:
|
|
99
|
+
break
|
|
100
|
+
# last_modified_record = data.get("lastModifiedRecord", None)
|
|
101
|
+
new_last_modified_timestamp = data.get("lastModifiedTimestamp", datetime(1970, 1, 1).isoformat())
|
|
102
|
+
if new_last_modified_timestamp == last_modified_timestamp:
|
|
103
|
+
# WARNING: This is a workaround for a bug in the Trimble API.
|
|
104
|
+
# In short, the API only returns 100 datapoints at a time, and if a timestamp has more than 100 datapoints
|
|
105
|
+
# associated it's not possible to get any datapoints after the 100th; the API ONLY lets me pick by timestamp
|
|
106
|
+
# and not by a generic page number or anything. Bulk operations seem to trigger this worse than most.
|
|
107
|
+
# As a workaround I will check if the timestamp we're going to "next" is the same as the current one, and add
|
|
108
|
+
# 1 millisecond if it is.
|
|
109
|
+
#
|
|
110
|
+
# This can skip over an unknown number of events! So far I've only seen problems due to mass deletions,
|
|
111
|
+
# but if there's a mass-edit functionality it could be an issue there too.
|
|
112
|
+
new_last_modified_timestamp = datetime.fromisoformat(new_last_modified_timestamp) + timedelta(milliseconds=1)
|
|
113
|
+
new_last_modified_timestamp = new_last_modified_timestamp.isoformat()
|
|
114
|
+
last_modified_timestamp = new_last_modified_timestamp
|
|
115
|
+
loguru.logger.debug(f"Fetching page from date {last_modified_timestamp}")
|
|
116
|
+
return all_results
|
|
117
|
+
|
|
118
|
+
async def create_place(self, place_req: dict, dry_run: bool = False):
|
|
119
|
+
custom_headers = await self._custom_headers()
|
|
120
|
+
if dry_run:
|
|
121
|
+
loguru.logger.debug(f"Dry run: Would create place {place_req}")
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
result = await self.client.post(
|
|
125
|
+
url=f"https://api.trimblemaps.com/places/v1/place",
|
|
126
|
+
json=place_req,
|
|
127
|
+
headers=custom_headers
|
|
128
|
+
)
|
|
129
|
+
loguru.logger.debug(result.status_code)
|
|
130
|
+
return result
|
|
131
|
+
|
|
File without changes
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from datetime import datetime, UTC
|
|
2
|
+
from typing import Self
|
|
3
|
+
|
|
4
|
+
from httpx import Response
|
|
5
|
+
|
|
6
|
+
from bb_integrations_lib.gravitate.base_api import BaseAPI
|
|
7
|
+
from bb_integrations_lib.provider.api.platform_science.model import JobDefinition, LoadDefinition
|
|
8
|
+
from bb_integrations_lib.secrets.credential_models import PlatformScienceCredential
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PlatformScienceClient(BaseAPI):
|
|
12
|
+
def __init__(self,
|
|
13
|
+
base_url: str,
|
|
14
|
+
client_id: str,
|
|
15
|
+
client_secret: str
|
|
16
|
+
):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.base_url = base_url
|
|
19
|
+
self.client_id = client_id
|
|
20
|
+
self.client_secret = client_secret
|
|
21
|
+
self._token = None
|
|
22
|
+
|
|
23
|
+
def __repr__(self):
|
|
24
|
+
return "Platform Science API client"
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_credential(cls, credential: PlatformScienceCredential) -> Self:
|
|
28
|
+
return cls(
|
|
29
|
+
base_url=credential.base_url,
|
|
30
|
+
client_id=credential.client_id,
|
|
31
|
+
client_secret=credential.client_secret,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
async def _get_token(self):
|
|
35
|
+
try:
|
|
36
|
+
resp = await self.post(
|
|
37
|
+
url=f"{self.base_url}oauth/token",
|
|
38
|
+
headers={
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"Accept": "application/json",
|
|
41
|
+
"api-version": "2.0"
|
|
42
|
+
},
|
|
43
|
+
json={
|
|
44
|
+
"grant_type": "client_credentials",
|
|
45
|
+
"scope": "admin workflow",
|
|
46
|
+
"client_id": self.client_id,
|
|
47
|
+
"client_secret": self.client_secret,
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
except Exception:
|
|
51
|
+
raise ValueError(f"Error getting token from {self.base_url}")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
self._token = resp.json()["access_token"]
|
|
55
|
+
return self._token
|
|
56
|
+
except Exception:
|
|
57
|
+
raise ValueError(f"Token response invalid from {self.base_url} -> {resp.status_code}")
|
|
58
|
+
|
|
59
|
+
async def _auth_request(self, method: str, **kwargs):
|
|
60
|
+
if not self._token:
|
|
61
|
+
await self._get_token()
|
|
62
|
+
|
|
63
|
+
headers = kwargs.pop("headers", {})
|
|
64
|
+
headers["Authorization"] = f"Bearer {self._token}"
|
|
65
|
+
headers["api-version"] = "2.0"
|
|
66
|
+
headers["Content-Type"] = "application/json"
|
|
67
|
+
kwargs["headers"] = headers
|
|
68
|
+
kwargs["url"] = f"{self.base_url}{kwargs.get('url', '')}"
|
|
69
|
+
kwargs["timeout"] = kwargs.get("timeout", 90)
|
|
70
|
+
|
|
71
|
+
response = await getattr(self, method)(**kwargs)
|
|
72
|
+
|
|
73
|
+
if response.status_code == 401:
|
|
74
|
+
await self._get_token() # refresh token
|
|
75
|
+
headers["authorization"] = f"Bearer {self._token}"
|
|
76
|
+
kwargs["headers"] = headers
|
|
77
|
+
response = await getattr(self, method)(**kwargs)
|
|
78
|
+
|
|
79
|
+
return response
|
|
80
|
+
|
|
81
|
+
async def auth_post(self, **kwargs) -> Response:
|
|
82
|
+
return await self._auth_request("post", **kwargs)
|
|
83
|
+
|
|
84
|
+
async def auth_get(self, **kwargs) -> Response:
|
|
85
|
+
return await self._auth_request("get", **kwargs)
|
|
86
|
+
|
|
87
|
+
async def auth_patch(self, **kwargs) -> Response:
|
|
88
|
+
return await self._auth_request("patch", **kwargs)
|
|
89
|
+
|
|
90
|
+
async def auth_put(self, **kwargs) -> Response:
|
|
91
|
+
return await self._auth_request("put", **kwargs)
|
|
92
|
+
|
|
93
|
+
async def auth_delete(self, **kwargs) -> Response:
|
|
94
|
+
return await self._auth_request("delete", **kwargs)
|
|
95
|
+
|
|
96
|
+
async def paginated_get(self, url: str, params: dict | None = None) -> list[dict]:
|
|
97
|
+
params = params or {}
|
|
98
|
+
first_page = await self.auth_get(url=url, params=params)
|
|
99
|
+
if first_page.status_code != 200:
|
|
100
|
+
raise RuntimeError(f"Error getting assets from {self.base_url}")
|
|
101
|
+
body = first_page.json()
|
|
102
|
+
data = body["data"]
|
|
103
|
+
total_pages = body["meta"]["pagination"]["total_pages"]
|
|
104
|
+
while body["meta"]["pagination"]["current_page"] < total_pages:
|
|
105
|
+
params["page"] = body["meta"]["pagination"]["current_page"] + 1
|
|
106
|
+
next_page = await self.auth_get(url="admin/assets", params=params)
|
|
107
|
+
body = next_page.json()
|
|
108
|
+
data.extend(body["data"])
|
|
109
|
+
return data
|
|
110
|
+
|
|
111
|
+
async def get_assets(self) -> list[dict]:
|
|
112
|
+
return await self.paginated_get(url="admin/assets")
|
|
113
|
+
|
|
114
|
+
async def get_drivers(self) -> list[dict]:
|
|
115
|
+
return await self.paginated_get(url="admin/drivers")
|
|
116
|
+
|
|
117
|
+
async def create_assets(self, req: list[dict]) -> Response:
|
|
118
|
+
return await self.auth_post(url="admin/assets", json=req)
|
|
119
|
+
|
|
120
|
+
async def get_amqp_connection(self, channel_name: str):
|
|
121
|
+
vhost = ""
|
|
122
|
+
conn_str = f"amqps://{self.client_id}:{self.client_secret}@amqp.pltsci.com:5671/{vhost}"
|
|
123
|
+
|
|
124
|
+
async def create_workflow_job(self, driver_id: str, job_definition: JobDefinition) -> Response:
|
|
125
|
+
return await self.auth_post(url=f"drivers/{driver_id}/jobs", json={
|
|
126
|
+
"job": job_definition.model_dump(mode="json", exclude_unset=True)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
async def update_workflow_job(self, driver_id: str, job_id: str, job_definition: JobDefinition) -> Response:
|
|
130
|
+
return await self.auth_put(url=f"drivers/{driver_id}/jobs/{job_id}", json={
|
|
131
|
+
"job": job_definition.model_dump(mode="json", exclude_unset=True)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
async def complete_workflow_job(self, job_id: str, completed_at: datetime | None = None) -> Response:
|
|
135
|
+
return await self.auth_patch(url=f"jobs/{job_id}/status", json={
|
|
136
|
+
"status": "tms_completed",
|
|
137
|
+
"completed_at": (completed_at or datetime.now(tz=UTC)).astimezone(tz=UTC).isoformat()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
async def delete_workflow_job(self, job_id: str) -> Response:
|
|
141
|
+
return await self.auth_delete(url=f"jobs/{job_id}/status")
|
|
142
|
+
|
|
143
|
+
async def create_load(self, driver_id: str, load_definition: LoadDefinition) -> Response:
|
|
144
|
+
return await self.auth_post(
|
|
145
|
+
url=f"admin/drivers/{driver_id}/loads",
|
|
146
|
+
json=load_definition.model_dump(mode="json", exclude_none=False)
|
|
147
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from datetime import datetime, date
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JobLocation(BaseModel):
|
|
8
|
+
external_id: str
|
|
9
|
+
name: str
|
|
10
|
+
address: str
|
|
11
|
+
latitude: str
|
|
12
|
+
longitude: str
|
|
13
|
+
city: str
|
|
14
|
+
state: str = Field(min_length=2, max_length=2)
|
|
15
|
+
country_code: str = Field(min_length=2, max_length=2)
|
|
16
|
+
postal_code: str | None = None
|
|
17
|
+
postal_splc: str | None = None
|
|
18
|
+
time_zone: Literal["US/Pacific", "US/Arizona", "US/Mountain", "US/Central", "US/Eastern"] | None = None
|
|
19
|
+
|
|
20
|
+
class JobTaskAppointment(BaseModel):
|
|
21
|
+
start_time: datetime
|
|
22
|
+
end_time: datetime
|
|
23
|
+
ready_time: datetime
|
|
24
|
+
late_time: datetime | None = None
|
|
25
|
+
|
|
26
|
+
class JobTask(BaseModel):
|
|
27
|
+
# external_data: JobTaskExternalData
|
|
28
|
+
remarks: list[str]
|
|
29
|
+
fields: dict[str, str]
|
|
30
|
+
id: str
|
|
31
|
+
external_id: str
|
|
32
|
+
order: int = Field(description="Index of the task within the step.")
|
|
33
|
+
type: str
|
|
34
|
+
status: str
|
|
35
|
+
name: str
|
|
36
|
+
completed: bool
|
|
37
|
+
completed_at: datetime | None = None
|
|
38
|
+
assets: str | None = None
|
|
39
|
+
|
|
40
|
+
class JobStep(BaseModel):
|
|
41
|
+
tasks: list[JobTask]
|
|
42
|
+
order: int = Field(description="Index of the step within the job.")
|
|
43
|
+
type: str
|
|
44
|
+
name: str
|
|
45
|
+
external_id: str
|
|
46
|
+
location_external_id: str
|
|
47
|
+
completed: bool
|
|
48
|
+
completed_at: datetime | None = None
|
|
49
|
+
is_disabled: bool | None = None
|
|
50
|
+
is_bypassable: bool | None = None
|
|
51
|
+
is_bypassed: bool | None = None
|
|
52
|
+
bypass_reason: str | None = None
|
|
53
|
+
is_reorderable: bool | None = None
|
|
54
|
+
reorder_reason: str | None = None
|
|
55
|
+
is_skippable: bool | None = None
|
|
56
|
+
is_skipped: bool | None = None
|
|
57
|
+
skip_reason: str | None = None
|
|
58
|
+
|
|
59
|
+
class ValueWithUnit(BaseModel):
|
|
60
|
+
value: float
|
|
61
|
+
uom: str
|
|
62
|
+
|
|
63
|
+
class ShipmentDetails(BaseModel):
|
|
64
|
+
total_distance: ValueWithUnit
|
|
65
|
+
|
|
66
|
+
class JobDefinition(BaseModel):
|
|
67
|
+
status: str # TODO: What statuses are possible?
|
|
68
|
+
external_id: str
|
|
69
|
+
locations: list[JobLocation]
|
|
70
|
+
steps: list[JobStep]
|
|
71
|
+
shipment_details: ShipmentDetails
|
|
72
|
+
|
|
73
|
+
class LoadEntity(BaseModel):
|
|
74
|
+
type: Literal["trailer", "bill_of_lading"]
|
|
75
|
+
value: str
|
|
76
|
+
|
|
77
|
+
class LoadDefinition(BaseModel):
|
|
78
|
+
start_date: date
|
|
79
|
+
end_date: date
|
|
80
|
+
load: str | None = None
|
|
81
|
+
user_external_id: str
|
|
82
|
+
entities: list[LoadEntity]
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Self
|
|
2
|
+
import httpx
|
|
3
|
+
|
|
4
|
+
from bb_integrations_lib.gravitate.base_api import BaseAPI
|
|
5
|
+
from bb_integrations_lib.secrets.credential_models import QTCredential
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QTApiClient(BaseAPI):
|
|
9
|
+
def __init__(self,
|
|
10
|
+
base_url: str,
|
|
11
|
+
qt_id: str,
|
|
12
|
+
carrier_id: str,
|
|
13
|
+
authorization: str):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.base_url = base_url
|
|
16
|
+
self.qt_id = qt_id
|
|
17
|
+
self.carrier_id = carrier_id
|
|
18
|
+
self.authorization = authorization
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_credential(cls, credential: QTCredential) -> Self:
|
|
22
|
+
return cls(**credential.model_dump(exclude={"type_tag"}))
|
|
23
|
+
|
|
24
|
+
async def get_inventory(self) -> list[dict]:
|
|
25
|
+
params = {
|
|
26
|
+
"qtId": self.qt_id,
|
|
27
|
+
"carrierId": self.carrier_id,
|
|
28
|
+
"authorization": self.authorization
|
|
29
|
+
}
|
|
30
|
+
response = await self.get(
|
|
31
|
+
f"{self.base_url}/api/ExternalEndpoint/GetLatestCarrierInventory",
|
|
32
|
+
params=params
|
|
33
|
+
)
|
|
34
|
+
response.raise_for_status()
|
|
35
|
+
return response.json()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
import asyncio
|
|
41
|
+
|
|
42
|
+
async def main():
|
|
43
|
+
client = QTApiClient(
|
|
44
|
+
base_url="https://petrocorpapi.quiktrip.com",
|
|
45
|
+
qt_id="EAGN",
|
|
46
|
+
carrier_id="41",
|
|
47
|
+
authorization="07572AD2B28DC42410D7CAD4ED126499FDE84F73A7D4A56CE2A1ADBE80117FDD10DC7923E9CFB06BF03FF9251CADD2F2163F7C341E954656FE046F3EC932CD7F"
|
|
48
|
+
)
|
|
49
|
+
inventory = await client.get_inventory()
|
|
50
|
+
print(inventory)
|
|
51
|
+
|
|
52
|
+
asyncio.run(main())
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import loguru
|
|
3
|
+
from bb_integrations_lib.provider.api.telapoint.model import CheckConnectionMethod, SoapEnvelope, OrderGetByOrderNumberMethod, TelapointNewOrder, OrderAdd
|
|
4
|
+
|
|
5
|
+
namespaces = {
|
|
6
|
+
'soapenv': "http://schemas.xmlsoap.org/soap/envelope/",
|
|
7
|
+
'tel': "http://schemas.datacontract.org/2004/07/TelaPoint.Api.TelaFuel.Models",
|
|
8
|
+
'v2': "http://api.telapoint.com/TelaFuel/v2",
|
|
9
|
+
'i': "http://www.w3.org/2001/XMLSchema-instance",
|
|
10
|
+
'wsse': "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
|
|
11
|
+
'wsu': "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TelapointClient(httpx.AsyncClient):
|
|
16
|
+
def __init__(self, username: str, password: str):
|
|
17
|
+
super().__init__(
|
|
18
|
+
base_url="https://api.telapoint.com/APIv2/TelaFuelService.svc",
|
|
19
|
+
headers={"Content-Type": "text/xml"}
|
|
20
|
+
)
|
|
21
|
+
self.base_url = "https://api.telapoint.com/APIv2/TelaFuelService.svc"
|
|
22
|
+
self.username = username
|
|
23
|
+
self.password = password
|
|
24
|
+
|
|
25
|
+
async def check_connection(self):
|
|
26
|
+
req = SoapEnvelope.build(self.username, self.password, CheckConnectionMethod)
|
|
27
|
+
|
|
28
|
+
response = await super().post(
|
|
29
|
+
url="",
|
|
30
|
+
headers={"SOAPAction": "http://api.telapoint.com/TelaFuel/v2/ITelaFuelService/CheckConnection"},
|
|
31
|
+
content=req.to_xml()
|
|
32
|
+
)
|
|
33
|
+
loguru.logger.debug(response.status_code)
|
|
34
|
+
return response
|
|
35
|
+
|
|
36
|
+
async def get_order_by_order_number(self, order_number: str):
|
|
37
|
+
req = SoapEnvelope.build(self.username, self.password, OrderGetByOrderNumberMethod, order_number=order_number)
|
|
38
|
+
|
|
39
|
+
response = await super().post(
|
|
40
|
+
url="",
|
|
41
|
+
headers={"SOAPAction": "http://api.telapoint.com/TelaFuel/v2/ITelaFuelService/OrderGetByOrderNumber"},
|
|
42
|
+
content=req.to_xml()
|
|
43
|
+
)
|
|
44
|
+
loguru.logger.debug(response.status_code)
|
|
45
|
+
return response
|
|
46
|
+
|
|
47
|
+
async def create_order(self, new_order: TelapointNewOrder):
|
|
48
|
+
req = SoapEnvelope.build_with_body(self.username, self.password, OrderAdd, OrderAdd(new_order=new_order))
|
|
49
|
+
|
|
50
|
+
response = await super().post(
|
|
51
|
+
url="",
|
|
52
|
+
headers={"SOAPAction": "http://api.telapoint.com/TelaFuel/v2/ITelaFuelService/OrderAdd"},
|
|
53
|
+
content=req.to_xml()
|
|
54
|
+
)
|
|
55
|
+
loguru.logger.debug(response.status_code)
|
|
56
|
+
return response
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
import asyncio
|
|
61
|
+
|
|
62
|
+
async def main():
|
|
63
|
+
tc = TelapointClient("<>", "<>")
|
|
64
|
+
resp = await tc.check_connection()
|
|
65
|
+
resp = await tc.get_order_by_order_number(order_number="51412828")
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from typing import Generic, TypeVar, Type
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import BaseXmlModel, element, attr
|
|
4
|
+
|
|
5
|
+
_nsmap = {
|
|
6
|
+
"soapenv": "http://schemas.xmlsoap.org/soap/envelope/",
|
|
7
|
+
"tel": "http://schemas.datacontract.org/2004/07/TelaPoint.Api.TelaFuel.Models",
|
|
8
|
+
"v2": "http://api.telapoint.com/TelaFuel/v2",
|
|
9
|
+
"i": "http://www.w3.org/2001/XMLSchema-instance",
|
|
10
|
+
"wsse": "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
|
|
11
|
+
"wsu": "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
####################### Copied from pydantic-xml docs for SOAP envelope tech. Probably don't touch.
|
|
15
|
+
|
|
16
|
+
AuthType = TypeVar('AuthType')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SoapHeader(
|
|
20
|
+
BaseXmlModel, Generic[AuthType],
|
|
21
|
+
tag='Header',
|
|
22
|
+
ns='soapenv',
|
|
23
|
+
nsmap=_nsmap,
|
|
24
|
+
):
|
|
25
|
+
auth: AuthType
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SoapMethod(BaseXmlModel):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
MethodType = TypeVar('MethodType', bound=SoapMethod)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SoapBody(
|
|
36
|
+
BaseXmlModel, Generic[MethodType],
|
|
37
|
+
tag='Body',
|
|
38
|
+
ns='soapenv',
|
|
39
|
+
nsmap=_nsmap,
|
|
40
|
+
):
|
|
41
|
+
call: MethodType
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
HeaderType = TypeVar('HeaderType', bound=SoapHeader)
|
|
45
|
+
BodyType = TypeVar('BodyType', bound=SoapBody)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _Password(BaseXmlModel, tag="Password", ns="wsse", nsmap=_nsmap):
|
|
49
|
+
type: str = attr("Type",
|
|
50
|
+
default="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText")
|
|
51
|
+
value: str
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class _UsernameToken(BaseXmlModel, tag="UsernameToken", ns="wsse", nsmap=_nsmap):
|
|
55
|
+
id: str = attr("Id", ns="wsu", default="uuid-3FE90A72-14E4-29C8-0782-232001AA1234-5")
|
|
56
|
+
username: str = element("Username", ns="wsse", text=True)
|
|
57
|
+
password: _Password
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Security(
|
|
61
|
+
BaseXmlModel,
|
|
62
|
+
tag='Security',
|
|
63
|
+
ns='wsse',
|
|
64
|
+
nsmap=_nsmap,
|
|
65
|
+
):
|
|
66
|
+
must_understand: str = attr("mustUnderstand", ns="wsu", default="1")
|
|
67
|
+
username_token: _UsernameToken
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SoapEnvelope(
|
|
71
|
+
BaseXmlModel,
|
|
72
|
+
Generic[MethodType],
|
|
73
|
+
tag='Envelope',
|
|
74
|
+
ns='soapenv',
|
|
75
|
+
nsmap=_nsmap,
|
|
76
|
+
):
|
|
77
|
+
header: SoapHeader[Security]
|
|
78
|
+
body: SoapBody[MethodType]
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def build(cls, username: str, password: str, method: Type[MethodType], **method_kwargs):
|
|
82
|
+
return cls[method](
|
|
83
|
+
header=SoapHeader[Security](
|
|
84
|
+
auth=Security(
|
|
85
|
+
username_token=_UsernameToken(
|
|
86
|
+
username=username,
|
|
87
|
+
password=_Password(value=password)
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
),
|
|
91
|
+
body=SoapBody(call=method(**method_kwargs))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def build_with_body(cls, username: str, password: str, method: Type[MethodType], body: MethodType):
|
|
96
|
+
return cls[method](
|
|
97
|
+
header=SoapHeader[Security](
|
|
98
|
+
auth=Security(
|
|
99
|
+
username_token=_UsernameToken(
|
|
100
|
+
username=username,
|
|
101
|
+
password=_Password(value=password)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
),
|
|
105
|
+
body=SoapBody(call=body)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
################################### End copy from pydantic-xml.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
Below this point you can define new methods. These are the things that go in the SOAP body in the request. They are
|
|
114
|
+
regular Pydantic XML models.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class CheckConnectionMethod(SoapMethod, tag="CheckConnection", ns="v2", nsmap=_nsmap):
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class OrderGetByOrderNumberMethod(SoapMethod, tag="OrderGetByOrderNumber", ns="v2", nsmap=_nsmap):
|
|
123
|
+
order_number: str = element("orderNumber", ns="v2", text=True)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class DropProduct(BaseXmlModel, tag="DropProduct", ns="tel", nsmap=_nsmap):
|
|
127
|
+
logical_tank_guid: str = element("LogicalTankGUID", ns="tel", text=True)
|
|
128
|
+
order_quantity: int = element("OrderQuantity", ns="tel", text=True)
|
|
129
|
+
product_guid: str = element("ProductGUID", ns="tel", text=True)
|
|
130
|
+
source_lift_sequence_number: int = element("SourceLiftSequenceNumber", ns="tel", text=True, default=1)
|
|
131
|
+
|
|
132
|
+
class DropProducts(BaseXmlModel, tag="OrderDrops", ns="tel", nsmap=_nsmap):
|
|
133
|
+
drop_products: list[DropProduct] = element("DropProduct", ns="tel")
|
|
134
|
+
|
|
135
|
+
class OrderDrop(BaseXmlModel, tag="OrderDrop", ns="tel", nsmap=_nsmap):
|
|
136
|
+
drop_duration: int = element("DropDuration", ns="tel", text=True)
|
|
137
|
+
drop_products: DropProducts = element("DropProducts", ns="tel")
|
|
138
|
+
earliest_date_time: str = element("EarliestDateTime", ns="tel", text=True)
|
|
139
|
+
latest_date_time: str = element("LatestDateTime", ns="tel", text=True)
|
|
140
|
+
order_type_id: int = element("OrderTypeID", ns="tel", text=True)
|
|
141
|
+
sequence_number: int = element("SequenceNumber", ns="tel", text=True, default=1)
|
|
142
|
+
site_guid: str = element("SiteGUID", ns="tel", text=True)
|
|
143
|
+
|
|
144
|
+
class OrderDrops(BaseXmlModel, tag="OrderDrops", ns="tel", nsmap=_nsmap):
|
|
145
|
+
order_drops: list[OrderDrop] = element("OrderDrop", ns="tel")
|
|
146
|
+
|
|
147
|
+
class LiftProduct(BaseXmlModel, tag="LiftProduct", ns="tel", nsmap=_nsmap):
|
|
148
|
+
order_quantity: int = element("OrderQuantity", ns="tel", text=True)
|
|
149
|
+
product_guid: str = element("ProductGUID", ns="tel", text=True)
|
|
150
|
+
|
|
151
|
+
class LiftProducts(BaseXmlModel, tag="LiftProducts", ns="tel", nsmap=_nsmap):
|
|
152
|
+
lift_products: list[LiftProduct] = element("LiftProduct", ns="tel")
|
|
153
|
+
|
|
154
|
+
class OrderLift(BaseXmlModel, tag="OrderLift", ns="tel", nsmap=_nsmap):
|
|
155
|
+
lift_products: LiftProducts = element("LiftProducts", ns="tel")
|
|
156
|
+
planned_lift_date_time: str = element("PlannedLiftDateTime", ns="tel", text=True)
|
|
157
|
+
sequence_number: int = element("SequenceNumber", ns="tel", text=True, default=1)
|
|
158
|
+
|
|
159
|
+
class OrderLifts(BaseXmlModel, tag="OrderLifts", ns="tel", nsmap=_nsmap):
|
|
160
|
+
order_lifts: list[OrderLift] = element("OrderLift", ns="tel")
|
|
161
|
+
|
|
162
|
+
class TelapointNewOrder(BaseXmlModel, tag="newOrder", ns="v2", nsmap=_nsmap):
|
|
163
|
+
bill_to_guid: str = element("BillToGUID", ns="tel", text=True)
|
|
164
|
+
bill_to_type: str = element("BillToType", ns="tel", text=True)
|
|
165
|
+
carrier_guid: str = element("CarrierGUID", ns="tel", text=True)
|
|
166
|
+
freight_lane_guid: str = element("FreightLaneGUID", ns="tel", text=True)
|
|
167
|
+
order_drops: OrderDrops = element("OrderDrops", ns="tel")
|
|
168
|
+
order_lifts: OrderLifts = element("OrderLifts", ns="tel")
|
|
169
|
+
order_status: str = element("OrderStatus", ns="tel", text=True)
|
|
170
|
+
order_type: str = element("OrderType", ns="tel", text=True)
|
|
171
|
+
po_number: str = element("PONumber", ns="tel", text=True)
|
|
172
|
+
parent_company_guid: str = element("ParentCompanyGUID", ns="tel", text=True)
|
|
173
|
+
|
|
174
|
+
class OrderAdd(SoapMethod, tag="OrderAdd", ns="v2", nsmap=_nsmap):
|
|
175
|
+
new_order: TelapointNewOrder
|
|
176
|
+
|
|
177
|
+
class OrderAddResponse(SoapMethod, tag="OrderAddResponse", ns="v2", nsmap=_nsmap):
|
|
178
|
+
order_add_result: str = element("OrderAddResult", text=True)
|
|
File without changes
|