def post(self, request: Request, pk: str):
        """
        Select your preferred rates to buy a shipment label.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException(
                f"The shipment is '{shipment.status}' and therefore already {ShipmentStatus.purchased.value}",
                code='state_error',
                status_code=status.HTTP_409_CONFLICT)

        payload = {
            **Shipment(shipment).data,
            **SerializerDecorator[ShipmentPurchaseData](data=request.data).data
        }

        # Submit shipment to carriers
        response: Shipment = SerializerDecorator[ShipmentValidationData](
            data=payload).save(user=request.user).instance

        # Update shipment state
        SerializerDecorator[ShipmentSerializer](
            shipment, data=DP.to_dict(response)).save()

        return Response(Shipment(shipment).data)
class ShipmentRates(APIView):
    logging_methods = ['GET']

    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}rates",
                         operation_summary="Fetch new shipment rates",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         })
    def get(self, request: Request, pk: str):
        """
        Refresh the list of the shipment rates
        """
        shipment = request.user.shipment_set.get(pk=pk)

        rate_response: RateResponse = SerializerDecorator[RateSerializer](
            data=ShipmentData(shipment).data).save(user=shipment.user).instance
        payload: dict = DP.to_dict(
            dict(rates=Rate(rate_response.rates, many=True).data,
                 messages=Message(rate_response.messages, many=True).data,
                 selected_rate=None))

        SerializerDecorator[ShipmentSerializer](shipment, data=payload).save()

        return Response(Shipment(shipment).data)
class ShipmentParcels(APIView):
    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}add_parcel",
                         operation_summary="Add a shipment parcel",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         },
                         request_body=ParcelData())
    def post(self, request: Request, pk: str):
        """
        Add a parcel to an existing shipment for a multi-parcel shipment.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException("Shipment already 'purchased'",
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        parcel = SerializerDecorator[ParcelSerializer](data=request.data).save(
            user=request.user).instance
        shipment.shipment_parcels.add(parcel)
        reset_related_shipment_rates(shipment)
        return Response(Shipment(shipment).data)
    def get(self, request: Request, pk: str):
        """
        Retrieve a shipment.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        return Response(Shipment(shipment).data)
class ShipmentCustoms(APIView):
    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}add_customs",
                         operation_summary="Add a customs declaration",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         },
                         request_body=CustomsData())
    def post(self, request: Request, pk: str):
        """
        Add the customs declaration for the shipment if non existent.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException("Shipment already 'purchased'",
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        if shipment.customs is not None:
            raise PurplShipApiException(
                "Shipment customs declaration already defined",
                code='state_error',
                status_code=status.HTTP_409_CONFLICT)

        SerializerDecorator[ShipmentSerializer](
            shipment, data=dict(customs=request.data)).save()
        reset_related_shipment_rates(shipment)
        return Response(Shipment(shipment).data)
Example #6
0
class ShippingList(APIView):

    @swagger_auto_schema(
        tags=['Proxy'],
        operation_id=f"{ENDPOINT_ID}buy_label",
        operation_summary="Buy a shipment label",
        request_body=ShippingRequest(),
        responses={200: Shipment(), 400: ErrorResponse()},
    )
    def post(self, request: Request):
        """
        Once the shipping rates are retrieved, provide the required info to
        submit the shipment by specifying your preferred rate.
        """
        payload = SerializerDecorator[ShippingRequestValidation](data=request.data).data

        response = Shipments.create(
            payload,
            resolve_tracking_url=(
                lambda shipment: reverse(
                    "purpleserver.proxy:shipment-tracking",
                    kwargs=dict(tracking_number=shipment.tracking_number, carrier_name=shipment.carrier_name)
                )
            )
        )

        return Response(Shipment(response).data, status=status.HTTP_201_CREATED)
class ShipmentPurchase(APIView):
    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}purchase",
                         operation_summary="Buy a shipment label",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         },
                         request_body=ShipmentPurchaseData())
    def post(self, request: Request, pk: str):
        """
        Select your preferred rates to buy a shipment label.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException(
                f"The shipment is '{shipment.status}' and therefore already {ShipmentStatus.purchased.value}",
                code='state_error',
                status_code=status.HTTP_409_CONFLICT)

        payload = {
            **Shipment(shipment).data,
            **SerializerDecorator[ShipmentPurchaseData](data=request.data).data
        }

        # Submit shipment to carriers
        response: Shipment = SerializerDecorator[ShipmentValidationData](
            data=payload).save(user=request.user).instance

        # Update shipment state
        SerializerDecorator[ShipmentSerializer](
            shipment, data=DP.to_dict(response)).save()

        return Response(Shipment(shipment).data)
    def post(self, request: Request, pk: str):
        """
        Add one or many options to your shipment.<br/>
        **eg:**<br/>
        - add shipment **insurance**
        - specify the preferred transaction **currency**
        - setup a **cash collected on delivery** option

        ```json
        {
            "insurance": 120,
            "currency": "USD"
        }
        ```

        And many more, check additional options available in the [reference](#operation/all_references).
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException("Shipment already 'purchased'",
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        payload: dict = DP.to_dict(dict(options=request.data))

        SerializerDecorator[ShipmentSerializer](shipment, data=payload).save()
        reset_related_shipment_rates(shipment)
        return Response(Shipment(shipment).data)
    def get(self, request: Request):
        """
        Retrieve all shipments.
        """
        shipments = request.user.shipment_set.all()

        response = self.paginate_queryset(Shipment(shipments, many=True).data)
        return self.get_paginated_response(response)
Example #10
0
    def post(self, request: Request):
        """
        Create a new shipment instance.
        """
        shipment = SerializerDecorator[ShipmentSerializer](
            data=request.data).save(user=request.user).instance

        return Response(Shipment(shipment).data,
                        status=status.HTTP_201_CREATED)
Example #11
0
    def get(self, request: Request, pk: str):
        """
        Refresh the list of the shipment rates
        """
        shipment = request.user.shipment_set.get(pk=pk)

        rate_response: RateResponse = SerializerDecorator[RateSerializer](
            data=ShipmentData(shipment).data).save(user=shipment.user).instance
        payload: dict = DP.to_dict(
            dict(rates=Rate(rate_response.rates, many=True).data,
                 messages=Message(rate_response.messages, many=True).data,
                 selected_rate=None))

        SerializerDecorator[ShipmentSerializer](shipment, data=payload).save()

        return Response(Shipment(shipment).data)
Example #12
0
    def post(self, request: Request, pk: str):
        """
        Add a parcel to an existing shipment for a multi-parcel shipment.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException("Shipment already 'purchased'",
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        parcel = SerializerDecorator[ParcelSerializer](data=request.data).save(
            user=request.user).instance
        shipment.shipment_parcels.add(parcel)
        reset_related_shipment_rates(shipment)
        return Response(Shipment(shipment).data)
Example #13
0
class ShipmentDetail(APIView):
    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}retrieve",
                         operation_summary="Retrieve a shipment",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         })
    def get(self, request: Request, pk: str):
        """
        Retrieve a shipment.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        return Response(Shipment(shipment).data)

    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}cancel",
                         operation_summary="Cancel a shipment",
                         responses={
                             200: OperationResponse(),
                             400: ErrorResponse()
                         })
    def delete(self, request: Request, pk: str):
        """
        Void a shipment with the associated label.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status not in [
                ShipmentStatus.purchased.value, ShipmentStatus.created.value
        ]:
            raise PurplShipApiException(
                f"The shipment is '{shipment.status}' and can therefore not be cancelled anymore...",
                code='state_error',
                status_code=status.HTTP_409_CONFLICT)

        if shipment.pickup_shipments.exists():
            raise PurplShipApiException((
                f"This shipment is scheduled for pickup '{shipment.pickup_shipments.first().pk}' "
                "Please cancel this shipment from the pickup before."),
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        confirmation = SerializerDecorator[ShipmentCancelSerializer](
            shipment, data={}).save()
        return Response(OperationResponse(confirmation.instance).data)
Example #14
0
    def post(self, request: Request):
        """
        Once the shipping rates are retrieved, provide the required info to
        submit the shipment by specifying your preferred rate.
        """
        payload = SerializerDecorator[ShippingRequestValidation](data=request.data).data

        response = Shipments.create(
            payload,
            resolve_tracking_url=(
                lambda shipment: reverse(
                    "purpleserver.proxy:shipment-tracking",
                    kwargs=dict(tracking_number=shipment.tracking_number, carrier_name=shipment.carrier_name)
                )
            )
        )

        return Response(Shipment(response).data, status=status.HTTP_201_CREATED)
Example #15
0
class ShipmentOptions(APIView):
    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}set_options",
                         operation_summary="Add shipment options",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         },
                         request_body=openapi.Schema(
                             title='options',
                             type=openapi.TYPE_OBJECT,
                             additional_properties=True,
                         ))
    def post(self, request: Request, pk: str):
        """
        Add one or many options to your shipment.<br/>
        **eg:**<br/>
        - add shipment **insurance**
        - specify the preferred transaction **currency**
        - setup a **cash collected on delivery** option

        ```json
        {
            "insurance": 120,
            "currency": "USD"
        }
        ```

        And many more, check additional options available in the [reference](#operation/all_references).
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException("Shipment already 'purchased'",
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        payload: dict = DP.to_dict(dict(options=request.data))

        SerializerDecorator[ShipmentSerializer](shipment, data=payload).save()
        reset_related_shipment_rates(shipment)
        return Response(Shipment(shipment).data)
Example #16
0
    def post(self, request: Request, pk: str):
        """
        Add the customs declaration for the shipment if non existent.
        """
        shipment = request.user.shipment_set.get(pk=pk)

        if shipment.status == ShipmentStatus.purchased.value:
            raise PurplShipApiException("Shipment already 'purchased'",
                                        code='state_error',
                                        status_code=status.HTTP_409_CONFLICT)

        if shipment.customs is not None:
            raise PurplShipApiException(
                "Shipment customs declaration already defined",
                code='state_error',
                status_code=status.HTTP_409_CONFLICT)

        SerializerDecorator[ShipmentSerializer](
            shipment, data=dict(customs=request.data)).save()
        reset_related_shipment_rates(shipment)
        return Response(Shipment(shipment).data)
Example #17
0
class ShipmentList(GenericAPIView):
    pagination_class = LimitOffsetPagination
    default_limit = 20

    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}list",
                         operation_summary="List all shipments",
                         responses={
                             200: Shipments(),
                             400: ErrorResponse()
                         })
    def get(self, request: Request):
        """
        Retrieve all shipments.
        """
        shipments = request.user.shipment_set.all()

        response = self.paginate_queryset(Shipment(shipments, many=True).data)
        return self.get_paginated_response(response)

    @swagger_auto_schema(tags=['Shipments'],
                         operation_id=f"{ENDPOINT_ID}create",
                         operation_summary="Create a shipment",
                         responses={
                             200: Shipment(),
                             400: ErrorResponse()
                         },
                         request_body=ShipmentData())
    def post(self, request: Request):
        """
        Create a new shipment instance.
        """
        shipment = SerializerDecorator[ShipmentSerializer](
            data=request.data).save(user=request.user).instance

        return Response(Shipment(shipment).data,
                        status=status.HTTP_201_CREATED)