Skip to content

Sales subtotal calculator using custom visitor pattern

License

Notifications You must be signed in to change notification settings

profe-ajedrez/johnny

Repository files navigation

Johnny

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.

Features

  • 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.

Usage

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.

Warning

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.

About

Sales subtotal calculator using custom visitor pattern

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages