Skip to main content

Charges

Charges play an integral role in determining the expenses and income associated with orders. They are added to an order and subsequently posted to an invoice through the reconciliation process.

The Lifecycle of a Charge:

  1. Creation:

    • A charge is initially appended to an order
  2. In Progress: Open or Pending

    • "Open" status, signifying that it hasn't been invoiced yet.
    • "Pending" status indicates it isn't ready for invoicing, perhaps because certain criteria haven't been met. For instance, the charge might be added, but the billed weight may not be available.
  3. Invoicing: Posted

    • Post-reconciliation, charges are transferred to the invoice.
    • At this stage, the charge's status transitions to "Posted".
  4. Payment: Paid

    • Once the associated invoice is settled, the charge status updates to "Paid".
    • Importantly, once marked as "Paid", a charge can neither be altered nor deleted.

Charge Details

  • Charge can be 2 main types: Freight and General.
  • Freight charges are used for freight-related items or services that are direct freight, e.g., Ocean Freight, Air Freight, Ground Freight, etc.
  • General charges are used for items or services that don't fit under Freight or the other listed categories.

Freight Charges

Freight Charge is a charge that is related to the freight service. It can be either a cost or income. It uses rates to calculate the amount and order commodity to calculate the quantity. In addition, rate and quantity can be entered manually in the charge if needed.

Allow Automatic Update

If this option is enabled, the charge will be automatically updated when the order is updated. For example, if the order commodity is changed, the charge quantity will be updated accordingly.

General Charges

General Charge is a charge that is not related to the freight service. General charge rate can be copied from Accounting Items or it can be entered manually in the charge.

Fields:

  • Accounting Item: Accounting item that is used to calculate the amount of the charge and to determine the account to which the charge will be posted.
  • Tax Code: Tax code that is used to calculate the tax amount of the charge.
  • Currency: Currency of the charge.
  • Description: Description of the charge.
  • Bill to Account: Customer Account to which the charge will be billed.
  • Paid As: Determines how the charge will be paid. It can be either "Prepaid" or "Collect".
  • Rate: Rate of the charge. _ (Freight Charge Only) _
  • Apply By: Determines how the quantity will be calculated. Possible values: Pieces, Weight, Volume, Container, Calculated (%) _ (Freight Charge Only) _
  • Is Consolidated: Determines whether the charge is consolidated or not.
  • Show on Document: Determines whether the charge will be shown on the invoice or not.
  • Allow Automatic Update: Determines whether the charge will be automatically updated when the order is updated. _ (Freight Charge Only) _
  • Quantity: Quantity of the charge.
  • Unit: Unit of the charge. Example: kg, m3, etc.
  • Price: Price of the charge.
  • Amount: Calculated amount of the charge. It is calculated as Quantity * Price.
  • Notes: Notes of the charge. Notes are shown on the invoice.

Charge Calculation

How Quantity is Calculated

When Allow Automatic Update is enabled, the system automatically calculates the charge quantity based on the Apply By setting and order commodities.

Apply By Types

The Apply By field determines how the charge quantity is calculated from order commodities.

Flat Rate
  • Quantity: Always 1
  • Use Case: Fixed fees that don't depend on shipment size (documentation fees, handling charges, flat delivery fees)
  • Calculation: Quantity = 1
  • Note: Price determines the total charge amount
Pieces
  • Quantity: Total number of pieces from matching commodities
  • Use Case: Per-piece handling, carton charges, piece-based storage fees
  • Calculation:
    • For containers: Sum of child commodity pieces (filtered by container and child BillToContactId)
    • For standalone: Sum of commodity pieces (filtered by BillToContactId)
  • Example:
    Container (null) with:
    - 10 pieces (Customer A)
    - 5 pieces (Customer B)

    Customer A charge: 10 pieces
    Customer B charge: 5 pieces
Weight
  • Quantity: Total weight in kg or lb from matching commodities
  • Use Case: Freight charges based on actual weight, weight-based storage
  • Calculation:
    • For containers: Sum of child commodity weight (filtered by container and child BillToContactId)
    • For standalone: Sum of commodity weight (filtered by BillToContactId)
    • Weight is converted to the rate's unit (kg or lb) before summing
  • Example:
    Container (null) with:
    - 100kg (Customer A)
    - 50kg (Customer B)
    - 5kg shared packing (null)

    Customer A charge: 100kg + 5kg = 105kg
    Customer B charge: 50kg + 5kg = 55kg
