Migrating from Legacy Services to the New Service Dimension Junction

Introduction

Historically, Services in GoMeddo were built as their own dedicated structure. A Service and Service Type record described what could be booked, a Service Availability record described on which Resource that Service was available and in what quantity, and a separate Service Reservation junction object linked Services to Reservations. This structure worked, but it lived outside of the generic Dimension framework that the rest of GoMeddo uses for Resources, Staff, and other bookable dimensions.

In the new structure, Service becomes a regular Dimension. The Service object is registered as a Dimension, the existing junction between Reservation and Service is registered as a Dimension Junction, and Service availability is expressed with Multi-Dimensional Availabilities (MDAs) between Resource and Service, instead of the separate Service Availability object. This means Services now benefit from the same tools available to every other Dimension: Conflict Rules, Filter Rules, Ranking Rules, and Dimension Junction Display Settings.

Before you start

This migration changes how Services are booked and priced, so take the following into account before you start.

  • This migration is only needed if you upgraded from 7.4 or below. Starting from 7.5 the new model is the default and the required dimensions and rules have already been created on install.

  • Do the migration in a sandbox first and validate a few test reservations with Services before doing this in production.

  • Do not remove your existing Service, Service Type or Service Availability records before you have confirmed the new structure works as expected. You can keep them around as a reference, or as a fallback, until you are confident in the new setup.

  • Make sure you know which Resources currently have Service Availability defined, and for which quantities, since you will need to recreate this as Multi-Dimensional Availabilities in step 4.

  • Note down any pricing or quantity fields you use today (for example Unit Price, Price, Quantity, Subtotal and Service Costs), since these will be mapped onto the new Dimension Junction in step 3.

  • Contextual price calculation needs to be configured before switching to the new service model.

  • Conflict Rules Need to be enabled before switching to the new service model.

Step 1: Upgrade to the latest package version

The Service Dimension Junction structure is only available from the package version that introduces it. Upgrade your org, starting with a sandbox, to the latest GoMeddo version that includes support for Services as a Dimension.

Step 2: Create the Service Dimension and reuse the existing junction

Do not use the Dimension Wizard for this step. The wizard is built to set up a brand new dimension, and optionally a brand new junction object, from scratch. Your org already has a junction between Reservation and Service from the legacy Service structure, so instead you create the Dimension record for Service directly and reuse that existing junction in step 3.

  1. Go to the app launcher and open the Dimension tab.

  2. Click New.

  3. Fill in the fields as described in the table below.

  4. Save the record.

Field

What to fill in

Name

B25__Service__c

Dimension Name Field

The API name of the field used as the display name for each Service record, in this case Name

Availability Lookup

B25__Service__c

MDA Applies To This Dimension Only

Checked

Step 3: Register the Service junction and map its fields

Reservations and Services are already linked through an existing junction object from the legacy structure (API name B25__Service_Reservation__c), which already stores fields such as Quantity, Unit Price, Price, Subtotal and Service Costs on each line. To make this junction work with the new Dimension framework, you register it as a Dimension Junction and tell GoMeddo which field on the junction holds which piece of information. This needs to be in place before you set up Availabilities in step 4, since the Availabilities rely on the Service Dimension being fully linked to Reservations first.

  1. Go to the app launcher and open the Dimension Junction tab.

  2. Click New.

  3. Fill in the fields as described in the table below.

  4. Save the record.

Field

What to fill in

Dimension

Lookup to the Service Dimension record you created in step 2

Name

The API name of the junction object being registered, in this case B25__Service_Reservation__c

Reservation Lookup API Name

The API name of the lookup field on the junction that points to the Reservation, in this case B25__Reservation__c

Dimension Lookup API Name

The API name of the lookup field on the junction that points to the Service, in this case B25__Service__c

Price Field

The field holding the unit price used in price calculations, in this case B25__Unit_Price__c

Default Price Field

The field holding the default price, in this case B25__Price__c

Quantity Field

The field holding the booked quantity, in this case B25__Quantity__c

Subtotal Field

The field holding the subtotal, in this case B25__Subtotal__c

Total Field

The field holding the total service costs, in this case B25__Service_Costs__c

If your org renamed or customized any of these fields, use your own API names instead of the ones above.

Next, configure the Dimension Junction Display Setting record for Service so the related list shows up correctly on the Reservation Form. Set the following two fields:

  • Field Set to use: B25__CustomFields

  • Collapsed Field Set To Use: B25__CollapsedFields

These are the field sets on the Service Reservation junction object that were already used to control which extra fields show up in the dropdown section and in the collapsed row overview under the legacy structure, so reusing them keeps the reservation form looking the same for your users. See Displaying Dimension Junctions on the Reservation Form for the rest of the fields on this setting, such as Reservation Type, Order and Label.

Step 4: Recreate your Service Availabilities as Multi-Dimensional Availabilities

The old Service Availability object made a Service available for a Resource, with an optional Capacity, and that availability propagated down to the Resource's children. The same result is achieved with a Multi-Dimensional Availability (MDA) between the Service and the Resource.

  1. For every Resource that currently has a Service Availability, create a new Availability record on the corresponding Service dimension record, using the Service lookup field and the resource lookup field.

  2. Enable the Is Dependent Availability checkbox on that Availability.

  3. Populate the lookup to the Resource the Service should be available for. This follows the same inheritance rules as before, so an availability set on a parent Resource still applies to its children.

  4. Copy over the time window and, if used, the Capacity from the old Service Availability record. (The new capacity field on availability might not be on the page layout in your org you can add it)

See Multi-Dimensional Availabilities for the full explanation of how MDAs behave, including how lookups on the reservation form filter each other and how MDAs are rendered on the calendar.

Step 5: Configure the rules for your services

With Service now a Dimension, you can move any business logic that used to depend on the legacy Service Availability and Capacity fields into a Conflict Rule linked to the Service Dimension. Go to the Conflict Rule tab, click New Rule, link it to the Service Dimension, and paste the formula below into the formula editor. It blocks a reservation when the requested quantity of a Service, combined with everything already booked for that same Service under the same availability, would exceed the Capacity of the matching Service availability. It also blocks the reservation when the Service has no matching availability at all.

// First we make sure that we are checking the service reservation junction
dimensionElement == "b25__servicereservations__r" AND
(
    //Check if the service has any availability
    isNotAvailable()
    OR
    (
        // Get the total service quantity of the current reservation for the service we are booking
        SUM(
            FILTER(b25__ServiceReservations__r as junction, junction.B25__Service__c == b25__ServiceReservations__r[junctionIndex].B25__Service__c),
            B25__Quantity__c, 1
        ) +
        // get all of the overlapping reservations that are under the same availability, filter down the service reservations related list to only the entries that are the same service as the one currently being checked.
        // Then sum all their quantities and compare the total quantity with the capacity of the currently active availability.
        SUM(
            FOREACH(
                FILTER(overlappingReservations as res, res.B25__Resource__r.B25__Full_Path__c STARTSWITH matchingAvailabilities[0].B25__Resource2__r.B25__Full_Path__c)
                as res,
                SUM(
                    FILTER(res.b25__ServiceReservations__r as junction, junction.B25__Service__c == b25__ServiceReservations__r[junctionIndex].B25__Service__c),
                    B25__Quantity__c, 1
                )
            )
        )
    ) > matchingAvailabilities[0].B25__Capacity__c
)

A few things to note about this rule:

  • The dimensionElement == "b25__servicereservations__r" check makes sure the rule only runs for the Service junction relationship, since a Conflict Rule linked to a dimension runs once for every way a reservation is connected to that dimension.

  • The first SUM adds up the quantity of every service line on the current reservation for the same Service.

  • The second SUM looks at all overlapping reservations under Resources that fall within the Resource of the matching availability, including child Resources through Full_Path__c STARTSWITH, and adds up their quantities for the same Service.

  • If the combined total is higher than the Capacity on the matching availability, or if there is no matching availability at all, the reservation is blocked.

Mark the rule as a hard conflict and give it a validation message such as "This service is not available in the requested quantity". Adjust the field API names if your org customized them.

Lookup filtering between Resource and Service on the reservation form is already handled automatically by the MDAs you created in step 4. See Conflict Rules and Filter Rules for the full list of operators, functions and examples. Note that Conflict Rules need to be enabled separately in GoMeddo Settings before they take effect.

Step 6: Enable the new Service structure

Once the Dimension, the junction mapping, the Availabilities and the rules are all in place you can disable the Enable Legacy services setting in the validation section of the GoMeddo settings page.
This causes GoMeddo to stop treating service as special. This is reversible so enabling this setting again will cause GoMeddo to once again look at the old objects for Services. Note that some of the configuration above (namely the dimension dimension junction and dimension junction display setting) will cause the reservation form to display services twice if legacy services is still enabled.

Enabling this setting changes how Services are booked and priced for all users at once. Test thoroughly in a sandbox first, and enable it in production during a quiet moment so you can quickly verify a few real reservations with Services afterwards.

What changes after migration

Reservation form

  • The built-in services section disappears; services render as an ordinary junction related list using Dimension Junction Display Settings (order, label, field sets all now apply).

  • Reservation Type → Hide Service Section stops having any effect. Use the display setting instead.

  • Legacy services search used the active checkbox to hide services from the search. To recreate this you can either add Custom Form Logic to filter the list. Or make sure those services have no availability.

  • Legacy services search appended the maximum remaining quantity to each search result. Dimension junctions don’t do this. This is in theory replicatable using Custom Form Logic however depending on the complexity of your availabilities non trivial.