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
| Column | Values | Purpose |
|---|---|---|
category | FOOD · EXPERIENCE · TRANSPORT · CHEF · WELLNESS · OTHER | Selects the shape of the attributes JSONB. See Attributes. |
kind | SINGLE · BUNDLE · VARIANT_PARENT | How the entry is composed. |
bundle_pricing_mode | ROLLUP · 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 invas_cost.VARIANT_PARENT— exposes multiple SKUs viavas_variant. Example:PREMIUM_SEDANwith variantsSWIFT_DZIRE_4H_40KMandSWIFT_DZIRE_8H_80KM. The parent is not bookable on its own; only its variants are.BUNDLE— composed of other VAS entries throughvas_bundle_item. Example:WEEKEND_NIGHT_PACKAGEincludesMOVIE_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:
attributesdiscriminates oncategory→FoodAttributes,ExperienceAttributes,TransportAttributes,ChefAttributes,WellnessAttributes,OtherAttributes.pricing_configdiscriminates ontype→FixedPricing,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
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:
listing_channel_value_added_service—(listing_id, channel_id, vas_id)override. Set by admins per listing.channel_value_added_service—(channel_id, vas_id, tag_name). Set by admins per channel.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
| Concept | File |
|---|---|
| Catalog entry | common/.../entity/catalog/Vas.java |
| Attribute hierarchy | VasAttributes.java + Food/Experience/Transport/Chef/Wellness/OtherAttributes.java |
| Pricing hierarchy | PricingConfig.java + Fixed/PerUnit/BasePlusOverage/Tiered/OnActualsPricing.java |
| Constraints | VasConstraints.java |
| Variants | VasVariant.java |
| Bundle composition | VasBundleItem.java |
| Customisation | VasChoiceGroup.java · VasChoiceOption.java |
| Migration | liquibase/.../changes/038-extend-vas-schema.sql |
| DAOs | dao/.../pricing/Vas*.java, dao/.../channel/ChannelValueAddedServiceDao.java, dao/.../listing/ListingChannelValueAddedServiceDao.java |
Next
- Attributes by category — examples for FOOD, EXPERIENCE, TRANSPORT, CHEF, WELLNESS, OTHER
- Pricing strategies — FIXED, PER_UNIT, BASE_PLUS_OVERAGE, TIERED, ON_ACTUALS
- Variants, bundles & choices — composing rich services
- Admin API reference — endpoints + sample requests