Volume
  • Quantity: Total volumetric weight from matching commodities
  • Use Case: Volume-based freight (especially air freight), cubic meter charges
  • Calculation:
    • For containers: Sum of child commodity volume weight (filtered by container and child BillToContactId)
    • For standalone: Sum of commodity volume weight (filtered by BillToContactId)
    • Volume weight = (Length × Width × Height) / divisor
    • Divisor typically 5000 for kg, 166 for lb (configurable)
  • Example:
    Container (null) with:
    - 2.5 m³ (Customer A)
    - 1.8 m³ (Customer B)

    Customer A charge: 2.5 m³
    Customer B charge: 1.8 m³
Chargeable Weight
  • Quantity: Higher of actual weight or volumetric weight for each commodity, then summed
  • Use Case: Standard freight pricing (ocean, air, ground) where both weight and volume matter
  • Calculation:
    • For each commodity: max(actual weight, volumetric weight)
    • Then sum all chargeable weights (filtered by container and child BillToContactId)
    • This ensures shipments pay for whichever dimension (weight or volume) is greater
  • Example:
    Container (null) with:
    - Item A: actual 100kg, volumetric 120kg → chargeable 120kg (Customer A)
    - Item B: actual 80kg, volumetric 60kg → chargeable 80kg (Customer A)

    Customer A charge: 120kg + 80kg = 200kg
  • Why It Matters: Light but bulky items pay for space they occupy; heavy compact items pay for weight
Container
  • Quantity: Count of containers by type
  • Use Case: Container-specific charges (terminal handling, container rental, per-container fees)
  • Calculation:
    • Counts distinct containers matching the charge's container type filter
    • Each container counts as 1 unit regardless of contents
    • Filters by container's BillToContactId
  • Example:
    Order with:
    - 2× 20ft containers (Customer A)
    - 1× 40ft container (Customer B)

    20ft Container Charge for Customer A: 2 containers
    40ft Container Charge for Customer B: 1 container
  • Note: This is the ONLY Apply By type that counts container commodities themselves rather than children
Calculated
  • Quantity: Percentage-based calculation from other charges
  • Use Case: Commission fees, percentage-based markups, profit shares
  • Calculation Types:
    • Income: Percentage of total income charges on the order
    • Expense: Percentage of total expense charges on the order
    • Profit: Percentage of difference between income and expense
  • Example:
    Order with:
    - Freight charge: $1000 (income)
    - Handling: $200 (income)
    - Cost: $800 (expense)

    Commission (5% of Income): $1200 × 0.05 = $60
    Profit Share (10% of Profit): ($1200 - $800) × 0.10 = $40

Split Billing with BillToContactId

The system supports split billing where different commodities in the same order can be billed to different customers using the BillToContactId field on commodities.

How BillToContactId Works

  • BillToContactId = null: Commodity is included in ALL charges (universal billing)
  • BillToContactId = Customer A: Commodity is ONLY included in charges where ApplyToContactId = Customer A

Example: Multi-Customer LCL Shipment

Order with 3 commodities:
├── Commodity A (30kg, BillToContactId = null) → Charged to ALL customers
├── Commodity B (50kg, BillToContactId = Customer 1) → Only Customer 1
└── Commodity C (70kg, BillToContactId = Customer 2) → Only Customer 2

Freight Charge 1 (ApplyToContactId = Customer 1, ApplyBy = Weight):
Quantity = 30kg (Commodity A) + 50kg (Commodity B) = 80kg

Freight Charge 2 (ApplyToContactId = Customer 2, ApplyBy = Weight):
Quantity = 30kg (Commodity A) + 70kg (Commodity C) = 100kg

Use Case: Co-loading where multiple customers share container space, each paying for their specific items plus shared materials.

Container Commodity Handling

When commodities are marked as containers (using Package Category with IsContainer = true), the system handles them specially:

Container vs. Standalone Commodities

