karrio-server-data 2025.5rc1__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.
Files changed (43) hide show
  1. karrio/server/data/__init__.py +0 -0
  2. karrio/server/data/admin.py +1 -0
  3. karrio/server/data/apps.py +13 -0
  4. karrio/server/data/filters.py +43 -0
  5. karrio/server/data/migrations/0001_initial.py +62 -0
  6. karrio/server/data/migrations/0002_alter_batchoperation_resource_type_and_more.py +28 -0
  7. karrio/server/data/migrations/0003_datatemplate_metadata_alter_batchoperation_resources.py +36 -0
  8. karrio/server/data/migrations/__init__.py +0 -0
  9. karrio/server/data/models.py +97 -0
  10. karrio/server/data/resources/__init__.py +53 -0
  11. karrio/server/data/resources/orders.py +523 -0
  12. karrio/server/data/resources/shipments.py +473 -0
  13. karrio/server/data/resources/trackers.py +212 -0
  14. karrio/server/data/serializers/__init__.py +26 -0
  15. karrio/server/data/serializers/base.py +107 -0
  16. karrio/server/data/serializers/batch.py +9 -0
  17. karrio/server/data/serializers/batch_orders.py +99 -0
  18. karrio/server/data/serializers/batch_shipments.py +102 -0
  19. karrio/server/data/serializers/batch_trackers.py +131 -0
  20. karrio/server/data/serializers/data.py +109 -0
  21. karrio/server/data/signals.py +52 -0
  22. karrio/server/data/tests.py +3 -0
  23. karrio/server/data/urls.py +13 -0
  24. karrio/server/data/views/__init__.py +0 -0
  25. karrio/server/data/views/batch.py +72 -0
  26. karrio/server/data/views/batch_order.py +40 -0
  27. karrio/server/data/views/batch_shipment.py +40 -0
  28. karrio/server/data/views/batch_tracking.py +40 -0
  29. karrio/server/data/views/data.py +171 -0
  30. karrio/server/events/task_definitions/__init__.py +1 -0
  31. karrio/server/events/task_definitions/data/__init__.py +136 -0
  32. karrio/server/events/task_definitions/data/batch.py +130 -0
  33. karrio/server/events/task_definitions/data/shipments.py +51 -0
  34. karrio/server/graph/schemas/__init__.py +1 -0
  35. karrio/server/graph/schemas/data/__init__.py +51 -0
  36. karrio/server/graph/schemas/data/inputs.py +39 -0
  37. karrio/server/graph/schemas/data/mutations.py +53 -0
  38. karrio/server/graph/schemas/data/types.py +78 -0
  39. karrio/server/settings/data.py +15 -0
  40. karrio_server_data-2025.5rc1.dist-info/METADATA +18 -0
  41. karrio_server_data-2025.5rc1.dist-info/RECORD +43 -0
  42. karrio_server_data-2025.5rc1.dist-info/WHEEL +5 -0
  43. karrio_server_data-2025.5rc1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,523 @@
