Skip to content

Commit

Permalink
Merge pull request #501 from karrioapi/proof-of-delivery
Browse files Browse the repository at this point in the history
feat: Proof of delivery
  • Loading branch information
danh91 authored Jan 20, 2024
2 parents d906298 + ff1aea4 commit c6b4a38
Show file tree
Hide file tree
Showing 26 changed files with 776 additions and 251 deletions.
2 changes: 1 addition & 1 deletion apps/api/karrio/server/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2023.9.13
2024.2.rc1
10 changes: 10 additions & 0 deletions modules/connectors/ups/karrio/providers/ups/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def _extract_details(
estimated_delivery = next(
iter([d.date for d in package.deliveryDate if d.type == "DEL"]), None
)
signature_image = lib.failsafe(
lambda: getattr(package.deliveryInformation.signature, "image", None)
)
delivery_image = lib.failsafe(
lambda: getattr(package.deliveryInformation.deliveryPhoto, "photo", None)
)
last_event = package.activity[0]
status = next(
(
Expand Down Expand Up @@ -103,6 +109,10 @@ def _extract_details(
destination.attentionName or destination.name if destination else None
),
),
images=models.Images(
delivery_image=delivery_image,
signature_image=signature_image,
),
)


Expand Down
16 changes: 15 additions & 1 deletion modules/connectors/ups/karrio/schemas/ups/tracking_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,25 @@ class DeliveryDateType:
date: Optional[int] = None


@s(auto_attribs=True)
class DeliveryPhotoType:
isNonPostalCodeCountry: Optional[bool] = None
photo: Optional[str] = None
photoCaptureInd: Optional[str] = None
photoDispositionCode: Optional[str] = None


@s(auto_attribs=True)
class SignatureType:
image: Optional[str] = None


@s(auto_attribs=True)
class DeliveryInformationType:
location: Optional[str] = None
receivedBy: Optional[int] = None
signature: Optional[str] = None
signature: Optional[SignatureType] = JStruct[SignatureType]
deliveryPhoto: Optional[DeliveryPhotoType] = JStruct[DeliveryPhotoType]


@s(auto_attribs=True)
Expand Down
10 changes: 9 additions & 1 deletion modules/connectors/ups/schemas/tracking_response.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,15 @@
"deliveryInformation": {
"location": "DEL",
"receivedBy": "163000",
"signature": "xxxxxxx"
"signature": {
"image": "encoding Base64"
},
"deliveryPhoto": {
"isNonPostalCodeCountry": false,
"photo": "encoding Base64",
"photoCaptureInd": "string",
"photoDispositionCode": "string"
}
},
"deliveryTime": {
"type": "DEL",
Expand Down
15 changes: 13 additions & 2 deletions modules/connectors/ups/tests/ups/test_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ def test_tracking_number_not_found_response_parsing(self):
"carrier_id": "ups",
"carrier_name": "ups",
"delivered": False,
"images": {
"delivery_image": "encoding Base64",
"signature_image": "encoding Base64",
},
"events": [
{
"code": "SR",
Expand All @@ -103,6 +107,7 @@ def test_tracking_number_not_found_response_parsing(self):
"package_weight": "string",
"package_weight_unit": "string",
"shipment_service": "UPS Ground",
"signed_by": "163000",
},
"status": "on_hold",
"tracking_number": "string",
Expand Down Expand Up @@ -223,10 +228,16 @@ def test_tracking_number_not_found_response_parsing(self):
}
],
"deliveryInformation": {
"location": "Front Door",
"receivedBy": "",
"location": "DEL",
"receivedBy": "163000",
"signature": {
"image": "encoding Base64"
},
"deliveryPhoto": {
"isNonPostalCodeCountry": false,
"photo": "encoding Base64",
"photoCaptureInd": "string",
"photoDispositionCode": "string"
}
},
"deliveryTime": {
Expand Down
4 changes: 3 additions & 1 deletion modules/core/karrio/server/core/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from karrio.core.models import (
DocumentDetails,
Documents,
Images,
Parcel,
Message,
Address as BaseAddress,
Expand Down Expand Up @@ -272,7 +273,8 @@ class Tracking:
events: typing.List[TrackingEvent] = jstruct.JList[TrackingEvent]

status: str = "unknown"
info: TrackingInfo = None
info: TrackingInfo = jstruct.JStruct[TrackingInfo]
images: Images = jstruct.JStruct[Images]
estimated_delivery: str = None
delivered: bool = None
test_mode: bool = None
Expand Down
70 changes: 53 additions & 17 deletions modules/core/karrio/server/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,21 @@ class PickupCancelRequest(serializers.Serializer):
)


class Images(serializers.Serializer):
delivery_image = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text="A delivery image in base64 string",
)
signature_image = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text="A signature image in base64 string",
)


class TrackingEvent(serializers.Serializer):
date = serializers.CharField(required=False, help_text="The tracking event's date")
description = serializers.CharField(
Expand Down Expand Up @@ -1049,9 +1064,14 @@ class TrackingDetails(serializers.Serializer):
meta = serializers.PlainDictField(
required=False, allow_null=True, help_text="provider specific metadata"
)
images = Images(
required=False,
allow_null=True,
help_text="The tracker documents",
)


class TrackingStatus(serializers.EntitySerializer, TrackingDetails):
class TrackerDetails(serializers.EntitySerializer, TrackingDetails):
object_type = serializers.CharField(
default="tracker", help_text="Specifies the object type"
)
Expand All @@ -1066,6 +1086,37 @@ class TrackingStatus(serializers.EntitySerializer, TrackingDetails):
)