The system processes commodities using two paths:

  1. Container Path: For commodities where PackageCategory.IsContainer = true

    • System drills down into child commodities (items inside the container)
    • Uses child commodity properties (pieces, weight, volume)
    • Filters based on both container and child BillToContactId:
      • If container has BillToContactId set, only charges matching that customer include this container's children
      • Within that container, children are further filtered by their own BillToContactId
    • Container's own properties (weight, pieces) are not counted - only children
  2. Standalone Path: For regular commodities

    • Uses commodity's own properties directly
    • Filters by commodity's BillToContactId

Example 1: Container with No BillToContactId (Shared Container)

Order:
└── Container Pallet (IsContainer = true, BillToContactId = null)
├── Child Item A (10 pieces, 20kg, BillToContactId = null)
├── Child Item B (5 pieces, 15kg, BillToContactId = Customer 1)
└── Child Item C (8 pieces, 25kg, BillToContactId = Customer 2)

Freight Charge (ApplyToContactId = Customer 1, ApplyBy = Pieces):
Container matches (null = any customer) → Check children:
Quantity = 10 pieces (Item A, null) + 5 pieces (Item B, Customer 1) = 15 pieces

Freight Charge (ApplyToContactId = Customer 2, ApplyBy = Weight):
Container matches (null = any customer) → Check children:
Quantity = 20kg (Item A, null) + 25kg (Item C, Customer 2) = 45kg

Use Case: LCL shipment where multiple customers share one container.

Example 2: Container Assigned to Specific Customer

Order:
├── Container A (IsContainer = true, BillToContactId = Customer 1)
│ ├── Child Item A1 (10 pieces, 20kg, BillToContactId = null)
│ └── Child Item A2 (5 pieces, 15kg, BillToContactId = Customer 1)
└── Container B (IsContainer = true, BillToContactId = Customer 2)
├── Child Item B1 (8 pieces, 25kg, BillToContactId = null)
└── Child Item B2 (3 pieces, 12kg, BillToContactId = Customer 2)

Freight Charge (ApplyToContactId = Customer 1, ApplyBy = Pieces):
Container A matches (Customer 1) → Check children:
10 pieces (A1, null) + 5 pieces (A2, Customer 1) = 15 pieces
Container B does NOT match (Customer 2) → Skip entire container
Total Quantity = 15 pieces

Freight Charge (ApplyToContactId = Customer 2, ApplyBy = Weight):
Container A does NOT match (Customer 1) → Skip entire container
Container B matches (Customer 2) → Check children:
25kg (B1, null) + 12kg (B2, Customer 2) = 37kg
Total Quantity = 37kg

Use Case: Full container loads (FCL) where each container belongs to one customer.

Example 3: Container with Mixed Child Ownership

Order:
└── Container (IsContainer = true, BillToContactId = Customer 1)
├── Child Item A (10 pieces, BillToContactId = null)
├── Child Item B (5 pieces, BillToContactId = Customer 1)
└── Child Item C (8 pieces, BillToContactId = Customer 2)

Freight Charge (ApplyToContactId = Customer 1, ApplyBy = Pieces):
Container matches (Customer 1) → Check children:
10 pieces (Item A, null) + 5 pieces (Item B, Customer 1) = 15 pieces
Item C is EXCLUDED (Customer 2 ≠ Customer 1)
Total Quantity = 15 pieces

Freight Charge (ApplyToContactId = Customer 2, ApplyBy = Pieces):
Container does NOT match (Customer 1 ≠ Customer 2) → Skip entire container
Total Quantity = 0 pieces

Important: Even though Child Item C has BillToContactId = Customer 2, it is NOT included in Customer 2's charge because the container itself belongs to Customer 1.

Important Notes:

  • ✅ Container's BillToContactId controls which charges can access the container
  • ✅ Child BillToContactId provides additional filtering within accessible containers
  • ✅ Container's pieces/weight are not counted - only child items are summed
  • BillToContactId = null means "any customer" at both container and child level

Expected Behavior by Apply By Type

Pieces Calculation

Order:
├── Standalone Item (10 pieces, BillToContactId = Customer 1)
└── Container (IsContainer = true)
├── Child A (5 pieces, BillToContactId = Customer 1)
└── Child B (3 pieces, BillToContactId = Customer 2)

