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,572 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import pprint
|
|
3
|
+
import warnings
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from json import JSONDecodeError
|
|
6
|
+
from typing import Dict, Iterable, Union, Tuple, Literal, List, Self
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from async_lru import alru_cache
|
|
9
|
+
from httpx import Response, HTTPStatusError
|
|
10
|
+
from loguru import logger
|
|
11
|
+
from bb_integrations_lib.gravitate.base_api import BaseAPI
|
|
12
|
+
from bb_integrations_lib.models.sd_api import OrderUpdateRequest, SendToCarrierRequest
|
|
13
|
+
from bb_integrations_lib.secrets import SDCredential
|
|
14
|
+
from bb_integrations_lib.protocols.flat_file import TankReading, PriceRow, TankSales, DriverCredential, \
|
|
15
|
+
SalesAdjustedDeliveryReading
|
|
16
|
+
from bb_integrations_lib.shared.model import SupplyPriceUpdateManyRequest, GetOrderBolsAndDropsRequest, \
|
|
17
|
+
GetFreightInvoicesRequest, SDSupplierInvoiceCreateRequest, SDGetAllSupplierReconciliationInvoiceRequest, \
|
|
18
|
+
SDDeliveryReconciliationMatchOverviewRequest, SDGetUnexportedOrdersRequest, SDSetOrderExportStatusRequest
|
|
19
|
+
from bb_integrations_lib.util.config.model import Config
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GravitateSDAPI(BaseAPI):
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
base_url: str,
|
|
26
|
+
client_id: str | None = None,
|
|
27
|
+
client_secret: str | None = None,
|
|
28
|
+
username: str | None = None,
|
|
29
|
+
password: str | None = None,
|
|
30
|
+
raise_errors: bool = True,
|
|
31
|
+
):
|
|
32
|
+
super().__init__(raise_errors)
|
|
33
|
+
self.base_url = self.valid_url(base_url)
|
|
34
|
+
self.client_id = client_id
|
|
35
|
+
self.client_secret = client_secret
|
|
36
|
+
self.username = username
|
|
37
|
+
self.password = password
|
|
38
|
+
self._token = None
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_config(cls, config_name: str, username: str | None = None, password: str | None = None):
|
|
42
|
+
warnings.warn("Use gravitate.sd_api.from_credential instead", DeprecationWarning, stacklevel=2)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_global_config(cls, c: Config):
|
|
46
|
+
warnings.warn("Use gravitate.sd_api.from_credential instead", DeprecationWarning, stacklevel=2)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_credential(cls, credential: SDCredential) -> Self:
|
|
50
|
+
return cls(
|
|
51
|
+
base_url=credential.host,
|
|
52
|
+
username=credential.username,
|
|
53
|
+
password=credential.password,
|
|
54
|
+
client_id=credential.client_id,
|
|
55
|
+
client_secret=credential.client_secret,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def valid_url(url: str) -> str:
|
|
60
|
+
if not url:
|
|
61
|
+
return ""
|
|
62
|
+
if not url.endswith("/"):
|
|
63
|
+
url += "/"
|
|
64
|
+
if "api" not in url and ":80" not in url and "local" not in url: # MOFO
|
|
65
|
+
url += "api/"
|
|
66
|
+
if not url.startswith("http"):
|
|
67
|
+
raise ValueError(f"Invalid URL: {url} must begin with http or https")
|
|
68
|
+
return url
|
|
69
|
+
|
|
70
|
+
async def _get_user_pass_token(self) -> str:
|
|
71
|
+
try:
|
|
72
|
+
resp = await self.post(
|
|
73
|
+
url=f"{self.base_url}token",
|
|
74
|
+
data={
|
|
75
|
+
"username": self.username,
|
|
76
|
+
"password": self.password,
|
|
77
|
+
"scope": "bbd",
|
|
78
|
+
},
|
|
79
|
+
timeout=120
|
|
80
|
+
)
|
|
81
|
+
resp.raise_for_status()
|
|
82
|
+
return resp.json()["access_token"]
|
|
83
|
+
except Exception as e:
|
|
84
|
+
logger.error(f"Error requesting token from {self.base_url} with username {self.username}: {e}")
|
|
85
|
+
raise e
|
|
86
|
+
|
|
87
|
+
async def _get_api_key_token(self) -> str:
|
|
88
|
+
try:
|
|
89
|
+
resp = await self.post(
|
|
90
|
+
url=f"{self.base_url}token",
|
|
91
|
+
data={
|
|
92
|
+
"client_id": self.client_id,
|
|
93
|
+
"client_secret": self.client_secret,
|
|
94
|
+
"scope": "bbd",
|
|
95
|
+
},
|
|
96
|
+
timeout=120
|
|
97
|
+
)
|
|
98
|
+
resp.raise_for_status()
|
|
99
|
+
return resp.json()["access_token"]
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.error(f"Error requesting token from {self.base_url} with client ID {self.client_id}: {e}")
|
|
102
|
+
raise e
|
|
103
|
+
|
|
104
|
+
async def _get_token(self):
|
|
105
|
+
if not (self.username and self.password) and not (self.client_id and self.client_secret):
|
|
106
|
+
raise RuntimeError("Missing credentials for token request")
|
|
107
|
+
|
|
108
|
+
if self.username and self.password:
|
|
109
|
+
try:
|
|
110
|
+
self._token = await self._get_user_pass_token()
|
|
111
|
+
return self._token
|
|
112
|
+
except:
|
|
113
|
+
pass
|
|
114
|
+
if self.client_id and self.client_secret:
|
|
115
|
+
try:
|
|
116
|
+
self._token = await self._get_api_key_token()
|
|
117
|
+
return self._token
|
|
118
|
+
except:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
raise Exception(
|
|
122
|
+
"Could not get token with either username/password or client ID/secret. Check your credentials.")
|
|
123
|
+
|
|
124
|
+
async def _auth_request(self, method: str, **kwargs):
|
|
125
|
+
if not self._token:
|
|
126
|
+
await self._get_token()
|
|
127
|
+
|
|
128
|
+
headers = kwargs.pop("headers", {})
|
|
129
|
+
headers["authorization"] = f"Bearer {self._token}"
|
|
130
|
+
kwargs["headers"] = headers
|
|
131
|
+
kwargs["url"] = f"{self.base_url}{kwargs.get('url', '')}"
|
|
132
|
+
kwargs["timeout"] = kwargs.get("timeout", 90)
|
|
133
|
+
|
|
134
|
+
response = await getattr(self, method)(**kwargs)
|
|
135
|
+
|
|
136
|
+
if response.status_code == 401:
|
|
137
|
+
await self._get_token() # refresh token
|
|
138
|
+
headers["authorization"] = f"Bearer {self._token}"
|
|
139
|
+
kwargs["headers"] = headers
|
|
140
|
+
response = await getattr(self, method)(**kwargs)
|
|
141
|
+
|
|
142
|
+
if response.status_code == 422:
|
|
143
|
+
try:
|
|
144
|
+
response_content = pprint.pformat(response.json())
|
|
145
|
+
except JSONDecodeError:
|
|
146
|
+
response_content = response.text
|
|
147
|
+
raise HTTPStatusError(
|
|
148
|
+
f"Bad request: \n{response_content}",
|
|
149
|
+
request=response.request,
|
|
150
|
+
response=response
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return response
|
|
154
|
+
|
|
155
|
+
async def token_post(self, **kwargs):
|
|
156
|
+
return await self._auth_request("post", **kwargs)
|
|
157
|
+
|
|
158
|
+
async def token_get(self, **kwargs):
|
|
159
|
+
return await self._auth_request("get", **kwargs)
|
|
160
|
+
|
|
161
|
+
async def call_ep(self, url: str, params: dict = None, json: dict = None, method: str = "POST") -> Response:
|
|
162
|
+
if method == "POST":
|
|
163
|
+
return await self.token_post(url=url, params=params, json=json)
|
|
164
|
+
if json is not None:
|
|
165
|
+
raise ValueError("JSON is not supported for GET requests")
|
|
166
|
+
return await self.token_get(url=url, params=params)
|
|
167
|
+
|
|
168
|
+
async def get_lp_config(self) -> Response:
|
|
169
|
+
url = f"best_buy/get_lp_config"
|
|
170
|
+
return await self.token_post(url=url)
|
|
171
|
+
|
|
172
|
+
async def set_lp_config(self, payload: dict) -> Response:
|
|
173
|
+
url = "best_buy/set_lp_config"
|
|
174
|
+
return await self.token_post(url=url, json=payload)
|
|
175
|
+
|
|
176
|
+
async def update_lp_config(self, updates: dict) -> Response:
|
|
177
|
+
existing = self.get_lp_config()
|
|
178
|
+
return await self.set_lp_config(payload={**existing, **updates})
|
|
179
|
+
|
|
180
|
+
async def get_model_config(self) -> Response:
|
|
181
|
+
url = "best_buy/get_model_config"
|
|
182
|
+
return await self.token_post(url=url)
|
|
183
|
+
|
|
184
|
+
async def set_model_config(self, payload: dict) -> Response:
|
|
185
|
+
url = "best_buy/set_model_config"
|
|
186
|
+
return await self.token_post(url=url, json=payload)
|
|
187
|
+
|
|
188
|
+
async def update_model_config(self, updates: dict) -> Response:
|
|
189
|
+
existing = await self.get_model_config()
|
|
190
|
+
return await self.set_model_config(payload={**existing, **updates})
|
|
191
|
+
|
|
192
|
+
async def update_tank_supply_many(self, payload: dict) -> Response:
|
|
193
|
+
url = "store/tank/supply/assign_many"
|
|
194
|
+
return await self.token_post(url=url, json=payload)
|
|
195
|
+
|
|
196
|
+
async def valuation_delivery_send(self, params: dict) -> Response:
|
|
197
|
+
url = "valuation/delivery/send"
|
|
198
|
+
return await self.token_post(url=url, params=params)
|
|
199
|
+
|
|
200
|
+
@alru_cache(maxsize=128)
|
|
201
|
+
async def get_all_store_tanks(self) -> Response:
|
|
202
|
+
url = "store/tank/all"
|
|
203
|
+
return await self.token_post(url=url)
|
|
204
|
+
|
|
205
|
+
@alru_cache(maxsize=128)
|
|
206
|
+
async def get_all_stores(self, include_tanks: bool = False) -> Response:
|
|
207
|
+
url = "v1/store/all"
|
|
208
|
+
return await self.token_post(url=url,
|
|
209
|
+
params={
|
|
210
|
+
"include_tanks": include_tanks,
|
|
211
|
+
},
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
async def get_all_store_tanks_lkp(self) -> Dict[str, dict]:
|
|
215
|
+
"""returns store tank lkp, key is store_number+tank_id"""
|
|
216
|
+
try:
|
|
217
|
+
data = await self.get_all_store_tanks()
|
|
218
|
+
data = data.json()
|
|
219
|
+
res = {f"{row['store_number']}{row['tank_id']}": row for row in data}
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(f"Error while building Tank lookup: {e}")
|
|
222
|
+
res = {}
|
|
223
|
+
return res
|
|
224
|
+
|
|
225
|
+
async def get_all_stores_lkp(self) -> Dict[str, dict]:
|
|
226
|
+
"""returns store lkp, key is store_number"""
|
|
227
|
+
try:
|
|
228
|
+
data = await self.get_all_stores()
|
|
229
|
+
data = data.json()
|
|
230
|
+
res = {f"{row['store_number']}": row for row in data}
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"Error while building store lookup: {e}")
|
|
233
|
+
res = {}
|
|
234
|
+
return res
|
|
235
|
+
|
|
236
|
+
async def get_valuation_report(self, start_date: str = None, end_date: str = None):
|
|
237
|
+
url = "valuation/valuation/export"
|
|
238
|
+
params = {"start_date": start_date, "end_date": end_date}
|
|
239
|
+
resp = await self.token_post(url=url, params=params)
|
|
240
|
+
if resp.status_code == 200:
|
|
241
|
+
with io.BytesIO(resp.content) as f:
|
|
242
|
+
df = pd.read_excel(f)
|
|
243
|
+
df["Site"] = df["Site"].astype(str)
|
|
244
|
+
df["Date"] = pd.to_datetime(df["Date"])
|
|
245
|
+
return df[["Site", "Product", "Date", "Initial Volume", "Final Volume"]]
|
|
246
|
+
else:
|
|
247
|
+
logger.error("Valuation Export Failed.")
|
|
248
|
+
|
|
249
|
+
async def upsert_many_counterparty(self, payload: list[dict]) -> Response:
|
|
250
|
+
url = "v1/counterparty/upsert_many"
|
|
251
|
+
return await self.token_post(url=url, json=payload)
|
|
252
|
+
|
|
253
|
+
async def driver_schedule_template_upsert(self, payload: dict) -> Response:
|
|
254
|
+
url = "driver_schedule/template/upsert"
|
|
255
|
+
return await self.token_post(url=url, json=payload)
|
|
256
|
+
|
|
257
|
+
async def import_sales_data(self, payload) -> Response:
|
|
258
|
+
url = "forecast/import_sales_data"
|
|
259
|
+
request_data = {"reqs": payload}
|
|
260
|
+
return await self.token_post(url=url, json=request_data)
|
|
261
|
+
|
|
262
|
+
async def update_store(self, payload) -> Response:
|
|
263
|
+
url = "store/update"
|
|
264
|
+
request_data = payload
|
|
265
|
+
return await self.token_post(url=url, json=request_data)
|
|
266
|
+
|
|
267
|
+
async def update_many_store(self, payload) -> Response:
|
|
268
|
+
url = "v1/store/upsert_many"
|
|
269
|
+
return await self.token_post(url=url, json=payload)
|
|
270
|
+
|
|
271
|
+
async def update_many_locations(self, payload) -> Response:
|
|
272
|
+
url = "v1/location/upsert_many"
|
|
273
|
+
return await self.token_post(url=url, json=payload)
|
|
274
|
+
|
|
275
|
+
async def forecast_import_sales_data(self, payload) -> Response:
|
|
276
|
+
url = "v1/import_sales_data"
|
|
277
|
+
return await self.token_post(url=url, json=payload)
|
|
278
|
+
|
|
279
|
+
async def payroll_export_file(self, date: str):
|
|
280
|
+
url = "v1/payroll/export_file"
|
|
281
|
+
resp = await self.token_get(url=url, params={"date": date})
|
|
282
|
+
resp.raise_for_status()
|
|
283
|
+
return resp
|
|
284
|
+
|
|
285
|
+
async def order_export_file(self, date: str, **kwargs):
|
|
286
|
+
url = "backoffice_erp/export"
|
|
287
|
+
resp = await self.token_get(url=url, params={"as_of": date, **kwargs})
|
|
288
|
+
resp.raise_for_status()
|
|
289
|
+
return resp
|
|
290
|
+
|
|
291
|
+
async def payroll_export(self, date: str, **kwargs):
|
|
292
|
+
url = "v1/payroll/export"
|
|
293
|
+
return await self.token_get(url=url, params={"date": date, **kwargs})
|
|
294
|
+
|
|
295
|
+
async def upload_readings(self, data: Iterable[TankReading], raise_error=True) -> Response:
|
|
296
|
+
"""Uploads tank readings to BBD. Set raise_error to false to squelch the exception that's thrown if at least one
|
|
297
|
+
reading does not upload."""
|
|
298
|
+
url = "v1/tank_readings/upload"
|
|
299
|
+
resp = await self.token_post(url=url, json=data)
|
|
300
|
+
if resp.status_code != 200:
|
|
301
|
+
logger.error(resp.text)
|
|
302
|
+
return resp
|
|
303
|
+
|
|
304
|
+
async def upload_tank_sales(self, data: Iterable[TankSales]) -> Dict:
|
|
305
|
+
url = "v1/import_sales_data"
|
|
306
|
+
json_data = [item.model_dump() if hasattr(item, 'model_dump') else item for item in data]
|
|
307
|
+
request_data = {"reqs": json_data}
|
|
308
|
+
resp = await self.token_post(url=url, json=request_data)
|
|
309
|
+
if resp.status_code != 200:
|
|
310
|
+
logger.error(resp.text)
|
|
311
|
+
raise Exception(f"Upload failed with status code {resp.status_code}: {resp.text}")
|
|
312
|
+
return resp.json()
|
|
313
|
+
|
|
314
|
+
async def upload_prices(self, data: Union[Iterable[PriceRow], Iterable[SupplyPriceUpdateManyRequest]]) -> Tuple[
|
|
315
|
+
int, Dict]:
|
|
316
|
+
url = "v1/price_update_many"
|
|
317
|
+
json_data = [item.model_dump() if hasattr(item, 'model_dump') else item for item in data]
|
|
318
|
+
resp = await self.token_post(url=url, json=json_data)
|
|
319
|
+
if resp.status_code != 200:
|
|
320
|
+
logger.error(resp.text)
|
|
321
|
+
raise Exception(f"Upload failed with status code {resp.status_code}: {resp.text}")
|
|
322
|
+
resp_data = resp.json()
|
|
323
|
+
if not resp_data.get("created") and not resp_data.get("bad_data"):
|
|
324
|
+
raise Exception(f"Uploaded prices responded with an error: {resp_data}")
|
|
325
|
+
return resp_data.get("created", 0) + resp_data.get("end_dated", 0), resp_data
|
|
326
|
+
|
|
327
|
+
async def upload_credentials(self, data: Iterable[DriverCredential]) -> Response:
|
|
328
|
+
url = "v1/driver/credential/upsert_many",
|
|
329
|
+
resp = await self.token_post(url=url, json=data)
|
|
330
|
+
if resp.status_code != 200:
|
|
331
|
+
logger.error(resp.text)
|
|
332
|
+
return resp.json()
|
|
333
|
+
|
|
334
|
+
async def upload_sales_adjusted_deliveries(self, data: Iterable[SalesAdjustedDeliveryReading]) -> int:
|
|
335
|
+
"""Upload many sales adjusted deliveries.
|
|
336
|
+
|
|
337
|
+
:returns: The number of successfully uploaded deliveries.
|
|
338
|
+
"""
|
|
339
|
+
url = "v1/sales_adjusted_delivery/upsert_many"
|
|
340
|
+
json_data = [x.model_dump(mode="json") for x in data]
|
|
341
|
+
resp = await self.token_post(url=url, json=json_data)
|
|
342
|
+
if resp.status_code != 200:
|
|
343
|
+
logger.error(resp.text)
|
|
344
|
+
if resp.content == b"null":
|
|
345
|
+
return 0
|
|
346
|
+
else:
|
|
347
|
+
uploaded_count = resp.json().get("inserted_count", 0)
|
|
348
|
+
return uploaded_count
|
|
349
|
+
|
|
350
|
+
async def upload_supplier_invoice(self, invoice: SDSupplierInvoiceCreateRequest) -> Response:
|
|
351
|
+
url = "v1/supplier_reconciliation/invoice/create"
|
|
352
|
+
resp = await self.token_post(url=url, json=invoice.model_dump(mode="json"))
|
|
353
|
+
if resp.status_code != 200:
|
|
354
|
+
logger.error(resp.text)
|
|
355
|
+
return resp
|
|
356
|
+
|
|
357
|
+
async def get_all_supplier_reconciliation_invoices(self,
|
|
358
|
+
req: SDGetAllSupplierReconciliationInvoiceRequest) -> Response:
|
|
359
|
+
url = "v1/supplier_reconciliation/invoice/all"
|
|
360
|
+
resp = await self.token_post(url=url, json=req.model_dump(mode="json"))
|
|
361
|
+
resp.raise_for_status()
|
|
362
|
+
return resp
|
|
363
|
+
|
|
364
|
+
async def delivery_reconciliation_match_overview(self,
|
|
365
|
+
req: SDDeliveryReconciliationMatchOverviewRequest) -> Response:
|
|
366
|
+
url = "v1/delivery_reconciliation/match/overview"
|
|
367
|
+
resp = await self.token_post(url=url, json=req.model_dump(mode="json"))
|
|
368
|
+
resp.raise_for_status()
|
|
369
|
+
return resp
|
|
370
|
+
|
|
371
|
+
async def all_counterparties(self) -> Response:
|
|
372
|
+
return await self.token_post(url="v1/counterparty/all")
|
|
373
|
+
|
|
374
|
+
async def all_locations(self) -> Response:
|
|
375
|
+
return await self.token_post(url="v1/location/all")
|
|
376
|
+
|
|
377
|
+
async def all_stores(self, include_tanks: bool = False) -> Response:
|
|
378
|
+
return await self.token_post(url="v1/store/all", params={"include_tanks": include_tanks})
|
|
379
|
+
|
|
380
|
+
async def all_drivers(self) -> Response:
|
|
381
|
+
return await self.token_post(url="v1/driver/all")
|
|
382
|
+
|
|
383
|
+
async def all_products(self) -> Response:
|
|
384
|
+
return await self.token_post(url="v1/product/all")
|
|
385
|
+
|
|
386
|
+
async def all_trailers(self) -> Response:
|
|
387
|
+
return await self.token_post(url="v1/trailer/all")
|
|
388
|
+
|
|
389
|
+
async def all_tractors(self) -> Response:
|
|
390
|
+
return await self.token_post(url="v1/tractor/all")
|
|
391
|
+
|
|
392
|
+
async def all_depots(self) -> Response:
|
|
393
|
+
return await self.token_post(url="v1/depot/all")
|
|
394
|
+
|
|
395
|
+
async def all_markets(self) -> Response:
|
|
396
|
+
return await self.token_post(url="v1/market/all")
|
|
397
|
+
|
|
398
|
+
async def get_unexported_orders(self, req: SDGetUnexportedOrdersRequest) -> Response:
|
|
399
|
+
return await self.token_post(url="v1/order/unexported", json=req.model_dump(mode="json"))
|
|
400
|
+
|
|
401
|
+
async def bulk_set_export_order_status(self, req: List[SDSetOrderExportStatusRequest]) -> Response:
|
|
402
|
+
serialized = [row.model_dump(mode="json") for row in req]
|
|
403
|
+
return await self.token_post(url="v1/order/set_export_status", json=serialized)
|
|
404
|
+
|
|
405
|
+
async def upsert_directives(self, req: list[dict]) -> Response:
|
|
406
|
+
return await self.token_post(url="v1/directive/upsert_many", json=req)
|
|
407
|
+
|
|
408
|
+
async def export_single_order(self, order_number: int, function_name: str) -> Response:
|
|
409
|
+
params = {"order_number": order_number, "function_name": function_name}
|
|
410
|
+
return await self.token_post(url=f"backoffice_erp/export_single", params=params)
|
|
411
|
+
|
|
412
|
+
async def mark_backhaul_exported(self, order_number: int) -> Response:
|
|
413
|
+
params = {"order_number": order_number}
|
|
414
|
+
return await self.token_post(url=f"backoffice_erp/mark_backhaul_exported", params=params)
|
|
415
|
+
|
|
416
|
+
async def get_orders(
|
|
417
|
+
self,
|
|
418
|
+
order_id: str | None = None,
|
|
419
|
+
order_number: str | int | None = None,
|
|
420
|
+
order_type: Literal["Regular", "Backhaul"] | None = None,
|
|
421
|
+
order_state: Literal["Accepted", "Assigned", "In Progress", "Complete", "Canceled"] | None = None,
|
|
422
|
+
order_date_start: datetime | None = None,
|
|
423
|
+
order_date_end: datetime | None = None,
|
|
424
|
+
last_change_date: datetime | None = None,
|
|
425
|
+
reference_order_number: str | None = None,
|
|
426
|
+
) -> Response:
|
|
427
|
+
body = {
|
|
428
|
+
k: v for k, v in {
|
|
429
|
+
"order_id": order_id,
|
|
430
|
+
"order_number": order_number,
|
|
431
|
+
"order_type": order_type,
|
|
432
|
+
"order_state": order_state,
|
|
433
|
+
"order_date_start": order_date_start.isoformat(timespec='microseconds') if order_date_start else None,
|
|
434
|
+
"order_date_end": order_date_end.isoformat(timespec='microseconds') if order_date_end else None,
|
|
435
|
+
"last_change_date": last_change_date.isoformat(timespec='microseconds') if last_change_date else None,
|
|
436
|
+
"reference_order_number": reference_order_number if reference_order_number else None,
|
|
437
|
+
}.items()
|
|
438
|
+
if v is not None
|
|
439
|
+
}
|
|
440
|
+
# POST null body instead of empty dict if we don't have any parameters
|
|
441
|
+
resp = await self.token_post(url="v1/order/get_orders", json=body or None)
|
|
442
|
+
resp.raise_for_status()
|
|
443
|
+
return resp
|
|
444
|
+
|
|
445
|
+
async def get_orders_overview(self, filter: dict):
|
|
446
|
+
data = {"filter": filter}
|
|
447
|
+
return await self.token_post(url="order/overview", json=data)
|
|
448
|
+
|
|
449
|
+
async def get_bols_and_drops(self, req: GetOrderBolsAndDropsRequest) -> Response:
|
|
450
|
+
url = "v1/bols_and_drops"
|
|
451
|
+
response = await self.token_post(url=url, json=req.model_dump(mode="json", exclude_none=True))
|
|
452
|
+
response.raise_for_status()
|
|
453
|
+
return response
|
|
454
|
+
|
|
455
|
+
async def get_order_allocations(self, order_number: str) -> Response:
|
|
456
|
+
params = {'order_number': order_number, 'save': False}
|
|
457
|
+
return await self.token_post(url='bol/allocate_bols', params=params)
|
|
458
|
+
|
|
459
|
+
async def get_driver_schedules(self, driver_shift: str, driver_schedule_date: datetime) -> Response:
|
|
460
|
+
data = {
|
|
461
|
+
"shift": driver_shift,
|
|
462
|
+
"date": driver_schedule_date,
|
|
463
|
+
}
|
|
464
|
+
return await self.token_post(url='driver_schedule/schedule', json=data)
|
|
465
|
+
|
|
466
|
+
async def get_driver_tracking(self, order_numbers: list[int] | None = None,
|
|
467
|
+
driver_schedule_ids: list[str] | None = None) -> Response:
|
|
468
|
+
data = {
|
|
469
|
+
"order_numbers": order_numbers,
|
|
470
|
+
"driver_schedule_ids": driver_schedule_ids,
|
|
471
|
+
}
|
|
472
|
+
return await self.token_post(url="v1/driver_tracking/all", json=data)
|
|
473
|
+
|
|
474
|
+
async def get_driver_schedules_tracking(self, driver_schedule_ids: list[str]) -> Response:
|
|
475
|
+
data = {"driver_schedule_ids": driver_schedule_ids}
|
|
476
|
+
return await self.token_post(url='v1/driver_tracking/all', json=data)
|
|
477
|
+
|
|
478
|
+
async def get_route_overview(self, location_id: str) -> Response:
|
|
479
|
+
data = {"location_id": location_id}
|
|
480
|
+
return await self.token_post(url="logistics/route/overview", json=data)
|
|
481
|
+
|
|
482
|
+
async def create_order(self, order: dict) -> Response:
|
|
483
|
+
return await self.token_post(url="v1/order/create", json=order)
|
|
484
|
+
|
|
485
|
+
async def run_smart_supply(self, req: dict) -> Response:
|
|
486
|
+
return await self.token_post(url="smart_supply/run", json=req)
|
|
487
|
+
|
|
488
|
+
async def get_smart_supply_state(self) -> Response:
|
|
489
|
+
return await self.token_post(url="smart_supply/state", json={})
|
|
490
|
+
|
|
491
|
+
async def cancel_smart_supply(self, id: str) -> Response:
|
|
492
|
+
return await self.token_post(url="smart_supply/cancel", params={"smart_supply_id": id})
|
|
493
|
+
|
|
494
|
+
async def update_allocations(self, req: dict) -> Response:
|
|
495
|
+
return await self.token_post(url="directive_management/update_allocations", json=req)
|
|
496
|
+
|
|
497
|
+
async def update_order_status(self, req: dict) -> Response:
|
|
498
|
+
return await self.token_post(url="v1/order/update_status", json=req)
|
|
499
|
+
|
|
500
|
+
async def cancel_order(self, req: dict) -> Response:
|
|
501
|
+
return await self.token_post(url="order/cancel_order", json=req)
|
|
502
|
+
|
|
503
|
+
async def save_bols(self, req: dict) -> Response:
|
|
504
|
+
return await self.token_post(url="v1/order/save_bol", json=req)
|
|
505
|
+
|
|
506
|
+
async def save_drop(self, req: dict) -> Response:
|
|
507
|
+
return await self.token_post(url="v1/order/save_drop", json=req)
|
|
508
|
+
|
|
509
|
+
async def get_tractors(self) -> Response:
|
|
510
|
+
return await self.token_post(url="v1/tractor/all")
|
|
511
|
+
|
|
512
|
+
async def get_drivers(self) -> Response:
|
|
513
|
+
return await self.token_post(url="v1/driver/all")
|
|
514
|
+
|
|
515
|
+
async def get_freight_invoices(self, req: GetFreightInvoicesRequest):
|
|
516
|
+
url = "v1/freight/invoice/all"
|
|
517
|
+
resp = await self.token_post(url=url, json=req.model_dump(mode="json", exclude_none=True))
|
|
518
|
+
resp.raise_for_status()
|
|
519
|
+
return resp
|
|
520
|
+
|
|
521
|
+
async def mark_invoices_exported(self, invoice_numbers: list[str]):
|
|
522
|
+
url = "v1/freight/invoice/mark_export"
|
|
523
|
+
resp = await self.token_post(url=url, json={"invoice_numbers": invoice_numbers})
|
|
524
|
+
resp.raise_for_status()
|
|
525
|
+
return resp
|
|
526
|
+
|
|
527
|
+
async def get_freight_v2(self, req: dict) -> Response:
|
|
528
|
+
response = await self.token_post(url="v2/order/freight", json=req)
|
|
529
|
+
response.raise_for_status()
|
|
530
|
+
return response
|
|
531
|
+
|
|
532
|
+
async def get_bol_images(self, order_numbers: list[str]) -> Response:
|
|
533
|
+
response = await self.token_post(url="v1/bol_images", json={"order_numbers": order_numbers})
|
|
534
|
+
return response
|
|
535
|
+
|
|
536
|
+
async def upload_bol_image(self, bol_number: str, order_number: str, note: str, file_name: str, photo_bytes: bytes):
|
|
537
|
+
params = {"bol_number": bol_number, "order_number": order_number, "load_or_drop": "load", "note": note}
|
|
538
|
+
files = {"file": (file_name, photo_bytes)}
|
|
539
|
+
response = await self.token_post(url="v1/order/upload_bol", params=params, files=files)
|
|
540
|
+
return response
|
|
541
|
+
|
|
542
|
+
async def update_order(self, req: OrderUpdateRequest) -> Response:
|
|
543
|
+
response = await self.token_post(url="v1/order/update", json=req.model_dump(mode="json"))
|
|
544
|
+
return response
|
|
545
|
+
|
|
546
|
+
async def assign_carrier(self, req: dict) -> Response:
|
|
547
|
+
response = await self.token_post(url="driver_schedule/assign_carrier_order", json=req)
|
|
548
|
+
return response
|
|
549
|
+
|
|
550
|
+
async def send_to_carrier(self, req: SendToCarrierRequest) -> Response:
|
|
551
|
+
response = await self.token_post(url="order/send_to_carrier", json=req.model_dump(mode="json"))
|
|
552
|
+
return response
|
|
553
|
+
|
|
554
|
+
async def set_order_export_status(self, order_ids: list[str], status: str) -> Response:
|
|
555
|
+
return await self.token_post(
|
|
556
|
+
url="v1/order/set_export_status",
|
|
557
|
+
json=[{
|
|
558
|
+
"order_id": n,
|
|
559
|
+
"status": status
|
|
560
|
+
} for n in order_ids]
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
async def upsert_special_pay_requests(self, req: list[dict]) -> Response:
|
|
564
|
+
return await self.token_post(url="v1/payroll/special_pay_request/upsert_many", json=req)
|
|
565
|
+
|
|
566
|
+
async def all_ratebooks(self):
|
|
567
|
+
params = {"book_type": "Revenue"}
|
|
568
|
+
response = await self.token_post(url="freight/ratebook/overview", params=params)
|
|
569
|
+
response.raise_for_status()
|
|
570
|
+
return response
|
|
571
|
+
|
|
572
|
+
|