Skip to main content

Trip Proposal Creation Forms

In xperise, the trip is the central object — every business booking belongs to a trip. Before a user can book a flight, hotel, or other service for work, they first create the trip that booking belongs to.

Depending on the user's company, a trip is created through one of two forms:

  • Simple form — for companies that do not require a proposal. The trip is a generic type and captures only a name and a time range (from → to). There is no approval step. See section 9.
  • Proposal form — for companies that require a proposal. This is the detailed, multi-step flow that most of this document describes, with extra rules for special companies such as VCB and SCG. A proposal is governed by policies (described in the Policy documentation), and it can be approved automatically when the policies allow.

This page is an implementation specification for these creation forms. It describes how they behave so another team — for example, the mobile team — can rebuild the same behavior on their own platform.

It covers, for each form:

  1. Which form to show for a given company.
  2. Field layout — what fields appear on each screen, in order.
  3. Validation — which fields are required, the rules, and the exact error messages.
  4. Logic — how fields react to each other and what the app prepares behind the scenes.
  5. What is sent to the server on submit.
How to read this document
  • Text in code font is an exact value you must match — a field/data name, an enum value, an error message, a company ID, a URL, or a numeric limit. These are the shared contract with the backend, so copy them exactly.
  • The web app is built with Vue; this spec describes behavior, not Vue. You do not need to copy component names or internal function names to reproduce the feature.
  • A few items are marked Known issue — do not copy. Those are current web bugs; build the intended behavior described there, not the bug.

The forms at a glance​

The Simple form is a single minimal screen. The Proposal form comes in three variants that share the same three-step wizard and differ only in details:

FormKindUsed byIn one line
Simple formSimple tripCompanies that do not require a proposalMinimal screen — trip name and time range only; no approval.
General formProposalAny company that requires a proposal (default)The general-purpose proposal form.
SCGProposalSCG (when re-declaration is enabled)Adds car rental, cash advance, and reloads the policy for international trips.
VCBProposalVietcombankStrictest — ward-level address, SharePoint approval link, per-kilometer allowance, room policy by rank and gender.

1. Which form to show​

The app decides in two steps: first which kind of form — Simple or Proposal — then, for a proposal, which variant.

Step 1 — Simple form or Proposal form?​

This depends on whether the company requires a proposal (the company policy flag proposal.require):

  • The company does not require a proposal → Simple form.
  • The company requires a proposal → Proposal form.

Exception: VCB always uses its Proposal form, even when a proposal is not required.

Step 2 — Which proposal variant?​

When a Proposal form is used, the company decides the variant:

CompanyProposal variant
VCBVCB form
SCG — only when re-declaration is enabledSCG form
Any other company (and SCG when re-declaration is off)General form

Plain-language summary

  • Companies that do not require proposals (for example TPBank, Techcombank, Toshiba, Unicharm) get the Simple form — just a name and a time range.
  • VCB always gets the VCB form.
  • SCG gets the SCG form only when re-declaration is turned on; otherwise it gets the General form.
  • Any other company that requires a proposal gets the General form.

2. Shared wizard behavior​

This section applies to the General form, SCG, and VCB (the three Proposal-form variants). The Simple form is different — see section 9.

Layout​

  • The form opens as a panel that slides in from the right of the screen (on mobile, treat it as a full-screen flow).
  • It has three steps: 1. Trip information → 2. Services → 3. Review & submit. A step indicator shows progress.

When it is a wizard vs. a single screen​

  • The three-step wizard runs only for Business trips at companies that require a proposal.
  • If the company does not require a proposal, step 1 becomes a single screen: the main button reads Close instead of Continue, and submitting creates the trip directly.

How validation and errors work​

  • Validation runs when the user tries to move to the next step (tapping Continue), and again on Submit.
  • If any field on the current step is invalid, the app stays on that step, highlights the invalid fields with a red border, and shows the inline message "Please fill in information here" under each one.
  • Only when every field on the step passes does the app advance.