Charge to Customer 1 (ApplyBy = Pieces):
Quantity = 10 (standalone) + 5 (child A) = 15 pieces

Weight Calculation

Order:
├── Standalone Item (100kg, BillToContactId = Customer 1)
└── Container (IsContainer = true)
├── Child A (50kg, BillToContactId = Customer 1)
└── Child B (30kg, BillToContactId = Customer 2)

Charge to Customer 1 (ApplyBy = Weight):
Quantity = 100kg (standalone) + 50kg (child A) = 150kg

Volume Calculation

Order:
├── Standalone Item (2 m³, BillToContactId = Customer 1)
└── Container (IsContainer = true)
├── Child A (1.5 m³, BillToContactId = Customer 1)
└── Child B (0.8 m³, BillToContactId = Customer 2)

Charge to Customer 1 (ApplyBy = Volume):
Quantity = 2 m³ (standalone) + 1.5 m³ (child A) = 3.5 m³

Chargeable Weight Calculation

Order:
├── Standalone Item (actual: 100kg, volumetric: 120kg, BillToContactId = Customer 1)
└── Container (IsContainer = true)
├── Child A (actual: 50kg, volumetric: 45kg, BillToContactId = Customer 1)
└── Child B (actual: 30kg, volumetric: 35kg, BillToContactId = Customer 2)

Charge to Customer 1 (ApplyBy = Chargeable Weight):
Quantity = max(100, 120) + max(50, 45) = 120kg + 50kg = 170kg

Note: Chargeable Weight takes the higher value between actual weight and volumetric weight for each commodity, then sums them per customer.

Common Scenarios

Scenario 1: Shared Packing Materials

Container:
├── Customer A boxes (BillToContactId = A, 100kg)
├── Customer B boxes (BillToContactId = B, 150kg)
└── Shared packing material (BillToContactId = null, 10kg)

Charge to Customer A (Weight):
100kg + 10kg (shared) = 110kg

Charge to Customer B (Weight):
150kg + 10kg (shared) = 160kg

Scenario 2: Mixed Container and Standalone

Order:
├── Container with Customer A items (50kg inside)
└── Standalone Customer B pallet (100kg, not a container)

Charge to Customer A (Weight):
50kg (from container children)

Charge to Customer B (Weight):
100kg (standalone item)

Important Rules

Container BillToContactId controls access - Set BillToContactId on containers to assign entire container to a specific customer (FCL). Leave as null for shared containers (LCL).

Child BillToContactId provides fine-grained control - Within accessible containers, children can be further filtered by their own BillToContactId.

Null means universal - BillToContactId = null at any level means "accessible to all customers":

  • Container with null: All customers can access its children
  • Child with null: Included for any customer accessing the container
  • Use for shared materials and multi-customer shipments

⚠️ Empty containers contribute nothing - If a container has no child items, it contributes 0 to charge calculations.

⚠️ Two-level filtering - Charges must pass BOTH filters:

  1. Container level: Charge's ApplyToContactId must match container's BillToContactId (or container is null)
  2. Child level: Charge's ApplyToContactId must match child's BillToContactId (or child is null)

Validation Rules

The system enforces the following rules:

  1. ✅ Container commodities can have BillToContactId set to assign entire container to a customer
  2. ✅ Container commodities can have BillToContactId = null for shared containers (LCL)
  3. ✅ Child commodities can have BillToContactId set for item-level customer assignment
  4. ✅ Child commodities can have BillToContactId = null for shared items within a container
  5. ⚠️ Charges with no matching commodities will have Quantity = 0

Troubleshooting

Problem: Charge quantity is 0

  • Cause 1: Container has BillToContactId set to a different customer
    • Solution: Check container's BillToContactId - if set to Customer A, only Customer A can access it
  • Cause 2: No child commodities match the charge's ApplyToContactId
    • Solution: Check that child BillToContactId values match the charge customer
  • Cause 3: Container is accessible but has no child items
    • Solution: Add child commodities inside the container

