Skip to main content

Value-Added Services (VAS)

Value-Added Services are paid extras booked on top of a stay — food, experiences, transport, chefs, wellness. The catalog supports five orthogonal shapes so a single data model can describe everything from "Only Snacks (no BBQ)" to "Premium Sedan – Swift Dzire, 4 Hours / 40 KMs" to "Movie Night + Bonfire + BBQ + High Tea" bundles.

The Model

value_added_service           ← catalog entry (id, name, category, kind, attributes, constraints)

├─ vas_variant ← SKUs (e.g. SWIFT_DZIRE_4H_40KM, SWIFT_DZIRE_8H_80KM)

├─ vas_bundle_item ← children, if kind = BUNDLE

├─ vas_choice_group ← pick-N-of-M customisations (e.g. "2 Veg + 2 Non-Veg")
│ └─ vas_choice_option

├─ vas_cost ← (vas, variant?, tag) → price + pricing_config

├─ channel_value_added_service ← per-channel availability + pricing override

└─ listing_channel_value_added_service ← per-listing override

Three Discriminators on the Catalog Entry

ColumnValuesPurpose
categoryFOOD · EXPERIENCE · TRANSPORT · CHEF · WELLNESS · OTHERSelects the shape of the attributes JSONB. See Attributes.
kindSINGLE · BUNDLE · VARIANT_PARENTHow the entry is composed.
bundle_pricing_modeROLLUP · SUM_CHILDREN (only for kind=BUNDLE)Whether a bundle has its own price or derives it from children.

kind in detail

  • SINGLE — a leaf item. Example: BONFIRE, BBQ_2V_2NV, RAIN_DANCE. Priced directly in vas_cost.
  • VARIANT_PARENT — exposes multiple SKUs via vas_variant. Example: PREMIUM_SEDAN with variants SWIFT_DZIRE_4H_40KM and SWIFT_DZIRE_8H_80KM. The parent is not bookable on its own; only its variants are.
  • BUNDLE — composed of other VAS entries through vas_bundle_item. Example: WEEKEND_NIGHT_PACKAGE includes MOVIE_NIGHT + BONFIRE + BBQ_VEG + BBQ_NONVEG + HIGH_TEA. Either has a rolled-up price (ROLLUP) or sums children at runtime (SUM_CHILDREN).

Type-Safe Polymorphism

attributes and pricing_config are stored in PostgreSQL JSONB columns but deserialized into sealed Java types. Jackson picks the concrete record from a discriminator on the JSON payload:

  • attributes discriminates on categoryFoodAttributes, ExperienceAttributes, TransportAttributes, ChefAttributes, WellnessAttributes, OtherAttributes.
  • pricing_config discriminates on typeFixedPricing, PerUnitPricing, BasePlusOveragePricing, TieredPricing, OnActualsPricing.

In Java:

public sealed interface VasAttributes
permits FoodAttributes, ExperienceAttributes, TransportAttributes,
ChefAttributes, WellnessAttributes, OtherAttributes {
VasCategory category();
}

Sealed types mean switch over VasAttributes is exhaustive — adding a new category is a compile-time push:

String label = switch (vas.getAttributes()) {
case FoodAttributes f -> "Food / " + f.mealType();
case TransportAttributes t -> t.vehicleLabel();
case ExperienceAttributes e -> e.experienceType();
case ChefAttributes c -> "Chef (" + c.chefType() + ")";
case WellnessAttributes w -> w.wellnessType();
case OtherAttributes o -> "Other";
};

Pricing Layers

Shared with meals

The layered pricing model (catalog → channel → listing) is identical for meals and VAS. The shared explanation lives at Add-on Pricing. This section is the VAS-specific cut.

Pricing is resolved by walking three tables in order, taking the first non-null override:

  1. listing_channel_value_added_service(listing_id, channel_id, vas_id) override. Set by admins per listing.
  2. channel_value_added_service(channel_id, vas_id, tag_name). Set by admins per channel.
  3. vas_cost(vas_id, variant_id?, tag_name). The baseline.

Every level can carry both the simple (price, pricing_type) pair and the structured pricing_config JSONB. The structured config is what enables BASE_PLUS_OVERAGE (sedan-style: ₹X for the first 4h/40km, then ₹/hr + ₹/km), TIERED (slab pricing), and ON_ACTUALS (chef grocery, reconciled later).

What's Where

ConceptFile
Catalog entrycommon/.../entity/catalog/Vas.java
Attribute hierarchyVasAttributes.java + Food/Experience/Transport/Chef/Wellness/OtherAttributes.java
Pricing hierarchyPricingConfig.java + Fixed/PerUnit/BasePlusOverage/Tiered/OnActualsPricing.java
ConstraintsVasConstraints.java
VariantsVasVariant.java
Bundle compositionVasBundleItem.java
CustomisationVasChoiceGroup.java · VasChoiceOption.java
Migrationliquibase/.../changes/038-extend-vas-schema.sql
DAOsdao/.../pricing/Vas*.java, dao/.../channel/ChannelValueAddedServiceDao.java, dao/.../listing/ListingChannelValueAddedServiceDao.java

Next