What the app prepares between steps​

  • The itinerary list is not built on step 1. It is generated when the user first reaches step 2, from the destinations entered on step 1 (see section 4).
  • Going back from step 3 to step 2 does not rebuild the itinerary — the user's step-2 entries are preserved.

Submitting​

  • On submit, the app sends the trip to the server (the create_trip operation). If the trip is being created on behalf of another user, the other user's id is included.
  • On success, the app either navigates to the trip detail page or, when embedded in a larger flow, reports success back to the parent and closes the panel.

3. Step 1 — Trip information​

Fields appear in this order. This is the shared baseline; company-specific differences follow the table.

Field (label)InputRequiredRules & behavior
Trip name (name)textYesIf empty, show "Please fill in information here".
TravelerspickerNoNot enforced here (no "at least one" check on this step). The number of travelers matters later for room allocation on step 2.
Traveler cost centersper-traveler percentage allocatorConditionalOnly shown for Business trips when the company requires a proposal and cost-center enforcement is on. Then, for each traveler, the percentages must total 100%, and a traveler must not be left with no cost center. Typing a value above 100 clamps it to 100; a value below 100 automatically adds another allocation row.
Departure (begin_location)location pickerYesA city must be chosen (the location must have a city).
Destinations + dates (locations[])location picker + date, one row per stopYesEach stop needs a location and a start date. Stops must be in chronological order; otherwise show the toast "Location dates must be in chronological order".
Full day / half day (half_day)toggle—Only shown when the trip is one day or shorter. A trip longer than one day is automatically set to full day.

VCB — differences​

  • Departure and destinations use a ward-level address picker. Ward is required for every location.
  • The final destination cannot be the same as the departure location; otherwise show "The final destination cannot be the same as the departure location".
  • Selecting the departure copies that location to every traveler as their home/office base.

SCG — differences​

  • Departure and destinations use a street-address picker.
  • Each stop's dates are split into a start date + start time (defaults to 00:00) and an end date + end time (defaults to 23:30).
  • The cost-center error also shows a toast: "Please assign at least one cost center to each traveler" (the General form shows only the inline red state, no toast).

General form — differences​

  • Uses a city picker (not street or ward level).
  • Dates are a single date range ("Period") rather than per-stop dates.
  • The trip is always Business (the Business/Personal choice is not offered).

4. Moving from step 1 to step 2​

When the user reaches step 2, the app builds the itinerary list from the destinations entered on step 1:

  • Each destination becomes one itinerary containing a flight and a hotel. Dates come from that destination's start/end dates.
  • If there is one destination, the flight is a round trip. If there are several destinations, each leg is one-way, and the app adds a final return leg (one-way) from the last destination back to the departure.

VCB — differences​

  • Transportation is fixed to company-arranged (not shown as a choice).
  • If the user did not change the locations, dates, or travelers, the existing itinerary is kept as-is (not rebuilt).

SCG — differences​

  • Any transportation and expense details the user already entered are preserved when the itinerary is rebuilt.
  • If any destination is international (country is not Vietnam), the app reloads the approval policy for that country, which can change the approval chain.

5. Step 2 — Services​

Each itinerary is shown as a card with a flight, a hotel, and room allocation (plus transportation/expense on some forms).

FieldInputRules & behavior
Flight — on/off, From / To airporttoggle + airport searchWhen the flight is on, both From and To airports are required (highlighted red if missing on submit).
Hotel — on/off, check-in / check-outtoggle + datesThe hotel location is read-only. If check-in is on or after check-out, check-out is automatically moved to +1 day.
Room allocationassign travelers to roomsRules depend on the form (below).

To move to step 3, the app validates the flight information, sets the trip's overall start/end dates from the first and last stops, and advances.