Problem: Customer cannot see items in a container

  • Cause: Container's BillToContactId is set to a different customer
  • Solution: For LCL/shared containers, set container's BillToContactId = null
  • Example: Container has BillToContactId = Customer A, so Customer B cannot access it even if children have BillToContactId = Customer B

Problem: Charge quantity includes unexpected items

  • Cause: Container or children have BillToContactId = null (universal)
  • Solution:
    • For FCL: Set container's BillToContactId to specific customer
    • For LCL: Set child BillToContactId to specific customers for item-level filtering
    • Only use null for intentionally shared items

Problem: Container weight/pieces not counted

  • Cause: Containers are meant to hold child items, not be counted themselves
  • Solution: Add the actual items as child commodities inside the container
  • Note: This is by design - container properties don't contribute to calculations

Problem: Weight charge is 0 even though container has weight

  • Cause: Container weight is not used when children exist - only child weights are summed
  • Solution: Create a "Box Total" child commodity with the container's weight
  • Example:
    Container: Box (Weight = 10kg, IsContainer = true)
    ├── Child: Box Total (Weight = 10kg, Pieces = 1) ← Add this!
    ├── Child: Item A (Weight = 0, Pieces = 5) ← Description only
    └── Child: Item B (Weight = 0, Pieces = 3) ← Description only

    Result: Weight charge = 10kg (from Box Total child)
  • See: Best Practices > For Boxes/Cartons with Only Container Weight

Best Practices

For LCL (Less than Container Load) Shipments

When handling shipments with multiple customers sharing container space:

  1. Create Container Structure:

    • Create a container commodity with Package Category where IsContainer = true
    • Set container's BillToContactId = null to make it accessible to all customers
    • Add child commodities for each customer's items inside the container
    • Add child commodities for shared materials (packing, dunnage, etc.)
  2. Set Customer Ownership:

    • Container: BillToContactId = null (shared by all customers)
    • Customer-specific children: Set BillToContactId to the owning customer
    • Shared children: Leave BillToContactId = null (e.g., packing materials)
  3. Verify Calculations:

    • All charge types (Pieces, Weight, Volume, Chargeable Weight) correctly drill into containers
    • Container with null is accessible to all customers
    • Each customer is charged only for their items plus proportional share of shared materials
    • Container properties (weight, pieces) are not counted in calculations

Example LCL Setup:

Container Pallet (IsContainer = true, BillToContactId = null)  ← Accessible to all
├── Customer A Items (BillToContactId = Customer A)
│ ├── Box 1: 10kg, 5 pieces
│ └── Box 2: 15kg, 3 pieces
├── Customer B Items (BillToContactId = Customer B)
│ ├── Box 3: 20kg, 7 pieces
│ └── Box 4: 12kg, 4 pieces
└── Shared Packing Materials (BillToContactId = null) ← Shared by all
└── Pallet + Wrap: 5kg, 1 piece

