Build a Tip Report

The Tip Report sample application shows how to create a tip report for sellers who pool tips for their staff.

Applies to: Customers API | GraphQL | Labor API | Orders API | Payments API | Team API

Capabilities: Payments | Commerce | Customers | Staff

Link to section

Overview

The Tip Report sample application shows earnings from a tip pool for each team member at a location in a given time period. It provides detail about the orders completed for each server and the tip amount for each order. The Python sample application also includes a script that populates your Square account Sandbox environment with a set of team members, labor shifts, orders, payments, and customers.

This scenario shows how to download, install, and run the sample. The second part of the scenario provides a detailed look at the Python code in the application.

Did you know?

If a seller is subscribed to Square for Restaurants Plus, tip pooling and cash tip tracking (attribution) is available within the application. The Square API supports tip pool reporting only.

By the end of this scenario, you'll know:

  • How to traverse the relationship between the Orders and Payments, Payments and Team, and Team and Labor APIs.
  • How to use the Team and Labor APIs together to get labor costs for a reporting period.
  • How to add test data to your Square account Sandbox environment by using a Python script.
  • How to attribute a tip, recorded in a payment, to a team member whose job was tip-eligible at the time of the payment.

The prerequisites for running the test data seeding script and report in the Sandbox environment are minimal, while running the Tip Reporting application in production imposes normal production prerequisites.

  • You have a Square account and a Square API based application.
  • You have a Sandbox personal access token to apply to the Tip Report sample application.

The Tip Report sample uses a data seeding script that creates the same types of records that come from seller production activity. The script uses a Sandbox access token from your Square account. The report requires that same sandbox access token.

The Tip Report application consists of two services: a Flask service for the backend and a React service for the frontend.

To start the Tip Report application, you need to install it locally where it runs on a localhost service on your computer. Follow the install instructions in the readme.md file for the application.

The seed-data.py script prompts you to choose a location to add test data. In the backend folder of the sample, run python3 seed-data.py --seed to add a set of records to your Sandbox.

Did you know?

You should consider creating a new Sandbox location dedicated to exploring the Tip Report sample. If you use an existing Sandbox location, the Tip Report will read the payments, orders, team members, and shifts from that location in addition to the objects added by the seeding script.

Records of the following types are added by the script:

Link to section

Output

The seeding script produces a JSON file with the IDs of each object that it creates. These objects are available to API Explorer, the Square Dashboard, your applications, and other Square products. The structure of the output is shown as follows:

Link to section

Example

Link to section

Clear your test data

After you've completed the Sandbox testing of your code, you can run python3 seed-data.py --clear to remove the team members, customers, and shifts that it created. The seeding script also creates completed orders and completed payments, which cannot be cleared by the script.

Start the application by opening it, select the location where you added test data, and review the list of employees. You can remove any employees who aren't enabled for tipping.

After you've made the selections, select Run report. The tip report shows results like the following example:

An image of the Tip report application after initial load.

You can choose Details where the button is enabled. A dialog opens to show you all the completed orders that made up the team member's activity for the reporting period.

A image showing tip report details for a selected team member.

The Tip Report sample application gets labor shifts, payment tips, team members, orders, and customers. It starts by getting all shifts for a location and a reporting period. The shifts are read to determine the total hours that all tip-eligible people worked in the period. Each shift worked by a selected team member is added to a dictionary for later processing.

The total hours worked and the collection of completed shifts are later used to disburse tips from the tip pool. The report divides the tip income between team members based on the hours they worked for the period.

A diagram showing how the sample application gets shifts and team members using API calls

The tip pool is calculated in the get_payment_data function. It gets all payments for the date range and reads each payment. For each payment with a tip, the tip pool is incremented by the tip amount. After all payments are read, the tip pool is disbursed to the team members.

The order ID in each payment is stored in the team/shift dictionary so that when the user wants to see shift details for a team member, a list of order IDs is sent in a GraphQL request that retrieves order and customer information.

To disburse tips, the application reads the shifts dictionary for the total hours worked by a team member and a percentage of the tip pool is credited to the team member based on their hours.

A diagram showing how the business logic in the sample processes a list of payments to get tips

Link to section

Application call flow

The following sections present call flow details in the order that the application calls the Square API. The flow starts when you select the Run Report button on the Tip Report application.

The application composes a set of objects comprised of fields from TeamMember, Payment, Shift, and calculated tips. The objects are stored in the team_member_shift_dict, a dictionary declared in the run_tip_report function. The report details in the output come from this dictionary.

The run_tip_report function starts the application call flow and returns the tip report to the client by gathering completed shifts, the team members who worked the shifts, and the payments completed during the reporting period.