VCB — differences​

  • Transportation is hidden (already company-arranged). The hotel offers a choice: Internal (booked through xperise) or External (paid by VCB or a third party). Hotels are not allowed for half-day or single-day trips.
  • Room allocation rules (when there is more than one traveler): at most 3 people per room; no mixing of male and female in a room; a single-occupancy room is only allowed for travelers whose level is on an approved list (12 senior levels). Breaking a rule shows the toast "Per VCB policy single-room occupancy…". Full details in VCB — Room Arrangement & Budgeting.

SCG — differences​

  • Transportation can be None, Company-arranged, Internal, or Personal. When Internal with a car rental, a car-rental form appears with:
    • Rental type: full day or one-way route.
    • Pickup and drop-off location and time.
    • Vehicles and quantity, vendor, and a contact person.
  • Car-rental validation: pickup, drop-off, travelers, vehicles, and route are all required; the pickup time must be in the future; and drop-off must be after pickup.

General form — differences​

  • The flight shows a Round trip / One-way choice with visible departure/return dates (VCB hides these dates).
Known issue — do not copy

On the web, the flight check on step 2 has a typo and only checks the arrival airport, so a missing departure airport can slip through. Intended behavior: when a flight is enabled, require both the departure and arrival airports. Build the intended rule.


6. Moving from step 2 to step 3​

  • The trip's overall start/end dates are set from the first and last stops.
  • On step 3, the app calls the pricing policy to show an estimated reimbursement.
  • VCB only: the travel distance and the per-kilometer allowance are calculated at submit time, not on step 2 or 3.

7. Step 3 — Review & submit​

The top of step 3 is a read-only summary (name, dates, itinerary, travelers, rooms). The fields the user can still edit are:

FieldInputRules & behavior
Approval documentfile upload or linkRequired when there is at least one approver to skip, or the policy requires a file. If missing, show "Please provide the approval document(s)".
ApproversselectorHidden when approval is turned off. Per-level checks depend on the form (below).
Trip purpose (note)text areaOptional. On SCG, the placeholder changes to "Need a cash advance…".

Checks that run on submit, by form​

CheckGeneral formSCGVCB
Approval file required (when the condition above is met)✓✓✓
Enough approvers assigned for each level✕✓✓
Approval link must be a VCB SharePoint URL✕✕✓
Departure and destination not the same in the itinerary✕✕✓
Block a trip whose end date is in the past✕✓–
Contact phone required for car rental (via dialog)✕✓✕
Add the per-kilometer allowance✕✕✓

VCB — approval link. The approval document must be a link whose host is vcb365-my.sharepoint.com or vcb365.sharepoint.com. Any other link is rejected with a message asking for a link that starts with the VCB SharePoint address.

SCG — contact phone. Before submitting, the app checks every car-rental contact person for a phone number. If any is missing, it opens a dialog to collect the phone number, validates it, and then continues the submit.


8. What is sent to the server​

All forms follow the same submit steps: keep only the itineraries that actually have a service, normalize the traveler list, rebuild the partner list, take the first traveler's cost center, then create the trip.

note

The field names below are the API contract. Match them exactly so the backend accepts the request.

Trip-level fields​

FieldContent
nameTrip name.
typeBUSINESS or PERSONAL.
begin_at, end_atStart/end timestamps. If they are equal, they are expanded to the start and end of that day. VCB always sets end_at to the end of the day.
begin_location, end_locationDeparture and final location (trimmed — see the field list below).
locations[]Each stop (trimmed).
itineraries[]The kept itineraries (see below).
partners[]Travelers, rebuilt as { email, first_name, last_name, user_id }.
cost_center_idThe first traveler's cost center.
noteTrip purpose.
passed_approver_ids[]Approvers that were skipped.
trip_approve_by[]Only sent when the approver list was changed, as { level, user_ids }; otherwise omitted.
passed_files[] (VCB)The approval link, as { url }.
request_noteOnly when editing an already-approved trip.
half_dayFull/half-day flag.

Which location fields are kept​

Different forms keep different location detail:

