Add tariff groups & fees
Each fee category for the port (harbour dues, pilotage, boatmen, waste…) is a tariff group. Inside each group you add one or more tariffs — the individual fee lines. This page walks through creating both.
Open the Tariff groups screen
Go to Sidebar → Admin → Cost Calculations → Tariff groups (route /invoicing-and-tariffs/tariff-groups/cost-calculation).
You need the Tariff admin permission (invoices.tariff_admin) to create or edit groups.
Step 1 — Create a tariff group
Click New and fill in the group details:
| Field | What to enter |
|---|---|
| Name | Descriptive name, e.g. Den Helder — Harbour dues |
| System code | pcc_<port>_<category>, e.g. pcc_denhelder_havengeld |
| Currency code | EUR |
| Starts on | First date the fees apply, e.g. 2026-01-01 |
| Ends on | Leave blank for open-ended |
| UI Template | Label shown as the result section header, e.g. Harbour dues |
| Tariff group type | Invoice calculation |
| Application area | Read-only — already set to cost calculation |
Click Save. You are now on the tariff group details page.
Step 2 — Gate the group to your port
Every tariff group must have a port gate condition. Without it the group's tariffs apply to all ports, not just yours.
On the Business logic card, open the Conditions tab and click Add:
| Field | Value |
|---|---|
| Logic type | Custom |
| Field type | ConditionString |
| Source field | $.__DefinitionSystemCode |
| String operator | Equals |
| Value | pccDenHelder (your port's System code — exactly as you set it on the object) |
Click Save on the condition. Repeat this on every tariff group you create for this port.
Step 3 — Add the tariffs (fees)
From the tariff group details page, navigate to the group's tariff list and click Add tariff. The tariff form has a Details card and a Business logic card.
Common fields for every tariff:
| Field | What to enter |
|---|---|
| Name | Fee name, e.g. Harbour dues — zeeschip Nieuwe Diep |
| Amount type | Number |
| Tariff type | Regular |
| Currency code | EUR |
| System code | Short stable code |
| UI Template | Label shown on the result line — required, cannot be empty |
| Starts on / Ends on | Match the fee schedule validity period |
The Amount source field controls which shape the fee takes. Choose Direct or Matrix and fill in the fields described in each shape below.
Shape A — a rate per unit
Use this when the fee is a fixed rate multiplied by a vessel attribute (e.g. €0.93 per GT).
Details card:
| Field | Value |
|---|---|
| Amount source | Direct |
| Amount | The rate, e.g. 0.93 |
| UI Template | e.g. Port dues ({{$.ship.gt}} × 0.93) — put the template in backticks if you want it literal, or use the actual Handlebars syntax inside the field |
Business logic — Calculations tab → Add:
| Field | Value |
|---|---|
| Logic type | Custom |
| Field type | CalculationPricePerUnit |
| Source field | $.ship.gt (or $.ship.loa, $.tugsInbound.numberOfTugs, etc.) |
| Calculation order | 0 |
This tells the engine to multiply the Amount by the value at that field path.
Business logic — Conditions tab (optional): if this rate only applies to certain ship types, add a ConditionArray (see Conditions reference below).
Den Helder example: harbour dues for zeeschepen at Nieuwe Diep — Amount 0.93, CalculationPricePerUnit source $.ship.gt.
Shape B — a bracket lookup
Use this when the fee depends on which band a value falls into (e.g. pilotage priced by draught bracket).
Details card:
| Field | Value |
|---|---|
| Amount source | Matrix |
| Amount | 0 |
| Amount source template | The Handlebars key that drives the lookup, e.g. {{pilotsOutbound.draft}} |
Business logic — Conditions tab → Add a ConditionNumeric to guard against zero/empty input:
| Field | Value |
|---|---|
| Field type | ConditionNumeric |
| Source field | pilotsOutbound.draft |
| Numeric operator | GreaterThan |
| Value | 0 |
Then fill the Tariff Matrix card (see Step 4).
Den Helder example: inbound pilotage — Amount source Matrix, Amount source template {{pilotsInbound.draft}}, 169 draught brackets in dm.
Shape C — a flat fee
Use this when the fee is a fixed amount, optionally only for certain vessel types or conditions.
Details card:
| Field | Value |
|---|---|
| Amount source | Direct |
| Amount | The fixed fee, e.g. 120 |
Business logic — Conditions tab: add conditions to restrict when the tariff fires (e.g. ConditionArray on $.podh.callPeriod with Keys day). Leave conditions empty if the fee always applies.
Do not add a CalculationPricePerUnit node — none is needed for a flat fee.
Den Helder example: boatmen daytime — Amount 120, ConditionArray on $.podh.callPeriod = ["day"].
Step 4 — Fill the tariff matrix (brackets)
The Tariff Matrix card appears only when Amount source = Matrix. It is not shown for Direct tariffs.
Each row is a bracket:
| Column | Notes |
|---|---|
| Name | Bracket label, e.g. 0–270 dm |
| System code | Auto-filled; editable |
| Amount | Rate for this bracket |
| From | Lower bound (inclusive). The first bracket must start at 0. |
| To | Upper bound (exclusive). The last bracket should use a large number, e.g. 9999, to cover all values. |
| UI Template | Label for this bracket's result line |
Brackets must be contiguous — the To of one row equals the From of the next. Gaps cause the lookup to return nothing.
Click Add at the bottom of the card to append a blank row. Fill each row and click Save.
Importing brackets from Excel
For large bracket tables (Den Helder pilotage has 169 rows), use the import:
- Make sure Amount source = Matrix is saved on the tariff — the Import Tariff Matrix button only appears in view mode (not edit mode).
- Click Import Tariff Matrix in the page header.
- In the modal, click Download example file first to get the correct column layout.
- Fill the example file with your brackets.
- Tick Replace existing tariff matrix if you want to overwrite any rows already there.
- Click Import.
Always start with Download example file — the column order in the template is the one the importer expects.
Conditions reference
Add conditions on the Conditions tab of the Business logic card (on the tariff, not the group — unless it is the port gate, which goes on the group).
| Condition type | When to use | Key fields |
|---|---|---|
| ConditionString | Port gate — match the port System code | Source field $.__DefinitionSystemCode, String operator Equals, Value = pccDenHelder |
| ConditionArray | Match against a List field (ship type, area, call period…) | Source field e.g. $.general.shipType, Array operator Any, Values = array of List Keys e.g. tanker_lng, bulkcarrier |
| ConditionNumeric | Numeric comparisons (draft > 0, GT between two values…) | Numeric operator: Between / Equals / GreaterThan / SmallerThan |
| ConditionBoolean | Yes/no flag fields | Boolean operator True or False |
The ConditionBoolean type (for fields like ispsApplicable) was not present in the reference data used to build this documentation. The UI form shows True / False operators, but verify the result against a live boolean condition before using it in production.
Some fees should fire only once per port call regardless of how many tariff lines match (for example a waste fee that should not double-count). This line grouping behaviour has no UI screen — it requires a configuration change by the SHIPM8 team. Contact support if a fee needs it.