Charge Calculations:
- Customer A: 25kg (A's items) + 5kg (shared) = 30kg, 9 pieces
- Customer B: 32kg (B's items) + 5kg (shared) = 37kg, 12 pieces

For FCL (Full Container Load) Shipments

When entire containers belong to specific customers:

  1. Create Container-Level Ownership:

    • Create container commodity with IsContainer = true
    • Set container's BillToContactId to the owning customer
    • Add child commodities for items inside
  2. Set Child Ownership:

    • Option A - Inherit from container: Leave children as BillToContactId = null (recommended)
    • Option B - Explicit assignment: Set children to same customer as container
    • Both options work the same due to two-level filtering
  3. Verify Calculations:

    • Only the customer assigned to the container can access it
    • Other customers cannot see this container or its contents
    • All child items are automatically included for the container's owner

Example FCL Setup:

Order:
├── Container A (IsContainer = true, BillToContactId = Customer A)
│ ├── Item A1: 100kg, 50 pieces (BillToContactId = null)
│ └── Item A2: 150kg, 30 pieces (BillToContactId = null)
└── Container B (IsContainer = true, BillToContactId = Customer B)
├── Item B1: 200kg, 40 pieces (BillToContactId = null)
└── Item B2: 180kg, 60 pieces (BillToContactId = null)

Charge Calculations:
- Customer A: 250kg, 80 pieces (only Container A's children)
- Customer B: 380kg, 100 pieces (only Container B's children)

For Single Customer Shipments (No Containers)

When using standalone commodities without containers:

  1. Simple Approach:

    • Create commodities directly on the order
    • Optionally set BillToContactId or leave as null
    • All charge types calculate correctly
  2. When to Use:

    • Small shipments (parcels, pallets, crates)
    • Single-customer orders without container structure
    • Simple billing scenarios

For Boxes/Cartons with Only Container Weight

When the container (box, carton) has measurable dimensions and weight, but children are only descriptions:

Scenario: A box weighs 10kg total, contains "5 shirts" and "3 books" as descriptions without individual weights.

Recommended Pattern: Create a "container total" child commodity

Container: Box #123 (IsContainer = true, Weight = 10kg, BillToContactId = Customer A)
├── Child: Box Total (Weight = 10kg, Pieces = 1, BillToContactId = Customer A) ← Represents container weight
├── Child: 5 Shirts (Weight = 0, Pieces = 5, BillToContactId = Customer A) ← Description only
└── Child: 3 Books (Weight = 0, Pieces = 3, BillToContactId = Customer A) ← Description only

Weight Charge for Customer A:
Quantity = 10kg (from "Box Total" child)
Note: Container's own 10kg is not counted, only the child

Pieces Charge for Customer A:
Quantity = 1 + 5 + 3 = 9 pieces (box total + shirts + books)

Why This Works:

  • Weight charges use the "Box Total" child commodity (10kg)
  • Pieces charges count all children including descriptions
  • Child commodities provide inventory detail
  • Maintains consistency with container drilling logic

Alternative Pattern for Mixed Ownership in LCL:

Container: Shared Box (IsContainer = true, Weight = 20kg, BillToContactId = null)
├── Customer A Box Total (Weight = 10kg, Pieces = 1, BillToContactId = Customer A)
│ (Represents Customer A's portion of the box)
├── Customer A: 5 Shirts (Weight = 0, Pieces = 5, BillToContactId = Customer A)
├── Customer B Box Total (Weight = 10kg, Pieces = 1, BillToContactId = Customer B)
└── Customer B: 3 Books (Weight = 0, Pieces = 3, BillToContactId = Customer B)

Weight Charges:
- Customer A: 10kg (from "Customer A Box Total")
- Customer B: 10kg (from "Customer B Box Total")

Pieces Charges:
- Customer A: 1 + 5 = 6 pieces
- Customer B: 1 + 3 = 4 pieces

Future Enhancement: System could automatically use container weight when children have zero/null weight, eliminating the need for "Box Total" child commodities.

For Shared Materials and Costs

When some items should be distributed across all customers:

  1. Set BillToContactId = null for:

    • Packing materials (pallets, wrap, strapping)
    • Shared dunnage or protection materials
    • Common handling supplies
  2. Calculation Behavior:

    • Items with BillToContactId = null are included in every customer's charges
    • Use this for costs that should be distributed proportionally
    • Ensure this is intentional to avoid unexpected billing

Charge Verification

Before posting charges to invoices:

  1. Review Quantities:

    • Verify that calculated quantities match expected values
    • Check that container children are being counted correctly
    • Ensure shared materials are distributed appropriately
    • Confirm container properties (weight, pieces) are not being counted
  2. Check Container Assignments:

    • For LCL: Verify containers have BillToContactId = null to allow multi-customer access
    • For FCL: Verify each container has BillToContactId set to correct customer
    • Confirm that customers can only access their assigned containers
  3. Check Child Commodity Assignments:

    • Within accessible containers, verify children have correct BillToContactId
    • Confirm shared items (packing materials) have BillToContactId = null
    • Ensure customer-specific items are assigned to correct customers
  4. Verify Two-Level Filtering:

    • Container level: Charge customer matches container's BillToContactId (or container is null)
    • Child level: Charge customer matches child's BillToContactId (or child is null)
    • Both filters must pass for items to be included
  5. Test Different Apply By Types:

    • Pieces, Weight, Volume, and Chargeable Weight should all respect the same filtering
    • All types drill into containers consistently
    • All types apply two-level filtering (container + child)
    • Verify calculations align with business expectations