Skip to content

💵 Helper class for calculating money amounts without rounding errors.

License

Notifications You must be signed in to change notification settings

chariz/money-calc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


money-calc

Helper class for calculating money amounts without rounding errors.

Installation

npm install --save money-calc
# or
yarn install money-calc
# or
pnpm install money-calc

Example

import Money, { MoneyAmount } from "money-calc";

interface Product {
	name: string;
	manufacturer: string;
	weightKg: MoneyAmount;
	unitsPerPack: MoneyAmount;
	price: MoneyAmount;
}

interface Discount {
	percent: MoneyAmount;
}

let product: Product = {
	name: "Mr Sparkle Dishwashing Detergent Powder, 2kg, 66 Washes",
	manufacturer: "Matsumura Fishworks & Tamaribuchi Heavy Manufacturing Concern",
	weightKg: "2.00",
	unitsPerPack: "66.00",
	price: {
		amount: "19.99",
		currency: "AUD"
	},
	shippingFee: {
		amount: "4.18",
		currency: "AUD"
	}
};

let discount: Discount = {
	percent: "20.00"
};

let discountRemainder = new Money("100.00")
	.subtract(discount.percent);

let originalPrice = new Money(product.price);

let finalPrice = new Money(originalPrice)
	.percent(discountRemainder)
	.add(product.shippingFee);

let pricePerKg = new Money(finalPrice)
	.div(product.weightKg);

let pricePerUnit = new Money(finalPrice)
	.div(product.unitsPerPack);

console.log(`Was ${originalPrice}, now ${finalPrice} w/shipping!`);
console.log(`Price per kilogram: ${pricePerKg}`);
console.log(`Price per unit: ${pricePerUnit}`);

let response = {
	product,
	discount,
	originalPrice,
	finalPrice,
	pricePerKg,
	pricePerUnit
};

// Stringify and then parse the object back in, to simulate sending the data
// over the wire to an API client.
let jsonified = JSON.parse(JSON.stringify(response));

console.log("JSON response:");
console.log(jsonified);

Running this script produces the following output:

Was A$19.99, now A$20.17!
Price per kilogram: A$10.09
Price per unit: A$0.31
JSON response:
{
  product: {
    name: 'Mr Sparkle Dishwashing Detergent Powder, 2kg, 66 Washes',
    manufacturer: 'Matsumura Fishworks & Tamaribuchi Heavy Manufacturing Concern',
    weightKg: '2.00',
    unitsPerPack: '66.00',
    price: { amount: '19.99', currency: 'AUD' },
    shippingFee: { amount: '4.18', currency: 'AUD' }
  },
  discount: { percent: '20.00' },
  originalPrice: { amount: '19.99', currency: 'AUD' },
  finalPrice: { amount: '20.17', currency: 'AUD' },
  pricePerKg: { amount: '10.09', currency: 'AUD' },
  pricePerUnit: { amount: '0.31', currency: 'AUD' }
}

Usage

MoneyAmount

A string of the format 1234.56. Two decimal places must be provided. For example: 12.00 or 12.30, rather than 12, 12.0, or 12.3. Numbers only - no digit separators or currency symbols! The TypeScript type definition will help ensure your inputs are of the right format.

MoneyInput

A money input can be represented as:

  • A string matching the format of MoneyAmount.
  • The exact string 0, as a shortcut for 0.00.
  • An instance of js-big-decimal.
  • An instance of the Decimal128 type, as exported by bson (the same type is also re-exported by the MongoDB driver). In MongoDB, this is called NumberDecimal, meaning you can store a NumberDecimal in a MongoDB document, then retrieve and pass it directly to Money.
  • You can also pass another instance of Money, or the simplified object returned by Money.toJSON().

Currency

Enumeration of all three-letter currency symbols that account for at least 1% of world trade, according to xe.com. Any other currency supported by ICU (the source of truth used by the Intl classes) will work, however.

ComparisonResult

An enumeration of possible outcomes from a numeric comparison:

  • ComparisonResult.Ascending = -1: The first number is less than the second number.
  • ComparisonResult.Same = 0: The two numbers are equal.
  • ComparisonResult.Descending = 1: The first number is greater than the second number.

Money

Money objects are immutable, and are designed to be used in a chainable sequence of operations.

new Money(amount: MoneyInput, currency?: Currency)

Initialise a new Money instance with the supplied MoneyInput.

If a currency is not specified, and the amount isn’t an instance of Money, it will fall back to the default of USD.

Throws TypeError if the input amount is invalid.

add(...amounts: MoneyInput[]): Money

Returns an instance of Money with the result of adding the supplied amounts in sequence.

subtract(...amounts: MoneyInput[]): Money

Returns an instance of Money with the result of subtracting the supplied amounts in sequence.

mul(...amounts: MoneyInput[]): Money

Returns an instance of Money with the result of multiplying by the supplied amounts in sequence.

div(...amounts: MoneyInput[]): Money

Returns an instance of Money with the result of dividing by the supplied amounts in sequence.

mod(...amounts: MoneyInput[]): Money

Returns an instance of Money with the result of the modulus (division remainder) of the supplied amounts in sequence.

percent(amount: MoneyInput): Money

Returns an instance of Money with the result of multiplying by a percentage between 0.00 and 100.00. This is effectively a shortcut for dividing the input amount by 100.00 before multiplying by that amount. If the percentage is between 0.00 and 1.00, use mul() instead.

negate(): Money

Returns an instance of Money with the amount negated - that is, a positive number becomes negative, and a negative number becomes positive.

cmp(amount: MoneyInput): ComparisonResult

Compares the amount with another amount, and returns the result of the comparison.

toString(): string

Formats the money amount for display in a user interface. Includes the appropriate currency sign for the supplied currency, in addition to thousand separators. For a machine-readable string, use amount.

toJSON(): { amount: MoneyAmount; currency: Currency }

Returns an object that can be used to represent the Money in a JSON object. You can then initialise an instance of Money on the client side by using this as a MoneyInput, or directly pass these parameters to the number formatting mechanism for the platform.

toJSON() is called by JSON.stringify() to retrieve a variant of the object that can be represented in JSON. As such, you likely won’t need to call this directly.

amount: string

The string value that can be stored in a database or serialized into a payload such as JSON. This amount is rounded to 2 decimal places, and is intended to be machine-readable. For a human-readable string, use toString().

Credits

Developed by Chariz:

License

Licensed under the Apache License, version 2.0. Refer to LICENSE.md.