Suppliers Service
Manages land service providers (DMCs), their hotel inventory, and tour packages with independent per-hotel pricing.
When to Use
Section titled “When to Use”- Managing external companies that provide ground services (hotels, tours, transfers)
- Creating tour packages linked to ProductTemplates
- Setting up per-hotel pricing with rate periods and room types
- Setting up pricing periods with weekday restrictions and blackout dates
- Configuring multi-tenant access for supplier managers
Data Architecture
Section titled “Data Architecture”Supplier (land service provider)├── SupplierHotel[] (hotel inventory)│ └── SupplierService (1:1 - per-hotel pricing)│ └── SupplierServiceRate[] (rate periods)│ └── SupplierServiceRatePrice[] (room type prices)├── SupplierActivity[] (excursions, day tours)│ └── SupplierService (1:1 - per-activity pricing)│ └── SupplierServiceRate[] → SupplierServiceRatePrice[]├── SupplierTransfer[] (airport/city transfers)│ └── SupplierService (1:1 - per-transfer pricing)│ └── SupplierServiceRate[] → SupplierServiceRatePrice[]└── SupplierTour[] (tour packages) ├── ProductTemplate (1:1 - defines itinerary structure) ├── SupplierTourItinerary[] (assignments per day) │ ├── guaranteed_hotel_id, upsell_hotel_id │ ├── supplier_tour_itinerary_activities (pivot) │ │ ├── type='included' → activities included in base price │ │ └── type='upsell' → optional upgrade activities │ └── supplier_tour_itinerary_transfers (pivot) │ ├── type='included' → transfers included in base price │ └── type='upsell' → optional upgrade transfers └── SupplierTourRate[] (pricing periods - legacy)Key Concepts
Section titled “Key Concepts”SupplierService Architecture
Section titled “SupplierService Architecture”SupplierService provides independent, per-hotel pricing separate from the legacy tour-based rates. Each hotel can have its own service with flexible pricing models.
Source: backend/app/Models/SupplierService.php
Service Types
Section titled “Service Types”| Type | Description | Use Case | FK Link |
|---|---|---|---|
| Hotel | Accommodation pricing | Per-hotel rates | supplier_hotel_id |
| Activity | Day tours, excursions | Per-person pricing | supplier_activity_id |
| Transfer | Airport/city transfers | Per-trip pricing | supplier_transfer_id |
Source: backend/app/Enums/SupplierServiceType.php
SupplierActivity
Section titled “SupplierActivity”Activities represent excursions, tours, and experiences that can be assigned to tour itineraries.
Source: backend/app/Models/SupplierActivity.php
Activity Fields
Section titled “Activity Fields”| Field | Description |
|---|---|
name | Activity name (e.g., “Great Wall Day Tour”) |
city | Location in CitySelect format |
duration_hours | Activity duration |
description | Detailed description |
images | Photo gallery |
Activity → Service Link
Section titled “Activity → Service Link”Each activity has ONE linked SupplierService for pricing:
// Activity has one service$activity->service; // SupplierService with pricing
// Service belongs to one activity$service->activity; // SupplierActivityWhen creating a service for an activity, the pricing model auto-switches to PerPerson.
SupplierTransfer
Section titled “SupplierTransfer”Transfers represent transportation services (airport pickups, city transfers) that can be assigned to tour itineraries.
Source: backend/app/Models/SupplierTransfer.php
Transfer Fields
Section titled “Transfer Fields”| Field | Description |
|---|---|
name | Transfer name (e.g., “Airport to Hotel Transfer”) |
city | Location in CitySelect format |
vehicle_type | Vehicle description (e.g., “Private Car”, “Minivan”) |
duration_minutes | Estimated duration |
description | Detailed description |
images | Photo gallery |
Transfer → Service Link
Section titled “Transfer → Service Link”Each transfer has ONE linked SupplierService for pricing:
// Transfer has one service$transfer->service; // SupplierService with pricing
// Service belongs to one transfer$service->transfer; // SupplierTransferActivity and Transfer Assignment Types
Section titled “Activity and Transfer Assignment Types”Tours can assign activities and transfers as included (in base price) or upsell (optional upgrade):
| Type | Description | In Offer Price? |
|---|---|---|
included | Base package items | Yes |
upsell | Optional premium items | No |
// Assign activity to tour itinerary$itinerary->includedActivities()->attach($activity->id, [ 'sort_order' => 0, 'type' => 'included']);
// Assign transfer to tour itinerary$itinerary->includedTransfers()->attach($transfer->id, [ 'sort_order' => 0, 'type' => 'included']);The same activity/transfer CAN be assigned as both types to the same day (unique constraint is on itinerary_id, item_id, type).
Source: backend/app/Models/SupplierTourItinerary.php
Pricing Models
Section titled “Pricing Models”| Model | Calculation | Example |
|---|---|---|
| PerNight | price × nights | Hotel accommodation |
| Package | Fixed price | All-inclusive tour |
| PerPerson | price × travelers | Group activities |
| PerTrip | Fixed price | Airport transfer |
Source: backend/app/Enums/ServicePricingModel.php
Rate Periods (SupplierServiceRate)
Section titled “Rate Periods (SupplierServiceRate)”Each service has rate periods defining when prices apply:
- Date range:
start_datetoend_date - Weekdays: Array of allowed days (e.g.,
["mon", "thu", "sat"]) - Exclusions: Blackout date ranges
- Allotment: Available inventory
Methods:
isDateAvailable($date)- Checks period, weekday, and exclusionsisDateExcluded($date)- Checks blackout ranges onlygetPriceForRoomType($roomType)- Returns price record
Source: backend/app/Models/SupplierServiceRate.php
Room Type Prices (SupplierServiceRatePrice)
Section titled “Room Type Prices (SupplierServiceRatePrice)”Prices per room configuration within a rate period:
| Code | Description |
|---|---|
| 1A | 1 Adult |
| 2A | 2 Adults |
| 3A | 3 Adults |
| 4A | 4 Adults |
| 1A+1CH | 1 Adult + 1 Child |
| 1A+2CH | 1 Adult + 2 Children |
| 1A+3CH | 1 Adult + 3 Children |
| 2A+1CH | 2 Adults + 1 Child |
| 2A+2CH | 2 Adults + 2 Children |
| 2A+1B | 2 Adults + 1 Baby |
| 2A+1CH+1B | 2 Adults + 1 Child + 1 Baby |
| 3A+1CH | 3 Adults + 1 Child |
| per_person | Per Person (Activity) |
| per_trip | Per Trip (Transfer) |
Special room types:
per_person- Used for activity pricing, multiplied by number of travelersper_trip- Used for transfer pricing, fixed price regardless of travelers
Currency auto-assignment: When creating prices without a currency, the system automatically assigns the supplier’s default currency.
Source: backend/app/Models/SupplierServiceRatePrice.php
SupplierTour and ProductTemplate Relationship
Section titled “SupplierTour and ProductTemplate Relationship”Tours are linked 1:1 with ProductTemplates. The ProductTemplate defines the itinerary structure (cities, nights, titles), while SupplierTour adds supplier-specific data (hotels, rates, guide info).
Source: backend/app/Models/SupplierTour.php
Rate Date Validation
Section titled “Rate Date Validation”Tour rates have specific validity rules checked by isDateAvailable():
- Date within
start_date-end_daterange - Weekday in allowed
weekdaysarray (e.g.,["mon", "thu"]) - Date not in
excluded_date_ranges(blackout periods)
Source: backend/app/Models/SupplierTourRate.php
Room Type Codes
Section titled “Room Type Codes”Standardized room type codes used across the system:
| Code | Description |
|---|---|
| 1A | 1 Adult |
| 2A | 2 Adults |
| 2A+1CH | 2 Adults + 1 Child |
| 2A+2CH | 2 Adults + 2 Children |
| 2A+1B | 2 Adults + 1 Baby |
Source: backend/app/Models/SupplierTourRateRoomPrice.php (roomTypeOptions())
SupplierHotel Room Types
Section titled “SupplierHotel Room Types”Hotels use a different room type configuration defined as model constants:
| Code | Description |
|---|---|
1 pax | 1 Pax (Single) |
2 pax | 2 Pax (Double) - Required |
3 pax | 3 Pax (Triple) |
2 pax 1 baby | 2 Pax + 1 Baby |
2 pax 1 child | 2 Pax + 1 Child |
2 pax 2 children | 2 Pax + 2 Children |
4 pax | 4 Pax (Quad) |
family | Family Room |
suite | Suite |
All hotels must include the “2 pax” room type.
Source: backend/app/Models/SupplierHotel.php (ROOM_TYPES, REQUIRED_ROOM_TYPE)
Connection to Offers
Section titled “Connection to Offers”Offers use SupplierService pricing from hotels AND included activities in the tour itinerary:
ProductByMarket → ProductTemplate ← SupplierTour → SupplierTourItinerary[] ↓ ┌─────────────┴─────────────┐ ↓ ↓ SupplierHotel[] SupplierActivity[] (included) ↓ ↓ SupplierService[] SupplierService[] ↓ ↓ Rate → Prices Rate → Prices ↓ ↓ └───────────┬───────────────┘ ↓ Offer (land_base_price)Offer Creation Flow
Section titled “Offer Creation Flow”- Select ProductByMarket (includes ProductTemplate and linked SupplierTour)
- System extracts all hotels from the tour’s itinerary
- System extracts all included activities (not upsells)
- System finds SupplierService for each hotel and activity
- Room type dropdown shows types available at ALL hotels (intersection)
- Price displayed is the TOTAL across all services for selected room type
- Offer stores the combined
land_base_price
Price Calculation
Section titled “Price Calculation”The land price sums hotel and included activity services:
Land Price = Σ(Hotel Services) + Σ(Included Activity Services)
Hotel Service Price = rate_price × nights (per_night model)Activity Service Price = rate_price × travelers (per_person model)Room type lookup:
- Hotels: Use selected room type (e.g.,
2A,2A+1CH) - Activities: Always use
per_personroom type
Source: backend/app/Filament/Resources/Offers/Pages/CreateOffer.php, backend/app/Filament/Resources/Offers/Schemas/OfferPreviewData.php
See Offers documentation for combined pricing details.
Multi-Tenant Access Control
Section titled “Multi-Tenant Access Control”SupplierManager Role
Section titled “SupplierManager Role”Users with SupplierManager role are automatically scoped to their assigned supplier. The Filament resources filter queries based on user.supplier_id.
Source: backend/app/Filament/Resources/Suppliers/SupplierTourResource.php
Permissions
Section titled “Permissions”| Permission | Description |
|---|---|
| supplier.view_any | List suppliers |
| supplier.view | View supplier details |
| supplier.create | Create suppliers (Admin only) |
| supplier.update | Update supplier |
| supplier.delete | Delete supplier (Admin only) |
| supplier_tour.* | Tour CRUD operations |
| supplier_tour_rate.* | Rate CRUD operations |
| supplier_hotel.* | Hotel CRUD operations |
Filament Admin Resources
Section titled “Filament Admin Resources”Navigation Group: “Suppliers Management”
Section titled “Navigation Group: “Suppliers Management””| Resource | Model | Description |
|---|---|---|
| Suppliers | Supplier | Company management |
| Hotels | SupplierHotel | Hotel inventory |
| Activities | SupplierActivity | Excursions, day tours |
| Transfers | SupplierTransfer | Airport/city transfers |
| Supplier Services | SupplierService | Per-hotel/activity/transfer pricing |
| Tours | SupplierTour | Tour packages |
| Tour Rates | SupplierTourRateRoomPrice | Flat rate view (legacy) |
Source: backend/app/Filament/Resources/Suppliers/
Tour Creation Wizard
Section titled “Tour Creation Wizard”Creating a SupplierTour uses an embedded wizard that also creates the ProductTemplate:
- Product Template Step: AI parser, title, duration, itinerary (cities/nights)
- Tour Details Step: Guide info, meals, transport, images
- Services Assignments Step: Three tabs:
- Hotels Tab: Grouped by stop with guaranteed/upsell hotel selections
- Activities Tab: Per-day included/upsell activity selections
- Transfers Tab: Per-day included/upsell transfer selections
AI Trip Parser
Section titled “AI Trip Parser”Paste raw trip information to auto-generate all template fields. Appears as a collapsible section at the top of Step 1.
Example input:
Asia India 9 nights - 2 Delhi - 1 Jodhpur - 2 Udaipur - 2 Jaipur - 1 Agra - 1 DelhiGenerates:
- Tour title and descriptions (short/long)
- Complete itinerary with POI locations resolved via Google Places
- Duration auto-calculated from total nights
- Hotel assignments regenerated from itinerary
Content is generated in the selected source language. Shows confirmation modal before replacing existing values.
Source: backend/app/Services/ProductTemplateAIService.php (generateFromRawInput())
Hotel Selection Performance
Section titled “Hotel Selection Performance”Hotel dropdowns use lazy loading to prevent N+1 issues with large inventories:
- Hotels fetched on-demand when user searches (not preloaded)
- Search filtered by itinerary location
- Results limited to 50 per search
Cascade behavior: Deleting SupplierTour also deletes its ProductTemplate. Deleting Supplier cascades to tours and hotels.
Source: backend/app/Filament/Resources/Suppliers/SupplierTours/Schemas/SupplierTourForm.php
Test Data Generation
Section titled “Test Data Generation”suppliers:activities:generate
Section titled “suppliers:activities:generate”Generate test supplier activities linked to services with per-person pricing.
./vendor/bin/sail artisan suppliers:activities:generateCreates activities for each supplier with linked SupplierService records and per_person rate prices.
Source: backend/app/Console/Commands/GenerateTestSupplierActivities.php
suppliers:hotels:generate
Section titled “suppliers:hotels:generate”Generate test supplier hotels with realistic luxury hotel data.
# Interactive mode with prompts./vendor/bin/sail artisan suppliers:hotels:generate
# Non-interactive with options./vendor/bin/sail artisan suppliers:hotels:generate \ --suppliers=1,2 \ --countries="Spain,France,Italy" \ --hotels-per-city=3 \ --use-aiOptions:
| Option | Description |
|---|---|
--suppliers=* | Supplier IDs (comma-separated) |
--countries=* | Country names from airports table |
--hotels-per-city=2 | Hotels per city (minimum 2) |
--use-ai | Use OpenRouter API for realistic data |
Key Features:
- Cities sourced from airports table (valid destinations only)
- AI mode generates realistic luxury hotel names via OpenRouter
- Fallback mode uses hotel brand names (Four Seasons, Ritz-Carlton, etc.)
- Room types always include required “2 pax” plus random optional types
- Sensible defaults: 1 supplier, 5 random countries, 2 hotels per city
Source: backend/app/Console/Commands/GenerateTestSupplierHotels.php, backend/app/Services/SupplierHotelGeneratorService.php
Database Schema
Section titled “Database Schema”Core Tables
Section titled “Core Tables”| Table | Purpose |
|---|---|
suppliers | Land service provider companies |
supplier_hotels | Hotel inventory per supplier |
supplier_activities | Excursion/tour inventory per supplier |
supplier_transfers | Transfer inventory per supplier |
supplier_services | Purchasable services (hotel, activity, transfer) |
supplier_service_rates | Rate periods with date/weekday constraints |
supplier_service_rate_prices | Room type prices per rate period |
Tour Tables
Section titled “Tour Tables”| Table | Purpose |
|---|---|
supplier_tours | Tour packages linked to ProductTemplates |
supplier_tour_itineraries | Hotel assignments per tour day |
supplier_tour_itinerary_activities | Activity pivot (included/upsell per day) |
supplier_tour_itinerary_transfers | Transfer pivot (included/upsell per day) |
supplier_tour_rates | Tour rate periods (legacy) |
supplier_tour_rate_room_prices | Tour room prices (legacy) |
Key Constraints
Section titled “Key Constraints”supplier_services.supplier_hotel_idis unique (one service per hotel)supplier_services.supplier_activity_idis unique (one service per activity)supplier_services.supplier_transfer_idis unique (one service per transfer)supplier_tour_itinerary_activitiesunique on(itinerary_id, activity_id, type)supplier_tour_itinerary_transfersunique on(itinerary_id, transfer_id, type)- Deleting a supplier cascades to services, hotels, activities, and transfers
- Deleting a service rate cascades to its prices
Source: backend/database/migrations/ (search for supplier)
Files:
- Models:
backend/app/Models/Supplier*.php - Enums:
backend/app/Enums/SupplierServiceType.php,backend/app/Enums/ServicePricingModel.php - Resources:
backend/app/Filament/Resources/Suppliers/ - Policies:
backend/app/Policies/Supplier*.php - Transfer Resource:
backend/app/Filament/Resources/Suppliers/SupplierTransfers/ - Activity Resource:
backend/app/Filament/Resources/Suppliers/SupplierActivities/ - Tour Service:
backend/app/Services/SupplierTourService.php - Preview DTO:
backend/app/Filament/Resources/Offers/Schemas/OfferPreviewData.php