<h1 align="center"> Dinero: Make exact monetary calculations</h1>
<p align="center">
<a href="https://pypi.org/project/dinero/">
<img alt="PyPI" src="https://img.shields.io/pypi/v/dinero">
</a>
<a href="https://pypi.org/project/dinero/">
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dinero">
</a>
<a href="https://github.com/wilfredinni/dinero/actions">
<img alt="Build status" src="https://github.com/wilfredinni/dinero/actions/workflows/test.yml/badge.svg" data-canonical-src="https://img.shields.io/github/workflow/status/Delgan/loguru/Tests/master" style="max-width: 100%;">
</a>
<a href="https://codecov.io/github/wilfredinni/dinero" >
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/wilfredinni/dinero">
</a>
<a href="https://www.codacy.com/gh/wilfredinni/dinero/dashboard?utm_source=github.com&utm_medium=referral&utm_content=wilfredinni/dinero&utm_campaign=Badge_Grade">
<img alt="Codacy grade" src="https://img.shields.io/codacy/grade/d6b13235aec14905968fb4b0e9a5e8fd">
</a>
<a href="https://github.com/wilfredinni/dinero/blob/master/LICENSE">
<img alt="PyPI - License" src="https://img.shields.io/pypi/l/dinero">
</a>
</p>
<p align="center">
<img width="300" height="200" src="https://media.tenor.com/EWRvErYGzPUAAAAC/bugs-bunny-looney-tunes.gif">
</p>
This project is inspired by the excellent [dinero.js](https://github.com/dinerojs/dinero.js) library.
Python Decimal instances are enough for basic monetary calculations, but when you face more complex use-cases they often show limitations and are not so intuitive to work with. Dinero provides a cleaner and easier to use API while still relying on the standard library. So it's still Decimal, but easier.
[Read the Documentation](https://wilfredinni.github.io/dinero/)
## Why Dinero?
A `Dinero` object is an immutable data structure representing a specific monetary value. It comes with methods for creating, parsing, manipulating, testing and formatting.
## Install
```bash
pip install dinero
```
## Initialization
To create a `Dinero` object, you need an `amount` that can be an `int`, `float`, `str` or `Decimal`, and a `currency`:
```python
from dinero import Dinero
from dinero.currencies import USD
amount = Dinero(100.4, USD)
```
## Properties
Every `Dinero` object has the following properties:
```python
>>> amount.raw_amount
Decimal('100.40')
```
```python
>>> amount.symbol
'$'
```
```python
>>> amount.code
'USD'
```
```python
>>> amount.exponent
2
```
```python
>>> amount.precision
10
```
## Formatting
### String
You can return a formatted string representation of `Dinero` with the `format` method:
```python
>>> Dinero(2.32, EUR).format()
'2.32'
```
```python
>>> Dinero(2.32, EUR).format(symbol=True)
'€2.32'
```
```python
>>> Dinero(2.32, EUR).format(currency=True)
'2.32 EUR'
```
```python
>>> Dinero(2.32, EUR).format(symbol=True, currency=True)
'€2.32 EUR'
```
### Dictionary
Return a `Dinero` instance as a Python Dictionary:
```python
>>> Dinero("3333.259", USD).to_dict()
{
'amount': '3333.26',
'currency':
{
'code': 'USD',
'base': 10,
'exponent': 2,
'symbol': '$'
}
}
```
```python
>>> Dinero('3333.26', USD).to_dict(amount_with_format=True)
{
'amount': '3,333.26',
'currency':
{
'code': 'USD',
'base': 10,
'exponent': 2,
'symbol': '$'
}
}
```
### Json
Return a `Dinero` instance as a `JSON` string:
```python
>>> Dinero('2,00', USD).to_json()
'{"amount": "3333.20", "currency": {"code": "USD", "base": 10...'
```
```python
>>> Dinero('2,00', USD).to_json(amount_with_format=True)
'{"amount": "3,333.26", "currency": {"code": "USD", "base": 10...'
```
## Operations
If the addend or subtrahend is an `str`, `int`, `float` or `Decimal`, it will be transformed, under the hood, to a Dinero instance using the same currency:
```python
# those operations
Dinero(1000, USD).add(Dinero(1000, USD))
Dinero(1000, USD) + Dinero(1000, USD)
# are equivalent to
Dinero(1000, USD).add(1000)
Dinero(1000, USD) + 1000
```
```python
# those operations
Dinero(1000, USD).subtract(Dinero(100, USD))
Dinero(1000, USD) - Dinero(100, USD)
# are equivalent to
Dinero(1000, USD).subtract(1000)
Dinero(1000, USD) - 100
```
Additions and subtractions must be between instances with the same `currency`:
```python
>>> total = Dinero(100, USD) + Dinero(100, EUR)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/.../dinero/_dinero.py", line 120, in __add__
addend_obj = self._get_instance(addend)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/.../dinero/_dinero.py", line 74, in _get_instance
raise DifferentCurrencyError("Currencies can not be different")
dinero.exceptions.DifferentCurrencyError: Currencies can not be different
```
The multiplicand and divisor can be `int`, `float` or of `Decimal` type:
```python
Dinero(1000, USD).multiply(2)
Dinero(1000, USD) * 2
```
```python
Dinero(1000, USD).divide(2)
Dinero(1000, USD) / 2
```
## Comparisons
```python
Dinero(1000, USD).equals_to(Dinero(1000, USD))
Dinero(1000, USD) == Dinero(1000, USD)
```
```python
Dinero(1000, USD).less_than(Dinero(1000, USD))
Dinero(1000, USD) < Dinero(1000, USD)
```
```python
Dinero(1000, USD).less_than_or_equal(Dinero(1000, USD))
Dinero(1000, USD) <= Dinero(1000, USD)
```
```python
Dinero(1000, USD).greater_than(Dinero(1000, USD))
Dinero(1000, USD) > Dinero(1000, USD)
```
```python
Dinero(1000, USD).greater_than_or_equal(Dinero(1000, USD))
Dinero(1000, USD) >= Dinero(1000, USD)
```
You can only compare to other `Dinero` objects:
```python
>>> Dinero(100, USD) == 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/.../dinero/_dinero.py", line 146, in __eq__
self._comparison_amount(amount)
File "/home/.../dinero/_dinero.py", line 103, in _comparison_amount
raise InvalidOperationError(InvalidOperationError.comparison_msg)
dinero.exceptions.InvalidOperationError: You can only compare against other Dinero instances.
```
## Currencies
The currency is one of the two pieces necessary to create a Dinero object.
A Dinero currency is composed of:
- A unique code.
- A base, or radix.
- An exponent.
- A symbol (optional)
```python
EUR: Currency = {
"code": "EUR",
"base": 10,
"exponent": 2,
"symbol": "€",
}
```
Dinero give you access to [more than 100 different currencies](/dinero/currencies/):
```python
>>> from dinero.currencies import USD, EUR, GBP, INR, CLP
```
```python
>>> Dinero(2.32, EUR)
Dinero(amount=2.32, currency={'code': 'EUR', 'base': 10, 'exponent': 2, 'symbol': '€'})
```
```python
>>> Dinero(2.32, EUR).format(symbol=True, currency=True)
'€2.32 EUR'
```
```python
>>> Dinero(2.32, EUR).raw_amount
Decimal('2.32')
```
### Custom Currencies
You can easily create custom currencies:
```python
from dinero import Dinero
BTC = {
"code": "BTC",
"base": 10,
"exponent": 2,
"symbol": "₿",
}
Dinero(1000.5, BTC)
```
### Type hints
If you are using `type hints` in your project you would want to import `dinero.types.Currency` to prevent warnings:
```python
class Currency(TypedDict):
code: str
base: int
exponent: int
symbol: NotRequired[str]
```
```python
from dinero.types import Currency
BTC: Currency = {
"code": "BTC",
"base": 10,
"exponent": 2,
"symbol": "₿",
}
Dinero(1000.5, BTC)
```