The no nosense sales subtotals calculator.
I'm still negative! I want to get up to zero
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠉⢦⠤⡄⠀⠀⠀⠀⠀⠀⠀⢻⠺⣆⠈⢆⠘⣷⡀⠘⣆⠀⠀⠀⠀⠈⠑⠰⠀⣿⠀⠿⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⢧⠀⠀⢿⠀⠀⠀⠀⠀⠀⠀⠀⠈⢧⣸⢦⠈⢦⠈⠿⠀⠻⢦⡀⠀⠀⠀⠀⠀⣦⣿⠀⣶⢰⠀⠀
⠀⠀⠰⡂⠀⠀⠀⠀⠀⠀⠀⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣠⡞⠲⣧⠀⠀⠀⠀⠀⠀⠀⠠⠼⢿⠙⣧⡈⢳⡀⠀⢶⣄⠈⠓⠦⠤⠤⠼⠟⠁⠸⠇⣼⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣰⠥⠳⢶⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠁⠀⠀⠀⠀⠂⢀⣀⣀⣤⠤⠤⠤⠘⠃⢸⣿⢦⡈⠲⣄⠙⠻⠶⠀⠀⢠⣤⣶⠟⢀⡴⠁⠀⠀
⠂⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⢀⠻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⣀⡤⠖⢚⣏⣩⣥⣴⣶⣶⣿⣷⣾⣿⣿⣿⣿⣿⣦⣄⣙⠲⢤⣄⣀⣀⣀⣠⡴⣯⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣤⠏⠙⠛⠀⠀⠀⢀⣠⠖⢋⣁⡤⠞⠋⢀⣠⠴⢚⣩⣤⣴⣿⣿⡿⢿⣭⣥⠤⠒⠒⠂⠀⠂⠀⠀⠈⠛⢿⣿⡏⠙⢛⢿⣿⣿⣯⡈⢳⡉⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⢀⣀⠴⠿⠒⠊⠉⣀⡤⢴⣺⢿⣷⣿⣛⣯⡭⠭⠴⠚⠛⠻⢶⣾⣿⣗⠦⣄⡀⠀⠀⠀⠀⠀⠀⢻⣧⠏⠉⢻⠷⣌⡙⢿⣆⠹⡀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠴⠚⠉⠀⢀⣀⣤⣖⣫⣵⠞⠋⠀⣼⣿⣿⣿⣅⠀⠀⠀⠀⠀⠀⠀⠙⣟⠻⢿⣶⠭⣷⣦⣄⣀⠀⠀⢨⡟⠀⠀⢸⠀⠀⠙⢮⣿⣧⣧⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢀⣠⠴⠚⠉⠀⢀⣤⠶⣏⡭⠗⠛⣧⠟⠁⠀⠀⣼⣻⠏⠻⣿⣿⣷⣾⣿⣿⣿⣿⣶⡶⢾⣧⣀⣩⣷⣾⣿⣷⢾⣿⣷⡿⠃⠀⠀⣸⣆⣀⣀⣈⣿⡟⣿⠀⢰⠀
⠀⠀⠀⠀⠀⠀⠀⠉⠀⢀⣠⠴⢚⣩⠶⡻⠋⠀⠀⢠⠏⠀⠀⢀⣾⣿⠃⠀⠀⠀⢉⣽⠍⣽⣿⡏⢛⣿⣿⠛⠿⣿⣟⠹⣿⡯⠴⠚⠉⠉⠀⠀⢀⣴⣿⣿⡿⣿⣿⣿⡇⠿⡀⡸⠀
⠀⠀⠀⠀⠀⠀⣠⣴⣞⣉⡴⠚⡽⢡⠞⠁⠀⠀⡰⠃⠀⠀⣠⣾⠟⠁⠀⢦⡀⠀⠀⠸⣿⣿⡀⠻⠛⢰⣾⡀⠀⡈⢿⢄⣻⣽⡄⠀⠀⠀⢠⣴⣿⣿⣿⠿⣿⣿⣿⣿⣿⠀⣿⠃⠀
⠀⠀⣀⡤⣶⡯⢻⢟⡿⠃⣰⣾⣷⠋⠀⠀⢀⠞⠁⠀⣠⣾⠟⠁⠀⠀⢰⡿⢿⡄⠀⠀⠀⠙⠹⡟⠛⠉⣍⣭⣴⣛⣴⣾⠟⠋⠁⠀⠀⠀⢸⡟⢿⢋⡿⢀⣼⣿⣿⣿⠇⠀⡇⠀⠀
⠀⠈⢰⠞⠁⣴⠿⠋⢀⣾⠟⡱⠃⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠀⠀⠀⡿⠀⠀⠀⠀⠀⠀⠀⢻⠇⠀⠀⠀⠈⠙⣿⣿⠅⠀⠀⠀⠀⠀⣀⣴⣿⣿⣿⣷⣾⠟⢋⡽⠋⠀⡴⠃⠀⠀
⠀⡰⢋⡠⠞⠁⠀⡴⡻⢋⡜⠁⠀⢀⠀⢀⡴⣿⢏⡇⠀⠀⠀⠀⠀⠰⠃⠀⠀⠀⠀⠀⠀⠀⠏⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⢠⣘⣿⣿⣿⣿⡿⠟⠉⣿⠖⠋⢀⡤⠚⠀⠀⠀⠀
⡾⠗⠉⠀⠀⠀⠀⡼⢁⠎⠀⠀⢠⠞⣠⢻⡟⠁⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡇⣿⣿⣿⣿⡐⠀⢸⣃⠤⠖⠋⠀⠀⠀⠀⠀⠀
⠀⠀⠀⢀⣤⢃⣼⠁⡼⠀⠀⢀⡟⣼⠁⢸⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⠟⠃⠃⠀⠀⠹⡇⠀⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣀⠠⠶⢛⣷⣿⠃⢰⡇⠀⠀⣸⢠⣿⡀⢸⡀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⡼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⣀⣴⠟⢩⡟⠀⡿⡇⠀⠀⡇⣾⣿⣷⣀⣧⠀⠘⡆⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⣀⣀⣹⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠟⠁⢠⣿⠇⡼⠁⣷⠀⢰⣷⣿⣿⣿⣿⠻⢧⡀⠹⡄⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣅⢀⣠⠤⠖⡿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢀⣿⣿⣰⠃⠀⣿⠀⢸⣿⣿⣿⣿⣿⡇⠀⣽⣦⠘⠂⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⠏⠀⢀⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⡞⡿⣿⠇⠀⠀⣿⠀⢸⣿⣿⣿⣿⣿⣿⡾⠛⠁⠀⣠⠜⠂⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣴⡖⠒⠛⠛⠲⢬⣥⣴⢠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⢸⢉⠁⡇⠀⣠⣼⣿⠀⢸⡿⠿⠛⢻⣿⣿⣿⣤⢖⢿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⠛⠻⠿⣿⣿⣯⣿⣿⣶⣾⠟⡱⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣄⡏⢸⢸⠁⢀⣾⠉⣿⡀⠀⡇⠀⠀⠸⣿⣿⣿⣿⣿⣿⣿⣌⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣶⠀⠀⠀⠈⠉⠉⠉⠉⢱⡾⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠙⢾⣸⢸⡞⠀⢰⣿⣧⡀⢻⡀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣶⣤⣀⣀⣀⣀⣤⡾⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠙⠿⣦⡀⢰⡟⠉⠙⢮⣿⣀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡀⠀⠐⠀⠀⠀⠀⠀⠀⠀⣹⣿⡿⠿⣏⢩⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠿⣿⡥⣤⣴⠞⠉⠉⠁⣠⢿⣿⣿⣿⣿⣿⣿⣛⣿⡿⠻⡿⣿⣶⣄⣧⠀⠀⠀⠀⠀⠚⠋⠁⠀⠀⢹⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠈⠛⢿⣇⠀⣠⡶⠛⠁⠀⣻⡿⠟⠋⠀⠀⣿⡛⠀⣠⢧⠸⡿⣏⡟⠷⣄⡀⠀⠀⠀⠀⠀⠀⣠⡾⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣀⢠⡀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣵⣶⣤⣶⣿⣯⠀⠰⠃⠀⠀⠈⢧⢠⠏⠨⢿⡱⠘⣿⡀⠈⠙⠓⠶⠶⠒⠚⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣛⣻⣿⣷⣄⡀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣥⣿⣷⣦⡴⠒⠛⢏⠀⠀⠀⠙⠲⢬⣹⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠉⠉⠛⢿⣿⣖⠦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿⣿⢁⣿⣀⣠⠀⣾⡧⠀⠀⠀⠀⠀⠈⠉⠙⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⠃⢀⣠⡤⠤⢤⣄⡀⠀⠀⠀⢠⣦⠄
⠀⠀⠀⠀⠀⠀⠙⢮⡳⣄⠙⠢⣀⠀⠀⠙⠿⣿⣿⣿⢹⡟⠃⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⡀⠀⠀⠀⠀⠉⠛⠀⠛⠋⠀⠀
⠉⠑⠒⠿⣶⣦⣀⠈⢳⣌⠳⣄⠈⠳⣄⠀⠀⠘⢿⣿⡾⠀⠀⠀⠀⠀⡷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠉⠙⢿⣾⣿⣆⡈⠳⣤⡉⣦⡀⠀⠈⢿⠇⠀⠀⠸⠁⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠋⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
Provides a set of types and functions to calculate sales subtotals, including taxes and discounts. It is designed to be flexible and extensible, allowing developers to define their own rules for calculating taxes and discounts.
-
Minimal dependencies: Only Gyro decimal as backend for calculations.
-
Decimal Arithmetic: The Johnny module provides a set of types and functions for decimal arithmetic, including Decimal types for representing decimal values and functions for adding, subtracting, multiplying, and dividing decimal values.
-
Uses custom Visitor pattern to allow developers to define their own rules for calculating taxes, discounts and other ways of manipulate the buffer.
To use the Johnny module, you need to import it into your Go code:
$ go get github.com/profe-ajedrez/johnny
import "github.com/profe-ajedrez/johnny"
Imagine the following case
1 You have the sale of a product with a unit value excluding taxes of $1044.543103448276
2 You will sell 35157
units
3 You will apply a 10%
discount
4 You will also apply a discount of $100
to the total without sales tax
5 You must apply a percentage tax of 16%
6 You must apply a tax in the amount of 0.04
per 100
units for packaging.
This case can be modeled as follows:
func main() {
// define entry values
unitValue := udfs("1044.543103448276")
qty := udfs("35157")
percDiscount := udfs("10")
amountLineDiscount := udfs("100")
percTax := udfs("16")
amountLineTax := qty.Div(HundredValue).Round(0).Mul(udfs("0.04"))
// define the buffer holder
calc := NewFromUnitValue(unitValue)
// define the visitors
qtyVisitor := WithQTY(qty)
percDiscVisitor := NewPercentualDiscount(percDiscount)
amountDiscVisitor := NewAmountDiscount(amountLineDiscount)
percTaxVisitor := NewUnbufferedPercTax(percTax)
amountTaxVisitor := NewUnbufferedAmountTax(amountLineTax)
// make the visitors visite the buffer holder
calc.Bind(qtyVisitor)
calc.Bind(percDiscVisitor)
calc.Bind(amountDiscVisitor)
calc.Bind(percTaxVisitor)
calc.Bind(amountTaxVisitor)
net = calc.Snapshot()
calc.Add(percTaxVisitor.Amount())
calc.Add(amountTaxVisitor.Amount())
brute = calc.Snapshot()
totalTaxes = percTaxVisitor.Amount().Add(amountTaxVisitor.Amount())
totalDiscounts = percDiscVisitor.Amount().Add(amountDiscVisitor.Amount())
fmt.Printf("net: %v", net)
fmt.Printf("brute: %v", brute)
fmt.Printf("totalTaxes: %v", totalTaxes)
fmt.Printf("totalDiscounts: %v", totalDiscounts)
// Output:
// net: udfs("33050601.6991379353988"),
// brute: udfs("38338712.051000005062608"),
// totalTaxes: udfs("5288110.351862069663808"),
// totalDiscounts: udfs("3672400.1887931039332"),
}
func udfs(d string) alpacadecimal.Decimal {
return unsafeDecFromStr(d)
}
func unsafeDecFromStr(d string) alpacadecimal.Decimal {
dec, _ := alpacadecimal.NewFromString(d)
return dec
}
See the examples folder for more usage examples.
Most of the visitors provided by this library do not perform any validation. For example, Tax and its derivatives do not verify that the ratio is greater than zero, which could cause a panic due to division by zero. This is a conscious decision, we leave it to the user to worry about whether the values are valid.