A shift is eligible to earn tips if the associated job title is tip enabled. The Shift object carries the shift['wage']['tip_eligible'] Boolean field. If the value is true, the team member can earn tips on the shift.

The Labor API can provide all completed shifts for all team members across all locations. The following query filters by the desired location and date range. Only shifts for that location and started within the report date range are returned.

The dictionary returned by this function doesn't yet have any tip amounts.

You can use API Explorer to experiment with the SearchShift endpoint using your own Square account data.

After gathering the shifts, the report gets the team members who worked those shifts.

This helper function uses BulkUpdateTeamMembers for its ability to accept a set of team member IDs, update those team members with values in the request body, and return full team objects. Because the function doesn't request updates — 'team_member': {}, the request is a no-op but does return the set of team members from the request list.

After this code runs, the team_member_shift_dict dictionary holds a set of team members and the shifts that they worked in the reporting period. The team member tip amount fields are still zero. The amounts are filled in the next step.

The Tip Report calculates the size of the tip pool by reading all payments at the location for the reporting period. To do this, the report calls the ListPayment endpoint with the report date range and location ID.

The resulting list is iterated to get the tip pool and record the order IDs in the team/shifts dictionary. If a payment doesn't have a tip or the payment isn't COMPLETE, it's skipped.

After the payment loop is complete, the tip pool and the total team hours worked are provided to the credit_tip_to_team_member function.

Did you know?

A cash payment can also record tip money. This lets the report account for tips made in cash when a buyer paid their bill in cash.

The tip amount per team member is calculated by dividing the tip pool by the total team work hours to get a tip amount per hour worked. The per hour amount is multiplied by the hours worked by a team member to get their total tips.

For example, if the team combined for a total of 150 hours and the tip pool size is $2,000 (money amounts are stored as pennies - 200,000 pennies), then the pennies per hour would be 1,333 or $13.33 dollars per hour. If a server worked 8 hours during the reporting period, they would earn $106 in tips.

Note

The tip calculation shown in this example might not be correct for your seller. You should update the logic in the function for your calculation. Be sure to write your tip calculations into team_member_shift_dict[key]['tips'] so that the client page reports your results.

Link to section

Helper functions

The following functions implement key technical details referenced in the Tip Report application code.

The application client presents a list of seller locations to choose from by calling the backend locations endpoint, which calls ListLocations. The name and ID of each location are returned to the client to be loaded in a selector.

class Locations(Resource): def get(self): result = square_client.locations.list_locations() return [{"name": location['name'], "id": location['id']}\ for location in result.body['locations']]

The application client calls this endpoint to fill a list of team members to select from.

The per-team-member tip calculation requires the total number of hours worked across all of a team member's shifts in the reporting period. This helper function returns the hours between the start and end of a shift.

def get_shift_length(start_time_str, end_time_str): # Convert strings to datetime objects start_time = datetime.strptime(start_time_str, '%Y-%m-%dT%H:%M:%SZ') end_time = datetime.strptime(end_time_str, '%Y-%m-%dT%H:%M:%SZ') # Calculate the time difference shift_length = end_time - start_time # Extract the hour difference return int(shift_length.total_seconds() / 3600)

Important

In production, a team member usually takes one or more breaks in the course of a shift. Every shift object has a breaks field, which is a list of all breaks taken by the team member during the shift. Each break has a start and end time. You should iterate on the break list and subtract break time from total shift time before returning the shift length.

Order details include fields from an Order object and other fields from a Customer object.

The Tip Report could have made a single BatchRetrieveOrders call but it would have also had to make a RetrieveCustomer call for every customer. Instead, the application uses a GraphQL query to return a set of orders with related customer fields based on the order IDs found in the payments retrieved earlier. This single GraphQL query replaces several Orders API and Customers API REST calls and results in a significant performance improvement.

  • You have a Square account and a Square API based application. For production payment processing and order completion, your account must first be activated at squareup.com/activation.
  • For applications that use OAuth, you have an OAuth access token that grants PAYMENTS_READ, CUSTOMERS_READ, EMPLOYEES_READ, TIMECARDS_READ, and ORDERS_READ permissions. To test while following along in this scenario, you can use a personal access token.
  • Tips are recorded with each payment.
  • The seller is using the Team application in the Square Dashboard to manage staff.
  • The seller is using Shifts Free or the Labor API to record shifts.
  • The seller has defined one or more job titles as being tip-eligible.
Link to section

Next steps

This scenario shows how to use Payments, Orders, Customers, Staff, and Labor APIs for use with sellers who aren't subscribing to Staff Plus but who need tip pool reporting. The Tip Report sample uses read operations on these APIs to get data. You should explore their full capability by reading the following topics: