Skip to content

Pricing & Costs UI

Frontend screens, components, and user flows for the cost and pricing matrix subsystem.

Component Overview

ComponentDescription
Costs.vueOperational cost section: form + matrix + extra weights + CSV + bulk
Pricing.vuePricing section: form + tabbed matrix + CSV + bulk + margin indicators
useServicePricing.jsComposable encapsulating all pricing logic (~820 lines)
Bulk.modal.vueShared modal for bulk cell operations (add/sub/replace)
UpgradePricing.modal.vueModal to recalculate pricing from costs by percentage
MatrixViewShared component (components/interface/Matrix/MatrixView.vue) for editable zone × range grids

Costs Section (Costs.vue)

Layout

┌──────────────────────────────────────────────────────────────┐
│  💰 Operational Costs                    [Upload ▼] [Save]   │
│  Subtitle: Configure carrier costs...     ├─ Download Costs   │
│                                           └─ Upgrade Pricing  │
├──────────────────────────────────────────────────────────────┤
│  FormGenerator:                                               │
│  [ Locale ▼ ]  [ Currency (disabled) ]  [ Weight Unit ▼ ]    │
│  [ Fuel Type ▼ ]  [ Fuel Percentage % ]                      │
├──────────────────────────────────────────────────────────────┤
│  MatrixView: "Costs"                                          │
│  ┌────────┬──────┬──────┬──────┬──────┐                      │
│  │ Weight │Zone 1│Zone 2│Zone 3│Zone 4│                      │
│  ├────────┼──────┼──────┼──────┼──────┤                      │
│  │ 0-1 KG │$120  │$150  │$180  │$210  │                      │
│  │ 1-5 KG │$180  │$220  │$260  │$300  │                      │
│  └────────┴──────┴──────┴──────┴──────┘                      │
├──────────────────────────────────────────────────────────────┤
│  MatrixView: "Extra Weight Cost"                              │
│  ┌────────┬──────┬──────┬──────┬──────┐                      │
│  │ 5 KG + │ $15  │ $18  │ $20  │ $25  │                      │
│  └────────┴──────┴──────┴──────┴──────┘                      │
├──────────────────────────────────────────────────────────────┤
│                                              [Save] ▸        │
└──────────────────────────────────────────────────────────────┘

Data Flow

  1. Load: onMountedGET /services/{id}/costsmatrixData ref + store
  2. Edit: Cell changes update matrixData locally via handleCellUpdate
  3. Bulk: Select cells → Bulk.modalhandleBulkActionApply updates cells in memory
  4. CSV Upload: Parse CSV → rebuild matrixData (zones, ranges, extra_weights) → update store
  5. Save: Validate form → build payload from MatrixView.getMatrixData()Swal.question()PUT /services/{id}/costs → reload

Form Fields

FieldTypeSource
locale_idSelectGET /catalog/locales
currencyInput (disabled)Auto-filled from locale
weight_unit_codeSelectStatic: KG, LB
fuel_typeSelectStatic: flat, dynamic
fuel_percentageInput-group (%)User input

CSV Structure

ColumnRequiredDescription
service_idYesService identifier
zoneYesZone number
weight_extra_costYesPer-unit extra weight cost
weight_maxYesWeight range upper bound
costYesCost value

Pricing Section (Pricing.vue)

Layout

┌──────────────────────────────────────────────────────────────┐
│  📈 Pricing                              [Upload ▼] [Save ▼] │
│  Subtitle: Configure selling prices...    └─ Download Pricing │
├──────────────────────────────────────────────────────────────┤
│  FormGenerator:                                               │
│  [ Locale ▼ ]  [ Currency (disabled) ]  [ Weight Unit ▼ ]    │
│  [ Operator ▼ ]  [ Extended Zone $ ]                         │
├──────────────────────────────────────────────────────────────┤
│  Tabs: [ Basic | Pro | Enterprise | Corporate ]               │
│                                                               │
│  Toggle: [ Markup | Margin ]                                  │
│                                                               │
│  ⚠️ Danger margin alert (if any cell < 15%)                  │
│  ℹ️ Disclaimer: Margin = (price - cost) / price × 100        │
│                                                               │
│  MatrixView: "Pricing Matrix"                                 │
│  ┌────────┬────────────┬────────────┬────────────┐           │
│  │ Weight │   Zone 1   │   Zone 2   │   Zone 3   │           │
│  ├────────┼────────────┼────────────┼────────────┤           │
│  │ 0-1 KG │ $180       │ $220       │ $260       │           │
│  │        │ 25% ✅     │ 30% ✅     │ 20% ⚠️     │           │
│  ├────────┼────────────┼────────────┼────────────┤           │
│  │ 1-5 KG │ $250       │ $300       │ $350       │           │
│  │        │ 22% ⚠️     │ 18% ⚠️     │ 12% 🔴     │           │
│  └────────┴────────────┴────────────┴────────────┘           │
│                                                               │
│  MatrixView: "Extra Weight Cost"                              │
│  ┌────────┬──────┬──────┬──────┐                             │
│  │ 5 KG + │ $22  │ $26  │ $30  │                             │
│  └────────┴──────┴──────┴──────┘                             │
├──────────────────────────────────────────────────────────────┤
│                        [Save All Tabs] [Save Current Tab] ▸  │
└──────────────────────────────────────────────────────────────┘

Cell Formatting

Each pricing cell displays HTML content (:cell-html-content="true") with two lines:

Flat operator (1):

  • Line 1: Selling price (with strikethrough if changed from initial)
  • Line 2: Margin/markup % with color class

Margin operator (2):

  • Line 1: Calculated selling price = graduated_cost × (1 + markup%/100)
  • Line 2: Markup or margin % depending on viewType toggle

The formatCell function in useServicePricing handles all formatting logic, including:

  • Fetching cost from costMatrix by row/col index
  • Computing graduated cost (cost + tax + fuel)
  • Calculating margin/markup based on viewType
  • Generating HTML with strikethrough for changed values

Margin Color Coding

Defined in constants/pricing.constant.js:

javascript
SECURE_MARGINS = {
  DANGER:  { MAX: 15 },        // text-danger (red)
  WARNING: { MIN: 15, MAX: 25 }, // text-warning (yellow)
  SAFE:    { MIN: 25 }          // text-success (green)
}

CSV Structure

ColumnRequiredDescription
service_idYesService identifier
zoneYesZone number
weight_extra_costYesPer-unit extra weight price
weight_maxYesWeight range upper bound
basic_planNoBasic tariff price/markup
pro_planNoPro tariff price/markup
enterprise_planNoEnterprise tariff price/markup
corporate_planNoCorporate tariff price/markup

Only tariff columns present in the CSV are updated; missing columns are preserved as-is.

useServicePricing Composable

The composable encapsulates all pricing state and logic. Used exclusively by Pricing.vue.

State

Ref/ComputedTypeDescription
matrixDatarefCurrent pricing data (zones, ranges per tab, extra_weights per tab)
localesrefLocale options for the form
loading / errorrefLoading and error state
tabscomputed4 tabs from RATE_TYPES
activeTabrefCurrently selected tab name
viewTyperef'markup' or 'margin'
costMatrixcomputedCost data from store (for margin calculations)
taxescomputedDefault tax rule from store

Key Methods

MethodDescription
loadData()Fetch pricing from API, update store
handleCellUpdate(event, tab, type)Immutably update a cell in matrixData
handleBulkActionApply(action)Apply bulk operation to selected cells
handleSaveActiveTab()Save current tab only
handleSaveAllTabs()Save all 4 tabs sequentially
buildPayloadForTab(tab)Build API payload from MatrixView refs
handleUpgradePricing(event)Recalculate all tabs from cost matrix + percentages
handleLoad(data, modalRef)Parse CSV and merge into matrixData
downloadPricing()Export all tabs as multi-sheet Excel
formatCell(value, meta)Format cell as HTML with price + margin
formatCellExtraWeight(value, meta)Format extra weight cell as HTML
hasDangerMarginInTab(tab)Check if any cell has margin < 15%
parseCsvToMatrixData(data, base)Parse CSV rows into matrix structure

Bulk Actions Modal (Bulk.modal.vue)

Shared between Costs and Pricing. Operates on selected cells from a MatrixView.

FieldOptions
OperationAdd, Subtract, Replace
TypeFixed amount, Percentage
ValueNumeric input
Decimal ConfigRound, Truncate
Decimal Places0–10

The modal emits apply with the operation config and the selected cell coordinates. The parent component applies the transformation to each cell.

Upgrade Pricing Modal (UpgradePricing.modal.vue)

Accessible from the Costs section dropdown. Configures bulk pricing recalculation.

SettingDescription
OperatorFlat or Margin (determines calculation method)
Decimal ConfigRound or Truncate
Decimal PlacesGlobal default
Per tariff typeEnable/disable, global %, or per-zone % with per-zone decimals

Emits apply via EventBus('upgrade-pricing') which is caught by useServicePricing.handleUpgradePricing().

Key Patterns

  • MatrixView refs by tab: setMatrixViewRef(tabName, el, type) stores refs in matrixViews and matrixViewExtraCosts objects, keyed by tab name
  • Immutable state updates: handleCellUpdate creates new arrays instead of mutating in place, ensuring Vue reactivity
  • Cross-component communication: EventBus for Upgrade Pricing (Costs → Pricing), avoiding tight coupling
  • Locale-currency sync: Watching locale_id changes to auto-fill currency from locale data
  • Operator-matrixData sync: Watching operator changes to keep matrixData.operator and serviceDetails.pricing.operator in sync

Envia Admin