Location fields kept, per form
VCB: address, area_code, city_code, city_name, map_url,
country_code, country_name, ward_code, ward_name, gg_place_id
SCG: address, area_code, city_code, city_name, map_url,
country_code, country_name (no ward)
General form: city_code, city_name, country_code, country_name, ward_code (reduced)

Which itineraries are kept​

An itinerary is only sent if it has at least one active service:

Keep this itinerary if…
flight is on
OR hotel is on
OR hotel is external
OR transportation is not "none"
OR it has at least one expense scenario

Each kept itinerary looks like this:

Itinerary sent to the server
{
begin_location, end_location, // trimmed location fields
distance, // in km, from the map distance service
flight: null | {
begin_at, begin_location, end_at, end_location, type,
arrival_code, arrival_airport_name,
departure_code, departure_airport_name,
travellers[]
},
hotel: null | {
begin_at, begin_location, end_at, end_location, travellers[], external?
},
transportation?: { type } // SCG car rental adds:
| {
type,
car_rental: [{
type, pickup_location, pickup_time,
dropoff_location, dropoff_time, route_id,
travellers: [{ user_id }],
vehicles: [{ id, quantity }],
vendor_id, note
}]
},
expense?: {
begin_at, end_at,
scenarios: [{ id, travellers: [{ begin_location, end_location, user_id, distance, cost_center }] }]
}
}

Travelers inside a flight or hotel are sent as:

Traveler entry
{ cost_center_id, cost_centers: [{ id, rate }], email, group_index, user_id }
// rate = percentage / 100

Per-form notes​

  • VCB includes company-arranged transportation, adds a per-kilometer expense scenario for each traveler, and carries the approval link in passed_files.
  • SCG sends the full car-rental details; its car-rental travelers include is_contact_person and phone_number; and it rejects a trip whose end date is in the past.
  • The General form usually has no transportation, car rental, or per-kilometer allowance — the simplest payload.
Approval document: file vs. link

This differs by form and is easy to get wrong:

  • SCG and the General form attach the approval document as a file. It is not part of the create-trip request — it is uploaded separately, after the trip is created.
  • VCB sends the approval document as a link, inside the create-trip request (passed_files).

9. The Simple form​

The Simple form is used when a company does not require proposals. It is a single minimal screen that creates a generic trip — there is no proposal wizard and no approval step.

Fields​

Field (label)InputRequiredRules & behavior
Trip name (name)textYesThe name of the trip.
Time (begin_at → end_at)date rangeYesThe trip's start and end (from → to). If start and end are the same day, they are expanded to the start and end of that day.

Everything else from the Proposal form (services, room allocation, approvers, approval document) does not apply. Submitting creates an ordinary trip directly.

Implementation note

For a company without proposals, build a simple one-screen "create trip" form with just a name and a time range; you do not need the three-step wizard. Cost-center handling is minimal (only the first traveler needs a cost center), and the proposal flow in the web app is intentionally turned off for this form.


10. Known issues — do not copy​

These are current web bugs. Build the intended behavior, not the bug.

AreaThe web bug todayIntended behavior to build
Flight airports (step 2)Only the arrival airport is checked (a typo); a missing departure airport can pass.When a flight is enabled, require both departure and arrival airports.
Simple form "Create" buttonThe button can get stuck showing a loading spinner when the cost-center check fails.Always stop the loading state when validation fails, so the button is usable again.
Demo routingThe "Demo" branch of the form-selection rules never triggers (a flag is hard-coded off).Only real SCG reaches the SCG form; no separate Demo path is needed.

Form-by-form summary​

FormKey differences to reproduce
Simple formSingle minimal screen — trip name and time range only; creates an ordinary trip (no proposal, no approval).
General formCity-level location; single date range; always Business; round-trip / one-way flight choice; the general-purpose proposal default.
SCGStreet address with split date/time; car rental with contact-phone requirement; cash-advance note; reloads policy for international trips; blocks past end dates.
VCBWard-level address; VCB SharePoint approval link; per-kilometer allowance; room policy by rank and gender; auto return-leg for multi-stop trips.