Skip to content

Additional Services & Charges UI

Frontend screens, components, and user flows for managing additional services and additional charges.

Component Overview

ComponentDescription
AdditionalServices.vueTable of optional services (mandatory=false) with toggle and actions
AdditionalCharges.vueTable of mandatory charges (mandatory=true) with toggle and actions
AdditionalService.offcanvas.vueShared offcanvas for add/edit (both services and charges)

Both table components are nearly identical — the key differences are:

  • The API call uses ?mandatory=false vs ?mandatory=true
  • AdditionalCharges opens the offcanvas with showAddForCharge() (sets mandatory=true by default)
  • The offcanvas shows a Conditions section only when mandatory=true

Table Layout

Both AdditionalServices.vue and AdditionalCharges.vue share the same layout:

┌──────────────────────────────────────────────────────────────┐
│  🧩 Additional Services                          [+ Add]     │
│  Subtitle: Optional add-on services...                       │
├──────────────────────────────────────────────────────────────┤
│  SimpleTable:                                                │
│  ┌─────────┬──────────┬────────┬─────┬─────┬────────┬──────┐│
│  │Descript.│ Operator │ Amount │ Min │ Max │ Active │Action││
│  ├─────────┼──────────┼────────┼─────┼─────┼────────┼──────┤│
│  │Insurance│Min+Comm  │ 3%     │ $50 │ —   │ [✓]    │[Edit]││
│  │Ext.Area │Range Plan│2 ranges│ —   │ —   │ [✓]    │[Edit]││
│  │Signature│Flat      │ $35    │ —   │ —   │ [ ]    │[Edit]││
│  └─────────┴──────────┴────────┴─────┴─────┴────────┴──────┘│
└──────────────────────────────────────────────────────────────┘

Cell Formatting

The formatCellValue function handles smart display based on operator type:

Operator TypeAmount Display
Percentage operators (2, 3, 5, 6, 11, 13, 18)15% (value × 100)
Flat USD (9)$25.00 USD
Highest Flat/% USD (13)minimum_amount shows in USD
Package Weight (10, 16)$5.00 / kg
Base + Weight (17)$100.00 + $5.00 / kg
Range-based (4, 15, 19)"2 ranges" (count of plan_definitions)
All othersCurrency formatted amount

Toggle Active

The toggle switch calls PATCH /services/{id}/additionals/{itemId} with { active: 0|1 } immediately on change. No confirmation dialog — the change is instant with a toast notification.

On error, the toggle reverts to its previous state via nextTick.

Offcanvas (AdditionalService.offcanvas.vue)

The shared offcanvas handles both add and edit for both services and charges. It opens at 66% width and contains 3 form sections plus an optional conditions section.

Entry Points

MethodCallerBehavior
showAdd()AdditionalServices.vueOpens in add mode, mandatory=false
showAddForCharge()AdditionalCharges.vueOpens in add mode, mandatory=true
showEdit(item)Both tablesOpens in edit mode, pre-fills all fields

Form Structure

┌──────────────────────────────────────────────────────────────┐
│  Add Additional Service                               [Save] │
├──────────────────────────────────────────────────────────────┤
│  [Service Type ▼] (add mode only)                            │
│                                                              │
│  ─── General Configuration ──────────────────────────────    │
│  [ Apply To ▼ ]                                              │
│  [ ] Mandatory    (info: auto-applied when conditions met)   │
│  [ ] Editable     (info: amount can be modified per shipment)│
│  [ ] Force Save   (info: save even if already present)       │
│  [ ] Active       (info: enable or disable)                  │
│                                                              │
│  ─── Taxes ──────────────────────────────────────────────    │
│  [ ] Include VAT  (info: VAT included in amount)             │
│  [ Tax % ▼ ]      (options from service tax rules)           │
│                                                              │
│  ─── Pricing ────────────────────────────────────────────    │
│  [ Operator ▼ ]   (locked in edit mode)                      │
│  [ Amount fields ] (dynamic, based on operator)              │
│                                                              │
│  ┌─ Plan Definitions (operators 4, 15, 19 only) ──────────┐ │
│  │  [+ Add Range]                                          │ │
│  │  ┌─────┬─────┬──────────┬────────┬─────────┬──────┐    │ │
│  │  │ Min │ Max │ Rate Type│ Amount │ Min Amt │ 🗑️  │    │ │
│  │  ├─────┼─────┼──────────┼────────┼─────────┼──────┤    │ │
│  │  │ 0   │ 5   │ Flat     │ $85    │ —       │ [x]  │    │ │
│  │  │ 5   │ 30  │ Highest  │ 15%    │ $85     │ [x]  │    │ │
│  │  └─────┴─────┴──────────┴────────┴─────────┴──────┘    │ │
│  └─────────────────────────────────────────────────────────┘ │
│                                                              │
│  ┌─ Conditions (mandatory=true only) ──────────────────────┐ │
│  │  [+ Add Condition]                                      │ │
│  │  ┌───────────┬──────────┬───────┬────────┬──────┐      │ │
│  │  │ Reference │ Operator │ Value │ Active │ 🗑️  │      │ │
│  │  ├───────────┼──────────┼───────┼────────┼──────┤      │ │
│  │  │ weight    │ >        │ 30    │ [✓]    │ [x]  │      │ │
│  │  │ zone      │ =        │ 8     │ [✓]    │ [x]  │      │ │
│  │  └───────────┴──────────┴───────┴────────┴──────┘      │ │
│  └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