class TrackingStatus(TrackerDetails):
images = None
delivery_image_url = serializers.URLField(
required=False,
allow_blank=True,
allow_null=True,
help_text="The shipment invoice URL",
)
signature_image_url = serializers.URLField(
required=False,
allow_blank=True,
allow_null=True,
help_text="The shipment invoice URL",
)


class Documents(serializers.Serializer):
label = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text="A shipping label in base64 string",
)
invoice = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text="A shipping invoice in base64 string",
)


class Rate(serializers.EntitySerializer):
object_type = serializers.CharField(
default="rate", help_text="Specifies the object type"
Expand Down Expand Up @@ -1242,21 +1293,6 @@ class ShipmentData(ShippingData):
)


class Documents(serializers.Serializer):
label = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text="The shipment label in base64 string",
)
invoice = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text="The shipment invoice in base64 string",
)


class ShipmentDetails(serializers.Serializer):
status = serializers.ChoiceField(
required=False,
Expand Down Expand Up @@ -1534,7 +1570,7 @@ class TrackingResponse(serializers.Serializer):
messages = Message(
required=False, many=True, help_text="The list of note or warning messages"
)
tracking = TrackingStatus(
tracking = TrackerDetails(
required=False, help_text="The tracking details retrieved"
)

Expand Down
13 changes: 13 additions & 0 deletions modules/core/karrio/server/core/tests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import logging
from rest_framework import status
from django.contrib.auth import get_user_model
from rest_framework.test import APITestCase as BaseAPITestCase, APIClient

from karrio.server.providers.models import MODELS
from karrio.server.user.models import Token

logger = logging.getLogger(__name__)


class APITestCase(BaseAPITestCase):
def setUp(self) -> None:
Expand Down Expand Up @@ -51,3 +55,12 @@ def setUp(self) -> None:
consumer_key="test",
consumer_secret="password",
)

def assertResponseNoErrors(self, response):
is_ok = f"{response.status_code}".startswith("2")

if is_ok is False or response.data.get("errors") is not None:
print(response.data)

self.assertTrue(is_ok)
assert response.data.get("errors") is None
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,23 @@ def save_updated_trackers(
tracker.estimated_delivery = details.estimated_delivery
changes.append("estimated_delivery")

if details.images is not None and (
details.images.delivery_image != tracker.delivery_image
or details.images.signature_image != tracker.signature_image
):
changes.append("delivery_image")
changes.append("signature_image")
tracker.delivery_image = (
details.images.delivery_image or tracker.delivery_image
)
tracker.signature_image = (
details.images.signature_image or tracker.signature_image
)

if any(info.keys()) and info != tracker.info:
tracker.info = serializers.process_dictionaries_mutations(
["info"], dict(info=info), tracker
)['info']
)["info"]
changes.append("info")

if any(changes):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ def test_get_updated_trackers(self):
"carrier_name": "dhl_universal",
"carrier_id": "dhl_universal",
"tracking_number": "00340434292135100124",
"delivery_image_url": None,
"signature_image_url": None,
"estimated_delivery": ANY,
"events": [
{
Expand All @@ -169,6 +171,8 @@ def test_get_updated_trackers(self):
"carrier_name": "ups",
"carrier_id": "ups_package",
"tracking_number": "1Z12345E6205277936",
"delivery_image_url": None,
"signature_image_url": None,
"estimated_delivery": ANY,
"events": [
{
Expand Down Expand Up @@ -262,6 +266,8 @@ def test_get_updated_trackers(self):
"test_mode": True,
"status": "in_transit",
"estimated_delivery": ANY,
"delivery_image_url": None,
"signature_image_url": None,
"messages": [],
"meta": {},
"metadata": {},
Expand Down Expand Up @@ -308,6 +314,8 @@ def test_get_updated_trackers(self):
"test_mode": True,
"status": "in_transit",
"estimated_delivery": ANY,
"delivery_image_url": None,
"signature_image_url": None,
"messages": [],
"meta": {},
"metadata": {},
Expand Down
2 changes: 2 additions & 0 deletions modules/graph/karrio/server/graph/schemas/base/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,8 @@ class TrackerType:
status: utils.TrackerStatusEnum
delivered: typing.Optional[bool]
estimated_delivery: typing.Optional[datetime.date]
document_image_url: typing.Optional[str]
signature_image_url: typing.Optional[str]
options: typing.Optional[utils.JSON]
meta: typing.Optional[utils.JSON]
shipment: typing.Optional["ShipmentType"]
Expand Down
2 changes: 1 addition & 1 deletion modules/graph/karrio/server/graph/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import logging
import dataclasses
from django.urls import reverse
from django.contrib.auth import get_user_model
from rest_framework import status
from django.contrib.auth import get_user_model
from rest_framework.test import APITestCase as BaseAPITestCase, APIClient

from karrio.server.providers.models import MODELS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.8 on 2024-01-20 16:00

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("manager", "0055_alter_tracking_status"),
]

operations = [
migrations.AddField(
model_name="tracking",
name="delivery_image",
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name="tracking",
name="signature_image",
field=models.TextField(blank=True, null=True),
),
]
Loading

0 comments on commit c6b4a38

Please sign in to comment.