Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Business problem

Several fees can be included in an order price, such as geographical, service, small cart and delivery. Currently, DMP only shows a delivery fee in the ticket it generates, calculated using the following formula:

DELIVERY_FEE = ORDER_TOTAL - SUM(ITEMS_IN_CART)

image-20240125-162939.png

For the example above we have 17.57 - (8.99 + 2.99) = 5.59. These discrepancies between the tickets generated by DMP and the POS can confuse the restaurant when the workers compare them. This document analyses possible solutions for this problem.

Tech discovery

Architecture AS-IS

An order incoming to DMP can come from either whitelabel or a food aggregator (eg. Glovo, Uber Eats). Although created using different endpoints, both receive a list of fees included.

For the food aggregator workflow we have the following diagram:

image-20240129-154651.png

Known issues

  1. Fulfillment service aggregating all fees into delivery fee
    This is part of the first implementation that we can see here :

        const deliveryFees = input.charges?.fees?.reduce((acc, fee) => {
          if (fee.type === PartnerFeeType.DELIVERY_FEE) {
            acc = fee.total?.amount ?? 0;
          }
          return acc;
        }, 0);
  2. DMP never receive the fees associated with the order
    Although DMP calculates the delivery fee in the WL during the quote workflow, this information is not persisted when the order is created. Besides that, no information regarding the other fees is received by it, as we can see in the create delivery webhook documentation.

Proposed solution

A simple solution is fixing the delivery fee in fulfillment calculation and altering the delivery integration to receive this information. Let’s split the solution into three steps:

  1. Fix the fee calculation in fulfillment service

  2. Send fee information in partners service

  3. Receive and persist fee information in DMP

  4. Display fee breakdown in expeditor tablet, including printed tickets

Let’s dive into more details in each one:

1. Fix the fee calculation in fulfillment service

For the first problem, we’ll need to map all the possible received fees (including new values to come) into the fee fields of the RBI Order. Taking delivery and service fee as examples:

intl-fulfillment-service/src/modules/channel-partner/channel-partner.service.ts

 async submitOrder(data: IPartnerSubmitComittedOrder): Promise<PartnerOrderResponseDto> {
    ...
    
    const fees = input.charges?.fees ?? [];
    const formattedFees = {
      baseDeliveryFeeCents: fees.find(({ type }) => type === 'DELIVERY_FEE')?.total?.amount ?? 0,
      serviceFeeCents: fees.find(({ type }) => type === 'SERVICE_FEE')?.total?.amount ?? 0,
    };
    
    ...
    
    const rbiOrder = await this.fulfillmentService.submitOrder({
      cart: deliveryCart,
      delivery: { deliveryMode, dropoff: parsedDelivery, ...formattedFees },
      externalReferenceId,
      rbiOrderStatus: input.rbiOrderStatus,
      status: input.partnerOrderStatus,
    });
 } 

For the code above, a request to /api/v1/channel-partner/orders?channel=bookingall including the following fees:

{
  "fees": [
    {
       "type": "DELIVERY_FEE",
       "total": {
         "currency": "EUR",
         "amount": 200
       }
     },
     {
       "type": "SERVICE_FEE",
       "total": {
         "currency": "EUR",
         "amount": 250
       }
     }
   ]
}

Will create an order with the appropriate fees in DynamoDB:

image-20240130-002210.png

2. Send fee information in partners service

In this step, it is necessary to include the new fee structure in the create delivery payload and format the values received to the currency format.

intl-partners-service/src/modules/webhooks/interfaces/webhook-delivery.interface.ts

 export interface IFees {
  deliveryFee: IPrice;
  serviceFee: IPrice;
}
export interface IWebhookCreateDelivery {
  ...
  fees: IFees;
}

intl-partners-service/src/modules/delivery/delivery.service.ts

Altering this request will change the payload sent to all delivery providers in international. If any of them have servers configured in strict mode, the integration could break. Ideally, we can use LaunchDarkly to configure which integrations will receive the new field.

public async createDelivery(input: CreateDeliveryRequestDto): Promise<CreateDeliveryResponseDto> {
   ...
   const mapRbiFees = (fees: FeesDto | undefined) => {
     return {
       deliveryFee: mapToMoney(fees?.baseDeliveryFee, IsoCountryCode2Char.ES),
       serviceFee: mapToMoney(fees?.serviceFee, IsoCountryCode2Char.ES),
     };
   };

   const payload: IWebhookCreateDelivery = {
       callbackUrl: `https://${partnersApi.host}/api/${partnersApi.version}/delivery/create/callback`,
       ...
       fees: mapRbiFees(input.fees),
  };
}

After configuring a mock webhook to hit a local Mockoon server, I could confirm the new fees would indeed be sent to the delivery provider:

image-20240130-025111.png

3. Receive and persist fee information in DMP

The create delivery request sent by partners-service is received by DMP /orders endpoint, which creates and OrderResponseDTO object in Dynamo DB. First we need to include the fee field in the three relevant DTOs: OrderResponseDto, DeliveryWebhookRbiDto and DeliveryWebhookDto.

intl-partners-delivery/src/modules/orders/dto/order-response.dto.ts

intl-partners-delivery/src/modules/orders/dto/delivery-webhook.dto.ts

intl-partners-delivery/src/modules/orders/dto/delivery-webhook-rbi.dto.ts

export class FeesDto {
  @Type(() => Amount)
  @ValidateNested()
  @IsObject()
  @ApiProperty({ description: 'Delivery fee charged in the order', type: Amount })
  deliveryFee: Amount;

  @Type(() => Amount)
  @ValidateNested()
  @IsObject()
  @ApiProperty({ description: 'Service fee charged in the order', type: Amount })
  serviceFee: Amount;
}

export class OrderResponseDto {
  ...
  
  @ApiProperty({ description: 'Fees charged in the order', type: FeesDto })
  fees?: FeesDto;
}

export class DeliveryWebhookRbiDto {
  ...
  
  @ApiProperty({ description: 'Fees charged in the order', type: FeesDto })
  fees?: FeesDto;
}
export class DeliveryWebhookDto {
  ...
  
  @ApiProperty({ description: 'Fees charged in the order', type: FeesDto })
  fees?: FeesDto;
}

After the above it’s done, it’s a matter of adjusting the relevant formatter methods used by the webhook.

async webhookCreateOrder(...) {
   ...
   const body: DeliveryWebhookDto = {
     ...
     fees: payload.fees,
   }; 
}

private async formatOrderFromWebhook(...) {
   return {
      ...
      fees: body.fees,
   }
}

And now the orders are created in dynamo with the fee information:

image-20240130-043024.png

4. Display fee breakdown in expeditor tablet, including printed tickets

We’ll have to align with the UI/UX team to find the best way to display the new information, but as long as the backend is returning it, expeditor tablet has only to adjust the Order interface and create the new react components responsible for showing the fees.

Task breakdown

[intl-fulfillment-service]

  1. Adjust fee calculation to stop aggregating all fees into delivery

[intl-partners-service]

  1. Update crete delivery to send the fee information

[intl-partners-delivery]

  1. Update create delivery webhook to receive the fee information

  2. Create/update e2e order tests to include the new scenario

[intl-expeditor-tablet]

  1. Display fee breakdown in order detail

  2. Validate if the order ticket is printed with the fees

  3. Create/update e2e order tests to include the new scenario

  • No labels

0 Comments

You are not logged in. Any changes you make will be marked as anonymous. You may want to Log In if you already have an account.