Dynamic Amount Fields

Amount fields change based on the selected operator. The mapping is defined in OPERATOR_FIELDS in pricing.constant.js:

OperatorFields Shown
1 (Flat)amount (currency)
2 (Percentage)amount (%)
3 (Highest)amount (%), minimum_amount (currency)
5 (Min+Commission)minimum_amount (currency), amount (%)
9 (Flat USD)amount (USD)
10 (Package Weight)amount (currency/kg)
16 (Weight or Min)minimum_amount (currency), amount (currency/kg)
17 (Base+Weight)base_amount (currency), amount (currency/kg)
4, 15, 19 (Range)No amount fields — uses plan_definitions table instead
7 (Web Service)No amount fields

Plan Definitions Table

Visible only for operators 4, 15, and 19. Each row represents a pricing range:

ColumnOp 4 (Weight)Op 15/19 (Amount/Insurance)
Min ValueWeight in kgAmount in currency
Max ValueWeight in kgAmount in currency
Rate TypeFlat (1), % (2), Highest (3)Same
AmountRate for this rangeRate for this range
Min AmountFlat fallback (op 3 only)Flat fallback (op 3 only)

Rate type operators within plan definitions:

  • 1 (Flat): Amount is a fixed fee
  • 2 (Percentage): Amount is stored as decimal (0.15), displayed as 15%
  • 3 (Highest): Higher of percentage or minimum_amount

Conditions Table

Visible only when mandatory=true (additional charges). Each row is an activation condition:

ColumnComponentDescription
Reference ValueSelect2Property to evaluate (weight, zone, length, etc.)
OperatorSelect2Comparison: >, >=, <, <=, =, !=
ValueText inputThreshold value
ActiveSwitchEnable/disable this condition
ActionsButtonRemove row

Payload Building

The buildPayloadForOperator function handles operator-specific payload construction:

  1. Reads form state from FormGenerator
  2. Converts percentage amounts from display (15) to API decimal (0.15) via toDecimalPct
  3. For range-based operators, calls buildPlanDefinitionsPayload() to serialize plan definitions
  4. For mandatory items, calls buildConditionsPayload() to serialize conditions
  5. Maps boolean switches to 0/1 integers

Validation

  1. Service type (add mode): Required, validated on submit
  2. Operator: Required, validated on submit
  3. Plan definitions (ops 4/15/19): At least one row required, each row must have min_value, max_value, operation_id, amount
  4. Form fields: Validated via FormGenerator + Vuelidate
  5. Field-level validation errors are shown inline with is-invalid classes

Constants (pricing.constant.js)

Key exports used by this subsystem:

ExportDescription
OPERATOR_IDSNamed constants for all 19 operators
OPERATOR_FIELDSPer-operator form field definitions
PERCENTAGE_AMOUNT_OPERATOR_IDSSet of operators that store amount as decimal
NO_AMOUNT_FIELDS_OPERATOR_IDSSet of operators with no amount form fields
PLAN_DEFINITIONS_OPERATOR_IDSArray of operators using plan_definitions (4, 15, 19)
PLAN_ROW_PERCENTAGE_IDSSet of plan definition rate types that use percentage (2, 3)
CONDITION_REFERENCE_VALUESArray of available condition reference values
CONDITION_OPERATORSArray of condition comparison operators
APPLY_TO_OPTIONSApply-to scope options (unit, package, shipment)

Key Patterns

  • Shared offcanvas: Single component handles both add and edit for both services and charges, reducing duplication
  • Dynamic form sections: FormGenerator with computed formWithGeneralConfig that rebuilds when operator changes
  • Operator-locked edit: operation_id select is disabled in edit mode to prevent data model conflicts
  • Percentage conversion: UI shows percentages as integers (15%), API stores as decimals (0.15); conversion happens in buildPayloadForOperator and formBaseForOperator
  • Catalog lazy-load: Operators and service types are fetched once on mount and cached in component refs
  • Teleport-free: Unlike Organization Settings, this offcanvas doesn't need teleport since it's not inside another overlay

Envia Admin