karrio-cli 2025.5rc3__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.
- karrio_cli/__init__.py +0 -0
- karrio_cli/__main__.py +105 -0
- karrio_cli/ai/README.md +335 -0
- karrio_cli/ai/__init__.py +0 -0
- karrio_cli/ai/commands.py +102 -0
- karrio_cli/ai/karrio_ai/__init__.py +1 -0
- karrio_cli/ai/karrio_ai/agent.py +972 -0
- karrio_cli/ai/karrio_ai/architecture/INTEGRATION_AGENT_PROMPT.md +497 -0
- karrio_cli/ai/karrio_ai/architecture/MAPPING_AGENT_PROMPT.md +355 -0
- karrio_cli/ai/karrio_ai/architecture/REAL_WORLD_TESTING.md +305 -0
- karrio_cli/ai/karrio_ai/architecture/SCHEMA_AGENT_PROMPT.md +183 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_AGENT_PROMPT.md +448 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_GUIDE.md +271 -0
- karrio_cli/ai/karrio_ai/enhanced_tools.py +943 -0
- karrio_cli/ai/karrio_ai/rag_system.py +503 -0
- karrio_cli/ai/karrio_ai/tests/test_agent.py +350 -0
- karrio_cli/ai/karrio_ai/tests/test_real_integration.py +360 -0
- karrio_cli/ai/karrio_ai/tests/test_real_world_scenarios.py +513 -0
- karrio_cli/commands/__init__.py +0 -0
- karrio_cli/commands/codegen.py +336 -0
- karrio_cli/commands/login.py +139 -0
- karrio_cli/commands/plugins.py +168 -0
- karrio_cli/commands/sdk.py +870 -0
- karrio_cli/common/queries.py +101 -0
- karrio_cli/common/utils.py +368 -0
- karrio_cli/resources/__init__.py +0 -0
- karrio_cli/resources/carriers.py +91 -0
- karrio_cli/resources/connections.py +207 -0
- karrio_cli/resources/events.py +151 -0
- karrio_cli/resources/logs.py +151 -0
- karrio_cli/resources/orders.py +144 -0
- karrio_cli/resources/shipments.py +210 -0
- karrio_cli/resources/trackers.py +287 -0
- karrio_cli/templates/__init__.py +9 -0
- karrio_cli/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-312.pyc +0 -0
- karrio_cli/templates/address.py +308 -0
- karrio_cli/templates/docs.py +150 -0
- karrio_cli/templates/documents.py +428 -0
- karrio_cli/templates/manifest.py +396 -0
- karrio_cli/templates/pickup.py +839 -0
- karrio_cli/templates/rates.py +638 -0
- karrio_cli/templates/sdk.py +947 -0
- karrio_cli/templates/shipments.py +892 -0
- karrio_cli/templates/tracking.py +437 -0
- karrio_cli-2025.5rc3.dist-info/METADATA +165 -0
- karrio_cli-2025.5rc3.dist-info/RECORD +68 -0
- karrio_cli-2025.5rc3.dist-info/WHEEL +5 -0
- karrio_cli-2025.5rc3.dist-info/entry_points.txt +2 -0
- karrio_cli-2025.5rc3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,428 @@
|
|
1
|
+
from jinja2 import Template
|
2
|
+
|
3
|
+
PROVIDER_DOCUMENT_UPLOAD_TEMPLATE = Template(
|
4
|
+
'''"""Karrio {{name}} document upload API implementation."""
|
5
|
+
|
6
|
+
# IMPLEMENTATION INSTRUCTIONS:
|
7
|
+
# 1. Uncomment the imports when the schema types are generated
|
8
|
+
# 2. Import the specific request and response types you need
|
9
|
+
# 3. Create a request instance with the appropriate request type
|
10
|
+
# 4. Extract document details from the response
|
11
|
+
|
12
|
+
import typing
|
13
|
+
import base64
|
14
|
+
import karrio.lib as lib
|
15
|
+
import karrio.core.models as models
|
16
|
+
import karrio.providers.{{id}}.error as error
|
17
|
+
import karrio.providers.{{id}}.utils as provider_utils
|
18
|
+
import karrio.providers.{{id}}.units as provider_units
|
19
|
+
|
20
|
+
|
21
|
+
def parse_document_upload_response(
|
22
|
+
_response: lib.Deserializable[{% if is_xml_api %}lib.Element{% else %}dict{% endif %}],
|
23
|
+
settings: provider_utils.Settings,
|
24
|
+
) -> typing.Tuple[models.DocumentUploadDetails, typing.List[models.Message]]:
|
25
|
+
"""
|
26
|
+
Parse document upload response from carrier API
|
27
|
+
|
28
|
+
_response: The carrier response to deserialize
|
29
|
+
settings: The carrier connection settings
|
30
|
+
|
31
|
+
Returns a tuple with (DocumentUploadDetails, List[Message])
|
32
|
+
"""
|
33
|
+
response = _response.deserialize()
|
34
|
+
messages = error.parse_error_response(response, settings)
|
35
|
+
details = _extract_details(response, settings)
|
36
|
+
|
37
|
+
return details, messages
|
38
|
+
|
39
|
+
|
40
|
+
def _extract_details(
|
41
|
+
data: {% if is_xml_api %}lib.Element{% else %}dict{% endif %},
|
42
|
+
settings: provider_utils.Settings,
|
43
|
+
) -> models.DocumentUploadDetails:
|
44
|
+
"""
|
45
|
+
Extract document upload details from carrier response data
|
46
|
+
|
47
|
+
data: The carrier-specific document upload response data
|
48
|
+
settings: The carrier connection settings
|
49
|
+
|
50
|
+
Returns a DocumentUploadDetails object with the uploaded document information
|
51
|
+
"""
|
52
|
+
{% if is_xml_api %}
|
53
|
+
# Example implementation for XML response:
|
54
|
+
# Extract document IDs and file names from the response
|
55
|
+
# documents = []
|
56
|
+
# doc_elements = lib.find_element("document", data)
|
57
|
+
# for doc_element in doc_elements:
|
58
|
+
# doc_id = lib.find_element("id", doc_element, first=True).text
|
59
|
+
# file_name = lib.find_element("file-name", doc_element, first=True).text
|
60
|
+
# documents.append({"document_id": doc_id, "file_name": file_name})
|
61
|
+
|
62
|
+
# For development, return sample data
|
63
|
+
documents = [{"document_id": "doc_123456", "file_name": "invoice.pdf"}]
|
64
|
+
{% else %}
|
65
|
+
# Example implementation for JSON response:
|
66
|
+
# documents = []
|
67
|
+
# doc_data = data.get("documents", [])
|
68
|
+
# for doc in doc_data:
|
69
|
+
# documents.append({
|
70
|
+
# "document_id": doc.get("id"),
|
71
|
+
# "file_name": doc.get("fileName")
|
72
|
+
# })
|
73
|
+
|
74
|
+
# For development, return sample data
|
75
|
+
documents = [{"document_id": "doc_123456", "file_name": "invoice.pdf"}]
|
76
|
+
{% endif %}
|
77
|
+
|
78
|
+
return models.DocumentUploadDetails(
|
79
|
+
carrier_id=settings.carrier_id,
|
80
|
+
carrier_name=settings.carrier_name,
|
81
|
+
documents=[
|
82
|
+
models.DocumentDetails(
|
83
|
+
document_id=doc["document_id"],
|
84
|
+
file_name=doc["file_name"],
|
85
|
+
)
|
86
|
+
for doc in documents
|
87
|
+
],
|
88
|
+
meta=dict(
|
89
|
+
# Additional carrier-specific metadata
|
90
|
+
upload_timestamp="2023-07-01T12:00:00Z"
|
91
|
+
),
|
92
|
+
)
|
93
|
+
|
94
|
+
|
95
|
+
def document_upload_request(
|
96
|
+
payload: models.DocumentUploadRequest,
|
97
|
+
settings: provider_utils.Settings,
|
98
|
+
) -> lib.Serializable:
|
99
|
+
"""
|
100
|
+
Create a document upload request for the carrier API
|
101
|
+
|
102
|
+
payload: The standardized DocumentUploadRequest from karrio
|
103
|
+
settings: The carrier connection settings
|
104
|
+
|
105
|
+
Returns a Serializable object that can be sent to the carrier API
|
106
|
+
"""
|
107
|
+
# Extract document files from the payload
|
108
|
+
documents = payload.document_files
|
109
|
+
|
110
|
+
{% if is_xml_api %}
|
111
|
+
# Example implementation for XML document upload request:
|
112
|
+
# import karrio.schemas.{{id}}.document_upload_request as {{id}}_req
|
113
|
+
#
|
114
|
+
# doc_list = []
|
115
|
+
# for doc in documents:
|
116
|
+
# doc_list.append({{id}}_req.Document(
|
117
|
+
# file_name=doc.get("doc_name", ""),
|
118
|
+
# file_format=doc.get("doc_format", "PDF"),
|
119
|
+
# document_type=doc.get("doc_type", "invoice"),
|
120
|
+
# file_content=doc.get("doc_file", "")
|
121
|
+
# ))
|
122
|
+
#
|
123
|
+
# request = {{id}}_req.DocumentUploadRequest(
|
124
|
+
# account_number=settings.account_number,
|
125
|
+
# documents=doc_list
|
126
|
+
# )
|
127
|
+
#
|
128
|
+
# return lib.Serializable(
|
129
|
+
# request,
|
130
|
+
# lambda _: lib.to_xml(
|
131
|
+
# _,
|
132
|
+
# name_="DocumentUploadRequest",
|
133
|
+
# namespacedef_='xmlns="http://{{id}}.com/schema/document/upload"'
|
134
|
+
# )
|
135
|
+
# )
|
136
|
+
|
137
|
+
# For development, return a simple XML request
|
138
|
+
document_content = next((doc.get("doc_file", "") for doc in documents), "")
|
139
|
+
document_name = next((doc.get("doc_name", "document.pdf") for doc in documents), "")
|
140
|
+
document_type = next((doc.get("doc_type", "invoice") for doc in documents), "")
|
141
|
+
document_format = next((doc.get("doc_format", "PDF") for doc in documents), "")
|
142
|
+
|
143
|
+
request = f"""<?xml version="1.0"?>
|
144
|
+
<document-upload-request>
|
145
|
+
<account-number>{settings.account_number}</account-number>
|
146
|
+
<document>
|
147
|
+
<file-name>{document_name}</file-name>
|
148
|
+
<file-format>{document_format}</file-format>
|
149
|
+
<document-type>{document_type}</document-type>
|
150
|
+
<file-content>{document_content}</file-content>
|
151
|
+
</document>
|
152
|
+
</document-upload-request>"""
|
153
|
+
|
154
|
+
return lib.Serializable(request, lambda r: r)
|
155
|
+
{% else %}
|
156
|
+
# Example implementation for JSON document upload request:
|
157
|
+
# import karrio.schemas.{{id}}.document_upload_request as {{id}}_req
|
158
|
+
#
|
159
|
+
# doc_list = []
|
160
|
+
# for doc in documents:
|
161
|
+
# doc_list.append({
|
162
|
+
# "fileName": doc.get("doc_name", ""),
|
163
|
+
# "fileFormat": doc.get("doc_format", "PDF"),
|
164
|
+
# "documentType": doc.get("doc_type", "invoice"),
|
165
|
+
# "fileContent": doc.get("doc_file", "")
|
166
|
+
# })
|
167
|
+
#
|
168
|
+
# request = {{id}}_req.DocumentUploadRequestType(
|
169
|
+
# accountNumber=settings.account_number,
|
170
|
+
# documents=doc_list
|
171
|
+
# )
|
172
|
+
#
|
173
|
+
# return lib.Serializable(request, lib.to_dict)
|
174
|
+
|
175
|
+
# For development, return a simple JSON request
|
176
|
+
doc_list = []
|
177
|
+
for doc in documents:
|
178
|
+
doc_list.append({
|
179
|
+
"fileName": doc.get("doc_name", ""),
|
180
|
+
"fileFormat": doc.get("doc_format", "PDF"),
|
181
|
+
"documentType": doc.get("doc_type", "invoice"),
|
182
|
+
"fileContent": doc.get("doc_file", "")
|
183
|
+
})
|
184
|
+
|
185
|
+
request = {
|
186
|
+
"accountNumber": settings.account_number,
|
187
|
+
"documents": doc_list
|
188
|
+
}
|
189
|
+
|
190
|
+
return lib.Serializable(request, lib.to_dict)
|
191
|
+
{% endif %}
|
192
|
+
'''
|
193
|
+
)
|
194
|
+
|
195
|
+
# Provider document upload template
|
196
|
+
PROVIDER_DOCUMENT_UPLOAD_TEMPLATE = Template(
|
197
|
+
'''"""Karrio {{name}} document upload API implementation."""
|
198
|
+
import attr
|
199
|
+
from typing import List, Dict, Any, Optional
|
200
|
+
from base64 import b64encode
|
201
|
+
from karrio.core.models import (
|
202
|
+
Message,
|
203
|
+
CheckoutResponse,
|
204
|
+
DocumentModels,
|
205
|
+
)
|
206
|
+
from karrio.core.errors import UploadDocumentError
|
207
|
+
from karrio.providers.{{id}}.utils import Settings
|
208
|
+
from karrio.providers.{{id}}.error import parse_error_response
|
209
|
+
|
210
|
+
|
211
|
+
def upload_document(
|
212
|
+
documents: List[DocumentModels.Document],
|
213
|
+
settings: Settings,
|
214
|
+
) -> CheckoutResponse:
|
215
|
+
"""Upload documents to carrier's API
|
216
|
+
Parameters:
|
217
|
+
documents: List of document to upload
|
218
|
+
settings: carrier connection settings
|
219
|
+
|
220
|
+
Returns:
|
221
|
+
upload document checkout response
|
222
|
+
"""
|
223
|
+
response = CheckoutResponse(messages=[], documents=[])
|
224
|
+
|
225
|
+
try:
|
226
|
+
# Implement carrier-specific document upload logic here
|
227
|
+
# For example:
|
228
|
+
# client = create_client(settings)
|
229
|
+
# for document in documents:
|
230
|
+
# result = client.upload_document(document)
|
231
|
+
# if result.successful:
|
232
|
+
# response.documents.append(
|
233
|
+
# DocumentModels.Document(
|
234
|
+
# id=result.id,
|
235
|
+
# type=document.type,
|
236
|
+
# name=document.name,
|
237
|
+
# )
|
238
|
+
# )
|
239
|
+
# else:
|
240
|
+
# response.messages.append(Message(code="error", message=result.error))
|
241
|
+
|
242
|
+
# Placeholder implementation
|
243
|
+
for document in documents:
|
244
|
+
reference_id = f"DOC-{document.name[:8]}-{document.type[:3]}"
|
245
|
+
response.documents.append(
|
246
|
+
DocumentModels.Document(
|
247
|
+
id=reference_id,
|
248
|
+
type=document.type,
|
249
|
+
name=document.name,
|
250
|
+
)
|
251
|
+
)
|
252
|
+
response.messages.append(
|
253
|
+
Message(
|
254
|
+
code="success",
|
255
|
+
message=f"Document {document.name} uploaded successfully",
|
256
|
+
details=f"Document ID: {reference_id}",
|
257
|
+
)
|
258
|
+
)
|
259
|
+
|
260
|
+
except Exception as e:
|
261
|
+
error_response = parse_error_response(response, e)
|
262
|
+
response.messages.append(
|
263
|
+
Message(
|
264
|
+
code="upload_document_error",
|
265
|
+
message=str(error_response),
|
266
|
+
carrier_name="{{id}}",
|
267
|
+
carrier_id="{{id}}",
|
268
|
+
)
|
269
|
+
)
|
270
|
+
|
271
|
+
return response
|
272
|
+
'''
|
273
|
+
)
|
274
|
+
|
275
|
+
# Test document upload template
|
276
|
+
TEST_DOCUMENT_UPLOAD_TEMPLATE = Template(
|
277
|
+
'''import unittest
|
278
|
+
from unittest.mock import patch, ANY
|
279
|
+
import karrio.core
|
280
|
+
from karrio.core.models import DocumentModels, CheckoutResponse
|
281
|
+
from .fixture import gateway
|
282
|
+
import logging
|
283
|
+
|
284
|
+
logger = logging.getLogger(__name__)
|
285
|
+
|
286
|
+
class TestDocumentUpload(unittest.TestCase):
|
287
|
+
def setUp(self):
|
288
|
+
self.maxDiff = None
|
289
|
+
self.settings = gateway.settings
|
290
|
+
|
291
|
+
# Test document
|
292
|
+
self.document = DocumentModels.Document(
|
293
|
+
type="COMMERCIAL_INVOICE",
|
294
|
+
name="invoice.pdf",
|
295
|
+
content="SGVsbG8gV29ybGQh",
|
296
|
+
format="PDF",
|
297
|
+
)
|
298
|
+
|
299
|
+
def test_document_upload(self):
|
300
|
+
with patch('karrio.providers.{{id}}.document.upload_document') as mock:
|
301
|
+
mock.return_value = CheckoutResponse(
|
302
|
+
messages=[],
|
303
|
+
documents=[DocumentModels.Document(
|
304
|
+
id="DOC123456",
|
305
|
+
type="COMMERCIAL_INVOICE",
|
306
|
+
name="invoice.pdf",
|
307
|
+
)]
|
308
|
+
)
|
309
|
+
|
310
|
+
# Check the function is called with expected parameters
|
311
|
+
response = karrio.core.checkout.upload_document(
|
312
|
+
documents=[self.document],
|
313
|
+
settings=self.settings
|
314
|
+
)
|
315
|
+
|
316
|
+
mock.assert_called_once_with([self.document], self.settings)
|
317
|
+
self.assertEqual(len(response.documents), 1)
|
318
|
+
'''
|
319
|
+
)
|
320
|
+
|
321
|
+
|
322
|
+
XML_SCHEMA_DOCUMENT_UPLOAD_REQUEST_TEMPLATE = Template("""<?xml version="1.0"?>
|
323
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/document-upload-request" xmlns="http://{{id}}.com/ws/document-upload-request" elementFormDefault="qualified">
|
324
|
+
<xsd:element name="document-upload-request">
|
325
|
+
<xsd:complexType>
|
326
|
+
<xsd:all>
|
327
|
+
<xsd:element name="account-number" type="xsd:string" minOccurs="0" />
|
328
|
+
<xsd:element name="documents">
|
329
|
+
<xsd:complexType>
|
330
|
+
<xsd:sequence>
|
331
|
+
<xsd:element name="document" maxOccurs="unbounded">
|
332
|
+
<xsd:complexType>
|
333
|
+
<xsd:all>
|
334
|
+
<xsd:element name="document-type" type="xsd:string" />
|
335
|
+
<xsd:element name="document-name" type="xsd:string" />
|
336
|
+
<xsd:element name="document-format" type="xsd:string" />
|
337
|
+
<xsd:element name="document-content" type="xsd:base64Binary" />
|
338
|
+
<xsd:element name="reference" type="xsd:string" minOccurs="0" />
|
339
|
+
</xsd:all>
|
340
|
+
</xsd:complexType>
|
341
|
+
</xsd:element>
|
342
|
+
</xsd:sequence>
|
343
|
+
</xsd:complexType>
|
344
|
+
</xsd:element>
|
345
|
+
</xsd:all>
|
346
|
+
</xsd:complexType>
|
347
|
+
</xsd:element>
|
348
|
+
</xsd:schema>
|
349
|
+
"""
|
350
|
+
)
|
351
|
+
|
352
|
+
XML_SCHEMA_DOCUMENT_UPLOAD_RESPONSE_TEMPLATE = Template("""<?xml version="1.0"?>
|
353
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/document-upload-response" xmlns="http://{{id}}.com/ws/document-upload-response" elementFormDefault="qualified">
|
354
|
+
<xsd:element name="document-upload-response">
|
355
|
+
<xsd:complexType>
|
356
|
+
<xsd:all>
|
357
|
+
<xsd:element name="status" type="xsd:string" />
|
358
|
+
<xsd:element name="documents" minOccurs="0">
|
359
|
+
<xsd:complexType>
|
360
|
+
<xsd:sequence>
|
361
|
+
<xsd:element name="document" maxOccurs="unbounded">
|
362
|
+
<xsd:complexType>
|
363
|
+
<xsd:all>
|
364
|
+
<xsd:element name="document-id" type="xsd:string" />
|
365
|
+
<xsd:element name="document-name" type="xsd:string" minOccurs="0" />
|
366
|
+
<xsd:element name="status" type="xsd:string" minOccurs="0" />
|
367
|
+
<xsd:element name="reference" type="xsd:string" minOccurs="0" />
|
368
|
+
</xsd:all>
|
369
|
+
</xsd:complexType>
|
370
|
+
</xsd:element>
|
371
|
+
</xsd:sequence>
|
372
|
+
</xsd:complexType>
|
373
|
+
</xsd:element>
|
374
|
+
</xsd:all>
|
375
|
+
</xsd:complexType>
|
376
|
+
</xsd:element>
|
377
|
+
</xsd:schema>
|
378
|
+
"""
|
379
|
+
)
|
380
|
+
|
381
|
+
# JSON schema templates for document upload operations
|
382
|
+
JSON_SCHEMA_DOCUMENT_UPLOAD_REQUEST_TEMPLATE = Template(
|
383
|
+
"""{
|
384
|
+
"documentUploadRequest": {
|
385
|
+
"accountNumber": "123456",
|
386
|
+
"documents": [
|
387
|
+
{
|
388
|
+
"documentType": "INVOICE",
|
389
|
+
"documentName": "invoice.pdf",
|
390
|
+
"documentFormat": "PDF",
|
391
|
+
"documentContent": "base64_encoded_document_data",
|
392
|
+
"reference": "REF123456"
|
393
|
+
},
|
394
|
+
{
|
395
|
+
"documentType": "COMMERCIAL_INVOICE",
|
396
|
+
"documentName": "commercial_invoice.pdf",
|
397
|
+
"documentFormat": "PDF",
|
398
|
+
"documentContent": "base64_encoded_document_data",
|
399
|
+
"reference": "REF789012"
|
400
|
+
}
|
401
|
+
]
|
402
|
+
}
|
403
|
+
}
|
404
|
+
"""
|
405
|
+
)
|
406
|
+
|
407
|
+
JSON_SCHEMA_DOCUMENT_UPLOAD_RESPONSE_TEMPLATE = Template(
|
408
|
+
"""{
|
409
|
+
"documentUploadResponse": {
|
410
|
+
"status": "completed",
|
411
|
+
"documents": [
|
412
|
+
{
|
413
|
+
"documentId": "DOC123456",
|
414
|
+
"documentName": "invoice.pdf",
|
415
|
+
"status": "uploaded",
|
416
|
+
"reference": "REF123456"
|
417
|
+
},
|
418
|
+
{
|
419
|
+
"documentId": "DOC789012",
|
420
|
+
"documentName": "commercial_invoice.pdf",
|
421
|
+
"status": "uploaded",
|
422
|
+
"reference": "REF789012"
|
423
|
+
}
|
424
|
+
]
|
425
|
+
}
|
426
|
+
}
|
427
|
+
"""
|
428
|
+
)
|