إنتقل إلى المحتوى الرئيسي

cost-rates


Cost Rates

The Cost Rates module provides the pricing foundation for all labor cost calculations in ProBeya. It stores per-user, per-role, and organization-default rates with date ranges, supports multiple currencies with exchange rate management, and implements a cascading lookup that always resolves the most specific applicable rate for any given resource and date.

Overview​

Accurate resource costing requires rates that reflect organizational reality: contractors may have different rates from full-time staff, overtime differs from standard time, and rates change over time with pay reviews and contract renewals. ProBeya models this through four rate types — internal standard, internal overtime, external standard, and external overtime — combined with a cascading lookup that searches from the most specific match (user-specific rate) to the most general (organization default).

The Cost Rates page is accessible at /{workspaceSlug}/portfolio/settings/cost-rates. It presents two management panels: one for labor cost rates and one for currency exchange rates.

Getting Started​

  1. Navigate to Portfolio > Settings > Cost Rates.
  2. Click Add Rate in the Labor Rates panel.
  3. Select the rate type: internal_standard, internal_overtime, external_standard, or external_overtime.
  4. Optionally assign the rate to a specific user or role. Leave both blank to create an organization default.
  5. Set the amount, currency, and effective date range (effectiveFrom and optional effectiveTo).
  6. Save.
  7. To add exchange rates, click Add Exchange Rate in the Exchange Rates panel and provide the currency pair, rate, and effective date.

Key Concepts​

Rate Types​

Rate TypeDescription
internal_standardStandard rate for internal (employee) resources during regular hours
internal_overtimeOvertime rate for internal resources
external_standardStandard rate for external (contractor or vendor) resources
external_overtimeOvertime rate for external resources

Cascading Rate Lookup​

When a cost calculation needs the rate for a specific resource, the resolve procedure applies a three-level cascade:

  1. User-specific rate: a rate row where userId matches the resource and the rate type matches. If the user has a rate effective on the target date, use it.
  2. Role-level rate: a rate row where roleId matches the resource's assigned role. Used when no user-specific rate exists.
  3. Organization default: a rate row with neither userId nor roleId set. Used as the fallback when neither user-specific nor role-level rates exist.

If no applicable rate is found after all three levels, the resolve procedure returns a NOT_FOUND error. This prevents silent zero-costing of timesheets and cost entries.

Date-Range Validity​

Each rate has an effectiveFrom date and an optional effectiveTo date. When resolving a rate for a specific date, only rates whose range includes that date are considered. This allows organizations to pre-configure future rate changes (e.g., annual pay reviews) without affecting current cost calculations.

Exchange Rates​

Exchange rates are stored as currency pair records with an effective date. They allow cost entries recorded in one currency to be converted to the portfolio's reporting currency for consolidated financial reports. The listExchangeRates procedure supports filtering by fromCurrency and toCurrency to retrieve applicable rates.

Reference Protection​

A cost rate cannot be deleted if it is referenced by one or more cost entries. The delete procedure checks the ppmCostEntries table before removing a rate and returns a PRECONDITION_FAILED error if references exist. This protects historical cost accuracy. To retire a rate, set its effectiveTo date rather than deleting it.

How It Works​

Creating a Rate​

ppmCostRates.create validates the input against createCostRateSchema from @probeya/shared. The amount field is stored as a string in the database to avoid floating-point precision loss on monetary values. The organizationId comes from the authenticated server context.

Resolving a Rate​

ppmCostRates.resolve accepts { rateType, userId?, roleId?, date } and runs the cascading lookup:

  1. Fetch all active rates of the requested type for the organization.
  2. Pass them to resolveCostRate from @probeya/shared/ppm/cost-rate-lookup.
  3. The lookup function applies the cascade logic and returns the most specific matching rate.

Updating a Rate​

Partial updates are supported — only fields provided in the input are changed. The amount field, if updated, is automatically cast to string before storage to maintain precision consistency.

Exchange Rate Management​

Exchange rates support the same create, list, and delete operations as cost rates but without the cascading lookup — the most recent rate for a given currency pair on or before the target date is used. Unlike cost rates, exchange rates have no referential protection: they can be deleted at any time.

Configuration​

SettingLocationDescription
Rate typeCost Rate formCategory of labor (internal/external, standard/overtime)
User assignmentCost Rate formOptional: pins the rate to a specific user
Role assignmentCost Rate formOptional: applies the rate to all users with this role
AmountCost Rate formNumeric rate value (stored as string for precision)
CurrencyCost Rate formISO 4217 currency code (e.g., EUR, USD)
Effective fromCost Rate formStart date of the rate's validity window
Effective toCost Rate formOptional end date. Leave blank for open-ended rates
Exchange rate pairExchange Rate formSource and target currency codes
Exchange rate valueExchange Rate formConversion multiplier (1 source unit = N target units)
Exchange rate dateExchange Rate formDate from which this exchange rate is effective

Permissions​

  • View cost rates: PPM admin or finance manager role.
  • Create cost rates: PPM admin or finance manager role.
  • Update cost rates: PPM admin or finance manager role.
  • Delete cost rates: PPM admin role; deletion blocked if referenced by cost entries.
  • Resolve rates: any authenticated user; used server-side during cost entry creation.
  • Manage exchange rates: PPM admin or finance manager role.

Tips & Best Practices​

  • Create an organization-default rate for each type: even if most resources have role or user-specific rates, having an organization default prevents the resolver from failing on resources whose rates have not been explicitly configured.
  • Use effectiveTo to expire old rates: rather than deleting rates that have expired, set an effectiveTo date. This preserves the rate history and ensures that historical cost recalculations remain accurate.
  • Separate internal and external rates: even if your current rates are the same for internal and external resources, defining them separately makes it easy to adjust one without affecting the other.
  • Pre-configure future rates: you can create a rate with a future effectiveFrom date. The resolver will ignore it until the date arrives, enabling smooth transitions for annual pay reviews.
  • Maintain exchange rates monthly: for multi-currency portfolios, update exchange rates at least monthly to keep financial reports representative of actual currency exposure.
Rate resolution transparency

When a timesheet entry or cost entry is created, the resolved rate is stored on the entry. If you later change a rate, historical entries are not retroactively recosted — they retain the rate that was in effect when they were created.

Cannot delete referenced rates

If a rate has been applied to any cost entry, it cannot be deleted. Set an effectiveTo date instead to prevent it from being selected for new entries going forward.