vincent15000's avatar

Design to refactor a billing code

Hello,

I work on an existing code written in pure HTML / CSS / PHP / JS / jQuery. The command from the owner is to refactor the code which calculates the consumptions and amounts to bill.

Here is a short description of the application (I can't say more about it). A service is sold to clients and this service can generate different bills according to the choosen options.

Each bill has already his own class (Option1Bill, Option2Bill, Option3Bill, ...) with its particularities and each one has its own calculate() method to calculate the consumptions and amounts with its proper rules, but all calculate() method has quite a similar code.

In the initial code, there is :

  • a Service

  • different Options, each that has to be billed Option has its own properties in its class Option1, Option2, Option3, ... with a relationship to the Service

  • there is a specific Bill clas for each Option, Option1Bill, Option2Bill, ...

class Service
{
	public $id;	
}
...
class Option
{
		public $id;
		public $price;
}
...
class OptionXBill
{
		public $id;
		public $consumption;
		public $amount;
		public option_id;
		public $service_id;
		...
		static function calculate()
		{
				// calculation code
		}
		...
}

I can't change this initial architecture.

I have already refactored the different calculate() methods for each Bill class, but I'm not satisfied. What I have done is :

  • create a Billable class to have a unique calculate() method

  • this Billable class has many functions to calculate the each OptionXBill

  • the Service class extends from the Billable class

  • I call this class from the Service isntance with an argument to specify which option has to be calculated, for example calculate('option1')

class Service extends Billable
{
		...
}
...
$service = getService($service_id);

$amountOption1 = $service->calculate('option1', $save);
$amountOption1 = $service->calculate('option2', $save);
$amountOption1 = $service->calculate('option3', $save);
$amountOption1 = $service->calculate('option4', $save);
...
// sum and save all amounts

The $save variable is a boolean to specify if the OptionXBill has to be saved in the database or just calculated

Then I can for example calculate the sum of the different amounts and save them in a global bill.

What's great is that I have a unique function to manage all bills. But the problem is that I have a very confusing code with a lot of conditions to take into account the particularities of each bill.

I'd like to have a more readable code, probably by using inheritence via a better way than what I've done.

I need advice to have a better structure for my code.

What would you suggest me ? For example would you suggest me to create other classes or some gateway classes to take into account the different kinds of bills or something else I don't thought about ? I have followed the serie from Jeffrey on PHP, he gives very good examples to use inheritence, but I have some difficulty to apply this in another context.

Thanks for your help.

Vincent

0 likes
2 replies
LaryAI's avatar
Level 58

One possible solution to make the code more readable and maintainable is to use inheritance and polymorphism. Instead of having separate classes for each option bill, create a base class called OptionBill and have each option bill class extend from it. Then, move the common code from the calculate() method to the base class and make it abstract. This way, each option bill class only needs to implement the specific rules for that option.

Here's an example implementation:

abstract class OptionBill
{
    protected $id;
    protected $consumption;
    protected $amount;
    protected $option;
    protected $service;

    public function __construct($id, $option, $service)
    {
        $this->id = $id;
        $this->option = $option;
        $this->service = $service;
    }

    public function calculate($save)
    {
        // common calculation code
        $this->consumption = $this->calculateConsumption();
        $this->amount = $this->calculateAmount();

        if ($save) {
            $this->save();
        }

        return $this->amount;
    }

    protected abstract function calculateConsumption();

    protected abstract function calculateAmount();

    protected function save()
    {
        // save to database
    }
}

class Option1Bill extends OptionBill
{
    protected function calculateConsumption()
    {
        // specific calculation for option 1
    }

    protected function calculateAmount()
    {
        // specific calculation for option 1
    }
}

class Option2Bill extends OptionBill
{
    protected function calculateConsumption()
    {
        // specific calculation for option 2
    }

    protected function calculateAmount()
    {
        // specific calculation for option 2
    }
}

// and so on for other option bills

Then, in the Service class, you can create an array of option bill objects and loop through them to calculate and sum the amounts:

class Service
{
    protected $id;
    protected $options = [];

    public function __construct($id)
    {
        $this->id = $id;
        $this->options[] = new Option1Bill(1, $this->getOption(1), $this);
        $this->options[] = new Option2Bill(2, $this->getOption(2), $this);
        // add other option bills here
    }

    public function calculate($save)
    {
        $totalAmount = 0;

        foreach ($this->options as $option) {
            $totalAmount += $option->calculate($save);
        }

        return $totalAmount;
    }

    protected function getOption($id)
    {
        // get option from database
    }
}

This way, you have a more modular and extensible code that is easier to read and maintain.

1 like

Please or to participate in this conversation.