1
+ from django.db.models import Q
2
+ from import_export import resources
3
+
4
+ import karrio.lib as lib
5
+ from karrio.server.orders.serializers.order import OrderSerializer
6
+ from karrio.server.orders.filters import OrderFilters
7
+ from karrio.server.orders import models
8
+
9
+ DEFAULT_HEADERS = {
10
+ # Order details
11
+ "id": "ID",
12
+ "order_id": "Order number",
13
+ "order_date": "Order date",
14
+ "order_source": "Order source",
15
+ "order_status": "Status",
16
+ "order_total": "Total",
17
+ "order_currency": "Order Currency",
18
+ "order_created_at": "Created at",
19
+ # Item details
20
+ "title": "Item title",
21
+ "description": "Item Description",
22
+ "quantity": "Item quantity",
23
+ "sku": "Item sku",
24
+ "hs_code": "HS tariff number",
25
+ "value_amount": "Item price",
26
+ "value_currency": "Item currency",
27
+ "weight": "Item weight",
28
+ "weight_unit": "Item weight unit",
29
+ # Shipping details
30
+ "shipping_to_name": "Shipping name",
31
+ "shipping_to_company": "Shipping company",
32
+ "shipping_to_address1": "Shipping address 1",
33
+ "shipping_to_address2": "Shipping address 2",
34
+ "shipping_to_city": "Shipping city",
35
+ "shipping_to_state": "Shipping state",
36
+ "shipping_to_postal_code": "Shipping postal code",
37
+ "shipping_to_country": "Shipping country",
38
+ "shipping_to_residential": "Shipping residential",
39
+ # Billing details
40
+ "shipping_from_name": "From name",
41
+ "shipping_from_company": "From company",
42
+ "shipping_from_address1": "From address 1",
43
+ "shipping_from_address2": "From address 2",
44
+ "shipping_from_city": "From city",
45
+ "shipping_from_state": "From state",
46
+ "shipping_from_postal_code": "From postal code",
47
+ "shipping_from_country": "From country",
48
+ "shipping_from_residential": "From residential",
49
+ # Billing details
50
+ "billing_name": "Billing name",
51
+ "billing_company": "Billing company",
52
+ "billing_address1": "Billing address 1",
53
+ "billing_address2": "Billing address 2",
54
+ "billing_city": "Billing city",
55
+ "billing_state": "Billing state",
56
+ "billing_postal_code": "Billing postal code",
57
+ "billing_country": "Billing country",
58
+ # extra
59
+ "metadata": "Item Metadata",
60
+ "options": "Options",
61
+ }
62
+
63
+
64
+ def order_export_resource(query_params: dict, context, **kwargs):
65
+ queryset = models.LineItem.access_by(context)
66
+ _exclude = query_params.get("exclude", "").split(",")
67
+ _fields = (
68
+ "description",
69
+ "quantity",
70
+ "weight",
71
+ "weight_unit",
72
+ "sku",
73
+ "hs_code",
74
+ "value_amount",
75
+ "value_currency",
76
+ "metadata",
77
+ )
78
+
79
+ if "status" not in query_params:
80
+ queryset = queryset.filter(
81
+ Q(commodity_order__status__in=["fulfilled", "delivered"]),
82
+ )
83
+
84
+ class Resource(resources.ModelResource):
85
+ class Meta:
86
+ model = models.LineItem
87
+ fields = _fields
88
+ exclude = _exclude
89
+ export_order = [k for k in DEFAULT_HEADERS.keys() if k not in _exclude]
90
+
91
+ def get_queryset(self):
92
+ orders = OrderFilters(query_params, models.Order.access_by(context)).qs
93
+ return queryset.filter(commodity_order__in=orders)
94
+
95
+ def get_export_headers(self):
96
+ headers = super().get_export_headers()
97
+ return [DEFAULT_HEADERS.get(k, k) for k in headers]
98
+
99
+ if "id" not in _exclude:
100
+ id = resources.Field()
101
+
102
+ def dehydrate_id(self, row):
103
+ return row.order.id
104
+
105
+ if "order_id" not in _exclude:
106
+ order_id = resources.Field()
107
+
108
+ def dehydrate_order_id(self, row):
109
+ return row.order.order_id
110
+
111
+ if "order_date" not in _exclude:
112
+ order_date = resources.Field()
113
+
114
+ def dehydrate_order_date(self, row):
115
+ return row.order.order_date
116
+
117
+ if "order_status" not in _exclude:
118
+ order_status = resources.Field()
119
+
120
+ def dehydrate_order_status(self, row):
121
+ return row.order.status
122
+
123
+ if "order_source" not in _exclude:
124
+ order_source = resources.Field()
125
+
126
+ def dehydrate_order_source(self, row):
127
+ return row.order.source
128
+
129
+ if "order_created_at" not in _exclude:
130
+ order_created_at = resources.Field()
131
+
132
+ def dehydrate_order_created_at(self, row):
133
+ return row.order.created_at
134
+
135
+ if "order_currency" not in _exclude:
136
+ order_currency = resources.Field()
137
+
138
+ def dehydrate_order_currency(self, row):
139
+ return row.value_currency
140
+
141
+ if "order_total" not in _exclude:
142
+ order_total = resources.Field()
143
+
144
+ def dehydrate_order_total(self, row):
145
+ return sum(
146
+ [
147
+ lib.to_decimal(li.value_amount) or 0.0
148
+ for li in row.order.line_items.all()
149
+ ],
150
+ 0.0,
151
+ )
152
+
153
+ if "options" not in _exclude:
154
+ options = resources.Field()
155
+
156
+ def dehydrate_options(self, row):
157
+ return row.order.options
158
+
159
+ if "shipping_to_name" not in _exclude:
160
+ shipping_to_name = resources.Field()
161
+
162
+ def dehydrate_shipping_to_name(self, row):
163
+ return row.order.shipping_to.person_name
164
+
165
+ if "shipping_to_company" not in _exclude:
166
+ shipping_to_company = resources.Field()
167
+
168
+ def dehydrate_shipping_to_company(self, row):
169
+ return row.order.shipping_to.company_name
170
+
171
+ if "shipping_to_address1" not in _exclude:
172
+ shipping_to_address1 = resources.Field()
173
+
174
+ def dehydrate_shipping_to_address1(self, row):
175
+ return row.order.shipping_to.address_line1
176
+
177
+ if "shipping_to_address2" not in _exclude:
178
+ shipping_to_address2 = resources.Field()
179
+
180
+ def dehydrate_shipping_to_address2(self, row):
181
+ return row.order.shipping_to.address_line2
182
+
183
+ if "shipping_to_city" not in _exclude:
184
+ shipping_to_city = resources.Field()
185
+
186
+ def dehydrate_shipping_to_city(self, row):
187
+ return row.order.shipping_to.city
188
+
189
+ if "shipping_to_state" not in _exclude:
190
+ shipping_to_state = resources.Field()
191
+
192
+ def dehydrate_shipping_to_state(self, row):
193
+ return row.order.shipping_to.state_code
194
+
195
+ if "shipping_to_postal_code" not in _exclude:
196
+ shipping_to_postal_code = resources.Field()
197
+
198
+ def dehydrate_shipping_to_postal_code(self, row):
199
+ return row.order.shipping_to.postal_code
200
+
201
+ if "shipping_to_country" not in _exclude:
202
+ shipping_to_country = resources.Field()
203
+
204
+ def dehydrate_shipping_to_country(self, row):
205
+ return row.order.shipping_to.country_code
206
+
207
+ if "shipping_to_residential" not in _exclude:
208
+ shipping_to_residential = resources.Field()
209
+
210
+ def dehydrate_shipping_to_residential(self, row):
211
+ return "yes" if row.order.shipping_to.residential else "no"
212
+
213
+ if "shipping_from_name" not in _exclude:
214
+ shipping_from_name = resources.Field()
215
+
216
+ def dehydrate_shipping_from_name(self, row):
217
+ return getattr(row.order.shipping_from, "person_name", None)
218
+
219
+ if "shipping_from_company" not in _exclude:
220
+ shipping_from_company = resources.Field()
221
+
222
+ def dehydrate_shipping_from_company(self, row):
223
+ return getattr(row.order.shipping_from, "company_name", None)
224
+
225
+ if "shipping_from_address1" not in _exclude:
226
+ shipping_from_address1 = resources.Field()
227
+
228
+ def dehydrate_shipping_from_address1(self, row):
229
+ return getattr(row.order.shipping_from, "address_line1", None)
230
+
231
+ if "shipping_from_address2" not in _exclude:
232
+ shipping_from_address2 = resources.Field()
233
+
234
+ def dehydrate_shipping_from_address2(self, row):
235
+ return getattr(row.order.shipping_from, "address_line2", None)
236
+
237
+ if "shipping_from_city" not in _exclude:
238
+ shipping_from_city = resources.Field()
239
+
240
+ def dehydrate_shipping_from_city(self, row):
241
+ return getattr(row.order.shipping_from, "city", None)
242
+
243
+ if "shipping_from_state" not in _exclude:
244
+ shipping_from_state = resources.Field()
245
+
246
+ def dehydrate_shipping_from_state(self, row):
247
+ return getattr(row.order.shipping_from, "state_code", None)
248
+
249
+ if "shipping_from_postal_code" not in _exclude:
250
+ shipping_from_postal_code = resources.Field()
251
+
252
+ def dehydrate_shipping_from_postal_code(self, row):
253
+ return getattr(row.order.shipping_from, "postal_code", None)
254
+
255
+ if "shipping_from_country" not in _exclude:
256
+ shipping_from_country = resources.Field()
257
+
258
+ def dehydrate_shipping_from_country(self, row):
259
+ return getattr(row.order.shipping_from, "country_code", None)
260
+
261
+ if "shipping_from_residential" not in _exclude:
262
+ shipping_from_residential = resources.Field()
263
+
264
+ def dehydrate_shipping_from_residential(self, row):
265
+ if getattr(row.order.shipping_from, "country_code", None) is None:
266
+ return None
267
+
268
+ return "yes" if row.order.shipping_from.residential else "no"
269
+
270
+ if "billing_name" not in _exclude:
271
+ billing_name = resources.Field()
272
+
273
+ def dehydrate_billing_name(self, row):
274
+ return getattr(row.order.billing_address, "person_name", None)
275
+
276
+ if "billing_company" not in _exclude:
277
+ billing_company = resources.Field()
278
+
279
+ def dehydrate_billing_company(self, row):
280
+ return getattr(row.order.billing_address, "company_name", None)
281
+
282
+ if "billing_address1" not in _exclude:
283
+ billing_address1 = resources.Field()
284
+
285
+ def dehydrate_billing_address1(self, row):
286
+ return getattr(row.order.billing_address, "address_line1", None)
287
+
288
+ if "billing_address2" not in _exclude:
289
+ billing_address2 = resources.Field()
290
+
291
+ def dehydrate_billing_address2(self, row):
292
+ return getattr(row.order.billing_address, "address_line2", None)
293
+
294
+ if "billing_city" not in _exclude:
295
+ billing_city = resources.Field()
296
+
297
+ def dehydrate_billing_city(self, row):
298
+ return getattr(row.order.billing_address, "city", None)
299
+
300
+ if "billing_state" not in _exclude:
301
+ billing_state = resources.Field()
302
+
303
+ def dehydrate_billing_state(self, row):
304
+ return getattr(row.order.billing_address, "state_code", None)
305
+
306
+ if "billing_postal_code" not in _exclude:
307
+ billing_postal_code = resources.Field()
308
+
309
+ def dehydrate_billing_postal_code(self, row):
310
+ return getattr(row.order.billing_address, "postal_code", None)
311
+
312
+ if "billing_country" not in _exclude:
313
+ billing_country = resources.Field()
314
+
315
+ def dehydrate_billing_country(self, row):
316
+ return getattr(row.order.billing_address, "country_code", None)
317
+
318
+ return Resource()
319
+
320
+
321
+ def order_import_resource(
322
+ query_params: dict,
323
+ context,
324
+ data_fields: dict = None,
325
+ batch_id: str = None,
326
+ **kwargs
327
+ ):
328
+ queryset = models.Order.access_by(context)
329
+ field_headers = data_fields if data_fields is not None else DEFAULT_HEADERS
330
+ _exclude = query_params.get("exclude", "").split(",")
331
+ _fields = (
332
+ "id",
333
+ "order_id",
334
+ "order_date",
335
+ "order_source",
336
+ "order_status",
337
+ "options",
338
+ "description",
339
+ "quantity",
340
+ "sku",
341
+ "hs_code",
342
+ "value_amount",
343
+ "value_currency",
344
+ "weight",
345
+ "weight_unit",
346
+ "metadata",
347
+ "shipping_to_name",
348
+ "shipping_to_company",
349
+ "shipping_to_address1",
350
+ "shipping_to_address2",
351
+ "shipping_to_city",
352
+ "shipping_to_state",
353
+ "shipping_to_postal_code",
354
+ "shipping_to_country",
355
+ "shipping_to_residential",
356
+ "shipping_from_name",
357
+ "shipping_from_company",
358
+ "shipping_from_address1",
359
+ "shipping_from_address2",
360
+ "shipping_from_city",
361
+ "shipping_from_state",
362
+ "shipping_from_postal_code",
363
+ "shipping_from_country",
364
+ "shipping_from_residential",
365
+ "billing_name",
366
+ "billing_company",
367
+ "billing_address1",
368
+ "billing_address2",
369
+ "billing_city",
370
+ "billing_state",
371
+ "billing_postal_code",
372
+ "billing_country",
373
+ )
374
+
375
+ _Base = type(
376
+ "ResourceFields",
377
+ (resources.ModelResource,),
378
+ {
379
+ k: resources.Field(readonly=(k not in models.Order.__dict__))
380
+ for k in field_headers.keys()
381
+ if k not in _exclude
382
+ },
383
+ )
384
+
385
+ class Resource(_Base, resources.ModelResource):
386
+ class Meta:
387
+ model = models.Order
388
+ fields = _fields
389
+ exclude = _exclude
390
+ export_order = [k for k in field_headers.keys() if k not in _exclude]
391
+ force_init_instance = True
392
+
393
+ def get_queryset(self):
394
+ return queryset
395
+
396
+ def get_export_headers(self):
397
+ headers = super().get_export_headers()
398
+ return [field_headers.get(k, k) for k in headers]
399
+
400
+ def init_instance(self, row=None):
401
+ order_id = row.get(field_headers["order_id"])
402
+ source = row.get(field_headers["order_source"])
403
+ meta = {} if batch_id is None else dict(meta=dict(batch_ids=[batch_id]))
404
+ queryset = models.Order.access_by(context).filter(
405
+ order_id=order_id, source=source
406
+ )
407
+
408
+ if queryset.exists():
409
+ _order = queryset.first()
410
+ _order.meta = {
411
+ **(_order.meta or {}),
412
+ "batch_ids": [
413
+ *(meta.get("batch_ids") or []),
414
+ *((_order.meta or {}).get("batch_ids") or []),
415
+ ],
416
+ }
417
+ _order.save()
418
+
419
+ instance = _order
420
+
421
+ else:
422
+ _data = lib.to_dict(
423
+ dict(
424
+ source=source,
425
+ order_id=order_id,
426
+ status="unfulfilled",
427
+ test_mode=context.test_mode,
428
+ created_by_id=context.user.id,
429
+ order_date=row.get(field_headers["order_date"]),
430
+ options=lib.to_dict(row.get(field_headers["options"]) or "{}"),
431
+ shipping_to=dict(
432
+ person_name=row.get(field_headers["shipping_to_name"]),
433
+ company_name=row.get(field_headers["shipping_to_company"]),
434
+ address_line1=row.get(
435
+ field_headers["shipping_to_address1"]
436
+ ),
437
+ address_line2=row.get(
438
+ field_headers["shipping_to_address2"]
439
+ ),
440
+ city=row.get(field_headers["shipping_to_city"]),
441
+ state_code=row.get(field_headers["shipping_to_state"]),
442
+ postal_code=row.get(
443
+ field_headers["shipping_to_postal_code"]
444
+ ),
445
+ country_code=row.get(field_headers["shipping_to_country"]),
446
+ residential=row.get(
447
+ field_headers["shipping_to_residential"]
448
+ ),
449
+ ),
450
+ line_items=[
451
+ dict(
452
+ description=row.get(field_headers["description"]),
453
+ quantity=row.get(field_headers["quantity"]),
454
+ sku=row.get(field_headers["sku"]),
455
+ hs_code=row.get(field_headers["hs_code"]),
456
+ value_amount=row.get(field_headers["value_amount"]),
457
+ value_currency=row.get(field_headers["value_currency"]),
458
+ weight=row.get(field_headers["weight"]),
459
+ weight_unit=row.get(field_headers["weight_unit"]),
460
+ )
461
+ ],
462
+ shipping_from=(
463
+ dict(
464
+ person_name=row.get(
465
+ field_headers["shipping_from_name"]
466
+ ),
467
+ company_name=row.get(
468
+ field_headers["shipping_from_company"]
469
+ ),
470
+ address_line1=row.get(
471
+ field_headers["shipping_from_address1"]
472
+ ),
473
+ address_line2=row.get(
474
+ field_headers["shipping_from_address2"]
475
+ ),
476
+ city=row.get(field_headers["shipping_from_city"]),
477
+ state_code=row.get(
478
+ field_headers["shipping_from_state"]
479
+ ),
480
+ postal_code=row.get(
481
+ field_headers["shipping_from_postal_code"]
482
+ ),
483
+ country_code=row.get(
484
+ field_headers["shipping_from_country"]
485
+ ),
486
+ residential=row.get(
487
+ field_headers["shipping_from_residential"]
488
+ ),
489
+ )
490
+ if any(["From" in key for key in row.keys()])
491
+ else None
492
+ ),
493
+ billing_address=(
494
+ dict(
495
+ person_name=row.get(field_headers["billing_name"]),
496
+ company_name=row.get(field_headers["billing_company"]),
497
+ address_line1=row.get(
498
+ field_headers["billing_address1"]
499
+ ),
500
+ address_line2=row.get(
501
+ field_headers["billing_address2"]
502
+ ),
503
+ city=row.get(field_headers["billing_city"]),
504
+ state_code=row.get(field_headers["billing_state"]),
505
+ postal_code=row.get(
506
+ field_headers["billing_postal_code"]
507
+ ),
508
+ country_code=row.get(field_headers["billing_country"]),
509
+ )
510
+ if any(["Billing" in key for key in row.keys()])
511
+ else None
512
+ ),
513
+ **meta,
514
+ )
515
+ )
516
+
517
+ instance = (
518
+ OrderSerializer.map(data=_data, context=context).save().instance
519
+ )
520
+
521
+ return instance
522
+
523
+ return Resource()