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:
-
Creation:
- A charge is initially appended to an order
-
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.
-
Invoicing: Posted
- Post-reconciliation, charges are transferred to the invoice.
- At this stage, the charge's status transitions to "Posted".
-
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)
- For containers: Sum of child commodity pieces (filtered by container and child
- 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
- For containers: Sum of child commodity weight (filtered by container and child
- 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)
- For containers: Sum of child commodity volume weight (filtered by container and child
- 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
- For each commodity:
- 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:
-
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
BillToContactIdset, only charges matching that customer include this container's children - Within that container, children are further filtered by their own
BillToContactId
- If container has
- Container's own properties (weight, pieces) are not counted - only children
-
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
BillToContactIdcontrols which charges can access the container - ✅ Child
BillToContactIdprovides additional filtering within accessible containers - ✅ Container's pieces/weight are not counted - only child items are summed
- ✅
BillToContactId = nullmeans "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:
- Container level: Charge's
ApplyToContactIdmust match container'sBillToContactId(or container isnull) - Child level: Charge's
ApplyToContactIdmust match child'sBillToContactId(or child isnull)
Validation Rules
The system enforces the following rules:
- ✅ Container commodities can have
BillToContactIdset to assign entire container to a customer - ✅ Container commodities can have
BillToContactId = nullfor shared containers (LCL) - ✅ Child commodities can have
BillToContactIdset for item-level customer assignment - ✅ Child commodities can have
BillToContactId = nullfor shared items within a container - ⚠️ Charges with no matching commodities will have
Quantity = 0
Troubleshooting
Problem: Charge quantity is 0
- Cause 1: Container has
BillToContactIdset to a different customer- Solution: Check container's
BillToContactId- if set to Customer A, only Customer A can access it
- Solution: Check container's
- Cause 2: No child commodities match the charge's
ApplyToContactId- Solution: Check that child
BillToContactIdvalues match the charge customer
- Solution: Check that child
- 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
BillToContactIdis 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 haveBillToContactId = Customer B
Problem: Charge quantity includes unexpected items
- Cause: Container or children have
BillToContactId = null(universal) - Solution:
- For FCL: Set container's
BillToContactIdto specific customer - For LCL: Set child
BillToContactIdto specific customers for item-level filtering - Only use
nullfor intentionally shared items
- For FCL: Set container's
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:
-
Create Container Structure:
- Create a container commodity with Package Category where
IsContainer = true - Set container's
BillToContactId = nullto 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.)
- Create a container commodity with Package Category where
-
Set Customer Ownership:
- Container:
BillToContactId = null(shared by all customers) - Customer-specific children: Set
BillToContactIdto the owning customer - Shared children: Leave
BillToContactId = null(e.g., packing materials)
- Container:
-
Verify Calculations:
- All charge types (Pieces, Weight, Volume, Chargeable Weight) correctly drill into containers
- Container with
nullis 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:
-
Create Container-Level Ownership:
- Create container commodity with
IsContainer = true - Set container's
BillToContactIdto the owning customer - Add child commodities for items inside
- Create container commodity with
-
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
- Option A - Inherit from container: Leave children as
-
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:
-
Simple Approach:
- Create commodities directly on the order
- Optionally set
BillToContactIdor leave asnull - All charge types calculate correctly
-
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:
-
Set BillToContactId = null for:
- Packing materials (pallets, wrap, strapping)
- Shared dunnage or protection materials
- Common handling supplies
-
Calculation Behavior:
- Items with
BillToContactId = nullare included in every customer's charges - Use this for costs that should be distributed proportionally
- Ensure this is intentional to avoid unexpected billing
- Items with
Charge Verification
Before posting charges to invoices:
-
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
-
Check Container Assignments:
- For LCL: Verify containers have
BillToContactId = nullto allow multi-customer access - For FCL: Verify each container has
BillToContactIdset to correct customer - Confirm that customers can only access their assigned containers
- For LCL: Verify containers have
-
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
- Within accessible containers, verify children have correct
-
Verify Two-Level Filtering:
- Container level: Charge customer matches container's
BillToContactId(or container isnull) - Child level: Charge customer matches child's
BillToContactId(or child isnull) - Both filters must pass for items to be included
- Container level: Charge customer matches container's
-
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