Building a Rate Library

August 25, 2026

In the previous post, I talked about how quoting a job in manufacturing is often still done by hand, following a pattern of using old spreadsheets, looking up prices from vendors each time a quote request comes in, and applying slightly different ideas on profit margins depending on who does the quoting.

It's a system that mostly works, but is prone to errors, and leaves a lot of space for improvement.

I addressed how to improve the process by creating a quoting app with the goal of making quoting easier, more efficient, faster, and more consistent for a manufacturing team.

A rate library

Creating a quoting app starts with building a central Rate Library. A rate library is simply a list of everything that goes into a quote: raw materials, labor rates, overhead, etc.

This actual items in the list change from business to business, but each one of them has a consistent shape:

  • a name
  • a unit
  • a current cost
  • a category

Examples of items in the rate library are:

  name: Steel plate 1/4 in.
  unit: sq. ft.
  cost: $12.50
  category: material

  name: CNC Machining
  unit: hour
  cost: $85
  category: labot

  name: Shop overhead
  unit: hour
  cost: $20
  category: overhead

Once a Rate Library is set up, it's quick and easy for the team member that actually prepares the quote to pick the appropriate item for the list, without worrying about current costs and units.

One simple choice, and the right item, with the right cost, is added to the quote.

If the current price for a material changes, it gets updated only in the Rate Library, and from that moment on every quote will automatically inherit the new price. Nothing to remember or look up for a particular quote, nothing to keep in sync by hand, few possibilites for errors.

It's a simple idea, but it's the difference between: "we sort of know what we charge for this" and "the whole team knows exactly what we charge for this".

Under the hood

(Feel free to skip this section if you are not interested in technical details)

The Rate Library, in this sample app, is a single database table named `materials`. Each entry has a category field (material, labor, and overhead). In a real app, Category entries would be held in a separate database table named `categories`, with a relationship to the `materials` table. Keeping the tables separate makes it easy to update or create new Categories separately from the Materials.

Here's what the underlying application model looks like:


class Material < ApplicationRecord
  CATEGORIES = %w[material labor overhead].freeze

  validates :name, presence: true
  validates :unit_cost_cents, numericality: { greater_than_or_equal_to: 0 }
  validates :category, inclusion: { in: CATEGORIES }

  scope :active, -> { where(active: true) }
end

Active/inactive materials

In our Materials, we have an `active` flag. The reason for this is that if we don't need a material anymore going forward, and remove it, old quotes based on this same material would be broken. Making a material `inactive` instead still leaves it as a reference for old quotes, while making it unavailable for new quotes moving forward.

Costs stored as 'cents' instead of dollars

When using currencies, best practice is to store the values in cents, instead of dollars. For example, $12.50 is stored in the database as `1250`, not `12.50`. Decimal numbers like `12.50` are stored by computers not as exact numbers, but as approximations, based on the available memory of each machine. When a decimal number is simply stored and retrieved, usually there is no problem, but when decimal numbers are added and multiplied together multiple times, as is the case in a quoting app, these approximations may cause unexpected incorrect results.

Storing the cost numbers as integers (that is, without a decimal point) prevents these kind of bugs, and always returns the correct numbers in calculations.

Under the hood

(Feel free to skip this section if you are not interested in technical details)

This idea of storing cents amounts instead of decimal numbers, creates a dilemma, though. We don't want users of our app to be forced to enter costs in cents, because we are used to enter decimal numbers for dollar amounts. So the various forms that handle currencies should still accept decimals.

But, if decimals are entered, we need a way to convert them to cents before storing into the database.

We also need to show dollar amounts in various places in our app, and we need to show the expected dollar format ($12.50 for example).

So, we also need logic that handles converting numbers from cents to decimal so they can be displayed correctly in the app views.

To solve this problem we add a couple of utility methods to our Material model that do this conversion both ways:


  # converting from cents to dollars
  def unit_cost_dollars
    format("%.2f", unit_cost_cents.to_i / 100.0)
  end

  # converting from dollars to cents
  def unit_cost_dollars=(value)
    self.unit_cost_cents = (value.to_s.gsub(/[^\d.]/, "").to_f * 100).round
  end

The Rate Library as a foundation

The Rate library may seem like a small detail at first, but it's really the underlying foundation of the quoting app. Get it right and consistent and the rest of the functionalities that build upon it will inherit that consistency for free.

In practice, everything that's built downstream from here is actually trustworthy because the actual quotes are built and calculated from a consistent, central location that keeps all underlying figures correct and updated.

What's next

The next post covers the actual quote building functionality that takes line items and makes sure the math behind it is correct and consistent every single time.