154 lines
5.4 KiB
Markdown
154 lines
5.4 KiB
Markdown
# PlanetExpress
|
|
|
|
Planet Express is your little helper to calculate possible discounts for your
|
|
shipments based on a given rule set and an available discount budget.
|
|
|
|
## Installation
|
|
|
|
Get the code from github and run `bundle install`
|
|
|
|
Afterwards you are ready to rumble.
|
|
|
|
I recommend ruby 2.6.6 or MRI 2.7 to run the code. Other ruby versions
|
|
should work as well, but are not officially supported.
|
|
|
|
## Usage
|
|
|
|
The input file has to adhere to the following format:
|
|
|
|
```
|
|
# ISO8601Date PackageSize Provider
|
|
2015-02-01 S PR
|
|
```
|
|
|
|
Now that you have an input file like that you can run the processing with
|
|
the following rake task:
|
|
|
|
```sh
|
|
$ bundle exec rake 'process[path/to/input_file.txt]'
|
|
|
|
# 2015-02-01 S MR 1.50 0.50
|
|
# 2015-02-02 S MR 1.50 0.50
|
|
# 2015-02-03 L LP 6.90 -
|
|
# 2015-02-05 S LP 1.50 -
|
|
# 2015-02-06 S MR 1.50 0.50
|
|
# 2015-02-06 L LP 6.90 -
|
|
# 2015-02-07 L MR 4.00 -
|
|
# 2015-02-08 M MR 3.00 -
|
|
# 2015-02-09 L LP 0.00 6.90
|
|
# 2015-02-10 L LP 6.90 -
|
|
# 2015-02-10 S MR 1.50 0.50
|
|
# 2015-02-10 S MR 1.50 0.50
|
|
# 2015-02-11 L LP 6.90 -
|
|
# 2015-02-12 M MR 3.00 -
|
|
# 2015-02-13 M LP 4.90 -
|
|
# 2015-02-15 S MR 1.50 0.50
|
|
# 2015-02-17 L LP 6.90 -
|
|
# 2015-02-17 S MR 1.90 0.10
|
|
# 2015-02-24 L LP 6.90 -
|
|
# 2015-02-29 CUSPS IGNORED
|
|
# 2015-03-01 S MR 1.50 0.50
|
|
```
|
|
|
|
## Architecture
|
|
|
|
The system is divided into four big groups:
|
|
|
|
- **Discounts** - provides a class to represent discounts as well as a budget
|
|
to keep track of the discount budgets for a given month
|
|
- **Shipments** - Basic representation of a shipment as in "an order that will be shipped"
|
|
- **ShippingOptions** - Struct and repository to represent providers and their shipping options
|
|
- **Rules** - contains all the logic for discount rules including a methods to apply
|
|
discounts to shipments
|
|
|
|
### Discounts
|
|
|
|
The discount class represents a discount that originated from a rule and contains
|
|
the monetary amount as well as the rule that was responsible for applying the discount.
|
|
|
|
Use the `DiscountBudgetForMonthRepository` to get the discount budget for a given month.
|
|
|
|
### Shipments
|
|
|
|
Shipments are the input for all calculation and have a date and a reference
|
|
to a shipping option. They also keep track of their applied discounts.
|
|
|
|
**Important:** There's a distinction between a valid shipment (one that was parsed correctly)
|
|
and an invalid shipment. To not make you constantly check whether you handle
|
|
a valid shipment or an invalid one, you can use the `if_valid` helper.
|
|
|
|
```ruby
|
|
shipment.if_valid do |valid_shipment|
|
|
# code that is only executed in case you have a valid shipment on hand
|
|
end
|
|
```
|
|
|
|
The package also includes a `ShipmentReader` that adheres to a simple enumerable
|
|
interface in order to decouple the IO/parsing from the remaining processing. If
|
|
you don't need it you can replace all instances of a `ShipmentReader` with a simple
|
|
array.
|
|
|
|
To provide structure output you can use the `ShipmentFormatter` which formats
|
|
a given `Shipment` in a CSV (whitespace separated) format with their discounts.
|
|
|
|
### Shipping Options
|
|
|
|
Shipping options are a struct to represent an individual shipping option, it
|
|
includes the price, the provider as well as the package size.
|
|
|
|
Use the `ShippingOptionRepository` to retrieve shipping options and query
|
|
for specific providers/package sizes.
|
|
|
|
### ShipmentDiscountCalculator
|
|
|
|
This class is the central entry point for calculating discounts for a list of
|
|
shipments. It takes a shipment_repository and a list of rules and
|
|
applies the list of rules to the given shipments
|
|
|
|
Example:
|
|
|
|
```ruby
|
|
calculator = ShipmentDiscountCalculator.new(
|
|
shipment_repository: ShipmentRepository.new(data: []),
|
|
rules: [Rule.new(...)])
|
|
|
|
calculator.run([Shipment.new])
|
|
```
|
|
|
|
### Repository
|
|
|
|
The repository base class is the managing unit to retrieve and store shared
|
|
state. When possible nobody should query the data directly, but define
|
|
a query method in the repository to retrieve the required data.
|
|
|
|
It provides an ActiveRecord like `where` method with which you can
|
|
query for multiple attributes in one call:
|
|
|
|
```ruby
|
|
repo.where(attribute_a: 5, attribute_b: "Test") # => enumerable matching the criteria
|
|
```
|
|
|
|
### Money Handling
|
|
|
|
In order to prevent floating errors when calculating discounts the system uses
|
|
integers and all prices are defined including the cents, so 1.5€ are represented
|
|
as `150`.
|
|
|
|
## Development
|
|
|
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
|
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
|
|
## Contributing
|
|
|
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/planet_express. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/planet_express/blob/master/CODE_OF_CONDUCT.md).
|
|
|
|
## License
|
|
|
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
|
|
## Code of Conduct
|
|
|
|
Everyone interacting in the PlanetExpress project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/planet_express/blob/master/CODE_OF_CONDUCT.md).
|